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