Annotation of embedaddon/php/ext/standard/php_crypt_r.c, revision 1.1.1.3
1.1.1.2 misho 1: /* $Id$ */
1.1 misho 2: /*
3: +----------------------------------------------------------------------+
4: | PHP Version 5 |
5: +----------------------------------------------------------------------+
1.1.1.3 ! misho 6: | Copyright (c) 1997-2013 The PHP Group |
1.1 misho 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, "$");
1.1.1.2 misho 227:
1.1 misho 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:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>