/* * Copyright (c) 1996-2007, OpenFWTK Development Group * All rights reserved. See LICENSE. */ /* This software module is a rewrite of the snk.c module to use MD5. Wherever possible data structures and methods have been exactly converted. Since this software uses a cryptographic one way function that is designed for authentication only (unlike DES which is useable for privacy) it is exportable. Some ugliness of code (for which I apologise) results from the translation. The SNK authentication routines store the secret calculator key as a set of 8 chars, encoded in hex values in the authentication database as the user's password. These 8 values are converted into a DES ECB mode key by the make_key_sched routine. For the MD5 version, we use a similar approach. The user's MD5 seed is stored as 8 chars also hex coded. This is used as an initializer to the MD5 function, then followed by the challenge. The user performs the same operation and if the values compare correctly, we know it is him. Where this system deviates from the SNK authentication scheme is that the user remembers a password string. The softmd5.c authenticator prompts the user for their secret password string, then MD5s is, and pulls the first 8 chars out to use as the initializer to the MD5 function. With an SNK, this step is unnecessary, since the user's "password" is used to unlock the stored DES key. All other transformations performed on the key are the same, except we don't use DES ECB. */ /* From the snk.c code: For those desiring to test their implementation, this is a dialog that will serve as a key base for a functional SNK004: ? softsnk -I -f foo Enter key: Verifying password Enter key: Enter key groups in octal: group 1: 123 group 2: 123 group 3: 123 group 4: 123 group 5: 123 group 6: 123 group 7: 123 group 8: 123 wrote key, checksum is 17d819 ? softsnk -f foo Enter key: Challenge: 987654 Response: 23162720 ? softsnk -f foo Enter key: Challenge: 1234567 Response: 26686397 ? script done on Sat Nov 19 13:27:00 1994 */ #include #include #include #include #include #include #include #include "firewall.h" #include "auth.h" #include "authdb.h" #ifdef AUTHPROTO_MDAUTH #include "global.h" #ifdef OpenBSD #include "ssl/md5.h" #else #include "openssl/md5.h" #endif static char *moduleId ATTR_UNUSED = "$Id: mdauth.c,v 1.8 2007/09/10 02:49:12 arkenoi Exp $"; extern long randomnumber(); extern size_t strlcat(char*,const char*,size_t); extern size_t strlcpy(char*,const char*,size_t); typedef unsigned char md5_cblock[8]; static int challenged = 0; static char challbuf[32]; int mdauthchallng(user,buf,bs) char *user; char *buf; int bs; { challenged = 1; strlcpy(buf,"MD5 Challenge \"",MAX_STR); snprintf(challbuf,32,"%6.6lu",randomnumber() % 999999); strlcat(buf,challbuf,MAX_STR); strlcat(buf,"\": ",MAX_STR); return(0); } /* is this ugly or what? it's late and I don't feel clever */ static int make_key_sched(s,k) char *s; md5_cblock k; { unsigned int k0; unsigned int k1; unsigned int k2; unsigned int k3; unsigned int k4; unsigned int k5; unsigned int k6; unsigned int k7; int x; x = sscanf(s, "%o %o %o %o %o %o %o %o", &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7); if(x != 8) return(1); k[0] = k0; k[1] = k1; k[2] = k2; k[3] = k3; k[4] = k4; k[5] = k5; k[6] = k6; k[7] = k7; return(0); } int mdauthverify(user,pass,ap,rbuf) char *user; char *pass; Auth *ap; char *rbuf; { MD5_CTX keysched; md5_cblock kblock; unsigned char digest[16]; char buf[12]; char cbuf[12]; int i; int j; unsigned long kval = 0; strlcpy(rbuf,"Permission Denied.",MAX_STR); if(!challenged) return(1); challenged = 0; /* lowercase the response code, in case it's hex */ for(i=0; pass[i]; i++) if(isupper(pass[i])) pass[i] = tolower(pass[i]); /* set up a key from the shared secret */ if(make_key_sched(ap->pw,kblock)) { strlcpy(rbuf,"Cannot decode user secret key",MAX_STR); return(1); } /* zeroize the entire buffer */ for(i = 0; i < 9; i++) buf[i] = '\0'; strncpy(buf,challbuf,8); /* begin MD5 specific code */ MD5_Init(&keysched); /* set the "key" (initializer) into MD5 */ MD5_Update(&keysched,kblock,8); /* push response through the rotating knives; normally DES ECB */ MD5_Update(&keysched,buf,8); /* wrap up MD5 into digest */ MD5_Final(digest,&keysched); /* emulate DES by pulling 8 bytes into cbuf */ bcopy(digest,cbuf,8); /* done MD5 specific code */ /* pull some bits out of the ciphertext into a long */ for(i=0; i<4; i++) for(j = 0; j < 8; j++) kval = (kval << 1) | ((cbuf[i] >> (7 - j)) & 1); /* crunch it into a hex string */ snprintf(buf,sizeof(buf),"%08lx",kval); if(!strcmp(pass,buf)) { strlcpy(rbuf,"ok",MAX_STR); return(0); } /* crunch hex to decimal and try that */ for(i=0; buf[i]; i++) if(buf[i] == 'a' || buf[i] == 'b' || buf[i] == 'c') buf[i] = '2'; else if(buf[i] == 'd' || buf[i] == 'e' || buf[i] == 'f') buf[i] = '3'; if(strcmp(pass,buf)) return(1); strlcpy(rbuf,"ok",MAX_STR); return(0); } int mdauthset(user,pass,ap,rbuf) char *user; char *pass; Auth *ap; char *rbuf; { md5_cblock kblock; if(make_key_sched(pass,kblock)) { strlcpy(rbuf,"Cannot decode user secret key",MAX_STR); return(0); } if(strlen(pass) >= AUTH_PWSIZ) { strlcpy(rbuf,"Secret key too long",MAX_STR); return(0); } strlcpy(ap->pw,pass,AUTH_PWSIZ); if(auth_dbputu(user,ap) == 0) strlcpy(rbuf,"Secret key changed",MAX_STR); else strlcpy(rbuf,"Database error.",MAX_STR); return(0); } #endif