xref: /PHP-5.5/ext/standard/php_crypt_r.c (revision 73c1be26)
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 
php_init_crypt_r()66 void php_init_crypt_r()
67 {
68 #ifdef ZTS
69 	php_crypt_extended_init_lock = tsrm_mutex_alloc();
70 #endif
71 }
72 
php_shutdown_crypt_r()73 void php_shutdown_crypt_r()
74 {
75 #ifdef ZTS
76 	tsrm_mutex_free(php_crypt_extended_init_lock);
77 #endif
78 }
79 
_crypt_extended_init_r(void)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
to64(char * s,int32_t v,int n)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
php_md5_crypt_r(const char * pw,const char * salt,char * out)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  */
php_md5_crypt_r(const char * pw,const char * salt,char * out)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