1 | /* |
2 | +----------------------------------------------------------------------+ |
3 | | PHP Version 5 | |
4 | +----------------------------------------------------------------------+ |
5 | | Copyright (c) 1997-2015 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: Stig Bakken <ssb@php.net> | |
16 | | Zeev Suraski <zeev@zend.com> | |
17 | | Rasmus Lerdorf <rasmus@php.net> | |
18 | | Pierre Joye <pierre@php.net> | |
19 | +----------------------------------------------------------------------+ |
20 | */ |
21 | |
22 | /* $Id$ */ |
23 | |
24 | #include <stdlib.h> |
25 | |
26 | #include "php.h" |
27 | #if HAVE_CRYPT |
28 | |
29 | #if HAVE_UNISTD_H |
30 | #include <unistd.h> |
31 | #endif |
32 | #if PHP_USE_PHP_CRYPT_R |
33 | # include "php_crypt_r.h" |
34 | # include "crypt_freesec.h" |
35 | #else |
36 | # if HAVE_CRYPT_H |
37 | # if defined(CRYPT_R_GNU_SOURCE) && !defined(_GNU_SOURCE) |
38 | # define _GNU_SOURCE |
39 | # endif |
40 | # include <crypt.h> |
41 | # endif |
42 | #endif |
43 | #if TM_IN_SYS_TIME |
44 | #include <sys/time.h> |
45 | #else |
46 | #include <time.h> |
47 | #endif |
48 | #if HAVE_STRING_H |
49 | #include <string.h> |
50 | #else |
51 | #include <strings.h> |
52 | #endif |
53 | |
54 | #ifdef PHP_WIN32 |
55 | #include <process.h> |
56 | #endif |
57 | |
58 | #include "php_lcg.h" |
59 | #include "php_crypt.h" |
60 | #include "php_rand.h" |
61 | |
62 | /* The capabilities of the crypt() function is determined by the test programs |
63 | * run by configure from aclocal.m4. They will set PHP_STD_DES_CRYPT, |
64 | * PHP_EXT_DES_CRYPT, PHP_MD5_CRYPT and PHP_BLOWFISH_CRYPT as appropriate |
65 | * for the target platform. */ |
66 | |
67 | #if PHP_STD_DES_CRYPT |
68 | #define PHP_MAX_SALT_LEN 2 |
69 | #endif |
70 | |
71 | #if PHP_EXT_DES_CRYPT |
72 | #undef PHP_MAX_SALT_LEN |
73 | #define PHP_MAX_SALT_LEN 9 |
74 | #endif |
75 | |
76 | #if PHP_MD5_CRYPT |
77 | #undef PHP_MAX_SALT_LEN |
78 | #define PHP_MAX_SALT_LEN 12 |
79 | #endif |
80 | |
81 | #if PHP_BLOWFISH_CRYPT |
82 | #undef PHP_MAX_SALT_LEN |
83 | #define PHP_MAX_SALT_LEN 60 |
84 | #endif |
85 | |
86 | #if PHP_SHA512_CRYPT |
87 | #undef PHP_MAX_SALT_LEN |
88 | #define PHP_MAX_SALT_LEN 123 |
89 | #endif |
90 | |
91 | |
92 | /* If the configure-time checks fail, we provide DES. |
93 | * XXX: This is a hack. Fix the real problem! */ |
94 | |
95 | #ifndef PHP_MAX_SALT_LEN |
96 | #define PHP_MAX_SALT_LEN 2 |
97 | #undef PHP_STD_DES_CRYPT |
98 | #define PHP_STD_DES_CRYPT 1 |
99 | #endif |
100 | |
101 | #define PHP_CRYPT_RAND php_rand(TSRMLS_C) |
102 | |
103 | PHP_MINIT_FUNCTION(crypt) /* {{{ */ |
104 | { |
105 | REGISTER_LONG_CONSTANT("CRYPT_SALT_LENGTH" , PHP_MAX_SALT_LEN, CONST_CS | CONST_PERSISTENT); |
106 | REGISTER_LONG_CONSTANT("CRYPT_STD_DES" , PHP_STD_DES_CRYPT, CONST_CS | CONST_PERSISTENT); |
107 | REGISTER_LONG_CONSTANT("CRYPT_EXT_DES" , PHP_EXT_DES_CRYPT, CONST_CS | CONST_PERSISTENT); |
108 | REGISTER_LONG_CONSTANT("CRYPT_MD5" , PHP_MD5_CRYPT, CONST_CS | CONST_PERSISTENT); |
109 | REGISTER_LONG_CONSTANT("CRYPT_BLOWFISH" , PHP_BLOWFISH_CRYPT, CONST_CS | CONST_PERSISTENT); |
110 | |
111 | #ifdef PHP_SHA256_CRYPT |
112 | REGISTER_LONG_CONSTANT("CRYPT_SHA256" , PHP_SHA256_CRYPT, CONST_CS | CONST_PERSISTENT); |
113 | #endif |
114 | |
115 | #ifdef PHP_SHA512_CRYPT |
116 | REGISTER_LONG_CONSTANT("CRYPT_SHA512" , PHP_SHA512_CRYPT, CONST_CS | CONST_PERSISTENT); |
117 | #endif |
118 | |
119 | #if PHP_USE_PHP_CRYPT_R |
120 | php_init_crypt_r(); |
121 | #endif |
122 | |
123 | return SUCCESS; |
124 | } |
125 | /* }}} */ |
126 | |
127 | PHP_MSHUTDOWN_FUNCTION(crypt) /* {{{ */ |
128 | { |
129 | #if PHP_USE_PHP_CRYPT_R |
130 | php_shutdown_crypt_r(); |
131 | #endif |
132 | |
133 | return SUCCESS; |
134 | } |
135 | /* }}} */ |
136 | |
137 | static unsigned char itoa64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ; |
138 | |
139 | static void php_to64(char *s, long v, int n) /* {{{ */ |
140 | { |
141 | while (--n >= 0) { |
142 | *s++ = itoa64[v&0x3f]; |
143 | v >>= 6; |
144 | } |
145 | } |
146 | /* }}} */ |
147 | |
148 | PHPAPI int php_crypt(const char *password, const int pass_len, const char *salt, int salt_len, char **result) |
149 | { |
150 | char *crypt_res; |
151 | /* Windows (win32/crypt) has a stripped down version of libxcrypt and |
152 | a CryptoApi md5_crypt implementation */ |
153 | #if PHP_USE_PHP_CRYPT_R |
154 | { |
155 | struct php_crypt_extended_data buffer; |
156 | |
157 | if (salt[0]=='$' && salt[1]=='1' && salt[2]=='$') { |
158 | char output[MD5_HASH_MAX_LEN], *out; |
159 | |
160 | out = php_md5_crypt_r(password, salt, output); |
161 | if (out) { |
162 | *result = estrdup(out); |
163 | return SUCCESS; |
164 | } |
165 | return FAILURE; |
166 | } else if (salt[0]=='$' && salt[1]=='6' && salt[2]=='$') { |
167 | char *output; |
168 | output = emalloc(PHP_MAX_SALT_LEN); |
169 | |
170 | crypt_res = php_sha512_crypt_r(password, salt, output, PHP_MAX_SALT_LEN); |
171 | if (!crypt_res) { |
172 | memset(output, 0, PHP_MAX_SALT_LEN); |
173 | efree(output); |
174 | return FAILURE; |
175 | } else { |
176 | *result = estrdup(output); |
177 | memset(output, 0, PHP_MAX_SALT_LEN); |
178 | efree(output); |
179 | return SUCCESS; |
180 | } |
181 | } else if (salt[0]=='$' && salt[1]=='5' && salt[2]=='$') { |
182 | char *output; |
183 | output = emalloc(PHP_MAX_SALT_LEN); |
184 | |
185 | crypt_res = php_sha256_crypt_r(password, salt, output, PHP_MAX_SALT_LEN); |
186 | if (!crypt_res) { |
187 | memset(output, 0, PHP_MAX_SALT_LEN); |
188 | efree(output); |
189 | return FAILURE; |
190 | } else { |
191 | *result = estrdup(output); |
192 | memset(output, 0, PHP_MAX_SALT_LEN); |
193 | efree(output); |
194 | return SUCCESS; |
195 | } |
196 | } else if ( |
197 | salt[0] == '$' && |
198 | salt[1] == '2' && |
199 | salt[3] == '$' && |
200 | salt[4] >= '0' && salt[4] <= '3' && |
201 | salt[5] >= '0' && salt[5] <= '9' && |
202 | salt[6] == '$') { |
203 | char output[PHP_MAX_SALT_LEN + 1]; |
204 | |
205 | memset(output, 0, PHP_MAX_SALT_LEN + 1); |
206 | |
207 | crypt_res = php_crypt_blowfish_rn(password, salt, output, sizeof(output)); |
208 | if (!crypt_res) { |
209 | memset(output, 0, PHP_MAX_SALT_LEN + 1); |
210 | return FAILURE; |
211 | } else { |
212 | *result = estrdup(output); |
213 | memset(output, 0, PHP_MAX_SALT_LEN + 1); |
214 | return SUCCESS; |
215 | } |
216 | } else { |
217 | memset(&buffer, 0, sizeof(buffer)); |
218 | _crypt_extended_init_r(); |
219 | |
220 | crypt_res = _crypt_extended_r(password, salt, &buffer); |
221 | if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) { |
222 | return FAILURE; |
223 | } else { |
224 | *result = estrdup(crypt_res); |
225 | return SUCCESS; |
226 | } |
227 | } |
228 | } |
229 | #else |
230 | |
231 | # if defined(HAVE_CRYPT_R) && (defined(_REENTRANT) || defined(_THREAD_SAFE)) |
232 | { |
233 | # if defined(CRYPT_R_STRUCT_CRYPT_DATA) |
234 | struct crypt_data buffer; |
235 | memset(&buffer, 0, sizeof(buffer)); |
236 | # elif defined(CRYPT_R_CRYPTD) |
237 | CRYPTD buffer; |
238 | # else |
239 | # error Data struct used by crypt_r() is unknown. Please report. |
240 | # endif |
241 | crypt_res = crypt_r(password, salt, &buffer); |
242 | if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) { |
243 | return FAILURE; |
244 | } else { |
245 | *result = estrdup(crypt_res); |
246 | return SUCCESS; |
247 | } |
248 | } |
249 | # endif |
250 | #endif |
251 | } |
252 | /* }}} */ |
253 | |
254 | |
255 | /* {{{ proto string crypt(string str [, string salt]) |
256 | Hash a string */ |
257 | PHP_FUNCTION(crypt) |
258 | { |
259 | char salt[PHP_MAX_SALT_LEN + 1]; |
260 | char *str, *salt_in = NULL, *result = NULL; |
261 | int str_len, salt_in_len = 0; |
262 | salt[0] = salt[PHP_MAX_SALT_LEN] = '\0'; |
263 | |
264 | /* This will produce suitable results if people depend on DES-encryption |
265 | * available (passing always 2-character salt). At least for glibc6.1 */ |
266 | memset(&salt[1], '$', PHP_MAX_SALT_LEN - 1); |
267 | |
268 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s" , &str, &str_len, &salt_in, &salt_in_len) == FAILURE) { |
269 | return; |
270 | } |
271 | |
272 | if (salt_in) { |
273 | memcpy(salt, salt_in, MIN(PHP_MAX_SALT_LEN, salt_in_len)); |
274 | } else { |
275 | php_error_docref(NULL TSRMLS_CC, E_NOTICE, "No salt parameter was specified. You must use a randomly generated salt and a strong hash function to produce a secure hash." ); |
276 | } |
277 | |
278 | /* The automatic salt generation covers standard DES, md5-crypt and Blowfish (simple) */ |
279 | if (!*salt) { |
280 | #if PHP_MD5_CRYPT |
281 | strncpy(salt, "$1$" , PHP_MAX_SALT_LEN); |
282 | php_to64(&salt[3], PHP_CRYPT_RAND, 4); |
283 | php_to64(&salt[7], PHP_CRYPT_RAND, 4); |
284 | strncpy(&salt[11], "$" , PHP_MAX_SALT_LEN - 11); |
285 | #elif PHP_STD_DES_CRYPT |
286 | php_to64(&salt[0], PHP_CRYPT_RAND, 2); |
287 | salt[2] = '\0'; |
288 | #endif |
289 | salt_in_len = strlen(salt); |
290 | } else { |
291 | salt_in_len = MIN(PHP_MAX_SALT_LEN, salt_in_len); |
292 | } |
293 | salt[salt_in_len] = '\0'; |
294 | |
295 | if (php_crypt(str, str_len, salt, salt_in_len, &result) == FAILURE) { |
296 | if (salt[0] == '*' && salt[1] == '0') { |
297 | RETURN_STRING("*1" , 1); |
298 | } else { |
299 | RETURN_STRING("*0" , 1); |
300 | } |
301 | } |
302 | RETURN_STRING(result, 0); |
303 | } |
304 | /* }}} */ |
305 | #endif |
306 | |
307 | /* |
308 | * Local variables: |
309 | * tab-width: 4 |
310 | * c-basic-offset: 4 |
311 | * End: |
312 | * vim600: sw=4 ts=4 fdm=marker |
313 | * vim<600: sw=4 ts=4 |
314 | */ |
315 | |