Annotation of embedaddon/php/ext/standard/php_crypt_r.c, revision 1.1.1.1
1.1 misho 1: /* $Id: php_crypt_r.c 321634 2012-01-01 13:15:04Z felipe $ */
2: /*
3: +----------------------------------------------------------------------+
4: | PHP Version 5 |
5: +----------------------------------------------------------------------+
6: | Copyright (c) 1997-2012 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 _MSC_VER >= 1500
223: if (strncpy_s(passwd + MD5_MAGIC_LEN, MD5_HASH_MAX_LEN - MD5_MAGIC_LEN, sp, sl + 1) != 0) {
224: goto _destroyCtx1;
225: }
226: passwd[MD5_MAGIC_LEN + sl] = '\0';
227: strcat_s(passwd, MD5_HASH_MAX_LEN, "$");
228: #else
229: /* VC6 version doesn't have strcat_s or strncpy_s */
230: strncpy(passwd + MD5_MAGIC_LEN, sp, sl + 1);
231: strcat(passwd, "$");
232: #endif
233: dwHashLen = 16;
234:
235: /* Fetch the ctx hash value */
236: CryptGetHashParam(ctx, HP_HASHVAL, final, &dwHashLen, 0);
237:
238: for (i = 0; i < 1000; i++) {
239: if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &ctx1)) {
240: goto _destroyCtx1;
241: }
242:
243: if ((i & 1) != 0) {
244: if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) {
245: goto _destroyCtx1;
246: }
247: } else {
248: if(!CryptHashData(ctx1, (BYTE *)final, 16, 0)) {
249: goto _destroyCtx1;
250: }
251: }
252:
253: if ((i % 3) != 0) {
254: if(!CryptHashData(ctx1, (BYTE *)sp, sl, 0)) {
255: goto _destroyCtx1;
256: }
257: }
258:
259: if ((i % 7) != 0) {
260: if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) {
261: goto _destroyCtx1;
262: }
263: }
264:
265: if ((i & 1) != 0) {
266: if(!CryptHashData(ctx1, (BYTE *)final, 16, 0)) {
267: goto _destroyCtx1;
268: }
269: } else {
270: if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) {
271: goto _destroyCtx1;
272: }
273: }
274:
275: /* Fetch the ctx hash value */
276: dwHashLen = 16;
277: CryptGetHashParam(ctx1, HP_HASHVAL, final, &dwHashLen, 0);
278: if(!(CryptDestroyHash(ctx1))) {
279: goto _destroyCtx0;
280: }
281: }
282:
283: ctx1 = (HCRYPTHASH) NULL;
284:
285: p = passwd + sl + MD5_MAGIC_LEN + 1;
286:
287: l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4;
288: l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4;
289: l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4;
290: l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4;
291: l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4;
292: l = final[11]; to64(p,l,2); p += 2;
293:
294: *p = '\0';
295:
296: memset(final, 0, sizeof(final));
297:
298:
299: _destroyCtx1:
300: if (ctx1) {
301: if (!CryptDestroyHash(ctx1)) {
302:
303: }
304: }
305:
306: _destroyCtx0:
307: CryptDestroyHash(ctx);
308:
309: _destroyProv:
310: /* Release the provider handle.*/
311: if(hCryptProv) {
312: if(!(CryptReleaseContext(hCryptProv, 0))) {
313: return NULL;
314: }
315: }
316:
317: return out;
318: }
319: #else
320:
321: /*
322: * MD5 password encryption.
323: */
324: char * php_md5_crypt_r(const char *pw, const char *salt, char *out)
325: {
326: static char passwd[MD5_HASH_MAX_LEN], *p;
327: const char *sp, *ep;
328: unsigned char final[16];
329: unsigned int i, sl, pwl;
330: PHP_MD5_CTX ctx, ctx1;
331: php_uint32 l;
332: int pl;
333:
334: pwl = strlen(pw);
335:
336: /* Refine the salt first */
337: sp = salt;
338:
339: /* If it starts with the magic string, then skip that */
340: if (strncmp(sp, MD5_MAGIC, MD5_MAGIC_LEN) == 0)
341: sp += MD5_MAGIC_LEN;
342:
343: /* It stops at the first '$', max 8 chars */
344: for (ep = sp; *ep != '\0' && *ep != '$' && ep < (sp + 8); ep++)
345: continue;
346:
347: /* get the length of the true salt */
348: sl = ep - sp;
349:
350: PHP_MD5Init(&ctx);
351:
352: /* The password first, since that is what is most unknown */
353: PHP_MD5Update(&ctx, (const unsigned char *)pw, pwl);
354:
355: /* Then our magic string */
356: PHP_MD5Update(&ctx, (const unsigned char *)MD5_MAGIC, MD5_MAGIC_LEN);
357:
358: /* Then the raw salt */
359: PHP_MD5Update(&ctx, (const unsigned char *)sp, sl);
360:
361: /* Then just as many characters of the MD5(pw,salt,pw) */
362: PHP_MD5Init(&ctx1);
363: PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
364: PHP_MD5Update(&ctx1, (const unsigned char *)sp, sl);
365: PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
366: PHP_MD5Final(final, &ctx1);
367:
368: for (pl = pwl; pl > 0; pl -= 16)
369: PHP_MD5Update(&ctx, final, (unsigned int)(pl > 16 ? 16 : pl));
370:
371: /* Don't leave anything around in vm they could use. */
372: memset(final, 0, sizeof(final));
373:
374: /* Then something really weird... */
375: for (i = pwl; i != 0; i >>= 1)
376: if ((i & 1) != 0)
377: PHP_MD5Update(&ctx, final, 1);
378: else
379: PHP_MD5Update(&ctx, (const unsigned char *)pw, 1);
380:
381: /* Now make the output string */
382: memcpy(passwd, MD5_MAGIC, MD5_MAGIC_LEN);
383: strlcpy(passwd + MD5_MAGIC_LEN, sp, sl + 1);
384: strcat(passwd, "$");
385:
386: PHP_MD5Final(final, &ctx);
387:
388: /*
389: * And now, just to make sure things don't run too fast. On a 60 MHz
390: * Pentium this takes 34 msec, so you would need 30 seconds to build
391: * a 1000 entry dictionary...
392: */
393: for (i = 0; i < 1000; i++) {
394: PHP_MD5Init(&ctx1);
395:
396: if ((i & 1) != 0)
397: PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
398: else
399: PHP_MD5Update(&ctx1, final, 16);
400:
401: if ((i % 3) != 0)
402: PHP_MD5Update(&ctx1, (const unsigned char *)sp, sl);
403:
404: if ((i % 7) != 0)
405: PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
406:
407: if ((i & 1) != 0)
408: PHP_MD5Update(&ctx1, final, 16);
409: else
410: PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
411:
412: PHP_MD5Final(final, &ctx1);
413: }
414:
415: p = passwd + sl + MD5_MAGIC_LEN + 1;
416:
417: l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4;
418: l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4;
419: l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4;
420: l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4;
421: l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4;
422: l = final[11] ; to64(p,l,2); p += 2;
423: *p = '\0';
424:
425: /* Don't leave anything around in vm they could use. */
426: memset(final, 0, sizeof(final));
427: return (passwd);
428: }
429:
430: #undef MD5_MAGIC
431: #undef MD5_MAGIC_LEN
432: #endif
433:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>