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 |