/******************************************************/
/* Support for SNK-004 smartcard for authentication.  */
/*                                                    */
/* In essence, SNK is jusr a DES calculator. User     */
/* receives challenge (6 or 8 decimal digits) from    */
/* a remote computer he wishes to authenticate to,    */
/* activates the SNK-004 card by turning it on and    */
/* typing in his 4-digit PIN, and then types in the   */
/* challenge. After "Ent" key is pressed, SNK will    */
/* encrypt the challenge given with the user secret   */
/* key it holds inside, and 8 digits'll be displayed  */
/* on the LCD screen (either hexadecimal, or decimal, */
/* depending on how the card was configured). Now     */
/* user reads that response from LCD and types it in  */
/* to the computer. Voila!                            */
/*                                                    */
/* So this file contains functions to do the following*/
/* things:                                            */
/*         - read flat file (ASCII) of registered     */
/*           SNK users, that contains user login      */
/*           names and their DES keys. This file      */
/*           is a WEAK point!                         */
/*                                                    */
/*         - generate a pseudo-random challenge to    */
/*           be sent to user;                         */
/*                                                    */
/*         - encrypt the challenge to obtain the      */
/*           response;                                */
/*                                                    */
/*         - to perform the dialog (i.e. send the     */
/*           challenge, retrieve response and         */
/*           do the comparison with the response      */
/*           we expect).                              */
/*                                                    */
/* User database is a flat ASCII file, very much      */
/* vulnerable. Make sure it is as secure as one       */
/* can make it.                                       */
/*                                                    */
/* Note that we support only hexadecimal mode of      */
/* SNK, as it is more secure.                         */
/*                                                    */
/* Database default location:                         */
/*                                                    */
/*        /usr/kerberos/database/snk.users            */
/*                                                    */
/* Database is a flat ASCII file with record format   */
/* as follows:                                        */
/*                                                    */
/*   username xxx xxx xxx xxx xxx xxx xxx xxx         */
/*                                                    */
/* where each "xxx" is 3-digit octal value of each    */
/* corresponding DES key that is loaded into SNK      */
/* card.                                              */
/*                                                    */
/*                                                    */
/* It is worth to note, that on Linux this code uses  */
/* /dev/urandom to get pseudorandom numbers for       */
/* challenge. Thanks to Ted Ts'o for creating it.     */
/*                                                    */
/* Dealing with SNK, this code borrows somewhat from  */
/* Cygnus CNS (port of Kerberos-IV). That code is     */
/* under Cygnus and MIT Copyright.                    */
/*                                                    */
/* Copyright (C) Uri Blumenthal, 1996                 */
/* Copyright (C) IBM Corp,       1996                 */
/*                                                    */
/* Do with this code whatever you wish as long as     */
/* this Copyright notice stays intact and the         */
/* Copyright holders are held harmless against        */
/* anything related to this code or its usage.        */
/*                                                    */
/* Written: 14 May 1996 by Uri Blumenthal             */
/*                         uri@watson.ibm.com         */
/*                                                    */
/******************************************************/

#ifdef SNK

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>

#ifdef DESCORE
#include "desCore.h"
#define des_key_schedule DesKeys
typedef unsigned char des_cblock[8];
#else
#include "des.h"
#endif /* DESCORE */


#ifndef _SNK_USERS_FILE
#define _SNK_USERS_FILE "/usr/kerberos/database/snk.users"
#endif /* _SNK_USERS_FILE */


typedef struct {
  char          user_name[32];
  unsigned char user_key [32];
  int           flags;
} USER_DATA;


static unsigned long
generate_random_challenge(char *u);


int issnkregistered(char *user, USER_DATA *ud)
{
  FILE *fp = fopen(_SNK_USERS_FILE, "r");
  USER_DATA u;

  memset((char *)&u, 0, sizeof(USER_DATA));
  
#ifdef DEBUG
  printf("SNK: looking for user \"%s\"\n", user);
#endif

  if (fp == (FILE *)0) { /* no users database */
#ifdef DEBUG
    printf("SNK: cannot open file \"%s\"\n",
	   _SNK_USERS_FILE);
#endif
    return -1;           /* return failure */
  }

  if (strlen(user) > 31) {
#ifdef DEBUG
    printf("User name \"%s\" too long (%d>31)\n", 
	   user, strlen(user));
#endif
    return -1; /* illegal username */
  }

  while (!feof(fp)) { 
    /* retrieve user record from the file */
    if (fscanf(fp, "%s %o %o %o %o %o %o %o %o\n",
	       u.user_name,
	       &u.user_key[0], &u.user_key[1], 
	       &u.user_key[2], &u.user_key[3], 
	       &u.user_key[4], &u.user_key[5], 
	       &u.user_key[6], &u.user_key[7]) != 9) {
      /* problem reading file, return failure */
#ifdef DEBUG
      printf("Error reading from the file...\n");
#endif
      return -1;  
    }
    
#ifdef DEBUG
    printf("Got record for \"%s\" with the key %3o %3o %3o %3o...\n",
	   u.user_name, u.user_key[0], u.user_key[1],
	   u.user_key[2], u.user_key[3]);
#endif    

    if (strcmp(user, u.user_name) == 0) {
      if (ud != (USER_DATA *)0) /* pass the structure */
	memcpy((unsigned char *)ud, (unsigned char *)&u,
	       sizeof(USER_DATA));
      return 0; /* found user! */
    }
  }
  return -1;
} /* end of issnkregistered() */


int snkauth(char *username)
{
  USER_DATA u;
  char   buf[80]; /* buffer for I/O with user */
  char   resp[9]; /* buf to keep encrypted response */
  int    i = 0;
  unsigned long challenge = 0;
  unsigned long k         = 0;
  des_cblock    response;

  des_key_schedule  snk_key;
  

  memset((unsigned char *)&u, 0, sizeof(USER_DATA));

  if (issnkregistered(username, &u) != 0) {
#ifdef DEBUG
    printf("User \"%s\" not registered for SNK...\n", username);
#endif
    return -1;
  }

  challenge  = generate_random_challenge(username);
  challenge %= 100000000;

  sprintf(buf, "%8.8u%c", challenge, 0);

  des_set_odd_parity((des_cblock *)&u.user_key[0]);
  if (des_key_sched(u.user_key, snk_key) != 0) {
#ifdef DEBUG
    printf("Error! User DES key is bad!\n");
#endif
    return -1;
  }
  des_ecb_encrypt((des_cblock *)buf, 
		  (des_cblock *)response, snk_key, 1);
  for (i = 0; i < 4; i++) {
    sprintf(&resp[2*i], "%1.1x%1.1x", 
	    (response[i] >> 4) & 0xF,
	    (response[i] & 0xF));
  }
  
  printf("\nChallenge:  %s\n\nResponse:  ", buf);
  memset(buf, 0, sizeof(buf));
  if (fgets(buf, 50, stdin) == (char *)0) {
#ifdef DEBUG
    printf("SNK: Cannot read user response!\n");
#endif
    return -1;
  }

  buf[strlen(buf)] = 0;

#ifdef DEBUG
  printf("Correct response: \"%s\"  got: \"%s\"\n",
	 resp, buf);
#endif /* DEBUG */

  sscanf(buf, "%x", &k);

  for (i = 0; i < 8; i++) {
    if (tolower(resp[i]) != tolower(buf[i])) {
#ifdef DEBUG
      printf("Digit #%d is incorrect! got %c wanted %c\n",
	     i+1, buf[i], resp[i]);
#endif
      return -1; /* wrong response */
    }
  } /* endfor comparing the buffer */

  printf("\nSNK authentication successful.\n\n");

  return 0; /* authenticated OK */
} /* end of snkauth() */


static unsigned long
generate_random_challenge(char *u)
{
  unsigned long l = time(0);

  FILE *f = fopen("/dev/urandom", "r");

  

  if (f == (FILE *)0) {
#ifdef DEBUG
    printf("Cannot open /dev/urandom!\n");
#endif
  }

  if((f == (FILE *)0) ||
     (fread((void *)&l, sizeof(l), 1, f) != 1)) {
    int i;
    des_key_schedule dk;
    char tmp_buf[8];

    strncpy(tmp_buf, u, 8);
    
    *((int *)&tmp_buf[0]) ^= l;

    des_set_odd_parity(tmp_buf);
    des_key_sched(tmp_buf, dk);

    des_ecb_encrypt((des_cblock *)tmp_buf,
		    (des_cblock *)tmp_buf, dk, 1);
    des_ecb_encrypt((des_cblock *)tmp_buf,
		    (des_cblock *)tmp_buf, dk, 1);
		    
    l = (
	 ((tmp_buf[0] ^ tmp_buf[4]) << 24) | 
	 ((tmp_buf[1] ^ tmp_buf[5]) << 16) |
	 ((tmp_buf[2] ^ tmp_buf[6]) <<  8) | 
	 (tmp_buf[3] ^ tmp_buf[7])
	 );
    
#ifdef DEBUG
    printf("Cannot read %d bytes from /dev/urandom!\n");
#endif
  } 

  return l;

} /* end of generate_random_challenge() */


#endif /* SNK */

