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