diff options
author | brandon <brandon@FreeBSD.org> | 1999-01-21 13:50:09 +0000 |
---|---|---|
committer | brandon <brandon@FreeBSD.org> | 1999-01-21 13:50:09 +0000 |
commit | 0cb1c5b095f3ccb2b3a2228a67aec0569258f62a (patch) | |
tree | f472b9120e94610b782de47336d68514b9406545 /lib/libcrypt/crypt.c | |
parent | 2c287cb830224d12f662854e87214efd9b8d8f70 (diff) | |
download | FreeBSD-src-0cb1c5b095f3ccb2b3a2228a67aec0569258f62a.zip FreeBSD-src-0cb1c5b095f3ccb2b3a2228a67aec0569258f62a.tar.gz |
Rewrite of crypt library to be more modular, and addition of the
Secure Hashing Algorithm - 1 (SHA-1), along with the further
refinement of what $x$salt$hash means. With this new crypt the
following are all acceptable:
$1$
$MD5$
$SHA1$
Note: $2$ is used by OpenBSD's Blowfish, which I considered adding
as $BF$, but there is no actual need for it with SHA-1. However,
somebody wishing to add OpenBSD password support could easilly add
it in now.
There is also a malloc_crypt() available in the library now, which
behaves exactly the same as crypt(), but it uses a malloced buffer
instead of a static buffer. However, this is not standard so will
likely not be used much (at all).
Also, for those interested I did a brief speed test Pentium 166/MMX,
which shows the DES crypt to do approximately 2640 crypts a CPU second,
MD5 to do about 62 crypts a CPU second and SHA1 to do about 18 crypts
a CPU second.
Reviewed by: Mark Murray
Diffstat (limited to 'lib/libcrypt/crypt.c')
-rw-r--r-- | lib/libcrypt/crypt.c | 276 |
1 files changed, 154 insertions, 122 deletions
diff --git a/lib/libcrypt/crypt.c b/lib/libcrypt/crypt.c index c0a8dc9..3d001a4 100644 --- a/lib/libcrypt/crypt.c +++ b/lib/libcrypt/crypt.c @@ -5,153 +5,185 @@ * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- - * - * $Id$ - * */ -#if defined(LIBC_SCCS) && !defined(lint) -static char rcsid[] = "$Header: /home/ncvs/src/lib/libcrypt/crypt.c,v 1.6 1997/01/14 06:09:00 jkh Exp $"; -#endif /* LIBC_SCCS and not lint */ +/* + * It has since been changed by Brandon Gillespie, the end result is not + * too clean, but it is clear and modular; there is no need for crypt() + * to be optimized (and actually a desire for the opposite) so I am not + * overly concerned. + */ + +/* + * Assumptions made with the format of passwords: + * + * + Any password beginning with a dollar-sign is assumed to be in + * the Modular Crypt Format (MCF), namely: $tag$salt$hash. Any + * algorithms added will also use this format. Other MCF assumptions: + * + The algorithm tag (field 1) will be less than five characters + * long (yay, arbitrary limits). Anything longer is ignored. + * New algorithm names are not allowed to be fully numeric as + * anything fully numeric is mapped from other OS's not following + * our standard, and from older versions of this standard (such as + * $1$ for MD5 passwords, rather than $MD5$). + * + The salt can be up to 16 characters in length (more arbitrary + * limits). + * + An invalid or unrecognized algorithm tag will default to use the + * 'best' encryption method--whatever that may be at the time. + * + If the MCF is not specified, use the 'best' method, unless DES + * is installed--then use DES. + * + Any password beginning with an underscore '_' is assumed to be + * the Extended DES Format, which has its own salt requirements, + * and is not the same as the MCF. + * + Salt must be limited to the same ascii64 character set the hash + * is encoded in (namely "./0-9A-Za-z"). + */ #include <unistd.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <md5.h> -#include <string.h> +#include <ctype.h> + +#define _CRYPT_C_ + +#include "crypt.h" + +#ifndef TRUE +#define TRUE 1 +#endif -static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ - "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +#ifndef FALSE +#define FALSE 0 +#endif -static void to64 __P((char *, unsigned long, int)); +/* + * commonly used througout all algorithms + */ -static void +static unsigned char ascii64[] = /* 0 ... 63 => ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +void to64(s, v, n) char *s; unsigned long v; int n; { -static void to64 __P((char *, unsigned long, int)); - while (--n >= 0) { - *s++ = itoa64[v&0x3f]; + *s++ = ascii64[v&0x3f]; v >>= 6; } } -/* - * UNIX password - * - * Use MD5 for what it is best at... - */ +static char * hash_word(password, salt, output) + const char *password; + const char *salt; + char *output; +{ + unsigned char spbuf[_CRYPT_MAX_SALT_LEN+1], + pwbuf[_CRYPT_OUTPUT_SIZE+1], + * ep, * sp, * pw; + unsigned int sl, pl, + tag = _CRYPT_DEFAULT_VERSION, + mcf = FALSE; + + memset(spbuf, 0, _CRYPT_MAX_SALT_LEN+1); + memset(pwbuf, 0, _CRYPT_MAX_SALT_LEN+1); + strncpy((char *) spbuf, (unsigned char *) salt, _CRYPT_MAX_SALT_LEN); + strncpy((char *) pwbuf, (unsigned char *) password, _CRYPT_OUTPUT_SIZE); + sp = &spbuf[0]; + pw = &pwbuf[0]; + pl = strlen((char *) pw); + + /* figure out what type of crypt is wanted */ + if (sp && sp[0] == '$') { + mcf = TRUE; + sp++; + if (strncasecmp((char *) sp, "MD5$", 4)==0) { + tag = _MD5_CRYPT; + sp += 4; + } else if (strncasecmp((char *) sp, "1$", 2)==0) { + tag = _MD5_CRYPT_OLD; + sp += 2; + } else if (strncasecmp((char *) sp, "SHA1$", 5)==0) { + tag = _SHS_CRYPT; + sp += 5; + } else { + tag = _CRYPT_DEFAULT_VERSION; + while (*sp && *sp != '$') + sp++; + if (*sp == '$') + sp++; + } + } + + /* Refine the salt. Go to the end, it stops at the first '$' or NULL */ + for (ep=sp; *ep && *ep != '$'; ep++) + continue; + + /* we have to do this so we dont overflow _PASSWORD_LEN */ + if ((ep - sp) > 16) { + sl = 16; + sp[16] = (char) NULL; + } else { + sl = ep - sp; + } + + switch (tag) { + case _MD5_CRYPT_OLD: + return crypt_md5(pw, pl, sp, sl, output, "$1$"); + case _MD5_CRYPT: + return crypt_md5(pw, pl, sp, sl, output, "$MD5$"); +#ifdef DES_CRYPT + case _DES_CRYPT: + return crypt_des(pw, pl, sp, sl, output, ""); +#endif + /* dropping a DES password through will likely cause problems, + but at least crypt() will return as it says it will (we cannot + return an error condition) */ + case _SHS_CRYPT: + default: + return crypt_shs(pw, pl, sp, sl, output, "$SHA1$"); + } +} char * -crypt(pw, salt) - register const char *pw; - register const char *salt; +crypt(password, salt) + const char *password; + const char *salt; { - static char *magic = "$1$"; /* - * This string is magic for - * this algorithm. Having - * it this way, we can get - * get better later on - */ - static char passwd[120], *p; - static const char *sp,*ep; - unsigned char final[16]; - int sl,pl,i,j; - MD5_CTX ctx,ctx1; - unsigned long l; - - /* Refine the Salt first */ - sp = salt; - - /* If it starts with the magic string, then skip that */ - if(!strncmp(sp,magic,strlen(magic))) - sp += strlen(magic); - - /* It stops at the first '$', max 8 chars */ - for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++) - continue; - - /* get the length of the true salt */ - sl = ep - sp; - - MD5Init(&ctx); - - /* The password first, since that is what is most unknown */ - MD5Update(&ctx,pw,strlen(pw)); - - /* Then our magic string */ - MD5Update(&ctx,magic,strlen(magic)); - - /* Then the raw salt */ - MD5Update(&ctx,sp,sl); - - /* Then just as many characters of the MD5(pw,salt,pw) */ - MD5Init(&ctx1); - MD5Update(&ctx1,pw,strlen(pw)); - MD5Update(&ctx1,sp,sl); - MD5Update(&ctx1,pw,strlen(pw)); - MD5Final(final,&ctx1); - for(pl = strlen(pw); pl > 0; pl -= 16) - MD5Update(&ctx,final,pl>16 ? 16 : pl); - - /* Don't leave anything around in vm they could use. */ - memset(final,0,sizeof final); - - /* Then something really weird... */ - for (i = strlen(pw); i ; i >>= 1) - if(i&1) - MD5Update(&ctx, final, 1); - else - MD5Update(&ctx, pw, 1); - - /* Now make the output string */ - strcpy(passwd,magic); - strncat(passwd,sp,sl); - strcat(passwd,"$"); - - MD5Final(final,&ctx); - - /* - * and now, just to make sure things don't run too fast - * On a 60 Mhz Pentium this takes 34 msec, so you would - * need 30 seconds to build a 1000 entry dictionary... - */ - for(i=0;i<1000;i++) { - MD5Init(&ctx1); - if(i & 1) - MD5Update(&ctx1,pw,strlen(pw)); - else - MD5Update(&ctx1,final,16); - - if(i % 3) - MD5Update(&ctx1,sp,sl); - - if(i % 7) - MD5Update(&ctx1,pw,strlen(pw)); - - if(i & 1) - MD5Update(&ctx1,final,16); - else - MD5Update(&ctx1,pw,strlen(pw)); - MD5Final(final,&ctx1); - } + static char output[_CRYPT_OUTPUT_SIZE]; + + return hash_word(password, salt, output); +} + +char * +malloc_crypt(password, salt) + const char *password; + const char *salt; +{ + char * output; + + output = (char *) malloc(sizeof(char) * _CRYPT_OUTPUT_SIZE); + return hash_word(password, salt, output); +} + +int +match_crypted(possible, crypted) + const char * possible, + * crypted; +{ + char * pc; + int match; - p = passwd + strlen(passwd); + pc = malloc_crypt(possible, crypted); - l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4; - l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4; - l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4; - l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4; - l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4; - l = final[11] ; to64(p,l,2); p += 2; - *p = '\0'; + match = !strcmp(pc, crypted); - /* Don't leave anything around in vm they could use. */ - memset(final,0,sizeof final); + free(pc); - return passwd; + return match; } +#undef _CRYPT_C_ |