/src/dovecot/src/lib-auth/password-scheme-md5crypt.c
Line | Count | Source |
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 | | |
10 | | /* |
11 | | * Ported from FreeBSD to Linux, only minimal changes. --marekm |
12 | | */ |
13 | | |
14 | | /* |
15 | | * Adapted from shadow-19990607 by Tudor Bosman, tudorb@jm.nu |
16 | | */ |
17 | | |
18 | | #include "lib.h" |
19 | | #include "safe-memset.h" |
20 | | #include "str.h" |
21 | | #include "md5.h" |
22 | | #include "password-scheme.h" |
23 | | |
24 | | static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ |
25 | | "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; |
26 | | |
27 | | static char magic[] = "$1$"; /* |
28 | | * This string is magic for |
29 | | * this algorithm. Having |
30 | | * it this way, we can get |
31 | | * get better later on |
32 | | */ |
33 | | |
34 | | static void |
35 | | to64(string_t *str, unsigned long v, int n) |
36 | 0 | { |
37 | 0 | while (--n >= 0) { |
38 | 0 | str_append_c(str, itoa64[v&0x3f]); |
39 | 0 | v >>= 6; |
40 | 0 | } |
41 | 0 | } |
42 | | |
43 | | /* |
44 | | * UNIX password |
45 | | * |
46 | | * Use MD5 for what it is best at... |
47 | | */ |
48 | | |
49 | | const char *password_generate_md5_crypt(const char *pw, const char *salt) |
50 | 0 | { |
51 | 0 | const char *sp,*ep; |
52 | 0 | unsigned char final[MD5_RESULTLEN]; |
53 | 0 | int sl,pl,i,j; |
54 | 0 | struct md5_context ctx,ctx1; |
55 | 0 | unsigned long l; |
56 | 0 | string_t *passwd; |
57 | 0 | size_t pw_len = strlen(pw); |
58 | | |
59 | | /* Refine the Salt first */ |
60 | 0 | sp = salt; |
61 | | |
62 | | /* If it starts with the magic string, then skip that */ |
63 | 0 | if (strncmp(sp, magic, sizeof(magic)-1) == 0) |
64 | 0 | sp += sizeof(magic)-1; |
65 | | |
66 | | /* It stops at the first '$', max 8 chars */ |
67 | 0 | for(ep=sp;*ep != '\0' && *ep != '$' && ep < (sp+8);ep++) |
68 | 0 | continue; |
69 | | |
70 | | /* get the length of the true salt */ |
71 | 0 | sl = ep - sp; |
72 | |
|
73 | 0 | md5_init(&ctx); |
74 | | |
75 | | /* The password first, since that is what is most unknown */ |
76 | 0 | md5_update(&ctx,pw,pw_len); |
77 | | |
78 | | /* Then our magic string */ |
79 | 0 | md5_update(&ctx,magic,sizeof(magic)-1); |
80 | | |
81 | | /* Then the raw salt */ |
82 | 0 | md5_update(&ctx,sp,sl); |
83 | | |
84 | | /* Then just as many characters of the MD5(pw,salt,pw) */ |
85 | 0 | md5_init(&ctx1); |
86 | 0 | md5_update(&ctx1,pw,pw_len); |
87 | 0 | md5_update(&ctx1,sp,sl); |
88 | 0 | md5_update(&ctx1,pw,pw_len); |
89 | 0 | md5_final(&ctx1,final); |
90 | 0 | for(pl = pw_len; pl > 0; pl -= MD5_RESULTLEN) |
91 | 0 | md5_update(&ctx,final,pl>MD5_RESULTLEN ? MD5_RESULTLEN : pl); |
92 | | |
93 | | /* Don't leave anything around in vm they could use. */ |
94 | 0 | safe_memset(final, 0, sizeof(final)); |
95 | | |
96 | | /* Then something really weird... */ |
97 | 0 | for (j=0,i = pw_len; i != 0; i >>= 1) |
98 | 0 | if ((i&1) != 0) |
99 | 0 | md5_update(&ctx, final+j, 1); |
100 | 0 | else |
101 | 0 | md5_update(&ctx, pw+j, 1); |
102 | | |
103 | | /* Now make the output string */ |
104 | 0 | passwd = t_str_new(sl + 64); |
105 | 0 | str_append(passwd, magic); |
106 | 0 | str_append_data(passwd, sp, sl); |
107 | 0 | str_append_c(passwd, '$'); |
108 | |
|
109 | 0 | md5_final(&ctx,final); |
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 | 0 | for(i=0;i<1000;i++) { |
117 | 0 | md5_init(&ctx1); |
118 | 0 | if((i & 1) != 0) |
119 | 0 | md5_update(&ctx1,pw,pw_len); |
120 | 0 | else |
121 | 0 | md5_update(&ctx1,final,MD5_RESULTLEN); |
122 | |
|
123 | 0 | if((i % 3) != 0) |
124 | 0 | md5_update(&ctx1,sp,sl); |
125 | |
|
126 | 0 | if((i % 7) != 0) |
127 | 0 | md5_update(&ctx1,pw,pw_len); |
128 | |
|
129 | 0 | if((i & 1) != 0) |
130 | 0 | md5_update(&ctx1,final,MD5_RESULTLEN); |
131 | 0 | else |
132 | 0 | md5_update(&ctx1,pw,pw_len); |
133 | 0 | md5_final(&ctx1,final); |
134 | 0 | } |
135 | |
|
136 | 0 | l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(passwd,l,4); |
137 | 0 | l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(passwd,l,4); |
138 | 0 | l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(passwd,l,4); |
139 | 0 | l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(passwd,l,4); |
140 | 0 | l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(passwd,l,4); |
141 | 0 | l = final[11] ; to64(passwd,l,2); |
142 | | |
143 | | /* Don't leave anything around in vm they could use. */ |
144 | 0 | safe_memset(final, 0, sizeof(final)); |
145 | |
|
146 | 0 | return str_c(passwd); |
147 | 0 | } |