#define _SHS_include_
#include "defs.h"
#include <ctype.h>
#include "execute.h"
#include "util.h"
#include "crypt.h"
static uChar ascii64[] = /* 0 ... 63 => ascii - 64 */
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
/*
// crypt() is not POSIX--this is here for backwards compatability,
// nasty word that.
*/
#ifdef USE_OS_CRYPT
extern char * crypt(const char *, const char *);
#endif
/*
// Encrypt a string. The salt can be NULL--force SHS encryption,
// match_crypted() will handle older DES passwords
*/
cStr * strcrypt(cStr * key, cStr * salt) {
char pwd_buf[SHS_OUTPUT_SIZE]; /* output buffer for the password */
uChar * pp, * sp, rsalt[9]; /* 8 chars of salt, one NULL */
Int x,
pl,
sl;
if (!salt) {
random_salt:
for (x=0; x < 8; x++)
rsalt[x] = ascii64[random_number(64)];
rsalt[8] = (uChar) NULL;
sp = rsalt;
sl = 8;
} else {
sp = (uChar *) string_chars(salt);
if (sp[0] == '$' && sp[1] == '2' && sp[2] == '$') {
sp += 3;
for (x=0; x < 8 && sp[x] != '$'; x++)
rsalt[x] = sp[x];
rsalt[x] = (uChar) NULL;
sp = rsalt;
sl = strlen((char *) sp);
if (!sl)
goto random_salt;
} else {
sl = string_length(salt);
}
}
pp = (uChar *) string_chars(key);
pl = string_length(key);
shs_crypt(pp, pl, sp, sl, pwd_buf);
return string_from_chars(pwd_buf, strlen(pwd_buf));
}
/*
// match the encrypted string, use SHS (default for crypt()) if
// 'encrypted' begins with "$2$" (FreeBSD standard); otherwise
// pass back to the OS crypt()
*/
Int match_crypted(cStr * encrypted, cStr * possible) {
uChar * ep, * pp, *sp, salt[9];
char p_buf[SHS_OUTPUT_SIZE];
Int sl,
el,
pl,
x;
ep = (uChar *) string_chars(encrypted);
el = string_length(encrypted);
pp = (uChar *) string_chars(possible);
pl = string_length(possible);
if (el < 3) {
cthrow(type_id, "Invalid password format");
return -1;
}
if (ep[0] == '$' && ep[1] == '2' && ep[2] == '$') {
sp = ep + 3;
for (x=0; x < 8 && sp[x] != '$'; x++)
salt[x] = sp[x];
salt[x] = (uChar) NULL;
sp = salt;
sl = strlen((char *) sp);
shs_crypt((uChar *) pp, pl, sp, sl, p_buf);
return (!strcmp((char *) ep, p_buf));
} else {
#ifdef USE_OS_CRYPT
#ifdef sys_freebsd
sp = ep;
#else
/* assume ancient DES format, with the first two chars as salt */
salt[0] = ep[0];
salt[1] = ep[1];
salt[2] = (uChar) NULL;
sp = salt;
#endif
return
(!strcmp((char *) ep, (char *) crypt((char *) pp, (char *) sp)));
#else
cthrow(type_id, "Driver was not compiled with OS crypt() support.");
return -1;
#endif
}
}
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
* 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: crypt.c,v 1.4 1996/07/12 18:56:01 jkh Exp $
*
***
*** This license somewhat applies to shs_crypt() as its based off PHK's
*** original MD5 crypt for FreeBSD -- Brandon Gillespie
***
*
*/
void to64(uChar *s, uInt v, Int n) {
while (--n >= 0) {
*s++ = ascii64[v&0x3f];
v >>= 6;
}
}
char * shs_crypt(const unsigned char * pw,
const Int pl,
const unsigned char * sp,
const Int sl,
char * passwd)
{
uChar *p;
uChar final[SHS_DIGEST_SIZE];
Int i,j;
SHS_CTX ctx,ctx1;
uInt l;
shsInit(&ctx);
/* The password first, since that is what is most unknown */
shsUpdate(&ctx,pw,pl);
/* Then our magic string */
shsUpdate(&ctx,(uChar *)"$2$",3);
/* Then the raw salt */
shsUpdate(&ctx,sp,sl);
/* Then just as many characters of the shs(pw,salt,pw) */
shsInit(&ctx1);
shsUpdate(&ctx1,pw,pl);
shsUpdate(&ctx1,sp,sl);
shsUpdate(&ctx1,pw,pl);
shsFinal(&ctx1,final);
for(i = pl; i > 0; i -= SHS_DIGEST_SIZE)
shsUpdate(&ctx,final,i>SHS_DIGEST_SIZE ? SHS_DIGEST_SIZE : i);
/* Don't leave anything around in vm they could use. */
memset(final,0,sizeof final);
/* Then something really weird... */
for (j=0,i = pl; i ; i >>= 1)
if(i&1)
shsUpdate(&ctx, final+j, 1);
else
shsUpdate(&ctx, pw+j, 1);
/* Now make the output string */
strcpy(passwd, "$2$");
strncat(passwd, (char *)sp, (Int)sl);
strcat(passwd, "$");
shsFinal(&ctx,final);
/*
* 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++) {
shsInit(&ctx1);
if(i & 1)
shsUpdate(&ctx1,pw,pl);
else
shsUpdate(&ctx1,final,SHS_DIGEST_SIZE);
if(i % 3)
shsUpdate(&ctx1,sp,sl);
if(i % 7)
shsUpdate(&ctx1,pw,pl);
if(i & 1)
shsUpdate(&ctx1,final,SHS_DIGEST_SIZE);
else
shsUpdate(&ctx1,pw,pl);
shsFinal(&ctx1,final);
}
p = (uChar *) passwd + strlen(passwd);
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[16]; to64(p,l,4); p += 4;
l = (final[ 5]<<16) | (final[11]<<8) | final[17]; to64(p,l,4); p += 4;
l = (final[18]<<8) | final[19]; to64(p,l,3); p += 3;
*p = '\0';
/* Don't leave anything around in vm they could use. */
memset(final,0,sizeof final);
return passwd;
}
#undef _crypt_c_