/* * Copyright (c) 1996-2007, OpenFWTK Development Group * All rights reserved. See LICENSE. */ #include #include #include #include #include #include #include #include #include #ifdef OpenBSD #include #include #include #include #else #include #include #include #include #endif #include "firewall.h" #include "firewall2.h" #include "auth.h" #include "authdb.h" static char *moduleId ATTR_UNUSED = "$Id: skey.c,v 1.10 2007/09/10 02:49:13 arkenoi Exp $"; #ifdef AUTHPROTO_SKEY #include "skey.h" #include #define MAX_PROTONAME_LEN 20 /* Default hash function to use (index into skey_hash_types array) */ #ifndef SKEY_HASH_DEFAULT #define SKEY_HASH_DEFAULT 1 #endif typedef struct { int n; char seed[SKEY_MAX_SEED_LEN]; char val[SKEY_MAX_CHALLENGE]; } skeydb; int s_keyset(char*,char*,Auth*,char*); static void f_md4(char *x); static void f_md5 (char *x); static void f_sha1(char *x); static void f_rmd160(char *x); static int keycrunch_md4(char *result, char *seed, char *passwd); static int keycrunch_md5(char *result, char *seed, char *passwd); static int keycrunch_sha1(char *result, char *seed, char *passwd); static int keycrunch_rmd160(char *result, char *seed, char *passwd); static void lowcase(char *s); /* Current hash type (index into skey_hash_types array) */ static int skey_hash_type = SKEY_HASH_DEFAULT; /* * Hash types we support. * Each has an associated keycrunch() and f() function. */ #define SKEY_ALGORITH_LAST 4 struct skey_algorithm_table { const char *name; int (*keycrunch) (char *, char *, char *); void (*f) (char *); }; static struct skey_algorithm_table skey_algorithm_table[] = { { "md4", keycrunch_md4, f_md4 }, { "md5", keycrunch_md5, f_md5 }, { "sha1", keycrunch_sha1, f_sha1 }, { "rmd160", keycrunch_rmd160, f_rmd160 } }; static int challenged = 0; extern AProto *codetoproto(); char* proto2hashname(char proto_typ) { static char hashname[SKEY_MAX_HASHNAME_LEN + 1]; char protoname[MAX_PROTONAME_LEN + 1]; AProto* proto; char *proto_extr; int i=0; hashname[SKEY_MAX_HASHNAME_LEN] = protoname[MAX_PROTONAME_LEN] = '\0'; strncpy(protoname, (proto = codetoproto(proto_typ)) ? proto->name : "skey-md4", MAX_PROTONAME_LEN); if((proto_extr = rindex(protoname, '-'))) while(i < SKEY_MAX_HASHNAME_LEN && (hashname[i++] = tolower(*++proto_extr))) ; else strncpy(hashname, "md4", SKEY_MAX_HASHNAME_LEN); return hashname; } int s_keychallng(user,buf,bs) char *user; char *buf; int bs; { Auth userrec; skeydb* skey; strlcpy(buf,"Skey Challenge ",MAX_STR); if(!auth_dbgetu(user, &userrec) && userrec.authproto_rec.active) { skey_set_algorithm(proto2hashname(userrec.atyp)); skey = (skeydb *)userrec.authproto_rec.buf; (void)snprintf(&buf[15], MAX_STR-16, "otp-%.*s %d %.*s", SKEY_MAX_HASHNAME_LEN, skey_get_algorithm(), skey->n - 1, SKEY_MAX_SEED_LEN, skey->seed); strlcat(buf, " :", MAX_STR); challenged = 1; return(0); } else { snprintf(buf,MAX_STR,"Skey not initialized for %s",user); return(1); } } int s_keyverify(user,pass,ap,rbuf) char *user; char *pass; Auth *ap; char *rbuf; { char key[SKEY_BINKEY_SIZE]; char fkey[SKEY_BINKEY_SIZE]; char filekey[SKEY_BINKEY_SIZE]; time_t now; struct tm *tm; char tbuf[27]; skeydb* skey; strlcpy(rbuf,"Permission Denied.",MAX_STR); if(!challenged) return(1); challenged = 0; time(&now); tm = localtime(&now); (void)strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm); if (pass == NULL) { return(1); } rip(pass); /* Convert pass to binary */ if (etob(key, pass) != 1 && atob8(key, pass) != 0) { /* Neither english words or ascii hex */ return(1); } /* Compute fkey = f(key) */ (void)memcpy(fkey, key, sizeof(key)); (void)fflush(stdout); f(fkey); /* Reread the s/key record NOW and leave DB (and open) */ /* * Obtain an exclusive lock on the DB file so the same password * cannot be used twice to get in to the system. */ if(auth_dbgetu_forupdate(user, ap) || !ap->authproto_rec.active) { auth_dbclose(); return(1); } skey_set_algorithm(proto2hashname(ap->atyp)); skey = (skeydb *)ap->authproto_rec.buf; /* And convert file value to hex for comparison */ atob8(filekey, skey->val); /* Do actual comparison */ if (memcmp(filekey, fkey, SKEY_BINKEY_SIZE) != 0){ /* Wrong pass */ auth_dbclose(); return(1); } btoa8(skey->val,key); skey->n--; ap->authproto_rec.active = (skey->n > 0); if(auth_dbputu(user,ap)) { sosay(0, "database error"); auth_dbclose(); /* Unlock DB file and close it */ return(1); } /* Unlock DB file and close it */ auth_dbclose(); strlcpy(rbuf,"ok",MAX_STR); if(skey->n < 5) strlcat(rbuf,"Warning -- Change password soon!",MAX_STR); return(0); } #define NAMELEN 2 int s_keyset_md4(user,pass,ap,rbuf) char *user; char *pass; Auth *ap; char *rbuf; { skey_set_algorithm("md4"); return(s_keyset(user,pass,ap,rbuf)); } int s_keyset_md5(user,pass,ap,rbuf) char *user; char *pass; Auth *ap; char *rbuf; { skey_set_algorithm("md5"); return(s_keyset(user,pass,ap,rbuf)); } int s_keyset_sha1(user,pass,ap,rbuf) char *user; char *pass; Auth *ap; char *rbuf; { skey_set_algorithm("sha1"); return(s_keyset(user,pass,ap,rbuf)); } int s_keyset_rmd160(user,pass,ap,rbuf) char *user; char *pass; Auth *ap; char *rbuf; { skey_set_algorithm("rmd160"); return(s_keyset(user,pass,ap,rbuf)); } /* this is a ripped-up version of skeyinit.c from the skey dist'n */ int s_keyset(user,pass,ap,rbuf) char *user; char *pass; Auth *ap; char *rbuf; { struct tm *tm; int n; int nn; char seed[18]; char key[8]; char defaultseed[17]; time_t now; char tbuf[27]; char hbuf[MAX_HOSTNAME]; char lastc; int l; char alg[32]; skeydb* skey; if((pass == (char *)0) || (*pass == '\0')) { strlcpy(rbuf,"NULL passwords not permitted for S/key.",MAX_STR); return(1); } time(&now); tm = localtime(&now); strlcpy(alg,skey_get_algorithm(),sizeof(alg)); strftime(tbuf, sizeof(tbuf), "%M%j", tm); gethostname(hbuf,sizeof(hbuf)); defaultseed[0] = hbuf[0]; defaultseed[1] = hbuf[1]; strlcpy(&defaultseed[NAMELEN],tbuf,sizeof(defaultseed)-NAMELEN); if(ap == NULL) { strlcpy(rbuf,"Database error.",MAX_STR); return(1); } else { skey = (skeydb *)ap->authproto_rec.buf; if(!ap->authproto_rec.active) skey->seed[0] = '\0'; else { l = strlen(skey->seed); if(l > 0) { lastc = skey->seed[l-1]; if(isdigit(lastc) && lastc != '9') { strlcpy(defaultseed, skey->seed, sizeof(defaultseed)); defaultseed[l-1] = lastc + 1; } if(isdigit(lastc) && lastc == '9' && l < 16) { strlcpy(defaultseed, skey->seed, sizeof(defaultseed)); defaultseed[l-1] = '0'; defaultseed[l] = '0'; defaultseed[l+1] = '\0'; } } } } skey_set_algorithm(alg); n = 664; strlcpy(seed,defaultseed,sizeof(seed)); /* Crunch seed and password into starting key */ if(keycrunch(key,seed,pass) != 0) { strlcpy(rbuf,"Database error.",MAX_STR); return(1); } nn = n; while(nn-- != 0) f(key); time(&now); tm = localtime(&now); strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm); btoa8(skey->val,key); skey->n = n; strncpy(skey->seed, seed, SKEY_MAX_SEED_LEN); ap->authproto_rec.active = 1; if(auth_dbputu(user,ap)) { sosay(0, "database error"); return(1); } snprintf(rbuf,MAX_STR,"ID %s s/key-%s is %d %s",user,alg,n,seed); return(0); } /* * Crunch a key: * concatenate the seed and the password, run through MD4_/5 and * collapse to 64 bits. This is defined as the user's starting key. */ int keycrunch(result, seed, passwd) char *result; /* SKEY_BINKEY_SIZE result */ char *seed; /* Seed, any length */ char *passwd; /* Password, any length */ { return(skey_algorithm_table[skey_hash_type].keycrunch(result, seed, passwd)); } static int keycrunch_md4(result, seed, passwd) char *result; /* SKEY_BINKEY_SIZE result */ char *seed; /* Seed, any length */ char *passwd; /* Password, any length */ { char *buf; MD4_CTX md; uint32_t results[4]; unsigned int buflen; buflen = strlen(seed) + strlen(passwd); if ((buf = (char *)xmalloc(buflen+1)) == NULL) return(-1); (void)strlcpy(buf, seed, buflen); lowcase(buf); (void)strlcat(buf, passwd, buflen+1); /* Crunch the key through MD4_ */ sevenbit(buf); MD4_Init(&md); MD4_Update(&md, (unsigned char *)buf, buflen); MD4_Final((unsigned char *)results, &md); (void)free(buf); /* Fold result from 128 to 64 bits */ results[0] ^= results[2]; results[1] ^= results[3]; (void)memcpy((void *)result, (void *)results, SKEY_BINKEY_SIZE); return(0); } static int keycrunch_md5(result, seed, passwd) char *result; /* SKEY_BINKEY_SIZE result */ char *seed; /* Seed, any length */ char *passwd; /* Password, any length */ { char *buf; MD5_CTX md; uint32_t results[4]; unsigned int buflen; buflen = strlen(seed) + strlen(passwd); if ((buf = (char *)xmalloc(buflen+1)) == NULL) return(-1); (void)strlcpy(buf, seed, buflen); lowcase(buf); (void)strlcat(buf, passwd, buflen+1); /* Crunch the key through MD5_ */ sevenbit(buf); MD5_Init(&md); MD5_Update(&md, (unsigned char *)buf, buflen); MD5_Final((unsigned char *)results, &md); (void)free(buf); /* Fold result from 128 to 64 bits */ results[0] ^= results[2]; results[1] ^= results[3]; (void)memcpy((void *)result, (void *)results, SKEY_BINKEY_SIZE); return(0); } static int keycrunch_sha1(result, seed, passwd) char *result; /* SKEY_BINKEY_SIZE result */ char *seed; /* Seed, any length */ char *passwd; /* Password, any length */ { char *buf; SHA_CTX sha; uint32_t results[5]; unsigned int buflen; buflen = strlen(seed) + strlen(passwd); if ((buf = (char *)xmalloc(buflen+1)) == NULL) return(-1); (void)strlcpy(buf, seed, buflen); lowcase(buf); (void)strlcat(buf, passwd, buflen+1); /* Crunch the key through SHA1 */ sevenbit(buf); SHA1_Init(&sha); SHA1_Update(&sha, (unsigned char *)buf, buflen); SHA1_Final((unsigned char *)results, &sha); (void)free(buf); /* Fold 160 to 64 bits */ results[0] ^= results[2]; results[1] ^= results[3]; results[0] ^= results[4]; (void)memcpy((void *)result, (void *)results, SKEY_BINKEY_SIZE); return(0); } static int keycrunch_rmd160(result, seed, passwd) char *result; /* SKEY_BINKEY_SIZE result */ char *seed; /* Seed, any length */ char *passwd; /* Password, any length */ { char *buf; RIPEMD160_CTX rmd; uint32_t results[5]; unsigned int buflen; buflen = strlen(seed) + strlen(passwd); if ((buf = (char *)xmalloc(buflen+1)) == NULL) return(-1); (void)strlcpy(buf, seed, buflen); lowcase(buf); (void)strlcat(buf, passwd, buflen+1); /* Crunch the key through RMD-160 */ sevenbit(buf); RIPEMD160_Init(&rmd); RIPEMD160_Update(&rmd, (unsigned char *)buf, buflen); RIPEMD160_Final((unsigned char *)results, &rmd); (void)free(buf); /* Fold 160 to 64 bits */ results[0] ^= results[2]; results[1] ^= results[3]; results[0] ^= results[4]; (void)memcpy((void *)result, (void *)results, SKEY_BINKEY_SIZE); return(0); } /* * The one-way function f(). * Takes SKEY_BINKEY_SIZE bytes and returns SKEY_BINKEY_SIZE bytes in place. */ void f(x) char *x; { skey_algorithm_table[skey_hash_type].f(x); } static void f_md4(x) char *x; { MD4_CTX md; uint32_t results[4]; MD4_Init(&md); MD4_Update(&md, (unsigned char *)x, SKEY_BINKEY_SIZE); MD4_Final((unsigned char *)results, &md); /* Fold 128 to 64 bits */ results[0] ^= results[2]; results[1] ^= results[3]; (void)memcpy((void *)x, (void *)results, SKEY_BINKEY_SIZE); } static void f_md5(x) char *x; { MD5_CTX md; uint32_t results[4]; MD5_Init(&md); MD5_Update(&md, (unsigned char *)x, SKEY_BINKEY_SIZE); MD5_Final((unsigned char *)results, &md); /* Fold 128 to 64 bits */ results[0] ^= results[2]; results[1] ^= results[3]; (void)memcpy((void *)x, (void *)results, SKEY_BINKEY_SIZE); } static void f_sha1(x) char *x; { SHA_CTX sha; uint32_t results[5]; SHA1_Init(&sha); SHA1_Update(&sha, (unsigned char *)x, SKEY_BINKEY_SIZE); SHA1_Final((unsigned char *)results, &sha); /* Fold 160 to 64 bits */ results[0] ^= results[2]; results[1] ^= results[3]; results[0] ^= results[4]; (void)memcpy((void *)x, (void *)results, SKEY_BINKEY_SIZE); } static void f_rmd160(x) char *x; { RIPEMD160_CTX rmd; uint32_t results[5]; RIPEMD160_Init(&rmd); RIPEMD160_Update(&rmd, (unsigned char *)x, SKEY_BINKEY_SIZE); RIPEMD160_Final((unsigned char *)results, &rmd); /* Fold 160 to 64 bits */ results[0] ^= results[2]; results[1] ^= results[3]; results[0] ^= results[4]; (void)memcpy((void *)x, (void *)results, SKEY_BINKEY_SIZE); } /* Strip trailing cr/lf from a line of text */ void rip(buf) char *buf; { buf += strcspn(buf, "\r\n"); if (*buf) *buf = '\0'; } /* Skip leading spaces from the string */ char * skipspace(cp) register char *cp; { while (*cp == ' ' || *cp == '\t') cp++; if (*cp == '\0') return(NULL); else return(cp); } /* Make sure line is all seven bits */ void sevenbit(s) char *s; { while (*s) *s++ &= 0x7f; } /* Convert string to lower case */ static void lowcase(s) char *s; { char *p; for (p = s; *p; p++) if (isupper(*p)) *p = tolower(*p); } /* Remove backspaced over characters from the string */ void backspace(buf) char *buf; { char bs = 0x8; char *cp = buf; char *out = buf; while (*cp) { if (*cp == bs) { if (out == buf) { cp++; continue; } else { cp++; out--; } } else { *out++ = *cp++; } } *out = '\0'; } /* * Convert 8-byte hex-ascii string to binary array * Returns 0 on success, -1 on error */ int atob8(out, in) register char *out; register char *in; { register int i; register int val; if (in == NULL || out == NULL) return(-1); for (i=0; i < 8; i++) { if ((in = skipspace(in)) == NULL) return(-1); if ((val = htoi(*in++)) == -1) return(-1); *out = val << 4; if ((in = skipspace(in)) == NULL) return(-1); if ((val = htoi(*in++)) == -1) return(-1); *out++ |= val; } return(0); } /* Convert 8-byte binary array to hex-ascii string */ int btoa8(out, in) register char *out; register char *in; { register int i; if (in == NULL || out == NULL) return(-1); for (i=0; i < 8; i++) { (void)snprintf(out, 3, "%02x", *in++ & 0xff); out += 2; } return(0); } /* Convert hex digit to binary integer */ int htoi(c) register int c; { if ('0' <= c && c <= '9') return(c - '0'); if ('a' <= c && c <= 'f') return(10 + c - 'a'); if ('A' <= c && c <= 'F') return(10 + c - 'A'); return(-1); } /* Set hash algorithm type */ char * skey_set_algorithm(new) char *new; { int i; for (i = 0; i < SKEY_ALGORITH_LAST; i++) { if (strcmp(new, skey_algorithm_table[i].name) == 0) { skey_hash_type = i; return(new); } } return(NULL); } /* Get current hash type */ const char * skey_get_algorithm() { return(skey_algorithm_table[skey_hash_type].name); } #endif