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