xref: /PHP-7.3/ext/standard/crypt.c (revision 9afce019)
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: Stig Bakken <ssb@php.net>                                   |
16    |          Zeev Suraski <zeev@php.net>                                 |
17    |          Rasmus Lerdorf <rasmus@php.net>                             |
18    |          Pierre Joye <pierre@php.net>                                |
19    +----------------------------------------------------------------------+
20 */
21 
22 #include <stdlib.h>
23 
24 #include "php.h"
25 
26 #if HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 #if PHP_USE_PHP_CRYPT_R
30 # include "php_crypt_r.h"
31 # include "crypt_freesec.h"
32 #else
33 # if HAVE_CRYPT_H
34 #  if defined(CRYPT_R_GNU_SOURCE) && !defined(_GNU_SOURCE)
35 #   define _GNU_SOURCE
36 #  endif
37 #  include <crypt.h>
38 # endif
39 #endif
40 #if TM_IN_SYS_TIME
41 #include <sys/time.h>
42 #else
43 #include <time.h>
44 #endif
45 #if HAVE_STRING_H
46 #include <string.h>
47 #else
48 #include <strings.h>
49 #endif
50 
51 #ifdef PHP_WIN32
52 #include <process.h>
53 #endif
54 
55 #include "php_crypt.h"
56 #include "php_random.h"
57 
58 /* sha512 crypt has the maximal salt length of 123 characters */
59 #define PHP_MAX_SALT_LEN 123
60 
61 /* Used to check DES salts to ensure that they contain only valid characters */
62 #define IS_VALID_SALT_CHARACTER(c) (((c) >= '.' && (c) <= '9') || ((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
63 
64 #define DES_INVALID_SALT_ERROR "Supplied salt is not valid for DES. Possible bug in provided salt format."
65 
66 
PHP_MINIT_FUNCTION(crypt)67 PHP_MINIT_FUNCTION(crypt) /* {{{ */
68 {
69 	REGISTER_LONG_CONSTANT("CRYPT_SALT_LENGTH", PHP_MAX_SALT_LEN, CONST_CS | CONST_PERSISTENT);
70 	REGISTER_LONG_CONSTANT("CRYPT_STD_DES", 1, CONST_CS | CONST_PERSISTENT);
71 	REGISTER_LONG_CONSTANT("CRYPT_EXT_DES", 1, CONST_CS | CONST_PERSISTENT);
72 	REGISTER_LONG_CONSTANT("CRYPT_MD5", 1, CONST_CS | CONST_PERSISTENT);
73 	REGISTER_LONG_CONSTANT("CRYPT_BLOWFISH", 1, CONST_CS | CONST_PERSISTENT);
74 	REGISTER_LONG_CONSTANT("CRYPT_SHA256", 1, CONST_CS | CONST_PERSISTENT);
75 	REGISTER_LONG_CONSTANT("CRYPT_SHA512", 1, CONST_CS | CONST_PERSISTENT);
76 
77 #if PHP_USE_PHP_CRYPT_R
78 	php_init_crypt_r();
79 #endif
80 
81 	return SUCCESS;
82 }
83 /* }}} */
84 
PHP_MSHUTDOWN_FUNCTION(crypt)85 PHP_MSHUTDOWN_FUNCTION(crypt) /* {{{ */
86 {
87 #if PHP_USE_PHP_CRYPT_R
88 	php_shutdown_crypt_r();
89 #endif
90 
91 	return SUCCESS;
92 }
93 /* }}} */
94 
95 static unsigned char itoa64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
96 
php_to64(char * s,int n)97 static void php_to64(char *s, int n) /* {{{ */
98 {
99 	while (--n >= 0) {
100 		*s = itoa64[*s & 0x3f];
101 		s++;
102 	}
103 }
104 /* }}} */
105 
php_crypt(const char * password,const int pass_len,const char * salt,int salt_len,zend_bool quiet)106 PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const char *salt, int salt_len, zend_bool quiet)
107 {
108 	char *crypt_res;
109 	zend_string *result;
110 /* Windows (win32/crypt) has a stripped down version of libxcrypt and
111 	a CryptoApi md5_crypt implementation */
112 #if PHP_USE_PHP_CRYPT_R
113 	{
114 		struct php_crypt_extended_data buffer;
115 
116 		if (salt[0]=='$' && salt[1]=='1' && salt[2]=='$') {
117 			char output[MD5_HASH_MAX_LEN], *out;
118 
119 			out = php_md5_crypt_r(password, salt, output);
120 			if (out) {
121 				return zend_string_init(out, strlen(out), 0);
122 			}
123 			return NULL;
124 		} else if (salt[0]=='$' && salt[1]=='6' && salt[2]=='$') {
125 			char *output;
126 			output = emalloc(PHP_MAX_SALT_LEN);
127 
128 			crypt_res = php_sha512_crypt_r(password, salt, output, PHP_MAX_SALT_LEN);
129 			if (!crypt_res) {
130 				ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN);
131 				efree(output);
132 				return NULL;
133 			} else {
134 				result = zend_string_init(output, strlen(output), 0);
135 				ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN);
136 				efree(output);
137 				return result;
138 			}
139 		} else if (salt[0]=='$' && salt[1]=='5' && salt[2]=='$') {
140 			char *output;
141 			output = emalloc(PHP_MAX_SALT_LEN);
142 
143 			crypt_res = php_sha256_crypt_r(password, salt, output, PHP_MAX_SALT_LEN);
144 			if (!crypt_res) {
145 				ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN);
146 				efree(output);
147 				return NULL;
148 			} else {
149 				result = zend_string_init(output, strlen(output), 0);
150 				ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN);
151 				efree(output);
152 				return result;
153 			}
154 		} else if (
155 				salt[0] == '$' &&
156 				salt[1] == '2' &&
157 				salt[3] == '$') {
158 			char output[PHP_MAX_SALT_LEN + 1];
159 
160 			memset(output, 0, PHP_MAX_SALT_LEN + 1);
161 
162 			crypt_res = php_crypt_blowfish_rn(password, salt, output, sizeof(output));
163 			if (!crypt_res) {
164 				ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
165 				return NULL;
166 			} else {
167 				result = zend_string_init(output, strlen(output), 0);
168 				ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
169 				return result;
170 			}
171         } else if (salt[0] == '*' && (salt[1] == '0' || salt[1] == '1')) {
172             return NULL;
173 		} else {
174 			/* DES Fallback */
175 
176 			/* Only check the salt if it's not EXT_DES */
177 			if (salt[0] != '_') {
178 				/* DES style hashes */
179 				if (!IS_VALID_SALT_CHARACTER(salt[0]) || !IS_VALID_SALT_CHARACTER(salt[1])) {
180 					if (!quiet) {
181 						/* error consistently about invalid DES fallbacks */
182 						php_error_docref(NULL, E_DEPRECATED, DES_INVALID_SALT_ERROR);
183 					}
184 				}
185 			}
186 
187 			memset(&buffer, 0, sizeof(buffer));
188 			_crypt_extended_init_r();
189 
190 			crypt_res = _crypt_extended_r(password, salt, &buffer);
191 			if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) {
192 				return NULL;
193 			} else {
194 				result = zend_string_init(crypt_res, strlen(crypt_res), 0);
195 				return result;
196 			}
197 		}
198 	}
199 #else
200 
201 	if (salt[0] != '$' && salt[0] != '_' && (!IS_VALID_SALT_CHARACTER(salt[0]) || !IS_VALID_SALT_CHARACTER(salt[1]))) {
202 		if (!quiet) {
203 			/* error consistently about invalid DES fallbacks */
204 			php_error_docref(NULL, E_DEPRECATED, DES_INVALID_SALT_ERROR);
205 		}
206 	}
207 
208 # if defined(HAVE_CRYPT_R) && (defined(_REENTRANT) || defined(_THREAD_SAFE))
209 	{
210 #  if defined(CRYPT_R_STRUCT_CRYPT_DATA)
211 		struct crypt_data buffer;
212 		memset(&buffer, 0, sizeof(buffer));
213 #  elif defined(CRYPT_R_CRYPTD)
214 		CRYPTD buffer;
215 #  else
216 #   error Data struct used by crypt_r() is unknown. Please report.
217 #  endif
218 		crypt_res = crypt_r(password, salt, &buffer);
219 	}
220 # elif defined(HAVE_CRYPT)
221 	crypt_res = crypt(password, salt);
222 # else
223 #  error No crypt() implementation
224 # endif
225 #endif
226 
227 	if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) {
228 		return NULL;
229 	} else {
230 		result = zend_string_init(crypt_res, strlen(crypt_res), 0);
231 		return result;
232 	}
233 }
234 /* }}} */
235 
236 
237 /* {{{ proto string crypt(string str [, string salt])
238    Hash a string */
PHP_FUNCTION(crypt)239 PHP_FUNCTION(crypt)
240 {
241 	char salt[PHP_MAX_SALT_LEN + 1];
242 	char *str, *salt_in = NULL;
243 	size_t str_len, salt_in_len = 0;
244 	zend_string *result;
245 
246 	ZEND_PARSE_PARAMETERS_START(1, 2)
247 		Z_PARAM_STRING(str, str_len)
248 		Z_PARAM_OPTIONAL
249 		Z_PARAM_STRING(salt_in, salt_in_len)
250 	ZEND_PARSE_PARAMETERS_END();
251 
252 	salt[0] = salt[PHP_MAX_SALT_LEN] = '\0';
253 
254 	/* This will produce suitable results if people depend on DES-encryption
255 	 * available (passing always 2-character salt). At least for glibc6.1 */
256 	memset(&salt[1], '$', PHP_MAX_SALT_LEN - 1);
257 
258 	if (salt_in) {
259 		memcpy(salt, salt_in, MIN(PHP_MAX_SALT_LEN, salt_in_len));
260 	} else {
261 		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.");
262 	}
263 
264 	/* The automatic salt generation covers standard DES, md5-crypt and Blowfish (simple) */
265 	if (!*salt) {
266 		strncpy(salt, "$1$", 3);
267 		php_random_bytes_throw(&salt[3], 8);
268 		php_to64(&salt[3], 8);
269 		strncpy(&salt[11], "$", PHP_MAX_SALT_LEN - 11);
270 		salt_in_len = strlen(salt);
271 	} else {
272 		salt_in_len = MIN(PHP_MAX_SALT_LEN, salt_in_len);
273 	}
274 	salt[salt_in_len] = '\0';
275 
276 	if ((result = php_crypt(str, (int)str_len, salt, (int)salt_in_len, 0)) == NULL) {
277 		if (salt[0] == '*' && salt[1] == '0') {
278 			RETURN_STRING("*1");
279 		} else {
280 			RETURN_STRING("*0");
281 		}
282 	}
283 	RETURN_STR(result);
284 }
285 /* }}} */
286 
287 /*
288  * Local variables:
289  * tab-width: 4
290  * c-basic-offset: 4
291  * End:
292  * vim600: sw=4 ts=4 fdm=marker
293  * vim<600: sw=4 ts=4
294  */
295