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