xref: /PHP-7.0/ext/standard/crypt.c (revision 478f119a)
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