1 /* 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <phk@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 * $Id$ 10 * 11 */ 12 13 #if defined(LIBC_SCCS) && !defined(lint) 14 static char rcsid[] = "$Header$"; 15 #endif /* LIBC_SCCS and not lint */ 16 17 #include <unistd.h> 18 #include <stdio.h> 19 #include <md5.h> 20 21 static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ 22 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 23 24 static void 25 to64(s, v, n) 26 char *s; 27 unsigned long v; 28 int n; 29 { 30 while (--n >= 0) { 31 *s++ = itoa64[v&0x3f]; 32 v >>= 6; 33 } 34 } 35 36 /* 37 * UNIX password 38 * 39 * Use MD5 for what it is best at... 40 */ 41 42 char * 43 crypt(pw, salt) 44 register const char *pw; 45 register const char *salt; 46 { 47 static char *magic = "$1$"; /* 48 * This string is magic for 49 * this algorithm. Having 50 * it this way, we can get 51 * get better later on 52 */ 53 static char passwd[120], *p; 54 static const char *sp,*ep; 55 unsigned char final[16]; 56 int sl,pl,i,j; 57 MD5_CTX ctx,ctx1; 58 unsigned long l; 59 60 /* Refine the Salt first */ 61 sp = salt; 62 63 /* If it starts with the magic string, then skip that */ 64 if(!strncmp(sp,magic,strlen(magic))) 65 sp += strlen(magic); 66 67 /* It stops at the first '$', max 8 chars */ 68 for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++) 69 continue; 70 71 /* get the length of the true salt */ 72 sl = ep - sp; 73 74 MD5Init(&ctx); 75 76 /* The password first, since that is what is most unknown */ 77 MD5Update(&ctx,pw,strlen(pw)); 78 79 /* Then our magic string */ 80 MD5Update(&ctx,magic,strlen(magic)); 81 82 /* Then the raw salt */ 83 MD5Update(&ctx,sp,sl); 84 85 /* Then just as many characters of the MD5(pw,salt,pw) */ 86 MD5Init(&ctx1); 87 MD5Update(&ctx1,pw,strlen(pw)); 88 MD5Update(&ctx1,sp,sl); 89 MD5Update(&ctx1,pw,strlen(pw)); 90 MD5Final(final,&ctx1); 91 for(pl = strlen(pw); pl > 0; pl -= 16) 92 MD5Update(&ctx,final,pl>16 ? 16 : pl); 93 94 /* Don't leave anything around in vm they could use. */ 95 memset(final,0,sizeof final); 96 97 /* Then something really weird... */ 98 for (j=0,i = strlen(pw); i ; i >>= 1) 99 if(i&1) 100 MD5Update(&ctx, final+j, 1); 101 else 102 MD5Update(&ctx, pw+j, 1); 103 104 /* Now make the output string */ 105 strcpy(passwd,magic); 106 strncat(passwd,sp,sl); 107 strcat(passwd,"$"); 108 109 MD5Final(final,&ctx); 110 111 /* 112 * and now, just to make sure things don't run too fast 113 * On a 60 Mhz Pentium this takes 34 msec, so you would 114 * need 30 seconds to build a 1000 entry dictionary... 115 */ 116 for(i=0;i<1000;i++) { 117 MD5Init(&ctx1); 118 if(i & 1) 119 MD5Update(&ctx1,pw,strlen(pw)); 120 else 121 MD5Update(&ctx1,final,16); 122 123 if(i % 3) 124 MD5Update(&ctx1,sp,sl); 125 126 if(i % 7) 127 MD5Update(&ctx1,pw,strlen(pw)); 128 129 if(i & 1) 130 MD5Update(&ctx1,final,16); 131 else 132 MD5Update(&ctx1,pw,strlen(pw)); 133 MD5Final(final,&ctx1); 134 } 135 136 p = passwd + strlen(passwd); 137 138 l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4; 139 l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4; 140 l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4; 141 l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4; 142 l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4; 143 l = final[11] ; to64(p,l,2); p += 2; 144 *p = '\0'; 145 146 /* Don't leave anything around in vm they could use. */ 147 memset(final,0,sizeof final); 148 149 return passwd; 150 } 151 152