xref: /PHP-8.2/ext/standard/crypt.c (revision e8c64b62)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Stig Bakken <ssb@php.net>                                   |
14    |          Zeev Suraski <zeev@php.net>                                 |
15    |          Rasmus Lerdorf <rasmus@php.net>                             |
16    |          Pierre Joye <pierre@php.net>                                |
17    +----------------------------------------------------------------------+
18 */
19 
20 #include <stdlib.h>
21 
22 #include "php.h"
23 
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27 #if PHP_USE_PHP_CRYPT_R
28 # include "php_crypt_r.h"
29 # include "crypt_freesec.h"
30 #else
31 # ifdef HAVE_CRYPT_H
32 #  if defined(CRYPT_R_GNU_SOURCE) && !defined(_GNU_SOURCE)
33 #   define _GNU_SOURCE
34 #  endif
35 #  include <crypt.h>
36 # endif
37 #endif
38 #include <time.h>
39 #include <string.h>
40 
41 #ifdef PHP_WIN32
42 #include <process.h>
43 #endif
44 
45 #include "php_crypt.h"
46 #include "ext/random/php_random.h"
47 
48 /* sha512 crypt has the maximal salt length of 123 characters */
49 #define PHP_MAX_SALT_LEN 123
50 
51 /* Used to check DES salts to ensure that they contain only valid characters */
52 #define IS_VALID_SALT_CHARACTER(c) (((c) >= '.' && (c) <= '9') || ((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
53 
PHP_MINIT_FUNCTION(crypt)54 PHP_MINIT_FUNCTION(crypt) /* {{{ */
55 {
56 	REGISTER_LONG_CONSTANT("CRYPT_SALT_LENGTH", PHP_MAX_SALT_LEN, CONST_CS | CONST_PERSISTENT);
57 	REGISTER_LONG_CONSTANT("CRYPT_STD_DES", 1, CONST_CS | CONST_PERSISTENT);
58 	REGISTER_LONG_CONSTANT("CRYPT_EXT_DES", 1, CONST_CS | CONST_PERSISTENT);
59 	REGISTER_LONG_CONSTANT("CRYPT_MD5", 1, CONST_CS | CONST_PERSISTENT);
60 	REGISTER_LONG_CONSTANT("CRYPT_BLOWFISH", 1, CONST_CS | CONST_PERSISTENT);
61 	REGISTER_LONG_CONSTANT("CRYPT_SHA256", 1, CONST_CS | CONST_PERSISTENT);
62 	REGISTER_LONG_CONSTANT("CRYPT_SHA512", 1, CONST_CS | CONST_PERSISTENT);
63 
64 #if PHP_USE_PHP_CRYPT_R
65 	php_init_crypt_r();
66 #endif
67 
68 	return SUCCESS;
69 }
70 /* }}} */
71 
PHP_MSHUTDOWN_FUNCTION(crypt)72 PHP_MSHUTDOWN_FUNCTION(crypt) /* {{{ */
73 {
74 #if PHP_USE_PHP_CRYPT_R
75 	php_shutdown_crypt_r();
76 #endif
77 
78 	return SUCCESS;
79 }
80 /* }}} */
81 
php_crypt(const char * password,const int pass_len,const char * salt,int salt_len,bool quiet)82 PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const char *salt, int salt_len, bool quiet)
83 {
84 	char *crypt_res;
85 	zend_string *result;
86 
87 	if (salt[0] == '*' && (salt[1] == '0' || salt[1] == '1')) {
88 		return NULL;
89 	}
90 
91 /* Windows (win32/crypt) has a stripped down version of libxcrypt and
92 	a CryptoApi md5_crypt implementation */
93 #if PHP_USE_PHP_CRYPT_R
94 	{
95 		struct php_crypt_extended_data buffer;
96 
97 		if (salt[0]=='$' && salt[1]=='1' && salt[2]=='$') {
98 			char output[MD5_HASH_MAX_LEN], *out;
99 
100 			out = php_md5_crypt_r(password, salt, output);
101 			if (out) {
102 				return zend_string_init(out, strlen(out), 0);
103 			}
104 			return NULL;
105 		} else if (salt[0]=='$' && salt[1]=='6' && salt[2]=='$') {
106 			char *output;
107 			output = emalloc(PHP_MAX_SALT_LEN);
108 
109 			crypt_res = php_sha512_crypt_r(password, salt, output, PHP_MAX_SALT_LEN);
110 			if (!crypt_res) {
111 				ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN);
112 				efree(output);
113 				return NULL;
114 			} else {
115 				result = zend_string_init(output, strlen(output), 0);
116 				ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN);
117 				efree(output);
118 				return result;
119 			}
120 		} else if (salt[0]=='$' && salt[1]=='5' && salt[2]=='$') {
121 			char *output;
122 			output = emalloc(PHP_MAX_SALT_LEN);
123 
124 			crypt_res = php_sha256_crypt_r(password, salt, output, PHP_MAX_SALT_LEN);
125 			if (!crypt_res) {
126 				ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN);
127 				efree(output);
128 				return NULL;
129 			} else {
130 				result = zend_string_init(output, strlen(output), 0);
131 				ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN);
132 				efree(output);
133 				return result;
134 			}
135 		} else if (
136 				salt[0] == '$' &&
137 				salt[1] == '2' &&
138 				salt[2] != 0 &&
139 				salt[3] == '$') {
140 			char output[PHP_MAX_SALT_LEN + 1];
141 
142 			memset(output, 0, PHP_MAX_SALT_LEN + 1);
143 
144 			crypt_res = php_crypt_blowfish_rn(password, salt, output, sizeof(output));
145 			if (!crypt_res) {
146 				ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
147 				return NULL;
148 			} else {
149 				result = zend_string_init(output, strlen(output), 0);
150 				ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
151 				return result;
152 			}
153 		} else if (salt[0] == '_'
154 				|| (IS_VALID_SALT_CHARACTER(salt[0]) && IS_VALID_SALT_CHARACTER(salt[1]))) {
155 			/* DES Fallback */
156 			memset(&buffer, 0, sizeof(buffer));
157 			_crypt_extended_init_r();
158 
159 			crypt_res = _crypt_extended_r((const unsigned char *) password, salt, &buffer);
160 			if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) {
161 				return NULL;
162 			} else {
163 				result = zend_string_init(crypt_res, strlen(crypt_res), 0);
164 				return result;
165 			}
166 		} else {
167 			/* Unknown hash type */
168 			return NULL;
169 		}
170 	}
171 #else
172 
173 # if defined(HAVE_CRYPT_R) && (defined(_REENTRANT) || defined(_THREAD_SAFE))
174 #  if defined(CRYPT_R_STRUCT_CRYPT_DATA)
175 	struct crypt_data buffer;
176 	memset(&buffer, 0, sizeof(buffer));
177 #  elif defined(CRYPT_R_CRYPTD)
178 	CRYPTD buffer;
179 #  else
180 #   error Data struct used by crypt_r() is unknown. Please report.
181 #  endif
182 	crypt_res = crypt_r(password, salt, &buffer);
183 # elif defined(HAVE_CRYPT)
184 	crypt_res = crypt(password, salt);
185 # else
186 #  error No crypt() implementation
187 # endif
188 #endif
189 
190 	if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) {
191 		return NULL;
192 	} else {
193 		result = zend_string_init(crypt_res, strlen(crypt_res), 0);
194 		return result;
195 	}
196 }
197 /* }}} */
198 
199 
200 /* {{{ Hash a string */
PHP_FUNCTION(crypt)201 PHP_FUNCTION(crypt)
202 {
203 	char salt[PHP_MAX_SALT_LEN + 1];
204 	char *str, *salt_in = NULL;
205 	size_t str_len, salt_in_len = 0;
206 	zend_string *result;
207 
208 	ZEND_PARSE_PARAMETERS_START(2, 2)
209 		Z_PARAM_STRING(str, str_len)
210 		Z_PARAM_STRING(salt_in, salt_in_len)
211 	ZEND_PARSE_PARAMETERS_END();
212 
213 	salt[0] = salt[PHP_MAX_SALT_LEN] = '\0';
214 
215 	/* This will produce suitable results if people depend on DES-encryption
216 	 * available (passing always 2-character salt). At least for glibc6.1 */
217 	memset(&salt[1], '$', PHP_MAX_SALT_LEN - 1);
218 	memcpy(salt, salt_in, MIN(PHP_MAX_SALT_LEN, salt_in_len));
219 
220 	salt_in_len = MIN(PHP_MAX_SALT_LEN, salt_in_len);
221 	salt[salt_in_len] = '\0';
222 
223 	if ((result = php_crypt(str, (int)str_len, salt, (int)salt_in_len, 0)) == NULL) {
224 		if (salt[0] == '*' && salt[1] == '0') {
225 			RETURN_STRING("*1");
226 		} else {
227 			RETURN_STRING("*0");
228 		}
229 	}
230 	RETURN_STR(result);
231 }
232 /* }}} */
233