1 /* 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <phk (at) login.dknet.dk> wrote this file. As long as you retain this notice you 5 * can do whatever you want with this stuff. If we meet some day, and you think 6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7 * ---------------------------------------------------------------------------- 8 */ 9 10 /* 11 * Ported from FreeBSD to Linux, only minimal changes. --marekm 12 */ 13 14 /* 15 * Adapted from shadow-19990607 by Tudor Bosman, tudorb (at) jm.nu 16 */ 17 18 #include "includes.h" 19 20 RCSID("$Id: md5crypt.c,v 1.5 2001/02/09 01:55:36 djm Exp $"); 21 22 #pragma ident "%Z%%M% %I% %E% SMI" 23 24 #if defined(HAVE_MD5_PASSWORDS) && !defined(HAVE_MD5_CRYPT) 25 26 #include <openssl/md5.h> 27 28 static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ 29 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 30 31 static char *magic = "$1$"; /* 32 * This string is magic for 33 * this algorithm. Having 34 * it this way, we can get 35 * get better later on 36 */ 37 38 static void 39 to64(char *s, unsigned long v, int n) 40 { 41 while (--n >= 0) { 42 *s++ = itoa64[v&0x3f]; 43 v >>= 6; 44 } 45 } 46 47 int 48 is_md5_salt(const char *salt) 49 { 50 return (!strncmp(salt, magic, strlen(magic))); 51 } 52 53 /* 54 * UNIX password 55 * 56 * Use MD5 for what it is best at... 57 */ 58 59 char * 60 md5_crypt(const char *pw, const char *salt) 61 { 62 static char passwd[120], *p; 63 static const char *sp,*ep; 64 unsigned char final[16]; 65 int sl,pl,i,j; 66 MD5_CTX ctx,ctx1; 67 unsigned long l; 68 69 /* Refine the Salt first */ 70 sp = salt; 71 72 /* If it starts with the magic string, then skip that */ 73 if(!strncmp(sp,magic,strlen(magic))) 74 sp += strlen(magic); 75 76 /* It stops at the first '$', max 8 chars */ 77 for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++) 78 continue; 79 80 /* get the length of the true salt */ 81 sl = ep - sp; 82 83 MD5_Init(&ctx); 84 85 /* The password first, since that is what is most unknown */ 86 MD5_Update(&ctx,pw,strlen(pw)); 87 88 /* Then our magic string */ 89 MD5_Update(&ctx,magic,strlen(magic)); 90 91 /* Then the raw salt */ 92 MD5_Update(&ctx,sp,sl); 93 94 /* Then just as many characters of the MD5(pw,salt,pw) */ 95 MD5_Init(&ctx1); 96 MD5_Update(&ctx1,pw,strlen(pw)); 97 MD5_Update(&ctx1,sp,sl); 98 MD5_Update(&ctx1,pw,strlen(pw)); 99 MD5_Final(final,&ctx1); 100 for(pl = strlen(pw); pl > 0; pl -= 16) 101 MD5_Update(&ctx,final,pl>16 ? 16 : pl); 102 103 /* Don't leave anything around in vm they could use. */ 104 memset(final,0,sizeof final); 105 106 /* Then something really weird... */ 107 for (j=0,i = strlen(pw); i ; i >>= 1) 108 if(i&1) 109 MD5_Update(&ctx, final+j, 1); 110 else 111 MD5_Update(&ctx, pw+j, 1); 112 113 /* Now make the output string */ 114 strcpy(passwd,magic); 115 strncat(passwd,sp,sl); 116 strcat(passwd,"$"); 117 118 MD5_Final(final,&ctx); 119 120 /* 121 * and now, just to make sure things don't run too fast 122 * On a 60 Mhz Pentium this takes 34 msec, so you would 123 * need 30 seconds to build a 1000 entry dictionary... 124 */ 125 for(i=0;i<1000;i++) { 126 MD5_Init(&ctx1); 127 if(i & 1) 128 MD5_Update(&ctx1,pw,strlen(pw)); 129 else 130 MD5_Update(&ctx1,final,16); 131 132 if(i % 3) 133 MD5_Update(&ctx1,sp,sl); 134 135 if(i % 7) 136 MD5_Update(&ctx1,pw,strlen(pw)); 137 138 if(i & 1) 139 MD5_Update(&ctx1,final,16); 140 else 141 MD5_Update(&ctx1,pw,strlen(pw)); 142 MD5_Final(final,&ctx1); 143 } 144 145 p = passwd + strlen(passwd); 146 147 l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4; 148 l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4; 149 l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4; 150 l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4; 151 l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4; 152 l = final[11] ; to64(p,l,2); p += 2; 153 *p = '\0'; 154 155 /* Don't leave anything around in vm they could use. */ 156 memset(final,0,sizeof final); 157 158 return passwd; 159 } 160 161 #endif /* defined(HAVE_MD5_PASSWORDS) && !defined(HAVE_MD5_CRYPT) */ 162