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