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