/src/php-src/ext/standard/php_crypt_r.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Copyright © The PHP Group and Contributors. | |
4 | | +----------------------------------------------------------------------+ |
5 | | | This source file is subject to the Modified BSD License that is | |
6 | | | bundled with this package in the file LICENSE, and is available | |
7 | | | through the World Wide Web at <https://www.php.net/license/>. | |
8 | | | | |
9 | | | SPDX-License-Identifier: BSD-3-Clause | |
10 | | +----------------------------------------------------------------------+ |
11 | | | Authors: Pierre Alain Joye <pajoye@php.net | |
12 | | +----------------------------------------------------------------------+ |
13 | | */ |
14 | | |
15 | | /* |
16 | | * License for the Unix md5crypt implementation (md5_crypt): |
17 | | * |
18 | | * ---------------------------------------------------------------------------- |
19 | | * "THE BEER-WARE LICENSE" (Revision 42): |
20 | | * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you |
21 | | * can do whatever you want with this stuff. If we meet some day, and you think |
22 | | * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp |
23 | | * ---------------------------------------------------------------------------- |
24 | | * |
25 | | * from FreeBSD: crypt.c,v 1.5 1996/10/14 08:34:02 phk Exp |
26 | | * via OpenBSD: md5crypt.c,v 1.9 1997/07/23 20:58:27 kstailey Exp |
27 | | * via NetBSD: md5crypt.c,v 1.4.2.1 2002/01/22 19:31:59 he Exp |
28 | | * |
29 | | */ |
30 | | |
31 | | #include "php.h" |
32 | | |
33 | | #include <string.h> |
34 | | |
35 | | #ifdef PHP_WIN32 |
36 | | # include <windows.h> |
37 | | # include <Wincrypt.h> |
38 | | #endif |
39 | | |
40 | | #include "php_crypt_r.h" |
41 | | #include "crypt_freesec.h" |
42 | | #include "ext/standard/md5.h" |
43 | | |
44 | | #ifdef ZTS |
45 | | MUTEX_T php_crypt_extended_init_lock; |
46 | | #endif |
47 | | |
48 | | void php_init_crypt_r(void) |
49 | 2 | { |
50 | | #ifdef ZTS |
51 | | php_crypt_extended_init_lock = tsrm_mutex_alloc(); |
52 | | #endif |
53 | 2 | } |
54 | | |
55 | | void php_shutdown_crypt_r(void) |
56 | 0 | { |
57 | | #ifdef ZTS |
58 | | tsrm_mutex_free(php_crypt_extended_init_lock); |
59 | | #endif |
60 | 0 | } |
61 | | |
62 | | void _crypt_extended_init_r(void) |
63 | 0 | { |
64 | 0 | static int initialized = 0; |
65 | |
|
66 | | #ifdef ZTS |
67 | | tsrm_mutex_lock(php_crypt_extended_init_lock); |
68 | | #endif |
69 | |
|
70 | 0 | if (!initialized) { |
71 | 0 | initialized = 1; |
72 | 0 | _crypt_extended_init(); |
73 | 0 | } |
74 | |
|
75 | | #ifdef ZTS |
76 | | tsrm_mutex_unlock(php_crypt_extended_init_lock); |
77 | | #endif |
78 | 0 | } |
79 | | |
80 | | /* MD5 crypt implementation using the windows CryptoApi */ |
81 | 0 | #define MD5_MAGIC "$1$" |
82 | 0 | #define MD5_MAGIC_LEN 3 |
83 | | |
84 | | static const unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ |
85 | | "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; |
86 | | |
87 | | /* Convert a 16/32 bit integer to Base64 string representation */ |
88 | | static void to64(char *s, int32_t v, int n) |
89 | 0 | { |
90 | 0 | while (--n >= 0) { |
91 | 0 | *s++ = itoa64[v & 0x3f]; |
92 | 0 | v >>= 6; |
93 | 0 | } |
94 | 0 | } |
95 | | |
96 | | /* |
97 | | * MD5 password encryption. |
98 | | */ |
99 | | char * php_md5_crypt_r(const char *pw, const char *salt, char *out) |
100 | 0 | { |
101 | 0 | ZEND_TLS char passwd[MD5_HASH_MAX_LEN], *p; |
102 | 0 | const char *sp, *ep; |
103 | 0 | unsigned char final[16]; |
104 | 0 | unsigned int i, sl, pwl; |
105 | 0 | PHP_MD5_CTX ctx, ctx1; |
106 | 0 | uint32_t l; |
107 | 0 | int pl; |
108 | |
|
109 | 0 | pwl = strlen(pw); |
110 | | |
111 | | /* Refine the salt first */ |
112 | 0 | sp = salt; |
113 | | |
114 | | /* If it starts with the magic string, then skip that */ |
115 | 0 | if (strncmp(sp, MD5_MAGIC, MD5_MAGIC_LEN) == 0) |
116 | 0 | sp += MD5_MAGIC_LEN; |
117 | | |
118 | | /* It stops at the first '$', max 8 chars */ |
119 | 0 | for (ep = sp; *ep != '\0' && *ep != '$' && ep < (sp + 8); ep++); |
120 | | |
121 | | /* get the length of the true salt */ |
122 | 0 | sl = ep - sp; |
123 | |
|
124 | 0 | PHP_MD5Init(&ctx); |
125 | | |
126 | | /* The password first, since that is what is most unknown */ |
127 | 0 | PHP_MD5Update(&ctx, (const unsigned char *)pw, pwl); |
128 | | |
129 | | /* Then our magic string */ |
130 | 0 | PHP_MD5Update(&ctx, (const unsigned char *)MD5_MAGIC, MD5_MAGIC_LEN); |
131 | | |
132 | | /* Then the raw salt */ |
133 | 0 | PHP_MD5Update(&ctx, (const unsigned char *)sp, sl); |
134 | | |
135 | | /* Then just as many characters of the MD5(pw,salt,pw) */ |
136 | 0 | PHP_MD5Init(&ctx1); |
137 | 0 | PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl); |
138 | 0 | PHP_MD5Update(&ctx1, (const unsigned char *)sp, sl); |
139 | 0 | PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl); |
140 | 0 | PHP_MD5Final(final, &ctx1); |
141 | |
|
142 | 0 | for (pl = pwl; pl > 0; pl -= 16) |
143 | 0 | PHP_MD5Update(&ctx, final, (unsigned int)(pl > 16 ? 16 : pl)); |
144 | | |
145 | | /* Don't leave anything around in vm they could use. */ |
146 | 0 | ZEND_SECURE_ZERO(final, sizeof(final)); |
147 | | |
148 | | /* Then something really weird... */ |
149 | 0 | for (i = pwl; i != 0; i >>= 1) |
150 | 0 | if ((i & 1) != 0) |
151 | 0 | PHP_MD5Update(&ctx, final, 1); |
152 | 0 | else |
153 | 0 | PHP_MD5Update(&ctx, (const unsigned char *)pw, 1); |
154 | | |
155 | | /* Now make the output string */ |
156 | 0 | memcpy(passwd, MD5_MAGIC, MD5_MAGIC_LEN); |
157 | 0 | strlcpy(passwd + MD5_MAGIC_LEN, sp, sl + 1); |
158 | 0 | strcat(passwd, "$"); |
159 | |
|
160 | 0 | PHP_MD5Final(final, &ctx); |
161 | | |
162 | | /* |
163 | | * And now, just to make sure things don't run too fast. On a 60 MHz |
164 | | * Pentium this takes 34 msec, so you would need 30 seconds to build |
165 | | * a 1000 entry dictionary... |
166 | | */ |
167 | 0 | for (i = 0; i < 1000; i++) { |
168 | 0 | PHP_MD5Init(&ctx1); |
169 | |
|
170 | 0 | if ((i & 1) != 0) |
171 | 0 | PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl); |
172 | 0 | else |
173 | 0 | PHP_MD5Update(&ctx1, final, 16); |
174 | |
|
175 | 0 | if ((i % 3) != 0) |
176 | 0 | PHP_MD5Update(&ctx1, (const unsigned char *)sp, sl); |
177 | |
|
178 | 0 | if ((i % 7) != 0) |
179 | 0 | PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl); |
180 | |
|
181 | 0 | if ((i & 1) != 0) |
182 | 0 | PHP_MD5Update(&ctx1, final, 16); |
183 | 0 | else |
184 | 0 | PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl); |
185 | |
|
186 | 0 | PHP_MD5Final(final, &ctx1); |
187 | 0 | } |
188 | |
|
189 | 0 | p = passwd + sl + MD5_MAGIC_LEN + 1; |
190 | |
|
191 | 0 | l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4; |
192 | 0 | l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4; |
193 | 0 | l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4; |
194 | 0 | l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4; |
195 | 0 | l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4; |
196 | 0 | l = final[11] ; to64(p,l,2); p += 2; |
197 | 0 | *p = '\0'; |
198 | | |
199 | | /* Don't leave anything around in vm they could use. */ |
200 | 0 | ZEND_SECURE_ZERO(final, sizeof(final)); |
201 | 0 | return (passwd); |
202 | 0 | } |