1 | /* $Id$ */ |
2 | /* |
3 | +----------------------------------------------------------------------+ |
4 | | PHP Version 5 | |
5 | +----------------------------------------------------------------------+ |
6 | | Copyright (c) 1997-2015 The PHP Group | |
7 | +----------------------------------------------------------------------+ |
8 | | This source file is subject to version 3.01 of the PHP license, | |
9 | | that is bundled with this package in the file LICENSE, and is | |
10 | | available through the world-wide-web at the following url: | |
11 | | http://www.php.net/license/3_01.txt | |
12 | | If you did not receive a copy of the PHP license and are unable to | |
13 | | obtain it through the world-wide-web, please send a note to | |
14 | | license@php.net so we can mail you a copy immediately. | |
15 | +----------------------------------------------------------------------+ |
16 | | Authors: Pierre Alain Joye <pajoye@php.net | |
17 | +----------------------------------------------------------------------+ |
18 | */ |
19 | |
20 | /* |
21 | * License for the Unix md5crypt implementation (md5_crypt): |
22 | * |
23 | * ---------------------------------------------------------------------------- |
24 | * "THE BEER-WARE LICENSE" (Revision 42): |
25 | * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you |
26 | * can do whatever you want with this stuff. If we meet some day, and you think |
27 | * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp |
28 | * ---------------------------------------------------------------------------- |
29 | * |
30 | * from FreeBSD: crypt.c,v 1.5 1996/10/14 08:34:02 phk Exp |
31 | * via OpenBSD: md5crypt.c,v 1.9 1997/07/23 20:58:27 kstailey Exp |
32 | * via NetBSD: md5crypt.c,v 1.4.2.1 2002/01/22 19:31:59 he Exp |
33 | * |
34 | */ |
35 | |
36 | #include "php.h" |
37 | |
38 | #include <string.h> |
39 | |
40 | #if PHP_WIN32 |
41 | # include <windows.h> |
42 | # include <Wincrypt.h> |
43 | #endif |
44 | |
45 | #ifdef HAVE_ATOMIC_H /* Solaris 10 defines atomic API within */ |
46 | # include <atomic.h> |
47 | #else |
48 | # include <signal.h> |
49 | #endif |
50 | #include "php_crypt_r.h" |
51 | #include "crypt_freesec.h" |
52 | |
53 | #if !PHP_WIN32 |
54 | #include "ext/standard/md5.h" |
55 | #endif |
56 | |
57 | #ifdef ZTS |
58 | MUTEX_T php_crypt_extended_init_lock; |
59 | #endif |
60 | |
61 | /* TODO: enable it when enabling vista/2k8 mode in tsrm */ |
62 | #if 0 |
63 | CONDITION_VARIABLE initialized; |
64 | #endif |
65 | |
66 | void php_init_crypt_r() |
67 | { |
68 | #ifdef ZTS |
69 | php_crypt_extended_init_lock = tsrm_mutex_alloc(); |
70 | #endif |
71 | } |
72 | |
73 | void php_shutdown_crypt_r() |
74 | { |
75 | #ifdef ZTS |
76 | tsrm_mutex_free(php_crypt_extended_init_lock); |
77 | #endif |
78 | } |
79 | |
80 | void _crypt_extended_init_r(void) |
81 | { |
82 | #ifdef PHP_WIN32 |
83 | LONG volatile initialized = 0; |
84 | #elif defined(HAVE_ATOMIC_H) /* Solaris 10 defines atomic API within */ |
85 | volatile unsigned int initialized = 0; |
86 | #else |
87 | static volatile sig_atomic_t initialized = 0; |
88 | #endif |
89 | |
90 | #ifdef ZTS |
91 | tsrm_mutex_lock(php_crypt_extended_init_lock); |
92 | #endif |
93 | |
94 | if (!initialized) { |
95 | #ifdef PHP_WIN32 |
96 | InterlockedIncrement(&initialized); |
97 | #elif defined(HAVE_SYNC_FETCH_AND_ADD) |
98 | __sync_fetch_and_add(&initialized, 1); |
99 | #elif defined(HAVE_ATOMIC_H) /* Solaris 10 defines atomic API within */ |
100 | membar_producer(); |
101 | atomic_add_int(&initialized, 1); |
102 | #endif |
103 | _crypt_extended_init(); |
104 | } |
105 | #ifdef ZTS |
106 | tsrm_mutex_unlock(php_crypt_extended_init_lock); |
107 | #endif |
108 | } |
109 | |
110 | /* MD% crypt implementation using the windows CryptoApi */ |
111 | #define MD5_MAGIC "$1$" |
112 | #define MD5_MAGIC_LEN 3 |
113 | |
114 | static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ |
115 | "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ; |
116 | |
117 | static void |
118 | to64(char *s, int32_t v, int n) |
119 | { |
120 | while (--n >= 0) { |
121 | *s++ = itoa64[v & 0x3f]; |
122 | v >>= 6; |
123 | } |
124 | } |
125 | |
126 | #if PHP_WIN32 |
127 | char * php_md5_crypt_r(const char *pw, const char *salt, char *out) { |
128 | HCRYPTPROV hCryptProv; |
129 | HCRYPTHASH ctx, ctx1; |
130 | unsigned int i, pwl, sl; |
131 | const BYTE magic_md5[4] = "$1$" ; |
132 | const DWORD magic_md5_len = 3; |
133 | DWORD dwHashLen; |
134 | int pl; |
135 | __int32 l; |
136 | const char *sp = salt; |
137 | const char *ep = salt; |
138 | char *p = NULL; |
139 | char *passwd = out; |
140 | unsigned char final[16]; |
141 | |
142 | /* Acquire a cryptographic provider context handle. */ |
143 | if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { |
144 | return NULL; |
145 | } |
146 | |
147 | pwl = (unsigned int) strlen(pw); |
148 | |
149 | /* Refine the salt first */ |
150 | sp = salt; |
151 | |
152 | /* If it starts with the magic string, then skip that */ |
153 | if (strncmp(sp, MD5_MAGIC, MD5_MAGIC_LEN) == 0) { |
154 | sp += MD5_MAGIC_LEN; |
155 | } |
156 | |
157 | /* It stops at the first '$', max 8 chars */ |
158 | for (ep = sp; *ep != '\0' && *ep != '$' && ep < (sp + 8); ep++) { |
159 | continue; |
160 | } |
161 | |
162 | /* get the length of the true salt */ |
163 | sl = ep - sp; |
164 | |
165 | /* Create an empty hash object. */ |
166 | if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &ctx)) { |
167 | goto _destroyProv; |
168 | } |
169 | |
170 | /* The password first, since that is what is most unknown */ |
171 | if(!CryptHashData(ctx, (BYTE *)pw, pwl, 0)) { |
172 | goto _destroyCtx0; |
173 | } |
174 | |
175 | /* Then our magic string */ |
176 | if(!CryptHashData(ctx, magic_md5, magic_md5_len, 0)) { |
177 | goto _destroyCtx0; |
178 | } |
179 | |
180 | /* Then the raw salt */ |
181 | if(!CryptHashData( ctx, (BYTE *)sp, sl, 0)) { |
182 | goto _destroyCtx0; |
183 | } |
184 | |
185 | /* MD5(pw,salt,pw), valid. */ |
186 | /* Then just as many characters of the MD5(pw,salt,pw) */ |
187 | if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &ctx1)) { |
188 | goto _destroyCtx0; |
189 | } |
190 | if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) { |
191 | goto _destroyCtx1; |
192 | } |
193 | if(!CryptHashData(ctx1, (BYTE *)sp, sl, 0)) { |
194 | goto _destroyCtx1; |
195 | } |
196 | if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) { |
197 | goto _destroyCtx1; |
198 | } |
199 | |
200 | dwHashLen = 16; |
201 | CryptGetHashParam(ctx1, HP_HASHVAL, final, &dwHashLen, 0); |
202 | /* MD5(pw,salt,pw). Valid. */ |
203 | |
204 | for (pl = pwl; pl > 0; pl -= 16) { |
205 | CryptHashData(ctx, final, (DWORD)(pl > 16 ? 16 : pl), 0); |
206 | } |
207 | |
208 | /* Don't leave anything around in vm they could use. */ |
209 | memset(final, 0, sizeof(final)); |
210 | |
211 | /* Then something really weird... */ |
212 | for (i = pwl; i != 0; i >>= 1) { |
213 | if ((i & 1) != 0) { |
214 | CryptHashData(ctx, (const BYTE *)final, 1, 0); |
215 | } else { |
216 | CryptHashData(ctx, (const BYTE *)pw, 1, 0); |
217 | } |
218 | } |
219 | |
220 | memcpy(passwd, MD5_MAGIC, MD5_MAGIC_LEN); |
221 | |
222 | if (strncpy_s(passwd + MD5_MAGIC_LEN, MD5_HASH_MAX_LEN - MD5_MAGIC_LEN, sp, sl + 1) != 0) { |
223 | goto _destroyCtx1; |
224 | } |
225 | passwd[MD5_MAGIC_LEN + sl] = '\0'; |
226 | strcat_s(passwd, MD5_HASH_MAX_LEN, "$" ); |
227 | |
228 | dwHashLen = 16; |
229 | |
230 | /* Fetch the ctx hash value */ |
231 | CryptGetHashParam(ctx, HP_HASHVAL, final, &dwHashLen, 0); |
232 | |
233 | for (i = 0; i < 1000; i++) { |
234 | if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &ctx1)) { |
235 | goto _destroyCtx1; |
236 | } |
237 | |
238 | if ((i & 1) != 0) { |
239 | if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) { |
240 | goto _destroyCtx1; |
241 | } |
242 | } else { |
243 | if(!CryptHashData(ctx1, (BYTE *)final, 16, 0)) { |
244 | goto _destroyCtx1; |
245 | } |
246 | } |
247 | |
248 | if ((i % 3) != 0) { |
249 | if(!CryptHashData(ctx1, (BYTE *)sp, sl, 0)) { |
250 | goto _destroyCtx1; |
251 | } |
252 | } |
253 | |
254 | if ((i % 7) != 0) { |
255 | if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) { |
256 | goto _destroyCtx1; |
257 | } |
258 | } |
259 | |
260 | if ((i & 1) != 0) { |
261 | if(!CryptHashData(ctx1, (BYTE *)final, 16, 0)) { |
262 | goto _destroyCtx1; |
263 | } |
264 | } else { |
265 | if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) { |
266 | goto _destroyCtx1; |
267 | } |
268 | } |
269 | |
270 | /* Fetch the ctx hash value */ |
271 | dwHashLen = 16; |
272 | CryptGetHashParam(ctx1, HP_HASHVAL, final, &dwHashLen, 0); |
273 | if(!(CryptDestroyHash(ctx1))) { |
274 | goto _destroyCtx0; |
275 | } |
276 | } |
277 | |
278 | ctx1 = (HCRYPTHASH) NULL; |
279 | |
280 | p = passwd + sl + MD5_MAGIC_LEN + 1; |
281 | |
282 | l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4; |
283 | l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4; |
284 | l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4; |
285 | l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4; |
286 | l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4; |
287 | l = final[11]; to64(p,l,2); p += 2; |
288 | |
289 | *p = '\0'; |
290 | |
291 | memset(final, 0, sizeof(final)); |
292 | |
293 | |
294 | _destroyCtx1: |
295 | if (ctx1) { |
296 | if (!CryptDestroyHash(ctx1)) { |
297 | |
298 | } |
299 | } |
300 | |
301 | _destroyCtx0: |
302 | CryptDestroyHash(ctx); |
303 | |
304 | _destroyProv: |
305 | /* Release the provider handle.*/ |
306 | if(hCryptProv) { |
307 | if(!(CryptReleaseContext(hCryptProv, 0))) { |
308 | return NULL; |
309 | } |
310 | } |
311 | |
312 | return out; |
313 | } |
314 | #else |
315 | |
316 | /* |
317 | * MD5 password encryption. |
318 | */ |
319 | char * php_md5_crypt_r(const char *pw, const char *salt, char *out) |
320 | { |
321 | static char passwd[MD5_HASH_MAX_LEN], *p; |
322 | const char *sp, *ep; |
323 | unsigned char final[16]; |
324 | unsigned int i, sl, pwl; |
325 | PHP_MD5_CTX ctx, ctx1; |
326 | php_uint32 l; |
327 | int pl; |
328 | |
329 | pwl = strlen(pw); |
330 | |
331 | /* Refine the salt first */ |
332 | sp = salt; |
333 | |
334 | /* If it starts with the magic string, then skip that */ |
335 | if (strncmp(sp, MD5_MAGIC, MD5_MAGIC_LEN) == 0) |
336 | sp += MD5_MAGIC_LEN; |
337 | |
338 | /* It stops at the first '$', max 8 chars */ |
339 | for (ep = sp; *ep != '\0' && *ep != '$' && ep < (sp + 8); ep++) |
340 | continue; |
341 | |
342 | /* get the length of the true salt */ |
343 | sl = ep - sp; |
344 | |
345 | PHP_MD5Init(&ctx); |
346 | |
347 | /* The password first, since that is what is most unknown */ |
348 | PHP_MD5Update(&ctx, (const unsigned char *)pw, pwl); |
349 | |
350 | /* Then our magic string */ |
351 | PHP_MD5Update(&ctx, (const unsigned char *)MD5_MAGIC, MD5_MAGIC_LEN); |
352 | |
353 | /* Then the raw salt */ |
354 | PHP_MD5Update(&ctx, (const unsigned char *)sp, sl); |
355 | |
356 | /* Then just as many characters of the MD5(pw,salt,pw) */ |
357 | PHP_MD5Init(&ctx1); |
358 | PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl); |
359 | PHP_MD5Update(&ctx1, (const unsigned char *)sp, sl); |
360 | PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl); |
361 | PHP_MD5Final(final, &ctx1); |
362 | |
363 | for (pl = pwl; pl > 0; pl -= 16) |
364 | PHP_MD5Update(&ctx, final, (unsigned int)(pl > 16 ? 16 : pl)); |
365 | |
366 | /* Don't leave anything around in vm they could use. */ |
367 | memset(final, 0, sizeof(final)); |
368 | |
369 | /* Then something really weird... */ |
370 | for (i = pwl; i != 0; i >>= 1) |
371 | if ((i & 1) != 0) |
372 | PHP_MD5Update(&ctx, final, 1); |
373 | else |
374 | PHP_MD5Update(&ctx, (const unsigned char *)pw, 1); |
375 | |
376 | /* Now make the output string */ |
377 | memcpy(passwd, MD5_MAGIC, MD5_MAGIC_LEN); |
378 | strlcpy(passwd + MD5_MAGIC_LEN, sp, sl + 1); |
379 | strcat(passwd, "$" ); |
380 | |
381 | PHP_MD5Final(final, &ctx); |
382 | |
383 | /* |
384 | * And now, just to make sure things don't run too fast. On a 60 MHz |
385 | * Pentium this takes 34 msec, so you would need 30 seconds to build |
386 | * a 1000 entry dictionary... |
387 | */ |
388 | for (i = 0; i < 1000; i++) { |
389 | PHP_MD5Init(&ctx1); |
390 | |
391 | if ((i & 1) != 0) |
392 | PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl); |
393 | else |
394 | PHP_MD5Update(&ctx1, final, 16); |
395 | |
396 | if ((i % 3) != 0) |
397 | PHP_MD5Update(&ctx1, (const unsigned char *)sp, sl); |
398 | |
399 | if ((i % 7) != 0) |
400 | PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl); |
401 | |
402 | if ((i & 1) != 0) |
403 | PHP_MD5Update(&ctx1, final, 16); |
404 | else |
405 | PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl); |
406 | |
407 | PHP_MD5Final(final, &ctx1); |
408 | } |
409 | |
410 | p = passwd + sl + MD5_MAGIC_LEN + 1; |
411 | |
412 | l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4; |
413 | l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4; |
414 | l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4; |
415 | l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4; |
416 | l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4; |
417 | l = final[11] ; to64(p,l,2); p += 2; |
418 | *p = '\0'; |
419 | |
420 | /* Don't leave anything around in vm they could use. */ |
421 | memset(final, 0, sizeof(final)); |
422 | return (passwd); |
423 | } |
424 | |
425 | #undef MD5_MAGIC |
426 | #undef MD5_MAGIC_LEN |
427 | #endif |
428 | |
429 | |