1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2017 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()
102
103 /* Used to check DES salts to ensure that they contain only valid characters */
104 #define IS_VALID_SALT_CHARACTER(c) (((c) >= '.' && (c) <= '9') || ((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
105
106 #define DES_INVALID_SALT_ERROR "Supplied salt is not valid for DES. Possible bug in provided salt format."
107
108
PHP_MINIT_FUNCTION(crypt)109 PHP_MINIT_FUNCTION(crypt) /* {{{ */
110 {
111 REGISTER_LONG_CONSTANT("CRYPT_SALT_LENGTH", PHP_MAX_SALT_LEN, CONST_CS | CONST_PERSISTENT);
112 REGISTER_LONG_CONSTANT("CRYPT_STD_DES", PHP_STD_DES_CRYPT, CONST_CS | CONST_PERSISTENT);
113 REGISTER_LONG_CONSTANT("CRYPT_EXT_DES", PHP_EXT_DES_CRYPT, CONST_CS | CONST_PERSISTENT);
114 REGISTER_LONG_CONSTANT("CRYPT_MD5", PHP_MD5_CRYPT, CONST_CS | CONST_PERSISTENT);
115 REGISTER_LONG_CONSTANT("CRYPT_BLOWFISH", PHP_BLOWFISH_CRYPT, CONST_CS | CONST_PERSISTENT);
116
117 #ifdef PHP_SHA256_CRYPT
118 REGISTER_LONG_CONSTANT("CRYPT_SHA256", PHP_SHA256_CRYPT, CONST_CS | CONST_PERSISTENT);
119 #endif
120
121 #ifdef PHP_SHA512_CRYPT
122 REGISTER_LONG_CONSTANT("CRYPT_SHA512", PHP_SHA512_CRYPT, CONST_CS | CONST_PERSISTENT);
123 #endif
124
125 #if PHP_USE_PHP_CRYPT_R
126 php_init_crypt_r();
127 #endif
128
129 return SUCCESS;
130 }
131 /* }}} */
132
PHP_MSHUTDOWN_FUNCTION(crypt)133 PHP_MSHUTDOWN_FUNCTION(crypt) /* {{{ */
134 {
135 #if PHP_USE_PHP_CRYPT_R
136 php_shutdown_crypt_r();
137 #endif
138
139 return SUCCESS;
140 }
141 /* }}} */
142
143 static unsigned char itoa64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
144
php_to64(char * s,zend_long v,int n)145 static void php_to64(char *s, zend_long v, int n) /* {{{ */
146 {
147 while (--n >= 0) {
148 *s++ = itoa64[v&0x3f];
149 v >>= 6;
150 }
151 }
152 /* }}} */
153
php_crypt(const char * password,const int pass_len,const char * salt,int salt_len,zend_bool quiet)154 PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const char *salt, int salt_len, zend_bool quiet)
155 {
156 char *crypt_res;
157 zend_string *result;
158 /* Windows (win32/crypt) has a stripped down version of libxcrypt and
159 a CryptoApi md5_crypt implementation */
160 #if PHP_USE_PHP_CRYPT_R
161 {
162 struct php_crypt_extended_data buffer;
163
164 if (salt[0]=='$' && salt[1]=='1' && salt[2]=='$') {
165 char output[MD5_HASH_MAX_LEN], *out;
166
167 out = php_md5_crypt_r(password, salt, output);
168 if (out) {
169 return zend_string_init(out, strlen(out), 0);
170 }
171 return NULL;
172 } else if (salt[0]=='$' && salt[1]=='6' && salt[2]=='$') {
173 char *output;
174 output = emalloc(PHP_MAX_SALT_LEN);
175
176 crypt_res = php_sha512_crypt_r(password, salt, output, PHP_MAX_SALT_LEN);
177 if (!crypt_res) {
178 memset(output, 0, PHP_MAX_SALT_LEN);
179 efree(output);
180 return NULL;
181 } else {
182 result = zend_string_init(output, strlen(output), 0);
183 memset(output, 0, PHP_MAX_SALT_LEN);
184 efree(output);
185 return result;
186 }
187 } else if (salt[0]=='$' && salt[1]=='5' && salt[2]=='$') {
188 char *output;
189 output = emalloc(PHP_MAX_SALT_LEN);
190
191 crypt_res = php_sha256_crypt_r(password, salt, output, PHP_MAX_SALT_LEN);
192 if (!crypt_res) {
193 memset(output, 0, PHP_MAX_SALT_LEN);
194 efree(output);
195 return NULL;
196 } else {
197 result = zend_string_init(output, strlen(output), 0);
198 memset(output, 0, PHP_MAX_SALT_LEN);
199 efree(output);
200 return result;
201 }
202 } else if (
203 salt[0] == '$' &&
204 salt[1] == '2' &&
205 salt[3] == '$') {
206 char output[PHP_MAX_SALT_LEN + 1];
207
208 memset(output, 0, PHP_MAX_SALT_LEN + 1);
209
210 crypt_res = php_crypt_blowfish_rn(password, salt, output, sizeof(output));
211 if (!crypt_res) {
212 ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
213 return NULL;
214 } else {
215 result = zend_string_init(output, strlen(output), 0);
216 ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
217 return result;
218 }
219 } else if (salt[0] == '*' && (salt[1] == '0' || salt[1] == '1')) {
220 return NULL;
221 } else {
222 /* DES Fallback */
223
224 /* Only check the salt if it's not EXT_DES */
225 if (salt[0] != '_') {
226 /* DES style hashes */
227 if (!IS_VALID_SALT_CHARACTER(salt[0]) || !IS_VALID_SALT_CHARACTER(salt[1])) {
228 if (!quiet) {
229 /* error consistently about invalid DES fallbacks */
230 php_error_docref(NULL, E_DEPRECATED, DES_INVALID_SALT_ERROR);
231 }
232 }
233 }
234
235 memset(&buffer, 0, sizeof(buffer));
236 _crypt_extended_init_r();
237
238 crypt_res = _crypt_extended_r(password, salt, &buffer);
239 if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) {
240 return NULL;
241 } else {
242 result = zend_string_init(crypt_res, strlen(crypt_res), 0);
243 return result;
244 }
245 }
246 }
247 #else
248
249 if (salt[0] != '$' && salt[0] != '_' && (!IS_VALID_SALT_CHARACTER(salt[0]) || !IS_VALID_SALT_CHARACTER(salt[1]))) {
250 if (!quiet) {
251 /* error consistently about invalid DES fallbacks */
252 php_error_docref(NULL, E_DEPRECATED, DES_INVALID_SALT_ERROR);
253 }
254 }
255
256 # if defined(HAVE_CRYPT_R) && (defined(_REENTRANT) || defined(_THREAD_SAFE))
257 {
258 # if defined(CRYPT_R_STRUCT_CRYPT_DATA)
259 struct crypt_data buffer;
260 memset(&buffer, 0, sizeof(buffer));
261 # elif defined(CRYPT_R_CRYPTD)
262 CRYPTD buffer;
263 # else
264 # error Data struct used by crypt_r() is unknown. Please report.
265 # endif
266 crypt_res = crypt_r(password, salt, &buffer);
267 }
268 # elif defined(HAVE_CRYPT)
269 crypt_res = crypt(password, salt);
270 # else
271 # error No crypt() implementation
272 # endif
273 #endif
274
275 if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) {
276 return NULL;
277 } else {
278 result = zend_string_init(crypt_res, strlen(crypt_res), 0);
279 return result;
280 }
281 }
282 /* }}} */
283
284
285 /* {{{ proto string crypt(string str [, string salt])
286 Hash a string */
PHP_FUNCTION(crypt)287 PHP_FUNCTION(crypt)
288 {
289 char salt[PHP_MAX_SALT_LEN + 1];
290 char *str, *salt_in = NULL;
291 size_t str_len, salt_in_len = 0;
292 zend_string *result;
293
294 salt[0] = salt[PHP_MAX_SALT_LEN] = '\0';
295
296 /* This will produce suitable results if people depend on DES-encryption
297 * available (passing always 2-character salt). At least for glibc6.1 */
298 memset(&salt[1], '$', PHP_MAX_SALT_LEN - 1);
299
300 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &str, &str_len, &salt_in, &salt_in_len) == FAILURE) {
301 return;
302 }
303
304 if (salt_in) {
305 memcpy(salt, salt_in, MIN(PHP_MAX_SALT_LEN, salt_in_len));
306 } else {
307 php_error_docref(NULL, E_NOTICE, "No salt parameter was specified. You must use a randomly generated salt and a strong hash function to produce a secure hash.");
308 }
309
310 /* The automatic salt generation covers standard DES, md5-crypt and Blowfish (simple) */
311 if (!*salt) {
312 #if PHP_MD5_CRYPT
313 strncpy(salt, "$1$", PHP_MAX_SALT_LEN);
314 php_to64(&salt[3], PHP_CRYPT_RAND, 4);
315 php_to64(&salt[7], PHP_CRYPT_RAND, 4);
316 strncpy(&salt[11], "$", PHP_MAX_SALT_LEN - 11);
317 #elif PHP_STD_DES_CRYPT
318 php_to64(&salt[0], PHP_CRYPT_RAND, 2);
319 salt[2] = '\0';
320 #endif
321 salt_in_len = strlen(salt);
322 } else {
323 salt_in_len = MIN(PHP_MAX_SALT_LEN, salt_in_len);
324 }
325 salt[salt_in_len] = '\0';
326
327 if ((result = php_crypt(str, (int)str_len, salt, (int)salt_in_len, 0)) == NULL) {
328 if (salt[0] == '*' && salt[1] == '0') {
329 RETURN_STRING("*1");
330 } else {
331 RETURN_STRING("*0");
332 }
333 }
334 RETURN_STR(result);
335 }
336 /* }}} */
337 #endif
338
339 /*
340 * Local variables:
341 * tab-width: 4
342 * c-basic-offset: 4
343 * End:
344 * vim600: sw=4 ts=4 fdm=marker
345 * vim<600: sw=4 ts=4
346 */
347