xref: /PHP-7.4/ext/openssl/openssl.c (revision 6724d5d4)
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 Venaas <venaas@php.net>                                |
16    |          Wez Furlong <wez@thebrainroom.com>                          |
17    |          Sascha Kettler <kettler@gmx.net>                            |
18    |          Pierre-Alain Joye <pierre@php.net>                          |
19    |          Marc Delling <delling@silpion.de> (PKCS12 functions)        |
20    |          Jakub Zelenka <bukka@php.net>                               |
21    +----------------------------------------------------------------------+
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include "php.h"
29 #include "php_ini.h"
30 #include "php_openssl.h"
31 #include "zend_exceptions.h"
32 
33 /* PHP Includes */
34 #include "ext/standard/file.h"
35 #include "ext/standard/info.h"
36 #include "ext/standard/php_fopen_wrappers.h"
37 #include "ext/standard/md5.h"
38 #include "ext/standard/base64.h"
39 #ifdef PHP_WIN32
40 # include "win32/winutil.h"
41 #endif
42 
43 /* OpenSSL includes */
44 #include <openssl/evp.h>
45 #include <openssl/bn.h>
46 #include <openssl/rsa.h>
47 #include <openssl/dsa.h>
48 #include <openssl/dh.h>
49 #include <openssl/x509.h>
50 #include <openssl/x509v3.h>
51 #include <openssl/crypto.h>
52 #include <openssl/pem.h>
53 #include <openssl/err.h>
54 #include <openssl/conf.h>
55 #include <openssl/rand.h>
56 #include <openssl/ssl.h>
57 #include <openssl/pkcs12.h>
58 
59 /* Common */
60 #include <time.h>
61 
62 #if (defined(PHP_WIN32) && defined(_MSC_VER) && _MSC_VER >= 1900)
63 #define timezone _timezone	/* timezone is called _timezone in LibC */
64 #endif
65 
66 #define MIN_KEY_LENGTH		384
67 
68 #define OPENSSL_ALGO_SHA1 	1
69 #define OPENSSL_ALGO_MD5	2
70 #define OPENSSL_ALGO_MD4	3
71 #ifdef HAVE_OPENSSL_MD2_H
72 #define OPENSSL_ALGO_MD2	4
73 #endif
74 #if PHP_OPENSSL_API_VERSION < 0x10100
75 #define OPENSSL_ALGO_DSS1	5
76 #endif
77 #define OPENSSL_ALGO_SHA224 6
78 #define OPENSSL_ALGO_SHA256 7
79 #define OPENSSL_ALGO_SHA384 8
80 #define OPENSSL_ALGO_SHA512 9
81 #define OPENSSL_ALGO_RMD160 10
82 #define DEBUG_SMIME	0
83 
84 #if !defined(OPENSSL_NO_EC) && defined(EVP_PKEY_EC)
85 #define HAVE_EVP_PKEY_EC 1
86 #endif
87 
88 ZEND_DECLARE_MODULE_GLOBALS(openssl)
89 
90 /* FIXME: Use the openssl constants instead of
91  * enum. It is now impossible to match real values
92  * against php constants. Also sorry to break the
93  * enum principles here, BC...
94  */
95 enum php_openssl_key_type {
96 	OPENSSL_KEYTYPE_RSA,
97 	OPENSSL_KEYTYPE_DSA,
98 	OPENSSL_KEYTYPE_DH,
99 	OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA,
100 #ifdef HAVE_EVP_PKEY_EC
101 	OPENSSL_KEYTYPE_EC = OPENSSL_KEYTYPE_DH +1
102 #endif
103 };
104 
105 enum php_openssl_cipher_type {
106 	PHP_OPENSSL_CIPHER_RC2_40,
107 	PHP_OPENSSL_CIPHER_RC2_128,
108 	PHP_OPENSSL_CIPHER_RC2_64,
109 	PHP_OPENSSL_CIPHER_DES,
110 	PHP_OPENSSL_CIPHER_3DES,
111 	PHP_OPENSSL_CIPHER_AES_128_CBC,
112 	PHP_OPENSSL_CIPHER_AES_192_CBC,
113 	PHP_OPENSSL_CIPHER_AES_256_CBC,
114 
115 	PHP_OPENSSL_CIPHER_DEFAULT = PHP_OPENSSL_CIPHER_RC2_40
116 };
117 
118 PHP_FUNCTION(openssl_get_md_methods);
119 PHP_FUNCTION(openssl_get_cipher_methods);
120 #ifdef HAVE_EVP_PKEY_EC
121 PHP_FUNCTION(openssl_get_curve_names);
122 #endif
123 
124 PHP_FUNCTION(openssl_digest);
125 PHP_FUNCTION(openssl_encrypt);
126 PHP_FUNCTION(openssl_decrypt);
127 PHP_FUNCTION(openssl_cipher_iv_length);
128 
129 PHP_FUNCTION(openssl_dh_compute_key);
130 PHP_FUNCTION(openssl_pkey_derive);
131 PHP_FUNCTION(openssl_random_pseudo_bytes);
132 
133 /* {{{ arginfo */
134 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 0, 2)
135 	ZEND_ARG_INFO(0, x509)
136 	ZEND_ARG_INFO(0, outfilename)
137 	ZEND_ARG_INFO(0, notext)
138 ZEND_END_ARG_INFO()
139 
140 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_export, 0, 0, 2)
141 	ZEND_ARG_INFO(0, x509)
142 	ZEND_ARG_INFO(1, out)
143 	ZEND_ARG_INFO(0, notext)
144 ZEND_END_ARG_INFO()
145 
146 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_fingerprint, 0, 0, 1)
147 	ZEND_ARG_INFO(0, x509)
148 	ZEND_ARG_INFO(0, method)
149 	ZEND_ARG_INFO(0, raw_output)
150 ZEND_END_ARG_INFO()
151 
152 ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_check_private_key, 0)
153 	ZEND_ARG_INFO(0, cert)
154 	ZEND_ARG_INFO(0, key)
155 ZEND_END_ARG_INFO()
156 
157 ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_verify, 0)
158 	ZEND_ARG_INFO(0, cert)
159 	ZEND_ARG_INFO(0, key)
160 ZEND_END_ARG_INFO()
161 
162 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_parse, 0, 0, 1)
163 	ZEND_ARG_INFO(0, x509)
164 	ZEND_ARG_INFO(0, shortname)
165 ZEND_END_ARG_INFO()
166 
167 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_checkpurpose, 0, 0, 2)
168 	ZEND_ARG_INFO(0, x509cert)
169 	ZEND_ARG_INFO(0, purpose)
170 	ZEND_ARG_INFO(0, cainfo) /* array */
171 	ZEND_ARG_INFO(0, untrustedfile)
172 ZEND_END_ARG_INFO()
173 
174 ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_read, 0)
175 	ZEND_ARG_INFO(0, cert)
176 ZEND_END_ARG_INFO()
177 
178 ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_free, 0)
179 	ZEND_ARG_INFO(0, x509)
180 ZEND_END_ARG_INFO()
181 
182 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs12_export_to_file, 0, 0, 4)
183 	ZEND_ARG_INFO(0, x509)
184 	ZEND_ARG_INFO(0, filename)
185 	ZEND_ARG_INFO(0, priv_key)
186 	ZEND_ARG_INFO(0, pass)
187 	ZEND_ARG_INFO(0, args) /* array */
188 ZEND_END_ARG_INFO()
189 
190 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs12_export, 0, 0, 4)
191 	ZEND_ARG_INFO(0, x509)
192 	ZEND_ARG_INFO(1, out)
193 	ZEND_ARG_INFO(0, priv_key)
194 	ZEND_ARG_INFO(0, pass)
195 	ZEND_ARG_INFO(0, args) /* array */
196 ZEND_END_ARG_INFO()
197 
198 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkcs12_read, 0)
199 	ZEND_ARG_INFO(0, PKCS12)
200 	ZEND_ARG_INFO(1, certs) /* array */
201 	ZEND_ARG_INFO(0, pass)
202 ZEND_END_ARG_INFO()
203 
204 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_export_to_file, 0, 0, 2)
205 	ZEND_ARG_INFO(0, csr)
206 	ZEND_ARG_INFO(0, outfilename)
207 	ZEND_ARG_INFO(0, notext)
208 ZEND_END_ARG_INFO()
209 
210 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_export, 0, 0, 2)
211 	ZEND_ARG_INFO(0, csr)
212 	ZEND_ARG_INFO(1, out)
213 	ZEND_ARG_INFO(0, notext)
214 ZEND_END_ARG_INFO()
215 
216 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_sign, 0, 0, 4)
217 	ZEND_ARG_INFO(0, csr)
218 	ZEND_ARG_INFO(0, x509)
219 	ZEND_ARG_INFO(0, priv_key)
220 	ZEND_ARG_INFO(0, days)
221 	ZEND_ARG_INFO(0, config_args) /* array */
222 	ZEND_ARG_INFO(0, serial)
223 ZEND_END_ARG_INFO()
224 
225 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_new, 0, 0, 2)
226 	ZEND_ARG_INFO(0, dn) /* array */
227 	ZEND_ARG_INFO(1, privkey)
228 	ZEND_ARG_INFO(0, configargs)
229 	ZEND_ARG_INFO(0, extraattribs)
230 ZEND_END_ARG_INFO()
231 
232 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_get_subject, 0, 0, 1)
233 	ZEND_ARG_INFO(0, csr)
234 	ZEND_ARG_INFO(0, use_shortnames)
235 ZEND_END_ARG_INFO()
236 
237 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_get_public_key, 0, 0, 1)
238 	ZEND_ARG_INFO(0, csr)
239 	ZEND_ARG_INFO(0, use_shortnames)
240 ZEND_END_ARG_INFO()
241 
242 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_new, 0, 0, 0)
243 	ZEND_ARG_INFO(0, configargs) /* array */
244 ZEND_END_ARG_INFO()
245 
246 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_export_to_file, 0, 0, 2)
247 	ZEND_ARG_INFO(0, key)
248 	ZEND_ARG_INFO(0, outfilename)
249 	ZEND_ARG_INFO(0, passphrase)
250 	ZEND_ARG_INFO(0, config_args) /* array */
251 ZEND_END_ARG_INFO()
252 
253 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_export, 0, 0, 2)
254 	ZEND_ARG_INFO(0, key)
255 	ZEND_ARG_INFO(1, out)
256 	ZEND_ARG_INFO(0, passphrase)
257 	ZEND_ARG_INFO(0, config_args) /* array */
258 ZEND_END_ARG_INFO()
259 
260 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_public, 0)
261 	ZEND_ARG_INFO(0, cert)
262 ZEND_END_ARG_INFO()
263 
264 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_free, 0)
265 	ZEND_ARG_INFO(0, key)
266 ZEND_END_ARG_INFO()
267 
268 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_get_private, 0, 0, 1)
269 	ZEND_ARG_INFO(0, key)
270 	ZEND_ARG_INFO(0, passphrase)
271 ZEND_END_ARG_INFO()
272 
273 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_details, 0)
274 	ZEND_ARG_INFO(0, key)
275 ZEND_END_ARG_INFO()
276 
277 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pbkdf2, 0, 0, 4)
278 	ZEND_ARG_INFO(0, password)
279 	ZEND_ARG_INFO(0, salt)
280 	ZEND_ARG_INFO(0, key_length)
281 	ZEND_ARG_INFO(0, iterations)
282 	ZEND_ARG_INFO(0, digest_algorithm)
283 ZEND_END_ARG_INFO()
284 
285 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_verify, 0, 0, 2)
286 	ZEND_ARG_INFO(0, filename)
287 	ZEND_ARG_INFO(0, flags)
288 	ZEND_ARG_INFO(0, signerscerts)
289 	ZEND_ARG_INFO(0, cainfo) /* array */
290 	ZEND_ARG_INFO(0, extracerts)
291 	ZEND_ARG_INFO(0, content)
292 	ZEND_ARG_INFO(0, pk7)
293 ZEND_END_ARG_INFO()
294 
295 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_encrypt, 0, 0, 4)
296 	ZEND_ARG_INFO(0, infile)
297 	ZEND_ARG_INFO(0, outfile)
298 	ZEND_ARG_INFO(0, recipcerts)
299 	ZEND_ARG_INFO(0, headers) /* array */
300 	ZEND_ARG_INFO(0, flags)
301 	ZEND_ARG_INFO(0, cipher)
302 ZEND_END_ARG_INFO()
303 
304 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_sign, 0, 0, 5)
305 	ZEND_ARG_INFO(0, infile)
306 	ZEND_ARG_INFO(0, outfile)
307 	ZEND_ARG_INFO(0, signcert)
308 	ZEND_ARG_INFO(0, signkey)
309 	ZEND_ARG_INFO(0, headers) /* array */
310 	ZEND_ARG_INFO(0, flags)
311 	ZEND_ARG_INFO(0, extracertsfilename)
312 ZEND_END_ARG_INFO()
313 
314 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_decrypt, 0, 0, 3)
315 	ZEND_ARG_INFO(0, infilename)
316 	ZEND_ARG_INFO(0, outfilename)
317 	ZEND_ARG_INFO(0, recipcert)
318 	ZEND_ARG_INFO(0, recipkey)
319 ZEND_END_ARG_INFO()
320 
321 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_read, 0, 0, 2)
322 	ZEND_ARG_INFO(0, infilename)
323 	ZEND_ARG_INFO(1, certs)
324 ZEND_END_ARG_INFO()
325 
326 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_private_encrypt, 0, 0, 3)
327 	ZEND_ARG_INFO(0, data)
328 	ZEND_ARG_INFO(1, crypted)
329 	ZEND_ARG_INFO(0, key)
330 	ZEND_ARG_INFO(0, padding)
331 ZEND_END_ARG_INFO()
332 
333 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_private_decrypt, 0, 0, 3)
334 	ZEND_ARG_INFO(0, data)
335 	ZEND_ARG_INFO(1, crypted)
336 	ZEND_ARG_INFO(0, key)
337 	ZEND_ARG_INFO(0, padding)
338 ZEND_END_ARG_INFO()
339 
340 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_public_encrypt, 0, 0, 3)
341 	ZEND_ARG_INFO(0, data)
342 	ZEND_ARG_INFO(1, crypted)
343 	ZEND_ARG_INFO(0, key)
344 	ZEND_ARG_INFO(0, padding)
345 ZEND_END_ARG_INFO()
346 
347 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_public_decrypt, 0, 0, 3)
348 	ZEND_ARG_INFO(0, data)
349 	ZEND_ARG_INFO(1, crypted)
350 	ZEND_ARG_INFO(0, key)
351 	ZEND_ARG_INFO(0, padding)
352 ZEND_END_ARG_INFO()
353 
354 ZEND_BEGIN_ARG_INFO(arginfo_openssl_error_string, 0)
355 ZEND_END_ARG_INFO()
356 
357 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_sign, 0, 0, 3)
358 	ZEND_ARG_INFO(0, data)
359 	ZEND_ARG_INFO(1, signature)
360 	ZEND_ARG_INFO(0, key)
361 	ZEND_ARG_INFO(0, method)
362 ZEND_END_ARG_INFO()
363 
364 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_verify, 0, 0, 3)
365 	ZEND_ARG_INFO(0, data)
366 	ZEND_ARG_INFO(0, signature)
367 	ZEND_ARG_INFO(0, key)
368 	ZEND_ARG_INFO(0, method)
369 ZEND_END_ARG_INFO()
370 
371 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_seal, 0, 0, 4)
372 	ZEND_ARG_INFO(0, data)
373 	ZEND_ARG_INFO(1, sealdata)
374 	ZEND_ARG_INFO(1, ekeys) /* array */
375 	ZEND_ARG_INFO(0, pubkeys) /* array */
376 	ZEND_ARG_INFO(0, method)
377 	ZEND_ARG_INFO(1, iv)
378 ZEND_END_ARG_INFO()
379 
380 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_open, 0, 0, 4)
381 	ZEND_ARG_INFO(0, data)
382 	ZEND_ARG_INFO(1, opendata)
383 	ZEND_ARG_INFO(0, ekey)
384 	ZEND_ARG_INFO(0, privkey)
385 	ZEND_ARG_INFO(0, method)
386 	ZEND_ARG_INFO(0, iv)
387 ZEND_END_ARG_INFO()
388 
389 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_md_methods, 0, 0, 0)
390 	ZEND_ARG_INFO(0, aliases)
391 ZEND_END_ARG_INFO()
392 
393 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_cipher_methods, 0, 0, 0)
394 	ZEND_ARG_INFO(0, aliases)
395 ZEND_END_ARG_INFO()
396 
397 #ifdef HAVE_EVP_PKEY_EC
398 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_curve_names, 0, 0, 0)
399 ZEND_END_ARG_INFO()
400 #endif
401 
402 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_digest, 0, 0, 2)
403 	ZEND_ARG_INFO(0, data)
404 	ZEND_ARG_INFO(0, method)
405 	ZEND_ARG_INFO(0, raw_output)
406 ZEND_END_ARG_INFO()
407 
408 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_encrypt, 0, 0, 3)
409 	ZEND_ARG_INFO(0, data)
410 	ZEND_ARG_INFO(0, method)
411 	ZEND_ARG_INFO(0, password)
412 	ZEND_ARG_INFO(0, options)
413 	ZEND_ARG_INFO(0, iv)
414 	ZEND_ARG_INFO(1, tag)
415 	ZEND_ARG_INFO(0, aad)
416 	ZEND_ARG_INFO(0, tag_length)
417 ZEND_END_ARG_INFO()
418 
419 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_decrypt, 0, 0, 3)
420 	ZEND_ARG_INFO(0, data)
421 	ZEND_ARG_INFO(0, method)
422 	ZEND_ARG_INFO(0, password)
423 	ZEND_ARG_INFO(0, options)
424 	ZEND_ARG_INFO(0, iv)
425 	ZEND_ARG_INFO(0, tag)
426 	ZEND_ARG_INFO(0, aad)
427 ZEND_END_ARG_INFO()
428 
429 ZEND_BEGIN_ARG_INFO(arginfo_openssl_cipher_iv_length, 0)
430 	ZEND_ARG_INFO(0, method)
431 ZEND_END_ARG_INFO()
432 
433 ZEND_BEGIN_ARG_INFO(arginfo_openssl_dh_compute_key, 0)
434 	ZEND_ARG_INFO(0, pub_key)
435 	ZEND_ARG_INFO(0, dh_key)
436 ZEND_END_ARG_INFO()
437 
438 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_derive, 0, 0, 2)
439 	ZEND_ARG_INFO(0, peer_pub_key)
440 	ZEND_ARG_INFO(0, priv_key)
441 	ZEND_ARG_INFO(0, keylen)
442 ZEND_END_ARG_INFO()
443 
444 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_random_pseudo_bytes, 0, 0, 1)
445 	ZEND_ARG_INFO(0, length)
446 	ZEND_ARG_INFO(1, result_is_strong)
447 ZEND_END_ARG_INFO()
448 
449 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_spki_new, 0, 0, 2)
450 	ZEND_ARG_INFO(0, privkey)
451 	ZEND_ARG_INFO(0, challenge)
452 	ZEND_ARG_INFO(0, algo)
453 ZEND_END_ARG_INFO()
454 
455 ZEND_BEGIN_ARG_INFO(arginfo_openssl_spki_verify, 0)
456 	ZEND_ARG_INFO(0, spki)
457 ZEND_END_ARG_INFO()
458 
459 ZEND_BEGIN_ARG_INFO(arginfo_openssl_spki_export, 0)
460 	ZEND_ARG_INFO(0, spki)
461 ZEND_END_ARG_INFO()
462 
463 ZEND_BEGIN_ARG_INFO(arginfo_openssl_spki_export_challenge, 0)
464 	ZEND_ARG_INFO(0, spki)
465 ZEND_END_ARG_INFO()
466 
467 ZEND_BEGIN_ARG_INFO(arginfo_openssl_get_cert_locations, 0)
468 ZEND_END_ARG_INFO()
469 /* }}} */
470 
471 /* {{{ openssl_functions[]
472  */
473 static const zend_function_entry openssl_functions[] = {
474 	PHP_FE(openssl_get_cert_locations, arginfo_openssl_get_cert_locations)
475 
476 /* spki functions */
477 	PHP_FE(openssl_spki_new, arginfo_openssl_spki_new)
478 	PHP_FE(openssl_spki_verify, arginfo_openssl_spki_verify)
479 	PHP_FE(openssl_spki_export, arginfo_openssl_spki_export)
480 	PHP_FE(openssl_spki_export_challenge, arginfo_openssl_spki_export_challenge)
481 
482 /* public/private key functions */
483 	PHP_FE(openssl_pkey_free,			arginfo_openssl_pkey_free)
484 	PHP_FE(openssl_pkey_new,			arginfo_openssl_pkey_new)
485 	PHP_FE(openssl_pkey_export,			arginfo_openssl_pkey_export)
486 	PHP_FE(openssl_pkey_export_to_file,	arginfo_openssl_pkey_export_to_file)
487 	PHP_FE(openssl_pkey_get_private,	arginfo_openssl_pkey_get_private)
488 	PHP_FE(openssl_pkey_get_public,		arginfo_openssl_pkey_get_public)
489 	PHP_FE(openssl_pkey_get_details,	arginfo_openssl_pkey_get_details)
490 
491 	PHP_FALIAS(openssl_free_key,		openssl_pkey_free, 			arginfo_openssl_pkey_free)
492 	PHP_FALIAS(openssl_get_privatekey,	openssl_pkey_get_private,	arginfo_openssl_pkey_get_private)
493 	PHP_FALIAS(openssl_get_publickey,	openssl_pkey_get_public,	arginfo_openssl_pkey_get_public)
494 
495 /* x.509 cert funcs */
496 	PHP_FE(openssl_x509_read,				arginfo_openssl_x509_read)
497 	PHP_FE(openssl_x509_free,				arginfo_openssl_x509_free)
498 	PHP_FE(openssl_x509_parse,			 	arginfo_openssl_x509_parse)
499 	PHP_FE(openssl_x509_checkpurpose,		arginfo_openssl_x509_checkpurpose)
500 	PHP_FE(openssl_x509_check_private_key,	arginfo_openssl_x509_check_private_key)
501 	PHP_FE(openssl_x509_verify,	            arginfo_openssl_x509_verify)
502 	PHP_FE(openssl_x509_export,				arginfo_openssl_x509_export)
503 	PHP_FE(openssl_x509_fingerprint,			arginfo_openssl_x509_fingerprint)
504 	PHP_FE(openssl_x509_export_to_file,		arginfo_openssl_x509_export_to_file)
505 
506 /* PKCS12 funcs */
507 	PHP_FE(openssl_pkcs12_export,			arginfo_openssl_pkcs12_export)
508 	PHP_FE(openssl_pkcs12_export_to_file,	arginfo_openssl_pkcs12_export_to_file)
509 	PHP_FE(openssl_pkcs12_read,				arginfo_openssl_pkcs12_read)
510 
511 /* CSR funcs */
512 	PHP_FE(openssl_csr_new,				arginfo_openssl_csr_new)
513 	PHP_FE(openssl_csr_export,			arginfo_openssl_csr_export)
514 	PHP_FE(openssl_csr_export_to_file,	arginfo_openssl_csr_export_to_file)
515 	PHP_FE(openssl_csr_sign,			arginfo_openssl_csr_sign)
516 	PHP_FE(openssl_csr_get_subject,		arginfo_openssl_csr_get_subject)
517 	PHP_FE(openssl_csr_get_public_key,	arginfo_openssl_csr_get_public_key)
518 
519 	PHP_FE(openssl_digest,				arginfo_openssl_digest)
520 	PHP_FE(openssl_encrypt,				arginfo_openssl_encrypt)
521 	PHP_FE(openssl_decrypt,				arginfo_openssl_decrypt)
522 	PHP_FE(openssl_cipher_iv_length,	arginfo_openssl_cipher_iv_length)
523 	PHP_FE(openssl_sign,				arginfo_openssl_sign)
524 	PHP_FE(openssl_verify,				arginfo_openssl_verify)
525 	PHP_FE(openssl_seal,				arginfo_openssl_seal)
526 	PHP_FE(openssl_open,				arginfo_openssl_open)
527 
528 	PHP_FE(openssl_pbkdf2,	arginfo_openssl_pbkdf2)
529 
530 /* for S/MIME handling */
531 	PHP_FE(openssl_pkcs7_verify,		arginfo_openssl_pkcs7_verify)
532 	PHP_FE(openssl_pkcs7_decrypt,		arginfo_openssl_pkcs7_decrypt)
533 	PHP_FE(openssl_pkcs7_sign,			arginfo_openssl_pkcs7_sign)
534 	PHP_FE(openssl_pkcs7_encrypt,		arginfo_openssl_pkcs7_encrypt)
535 	PHP_FE(openssl_pkcs7_read,			arginfo_openssl_pkcs7_read)
536 
537 	PHP_FE(openssl_private_encrypt,		arginfo_openssl_private_encrypt)
538 	PHP_FE(openssl_private_decrypt,		arginfo_openssl_private_decrypt)
539 	PHP_FE(openssl_public_encrypt,		arginfo_openssl_public_encrypt)
540 	PHP_FE(openssl_public_decrypt,		arginfo_openssl_public_decrypt)
541 
542 	PHP_FE(openssl_get_md_methods,		arginfo_openssl_get_md_methods)
543 	PHP_FE(openssl_get_cipher_methods,	arginfo_openssl_get_cipher_methods)
544 #ifdef HAVE_EVP_PKEY_EC
545 	PHP_FE(openssl_get_curve_names,		arginfo_openssl_get_curve_names)
546 #endif
547 
548 	PHP_FE(openssl_dh_compute_key,		arginfo_openssl_dh_compute_key)
549 	PHP_FE(openssl_pkey_derive,		arginfo_openssl_pkey_derive)
550 
551 	PHP_FE(openssl_random_pseudo_bytes,	arginfo_openssl_random_pseudo_bytes)
552 	PHP_FE(openssl_error_string, arginfo_openssl_error_string)
553 	PHP_FE_END
554 };
555 /* }}} */
556 
557 /* {{{ openssl_module_entry
558  */
559 zend_module_entry openssl_module_entry = {
560 	STANDARD_MODULE_HEADER,
561 	"openssl",
562 	openssl_functions,
563 	PHP_MINIT(openssl),
564 	PHP_MSHUTDOWN(openssl),
565 	NULL,
566 	NULL,
567 	PHP_MINFO(openssl),
568 	PHP_OPENSSL_VERSION,
569 	PHP_MODULE_GLOBALS(openssl),
570 	PHP_GINIT(openssl),
571 	PHP_GSHUTDOWN(openssl),
572 	NULL,
573 	STANDARD_MODULE_PROPERTIES_EX
574 };
575 /* }}} */
576 
577 #ifdef COMPILE_DL_OPENSSL
ZEND_GET_MODULE(openssl)578 ZEND_GET_MODULE(openssl)
579 #endif
580 
581 /* {{{ OpenSSL compatibility functions and macros */
582 #if PHP_OPENSSL_API_VERSION < 0x10100
583 #define EVP_PKEY_get0_RSA(_pkey) _pkey->pkey.rsa
584 #define EVP_PKEY_get0_DH(_pkey) _pkey->pkey.dh
585 #define EVP_PKEY_get0_DSA(_pkey) _pkey->pkey.dsa
586 #define EVP_PKEY_get0_EC_KEY(_pkey) _pkey->pkey.ec
587 
588 static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
589 {
590 	r->n = n;
591 	r->e = e;
592 	r->d = d;
593 
594 	return 1;
595 }
596 
RSA_set0_factors(RSA * r,BIGNUM * p,BIGNUM * q)597 static int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q)
598 {
599 	r->p = p;
600 	r->q = q;
601 
602 	return 1;
603 }
604 
RSA_set0_crt_params(RSA * r,BIGNUM * dmp1,BIGNUM * dmq1,BIGNUM * iqmp)605 static int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp)
606 {
607 	r->dmp1 = dmp1;
608 	r->dmq1 = dmq1;
609 	r->iqmp = iqmp;
610 
611 	return 1;
612 }
613 
RSA_get0_key(const RSA * r,const BIGNUM ** n,const BIGNUM ** e,const BIGNUM ** d)614 static void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
615 {
616 	*n = r->n;
617 	*e = r->e;
618 	*d = r->d;
619 }
620 
RSA_get0_factors(const RSA * r,const BIGNUM ** p,const BIGNUM ** q)621 static void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
622 {
623 	*p = r->p;
624 	*q = r->q;
625 }
626 
RSA_get0_crt_params(const RSA * r,const BIGNUM ** dmp1,const BIGNUM ** dmq1,const BIGNUM ** iqmp)627 static void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp)
628 {
629 	*dmp1 = r->dmp1;
630 	*dmq1 = r->dmq1;
631 	*iqmp = r->iqmp;
632 }
633 
DH_get0_pqg(const DH * dh,const BIGNUM ** p,const BIGNUM ** q,const BIGNUM ** g)634 static void DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
635 {
636 	*p = dh->p;
637 	*q = dh->q;
638 	*g = dh->g;
639 }
640 
DH_set0_pqg(DH * dh,BIGNUM * p,BIGNUM * q,BIGNUM * g)641 static int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
642 {
643 	dh->p = p;
644 	dh->q = q;
645 	dh->g = g;
646 
647 	return 1;
648 }
649 
DH_get0_key(const DH * dh,const BIGNUM ** pub_key,const BIGNUM ** priv_key)650 static void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
651 {
652 	*pub_key = dh->pub_key;
653 	*priv_key = dh->priv_key;
654 }
655 
DH_set0_key(DH * dh,BIGNUM * pub_key,BIGNUM * priv_key)656 static int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
657 {
658 	dh->pub_key = pub_key;
659 	dh->priv_key = priv_key;
660 
661 	return 1;
662 }
663 
DSA_get0_pqg(const DSA * d,const BIGNUM ** p,const BIGNUM ** q,const BIGNUM ** g)664 static void DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
665 {
666 	*p = d->p;
667 	*q = d->q;
668 	*g = d->g;
669 }
670 
DSA_set0_pqg(DSA * d,BIGNUM * p,BIGNUM * q,BIGNUM * g)671 int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
672 {
673 	d->p = p;
674 	d->q = q;
675 	d->g = g;
676 
677 	return 1;
678 }
679 
DSA_get0_key(const DSA * d,const BIGNUM ** pub_key,const BIGNUM ** priv_key)680 static void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key)
681 {
682 	*pub_key = d->pub_key;
683 	*priv_key = d->priv_key;
684 }
685 
DSA_set0_key(DSA * d,BIGNUM * pub_key,BIGNUM * priv_key)686 int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
687 {
688 	d->pub_key = pub_key;
689 	d->priv_key = priv_key;
690 
691 	return 1;
692 }
693 
ASN1_STRING_get0_data(const ASN1_STRING * asn1)694 static const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1)
695 {
696 	return M_ASN1_STRING_data(asn1);
697 }
698 
699 #if PHP_OPENSSL_API_VERSION < 0x10002
700 
X509_get_signature_nid(const X509 * x)701 static int X509_get_signature_nid(const X509 *x)
702 {
703 	return OBJ_obj2nid(x->sig_alg->algorithm);
704 }
705 
706 #endif
707 
708 #define OpenSSL_version		SSLeay_version
709 #define OPENSSL_VERSION		SSLEAY_VERSION
710 #define X509_getm_notBefore	X509_get_notBefore
711 #define X509_getm_notAfter	X509_get_notAfter
712 #define EVP_CIPHER_CTX_reset	EVP_CIPHER_CTX_cleanup
713 
714 #endif
715 /* }}} */
716 
717 /* number conversion flags checks */
718 #define PHP_OPENSSL_CHECK_NUMBER_CONVERSION(_cond, _name) \
719 	do { \
720 		if (_cond) { \
721 			php_error_docref(NULL, E_WARNING, #_name" is too long"); \
722 			RETURN_FALSE; \
723 		} \
724 	} while(0)
725 /* number conversion flags checks */
726 #define PHP_OPENSSL_CHECK_NUMBER_CONVERSION_NORET(_cond, _name) \
727 	do { \
728 		if (_cond) { \
729 			php_error_docref(NULL, E_WARNING, #_name" is too long"); \
730 			return NULL; \
731 		} \
732 	} while(0)
733 /* check if size_t can be safely casted to int */
734 #define PHP_OPENSSL_CHECK_SIZE_T_TO_INT(_var, _name) \
735 	PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_SIZE_T_INT_OVFL(_var), _name)
736 /* check if size_t can be safely casted to int */
737 #define PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NORET(_var, _name) \
738 	PHP_OPENSSL_CHECK_NUMBER_CONVERSION_NORET(ZEND_SIZE_T_INT_OVFL(_var), _name)
739 /* check if size_t can be safely casted to unsigned int */
740 #define PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(_var, _name) \
741 	PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_SIZE_T_UINT_OVFL(_var), _name)
742 /* check if long can be safely casted to int */
743 #define PHP_OPENSSL_CHECK_LONG_TO_INT(_var, _name) \
744 	PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_LONG_EXCEEDS_INT(_var), _name)
745 /* check if long can be safely casted to int */
746 #define PHP_OPENSSL_CHECK_LONG_TO_INT_NORET(_var, _name) \
747 	PHP_OPENSSL_CHECK_NUMBER_CONVERSION_NORET(ZEND_LONG_EXCEEDS_INT(_var), _name)
748 
749 /* {{{ php_openssl_store_errors */
php_openssl_store_errors()750 void php_openssl_store_errors()
751 {
752 	struct php_openssl_errors *errors;
753 	int error_code = ERR_get_error();
754 
755 	if (!error_code) {
756 		return;
757 	}
758 
759 	if (!OPENSSL_G(errors)) {
760 		OPENSSL_G(errors) = pecalloc(1, sizeof(struct php_openssl_errors), 1);
761 	}
762 
763 	errors = OPENSSL_G(errors);
764 
765 	do {
766 		errors->top = (errors->top + 1) % ERR_NUM_ERRORS;
767 		if (errors->top == errors->bottom) {
768 			errors->bottom = (errors->bottom + 1) % ERR_NUM_ERRORS;
769 		}
770 		errors->buffer[errors->top] = error_code;
771 	} while ((error_code = ERR_get_error()));
772 
773 }
774 /* }}} */
775 
776 static int le_key;
777 static int le_x509;
778 static int le_csr;
779 static int ssl_stream_data_index;
780 
php_openssl_get_x509_list_id(void)781 int php_openssl_get_x509_list_id(void) /* {{{ */
782 {
783 	return le_x509;
784 }
785 /* }}} */
786 
787 /* {{{ resource destructors */
php_openssl_pkey_free(zend_resource * rsrc)788 static void php_openssl_pkey_free(zend_resource *rsrc)
789 {
790 	EVP_PKEY *pkey = (EVP_PKEY *)rsrc->ptr;
791 
792 	assert(pkey != NULL);
793 
794 	EVP_PKEY_free(pkey);
795 }
796 
php_openssl_x509_free(zend_resource * rsrc)797 static void php_openssl_x509_free(zend_resource *rsrc)
798 {
799 	X509 *x509 = (X509 *)rsrc->ptr;
800 	X509_free(x509);
801 }
802 
php_openssl_csr_free(zend_resource * rsrc)803 static void php_openssl_csr_free(zend_resource *rsrc)
804 {
805 	X509_REQ * csr = (X509_REQ*)rsrc->ptr;
806 	X509_REQ_free(csr);
807 }
808 /* }}} */
809 
810 /* {{{ openssl open_basedir check */
php_openssl_open_base_dir_chk(char * filename)811 inline static int php_openssl_open_base_dir_chk(char *filename)
812 {
813 	if (php_check_open_basedir(filename)) {
814 		return -1;
815 	}
816 
817 	return 0;
818 }
819 /* }}} */
820 
php_openssl_get_stream_from_ssl_handle(const SSL * ssl)821 php_stream* php_openssl_get_stream_from_ssl_handle(const SSL *ssl)
822 {
823 	return (php_stream*)SSL_get_ex_data(ssl, ssl_stream_data_index);
824 }
825 
php_openssl_get_ssl_stream_data_index()826 int php_openssl_get_ssl_stream_data_index()
827 {
828 	return ssl_stream_data_index;
829 }
830 
831 /* openssl -> PHP "bridging" */
832 /* true global; readonly after module startup */
833 static char default_ssl_conf_filename[MAXPATHLEN];
834 
835 struct php_x509_request { /* {{{ */
836 	LHASH_OF(CONF_VALUE) * global_config;	/* Global SSL config */
837 	LHASH_OF(CONF_VALUE) * req_config;		/* SSL config for this request */
838 	const EVP_MD * md_alg;
839 	const EVP_MD * digest;
840 	char	* section_name,
841 			* config_filename,
842 			* digest_name,
843 			* extensions_section,
844 			* request_extensions_section;
845 	int priv_key_bits;
846 	int priv_key_type;
847 
848 	int priv_key_encrypt;
849 
850 #ifdef HAVE_EVP_PKEY_EC
851 	int curve_name;
852 #endif
853 
854 	EVP_PKEY * priv_key;
855 
856 	const EVP_CIPHER * priv_key_encrypt_cipher;
857 };
858 /* }}} */
859 
860 static X509 * php_openssl_x509_from_zval(zval * val, int makeresource, zend_resource **resourceval);
861 static EVP_PKEY * php_openssl_evp_from_zval(
862 		zval * val, int public_key, char *passphrase, size_t passphrase_len,
863 		int makeresource, zend_resource **resourceval);
864 static int php_openssl_is_private_key(EVP_PKEY* pkey);
865 static X509_STORE * php_openssl_setup_verify(zval * calist);
866 static STACK_OF(X509) * php_openssl_load_all_certs_from_file(char *certfile);
867 static X509_REQ * php_openssl_csr_from_zval(zval * val, int makeresource, zend_resource ** resourceval);
868 static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req);
869 
php_openssl_add_assoc_name_entry(zval * val,char * key,X509_NAME * name,int shortname)870 static void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname) /* {{{ */
871 {
872 	zval *data;
873 	zval subitem, tmp;
874 	int i;
875 	char *sname;
876 	int nid;
877 	X509_NAME_ENTRY * ne;
878 	ASN1_STRING * str = NULL;
879 	ASN1_OBJECT * obj;
880 
881 	if (key != NULL) {
882 		array_init(&subitem);
883 	} else {
884 		ZVAL_COPY_VALUE(&subitem, val);
885 	}
886 
887 	for (i = 0; i < X509_NAME_entry_count(name); i++) {
888 		const unsigned char *to_add = NULL;
889 		int to_add_len = 0;
890 		unsigned char *to_add_buf = NULL;
891 
892 		ne = X509_NAME_get_entry(name, i);
893 		obj = X509_NAME_ENTRY_get_object(ne);
894 		nid = OBJ_obj2nid(obj);
895 
896 		if (shortname) {
897 			sname = (char *) OBJ_nid2sn(nid);
898 		} else {
899 			sname = (char *) OBJ_nid2ln(nid);
900 		}
901 
902 		str = X509_NAME_ENTRY_get_data(ne);
903 		if (ASN1_STRING_type(str) != V_ASN1_UTF8STRING) {
904 			/* ASN1_STRING_to_UTF8(3): The converted data is copied into a newly allocated buffer */
905 			to_add_len = ASN1_STRING_to_UTF8(&to_add_buf, str);
906 			to_add = to_add_buf;
907 		} else {
908 			/* ASN1_STRING_get0_data(3): Since this is an internal pointer it should not be freed or modified in any way */
909 			to_add = ASN1_STRING_get0_data(str);
910 			to_add_len = ASN1_STRING_length(str);
911 		}
912 
913 		if (to_add_len != -1) {
914 			if ((data = zend_hash_str_find(Z_ARRVAL(subitem), sname, strlen(sname))) != NULL) {
915 				if (Z_TYPE_P(data) == IS_ARRAY) {
916 					add_next_index_stringl(data, (const char *)to_add, to_add_len);
917 				} else if (Z_TYPE_P(data) == IS_STRING) {
918 					array_init(&tmp);
919 					add_next_index_str(&tmp, zend_string_copy(Z_STR_P(data)));
920 					add_next_index_stringl(&tmp, (const char *)to_add, to_add_len);
921 					zend_hash_str_update(Z_ARRVAL(subitem), sname, strlen(sname), &tmp);
922 				}
923 			} else {
924 				/* it might be better to expand it and pass zval from ZVAL_STRING
925 				 * to zend_symtable_str_update so we do not silently drop const
926 				 * but we need a test to cover this part first */
927 				add_assoc_stringl(&subitem, sname, (char *)to_add, to_add_len);
928 			}
929 		} else {
930 			php_openssl_store_errors();
931 		}
932 
933 		if (to_add_buf != NULL) {
934 			OPENSSL_free(to_add_buf);
935 		}
936 	}
937 
938 	if (key != NULL) {
939 		zend_hash_str_update(Z_ARRVAL_P(val), key, strlen(key), &subitem);
940 	}
941 }
942 /* }}} */
943 
php_openssl_add_assoc_asn1_string(zval * val,char * key,ASN1_STRING * str)944 static void php_openssl_add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str) /* {{{ */
945 {
946 	add_assoc_stringl(val, key, (char *)str->data, str->length);
947 }
948 /* }}} */
949 
php_openssl_asn1_time_to_time_t(ASN1_UTCTIME * timestr)950 static time_t php_openssl_asn1_time_to_time_t(ASN1_UTCTIME * timestr) /* {{{ */
951 {
952 /*
953 	This is how the time string is formatted:
954 
955    snprintf(p, sizeof(p), "%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100,
956 	  ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
957 */
958 
959 	time_t ret;
960 	struct tm thetime;
961 	char * strbuf;
962 	char * thestr;
963 	long gmadjust = 0;
964 	size_t timestr_len;
965 
966 	if (ASN1_STRING_type(timestr) != V_ASN1_UTCTIME && ASN1_STRING_type(timestr) != V_ASN1_GENERALIZEDTIME) {
967 		php_error_docref(NULL, E_WARNING, "illegal ASN1 data type for timestamp");
968 		return (time_t)-1;
969 	}
970 
971 	timestr_len = (size_t)ASN1_STRING_length(timestr);
972 
973 	if (timestr_len != strlen((const char *)ASN1_STRING_get0_data(timestr))) {
974 		php_error_docref(NULL, E_WARNING, "illegal length in timestamp");
975 		return (time_t)-1;
976 	}
977 
978 	if (timestr_len < 13 && timestr_len != 11) {
979 		php_error_docref(NULL, E_WARNING, "unable to parse time string %s correctly", timestr->data);
980 		return (time_t)-1;
981 	}
982 
983 	if (ASN1_STRING_type(timestr) == V_ASN1_GENERALIZEDTIME && timestr_len < 15) {
984 		php_error_docref(NULL, E_WARNING, "unable to parse time string %s correctly", timestr->data);
985 		return (time_t)-1;
986 	}
987 
988 	strbuf = estrdup((const char *)ASN1_STRING_get0_data(timestr));
989 
990 	memset(&thetime, 0, sizeof(thetime));
991 
992 	/* we work backwards so that we can use atoi more easily */
993 
994 	thestr = strbuf + timestr_len - 3;
995 
996 	if (timestr_len == 11) {
997 		thetime.tm_sec = 0;
998 	} else {
999 		thetime.tm_sec = atoi(thestr);
1000 		*thestr = '\0';
1001 		thestr -= 2;
1002 	}
1003 	thetime.tm_min = atoi(thestr);
1004 	*thestr = '\0';
1005 	thestr -= 2;
1006 	thetime.tm_hour = atoi(thestr);
1007 	*thestr = '\0';
1008 	thestr -= 2;
1009 	thetime.tm_mday = atoi(thestr);
1010 	*thestr = '\0';
1011 	thestr -= 2;
1012 	thetime.tm_mon = atoi(thestr)-1;
1013 
1014 	*thestr = '\0';
1015 	if( ASN1_STRING_type(timestr) == V_ASN1_UTCTIME ) {
1016 		thestr -= 2;
1017 		thetime.tm_year = atoi(thestr);
1018 
1019 		if (thetime.tm_year < 68) {
1020 			thetime.tm_year += 100;
1021 		}
1022 	} else if( ASN1_STRING_type(timestr) == V_ASN1_GENERALIZEDTIME ) {
1023 		thestr -= 4;
1024 		thetime.tm_year = atoi(thestr) - 1900;
1025 	}
1026 
1027 
1028 	thetime.tm_isdst = -1;
1029 	ret = mktime(&thetime);
1030 
1031 #if HAVE_STRUCT_TM_TM_GMTOFF
1032 	gmadjust = thetime.tm_gmtoff;
1033 #else
1034 	/*
1035 	** If correcting for daylight savings time, we set the adjustment to
1036 	** the value of timezone - 3600 seconds. Otherwise, we need to overcorrect and
1037 	** set the adjustment to the main timezone + 3600 seconds.
1038 	*/
1039 	gmadjust = -(thetime.tm_isdst ? (long)timezone - 3600 : (long)timezone);
1040 #endif
1041 	ret += gmadjust;
1042 
1043 	efree(strbuf);
1044 
1045 	return ret;
1046 }
1047 /* }}} */
1048 
php_openssl_config_check_syntax(const char * section_label,const char * config_filename,const char * section,LHASH_OF (CONF_VALUE)* config)1049 static inline int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, LHASH_OF(CONF_VALUE) * config) /* {{{ */
1050 {
1051 	X509V3_CTX ctx;
1052 
1053 	X509V3_set_ctx_test(&ctx);
1054 	X509V3_set_conf_lhash(&ctx, config);
1055 	if (!X509V3_EXT_add_conf(config, &ctx, (char *)section, NULL)) {
1056 		php_openssl_store_errors();
1057 		php_error_docref(NULL, E_WARNING, "Error loading %s section %s of %s",
1058 				section_label,
1059 				section,
1060 				config_filename);
1061 		return FAILURE;
1062 	}
1063 	return SUCCESS;
1064 }
1065 /* }}} */
1066 
php_openssl_conf_get_string(LHASH_OF (CONF_VALUE)* conf,const char * group,const char * name)1067 static char *php_openssl_conf_get_string(
1068 		LHASH_OF(CONF_VALUE) *conf, const char *group, const char *name) {
1069 	char *str = CONF_get_string(conf, group, name);
1070 	if (str == NULL) {
1071 		/* OpenSSL reports an error if a configuration value is not found.
1072 		 * However, we don't want to generate errors for optional configuration. */
1073 		ERR_clear_error();
1074 	}
1075 	return str;
1076 }
1077 
php_openssl_add_oid_section(struct php_x509_request * req)1078 static int php_openssl_add_oid_section(struct php_x509_request * req) /* {{{ */
1079 {
1080 	char * str;
1081 	STACK_OF(CONF_VALUE) * sktmp;
1082 	CONF_VALUE * cnf;
1083 	int i;
1084 
1085 	str = php_openssl_conf_get_string(req->req_config, NULL, "oid_section");
1086 	if (str == NULL) {
1087 		return SUCCESS;
1088 	}
1089 	sktmp = CONF_get_section(req->req_config, str);
1090 	if (sktmp == NULL) {
1091 		php_openssl_store_errors();
1092 		php_error_docref(NULL, E_WARNING, "problem loading oid section %s", str);
1093 		return FAILURE;
1094 	}
1095 	for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++) {
1096 		cnf = sk_CONF_VALUE_value(sktmp, i);
1097 		if (OBJ_sn2nid(cnf->name) == NID_undef && OBJ_ln2nid(cnf->name) == NID_undef &&
1098 				OBJ_create(cnf->value, cnf->name, cnf->name) == NID_undef) {
1099 			php_openssl_store_errors();
1100 			php_error_docref(NULL, E_WARNING, "problem creating object %s=%s", cnf->name, cnf->value);
1101 			return FAILURE;
1102 		}
1103 	}
1104 	return SUCCESS;
1105 }
1106 /* }}} */
1107 
1108 #define PHP_SSL_REQ_INIT(req)		memset(req, 0, sizeof(*req))
1109 #define PHP_SSL_REQ_DISPOSE(req)	php_openssl_dispose_config(req)
1110 #define PHP_SSL_REQ_PARSE(req, zval)	php_openssl_parse_config(req, zval)
1111 
1112 #define PHP_SSL_CONFIG_SYNTAX_CHECK(var) if (req->var && php_openssl_config_check_syntax(#var, \
1113 			req->config_filename, req->var, req->req_config) == FAILURE) return FAILURE
1114 
1115 #define SET_OPTIONAL_STRING_ARG(key, varname, defval)	\
1116 	do { \
1117 		if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_STRING) { \
1118 			varname = Z_STRVAL_P(item); \
1119 		} else { \
1120 			varname = defval; \
1121 			if (varname == NULL) { \
1122 				php_openssl_store_errors(); \
1123 			} \
1124 		} \
1125 	} while(0)
1126 
1127 #define SET_OPTIONAL_LONG_ARG(key, varname, defval)	\
1128 	if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_LONG) \
1129 		varname = (int)Z_LVAL_P(item); \
1130 	else \
1131 		varname = defval
1132 
1133 static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo);
1134 
1135 /* {{{ strip line endings from spkac */
php_openssl_spki_cleanup(const char * src,char * dest)1136 static int php_openssl_spki_cleanup(const char *src, char *dest)
1137 {
1138 	int removed = 0;
1139 
1140 	while (*src) {
1141 		if (*src != '\n' && *src != '\r') {
1142 			*dest++ = *src;
1143 		} else {
1144 			++removed;
1145 		}
1146 		++src;
1147 	}
1148 	*dest = 0;
1149 	return removed;
1150 }
1151 /* }}} */
1152 
php_openssl_parse_config(struct php_x509_request * req,zval * optional_args)1153 static int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args) /* {{{ */
1154 {
1155 	char * str;
1156 	zval * item;
1157 
1158 	SET_OPTIONAL_STRING_ARG("config", req->config_filename, default_ssl_conf_filename);
1159 	SET_OPTIONAL_STRING_ARG("config_section_name", req->section_name, "req");
1160 	req->global_config = CONF_load(NULL, default_ssl_conf_filename, NULL);
1161 	if (req->global_config == NULL) {
1162 		php_openssl_store_errors();
1163 	}
1164 	req->req_config = CONF_load(NULL, req->config_filename, NULL);
1165 	if (req->req_config == NULL) {
1166 		php_openssl_store_errors();
1167 		return FAILURE;
1168 	}
1169 
1170 	/* read in the oids */
1171 	str = php_openssl_conf_get_string(req->req_config, NULL, "oid_file");
1172 	if (str != NULL && !php_openssl_open_base_dir_chk(str)) {
1173 		BIO *oid_bio = BIO_new_file(str, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
1174 		if (oid_bio) {
1175 			OBJ_create_objects(oid_bio);
1176 			BIO_free(oid_bio);
1177 			php_openssl_store_errors();
1178 		}
1179 	}
1180 	if (php_openssl_add_oid_section(req) == FAILURE) {
1181 		return FAILURE;
1182 	}
1183 	SET_OPTIONAL_STRING_ARG("digest_alg", req->digest_name,
1184 		php_openssl_conf_get_string(req->req_config, req->section_name, "default_md"));
1185 	SET_OPTIONAL_STRING_ARG("x509_extensions", req->extensions_section,
1186 		php_openssl_conf_get_string(req->req_config, req->section_name, "x509_extensions"));
1187 	SET_OPTIONAL_STRING_ARG("req_extensions", req->request_extensions_section,
1188 		php_openssl_conf_get_string(req->req_config, req->section_name, "req_extensions"));
1189 	SET_OPTIONAL_LONG_ARG("private_key_bits", req->priv_key_bits,
1190 		CONF_get_number(req->req_config, req->section_name, "default_bits"));
1191 
1192 	SET_OPTIONAL_LONG_ARG("private_key_type", req->priv_key_type, OPENSSL_KEYTYPE_DEFAULT);
1193 
1194 	if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), "encrypt_key", sizeof("encrypt_key")-1)) != NULL) {
1195 		req->priv_key_encrypt = Z_TYPE_P(item) == IS_TRUE ? 1 : 0;
1196 	} else {
1197 		str = php_openssl_conf_get_string(req->req_config, req->section_name, "encrypt_rsa_key");
1198 		if (str == NULL) {
1199 			str = php_openssl_conf_get_string(req->req_config, req->section_name, "encrypt_key");
1200 		}
1201 		if (str != NULL && strcmp(str, "no") == 0) {
1202 			req->priv_key_encrypt = 0;
1203 		} else {
1204 			req->priv_key_encrypt = 1;
1205 		}
1206 	}
1207 
1208 	if (req->priv_key_encrypt &&
1209 		optional_args &&
1210 		(item = zend_hash_str_find(Z_ARRVAL_P(optional_args), "encrypt_key_cipher", sizeof("encrypt_key_cipher")-1)) != NULL &&
1211 		Z_TYPE_P(item) == IS_LONG
1212 	) {
1213 		zend_long cipher_algo = Z_LVAL_P(item);
1214 		const EVP_CIPHER* cipher = php_openssl_get_evp_cipher_from_algo(cipher_algo);
1215 		if (cipher == NULL) {
1216 			php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm for private key.");
1217 			return FAILURE;
1218 		} else {
1219 			req->priv_key_encrypt_cipher = cipher;
1220 		}
1221 	} else {
1222 		req->priv_key_encrypt_cipher = NULL;
1223 	}
1224 
1225 	/* digest alg */
1226 	if (req->digest_name == NULL) {
1227 		req->digest_name = php_openssl_conf_get_string(req->req_config, req->section_name, "default_md");
1228 	}
1229 	if (req->digest_name != NULL) {
1230 		req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name);
1231 	}
1232 	if (req->md_alg == NULL) {
1233 		req->md_alg = req->digest = EVP_sha1();
1234 		php_openssl_store_errors();
1235 	}
1236 
1237 	PHP_SSL_CONFIG_SYNTAX_CHECK(extensions_section);
1238 #ifdef HAVE_EVP_PKEY_EC
1239 	/* set the ec group curve name */
1240 	req->curve_name = NID_undef;
1241 	if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), "curve_name", sizeof("curve_name")-1)) != NULL
1242 		&& Z_TYPE_P(item) == IS_STRING) {
1243 		req->curve_name = OBJ_sn2nid(Z_STRVAL_P(item));
1244 		if (req->curve_name == NID_undef) {
1245 			php_error_docref(NULL, E_WARNING, "Unknown elliptic curve (short) name %s", Z_STRVAL_P(item));
1246 			return FAILURE;
1247 		}
1248 	}
1249 #endif
1250 
1251 	/* set the string mask */
1252 	str = php_openssl_conf_get_string(req->req_config, req->section_name, "string_mask");
1253 	if (str != NULL && !ASN1_STRING_set_default_mask_asc(str)) {
1254 		php_error_docref(NULL, E_WARNING, "Invalid global string mask setting %s", str);
1255 		return FAILURE;
1256 	}
1257 
1258 	PHP_SSL_CONFIG_SYNTAX_CHECK(request_extensions_section);
1259 
1260 	return SUCCESS;
1261 }
1262 /* }}} */
1263 
php_openssl_dispose_config(struct php_x509_request * req)1264 static void php_openssl_dispose_config(struct php_x509_request * req) /* {{{ */
1265 {
1266 	if (req->priv_key) {
1267 		EVP_PKEY_free(req->priv_key);
1268 		req->priv_key = NULL;
1269 	}
1270 	if (req->global_config) {
1271 		CONF_free(req->global_config);
1272 		req->global_config = NULL;
1273 	}
1274 	if (req->req_config) {
1275 		CONF_free(req->req_config);
1276 		req->req_config = NULL;
1277 	}
1278 }
1279 /* }}} */
1280 
1281 #if defined(PHP_WIN32) || PHP_OPENSSL_API_VERSION >= 0x10100
1282 #define PHP_OPENSSL_RAND_ADD_TIME() ((void) 0)
1283 #else
1284 #define PHP_OPENSSL_RAND_ADD_TIME() php_openssl_rand_add_timeval()
1285 
php_openssl_rand_add_timeval()1286 static inline void php_openssl_rand_add_timeval()  /* {{{ */
1287 {
1288 	struct timeval tv;
1289 
1290 	gettimeofday(&tv, NULL);
1291 	RAND_add(&tv, sizeof(tv), 0.0);
1292 }
1293 /* }}} */
1294 
1295 #endif
1296 
php_openssl_load_rand_file(const char * file,int * egdsocket,int * seeded)1297 static int php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded) /* {{{ */
1298 {
1299 	char buffer[MAXPATHLEN];
1300 
1301 	*egdsocket = 0;
1302 	*seeded = 0;
1303 
1304 	if (file == NULL) {
1305 		file = RAND_file_name(buffer, sizeof(buffer));
1306 #ifdef HAVE_RAND_EGD
1307 	} else if (RAND_egd(file) > 0) {
1308 		/* if the given filename is an EGD socket, don't
1309 		 * write anything back to it */
1310 		*egdsocket = 1;
1311 		return SUCCESS;
1312 #endif
1313 	}
1314 	if (file == NULL || !RAND_load_file(file, -1)) {
1315 		if (RAND_status() == 0) {
1316 			php_openssl_store_errors();
1317 			php_error_docref(NULL, E_WARNING, "unable to load random state; not enough random data!");
1318 			return FAILURE;
1319 		}
1320 		return FAILURE;
1321 	}
1322 	*seeded = 1;
1323 	return SUCCESS;
1324 }
1325 /* }}} */
1326 
php_openssl_write_rand_file(const char * file,int egdsocket,int seeded)1327 static int php_openssl_write_rand_file(const char * file, int egdsocket, int seeded) /* {{{ */
1328 {
1329 	char buffer[MAXPATHLEN];
1330 
1331 
1332 	if (egdsocket || !seeded) {
1333 		/* if we did not manage to read the seed file, we should not write
1334 		 * a low-entropy seed file back */
1335 		return FAILURE;
1336 	}
1337 	if (file == NULL) {
1338 		file = RAND_file_name(buffer, sizeof(buffer));
1339 	}
1340 	PHP_OPENSSL_RAND_ADD_TIME();
1341 	if (file == NULL || !RAND_write_file(file)) {
1342 		php_openssl_store_errors();
1343 		php_error_docref(NULL, E_WARNING, "unable to write random state");
1344 		return FAILURE;
1345 	}
1346 	return SUCCESS;
1347 }
1348 /* }}} */
1349 
php_openssl_get_evp_md_from_algo(zend_long algo)1350 static EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo) { /* {{{ */
1351 	EVP_MD *mdtype;
1352 
1353 	switch (algo) {
1354 		case OPENSSL_ALGO_SHA1:
1355 			mdtype = (EVP_MD *) EVP_sha1();
1356 			break;
1357 		case OPENSSL_ALGO_MD5:
1358 			mdtype = (EVP_MD *) EVP_md5();
1359 			break;
1360 		case OPENSSL_ALGO_MD4:
1361 			mdtype = (EVP_MD *) EVP_md4();
1362 			break;
1363 #ifdef HAVE_OPENSSL_MD2_H
1364 		case OPENSSL_ALGO_MD2:
1365 			mdtype = (EVP_MD *) EVP_md2();
1366 			break;
1367 #endif
1368 #if PHP_OPENSSL_API_VERSION < 0x10100
1369 		case OPENSSL_ALGO_DSS1:
1370 			mdtype = (EVP_MD *) EVP_dss1();
1371 			break;
1372 #endif
1373 		case OPENSSL_ALGO_SHA224:
1374 			mdtype = (EVP_MD *) EVP_sha224();
1375 			break;
1376 		case OPENSSL_ALGO_SHA256:
1377 			mdtype = (EVP_MD *) EVP_sha256();
1378 			break;
1379 		case OPENSSL_ALGO_SHA384:
1380 			mdtype = (EVP_MD *) EVP_sha384();
1381 			break;
1382 		case OPENSSL_ALGO_SHA512:
1383 			mdtype = (EVP_MD *) EVP_sha512();
1384 			break;
1385 		case OPENSSL_ALGO_RMD160:
1386 			mdtype = (EVP_MD *) EVP_ripemd160();
1387 			break;
1388 		default:
1389 			return NULL;
1390 			break;
1391 	}
1392 	return mdtype;
1393 }
1394 /* }}} */
1395 
php_openssl_get_evp_cipher_from_algo(zend_long algo)1396 static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo) { /* {{{ */
1397 	switch (algo) {
1398 #ifndef OPENSSL_NO_RC2
1399 		case PHP_OPENSSL_CIPHER_RC2_40:
1400 			return EVP_rc2_40_cbc();
1401 			break;
1402 		case PHP_OPENSSL_CIPHER_RC2_64:
1403 			return EVP_rc2_64_cbc();
1404 			break;
1405 		case PHP_OPENSSL_CIPHER_RC2_128:
1406 			return EVP_rc2_cbc();
1407 			break;
1408 #endif
1409 
1410 #ifndef OPENSSL_NO_DES
1411 		case PHP_OPENSSL_CIPHER_DES:
1412 			return EVP_des_cbc();
1413 			break;
1414 		case PHP_OPENSSL_CIPHER_3DES:
1415 			return EVP_des_ede3_cbc();
1416 			break;
1417 #endif
1418 
1419 #ifndef OPENSSL_NO_AES
1420 		case PHP_OPENSSL_CIPHER_AES_128_CBC:
1421 			return EVP_aes_128_cbc();
1422 			break;
1423 		case PHP_OPENSSL_CIPHER_AES_192_CBC:
1424 			return EVP_aes_192_cbc();
1425 			break;
1426 		case PHP_OPENSSL_CIPHER_AES_256_CBC:
1427 			return EVP_aes_256_cbc();
1428 			break;
1429 #endif
1430 
1431 
1432 		default:
1433 			return NULL;
1434 			break;
1435 	}
1436 }
1437 /* }}} */
1438 
1439 /* {{{ INI Settings */
1440 PHP_INI_BEGIN()
1441 	PHP_INI_ENTRY("openssl.cafile", NULL, PHP_INI_PERDIR, NULL)
1442 	PHP_INI_ENTRY("openssl.capath", NULL, PHP_INI_PERDIR, NULL)
PHP_INI_END()1443 PHP_INI_END()
1444 /* }}} */
1445 
1446 /* {{{ PHP_MINIT_FUNCTION
1447  */
1448 PHP_MINIT_FUNCTION(openssl)
1449 {
1450 	char * config_filename;
1451 
1452 	le_key = zend_register_list_destructors_ex(php_openssl_pkey_free, NULL, "OpenSSL key", module_number);
1453 	le_x509 = zend_register_list_destructors_ex(php_openssl_x509_free, NULL, "OpenSSL X.509", module_number);
1454 	le_csr = zend_register_list_destructors_ex(php_openssl_csr_free, NULL, "OpenSSL X.509 CSR", module_number);
1455 
1456 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
1457 	OPENSSL_config(NULL);
1458 	SSL_library_init();
1459 	OpenSSL_add_all_ciphers();
1460 	OpenSSL_add_all_digests();
1461 	OpenSSL_add_all_algorithms();
1462 
1463 #if !defined(OPENSSL_NO_AES) && defined(EVP_CIPH_CCM_MODE) && OPENSSL_VERSION_NUMBER < 0x100020000
1464 	EVP_add_cipher(EVP_aes_128_ccm());
1465 	EVP_add_cipher(EVP_aes_192_ccm());
1466 	EVP_add_cipher(EVP_aes_256_ccm());
1467 #endif
1468 
1469 	SSL_load_error_strings();
1470 #else
1471 	OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
1472 #endif
1473 
1474 	/* register a resource id number with OpenSSL so that we can map SSL -> stream structures in
1475 	 * OpenSSL callbacks */
1476 	ssl_stream_data_index = SSL_get_ex_new_index(0, "PHP stream index", NULL, NULL, NULL);
1477 
1478 	REGISTER_STRING_CONSTANT("OPENSSL_VERSION_TEXT", OPENSSL_VERSION_TEXT, CONST_CS|CONST_PERSISTENT);
1479 	REGISTER_LONG_CONSTANT("OPENSSL_VERSION_NUMBER", OPENSSL_VERSION_NUMBER, CONST_CS|CONST_PERSISTENT);
1480 
1481 	/* purposes for cert purpose checking */
1482 	REGISTER_LONG_CONSTANT("X509_PURPOSE_SSL_CLIENT", X509_PURPOSE_SSL_CLIENT, CONST_CS|CONST_PERSISTENT);
1483 	REGISTER_LONG_CONSTANT("X509_PURPOSE_SSL_SERVER", X509_PURPOSE_SSL_SERVER, CONST_CS|CONST_PERSISTENT);
1484 	REGISTER_LONG_CONSTANT("X509_PURPOSE_NS_SSL_SERVER", X509_PURPOSE_NS_SSL_SERVER, CONST_CS|CONST_PERSISTENT);
1485 	REGISTER_LONG_CONSTANT("X509_PURPOSE_SMIME_SIGN", X509_PURPOSE_SMIME_SIGN, CONST_CS|CONST_PERSISTENT);
1486 	REGISTER_LONG_CONSTANT("X509_PURPOSE_SMIME_ENCRYPT", X509_PURPOSE_SMIME_ENCRYPT, CONST_CS|CONST_PERSISTENT);
1487 	REGISTER_LONG_CONSTANT("X509_PURPOSE_CRL_SIGN", X509_PURPOSE_CRL_SIGN, CONST_CS|CONST_PERSISTENT);
1488 #ifdef X509_PURPOSE_ANY
1489 	REGISTER_LONG_CONSTANT("X509_PURPOSE_ANY", X509_PURPOSE_ANY, CONST_CS|CONST_PERSISTENT);
1490 #endif
1491 
1492 	/* signature algorithm constants */
1493 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA1", OPENSSL_ALGO_SHA1, CONST_CS|CONST_PERSISTENT);
1494 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD5", OPENSSL_ALGO_MD5, CONST_CS|CONST_PERSISTENT);
1495 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD4", OPENSSL_ALGO_MD4, CONST_CS|CONST_PERSISTENT);
1496 #ifdef HAVE_OPENSSL_MD2_H
1497 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD2", OPENSSL_ALGO_MD2, CONST_CS|CONST_PERSISTENT);
1498 #endif
1499 #if PHP_OPENSSL_API_VERSION < 0x10100
1500 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_DSS1", OPENSSL_ALGO_DSS1, CONST_CS|CONST_PERSISTENT);
1501 #endif
1502 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA224", OPENSSL_ALGO_SHA224, CONST_CS|CONST_PERSISTENT);
1503 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA256", OPENSSL_ALGO_SHA256, CONST_CS|CONST_PERSISTENT);
1504 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA384", OPENSSL_ALGO_SHA384, CONST_CS|CONST_PERSISTENT);
1505 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA512", OPENSSL_ALGO_SHA512, CONST_CS|CONST_PERSISTENT);
1506 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_RMD160", OPENSSL_ALGO_RMD160, CONST_CS|CONST_PERSISTENT);
1507 
1508 	/* flags for S/MIME */
1509 	REGISTER_LONG_CONSTANT("PKCS7_DETACHED", PKCS7_DETACHED, CONST_CS|CONST_PERSISTENT);
1510 	REGISTER_LONG_CONSTANT("PKCS7_TEXT", PKCS7_TEXT, CONST_CS|CONST_PERSISTENT);
1511 	REGISTER_LONG_CONSTANT("PKCS7_NOINTERN", PKCS7_NOINTERN, CONST_CS|CONST_PERSISTENT);
1512 	REGISTER_LONG_CONSTANT("PKCS7_NOVERIFY", PKCS7_NOVERIFY, CONST_CS|CONST_PERSISTENT);
1513 	REGISTER_LONG_CONSTANT("PKCS7_NOCHAIN", PKCS7_NOCHAIN, CONST_CS|CONST_PERSISTENT);
1514 	REGISTER_LONG_CONSTANT("PKCS7_NOCERTS", PKCS7_NOCERTS, CONST_CS|CONST_PERSISTENT);
1515 	REGISTER_LONG_CONSTANT("PKCS7_NOATTR", PKCS7_NOATTR, CONST_CS|CONST_PERSISTENT);
1516 	REGISTER_LONG_CONSTANT("PKCS7_BINARY", PKCS7_BINARY, CONST_CS|CONST_PERSISTENT);
1517 	REGISTER_LONG_CONSTANT("PKCS7_NOSIGS", PKCS7_NOSIGS, CONST_CS|CONST_PERSISTENT);
1518 
1519 	REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_PADDING", RSA_PKCS1_PADDING, CONST_CS|CONST_PERSISTENT);
1520 	REGISTER_LONG_CONSTANT("OPENSSL_SSLV23_PADDING", RSA_SSLV23_PADDING, CONST_CS|CONST_PERSISTENT);
1521 	REGISTER_LONG_CONSTANT("OPENSSL_NO_PADDING", RSA_NO_PADDING, CONST_CS|CONST_PERSISTENT);
1522 	REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_OAEP_PADDING", RSA_PKCS1_OAEP_PADDING, CONST_CS|CONST_PERSISTENT);
1523 
1524 	/* Informational stream wrapper constants */
1525 	REGISTER_STRING_CONSTANT("OPENSSL_DEFAULT_STREAM_CIPHERS", OPENSSL_DEFAULT_STREAM_CIPHERS, CONST_CS|CONST_PERSISTENT);
1526 
1527 	/* Ciphers */
1528 #ifndef OPENSSL_NO_RC2
1529 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_40", PHP_OPENSSL_CIPHER_RC2_40, CONST_CS|CONST_PERSISTENT);
1530 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_128", PHP_OPENSSL_CIPHER_RC2_128, CONST_CS|CONST_PERSISTENT);
1531 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_64", PHP_OPENSSL_CIPHER_RC2_64, CONST_CS|CONST_PERSISTENT);
1532 #endif
1533 #ifndef OPENSSL_NO_DES
1534 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_DES", PHP_OPENSSL_CIPHER_DES, CONST_CS|CONST_PERSISTENT);
1535 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_3DES", PHP_OPENSSL_CIPHER_3DES, CONST_CS|CONST_PERSISTENT);
1536 #endif
1537 #ifndef OPENSSL_NO_AES
1538 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_128_CBC", PHP_OPENSSL_CIPHER_AES_128_CBC, CONST_CS|CONST_PERSISTENT);
1539 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_192_CBC", PHP_OPENSSL_CIPHER_AES_192_CBC, CONST_CS|CONST_PERSISTENT);
1540 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_256_CBC", PHP_OPENSSL_CIPHER_AES_256_CBC, CONST_CS|CONST_PERSISTENT);
1541 #endif
1542 
1543 	/* Values for key types */
1544 	REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_RSA", OPENSSL_KEYTYPE_RSA, CONST_CS|CONST_PERSISTENT);
1545 #ifndef NO_DSA
1546 	REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DSA", OPENSSL_KEYTYPE_DSA, CONST_CS|CONST_PERSISTENT);
1547 #endif
1548 	REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DH", OPENSSL_KEYTYPE_DH, CONST_CS|CONST_PERSISTENT);
1549 #ifdef HAVE_EVP_PKEY_EC
1550 	REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_EC", OPENSSL_KEYTYPE_EC, CONST_CS|CONST_PERSISTENT);
1551 #endif
1552 
1553 	REGISTER_LONG_CONSTANT("OPENSSL_RAW_DATA", OPENSSL_RAW_DATA, CONST_CS|CONST_PERSISTENT);
1554 	REGISTER_LONG_CONSTANT("OPENSSL_ZERO_PADDING", OPENSSL_ZERO_PADDING, CONST_CS|CONST_PERSISTENT);
1555 	REGISTER_LONG_CONSTANT("OPENSSL_DONT_ZERO_PAD_KEY", OPENSSL_DONT_ZERO_PAD_KEY, CONST_CS|CONST_PERSISTENT);
1556 
1557 #ifndef OPENSSL_NO_TLSEXT
1558 	/* SNI support included */
1559 	REGISTER_LONG_CONSTANT("OPENSSL_TLSEXT_SERVER_NAME", 1, CONST_CS|CONST_PERSISTENT);
1560 #endif
1561 
1562 	/* Determine default SSL configuration file */
1563 	config_filename = getenv("OPENSSL_CONF");
1564 	if (config_filename == NULL) {
1565 		config_filename = getenv("SSLEAY_CONF");
1566 	}
1567 
1568 	/* default to 'openssl.cnf' if no environment variable is set */
1569 	if (config_filename == NULL) {
1570 		snprintf(default_ssl_conf_filename, sizeof(default_ssl_conf_filename), "%s/%s",
1571 				X509_get_default_cert_area(),
1572 				"openssl.cnf");
1573 	} else {
1574 		strlcpy(default_ssl_conf_filename, config_filename, sizeof(default_ssl_conf_filename));
1575 	}
1576 
1577 	php_stream_xport_register("ssl", php_openssl_ssl_socket_factory);
1578 #ifndef OPENSSL_NO_SSL3
1579 	php_stream_xport_register("sslv3", php_openssl_ssl_socket_factory);
1580 #endif
1581 	php_stream_xport_register("tls", php_openssl_ssl_socket_factory);
1582 	php_stream_xport_register("tlsv1.0", php_openssl_ssl_socket_factory);
1583 	php_stream_xport_register("tlsv1.1", php_openssl_ssl_socket_factory);
1584 	php_stream_xport_register("tlsv1.2", php_openssl_ssl_socket_factory);
1585 #if OPENSSL_VERSION_NUMBER >= 0x10101000
1586 	php_stream_xport_register("tlsv1.3", php_openssl_ssl_socket_factory);
1587 #endif
1588 
1589 	/* override the default tcp socket provider */
1590 	php_stream_xport_register("tcp", php_openssl_ssl_socket_factory);
1591 
1592 	php_register_url_stream_wrapper("https", &php_stream_http_wrapper);
1593 	php_register_url_stream_wrapper("ftps", &php_stream_ftp_wrapper);
1594 
1595 	REGISTER_INI_ENTRIES();
1596 
1597 	return SUCCESS;
1598 }
1599 /* }}} */
1600 
1601 /* {{{ PHP_GINIT_FUNCTION
1602 */
PHP_GINIT_FUNCTION(openssl)1603 PHP_GINIT_FUNCTION(openssl)
1604 {
1605 #if defined(COMPILE_DL_OPENSSL) && defined(ZTS)
1606 	ZEND_TSRMLS_CACHE_UPDATE();
1607 #endif
1608 	openssl_globals->errors = NULL;
1609 }
1610 /* }}} */
1611 
1612 /* {{{ PHP_GSHUTDOWN_FUNCTION
1613 */
PHP_GSHUTDOWN_FUNCTION(openssl)1614 PHP_GSHUTDOWN_FUNCTION(openssl)
1615 {
1616 	if (openssl_globals->errors) {
1617 		pefree(openssl_globals->errors, 1);
1618 	}
1619 }
1620 /* }}} */
1621 
1622 /* {{{ PHP_MINFO_FUNCTION
1623  */
PHP_MINFO_FUNCTION(openssl)1624 PHP_MINFO_FUNCTION(openssl)
1625 {
1626 	php_info_print_table_start();
1627 	php_info_print_table_row(2, "OpenSSL support", "enabled");
1628 	php_info_print_table_row(2, "OpenSSL Library Version", OpenSSL_version(OPENSSL_VERSION));
1629 	php_info_print_table_row(2, "OpenSSL Header Version", OPENSSL_VERSION_TEXT);
1630 	php_info_print_table_row(2, "Openssl default config", default_ssl_conf_filename);
1631 	php_info_print_table_end();
1632 	DISPLAY_INI_ENTRIES();
1633 }
1634 /* }}} */
1635 
1636 /* {{{ PHP_MSHUTDOWN_FUNCTION
1637  */
PHP_MSHUTDOWN_FUNCTION(openssl)1638 PHP_MSHUTDOWN_FUNCTION(openssl)
1639 {
1640 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
1641 	EVP_cleanup();
1642 
1643 	/* prevent accessing locking callback from unloaded extension */
1644 	CRYPTO_set_locking_callback(NULL);
1645 	/* free allocated error strings */
1646 	ERR_free_strings();
1647 	CONF_modules_free();
1648 #endif
1649 
1650 	php_unregister_url_stream_wrapper("https");
1651 	php_unregister_url_stream_wrapper("ftps");
1652 
1653 	php_stream_xport_unregister("ssl");
1654 #ifndef OPENSSL_NO_SSL3
1655 	php_stream_xport_unregister("sslv3");
1656 #endif
1657 	php_stream_xport_unregister("tls");
1658 	php_stream_xport_unregister("tlsv1.0");
1659 	php_stream_xport_unregister("tlsv1.1");
1660 	php_stream_xport_unregister("tlsv1.2");
1661 #if OPENSSL_VERSION_NUMBER >= 0x10101000
1662 	php_stream_xport_unregister("tlsv1.3");
1663 #endif
1664 
1665 	/* reinstate the default tcp handler */
1666 	php_stream_xport_register("tcp", php_stream_generic_socket_factory);
1667 
1668 	UNREGISTER_INI_ENTRIES();
1669 
1670 	return SUCCESS;
1671 }
1672 /* }}} */
1673 
1674 /* {{{ x509 cert functions */
1675 
1676 /* {{{ proto array openssl_get_cert_locations(void)
1677    Retrieve an array mapping available certificate locations */
PHP_FUNCTION(openssl_get_cert_locations)1678 PHP_FUNCTION(openssl_get_cert_locations)
1679 {
1680 	array_init(return_value);
1681 
1682 	add_assoc_string(return_value, "default_cert_file", (char *) X509_get_default_cert_file());
1683 	add_assoc_string(return_value, "default_cert_file_env", (char *) X509_get_default_cert_file_env());
1684 	add_assoc_string(return_value, "default_cert_dir", (char *) X509_get_default_cert_dir());
1685 	add_assoc_string(return_value, "default_cert_dir_env", (char *) X509_get_default_cert_dir_env());
1686 	add_assoc_string(return_value, "default_private_dir", (char *) X509_get_default_private_dir());
1687 	add_assoc_string(return_value, "default_default_cert_area", (char *) X509_get_default_cert_area());
1688 	add_assoc_string(return_value, "ini_cafile",
1689 		zend_ini_string("openssl.cafile", sizeof("openssl.cafile")-1, 0));
1690 	add_assoc_string(return_value, "ini_capath",
1691 		zend_ini_string("openssl.capath", sizeof("openssl.capath")-1, 0));
1692 }
1693 /* }}} */
1694 
1695 
1696 /* {{{ php_openssl_x509_from_zval
1697 	Given a zval, coerce it into an X509 object.
1698 	The zval can be:
1699 		. X509 resource created using openssl_read_x509()
1700 		. if it starts with file:// then it will be interpreted as the path to that cert
1701 		. it will be interpreted as the cert data
1702 	If you supply makeresource, the result will be registered as an x509 resource and
1703 	it's value returned in makeresource.
1704 */
php_openssl_x509_from_zval(zval * val,int makeresource,zend_resource ** resourceval)1705 static X509 * php_openssl_x509_from_zval(zval * val, int makeresource, zend_resource **resourceval)
1706 {
1707 	X509 *cert = NULL;
1708 	BIO *in;
1709 
1710 	if (resourceval) {
1711 		*resourceval = NULL;
1712 	}
1713 	if (Z_TYPE_P(val) == IS_RESOURCE) {
1714 		/* is it an x509 resource ? */
1715 		void * what;
1716 		zend_resource *res = Z_RES_P(val);
1717 
1718 		what = zend_fetch_resource(res, "OpenSSL X.509", le_x509);
1719 		if (!what) {
1720 			return NULL;
1721 		}
1722 		if (resourceval) {
1723 			*resourceval = res;
1724 			if (makeresource) {
1725 				Z_ADDREF_P(val);
1726 			}
1727 		}
1728 		return (X509*)what;
1729 	}
1730 
1731 	if (!(Z_TYPE_P(val) == IS_STRING || Z_TYPE_P(val) == IS_OBJECT)) {
1732 		return NULL;
1733 	}
1734 
1735 	/* force it to be a string and check if it refers to a file */
1736 	if (!try_convert_to_string(val)) {
1737 		return NULL;
1738 	}
1739 
1740 	if (Z_STRLEN_P(val) > 7 && memcmp(Z_STRVAL_P(val), "file://", sizeof("file://") - 1) == 0) {
1741 
1742 		if (php_openssl_open_base_dir_chk(Z_STRVAL_P(val) + (sizeof("file://") - 1))) {
1743 			return NULL;
1744 		}
1745 
1746 		in = BIO_new_file(Z_STRVAL_P(val) + (sizeof("file://") - 1), PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
1747 		if (in == NULL) {
1748 			php_openssl_store_errors();
1749 			return NULL;
1750 		}
1751 		cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1752 
1753 	} else {
1754 
1755 		in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
1756 		if (in == NULL) {
1757 			php_openssl_store_errors();
1758 			return NULL;
1759 		}
1760 #ifdef TYPEDEF_D2I_OF
1761 		cert = (X509 *) PEM_ASN1_read_bio((d2i_of_void *)d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
1762 #else
1763 		cert = (X509 *) PEM_ASN1_read_bio((char *(*)())d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
1764 #endif
1765 	}
1766 
1767 	if (!BIO_free(in)) {
1768 		php_openssl_store_errors();
1769 	}
1770 
1771 	if (cert == NULL) {
1772 		php_openssl_store_errors();
1773 		return NULL;
1774 	}
1775 
1776 	if (makeresource && resourceval) {
1777 		*resourceval = zend_register_resource(cert, le_x509);
1778 	}
1779 	return cert;
1780 }
1781 
1782 /* }}} */
1783 
1784 /* {{{ proto bool openssl_x509_export_to_file(mixed x509, string outfilename [, bool notext = true])
1785    Exports a CERT to file or a var */
PHP_FUNCTION(openssl_x509_export_to_file)1786 PHP_FUNCTION(openssl_x509_export_to_file)
1787 {
1788 	X509 * cert;
1789 	zval * zcert;
1790 	zend_bool notext = 1;
1791 	BIO * bio_out;
1792 	char * filename;
1793 	size_t filename_len;
1794 
1795 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zp|b", &zcert, &filename, &filename_len, &notext) == FAILURE) {
1796 		return;
1797 	}
1798 	RETVAL_FALSE;
1799 
1800 	cert = php_openssl_x509_from_zval(zcert, 0, NULL);
1801 	if (cert == NULL) {
1802 		php_error_docref(NULL, E_WARNING, "cannot get cert from parameter 1");
1803 		return;
1804 	}
1805 
1806 	if (php_openssl_open_base_dir_chk(filename)) {
1807 		return;
1808 	}
1809 
1810 	bio_out = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
1811 	if (bio_out) {
1812 		if (!notext && !X509_print(bio_out, cert)) {
1813 			php_openssl_store_errors();
1814 		}
1815 		if (!PEM_write_bio_X509(bio_out, cert)) {
1816 			php_openssl_store_errors();
1817 		}
1818 
1819 		RETVAL_TRUE;
1820 	} else {
1821 		php_openssl_store_errors();
1822 		php_error_docref(NULL, E_WARNING, "error opening file %s", filename);
1823 	}
1824 	if (Z_TYPE_P(zcert) != IS_RESOURCE) {
1825 		X509_free(cert);
1826 	}
1827 
1828 	if (!BIO_free(bio_out)) {
1829 		php_openssl_store_errors();
1830 	}
1831 }
1832 /* }}} */
1833 
1834 /* {{{ proto string openssl_spki_new(mixed zpkey, string challenge [, mixed method])
1835    Creates new private key (or uses existing) and creates a new spki cert
1836    outputting results to var */
PHP_FUNCTION(openssl_spki_new)1837 PHP_FUNCTION(openssl_spki_new)
1838 {
1839 	size_t challenge_len;
1840 	char * challenge = NULL, * spkstr = NULL;
1841 	zend_string * s = NULL;
1842 	zend_resource *keyresource = NULL;
1843 	const char *spkac = "SPKAC=";
1844 	zend_long algo = OPENSSL_ALGO_MD5;
1845 
1846 	zval *method = NULL;
1847 	zval * zpkey = NULL;
1848 	EVP_PKEY * pkey = NULL;
1849 	NETSCAPE_SPKI *spki=NULL;
1850 	const EVP_MD *mdtype;
1851 
1852 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs|z", &zpkey, &challenge, &challenge_len, &method) == FAILURE) {
1853 		return;
1854 	}
1855 	RETVAL_FALSE;
1856 
1857 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(challenge_len, challenge);
1858 	pkey = php_openssl_evp_from_zval(zpkey, 0, challenge, challenge_len, 1, &keyresource);
1859 
1860 	if (pkey == NULL) {
1861 		php_error_docref(NULL, E_WARNING, "Unable to use supplied private key");
1862 		goto cleanup;
1863 	}
1864 
1865 	if (method != NULL) {
1866 		if (Z_TYPE_P(method) == IS_LONG) {
1867 			algo = Z_LVAL_P(method);
1868 		} else {
1869 			php_error_docref(NULL, E_WARNING, "Algorithm must be of supported type");
1870 			goto cleanup;
1871 		}
1872 	}
1873 	mdtype = php_openssl_get_evp_md_from_algo(algo);
1874 
1875 	if (!mdtype) {
1876 		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm");
1877 		goto cleanup;
1878 	}
1879 
1880 	if ((spki = NETSCAPE_SPKI_new()) == NULL) {
1881 		php_openssl_store_errors();
1882 		php_error_docref(NULL, E_WARNING, "Unable to create new SPKAC");
1883 		goto cleanup;
1884 	}
1885 
1886 	if (challenge) {
1887 		if (!ASN1_STRING_set(spki->spkac->challenge, challenge, (int)challenge_len)) {
1888 			php_openssl_store_errors();
1889 			php_error_docref(NULL, E_WARNING, "Unable to set challenge data");
1890 			goto cleanup;
1891 		}
1892 	}
1893 
1894 	if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) {
1895 		php_openssl_store_errors();
1896 		php_error_docref(NULL, E_WARNING, "Unable to embed public key");
1897 		goto cleanup;
1898 	}
1899 
1900 	if (!NETSCAPE_SPKI_sign(spki, pkey, mdtype)) {
1901 		php_openssl_store_errors();
1902 		php_error_docref(NULL, E_WARNING, "Unable to sign with specified algorithm");
1903 		goto cleanup;
1904 	}
1905 
1906 	spkstr = NETSCAPE_SPKI_b64_encode(spki);
1907 	if (!spkstr){
1908 		php_openssl_store_errors();
1909 		php_error_docref(NULL, E_WARNING, "Unable to encode SPKAC");
1910 		goto cleanup;
1911 	}
1912 
1913 	s = zend_string_alloc(strlen(spkac) + strlen(spkstr), 0);
1914 	sprintf(ZSTR_VAL(s), "%s%s", spkac, spkstr);
1915 	ZSTR_LEN(s) = strlen(ZSTR_VAL(s));
1916 	OPENSSL_free(spkstr);
1917 
1918 	RETVAL_STR(s);
1919 	goto cleanup;
1920 
1921 cleanup:
1922 
1923 	if (spki != NULL) {
1924 		NETSCAPE_SPKI_free(spki);
1925 	}
1926 	if (keyresource == NULL && pkey != NULL) {
1927 		EVP_PKEY_free(pkey);
1928 	}
1929 
1930 	if (s && ZSTR_LEN(s) <= 0) {
1931 		RETVAL_FALSE;
1932 	}
1933 
1934 	if (keyresource == NULL && s != NULL) {
1935 		zend_string_release_ex(s, 0);
1936 	}
1937 }
1938 /* }}} */
1939 
1940 /* {{{ proto bool openssl_spki_verify(string spki)
1941    Verifies spki returns boolean */
PHP_FUNCTION(openssl_spki_verify)1942 PHP_FUNCTION(openssl_spki_verify)
1943 {
1944 	size_t spkstr_len;
1945 	int i = 0, spkstr_cleaned_len = 0;
1946 	char *spkstr, * spkstr_cleaned = NULL;
1947 
1948 	EVP_PKEY *pkey = NULL;
1949 	NETSCAPE_SPKI *spki = NULL;
1950 
1951 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &spkstr, &spkstr_len) == FAILURE) {
1952 		return;
1953 	}
1954 	RETVAL_FALSE;
1955 
1956 	spkstr_cleaned = emalloc(spkstr_len + 1);
1957 	spkstr_cleaned_len = (int)(spkstr_len - php_openssl_spki_cleanup(spkstr, spkstr_cleaned));
1958 
1959 	if (spkstr_cleaned_len == 0) {
1960 		php_error_docref(NULL, E_WARNING, "Invalid SPKAC");
1961 		goto cleanup;
1962 	}
1963 
1964 	spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
1965 	if (spki == NULL) {
1966 		php_openssl_store_errors();
1967 		php_error_docref(NULL, E_WARNING, "Unable to decode supplied SPKAC");
1968 		goto cleanup;
1969 	}
1970 
1971 	pkey = X509_PUBKEY_get(spki->spkac->pubkey);
1972 	if (pkey == NULL) {
1973 		php_openssl_store_errors();
1974 		php_error_docref(NULL, E_WARNING, "Unable to acquire signed public key");
1975 		goto cleanup;
1976 	}
1977 
1978 	i = NETSCAPE_SPKI_verify(spki, pkey);
1979 	goto cleanup;
1980 
1981 cleanup:
1982 	if (spki != NULL) {
1983 		NETSCAPE_SPKI_free(spki);
1984 	}
1985 	if (pkey != NULL) {
1986 		EVP_PKEY_free(pkey);
1987 	}
1988 	if (spkstr_cleaned != NULL) {
1989 		efree(spkstr_cleaned);
1990 	}
1991 
1992 	if (i > 0) {
1993 		RETVAL_TRUE;
1994 	} else {
1995 		php_openssl_store_errors();
1996 	}
1997 }
1998 /* }}} */
1999 
2000 /* {{{ proto string openssl_spki_export(string spki)
2001    Exports public key from existing spki to var */
PHP_FUNCTION(openssl_spki_export)2002 PHP_FUNCTION(openssl_spki_export)
2003 {
2004 	size_t spkstr_len;
2005 	char *spkstr, * spkstr_cleaned = NULL, * s = NULL;
2006 	int spkstr_cleaned_len;
2007 
2008 	EVP_PKEY *pkey = NULL;
2009 	NETSCAPE_SPKI *spki = NULL;
2010 	BIO *out = NULL;
2011 
2012 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &spkstr, &spkstr_len) == FAILURE) {
2013 		return;
2014 	}
2015 	RETVAL_FALSE;
2016 
2017 	spkstr_cleaned = emalloc(spkstr_len + 1);
2018 	spkstr_cleaned_len = (int)(spkstr_len - php_openssl_spki_cleanup(spkstr, spkstr_cleaned));
2019 
2020 	if (spkstr_cleaned_len == 0) {
2021 		php_error_docref(NULL, E_WARNING, "Invalid SPKAC");
2022 		goto cleanup;
2023 	}
2024 
2025 	spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
2026 	if (spki == NULL) {
2027 		php_openssl_store_errors();
2028 		php_error_docref(NULL, E_WARNING, "Unable to decode supplied SPKAC");
2029 		goto cleanup;
2030 	}
2031 
2032 	pkey = X509_PUBKEY_get(spki->spkac->pubkey);
2033 	if (pkey == NULL) {
2034 		php_openssl_store_errors();
2035 		php_error_docref(NULL, E_WARNING, "Unable to acquire signed public key");
2036 		goto cleanup;
2037 	}
2038 
2039 	out = BIO_new(BIO_s_mem());
2040 	if (out && PEM_write_bio_PUBKEY(out, pkey)) {
2041 		BUF_MEM *bio_buf;
2042 
2043 		BIO_get_mem_ptr(out, &bio_buf);
2044 		RETVAL_STRINGL((char *)bio_buf->data, bio_buf->length);
2045 	} else {
2046 		php_openssl_store_errors();
2047 	}
2048 	goto cleanup;
2049 
2050 cleanup:
2051 
2052 	if (spki != NULL) {
2053 		NETSCAPE_SPKI_free(spki);
2054 	}
2055 	if (out != NULL) {
2056 		BIO_free_all(out);
2057 	}
2058 	if (pkey != NULL) {
2059 		EVP_PKEY_free(pkey);
2060 	}
2061 	if (spkstr_cleaned != NULL) {
2062 		efree(spkstr_cleaned);
2063 	}
2064 	if (s != NULL) {
2065 		efree(s);
2066 	}
2067 }
2068 /* }}} */
2069 
2070 /* {{{ proto string openssl_spki_export_challenge(string spki)
2071    Exports spkac challenge from existing spki to var */
PHP_FUNCTION(openssl_spki_export_challenge)2072 PHP_FUNCTION(openssl_spki_export_challenge)
2073 {
2074 	size_t spkstr_len;
2075 	char *spkstr, * spkstr_cleaned = NULL;
2076 	int spkstr_cleaned_len;
2077 
2078 	NETSCAPE_SPKI *spki = NULL;
2079 
2080 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &spkstr, &spkstr_len) == FAILURE) {
2081 		return;
2082 	}
2083 	RETVAL_FALSE;
2084 
2085 	spkstr_cleaned = emalloc(spkstr_len + 1);
2086 	spkstr_cleaned_len = (int)(spkstr_len - php_openssl_spki_cleanup(spkstr, spkstr_cleaned));
2087 
2088 	if (spkstr_cleaned_len == 0) {
2089 		php_error_docref(NULL, E_WARNING, "Invalid SPKAC");
2090 		goto cleanup;
2091 	}
2092 
2093 	spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
2094 	if (spki == NULL) {
2095 		php_openssl_store_errors();
2096 		php_error_docref(NULL, E_WARNING, "Unable to decode SPKAC");
2097 		goto cleanup;
2098 	}
2099 
2100 	RETVAL_STRING((const char *)ASN1_STRING_get0_data(spki->spkac->challenge));
2101 	goto cleanup;
2102 
2103 cleanup:
2104 	if (spkstr_cleaned != NULL) {
2105 		efree(spkstr_cleaned);
2106 	}
2107 	if (spki) {
2108 		NETSCAPE_SPKI_free(spki);
2109 	}
2110 }
2111 /* }}} */
2112 
2113 /* {{{ proto bool openssl_x509_export(mixed x509, string &out [, bool notext = true])
2114    Exports a CERT to file or a var */
PHP_FUNCTION(openssl_x509_export)2115 PHP_FUNCTION(openssl_x509_export)
2116 {
2117 	X509 * cert;
2118 	zval * zcert, *zout;
2119 	zend_bool notext = 1;
2120 	BIO * bio_out;
2121 
2122 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|b", &zcert, &zout, &notext) == FAILURE) {
2123 		return;
2124 	}
2125 	RETVAL_FALSE;
2126 
2127 	cert = php_openssl_x509_from_zval(zcert, 0, NULL);
2128 	if (cert == NULL) {
2129 		php_error_docref(NULL, E_WARNING, "cannot get cert from parameter 1");
2130 		return;
2131 	}
2132 
2133 	bio_out = BIO_new(BIO_s_mem());
2134 	if (!bio_out) {
2135 		php_openssl_store_errors();
2136 		goto cleanup;
2137 	}
2138 	if (!notext && !X509_print(bio_out, cert)) {
2139 		php_openssl_store_errors();
2140 	}
2141 	if (PEM_write_bio_X509(bio_out, cert)) {
2142 		BUF_MEM *bio_buf;
2143 
2144 		BIO_get_mem_ptr(bio_out, &bio_buf);
2145 		ZEND_TRY_ASSIGN_REF_STRINGL(zout, bio_buf->data, bio_buf->length);
2146 
2147 		RETVAL_TRUE;
2148 	} else {
2149 		php_openssl_store_errors();
2150 	}
2151 
2152 	BIO_free(bio_out);
2153 
2154 cleanup:
2155 	if (Z_TYPE_P(zcert) != IS_RESOURCE) {
2156 		X509_free(cert);
2157 	}
2158 }
2159 /* }}} */
2160 
php_openssl_x509_fingerprint(X509 * peer,const char * method,zend_bool raw)2161 zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, zend_bool raw)
2162 {
2163 	unsigned char md[EVP_MAX_MD_SIZE];
2164 	const EVP_MD *mdtype;
2165 	unsigned int n;
2166 	zend_string *ret;
2167 
2168 	if (!(mdtype = EVP_get_digestbyname(method))) {
2169 		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm");
2170 		return NULL;
2171 	} else if (!X509_digest(peer, mdtype, md, &n)) {
2172 		php_openssl_store_errors();
2173 		php_error_docref(NULL, E_ERROR, "Could not generate signature");
2174 		return NULL;
2175 	}
2176 
2177 	if (raw) {
2178 		ret = zend_string_init((char*)md, n, 0);
2179 	} else {
2180 		ret = zend_string_alloc(n * 2, 0);
2181 		make_digest_ex(ZSTR_VAL(ret), md, n);
2182 		ZSTR_VAL(ret)[n * 2] = '\0';
2183 	}
2184 
2185 	return ret;
2186 }
2187 
PHP_FUNCTION(openssl_x509_fingerprint)2188 PHP_FUNCTION(openssl_x509_fingerprint)
2189 {
2190 	X509 *cert;
2191 	zval *zcert;
2192 	zend_bool raw_output = 0;
2193 	char *method = "sha1";
2194 	size_t method_len;
2195 	zend_string *fingerprint;
2196 
2197 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|sb", &zcert, &method, &method_len, &raw_output) == FAILURE) {
2198 		return;
2199 	}
2200 
2201 	cert = php_openssl_x509_from_zval(zcert, 0, NULL);
2202 	if (cert == NULL) {
2203 		php_error_docref(NULL, E_WARNING, "cannot get cert from parameter 1");
2204 		RETURN_FALSE;
2205 	}
2206 
2207 	fingerprint = php_openssl_x509_fingerprint(cert, method, raw_output);
2208 	if (fingerprint) {
2209 		RETVAL_STR(fingerprint);
2210 	} else {
2211 		RETVAL_FALSE;
2212 	}
2213 
2214 	if (Z_TYPE_P(zcert) != IS_RESOURCE) {
2215 		X509_free(cert);
2216 	}
2217 }
2218 
2219 /* {{{ proto bool openssl_x509_check_private_key(mixed cert, mixed key)
2220    Checks if a private key corresponds to a CERT */
PHP_FUNCTION(openssl_x509_check_private_key)2221 PHP_FUNCTION(openssl_x509_check_private_key)
2222 {
2223 	zval * zcert, *zkey;
2224 	X509 * cert = NULL;
2225 	EVP_PKEY * key = NULL;
2226 	zend_resource *keyresource = NULL;
2227 
2228 	RETVAL_FALSE;
2229 
2230 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &zcert, &zkey) == FAILURE) {
2231 		return;
2232 	}
2233 	cert = php_openssl_x509_from_zval(zcert, 0, NULL);
2234 	if (cert == NULL) {
2235 		RETURN_FALSE;
2236 	}
2237 	key = php_openssl_evp_from_zval(zkey, 0, "", 0, 1, &keyresource);
2238 	if (key) {
2239 		RETVAL_BOOL(X509_check_private_key(cert, key));
2240 	}
2241 
2242 	if (keyresource == NULL && key) {
2243 		EVP_PKEY_free(key);
2244 	}
2245 	if (Z_TYPE_P(zcert) != IS_RESOURCE) {
2246 		X509_free(cert);
2247 	}
2248 }
2249 /* }}} */
2250 
2251 /* {{{ proto int openssl_x509_verify(mixed cert, mixed key)
2252    Verifies the signature of certificate cert using public key key */
PHP_FUNCTION(openssl_x509_verify)2253 PHP_FUNCTION(openssl_x509_verify)
2254 {
2255 	zval * zcert, *zkey;
2256 	X509 * cert = NULL;
2257 	EVP_PKEY * key = NULL;
2258 	zend_resource *keyresource = NULL;
2259 	int err = -1;
2260 
2261 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &zcert, &zkey) == FAILURE) {
2262 		return;
2263 	}
2264 	cert = php_openssl_x509_from_zval(zcert, 0, NULL);
2265 	if (cert == NULL) {
2266 		RETURN_LONG(err);
2267 	}
2268 	key = php_openssl_evp_from_zval(zkey, 1, NULL, 0, 0, &keyresource);
2269 	if (key == NULL) {
2270 		X509_free(cert);
2271 		RETURN_LONG(err);
2272 	}
2273 
2274 	err = X509_verify(cert, key);
2275 
2276 	if (err < 0) {
2277 		php_openssl_store_errors();
2278 	}
2279 
2280 	if (keyresource == NULL && key) {
2281 		EVP_PKEY_free(key);
2282 	}
2283 	if (Z_TYPE_P(zcert) != IS_RESOURCE) {
2284 		X509_free(cert);
2285 	}
2286 
2287 	RETURN_LONG(err);
2288 }
2289 /* }}} */
2290 
2291 /* Special handling of subjectAltName, see CVE-2013-4073
2292  * Christian Heimes
2293  */
2294 
openssl_x509v3_subjectAltName(BIO * bio,X509_EXTENSION * extension)2295 static int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)
2296 {
2297 	GENERAL_NAMES *names;
2298 	const X509V3_EXT_METHOD *method = NULL;
2299 	ASN1_OCTET_STRING *extension_data;
2300 	long i, length, num;
2301 	const unsigned char *p;
2302 
2303 	method = X509V3_EXT_get(extension);
2304 	if (method == NULL) {
2305 		return -1;
2306 	}
2307 
2308 	extension_data = X509_EXTENSION_get_data(extension);
2309 	p = extension_data->data;
2310 	length = extension_data->length;
2311 	if (method->it) {
2312 		names = (GENERAL_NAMES*) (ASN1_item_d2i(NULL, &p, length,
2313 			ASN1_ITEM_ptr(method->it)));
2314 	} else {
2315 		names = (GENERAL_NAMES*) (method->d2i(NULL, &p, length));
2316 	}
2317 	if (names == NULL) {
2318 		php_openssl_store_errors();
2319 		return -1;
2320 	}
2321 
2322 	num = sk_GENERAL_NAME_num(names);
2323 	for (i = 0; i < num; i++) {
2324 		GENERAL_NAME *name;
2325 		ASN1_STRING *as;
2326 		name = sk_GENERAL_NAME_value(names, i);
2327 		switch (name->type) {
2328 			case GEN_EMAIL:
2329 				BIO_puts(bio, "email:");
2330 				as = name->d.rfc822Name;
2331 				BIO_write(bio, ASN1_STRING_get0_data(as),
2332 					ASN1_STRING_length(as));
2333 				break;
2334 			case GEN_DNS:
2335 				BIO_puts(bio, "DNS:");
2336 				as = name->d.dNSName;
2337 				BIO_write(bio, ASN1_STRING_get0_data(as),
2338 					ASN1_STRING_length(as));
2339 				break;
2340 			case GEN_URI:
2341 				BIO_puts(bio, "URI:");
2342 				as = name->d.uniformResourceIdentifier;
2343 				BIO_write(bio, ASN1_STRING_get0_data(as),
2344 					ASN1_STRING_length(as));
2345 				break;
2346 			default:
2347 				/* use builtin print for GEN_OTHERNAME, GEN_X400,
2348 				 * GEN_EDIPARTY, GEN_DIRNAME, GEN_IPADD and GEN_RID
2349 				 */
2350 				GENERAL_NAME_print(bio, name);
2351 			}
2352 			/* trailing ', ' except for last element */
2353 			if (i < (num - 1)) {
2354 				BIO_puts(bio, ", ");
2355 			}
2356 	}
2357 	sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
2358 
2359 	return 0;
2360 }
2361 
2362 /* {{{ proto array openssl_x509_parse(mixed x509 [, bool shortnames=true])
2363    Returns an array of the fields/values of the CERT */
PHP_FUNCTION(openssl_x509_parse)2364 PHP_FUNCTION(openssl_x509_parse)
2365 {
2366 	zval * zcert;
2367 	X509 * cert = NULL;
2368 	int i, sig_nid;
2369 	zend_bool useshortnames = 1;
2370 	char * tmpstr;
2371 	zval subitem;
2372 	X509_EXTENSION *extension;
2373 	X509_NAME *subject_name;
2374 	char *cert_name;
2375 	char *extname;
2376 	BIO *bio_out;
2377 	BUF_MEM *bio_buf;
2378 	ASN1_INTEGER *asn1_serial;
2379 	BIGNUM *bn_serial;
2380 	char *str_serial;
2381 	char *hex_serial;
2382 	char buf[256];
2383 
2384 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &zcert, &useshortnames) == FAILURE) {
2385 		return;
2386 	}
2387 	cert = php_openssl_x509_from_zval(zcert, 0, NULL);
2388 	if (cert == NULL) {
2389 		RETURN_FALSE;
2390 	}
2391 	array_init(return_value);
2392 
2393 	subject_name = X509_get_subject_name(cert);
2394 	cert_name = X509_NAME_oneline(subject_name, NULL, 0);
2395 	add_assoc_string(return_value, "name", cert_name);
2396 	OPENSSL_free(cert_name);
2397 
2398 	php_openssl_add_assoc_name_entry(return_value, "subject", subject_name, useshortnames);
2399 	/* hash as used in CA directories to lookup cert by subject name */
2400 	{
2401 		char buf[32];
2402 		snprintf(buf, sizeof(buf), "%08lx", X509_subject_name_hash(cert));
2403 		add_assoc_string(return_value, "hash", buf);
2404 	}
2405 
2406 	php_openssl_add_assoc_name_entry(return_value, "issuer", X509_get_issuer_name(cert), useshortnames);
2407 	add_assoc_long(return_value, "version", X509_get_version(cert));
2408 
2409 	asn1_serial = X509_get_serialNumber(cert);
2410 
2411 	bn_serial = ASN1_INTEGER_to_BN(asn1_serial, NULL);
2412 	/* Can return NULL on error or memory allocation failure */
2413 	if (!bn_serial) {
2414 		php_openssl_store_errors();
2415 		RETURN_FALSE;
2416 	}
2417 
2418 	hex_serial = BN_bn2hex(bn_serial);
2419 	BN_free(bn_serial);
2420 	/* Can return NULL on error or memory allocation failure */
2421 	if (!hex_serial) {
2422 		php_openssl_store_errors();
2423 		RETURN_FALSE;
2424 	}
2425 
2426 	str_serial = i2s_ASN1_INTEGER(NULL, asn1_serial);
2427 	add_assoc_string(return_value, "serialNumber", str_serial);
2428 	OPENSSL_free(str_serial);
2429 
2430 	/* Return the hex representation of the serial number, as defined by OpenSSL */
2431 	add_assoc_string(return_value, "serialNumberHex", hex_serial);
2432 	OPENSSL_free(hex_serial);
2433 
2434 	php_openssl_add_assoc_asn1_string(return_value, "validFrom", 	X509_getm_notBefore(cert));
2435 	php_openssl_add_assoc_asn1_string(return_value, "validTo", 		X509_getm_notAfter(cert));
2436 
2437 	add_assoc_long(return_value, "validFrom_time_t", php_openssl_asn1_time_to_time_t(X509_getm_notBefore(cert)));
2438 	add_assoc_long(return_value, "validTo_time_t",  php_openssl_asn1_time_to_time_t(X509_getm_notAfter(cert)));
2439 
2440 	tmpstr = (char *)X509_alias_get0(cert, NULL);
2441 	if (tmpstr) {
2442 		add_assoc_string(return_value, "alias", tmpstr);
2443 	}
2444 
2445 	sig_nid = X509_get_signature_nid(cert);
2446 	add_assoc_string(return_value, "signatureTypeSN", (char*)OBJ_nid2sn(sig_nid));
2447 	add_assoc_string(return_value, "signatureTypeLN", (char*)OBJ_nid2ln(sig_nid));
2448 	add_assoc_long(return_value, "signatureTypeNID", sig_nid);
2449 	array_init(&subitem);
2450 
2451 	/* NOTE: the purposes are added as integer keys - the keys match up to the X509_PURPOSE_SSL_XXX defines
2452 	   in x509v3.h */
2453 	for (i = 0; i < X509_PURPOSE_get_count(); i++) {
2454 		int id, purpset;
2455 		char * pname;
2456 		X509_PURPOSE * purp;
2457 		zval subsub;
2458 
2459 		array_init(&subsub);
2460 
2461 		purp = X509_PURPOSE_get0(i);
2462 		id = X509_PURPOSE_get_id(purp);
2463 
2464 		purpset = X509_check_purpose(cert, id, 0);
2465 		add_index_bool(&subsub, 0, purpset);
2466 
2467 		purpset = X509_check_purpose(cert, id, 1);
2468 		add_index_bool(&subsub, 1, purpset);
2469 
2470 		pname = useshortnames ? X509_PURPOSE_get0_sname(purp) : X509_PURPOSE_get0_name(purp);
2471 		add_index_string(&subsub, 2, pname);
2472 
2473 		/* NOTE: if purpset > 1 then it's a warning - we should mention it ? */
2474 
2475 		add_index_zval(&subitem, id, &subsub);
2476 	}
2477 	add_assoc_zval(return_value, "purposes", &subitem);
2478 
2479 	array_init(&subitem);
2480 
2481 
2482 	for (i = 0; i < X509_get_ext_count(cert); i++) {
2483 		int nid;
2484 		extension = X509_get_ext(cert, i);
2485 		nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
2486 		if (nid != NID_undef) {
2487 			extname = (char *)OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(extension)));
2488 		} else {
2489 			OBJ_obj2txt(buf, sizeof(buf)-1, X509_EXTENSION_get_object(extension), 1);
2490 			extname = buf;
2491 		}
2492 		bio_out = BIO_new(BIO_s_mem());
2493 		if (bio_out == NULL) {
2494 			php_openssl_store_errors();
2495 			RETURN_FALSE;
2496 		}
2497 		if (nid == NID_subject_alt_name) {
2498 			if (openssl_x509v3_subjectAltName(bio_out, extension) == 0) {
2499 				BIO_get_mem_ptr(bio_out, &bio_buf);
2500 				add_assoc_stringl(&subitem, extname, bio_buf->data, bio_buf->length);
2501 			} else {
2502 				zend_array_destroy(Z_ARR_P(return_value));
2503 				BIO_free(bio_out);
2504 				if (Z_TYPE_P(zcert) != IS_RESOURCE) {
2505 					X509_free(cert);
2506 				}
2507 				RETURN_FALSE;
2508 			}
2509 		}
2510 		else if (X509V3_EXT_print(bio_out, extension, 0, 0)) {
2511 			BIO_get_mem_ptr(bio_out, &bio_buf);
2512 			add_assoc_stringl(&subitem, extname, bio_buf->data, bio_buf->length);
2513 		} else {
2514 			php_openssl_add_assoc_asn1_string(&subitem, extname, X509_EXTENSION_get_data(extension));
2515 		}
2516 		BIO_free(bio_out);
2517 	}
2518 	add_assoc_zval(return_value, "extensions", &subitem);
2519 	if (Z_TYPE_P(zcert) != IS_RESOURCE) {
2520 		X509_free(cert);
2521 	}
2522 }
2523 /* }}} */
2524 
2525 /* {{{ php_openssl_load_all_certs_from_file */
STACK_OF(X509)2526 static STACK_OF(X509) *php_openssl_load_all_certs_from_file(char *certfile)
2527 {
2528 	STACK_OF(X509_INFO) *sk=NULL;
2529 	STACK_OF(X509) *stack=NULL, *ret=NULL;
2530 	BIO *in=NULL;
2531 	X509_INFO *xi;
2532 
2533 	if(!(stack = sk_X509_new_null())) {
2534 		php_openssl_store_errors();
2535 		php_error_docref(NULL, E_ERROR, "memory allocation failure");
2536 		goto end;
2537 	}
2538 
2539 	if (php_openssl_open_base_dir_chk(certfile)) {
2540 		sk_X509_free(stack);
2541 		goto end;
2542 	}
2543 
2544 	if (!(in=BIO_new_file(certfile, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY)))) {
2545 		php_openssl_store_errors();
2546 		php_error_docref(NULL, E_WARNING, "error opening the file, %s", certfile);
2547 		sk_X509_free(stack);
2548 		goto end;
2549 	}
2550 
2551 	/* This loads from a file, a stack of x509/crl/pkey sets */
2552 	if (!(sk=PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) {
2553 		php_openssl_store_errors();
2554 		php_error_docref(NULL, E_WARNING, "error reading the file, %s", certfile);
2555 		sk_X509_free(stack);
2556 		goto end;
2557 	}
2558 
2559 	/* scan over it and pull out the certs */
2560 	while (sk_X509_INFO_num(sk)) {
2561 		xi=sk_X509_INFO_shift(sk);
2562 		if (xi->x509 != NULL) {
2563 			sk_X509_push(stack,xi->x509);
2564 			xi->x509=NULL;
2565 		}
2566 		X509_INFO_free(xi);
2567 	}
2568 	if (!sk_X509_num(stack)) {
2569 		php_error_docref(NULL, E_WARNING, "no certificates in file, %s", certfile);
2570 		sk_X509_free(stack);
2571 		goto end;
2572 	}
2573 	ret = stack;
2574 end:
2575 	BIO_free(in);
2576 	sk_X509_INFO_free(sk);
2577 
2578 	return ret;
2579 }
2580 /* }}} */
2581 
2582 /* {{{ check_cert */
check_cert(X509_STORE * ctx,X509 * x,STACK_OF (X509)* untrustedchain,int purpose)2583 static int check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain, int purpose)
2584 {
2585 	int ret=0;
2586 	X509_STORE_CTX *csc;
2587 
2588 	csc = X509_STORE_CTX_new();
2589 	if (csc == NULL) {
2590 		php_openssl_store_errors();
2591 		php_error_docref(NULL, E_ERROR, "memory allocation failure");
2592 		return 0;
2593 	}
2594 	if (!X509_STORE_CTX_init(csc, ctx, x, untrustedchain)) {
2595 		php_openssl_store_errors();
2596 		php_error_docref(NULL, E_WARNING, "cert store initialization failed");
2597 		return 0;
2598 	}
2599 	if (purpose >= 0 && !X509_STORE_CTX_set_purpose(csc, purpose)) {
2600 		php_openssl_store_errors();
2601 	}
2602 	ret = X509_verify_cert(csc);
2603 	if (ret < 0) {
2604 		php_openssl_store_errors();
2605 	}
2606 	X509_STORE_CTX_free(csc);
2607 
2608 	return ret;
2609 }
2610 /* }}} */
2611 
2612 /* {{{ proto int openssl_x509_checkpurpose(mixed x509cert, int purpose, array cainfo [, string untrustedfile])
2613    Checks the CERT to see if it can be used for the purpose in purpose. cainfo holds information about trusted CAs */
PHP_FUNCTION(openssl_x509_checkpurpose)2614 PHP_FUNCTION(openssl_x509_checkpurpose)
2615 {
2616 	zval * zcert, * zcainfo = NULL;
2617 	X509_STORE * cainfo = NULL;
2618 	X509 * cert = NULL;
2619 	STACK_OF(X509) * untrustedchain = NULL;
2620 	zend_long purpose;
2621 	char * untrusted = NULL;
2622 	size_t untrusted_len = 0;
2623 	int ret;
2624 
2625 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl|a!s", &zcert, &purpose, &zcainfo, &untrusted, &untrusted_len) == FAILURE) {
2626 		return;
2627 	}
2628 
2629 	RETVAL_LONG(-1);
2630 
2631 	if (untrusted) {
2632 		untrustedchain = php_openssl_load_all_certs_from_file(untrusted);
2633 		if (untrustedchain == NULL) {
2634 			goto clean_exit;
2635 		}
2636 	}
2637 
2638 	cainfo = php_openssl_setup_verify(zcainfo);
2639 	if (cainfo == NULL) {
2640 		goto clean_exit;
2641 	}
2642 	cert = php_openssl_x509_from_zval(zcert, 0, NULL);
2643 	if (cert == NULL) {
2644 		goto clean_exit;
2645 	}
2646 
2647 	ret = check_cert(cainfo, cert, untrustedchain, (int)purpose);
2648 	if (ret != 0 && ret != 1) {
2649 		RETVAL_LONG(ret);
2650 	} else {
2651 		RETVAL_BOOL(ret);
2652 	}
2653 	if (Z_TYPE_P(zcert) != IS_RESOURCE) {
2654 		X509_free(cert);
2655 	}
2656 clean_exit:
2657 	if (cainfo) {
2658 		X509_STORE_free(cainfo);
2659 	}
2660 	if (untrustedchain) {
2661 		sk_X509_pop_free(untrustedchain, X509_free);
2662 	}
2663 }
2664 /* }}} */
2665 
2666 /* {{{ php_openssl_setup_verify
2667  * calist is an array containing file and directory names.  create a
2668  * certificate store and add those certs to it for use in verification.
2669 */
php_openssl_setup_verify(zval * calist)2670 static X509_STORE *php_openssl_setup_verify(zval *calist)
2671 {
2672 	X509_STORE *store;
2673 	X509_LOOKUP * dir_lookup, * file_lookup;
2674 	int ndirs = 0, nfiles = 0;
2675 	zval * item;
2676 	zend_stat_t sb;
2677 
2678 	store = X509_STORE_new();
2679 
2680 	if (store == NULL) {
2681 		php_openssl_store_errors();
2682 		return NULL;
2683 	}
2684 
2685 	if (calist && (Z_TYPE_P(calist) == IS_ARRAY)) {
2686 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(calist), item) {
2687 			zend_string *str = zval_try_get_string(item);
2688 			if (UNEXPECTED(!str)) {
2689 				return NULL;
2690 			}
2691 
2692 			if (VCWD_STAT(ZSTR_VAL(str), &sb) == -1) {
2693 				php_error_docref(NULL, E_WARNING, "unable to stat %s", ZSTR_VAL(str));
2694 				zend_string_release(str);
2695 				continue;
2696 			}
2697 
2698 			if ((sb.st_mode & S_IFREG) == S_IFREG) {
2699 				file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
2700 				if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, ZSTR_VAL(str), X509_FILETYPE_PEM)) {
2701 					php_openssl_store_errors();
2702 					php_error_docref(NULL, E_WARNING, "error loading file %s", ZSTR_VAL(str));
2703 				} else {
2704 					nfiles++;
2705 				}
2706 				file_lookup = NULL;
2707 			} else {
2708 				dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
2709 				if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, ZSTR_VAL(str), X509_FILETYPE_PEM)) {
2710 					php_openssl_store_errors();
2711 					php_error_docref(NULL, E_WARNING, "error loading directory %s", ZSTR_VAL(str));
2712 				} else {
2713 					ndirs++;
2714 				}
2715 				dir_lookup = NULL;
2716 			}
2717 			zend_string_release(str);
2718 		} ZEND_HASH_FOREACH_END();
2719 	}
2720 	if (nfiles == 0) {
2721 		file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
2722 		if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT)) {
2723 			php_openssl_store_errors();
2724 		}
2725 	}
2726 	if (ndirs == 0) {
2727 		dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
2728 		if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT)) {
2729 			php_openssl_store_errors();
2730 		}
2731 	}
2732 	return store;
2733 }
2734 /* }}} */
2735 
2736 /* {{{ proto resource openssl_x509_read(mixed cert)
2737    Reads X.509 certificates */
PHP_FUNCTION(openssl_x509_read)2738 PHP_FUNCTION(openssl_x509_read)
2739 {
2740 	zval *cert;
2741 	X509 *x509;
2742 	zend_resource *res;
2743 
2744 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &cert) == FAILURE) {
2745 		return;
2746 	}
2747 	x509 = php_openssl_x509_from_zval(cert, 1, &res);
2748 	ZVAL_RES(return_value, res);
2749 
2750 	if (x509 == NULL) {
2751 		php_error_docref(NULL, E_WARNING, "supplied parameter cannot be coerced into an X509 certificate!");
2752 		RETURN_FALSE;
2753 	}
2754 }
2755 /* }}} */
2756 
2757 /* {{{ proto void openssl_x509_free(resource x509)
2758    Frees X.509 certificates */
PHP_FUNCTION(openssl_x509_free)2759 PHP_FUNCTION(openssl_x509_free)
2760 {
2761 	zval *x509;
2762 	X509 *cert;
2763 
2764 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &x509) == FAILURE) {
2765 		return;
2766 	}
2767 	if ((cert = (X509 *)zend_fetch_resource(Z_RES_P(x509), "OpenSSL X.509", le_x509)) == NULL) {
2768 		RETURN_FALSE;
2769 	}
2770 	zend_list_close(Z_RES_P(x509));
2771 }
2772 /* }}} */
2773 
2774 /* }}} */
2775 
2776 /* Pop all X509 from Stack and free them, free the stack afterwards */
php_sk_X509_free(STACK_OF (X509)* sk)2777 static void php_sk_X509_free(STACK_OF(X509) * sk) /* {{{ */
2778 {
2779 	for (;;) {
2780 		X509* x = sk_X509_pop(sk);
2781 		if (!x) break;
2782 		X509_free(x);
2783 	}
2784 	sk_X509_free(sk);
2785 }
2786 /* }}} */
2787 
STACK_OF(X509)2788 static STACK_OF(X509) * php_array_to_X509_sk(zval * zcerts) /* {{{ */
2789 {
2790 	zval * zcertval;
2791 	STACK_OF(X509) * sk = NULL;
2792 	X509 * cert;
2793 	zend_resource *certresource;
2794 
2795 	sk = sk_X509_new_null();
2796 
2797 	/* get certs */
2798 	if (Z_TYPE_P(zcerts) == IS_ARRAY) {
2799 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zcerts), zcertval) {
2800 			cert = php_openssl_x509_from_zval(zcertval, 0, &certresource);
2801 			if (cert == NULL) {
2802 				goto clean_exit;
2803 			}
2804 
2805 			if (certresource != NULL) {
2806 				cert = X509_dup(cert);
2807 
2808 				if (cert == NULL) {
2809 					php_openssl_store_errors();
2810 					goto clean_exit;
2811 				}
2812 
2813 			}
2814 			sk_X509_push(sk, cert);
2815 		} ZEND_HASH_FOREACH_END();
2816 	} else {
2817 		/* a single certificate */
2818 		cert = php_openssl_x509_from_zval(zcerts, 0, &certresource);
2819 
2820 		if (cert == NULL) {
2821 			goto clean_exit;
2822 		}
2823 
2824 		if (certresource != NULL) {
2825 			cert = X509_dup(cert);
2826 			if (cert == NULL) {
2827 				php_openssl_store_errors();
2828 				goto clean_exit;
2829 			}
2830 		}
2831 		sk_X509_push(sk, cert);
2832 	}
2833 
2834 clean_exit:
2835 	return sk;
2836 }
2837 /* }}} */
2838 
2839 /* {{{ proto bool openssl_pkcs12_export_to_file(mixed x509, string filename, mixed priv_key, string pass[, array args])
2840    Creates and exports a PKCS to file */
PHP_FUNCTION(openssl_pkcs12_export_to_file)2841 PHP_FUNCTION(openssl_pkcs12_export_to_file)
2842 {
2843 	X509 * cert = NULL;
2844 	BIO * bio_out = NULL;
2845 	PKCS12 * p12 = NULL;
2846 	char * filename;
2847 	char * friendly_name = NULL;
2848 	size_t filename_len;
2849 	char * pass;
2850 	size_t pass_len;
2851 	zval *zcert = NULL, *zpkey = NULL, *args = NULL;
2852 	EVP_PKEY *priv_key = NULL;
2853 	zend_resource *keyresource;
2854 	zval * item;
2855 	STACK_OF(X509) *ca = NULL;
2856 
2857 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zpzs|a", &zcert, &filename, &filename_len, &zpkey, &pass, &pass_len, &args) == FAILURE)
2858 		return;
2859 
2860 	RETVAL_FALSE;
2861 
2862 	cert = php_openssl_x509_from_zval(zcert, 0, NULL);
2863 	if (cert == NULL) {
2864 		php_error_docref(NULL, E_WARNING, "cannot get cert from parameter 1");
2865 		return;
2866 	}
2867 	priv_key = php_openssl_evp_from_zval(zpkey, 0, "", 0, 1, &keyresource);
2868 	if (priv_key == NULL) {
2869 		php_error_docref(NULL, E_WARNING, "cannot get private key from parameter 3");
2870 		goto cleanup;
2871 	}
2872 	if (!X509_check_private_key(cert, priv_key)) {
2873 		php_openssl_store_errors();
2874 		php_error_docref(NULL, E_WARNING, "private key does not correspond to cert");
2875 		goto cleanup;
2876 	}
2877 	if (php_openssl_open_base_dir_chk(filename)) {
2878 		goto cleanup;
2879 	}
2880 
2881 	/* parse extra config from args array, promote this to an extra function */
2882 	if (args &&
2883 		(item = zend_hash_str_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name")-1)) != NULL &&
2884 		Z_TYPE_P(item) == IS_STRING
2885 	) {
2886 		friendly_name = Z_STRVAL_P(item);
2887 	}
2888 	/* certpbe (default RC2-40)
2889 	   keypbe (default 3DES)
2890 	   friendly_caname
2891 	*/
2892 
2893 	if (args && (item = zend_hash_str_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts")-1)) != NULL) {
2894 		ca = php_array_to_X509_sk(item);
2895 	}
2896 	/* end parse extra config */
2897 
2898 	/*PKCS12 *PKCS12_create(char *pass, char *name, EVP_PKEY *pkey, X509 *cert, STACK_OF(X509) *ca,
2899 				int nid_key, int nid_cert, int iter, int mac_iter, int keytype);*/
2900 
2901 	p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
2902 	if (p12 != NULL) {
2903 		bio_out = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
2904 		if (bio_out != NULL) {
2905 
2906 			i2d_PKCS12_bio(bio_out, p12);
2907 			BIO_free(bio_out);
2908 
2909 			RETVAL_TRUE;
2910 		} else {
2911 			php_openssl_store_errors();
2912 			php_error_docref(NULL, E_WARNING, "error opening file %s", filename);
2913 		}
2914 
2915 		PKCS12_free(p12);
2916 	} else {
2917 		php_openssl_store_errors();
2918 	}
2919 
2920 	php_sk_X509_free(ca);
2921 
2922 cleanup:
2923 
2924 	if (keyresource == NULL && priv_key) {
2925 		EVP_PKEY_free(priv_key);
2926 	}
2927 
2928 	if (Z_TYPE_P(zcert) != IS_RESOURCE) {
2929 		X509_free(cert);
2930 	}
2931 }
2932 /* }}} */
2933 
2934 /* {{{ proto bool openssl_pkcs12_export(mixed x509, string &out, mixed priv_key, string pass[, array args])
2935    Creates and exports a PKCS12 to a var */
PHP_FUNCTION(openssl_pkcs12_export)2936 PHP_FUNCTION(openssl_pkcs12_export)
2937 {
2938 	X509 * cert = NULL;
2939 	BIO * bio_out;
2940 	PKCS12 * p12 = NULL;
2941 	zval * zcert = NULL, *zout = NULL, *zpkey, *args = NULL;
2942 	EVP_PKEY *priv_key = NULL;
2943 	zend_resource *keyresource;
2944 	char * pass;
2945 	size_t pass_len;
2946 	char * friendly_name = NULL;
2947 	zval * item;
2948 	STACK_OF(X509) *ca = NULL;
2949 
2950 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzzs|a", &zcert, &zout, &zpkey, &pass, &pass_len, &args) == FAILURE)
2951 		return;
2952 
2953 	RETVAL_FALSE;
2954 
2955 	cert = php_openssl_x509_from_zval(zcert, 0, NULL);
2956 	if (cert == NULL) {
2957 		php_error_docref(NULL, E_WARNING, "cannot get cert from parameter 1");
2958 		return;
2959 	}
2960 	priv_key = php_openssl_evp_from_zval(zpkey, 0, "", 0, 1, &keyresource);
2961 	if (priv_key == NULL) {
2962 		php_error_docref(NULL, E_WARNING, "cannot get private key from parameter 3");
2963 		goto cleanup;
2964 	}
2965 	if (!X509_check_private_key(cert, priv_key)) {
2966 		php_error_docref(NULL, E_WARNING, "private key does not correspond to cert");
2967 		goto cleanup;
2968 	}
2969 
2970 	/* parse extra config from args array, promote this to an extra function */
2971 	if (args &&
2972 		(item = zend_hash_str_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name")-1)) != NULL &&
2973 		Z_TYPE_P(item) == IS_STRING
2974 	) {
2975 		friendly_name = Z_STRVAL_P(item);
2976 	}
2977 
2978 	if (args && (item = zend_hash_str_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts")-1)) != NULL) {
2979 		ca = php_array_to_X509_sk(item);
2980 	}
2981 	/* end parse extra config */
2982 
2983 	p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
2984 
2985 	if (p12 != NULL) {
2986 		bio_out = BIO_new(BIO_s_mem());
2987 		if (i2d_PKCS12_bio(bio_out, p12)) {
2988 			BUF_MEM *bio_buf;
2989 
2990 			BIO_get_mem_ptr(bio_out, &bio_buf);
2991 			ZEND_TRY_ASSIGN_REF_STRINGL(zout, bio_buf->data, bio_buf->length);
2992 
2993 			RETVAL_TRUE;
2994 		} else {
2995 			php_openssl_store_errors();
2996 		}
2997 
2998 		BIO_free(bio_out);
2999 		PKCS12_free(p12);
3000 	} else {
3001 		php_openssl_store_errors();
3002 	}
3003 	php_sk_X509_free(ca);
3004 
3005 cleanup:
3006 
3007 	if (keyresource == NULL && priv_key) {
3008 		EVP_PKEY_free(priv_key);
3009 	}
3010 	if (Z_TYPE_P(zcert) != IS_RESOURCE) {
3011 		X509_free(cert);
3012 	}
3013 }
3014 /* }}} */
3015 
3016 /* {{{ proto bool openssl_pkcs12_read(string PKCS12, array &certs, string pass)
3017    Parses a PKCS12 to an array */
PHP_FUNCTION(openssl_pkcs12_read)3018 PHP_FUNCTION(openssl_pkcs12_read)
3019 {
3020 	zval *zout = NULL, zextracerts, zcert, zpkey;
3021 	char *pass, *zp12;
3022 	size_t pass_len, zp12_len;
3023 	PKCS12 * p12 = NULL;
3024 	EVP_PKEY * pkey = NULL;
3025 	X509 * cert = NULL;
3026 	STACK_OF(X509) * ca = NULL;
3027 	BIO * bio_in = NULL;
3028 	int i;
3029 
3030 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szs", &zp12, &zp12_len, &zout, &pass, &pass_len) == FAILURE)
3031 		return;
3032 
3033 	RETVAL_FALSE;
3034 
3035 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(zp12_len, pkcs12);
3036 
3037 	bio_in = BIO_new(BIO_s_mem());
3038 
3039 	if (0 >= BIO_write(bio_in, zp12, (int)zp12_len)) {
3040 		php_openssl_store_errors();
3041 		goto cleanup;
3042 	}
3043 
3044 	if (d2i_PKCS12_bio(bio_in, &p12) && PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
3045 		BIO * bio_out;
3046 		int cert_num;
3047 
3048 		zout = zend_try_array_init(zout);
3049 		if (!zout) {
3050 			goto cleanup;
3051 		}
3052 
3053 		if (cert) {
3054 			bio_out = BIO_new(BIO_s_mem());
3055 			if (PEM_write_bio_X509(bio_out, cert)) {
3056 				BUF_MEM *bio_buf;
3057 				BIO_get_mem_ptr(bio_out, &bio_buf);
3058 				ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
3059 				add_assoc_zval(zout, "cert", &zcert);
3060 			} else {
3061 				php_openssl_store_errors();
3062 			}
3063 			BIO_free(bio_out);
3064 		}
3065 
3066 		if (pkey) {
3067 			bio_out = BIO_new(BIO_s_mem());
3068 			if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) {
3069 				BUF_MEM *bio_buf;
3070 				BIO_get_mem_ptr(bio_out, &bio_buf);
3071 				ZVAL_STRINGL(&zpkey, bio_buf->data, bio_buf->length);
3072 				add_assoc_zval(zout, "pkey", &zpkey);
3073 			} else {
3074 				php_openssl_store_errors();
3075 			}
3076 			BIO_free(bio_out);
3077 		}
3078 
3079 		cert_num = sk_X509_num(ca);
3080 		if (ca && cert_num) {
3081 			array_init(&zextracerts);
3082 
3083 			for (i = 0; i < cert_num; i++) {
3084 				zval zextracert;
3085 				X509* aCA = sk_X509_pop(ca);
3086 				if (!aCA) break;
3087 
3088 				bio_out = BIO_new(BIO_s_mem());
3089 				if (PEM_write_bio_X509(bio_out, aCA)) {
3090 					BUF_MEM *bio_buf;
3091 					BIO_get_mem_ptr(bio_out, &bio_buf);
3092 					ZVAL_STRINGL(&zextracert, bio_buf->data, bio_buf->length);
3093 					add_index_zval(&zextracerts, i, &zextracert);
3094 				}
3095 
3096 				X509_free(aCA);
3097 				BIO_free(bio_out);
3098 			}
3099 
3100 			sk_X509_free(ca);
3101 			add_assoc_zval(zout, "extracerts", &zextracerts);
3102 		}
3103 
3104 		RETVAL_TRUE;
3105 	} else {
3106 		php_openssl_store_errors();
3107 	}
3108 
3109 	cleanup:
3110 	if (bio_in) {
3111 		BIO_free(bio_in);
3112 	}
3113 	if (pkey) {
3114 		EVP_PKEY_free(pkey);
3115 	}
3116 	if (cert) {
3117 		X509_free(cert);
3118 	}
3119 	if (p12) {
3120 		PKCS12_free(p12);
3121 	}
3122 }
3123 /* }}} */
3124 
3125 /* {{{ x509 CSR functions */
3126 
3127 /* {{{ php_openssl_make_REQ */
php_openssl_make_REQ(struct php_x509_request * req,X509_REQ * csr,zval * dn,zval * attribs)3128 static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, zval * dn, zval * attribs)
3129 {
3130 	STACK_OF(CONF_VALUE) * dn_sk, *attr_sk = NULL;
3131 	char * str, *dn_sect, *attr_sect;
3132 
3133 	dn_sect = CONF_get_string(req->req_config, req->section_name, "distinguished_name");
3134 	if (dn_sect == NULL) {
3135 		php_openssl_store_errors();
3136 		return FAILURE;
3137 	}
3138 	dn_sk = CONF_get_section(req->req_config, dn_sect);
3139 	if (dn_sk == NULL) {
3140 		php_openssl_store_errors();
3141 		return FAILURE;
3142 	}
3143 	attr_sect = php_openssl_conf_get_string(req->req_config, req->section_name, "attributes");
3144 	if (attr_sect == NULL) {
3145 		attr_sk = NULL;
3146 	} else {
3147 		attr_sk = CONF_get_section(req->req_config, attr_sect);
3148 		if (attr_sk == NULL) {
3149 			php_openssl_store_errors();
3150 			return FAILURE;
3151 		}
3152 	}
3153 	/* setup the version number: version 1 */
3154 	if (X509_REQ_set_version(csr, 0L)) {
3155 		int i, nid;
3156 		char * type;
3157 		CONF_VALUE * v;
3158 		X509_NAME * subj;
3159 		zval * item;
3160 		zend_string * strindex = NULL;
3161 
3162 		subj = X509_REQ_get_subject_name(csr);
3163 		/* apply values from the dn hash */
3164 		ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(dn), strindex, item) {
3165 			if (strindex) {
3166 				int nid = OBJ_txt2nid(ZSTR_VAL(strindex));
3167 				if (nid != NID_undef) {
3168 					zend_string *str_item = zval_try_get_string(item);
3169 					if (UNEXPECTED(!str_item)) {
3170 						return FAILURE;
3171 					}
3172 					if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8,
3173 								(unsigned char*)ZSTR_VAL(str_item), -1, -1, 0))
3174 					{
3175 						php_openssl_store_errors();
3176 						php_error_docref(NULL, E_WARNING,
3177 							"dn: add_entry_by_NID %d -> %s (failed; check error"
3178 							" queue and value of string_mask OpenSSL option "
3179 							"if illegal characters are reported)",
3180 							nid, ZSTR_VAL(str_item));
3181 						zend_string_release(str_item);
3182 						return FAILURE;
3183 					}
3184 					zend_string_release(str_item);
3185 				} else {
3186 					php_error_docref(NULL, E_WARNING, "dn: %s is not a recognized name", ZSTR_VAL(strindex));
3187 				}
3188 			}
3189 		} ZEND_HASH_FOREACH_END();
3190 
3191 		/* Finally apply defaults from config file */
3192 		for(i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) {
3193 			size_t len;
3194 			char buffer[200 + 1]; /*200 + \0 !*/
3195 
3196 			v = sk_CONF_VALUE_value(dn_sk, i);
3197 			type = v->name;
3198 
3199 			len = strlen(type);
3200 			if (len < sizeof("_default")) {
3201 				continue;
3202 			}
3203 			len -= sizeof("_default") - 1;
3204 			if (strcmp("_default", type + len) != 0) {
3205 				continue;
3206 			}
3207 			if (len > 200) {
3208 				len = 200;
3209 			}
3210 			memcpy(buffer, type, len);
3211 			buffer[len] = '\0';
3212 			type = buffer;
3213 
3214 			/* Skip past any leading X. X: X, etc to allow for multiple
3215 			 * instances */
3216 			for (str = type; *str; str++) {
3217 				if (*str == ':' || *str == ',' || *str == '.') {
3218 					str++;
3219 					if (*str) {
3220 						type = str;
3221 					}
3222 					break;
3223 				}
3224 			}
3225 			/* if it is already set, skip this */
3226 			nid = OBJ_txt2nid(type);
3227 			if (X509_NAME_get_index_by_NID(subj, nid, -1) >= 0) {
3228 				continue;
3229 			}
3230 			if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_UTF8, (unsigned char*)v->value, -1, -1, 0)) {
3231 				php_openssl_store_errors();
3232 				php_error_docref(NULL, E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value);
3233 				return FAILURE;
3234 			}
3235 			if (!X509_NAME_entry_count(subj)) {
3236 				php_error_docref(NULL, E_WARNING, "no objects specified in config file");
3237 				return FAILURE;
3238 			}
3239 		}
3240 		if (attribs) {
3241 			ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(attribs), strindex, item) {
3242 				int nid;
3243 
3244 				if (NULL == strindex) {
3245 					php_error_docref(NULL, E_WARNING, "dn: numeric fild names are not supported");
3246 					continue;
3247 				}
3248 
3249 				nid = OBJ_txt2nid(ZSTR_VAL(strindex));
3250 				if (nid != NID_undef) {
3251 					zend_string *str_item = zval_try_get_string(item);
3252 					if (UNEXPECTED(!str_item)) {
3253 						return FAILURE;
3254 					}
3255 					if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, (unsigned char*)ZSTR_VAL(str_item), -1, -1, 0)) {
3256 						php_openssl_store_errors();
3257 						php_error_docref(NULL, E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, ZSTR_VAL(str_item));
3258 						zend_string_release(str_item);
3259 						return FAILURE;
3260 					}
3261 					zend_string_release(str_item);
3262 				} else {
3263 					php_error_docref(NULL, E_WARNING, "dn: %s is not a recognized name", ZSTR_VAL(strindex));
3264 				}
3265 			} ZEND_HASH_FOREACH_END();
3266 			for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) {
3267 				v = sk_CONF_VALUE_value(attr_sk, i);
3268 				/* if it is already set, skip this */
3269 				nid = OBJ_txt2nid(v->name);
3270 				if (X509_REQ_get_attr_by_NID(csr, nid, -1) >= 0) {
3271 					continue;
3272 				}
3273 				if (!X509_REQ_add1_attr_by_txt(csr, v->name, MBSTRING_UTF8, (unsigned char*)v->value, -1)) {
3274 					php_openssl_store_errors();
3275 					php_error_docref(NULL, E_WARNING,
3276 						"add1_attr_by_txt %s -> %s (failed; check error queue "
3277 						"and value of string_mask OpenSSL option if illegal "
3278 						"characters are reported)",
3279 						v->name, v->value);
3280 					return FAILURE;
3281 				}
3282 			}
3283 		}
3284 	} else {
3285 		php_openssl_store_errors();
3286 	}
3287 
3288 	if (!X509_REQ_set_pubkey(csr, req->priv_key)) {
3289 		php_openssl_store_errors();
3290 	}
3291 	return SUCCESS;
3292 }
3293 /* }}} */
3294 
3295 /* {{{ php_openssl_csr_from_zval */
php_openssl_csr_from_zval(zval * val,int makeresource,zend_resource ** resourceval)3296 static X509_REQ * php_openssl_csr_from_zval(zval * val, int makeresource, zend_resource **resourceval)
3297 {
3298 	X509_REQ * csr = NULL;
3299 	char * filename = NULL;
3300 	BIO * in;
3301 
3302 	if (resourceval) {
3303 		*resourceval = NULL;
3304 	}
3305 	if (Z_TYPE_P(val) == IS_RESOURCE) {
3306 		void * what;
3307 		zend_resource *res = Z_RES_P(val);
3308 
3309 		what = zend_fetch_resource(res, "OpenSSL X.509 CSR", le_csr);
3310 		if (what) {
3311 			if (resourceval) {
3312 				*resourceval = res;
3313 				if (makeresource) {
3314 					Z_ADDREF_P(val);
3315 				}
3316 			}
3317 			return (X509_REQ*)what;
3318 		}
3319 		return NULL;
3320 	} else if (Z_TYPE_P(val) != IS_STRING) {
3321 		return NULL;
3322 	}
3323 
3324 	if (Z_STRLEN_P(val) > 7 && memcmp(Z_STRVAL_P(val), "file://", sizeof("file://") - 1) == 0) {
3325 		filename = Z_STRVAL_P(val) + (sizeof("file://") - 1);
3326 	}
3327 	if (filename) {
3328 		if (php_openssl_open_base_dir_chk(filename)) {
3329 			return NULL;
3330 		}
3331 		in = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
3332 	} else {
3333 		in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
3334 	}
3335 
3336 	if (in == NULL) {
3337 		php_openssl_store_errors();
3338 		return NULL;
3339 	}
3340 
3341 	csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL);
3342 	if (csr == NULL) {
3343 		php_openssl_store_errors();
3344 	}
3345 
3346 	BIO_free(in);
3347 
3348 	return csr;
3349 }
3350 /* }}} */
3351 
3352 /* {{{ proto bool openssl_csr_export_to_file(resource csr, string outfilename [, bool notext=true])
3353    Exports a CSR to file */
PHP_FUNCTION(openssl_csr_export_to_file)3354 PHP_FUNCTION(openssl_csr_export_to_file)
3355 {
3356 	X509_REQ * csr;
3357 	zval * zcsr = NULL;
3358 	zend_bool notext = 1;
3359 	char * filename = NULL;
3360 	size_t filename_len;
3361 	BIO * bio_out;
3362 	zend_resource *csr_resource;
3363 
3364 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rp|b", &zcsr, &filename, &filename_len, &notext) == FAILURE) {
3365 		return;
3366 	}
3367 	RETVAL_FALSE;
3368 
3369 	csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource);
3370 	if (csr == NULL) {
3371 		php_error_docref(NULL, E_WARNING, "cannot get CSR from parameter 1");
3372 		return;
3373 	}
3374 
3375 	if (php_openssl_open_base_dir_chk(filename)) {
3376 		return;
3377 	}
3378 
3379 	bio_out = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
3380 	if (bio_out != NULL) {
3381 		if (!notext && !X509_REQ_print(bio_out, csr)) {
3382 			php_openssl_store_errors();
3383 		}
3384 		if (!PEM_write_bio_X509_REQ(bio_out, csr)) {
3385 			php_error_docref(NULL, E_WARNING, "error writing PEM to file %s", filename);
3386 			php_openssl_store_errors();
3387 		} else {
3388 			RETVAL_TRUE;
3389 		}
3390 		BIO_free(bio_out);
3391 	} else {
3392 		php_openssl_store_errors();
3393 		php_error_docref(NULL, E_WARNING, "error opening file %s", filename);
3394 	}
3395 
3396 	if (csr_resource == NULL && csr != NULL) {
3397 		X509_REQ_free(csr);
3398 	}
3399 }
3400 /* }}} */
3401 
3402 /* {{{ proto bool openssl_csr_export(resource csr, string &out [, bool notext=true])
3403    Exports a CSR to file or a var */
PHP_FUNCTION(openssl_csr_export)3404 PHP_FUNCTION(openssl_csr_export)
3405 {
3406 	X509_REQ * csr;
3407 	zval * zcsr = NULL, *zout=NULL;
3408 	zend_bool notext = 1;
3409 	BIO * bio_out;
3410 	zend_resource *csr_resource;
3411 
3412 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz|b", &zcsr, &zout, &notext) == FAILURE) {
3413 		return;
3414 	}
3415 
3416 	RETVAL_FALSE;
3417 
3418 	csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource);
3419 	if (csr == NULL) {
3420 		php_error_docref(NULL, E_WARNING, "cannot get CSR from parameter 1");
3421 		return;
3422 	}
3423 
3424 	/* export to a var */
3425 
3426 	bio_out = BIO_new(BIO_s_mem());
3427 	if (!notext && !X509_REQ_print(bio_out, csr)) {
3428 		php_openssl_store_errors();
3429 	}
3430 
3431 	if (PEM_write_bio_X509_REQ(bio_out, csr)) {
3432 		BUF_MEM *bio_buf;
3433 
3434 		BIO_get_mem_ptr(bio_out, &bio_buf);
3435 		ZEND_TRY_ASSIGN_REF_STRINGL(zout, bio_buf->data, bio_buf->length);
3436 
3437 		RETVAL_TRUE;
3438 	} else {
3439 		php_openssl_store_errors();
3440 	}
3441 
3442 	if (csr_resource == NULL && csr) {
3443 		X509_REQ_free(csr);
3444 	}
3445 	BIO_free(bio_out);
3446 }
3447 /* }}} */
3448 
3449 /* {{{ proto resource openssl_csr_sign(mixed csr, mixed x509, mixed priv_key, int days [, array config_args [, int serial]])
3450    Signs a cert with another CERT */
PHP_FUNCTION(openssl_csr_sign)3451 PHP_FUNCTION(openssl_csr_sign)
3452 {
3453 	zval * zcert = NULL, *zcsr, *zpkey, *args = NULL;
3454 	zend_long num_days;
3455 	zend_long serial = Z_L(0);
3456 	X509 * cert = NULL, *new_cert = NULL;
3457 	X509_REQ * csr;
3458 	EVP_PKEY * key = NULL, *priv_key = NULL;
3459 	zend_resource *csr_resource, *certresource = NULL, *keyresource = NULL;
3460 	int i;
3461 	struct php_x509_request req;
3462 
3463 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz!zl|a!l", &zcsr, &zcert, &zpkey, &num_days, &args, &serial) == FAILURE)
3464 		return;
3465 
3466 	RETVAL_FALSE;
3467 	PHP_SSL_REQ_INIT(&req);
3468 
3469 	csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource);
3470 	if (csr == NULL) {
3471 		php_error_docref(NULL, E_WARNING, "cannot get CSR from parameter 1");
3472 		return;
3473 	}
3474 	if (zcert) {
3475 		cert = php_openssl_x509_from_zval(zcert, 0, &certresource);
3476 		if (cert == NULL) {
3477 			php_error_docref(NULL, E_WARNING, "cannot get cert from parameter 2");
3478 			goto cleanup;
3479 		}
3480 	}
3481 	priv_key = php_openssl_evp_from_zval(zpkey, 0, "", 0, 1, &keyresource);
3482 	if (priv_key == NULL) {
3483 		php_error_docref(NULL, E_WARNING, "cannot get private key from parameter 3");
3484 		goto cleanup;
3485 	}
3486 	if (cert && !X509_check_private_key(cert, priv_key)) {
3487 		php_openssl_store_errors();
3488 		php_error_docref(NULL, E_WARNING, "private key does not correspond to signing cert");
3489 		goto cleanup;
3490 	}
3491 
3492 	if (PHP_SSL_REQ_PARSE(&req, args) == FAILURE) {
3493 		goto cleanup;
3494 	}
3495 	/* Check that the request matches the signature */
3496 	key = X509_REQ_get_pubkey(csr);
3497 	if (key == NULL) {
3498 		php_openssl_store_errors();
3499 		php_error_docref(NULL, E_WARNING, "error unpacking public key");
3500 		goto cleanup;
3501 	}
3502 	i = X509_REQ_verify(csr, key);
3503 
3504 	if (i < 0) {
3505 		php_openssl_store_errors();
3506 		php_error_docref(NULL, E_WARNING, "Signature verification problems");
3507 		goto cleanup;
3508 	}
3509 	else if (i == 0) {
3510 		php_error_docref(NULL, E_WARNING, "Signature did not match the certificate request");
3511 		goto cleanup;
3512 	}
3513 
3514 	/* Now we can get on with it */
3515 
3516 	new_cert = X509_new();
3517 	if (new_cert == NULL) {
3518 		php_openssl_store_errors();
3519 		php_error_docref(NULL, E_WARNING, "No memory");
3520 		goto cleanup;
3521 	}
3522 	/* Version 3 cert */
3523 	if (!X509_set_version(new_cert, 2)) {
3524 		goto cleanup;
3525 	}
3526 
3527 #if PHP_OPENSSL_API_VERSION >= 0x10100 && !defined (LIBRESSL_VERSION_NUMBER)
3528 	ASN1_INTEGER_set_int64(X509_get_serialNumber(new_cert), serial);
3529 #else
3530 	ASN1_INTEGER_set(X509_get_serialNumber(new_cert), serial);
3531 #endif
3532 
3533 	X509_set_subject_name(new_cert, X509_REQ_get_subject_name(csr));
3534 
3535 	if (cert == NULL) {
3536 		cert = new_cert;
3537 	}
3538 	if (!X509_set_issuer_name(new_cert, X509_get_subject_name(cert))) {
3539 		php_openssl_store_errors();
3540 		goto cleanup;
3541 	}
3542 	X509_gmtime_adj(X509_getm_notBefore(new_cert), 0);
3543 	X509_gmtime_adj(X509_getm_notAfter(new_cert), 60*60*24*(long)num_days);
3544 	i = X509_set_pubkey(new_cert, key);
3545 	if (!i) {
3546 		php_openssl_store_errors();
3547 		goto cleanup;
3548 	}
3549 	if (req.extensions_section) {
3550 		X509V3_CTX ctx;
3551 
3552 		X509V3_set_ctx(&ctx, cert, new_cert, csr, NULL, 0);
3553 		X509V3_set_conf_lhash(&ctx, req.req_config);
3554 		if (!X509V3_EXT_add_conf(req.req_config, &ctx, req.extensions_section, new_cert)) {
3555 			php_openssl_store_errors();
3556 			goto cleanup;
3557 		}
3558 	}
3559 
3560 	/* Now sign it */
3561 	if (!X509_sign(new_cert, priv_key, req.digest)) {
3562 		php_openssl_store_errors();
3563 		php_error_docref(NULL, E_WARNING, "failed to sign it");
3564 		goto cleanup;
3565 	}
3566 
3567 	/* Succeeded; lets return the cert */
3568 	ZVAL_RES(return_value, zend_register_resource(new_cert, le_x509));
3569 	new_cert = NULL;
3570 
3571 cleanup:
3572 
3573 	if (cert == new_cert) {
3574 		cert = NULL;
3575 	}
3576 	PHP_SSL_REQ_DISPOSE(&req);
3577 
3578 	if (keyresource == NULL && priv_key) {
3579 		EVP_PKEY_free(priv_key);
3580 	}
3581 	if (key) {
3582 		EVP_PKEY_free(key);
3583 	}
3584 	if (csr_resource == NULL && csr) {
3585 		X509_REQ_free(csr);
3586 	}
3587 	if (zcert && certresource == NULL && cert) {
3588 		X509_free(cert);
3589 	}
3590 	if (new_cert) {
3591 		X509_free(new_cert);
3592 	}
3593 }
3594 /* }}} */
3595 
3596 /* {{{ proto bool openssl_csr_new(array dn, resource &privkey [, array configargs [, array extraattribs]])
3597    Generates a privkey and CSR */
PHP_FUNCTION(openssl_csr_new)3598 PHP_FUNCTION(openssl_csr_new)
3599 {
3600 	struct php_x509_request req;
3601 	zval * args = NULL, * dn, *attribs = NULL;
3602 	zval * out_pkey;
3603 	X509_REQ * csr = NULL;
3604 	int we_made_the_key = 1;
3605 	zend_resource *key_resource;
3606 
3607 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "az|a!a!", &dn, &out_pkey, &args, &attribs) == FAILURE) {
3608 		return;
3609 	}
3610 	RETVAL_FALSE;
3611 
3612 	PHP_SSL_REQ_INIT(&req);
3613 
3614 	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
3615 		zval *out_pkey_val = out_pkey;
3616 		ZVAL_DEREF(out_pkey_val);
3617 
3618 		/* Generate or use a private key */
3619 		if (Z_TYPE_P(out_pkey_val) != IS_NULL) {
3620 			req.priv_key = php_openssl_evp_from_zval(out_pkey_val, 0, NULL, 0, 0, &key_resource);
3621 			if (req.priv_key != NULL) {
3622 				we_made_the_key = 0;
3623 			}
3624 		}
3625 		if (req.priv_key == NULL) {
3626 			php_openssl_generate_private_key(&req);
3627 		}
3628 		if (req.priv_key == NULL) {
3629 			php_error_docref(NULL, E_WARNING, "Unable to generate a private key");
3630 		} else {
3631 			csr = X509_REQ_new();
3632 			if (csr) {
3633 				if (php_openssl_make_REQ(&req, csr, dn, attribs) == SUCCESS) {
3634 					X509V3_CTX ext_ctx;
3635 
3636 					X509V3_set_ctx(&ext_ctx, NULL, NULL, csr, NULL, 0);
3637 					X509V3_set_conf_lhash(&ext_ctx, req.req_config);
3638 
3639 					/* Add extensions */
3640 					if (req.request_extensions_section && !X509V3_EXT_REQ_add_conf(req.req_config,
3641 								&ext_ctx, req.request_extensions_section, csr))
3642 					{
3643 						php_openssl_store_errors();
3644 						php_error_docref(NULL, E_WARNING, "Error loading extension section %s", req.request_extensions_section);
3645 					} else {
3646 						RETVAL_TRUE;
3647 
3648 						if (X509_REQ_sign(csr, req.priv_key, req.digest)) {
3649 							ZVAL_RES(return_value, zend_register_resource(csr, le_csr));
3650 							csr = NULL;
3651 						} else {
3652 							php_openssl_store_errors();
3653 							php_error_docref(NULL, E_WARNING, "Error signing request");
3654 						}
3655 
3656 						if (we_made_the_key) {
3657 							/* and a resource for the private key */
3658 							ZEND_TRY_ASSIGN_REF_RES(out_pkey, zend_register_resource(req.priv_key, le_key));
3659 							req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
3660 						} else if (key_resource != NULL) {
3661 							req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
3662 						}
3663 					}
3664 				}
3665 				else {
3666 					if (!we_made_the_key) {
3667 						/* if we have not made the key we are not supposed to zap it by calling dispose! */
3668 						req.priv_key = NULL;
3669 					}
3670 				}
3671 			} else {
3672 				php_openssl_store_errors();
3673 			}
3674 
3675 		}
3676 	}
3677 	if (csr) {
3678 		X509_REQ_free(csr);
3679 	}
3680 	PHP_SSL_REQ_DISPOSE(&req);
3681 }
3682 /* }}} */
3683 
3684 /* {{{ proto mixed openssl_csr_get_subject(mixed csr)
3685    Returns the subject of a CERT or FALSE on error */
PHP_FUNCTION(openssl_csr_get_subject)3686 PHP_FUNCTION(openssl_csr_get_subject)
3687 {
3688 	zval * zcsr;
3689 	zend_bool use_shortnames = 1;
3690 	zend_resource *csr_resource;
3691 	X509_NAME * subject;
3692 	X509_REQ * csr;
3693 
3694 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &zcsr, &use_shortnames) == FAILURE) {
3695 		return;
3696 	}
3697 
3698 	csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource);
3699 
3700 	if (csr == NULL) {
3701 		RETURN_FALSE;
3702 	}
3703 
3704 	subject = X509_REQ_get_subject_name(csr);
3705 
3706 	array_init(return_value);
3707 	php_openssl_add_assoc_name_entry(return_value, NULL, subject, use_shortnames);
3708 
3709 	if (!csr_resource) {
3710 		X509_REQ_free(csr);
3711 	}
3712 }
3713 /* }}} */
3714 
3715 /* {{{ proto mixed openssl_csr_get_public_key(mixed csr)
3716 	Returns the subject of a CERT or FALSE on error */
PHP_FUNCTION(openssl_csr_get_public_key)3717 PHP_FUNCTION(openssl_csr_get_public_key)
3718 {
3719 	zval * zcsr;
3720 	zend_bool use_shortnames = 1;
3721 	zend_resource *csr_resource;
3722 
3723 	X509_REQ *orig_csr, *csr;
3724 	EVP_PKEY *tpubkey;
3725 
3726 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &zcsr, &use_shortnames) == FAILURE) {
3727 		return;
3728 	}
3729 
3730 	orig_csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource);
3731 
3732 	if (orig_csr == NULL) {
3733 		RETURN_FALSE;
3734 	}
3735 
3736 #if PHP_OPENSSL_API_VERSION >= 0x10100
3737 	/* Due to changes in OpenSSL 1.1 related to locking when decoding CSR,
3738 	 * the pub key is not changed after assigning. It means if we pass
3739 	 * a private key, it will be returned including the private part.
3740 	 * If we duplicate it, then we get just the public part which is
3741 	 * the same behavior as for OpenSSL 1.0 */
3742 	csr = X509_REQ_dup(orig_csr);
3743 #else
3744 	csr = orig_csr;
3745 #endif
3746 
3747 	/* Retrieve the public key from the CSR */
3748 	tpubkey = X509_REQ_get_pubkey(csr);
3749 
3750 	if (csr != orig_csr) {
3751 		/* We need to free the duplicated CSR */
3752 		X509_REQ_free(csr);
3753 	}
3754 
3755 	if (!csr_resource) {
3756 		/* We also need to free the original CSR if it was freshly created */
3757 		X509_REQ_free(orig_csr);
3758 	}
3759 
3760 	if (tpubkey == NULL) {
3761 		php_openssl_store_errors();
3762 		RETURN_FALSE;
3763 	}
3764 
3765 	RETURN_RES(zend_register_resource(tpubkey, le_key));
3766 }
3767 /* }}} */
3768 
3769 /* }}} */
3770 
3771 /* {{{ EVP Public/Private key functions */
3772 
3773 struct php_openssl_pem_password {
3774 	char *key;
3775 	int len;
3776 };
3777 
3778 /* {{{ php_openssl_pem_password_cb */
php_openssl_pem_password_cb(char * buf,int size,int rwflag,void * userdata)3779 static int php_openssl_pem_password_cb(char *buf, int size, int rwflag, void *userdata)
3780 {
3781 	struct php_openssl_pem_password *password = userdata;
3782 
3783 	if (password == NULL || password->key == NULL) {
3784 		return -1;
3785 	}
3786 
3787 	size = (password->len > size) ? size : password->len;
3788 	memcpy(buf, password->key, size);
3789 
3790 	return size;
3791 }
3792 /* }}} */
3793 
3794 /* {{{ php_openssl_evp_from_zval
3795    Given a zval, coerce it into a EVP_PKEY object.
3796 	It can be:
3797 		1. private key resource from openssl_get_privatekey()
3798 		2. X509 resource -> public key will be extracted from it
3799 		3. if it starts with file:// interpreted as path to key file
3800 		4. interpreted as the data from the cert/key file and interpreted in same way as openssl_get_privatekey()
3801 		5. an array(0 => [items 2..4], 1 => passphrase)
3802 		6. if val is a string (possibly starting with file:///) and it is not an X509 certificate, then interpret as public key
3803 	NOTE: If you are requesting a private key but have not specified a passphrase, you should use an
3804 	empty string rather than NULL for the passphrase - NULL causes a passphrase prompt to be emitted in
3805 	the Apache error log!
3806 */
php_openssl_evp_from_zval(zval * val,int public_key,char * passphrase,size_t passphrase_len,int makeresource,zend_resource ** resourceval)3807 static EVP_PKEY * php_openssl_evp_from_zval(
3808 		zval * val, int public_key, char *passphrase, size_t passphrase_len,
3809 		int makeresource, zend_resource **resourceval)
3810 {
3811 	EVP_PKEY * key = NULL;
3812 	X509 * cert = NULL;
3813 	int free_cert = 0;
3814 	zend_resource *cert_res = NULL;
3815 	char * filename = NULL;
3816 	zval tmp;
3817 
3818 	ZVAL_NULL(&tmp);
3819 
3820 #define TMP_CLEAN \
3821 	if (Z_TYPE(tmp) == IS_STRING) {\
3822 		zval_ptr_dtor_str(&tmp); \
3823 	} \
3824 	return NULL;
3825 
3826 	if (resourceval) {
3827 		*resourceval = NULL;
3828 	}
3829 	if (Z_TYPE_P(val) == IS_ARRAY) {
3830 		zval * zphrase;
3831 
3832 		/* get passphrase */
3833 
3834 		if ((zphrase = zend_hash_index_find(Z_ARRVAL_P(val), 1)) == NULL) {
3835 			php_error_docref(NULL, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)");
3836 			return NULL;
3837 		}
3838 
3839 		if (Z_TYPE_P(zphrase) == IS_STRING) {
3840 			passphrase = Z_STRVAL_P(zphrase);
3841 			passphrase_len = Z_STRLEN_P(zphrase);
3842 		} else {
3843 			ZVAL_COPY(&tmp, zphrase);
3844 			if (!try_convert_to_string(&tmp)) {
3845 				return NULL;
3846 			}
3847 
3848 			passphrase = Z_STRVAL(tmp);
3849 			passphrase_len = Z_STRLEN(tmp);
3850 		}
3851 
3852 		/* now set val to be the key param and continue */
3853 		if ((val = zend_hash_index_find(Z_ARRVAL_P(val), 0)) == NULL) {
3854 			php_error_docref(NULL, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)");
3855 			TMP_CLEAN;
3856 		}
3857 	}
3858 
3859 	if (Z_TYPE_P(val) == IS_RESOURCE) {
3860 		void * what;
3861 		zend_resource * res = Z_RES_P(val);
3862 
3863 		what = zend_fetch_resource2(res, "OpenSSL X.509/key", le_x509, le_key);
3864 		if (!what) {
3865 			TMP_CLEAN;
3866 		}
3867 		if (res->type == le_x509) {
3868 			/* extract key from cert, depending on public_key param */
3869 			cert = (X509*)what;
3870 			free_cert = 0;
3871 		} else if (res->type == le_key) {
3872 			int is_priv;
3873 
3874 			is_priv = php_openssl_is_private_key((EVP_PKEY*)what);
3875 
3876 			/* check whether it is actually a private key if requested */
3877 			if (!public_key && !is_priv) {
3878 				php_error_docref(NULL, E_WARNING, "supplied key param is a public key");
3879 				TMP_CLEAN;
3880 			}
3881 
3882 			if (public_key && is_priv) {
3883 				php_error_docref(NULL, E_WARNING, "Don't know how to get public key from this private key");
3884 				TMP_CLEAN;
3885 			} else {
3886 				if (Z_TYPE(tmp) == IS_STRING) {
3887 					zval_ptr_dtor_str(&tmp);
3888 				}
3889 				/* got the key - return it */
3890 				if (resourceval) {
3891 					*resourceval = res;
3892 					Z_ADDREF_P(val);
3893 				}
3894 				return (EVP_PKEY*)what;
3895 			}
3896 		} else {
3897 			/* other types could be used here - eg: file pointers and read in the data from them */
3898 			TMP_CLEAN;
3899 		}
3900 	} else {
3901 		/* force it to be a string and check if it refers to a file */
3902 		/* passing non string values leaks, object uses toString, it returns NULL
3903 		 * See bug38255.phpt
3904 		 */
3905 		if (!(Z_TYPE_P(val) == IS_STRING || Z_TYPE_P(val) == IS_OBJECT)) {
3906 			TMP_CLEAN;
3907 		}
3908 		if (!try_convert_to_string(val)) {
3909 			TMP_CLEAN;
3910 		}
3911 
3912 		if (Z_STRLEN_P(val) > 7 && memcmp(Z_STRVAL_P(val), "file://", sizeof("file://") - 1) == 0) {
3913 			filename = Z_STRVAL_P(val) + (sizeof("file://") - 1);
3914 			if (php_openssl_open_base_dir_chk(filename)) {
3915 				TMP_CLEAN;
3916 			}
3917 		}
3918 		/* it's an X509 file/cert of some kind, and we need to extract the data from that */
3919 		if (public_key) {
3920 			cert = php_openssl_x509_from_zval(val, 0, &cert_res);
3921 			free_cert = (cert_res == NULL);
3922 			/* actual extraction done later */
3923 			if (!cert) {
3924 				/* not a X509 certificate, try to retrieve public key */
3925 				BIO* in;
3926 				if (filename) {
3927 					in = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
3928 				} else {
3929 					in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
3930 				}
3931 				if (in == NULL) {
3932 					php_openssl_store_errors();
3933 					TMP_CLEAN;
3934 				}
3935 				key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL);
3936 				BIO_free(in);
3937 			}
3938 		} else {
3939 			/* we want the private key */
3940 			BIO *in;
3941 
3942 			if (filename) {
3943 				in = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
3944 			} else {
3945 				in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
3946 			}
3947 
3948 			if (in == NULL) {
3949 				TMP_CLEAN;
3950 			}
3951 			if (passphrase == NULL) {
3952 				key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
3953 			} else {
3954 				struct php_openssl_pem_password password;
3955 				password.key = passphrase;
3956 				password.len = passphrase_len;
3957 				key = PEM_read_bio_PrivateKey(in, NULL, php_openssl_pem_password_cb, &password);
3958 			}
3959 			BIO_free(in);
3960 		}
3961 	}
3962 
3963 	if (key == NULL) {
3964 		php_openssl_store_errors();
3965 
3966 		if (public_key && cert) {
3967 			/* extract public key from X509 cert */
3968 			key = (EVP_PKEY *) X509_get_pubkey(cert);
3969 			if (key == NULL) {
3970 				php_openssl_store_errors();
3971 			}
3972 		}
3973 	}
3974 
3975 	if (free_cert && cert) {
3976 		X509_free(cert);
3977 	}
3978 	if (key && makeresource && resourceval) {
3979 		*resourceval = zend_register_resource(key, le_key);
3980 	}
3981 	if (Z_TYPE(tmp) == IS_STRING) {
3982 		zval_ptr_dtor_str(&tmp);
3983 	}
3984 	return key;
3985 }
3986 /* }}} */
3987 
3988 /* {{{ php_openssl_generate_private_key */
php_openssl_generate_private_key(struct php_x509_request * req)3989 static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req)
3990 {
3991 	char * randfile = NULL;
3992 	int egdsocket, seeded;
3993 	EVP_PKEY * return_val = NULL;
3994 
3995 	if (req->priv_key_bits < MIN_KEY_LENGTH) {
3996 		php_error_docref(NULL, E_WARNING, "private key length is too short; it needs to be at least %d bits, not %d",
3997 				MIN_KEY_LENGTH, req->priv_key_bits);
3998 		return NULL;
3999 	}
4000 
4001 	randfile = php_openssl_conf_get_string(req->req_config, req->section_name, "RANDFILE");
4002 	php_openssl_load_rand_file(randfile, &egdsocket, &seeded);
4003 
4004 	if ((req->priv_key = EVP_PKEY_new()) != NULL) {
4005 		switch(req->priv_key_type) {
4006 			case OPENSSL_KEYTYPE_RSA:
4007 				{
4008 					RSA* rsaparam;
4009 #if OPENSSL_VERSION_NUMBER < 0x10002000L
4010 					/* OpenSSL 1.0.2 deprecates RSA_generate_key */
4011 					PHP_OPENSSL_RAND_ADD_TIME();
4012 					rsaparam = (RSA*)RSA_generate_key(req->priv_key_bits, RSA_F4, NULL, NULL);
4013 #else
4014 					{
4015 						BIGNUM *bne = (BIGNUM *)BN_new();
4016 						if (BN_set_word(bne, RSA_F4) != 1) {
4017 							BN_free(bne);
4018 							php_error_docref(NULL, E_WARNING, "failed setting exponent");
4019 							return NULL;
4020 						}
4021 						rsaparam = RSA_new();
4022 						PHP_OPENSSL_RAND_ADD_TIME();
4023 						if (rsaparam == NULL || !RSA_generate_key_ex(rsaparam, req->priv_key_bits, bne, NULL)) {
4024 							php_openssl_store_errors();
4025 							RSA_free(rsaparam);
4026 							rsaparam = NULL;
4027 						}
4028 						BN_free(bne);
4029 					}
4030 #endif
4031 					if (rsaparam && EVP_PKEY_assign_RSA(req->priv_key, rsaparam)) {
4032 						return_val = req->priv_key;
4033 					} else {
4034 						php_openssl_store_errors();
4035 					}
4036 				}
4037 				break;
4038 #if !defined(NO_DSA)
4039 			case OPENSSL_KEYTYPE_DSA:
4040 				PHP_OPENSSL_RAND_ADD_TIME();
4041 				{
4042 					DSA *dsaparam = DSA_new();
4043 					if (dsaparam && DSA_generate_parameters_ex(dsaparam, req->priv_key_bits, NULL, 0, NULL, NULL, NULL)) {
4044 						DSA_set_method(dsaparam, DSA_get_default_method());
4045 						if (DSA_generate_key(dsaparam)) {
4046 							if (EVP_PKEY_assign_DSA(req->priv_key, dsaparam)) {
4047 								return_val = req->priv_key;
4048 							} else {
4049 								php_openssl_store_errors();
4050 							}
4051 						} else {
4052 							php_openssl_store_errors();
4053 							DSA_free(dsaparam);
4054 						}
4055 					} else {
4056 						php_openssl_store_errors();
4057 					}
4058 				}
4059 				break;
4060 #endif
4061 #if !defined(NO_DH)
4062 			case OPENSSL_KEYTYPE_DH:
4063 				PHP_OPENSSL_RAND_ADD_TIME();
4064 				{
4065 					int codes = 0;
4066 					DH *dhparam = DH_new();
4067 					if (dhparam && DH_generate_parameters_ex(dhparam, req->priv_key_bits, 2, NULL)) {
4068 						DH_set_method(dhparam, DH_get_default_method());
4069 						if (DH_check(dhparam, &codes) && codes == 0 && DH_generate_key(dhparam)) {
4070 							if (EVP_PKEY_assign_DH(req->priv_key, dhparam)) {
4071 								return_val = req->priv_key;
4072 							} else {
4073 								php_openssl_store_errors();
4074 							}
4075 						} else {
4076 							php_openssl_store_errors();
4077 							DH_free(dhparam);
4078 						}
4079 					} else {
4080 						php_openssl_store_errors();
4081 					}
4082 				}
4083 				break;
4084 #endif
4085 #ifdef HAVE_EVP_PKEY_EC
4086 			case OPENSSL_KEYTYPE_EC:
4087 				{
4088 					EC_KEY *eckey;
4089 					if (req->curve_name == NID_undef) {
4090 						php_error_docref(NULL, E_WARNING, "Missing configuration value: 'curve_name' not set");
4091 						return NULL;
4092 					}
4093 					eckey = EC_KEY_new_by_curve_name(req->curve_name);
4094 					if (eckey) {
4095 						EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
4096 						if (EC_KEY_generate_key(eckey) &&
4097 							EVP_PKEY_assign_EC_KEY(req->priv_key, eckey)) {
4098 							return_val = req->priv_key;
4099 						} else {
4100 							EC_KEY_free(eckey);
4101 						}
4102 					}
4103 				}
4104 				break;
4105 #endif
4106 			default:
4107 				php_error_docref(NULL, E_WARNING, "Unsupported private key type");
4108 		}
4109 	} else {
4110 		php_openssl_store_errors();
4111 	}
4112 
4113 	php_openssl_write_rand_file(randfile, egdsocket, seeded);
4114 
4115 	if (return_val == NULL) {
4116 		EVP_PKEY_free(req->priv_key);
4117 		req->priv_key = NULL;
4118 		return NULL;
4119 	}
4120 
4121 	return return_val;
4122 }
4123 /* }}} */
4124 
4125 /* {{{ php_openssl_is_private_key
4126 	Check whether the supplied key is a private key by checking if the secret prime factors are set */
php_openssl_is_private_key(EVP_PKEY * pkey)4127 static int php_openssl_is_private_key(EVP_PKEY* pkey)
4128 {
4129 	assert(pkey != NULL);
4130 
4131 	switch (EVP_PKEY_id(pkey)) {
4132 		case EVP_PKEY_RSA:
4133 		case EVP_PKEY_RSA2:
4134 			{
4135 				RSA *rsa = EVP_PKEY_get0_RSA(pkey);
4136 				if (rsa != NULL) {
4137 					const BIGNUM *p, *q;
4138 
4139 					RSA_get0_factors(rsa, &p, &q);
4140 					 if (p == NULL || q == NULL) {
4141 						return 0;
4142 					 }
4143 				}
4144 			}
4145 			break;
4146 		case EVP_PKEY_DSA:
4147 		case EVP_PKEY_DSA1:
4148 		case EVP_PKEY_DSA2:
4149 		case EVP_PKEY_DSA3:
4150 		case EVP_PKEY_DSA4:
4151 			{
4152 				DSA *dsa = EVP_PKEY_get0_DSA(pkey);
4153 				if (dsa != NULL) {
4154 					const BIGNUM *p, *q, *g, *pub_key, *priv_key;
4155 
4156 					DSA_get0_pqg(dsa, &p, &q, &g);
4157 					if (p == NULL || q == NULL) {
4158 						return 0;
4159 					}
4160 
4161 					DSA_get0_key(dsa, &pub_key, &priv_key);
4162 					if (priv_key == NULL) {
4163 						return 0;
4164 					}
4165 				}
4166 			}
4167 			break;
4168 		case EVP_PKEY_DH:
4169 			{
4170 				DH *dh = EVP_PKEY_get0_DH(pkey);
4171 				if (dh != NULL) {
4172 					const BIGNUM *p, *q, *g, *pub_key, *priv_key;
4173 
4174 					DH_get0_pqg(dh, &p, &q, &g);
4175 					if (p == NULL) {
4176 						return 0;
4177 					}
4178 
4179 					DH_get0_key(dh, &pub_key, &priv_key);
4180 					if (priv_key == NULL) {
4181 						return 0;
4182 					}
4183 				}
4184 			}
4185 			break;
4186 #ifdef HAVE_EVP_PKEY_EC
4187 		case EVP_PKEY_EC:
4188 			{
4189 				EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
4190 				if (ec != NULL && NULL == EC_KEY_get0_private_key(ec)) {
4191 					return 0;
4192 				}
4193 			}
4194 			break;
4195 #endif
4196 		default:
4197 			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
4198 			break;
4199 	}
4200 	return 1;
4201 }
4202 /* }}} */
4203 
4204 #define OPENSSL_GET_BN(_array, _bn, _name) do { \
4205 		if (_bn != NULL) { \
4206 			int len = BN_num_bytes(_bn); \
4207 			zend_string *str = zend_string_alloc(len, 0); \
4208 			BN_bn2bin(_bn, (unsigned char*)ZSTR_VAL(str)); \
4209 			ZSTR_VAL(str)[len] = 0; \
4210 			add_assoc_str(&_array, #_name, str); \
4211 		} \
4212 	} while (0);
4213 
4214 #define OPENSSL_PKEY_GET_BN(_type, _name) OPENSSL_GET_BN(_type, _name, _name)
4215 
4216 #define OPENSSL_PKEY_SET_BN(_data, _name) do { \
4217 		zval *bn; \
4218 		if ((bn = zend_hash_str_find(Z_ARRVAL_P(_data), #_name, sizeof(#_name)-1)) != NULL && \
4219 				Z_TYPE_P(bn) == IS_STRING) { \
4220 			_name = BN_bin2bn( \
4221 				(unsigned char*)Z_STRVAL_P(bn), \
4222 				(int)Z_STRLEN_P(bn), NULL); \
4223 		} else { \
4224 			_name = NULL; \
4225 		} \
4226 	} while (0);
4227 
4228 /* {{{ php_openssl_pkey_init_rsa */
php_openssl_pkey_init_and_assign_rsa(EVP_PKEY * pkey,RSA * rsa,zval * data)4229 static zend_bool php_openssl_pkey_init_and_assign_rsa(EVP_PKEY *pkey, RSA *rsa, zval *data)
4230 {
4231 	BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
4232 
4233 	OPENSSL_PKEY_SET_BN(data, n);
4234 	OPENSSL_PKEY_SET_BN(data, e);
4235 	OPENSSL_PKEY_SET_BN(data, d);
4236 	if (!n || !d || !RSA_set0_key(rsa, n, e, d)) {
4237 		return 0;
4238 	}
4239 
4240 	OPENSSL_PKEY_SET_BN(data, p);
4241 	OPENSSL_PKEY_SET_BN(data, q);
4242 	if ((p || q) && !RSA_set0_factors(rsa, p, q)) {
4243 		return 0;
4244 	}
4245 
4246 	OPENSSL_PKEY_SET_BN(data, dmp1);
4247 	OPENSSL_PKEY_SET_BN(data, dmq1);
4248 	OPENSSL_PKEY_SET_BN(data, iqmp);
4249 	if ((dmp1 || dmq1 || iqmp) && !RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp)) {
4250 		return 0;
4251 	}
4252 
4253 	if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
4254 		php_openssl_store_errors();
4255 		return 0;
4256 	}
4257 
4258 	return 1;
4259 }
4260 
4261 /* {{{ php_openssl_pkey_init_dsa */
php_openssl_pkey_init_dsa(DSA * dsa,zval * data)4262 static zend_bool php_openssl_pkey_init_dsa(DSA *dsa, zval *data)
4263 {
4264 	BIGNUM *p, *q, *g, *priv_key, *pub_key;
4265 	const BIGNUM *priv_key_const, *pub_key_const;
4266 
4267 	OPENSSL_PKEY_SET_BN(data, p);
4268 	OPENSSL_PKEY_SET_BN(data, q);
4269 	OPENSSL_PKEY_SET_BN(data, g);
4270 	if (!p || !q || !g || !DSA_set0_pqg(dsa, p, q, g)) {
4271 		return 0;
4272 	}
4273 
4274 	OPENSSL_PKEY_SET_BN(data, pub_key);
4275 	OPENSSL_PKEY_SET_BN(data, priv_key);
4276 	if (pub_key) {
4277 		return DSA_set0_key(dsa, pub_key, priv_key);
4278 	}
4279 
4280 	/* generate key */
4281 	PHP_OPENSSL_RAND_ADD_TIME();
4282 	if (!DSA_generate_key(dsa)) {
4283 		php_openssl_store_errors();
4284 		return 0;
4285 	}
4286 
4287 	/* if BN_mod_exp return -1, then DSA_generate_key succeed for failed key
4288 	 * so we need to double check that public key is created */
4289 	DSA_get0_key(dsa, &pub_key_const, &priv_key_const);
4290 	if (!pub_key_const || BN_is_zero(pub_key_const)) {
4291 		return 0;
4292 	}
4293 	/* all good */
4294 	return 1;
4295 }
4296 /* }}} */
4297 
4298 /* {{{ php_openssl_dh_pub_from_priv */
php_openssl_dh_pub_from_priv(BIGNUM * priv_key,BIGNUM * g,BIGNUM * p)4299 static BIGNUM *php_openssl_dh_pub_from_priv(BIGNUM *priv_key, BIGNUM *g, BIGNUM *p)
4300 {
4301 	BIGNUM *pub_key, *priv_key_const_time;
4302 	BN_CTX *ctx;
4303 
4304 	pub_key = BN_new();
4305 	if (pub_key == NULL) {
4306 		php_openssl_store_errors();
4307 		return NULL;
4308 	}
4309 
4310 	priv_key_const_time = BN_new();
4311 	if (priv_key_const_time == NULL) {
4312 		BN_free(pub_key);
4313 		php_openssl_store_errors();
4314 		return NULL;
4315 	}
4316 	ctx = BN_CTX_new();
4317 	if (ctx == NULL) {
4318 		BN_free(pub_key);
4319 		BN_free(priv_key_const_time);
4320 		php_openssl_store_errors();
4321 		return NULL;
4322 	}
4323 
4324 	BN_with_flags(priv_key_const_time, priv_key, BN_FLG_CONSTTIME);
4325 
4326 	if (!BN_mod_exp_mont(pub_key, g, priv_key_const_time, p, ctx, NULL)) {
4327 		BN_free(pub_key);
4328 		php_openssl_store_errors();
4329 		pub_key = NULL;
4330 	}
4331 
4332 	BN_free(priv_key_const_time);
4333 	BN_CTX_free(ctx);
4334 
4335 	return pub_key;
4336 }
4337 /* }}} */
4338 
4339 /* {{{ php_openssl_pkey_init_dh */
php_openssl_pkey_init_dh(DH * dh,zval * data)4340 static zend_bool php_openssl_pkey_init_dh(DH *dh, zval *data)
4341 {
4342 	BIGNUM *p, *q, *g, *priv_key, *pub_key;
4343 
4344 	OPENSSL_PKEY_SET_BN(data, p);
4345 	OPENSSL_PKEY_SET_BN(data, q);
4346 	OPENSSL_PKEY_SET_BN(data, g);
4347 	if (!p || !g || !DH_set0_pqg(dh, p, q, g)) {
4348 		return 0;
4349 	}
4350 
4351 	OPENSSL_PKEY_SET_BN(data, priv_key);
4352 	OPENSSL_PKEY_SET_BN(data, pub_key);
4353 	if (pub_key) {
4354 		return DH_set0_key(dh, pub_key, priv_key);
4355 	}
4356 	if (priv_key) {
4357 		pub_key = php_openssl_dh_pub_from_priv(priv_key, g, p);
4358 		if (pub_key == NULL) {
4359 			return 0;
4360 		}
4361 		return DH_set0_key(dh, pub_key, priv_key);
4362 	}
4363 
4364 	/* generate key */
4365 	PHP_OPENSSL_RAND_ADD_TIME();
4366 	if (!DH_generate_key(dh)) {
4367 		php_openssl_store_errors();
4368 		return 0;
4369 	}
4370 	/* all good */
4371 	return 1;
4372 }
4373 /* }}} */
4374 
4375 /* {{{ proto resource openssl_pkey_new([array configargs])
4376    Generates a new private key */
PHP_FUNCTION(openssl_pkey_new)4377 PHP_FUNCTION(openssl_pkey_new)
4378 {
4379 	struct php_x509_request req;
4380 	zval * args = NULL;
4381 	zval *data;
4382 
4383 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &args) == FAILURE) {
4384 		return;
4385 	}
4386 	RETVAL_FALSE;
4387 
4388 	if (args && Z_TYPE_P(args) == IS_ARRAY) {
4389 		EVP_PKEY *pkey;
4390 
4391 		if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "rsa", sizeof("rsa")-1)) != NULL &&
4392 			Z_TYPE_P(data) == IS_ARRAY) {
4393 			pkey = EVP_PKEY_new();
4394 			if (pkey) {
4395 				RSA *rsa = RSA_new();
4396 				if (rsa) {
4397 					if (php_openssl_pkey_init_and_assign_rsa(pkey, rsa, data)) {
4398 						RETURN_RES(zend_register_resource(pkey, le_key));
4399 					}
4400 					RSA_free(rsa);
4401 				} else {
4402 					php_openssl_store_errors();
4403 				}
4404 				EVP_PKEY_free(pkey);
4405 			} else {
4406 				php_openssl_store_errors();
4407 			}
4408 			RETURN_FALSE;
4409 		} else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "dsa", sizeof("dsa") - 1)) != NULL &&
4410 			Z_TYPE_P(data) == IS_ARRAY) {
4411 			pkey = EVP_PKEY_new();
4412 			if (pkey) {
4413 				DSA *dsa = DSA_new();
4414 				if (dsa) {
4415 					if (php_openssl_pkey_init_dsa(dsa, data)) {
4416 						if (EVP_PKEY_assign_DSA(pkey, dsa)) {
4417 							RETURN_RES(zend_register_resource(pkey, le_key));
4418 						} else {
4419 							php_openssl_store_errors();
4420 						}
4421 					}
4422 					DSA_free(dsa);
4423 				} else {
4424 					php_openssl_store_errors();
4425 				}
4426 				EVP_PKEY_free(pkey);
4427 			} else {
4428 				php_openssl_store_errors();
4429 			}
4430 			RETURN_FALSE;
4431 		} else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "dh", sizeof("dh") - 1)) != NULL &&
4432 			Z_TYPE_P(data) == IS_ARRAY) {
4433 			pkey = EVP_PKEY_new();
4434 			if (pkey) {
4435 				DH *dh = DH_new();
4436 				if (dh) {
4437 					if (php_openssl_pkey_init_dh(dh, data)) {
4438 						if (EVP_PKEY_assign_DH(pkey, dh)) {
4439 							ZVAL_COPY_VALUE(return_value, zend_list_insert(pkey, le_key));
4440 							return;
4441 						} else {
4442 							php_openssl_store_errors();
4443 						}
4444 					}
4445 					DH_free(dh);
4446 				} else {
4447 					php_openssl_store_errors();
4448 				}
4449 				EVP_PKEY_free(pkey);
4450 			} else {
4451 				php_openssl_store_errors();
4452 			}
4453 			RETURN_FALSE;
4454 #ifdef HAVE_EVP_PKEY_EC
4455 		} else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "ec", sizeof("ec") - 1)) != NULL &&
4456 			Z_TYPE_P(data) == IS_ARRAY) {
4457 			EC_KEY *eckey = NULL;
4458 			EC_GROUP *group = NULL;
4459 			EC_POINT *pnt = NULL;
4460 			BIGNUM *d = NULL;
4461 			pkey = EVP_PKEY_new();
4462 			if (pkey) {
4463 				eckey = EC_KEY_new();
4464 				if (eckey) {
4465 					EC_GROUP *group = NULL;
4466 					zval *bn;
4467 					zval *x;
4468 					zval *y;
4469 
4470 					if ((bn = zend_hash_str_find(Z_ARRVAL_P(data), "curve_name", sizeof("curve_name") - 1)) != NULL &&
4471 							Z_TYPE_P(bn) == IS_STRING) {
4472 						int nid = OBJ_sn2nid(Z_STRVAL_P(bn));
4473 						if (nid != NID_undef) {
4474 							group = EC_GROUP_new_by_curve_name(nid);
4475 							if (!group) {
4476 								php_openssl_store_errors();
4477 								goto clean_exit;
4478 							}
4479 							EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
4480 							EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);
4481 							if (!EC_KEY_set_group(eckey, group)) {
4482 								php_openssl_store_errors();
4483 								goto clean_exit;
4484 							}
4485 						}
4486 					}
4487 
4488 					if (group == NULL) {
4489 						php_error_docref(NULL, E_WARNING, "Unknown curve_name");
4490 						goto clean_exit;
4491 					}
4492 
4493 					// The public key 'pnt' can be calculated from 'd' or is defined by 'x' and 'y'
4494 					if ((bn = zend_hash_str_find(Z_ARRVAL_P(data), "d", sizeof("d") - 1)) != NULL &&
4495 							Z_TYPE_P(bn) == IS_STRING) {
4496 						d = BN_bin2bn((unsigned char*) Z_STRVAL_P(bn), Z_STRLEN_P(bn), NULL);
4497 						if (!EC_KEY_set_private_key(eckey, d)) {
4498 							php_openssl_store_errors();
4499 							goto clean_exit;
4500 						}
4501 						// Calculate the public key by multiplying the Point Q with the public key
4502 						// P = d * Q
4503 						pnt = EC_POINT_new(group);
4504 						if (!pnt || !EC_POINT_mul(group, pnt, d, NULL, NULL, NULL)) {
4505 							php_openssl_store_errors();
4506 							goto clean_exit;
4507 						}
4508 
4509 						BN_free(d);
4510 					} else if ((x = zend_hash_str_find(Z_ARRVAL_P(data), "x", sizeof("x") - 1)) != NULL &&
4511 							Z_TYPE_P(x) == IS_STRING &&
4512 							(y = zend_hash_str_find(Z_ARRVAL_P(data), "y", sizeof("y") - 1)) != NULL &&
4513 							Z_TYPE_P(y) == IS_STRING) {
4514 						pnt = EC_POINT_new(group);
4515 						if (pnt == NULL) {
4516 							php_openssl_store_errors();
4517 							goto clean_exit;
4518 						}
4519 						if (!EC_POINT_set_affine_coordinates_GFp(
4520 								group, pnt, BN_bin2bn((unsigned char*) Z_STRVAL_P(x), Z_STRLEN_P(x), NULL),
4521 								BN_bin2bn((unsigned char*) Z_STRVAL_P(y), Z_STRLEN_P(y), NULL), NULL)) {
4522 							php_openssl_store_errors();
4523 							goto clean_exit;
4524 						}
4525 					}
4526 
4527 					if (pnt != NULL) {
4528 						if (!EC_KEY_set_public_key(eckey, pnt)) {
4529 							php_openssl_store_errors();
4530 							goto clean_exit;
4531 						}
4532 						EC_POINT_free(pnt);
4533 						pnt = NULL;
4534 					}
4535 
4536 					if (!EC_KEY_check_key(eckey)) {
4537 						PHP_OPENSSL_RAND_ADD_TIME();
4538 						EC_KEY_generate_key(eckey);
4539 						php_openssl_store_errors();
4540 					}
4541 					if (EC_KEY_check_key(eckey) && EVP_PKEY_assign_EC_KEY(pkey, eckey)) {
4542 						EC_GROUP_free(group);
4543 						RETURN_RES(zend_register_resource(pkey, le_key));
4544 					} else {
4545 						php_openssl_store_errors();
4546 					}
4547 				} else {
4548 					php_openssl_store_errors();
4549 				}
4550 			} else {
4551 				php_openssl_store_errors();
4552 			}
4553 clean_exit:
4554 			if (d != NULL) {
4555 				BN_free(d);
4556 			}
4557 			if (pnt != NULL) {
4558 				EC_POINT_free(pnt);
4559 			}
4560 			if (group != NULL) {
4561 				EC_GROUP_free(group);
4562 			}
4563 			if (eckey != NULL) {
4564 				EC_KEY_free(eckey);
4565 			}
4566 			if (pkey != NULL) {
4567 				EVP_PKEY_free(pkey);
4568 			}
4569 			RETURN_FALSE;
4570 #endif
4571 		}
4572 	}
4573 
4574 	PHP_SSL_REQ_INIT(&req);
4575 
4576 	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
4577 		if (php_openssl_generate_private_key(&req)) {
4578 			/* pass back a key resource */
4579 			RETVAL_RES(zend_register_resource(req.priv_key, le_key));
4580 			/* make sure the cleanup code doesn't zap it! */
4581 			req.priv_key = NULL;
4582 		}
4583 	}
4584 	PHP_SSL_REQ_DISPOSE(&req);
4585 }
4586 /* }}} */
4587 
4588 /* {{{ proto bool openssl_pkey_export_to_file(mixed key, string outfilename [, string passphrase, array config_args)
4589    Gets an exportable representation of a key into a file */
PHP_FUNCTION(openssl_pkey_export_to_file)4590 PHP_FUNCTION(openssl_pkey_export_to_file)
4591 {
4592 	struct php_x509_request req;
4593 	zval * zpkey, * args = NULL;
4594 	char * passphrase = NULL;
4595 	size_t passphrase_len = 0;
4596 	char * filename = NULL;
4597 	size_t filename_len = 0;
4598 	zend_resource *key_resource = NULL;
4599 	int pem_write = 0;
4600 	EVP_PKEY * key;
4601 	BIO * bio_out = NULL;
4602 	const EVP_CIPHER * cipher;
4603 
4604 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zp|s!a!", &zpkey, &filename, &filename_len, &passphrase, &passphrase_len, &args) == FAILURE) {
4605 		return;
4606 	}
4607 	RETVAL_FALSE;
4608 
4609 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(passphrase_len, passphrase);
4610 	key = php_openssl_evp_from_zval(zpkey, 0, passphrase, passphrase_len, 0, &key_resource);
4611 
4612 	if (key == NULL) {
4613 		php_error_docref(NULL, E_WARNING, "cannot get key from parameter 1");
4614 		RETURN_FALSE;
4615 	}
4616 
4617 	if (php_openssl_open_base_dir_chk(filename)) {
4618 		RETURN_FALSE;
4619 	}
4620 
4621 	PHP_SSL_REQ_INIT(&req);
4622 
4623 	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
4624 		bio_out = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
4625 		if (bio_out == NULL) {
4626 			php_openssl_store_errors();
4627 			goto clean_exit;
4628 		}
4629 
4630 		if (passphrase && req.priv_key_encrypt) {
4631 			if (req.priv_key_encrypt_cipher) {
4632 				cipher = req.priv_key_encrypt_cipher;
4633 			} else {
4634 				cipher = (EVP_CIPHER *) EVP_des_ede3_cbc();
4635 			}
4636 		} else {
4637 			cipher = NULL;
4638 		}
4639 
4640 		switch (EVP_PKEY_base_id(key)) {
4641 #ifdef HAVE_EVP_PKEY_EC
4642 			case EVP_PKEY_EC:
4643 				pem_write = PEM_write_bio_ECPrivateKey(
4644 						bio_out, EVP_PKEY_get0_EC_KEY(key), cipher,
4645 						(unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
4646 				break;
4647 #endif
4648 			default:
4649 				pem_write = PEM_write_bio_PrivateKey(
4650 						bio_out, key, cipher,
4651 						(unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
4652 				break;
4653 		}
4654 
4655 		if (pem_write) {
4656 			/* Success!
4657 			 * If returning the output as a string, do so now */
4658 			RETVAL_TRUE;
4659 		} else {
4660 			php_openssl_store_errors();
4661 		}
4662 	}
4663 
4664 clean_exit:
4665 	PHP_SSL_REQ_DISPOSE(&req);
4666 
4667 	if (key_resource == NULL && key) {
4668 		EVP_PKEY_free(key);
4669 	}
4670 	if (bio_out) {
4671 		BIO_free(bio_out);
4672 	}
4673 }
4674 /* }}} */
4675 
4676 /* {{{ proto bool openssl_pkey_export(mixed key, &mixed out [, string passphrase [, array config_args]])
4677    Gets an exportable representation of a key into a string or file */
PHP_FUNCTION(openssl_pkey_export)4678 PHP_FUNCTION(openssl_pkey_export)
4679 {
4680 	struct php_x509_request req;
4681 	zval * zpkey, * args = NULL, *out;
4682 	char * passphrase = NULL; size_t passphrase_len = 0;
4683 	int pem_write = 0;
4684 	zend_resource *key_resource = NULL;
4685 	EVP_PKEY * key;
4686 	BIO * bio_out = NULL;
4687 	const EVP_CIPHER * cipher;
4688 
4689 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|s!a!", &zpkey, &out, &passphrase, &passphrase_len, &args) == FAILURE) {
4690 		return;
4691 	}
4692 	RETVAL_FALSE;
4693 
4694 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(passphrase_len, passphrase);
4695 	key = php_openssl_evp_from_zval(zpkey, 0, passphrase, passphrase_len, 0, &key_resource);
4696 
4697 	if (key == NULL) {
4698 		php_error_docref(NULL, E_WARNING, "cannot get key from parameter 1");
4699 		RETURN_FALSE;
4700 	}
4701 
4702 	PHP_SSL_REQ_INIT(&req);
4703 
4704 	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
4705 		bio_out = BIO_new(BIO_s_mem());
4706 
4707 		if (passphrase && req.priv_key_encrypt) {
4708 			if (req.priv_key_encrypt_cipher) {
4709 				cipher = req.priv_key_encrypt_cipher;
4710 			} else {
4711 				cipher = (EVP_CIPHER *) EVP_des_ede3_cbc();
4712 			}
4713 		} else {
4714 			cipher = NULL;
4715 		}
4716 
4717 		switch (EVP_PKEY_base_id(key)) {
4718 #ifdef HAVE_EVP_PKEY_EC
4719 			case EVP_PKEY_EC:
4720 				pem_write = PEM_write_bio_ECPrivateKey(
4721 						bio_out, EVP_PKEY_get0_EC_KEY(key), cipher,
4722 						(unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
4723 				break;
4724 #endif
4725 			default:
4726 				pem_write = PEM_write_bio_PrivateKey(
4727 						bio_out, key, cipher,
4728 						(unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
4729 				break;
4730 		}
4731 
4732 		if (pem_write) {
4733 			/* Success!
4734 			 * If returning the output as a string, do so now */
4735 
4736 			char * bio_mem_ptr;
4737 			long bio_mem_len;
4738 			RETVAL_TRUE;
4739 
4740 			bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
4741 			ZEND_TRY_ASSIGN_REF_STRINGL(out, bio_mem_ptr, bio_mem_len);
4742 		} else {
4743 			php_openssl_store_errors();
4744 		}
4745 	}
4746 	PHP_SSL_REQ_DISPOSE(&req);
4747 
4748 	if (key_resource == NULL && key) {
4749 		EVP_PKEY_free(key);
4750 	}
4751 	if (bio_out) {
4752 		BIO_free(bio_out);
4753 	}
4754 }
4755 /* }}} */
4756 
4757 /* {{{ proto int openssl_pkey_get_public(mixed cert)
4758    Gets public key from X.509 certificate */
PHP_FUNCTION(openssl_pkey_get_public)4759 PHP_FUNCTION(openssl_pkey_get_public)
4760 {
4761 	zval *cert;
4762 	EVP_PKEY *pkey;
4763 	zend_resource *res;
4764 
4765 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &cert) == FAILURE) {
4766 		return;
4767 	}
4768 	pkey = php_openssl_evp_from_zval(cert, 1, NULL, 0, 1, &res);
4769 	if (pkey == NULL) {
4770 		RETURN_FALSE;
4771 	}
4772 	ZVAL_RES(return_value, res);
4773 }
4774 /* }}} */
4775 
4776 /* {{{ proto void openssl_pkey_free(int key)
4777    Frees a key */
PHP_FUNCTION(openssl_pkey_free)4778 PHP_FUNCTION(openssl_pkey_free)
4779 {
4780 	zval *key;
4781 	EVP_PKEY *pkey;
4782 
4783 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &key) == FAILURE) {
4784 		return;
4785 	}
4786 	if ((pkey = (EVP_PKEY *)zend_fetch_resource(Z_RES_P(key), "OpenSSL key", le_key)) == NULL) {
4787 		RETURN_FALSE;
4788 	}
4789 	zend_list_close(Z_RES_P(key));
4790 }
4791 /* }}} */
4792 
4793 /* {{{ proto int openssl_pkey_get_private(string key [, string passphrase])
4794    Gets private keys */
PHP_FUNCTION(openssl_pkey_get_private)4795 PHP_FUNCTION(openssl_pkey_get_private)
4796 {
4797 	zval *cert;
4798 	EVP_PKEY *pkey;
4799 	char * passphrase = "";
4800 	size_t passphrase_len = sizeof("")-1;
4801 	zend_resource *res;
4802 
4803 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s", &cert, &passphrase, &passphrase_len) == FAILURE) {
4804 		return;
4805 	}
4806 
4807 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(passphrase_len, passphrase);
4808 	pkey = php_openssl_evp_from_zval(cert, 0, passphrase, passphrase_len, 1, &res);
4809 
4810 	if (pkey == NULL) {
4811 		RETURN_FALSE;
4812 	}
4813 	ZVAL_RES(return_value, res);
4814 }
4815 
4816 /* }}} */
4817 
4818 /* {{{ proto resource openssl_pkey_get_details(resource key)
4819 	returns an array with the key details (bits, pkey, type)*/
PHP_FUNCTION(openssl_pkey_get_details)4820 PHP_FUNCTION(openssl_pkey_get_details)
4821 {
4822 	zval *key;
4823 	EVP_PKEY *pkey;
4824 	BIO *out;
4825 	unsigned int pbio_len;
4826 	char *pbio;
4827 	zend_long ktype;
4828 
4829 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &key) == FAILURE) {
4830 		return;
4831 	}
4832 	if ((pkey = (EVP_PKEY *)zend_fetch_resource(Z_RES_P(key), "OpenSSL key", le_key)) == NULL) {
4833 		RETURN_FALSE;
4834 	}
4835 	out = BIO_new(BIO_s_mem());
4836 	if (!PEM_write_bio_PUBKEY(out, pkey)) {
4837 		BIO_free(out);
4838 		php_openssl_store_errors();
4839 		RETURN_FALSE;
4840 	}
4841 	pbio_len = BIO_get_mem_data(out, &pbio);
4842 
4843 	array_init(return_value);
4844 	add_assoc_long(return_value, "bits", EVP_PKEY_bits(pkey));
4845 	add_assoc_stringl(return_value, "key", pbio, pbio_len);
4846 	/*TODO: Use the real values once the openssl constants are used
4847 	 * See the enum at the top of this file
4848 	 */
4849 	switch (EVP_PKEY_base_id(pkey)) {
4850 		case EVP_PKEY_RSA:
4851 		case EVP_PKEY_RSA2:
4852 			{
4853 				RSA *rsa = EVP_PKEY_get0_RSA(pkey);
4854 				ktype = OPENSSL_KEYTYPE_RSA;
4855 
4856 				if (rsa != NULL) {
4857 					zval z_rsa;
4858 					const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
4859 
4860 					RSA_get0_key(rsa, &n, &e, &d);
4861 					RSA_get0_factors(rsa, &p, &q);
4862 					RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
4863 
4864 					array_init(&z_rsa);
4865 					OPENSSL_PKEY_GET_BN(z_rsa, n);
4866 					OPENSSL_PKEY_GET_BN(z_rsa, e);
4867 					OPENSSL_PKEY_GET_BN(z_rsa, d);
4868 					OPENSSL_PKEY_GET_BN(z_rsa, p);
4869 					OPENSSL_PKEY_GET_BN(z_rsa, q);
4870 					OPENSSL_PKEY_GET_BN(z_rsa, dmp1);
4871 					OPENSSL_PKEY_GET_BN(z_rsa, dmq1);
4872 					OPENSSL_PKEY_GET_BN(z_rsa, iqmp);
4873 					add_assoc_zval(return_value, "rsa", &z_rsa);
4874 				}
4875 			}
4876 			break;
4877 		case EVP_PKEY_DSA:
4878 		case EVP_PKEY_DSA2:
4879 		case EVP_PKEY_DSA3:
4880 		case EVP_PKEY_DSA4:
4881 			{
4882 				DSA *dsa = EVP_PKEY_get0_DSA(pkey);
4883 				ktype = OPENSSL_KEYTYPE_DSA;
4884 
4885 				if (dsa != NULL) {
4886 					zval z_dsa;
4887 					const BIGNUM *p, *q, *g, *priv_key, *pub_key;
4888 
4889 					DSA_get0_pqg(dsa, &p, &q, &g);
4890 					DSA_get0_key(dsa, &pub_key, &priv_key);
4891 
4892 					array_init(&z_dsa);
4893 					OPENSSL_PKEY_GET_BN(z_dsa, p);
4894 					OPENSSL_PKEY_GET_BN(z_dsa, q);
4895 					OPENSSL_PKEY_GET_BN(z_dsa, g);
4896 					OPENSSL_PKEY_GET_BN(z_dsa, priv_key);
4897 					OPENSSL_PKEY_GET_BN(z_dsa, pub_key);
4898 					add_assoc_zval(return_value, "dsa", &z_dsa);
4899 				}
4900 			}
4901 			break;
4902 		case EVP_PKEY_DH:
4903 			{
4904 				DH *dh = EVP_PKEY_get0_DH(pkey);
4905 				ktype = OPENSSL_KEYTYPE_DH;
4906 
4907 				if (dh != NULL) {
4908 					zval z_dh;
4909 					const BIGNUM *p, *q, *g, *priv_key, *pub_key;
4910 
4911 					DH_get0_pqg(dh, &p, &q, &g);
4912 					DH_get0_key(dh, &pub_key, &priv_key);
4913 
4914 					array_init(&z_dh);
4915 					OPENSSL_PKEY_GET_BN(z_dh, p);
4916 					OPENSSL_PKEY_GET_BN(z_dh, g);
4917 					OPENSSL_PKEY_GET_BN(z_dh, priv_key);
4918 					OPENSSL_PKEY_GET_BN(z_dh, pub_key);
4919 					add_assoc_zval(return_value, "dh", &z_dh);
4920 				}
4921 			}
4922 			break;
4923 #ifdef HAVE_EVP_PKEY_EC
4924 		case EVP_PKEY_EC:
4925 			ktype = OPENSSL_KEYTYPE_EC;
4926 			if (EVP_PKEY_get0_EC_KEY(pkey) != NULL) {
4927 				zval ec;
4928 				const EC_GROUP *ec_group;
4929 				const EC_POINT *pub;
4930 				int nid;
4931 				char *crv_sn;
4932 				ASN1_OBJECT *obj;
4933 				// openssl recommends a buffer length of 80
4934 				char oir_buf[80];
4935 				const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
4936 				BIGNUM *x = BN_new();
4937 				BIGNUM *y = BN_new();
4938 				const BIGNUM *d;
4939 
4940 				ec_group = EC_KEY_get0_group(ec_key);
4941 
4942 				// Curve nid (numerical identifier) used for ASN1 mapping
4943 				nid = EC_GROUP_get_curve_name(ec_group);
4944 				if (nid == NID_undef) {
4945 					break;
4946 				}
4947 				array_init(&ec);
4948 
4949 				// Short object name
4950 				crv_sn = (char*) OBJ_nid2sn(nid);
4951 				if (crv_sn != NULL) {
4952 					add_assoc_string(&ec, "curve_name", crv_sn);
4953 				}
4954 
4955 				obj = OBJ_nid2obj(nid);
4956 				if (obj != NULL) {
4957 					int oir_len = OBJ_obj2txt(oir_buf, sizeof(oir_buf), obj, 1);
4958 					add_assoc_stringl(&ec, "curve_oid", (char*) oir_buf, oir_len);
4959 					ASN1_OBJECT_free(obj);
4960 				}
4961 
4962 				pub = EC_KEY_get0_public_key(ec_key);
4963 
4964 				if (EC_POINT_get_affine_coordinates_GFp(ec_group, pub, x, y, NULL)) {
4965 					OPENSSL_GET_BN(ec, x, x);
4966 					OPENSSL_GET_BN(ec, y, y);
4967 				} else {
4968 					php_openssl_store_errors();
4969 				}
4970 
4971 				if ((d = EC_KEY_get0_private_key(EVP_PKEY_get0_EC_KEY(pkey))) != NULL) {
4972 					OPENSSL_GET_BN(ec, d, d);
4973 				}
4974 
4975 				add_assoc_zval(return_value, "ec", &ec);
4976 
4977 				BN_free(x);
4978 				BN_free(y);
4979 			}
4980 			break;
4981 #endif
4982 		default:
4983 			ktype = -1;
4984 			break;
4985 	}
4986 	add_assoc_long(return_value, "type", ktype);
4987 
4988 	BIO_free(out);
4989 }
4990 /* }}} */
4991 
4992 /* {{{ proto string openssl_dh_compute_key(string pub_key, resource dh_key)
4993    Computes shared secret for public value of remote DH key and local DH key */
PHP_FUNCTION(openssl_dh_compute_key)4994 PHP_FUNCTION(openssl_dh_compute_key)
4995 {
4996 	zval *key;
4997 	char *pub_str;
4998 	size_t pub_len;
4999 	DH *dh;
5000 	EVP_PKEY *pkey;
5001 	BIGNUM *pub;
5002 	zend_string *data;
5003 	int len;
5004 
5005 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sr", &pub_str, &pub_len, &key) == FAILURE) {
5006 		return;
5007 	}
5008 	if ((pkey = (EVP_PKEY *)zend_fetch_resource(Z_RES_P(key), "OpenSSL key", le_key)) == NULL) {
5009 		RETURN_FALSE;
5010 	}
5011 	if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) {
5012 		RETURN_FALSE;
5013 	}
5014 	dh = EVP_PKEY_get0_DH(pkey);
5015 	if (dh == NULL) {
5016 		RETURN_FALSE;
5017 	}
5018 
5019 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(pub_len, pub_key);
5020 	pub = BN_bin2bn((unsigned char*)pub_str, (int)pub_len, NULL);
5021 
5022 	data = zend_string_alloc(DH_size(dh), 0);
5023 	len = DH_compute_key((unsigned char*)ZSTR_VAL(data), pub, dh);
5024 
5025 	if (len >= 0) {
5026 		ZSTR_LEN(data) = len;
5027 		ZSTR_VAL(data)[len] = 0;
5028 		RETVAL_NEW_STR(data);
5029 	} else {
5030 		php_openssl_store_errors();
5031 		zend_string_release_ex(data, 0);
5032 		RETVAL_FALSE;
5033 	}
5034 
5035 	BN_free(pub);
5036 }
5037 /* }}} */
5038 
5039 /* {{{ proto string openssl_pkey_derive(peer_pub_key, priv_key, int keylen=NULL)
5040    Computes shared secret for public value of remote and local DH or ECDH key */
PHP_FUNCTION(openssl_pkey_derive)5041 PHP_FUNCTION(openssl_pkey_derive)
5042 {
5043 	zval *priv_key;
5044 	zval *peer_pub_key;
5045 	EVP_PKEY *pkey;
5046 	EVP_PKEY *peer_key;
5047 	size_t key_size;
5048 	zend_long key_len = 0;
5049 	zend_string *result;
5050 
5051 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &peer_pub_key, &priv_key, &key_len) == FAILURE) {
5052 		RETURN_FALSE;
5053 	}
5054 	if (key_len < 0) {
5055 		php_error_docref(NULL, E_WARNING, "keylen < 0, assuming NULL");
5056 	}
5057 	key_size = key_len;
5058 	if ((pkey = php_openssl_evp_from_zval(priv_key, 0, "", 0, 0, NULL)) == NULL
5059 		|| (peer_key = php_openssl_evp_from_zval(peer_pub_key, 1, NULL, 0, 0, NULL)) == NULL) {
5060 		RETURN_FALSE;
5061 	}
5062 	EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
5063 	if (!ctx) {
5064 		RETURN_FALSE;
5065 	}
5066 	if (EVP_PKEY_derive_init(ctx) > 0
5067 		&& EVP_PKEY_derive_set_peer(ctx, peer_key) > 0
5068 		&& (key_size > 0 || EVP_PKEY_derive(ctx, NULL, &key_size) > 0)
5069 		&& (result = zend_string_alloc(key_size, 0)) != NULL) {
5070 		if (EVP_PKEY_derive(ctx, (unsigned char*)ZSTR_VAL(result), &key_size) > 0) {
5071 			ZSTR_LEN(result) = key_size;
5072 			ZSTR_VAL(result)[key_size] = 0;
5073 			RETVAL_NEW_STR(result);
5074 		} else {
5075 			php_openssl_store_errors();
5076 			zend_string_release_ex(result, 0);
5077 			RETVAL_FALSE;
5078 		}
5079 	} else {
5080 		RETVAL_FALSE;
5081 	}
5082 	EVP_PKEY_CTX_free(ctx);
5083 }
5084 /* }}} */
5085 
5086 
5087 /* {{{ proto string openssl_pbkdf2(string password, string salt, int key_length, int iterations [, string digest_method = "sha1"])
5088    Generates a PKCS5 v2 PBKDF2 string, defaults to sha1 */
PHP_FUNCTION(openssl_pbkdf2)5089 PHP_FUNCTION(openssl_pbkdf2)
5090 {
5091 	zend_long key_length = 0, iterations = 0;
5092 	char *password;
5093 	size_t password_len;
5094 	char *salt;
5095 	size_t salt_len;
5096 	char *method;
5097 	size_t method_len = 0;
5098 	zend_string *out_buffer;
5099 
5100 	const EVP_MD *digest;
5101 
5102 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssll|s",
5103 				&password, &password_len,
5104 				&salt, &salt_len,
5105 				&key_length, &iterations,
5106 				&method, &method_len) == FAILURE) {
5107 		return;
5108 	}
5109 
5110 	if (key_length <= 0) {
5111 		RETURN_FALSE;
5112 	}
5113 
5114 	if (method_len) {
5115 		digest = EVP_get_digestbyname(method);
5116 	} else {
5117 		digest = EVP_sha1();
5118 	}
5119 
5120 	if (!digest) {
5121 		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm");
5122 		RETURN_FALSE;
5123 	}
5124 
5125 	PHP_OPENSSL_CHECK_LONG_TO_INT(key_length, key);
5126 	PHP_OPENSSL_CHECK_LONG_TO_INT(iterations, iterations);
5127 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password);
5128 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(salt_len, salt);
5129 
5130 	out_buffer = zend_string_alloc(key_length, 0);
5131 
5132 	if (PKCS5_PBKDF2_HMAC(password, (int)password_len, (unsigned char *)salt, (int)salt_len, (int)iterations, digest, (int)key_length, (unsigned char*)ZSTR_VAL(out_buffer)) == 1) {
5133 		ZSTR_VAL(out_buffer)[key_length] = 0;
5134 		RETURN_NEW_STR(out_buffer);
5135 	} else {
5136 		php_openssl_store_errors();
5137 		zend_string_release_ex(out_buffer, 0);
5138 		RETURN_FALSE;
5139 	}
5140 }
5141 /* }}} */
5142 
5143 /* {{{ PKCS7 S/MIME functions */
5144 
5145 /* {{{ proto bool openssl_pkcs7_verify(string filename, int flags [, string signerscerts [, array cainfo [, string extracerts [, string content [, string pk7]]]]])
5146    Verifys that the data block is intact, the signer is who they say they are, and returns the CERTs of the signers */
PHP_FUNCTION(openssl_pkcs7_verify)5147 PHP_FUNCTION(openssl_pkcs7_verify)
5148 {
5149 	X509_STORE * store = NULL;
5150 	zval * cainfo = NULL;
5151 	STACK_OF(X509) *signers= NULL;
5152 	STACK_OF(X509) *others = NULL;
5153 	PKCS7 * p7 = NULL;
5154 	BIO * in = NULL, * datain = NULL, * dataout = NULL, * p7bout  = NULL;
5155 	zend_long flags = 0;
5156 	char * filename;
5157 	size_t filename_len;
5158 	char * extracerts = NULL;
5159 	size_t extracerts_len = 0;
5160 	char * signersfilename = NULL;
5161 	size_t signersfilename_len = 0;
5162 	char * datafilename = NULL;
5163 	size_t datafilename_len = 0;
5164 	char * p7bfilename = NULL;
5165 	size_t p7bfilename_len = 0;
5166 
5167 	RETVAL_LONG(-1);
5168 
5169 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "pl|pappp", &filename, &filename_len,
5170 				&flags, &signersfilename, &signersfilename_len, &cainfo,
5171 				&extracerts, &extracerts_len, &datafilename, &datafilename_len, &p7bfilename, &p7bfilename_len) == FAILURE) {
5172 		return;
5173 	}
5174 
5175 	if (extracerts) {
5176 		others = php_openssl_load_all_certs_from_file(extracerts);
5177 		if (others == NULL) {
5178 			goto clean_exit;
5179 		}
5180 	}
5181 
5182 	flags = flags & ~PKCS7_DETACHED;
5183 
5184 	store = php_openssl_setup_verify(cainfo);
5185 
5186 	if (!store) {
5187 		goto clean_exit;
5188 	}
5189 	if (php_openssl_open_base_dir_chk(filename)) {
5190 		goto clean_exit;
5191 	}
5192 
5193 	in = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_R(flags));
5194 	if (in == NULL) {
5195 		php_openssl_store_errors();
5196 		goto clean_exit;
5197 	}
5198 	p7 = SMIME_read_PKCS7(in, &datain);
5199 	if (p7 == NULL) {
5200 #if DEBUG_SMIME
5201 		zend_printf("SMIME_read_PKCS7 failed\n");
5202 #endif
5203 		php_openssl_store_errors();
5204 		goto clean_exit;
5205 	}
5206 
5207 	if (datafilename) {
5208 
5209 		if (php_openssl_open_base_dir_chk(datafilename)) {
5210 			goto clean_exit;
5211 		}
5212 
5213 		dataout = BIO_new_file(datafilename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5214 		if (dataout == NULL) {
5215 			php_openssl_store_errors();
5216 			goto clean_exit;
5217 		}
5218 	}
5219 
5220 	if (p7bfilename) {
5221 
5222 		if (php_openssl_open_base_dir_chk(p7bfilename)) {
5223 			goto clean_exit;
5224 		}
5225 
5226 		p7bout = BIO_new_file(p7bfilename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5227 		if (p7bout == NULL) {
5228 			php_openssl_store_errors();
5229 			goto clean_exit;
5230 		}
5231 	}
5232 #if DEBUG_SMIME
5233 	zend_printf("Calling PKCS7 verify\n");
5234 #endif
5235 
5236 	if (PKCS7_verify(p7, others, store, datain, dataout, (int)flags)) {
5237 
5238 		RETVAL_TRUE;
5239 
5240 		if (signersfilename) {
5241 			BIO *certout;
5242 
5243 			if (php_openssl_open_base_dir_chk(signersfilename)) {
5244 				goto clean_exit;
5245 			}
5246 
5247 			certout = BIO_new_file(signersfilename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5248 			if (certout) {
5249 				int i;
5250 				signers = PKCS7_get0_signers(p7, NULL, (int)flags);
5251 				if (signers != NULL) {
5252 
5253 					for (i = 0; i < sk_X509_num(signers); i++) {
5254 						if (!PEM_write_bio_X509(certout, sk_X509_value(signers, i))) {
5255 							php_openssl_store_errors();
5256 							RETVAL_LONG(-1);
5257 							php_error_docref(NULL, E_WARNING, "failed to write signer %d", i);
5258 						}
5259 					}
5260 
5261 					sk_X509_free(signers);
5262 				} else {
5263 					RETVAL_LONG(-1);
5264 					php_openssl_store_errors();
5265 				}
5266 
5267 				BIO_free(certout);
5268 			} else {
5269 				php_openssl_store_errors();
5270 				php_error_docref(NULL, E_WARNING, "signature OK, but cannot open %s for writing", signersfilename);
5271 				RETVAL_LONG(-1);
5272 			}
5273 
5274 			if (p7bout) {
5275 				PEM_write_bio_PKCS7(p7bout, p7);
5276 			}
5277 		}
5278 	} else {
5279 		php_openssl_store_errors();
5280 		RETVAL_FALSE;
5281 	}
5282 clean_exit:
5283 	if (p7bout) {
5284 		BIO_free(p7bout);
5285 	}
5286 	X509_STORE_free(store);
5287 	BIO_free(datain);
5288 	BIO_free(in);
5289 	BIO_free(dataout);
5290 	PKCS7_free(p7);
5291 	sk_X509_pop_free(others, X509_free);
5292 }
5293 /* }}} */
5294 
5295 /* {{{ proto bool openssl_pkcs7_encrypt(string infile, string outfile, mixed recipcerts, array headers [, int flags [, int cipher]])
5296    Encrypts the message in the file named infile with the certificates in recipcerts and output the result to the file named outfile */
PHP_FUNCTION(openssl_pkcs7_encrypt)5297 PHP_FUNCTION(openssl_pkcs7_encrypt)
5298 {
5299 	zval * zrecipcerts, * zheaders = NULL;
5300 	STACK_OF(X509) * recipcerts = NULL;
5301 	BIO * infile = NULL, * outfile = NULL;
5302 	zend_long flags = 0;
5303 	PKCS7 * p7 = NULL;
5304 	zval * zcertval;
5305 	X509 * cert;
5306 	const EVP_CIPHER *cipher = NULL;
5307 	zend_long cipherid = PHP_OPENSSL_CIPHER_DEFAULT;
5308 	zend_string * strindex;
5309 	char * infilename = NULL;
5310 	size_t infilename_len;
5311 	char * outfilename = NULL;
5312 	size_t outfilename_len;
5313 
5314 	RETVAL_FALSE;
5315 
5316 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppza!|ll", &infilename, &infilename_len,
5317 				&outfilename, &outfilename_len, &zrecipcerts, &zheaders, &flags, &cipherid) == FAILURE)
5318 		return;
5319 
5320 
5321 	if (php_openssl_open_base_dir_chk(infilename) || php_openssl_open_base_dir_chk(outfilename)) {
5322 		return;
5323 	}
5324 
5325 	infile = BIO_new_file(infilename, PHP_OPENSSL_BIO_MODE_R(flags));
5326 	if (infile == NULL) {
5327 		php_openssl_store_errors();
5328 		goto clean_exit;
5329 	}
5330 
5331 	outfile = BIO_new_file(outfilename, PHP_OPENSSL_BIO_MODE_W(flags));
5332 	if (outfile == NULL) {
5333 		php_openssl_store_errors();
5334 		goto clean_exit;
5335 	}
5336 
5337 	recipcerts = sk_X509_new_null();
5338 
5339 	/* get certs */
5340 	if (Z_TYPE_P(zrecipcerts) == IS_ARRAY) {
5341 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zrecipcerts), zcertval) {
5342 			zend_resource *certresource;
5343 
5344 			cert = php_openssl_x509_from_zval(zcertval, 0, &certresource);
5345 			if (cert == NULL) {
5346 				goto clean_exit;
5347 			}
5348 
5349 			if (certresource != NULL) {
5350 				/* we shouldn't free this particular cert, as it is a resource.
5351 					make a copy and push that on the stack instead */
5352 				cert = X509_dup(cert);
5353 				if (cert == NULL) {
5354 					php_openssl_store_errors();
5355 					goto clean_exit;
5356 				}
5357 			}
5358 			sk_X509_push(recipcerts, cert);
5359 		} ZEND_HASH_FOREACH_END();
5360 	} else {
5361 		/* a single certificate */
5362 		zend_resource *certresource;
5363 
5364 		cert = php_openssl_x509_from_zval(zrecipcerts, 0, &certresource);
5365 		if (cert == NULL) {
5366 			goto clean_exit;
5367 		}
5368 
5369 		if (certresource != NULL) {
5370 			/* we shouldn't free this particular cert, as it is a resource.
5371 				make a copy and push that on the stack instead */
5372 			cert = X509_dup(cert);
5373 			if (cert == NULL) {
5374 				php_openssl_store_errors();
5375 				goto clean_exit;
5376 			}
5377 		}
5378 		sk_X509_push(recipcerts, cert);
5379 	}
5380 
5381 	/* sanity check the cipher */
5382 	cipher = php_openssl_get_evp_cipher_from_algo(cipherid);
5383 	if (cipher == NULL) {
5384 		/* shouldn't happen */
5385 		php_error_docref(NULL, E_WARNING, "Failed to get cipher");
5386 		goto clean_exit;
5387 	}
5388 
5389 	p7 = PKCS7_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, (int)flags);
5390 
5391 	if (p7 == NULL) {
5392 		php_openssl_store_errors();
5393 		goto clean_exit;
5394 	}
5395 
5396 	/* tack on extra headers */
5397 	if (zheaders) {
5398 		ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, zcertval) {
5399 			zend_string *str = zval_try_get_string(zcertval);
5400 			if (UNEXPECTED(!str)) {
5401 				goto clean_exit;
5402 			}
5403 			if (strindex) {
5404 				BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), ZSTR_VAL(str));
5405 			} else {
5406 				BIO_printf(outfile, "%s\n", ZSTR_VAL(str));
5407 			}
5408 			zend_string_release(str);
5409 		} ZEND_HASH_FOREACH_END();
5410 	}
5411 
5412 	(void)BIO_reset(infile);
5413 
5414 	/* write the encrypted data */
5415 	if (!SMIME_write_PKCS7(outfile, p7, infile, (int)flags)) {
5416 		php_openssl_store_errors();
5417 		goto clean_exit;
5418 	}
5419 
5420 	RETVAL_TRUE;
5421 
5422 clean_exit:
5423 	PKCS7_free(p7);
5424 	BIO_free(infile);
5425 	BIO_free(outfile);
5426 	if (recipcerts) {
5427 		sk_X509_pop_free(recipcerts, X509_free);
5428 	}
5429 }
5430 /* }}} */
5431 
5432 /* {{{ proto bool openssl_pkcs7_read(string P7B, array &certs)
5433    Exports the PKCS7 file to an array of PEM certificates */
PHP_FUNCTION(openssl_pkcs7_read)5434 PHP_FUNCTION(openssl_pkcs7_read)
5435 {
5436 	zval * zout = NULL, zcert;
5437 	char *p7b;
5438 	size_t p7b_len;
5439 	STACK_OF(X509) *certs = NULL;
5440 	STACK_OF(X509_CRL) *crls = NULL;
5441 	BIO * bio_in = NULL, * bio_out = NULL;
5442 	PKCS7 * p7 = NULL;
5443 	int i;
5444 
5445 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &p7b, &p7b_len,
5446 				&zout) == FAILURE) {
5447 		return;
5448 	}
5449 
5450 	RETVAL_FALSE;
5451 
5452 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(p7b_len, p7b);
5453 
5454 	bio_in = BIO_new(BIO_s_mem());
5455 	if (bio_in == NULL) {
5456 		goto clean_exit;
5457 	}
5458 
5459 	if (0 >= BIO_write(bio_in, p7b, (int)p7b_len)) {
5460 		php_openssl_store_errors();
5461 		goto clean_exit;
5462 	}
5463 
5464 	p7 = PEM_read_bio_PKCS7(bio_in, NULL, NULL, NULL);
5465 	if (p7 == NULL) {
5466 		php_openssl_store_errors();
5467 		goto clean_exit;
5468 	}
5469 
5470 	switch (OBJ_obj2nid(p7->type)) {
5471 		case NID_pkcs7_signed:
5472 			if (p7->d.sign != NULL) {
5473 				certs = p7->d.sign->cert;
5474 				crls = p7->d.sign->crl;
5475 			}
5476 			break;
5477 		case NID_pkcs7_signedAndEnveloped:
5478 			if (p7->d.signed_and_enveloped != NULL) {
5479 				certs = p7->d.signed_and_enveloped->cert;
5480 				crls = p7->d.signed_and_enveloped->crl;
5481 			}
5482 			break;
5483 		default:
5484 			break;
5485 	}
5486 
5487 	zout = zend_try_array_init(zout);
5488 	if (!zout) {
5489 		goto clean_exit;
5490 	}
5491 
5492 	if (certs != NULL) {
5493 		for (i = 0; i < sk_X509_num(certs); i++) {
5494 			X509* ca = sk_X509_value(certs, i);
5495 
5496 			bio_out = BIO_new(BIO_s_mem());
5497 			if (bio_out && PEM_write_bio_X509(bio_out, ca)) {
5498 				BUF_MEM *bio_buf;
5499 				BIO_get_mem_ptr(bio_out, &bio_buf);
5500 				ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
5501 				add_index_zval(zout, i, &zcert);
5502 				BIO_free(bio_out);
5503 			}
5504 		}
5505 	}
5506 
5507 	if (crls != NULL) {
5508 		for (i = 0; i < sk_X509_CRL_num(crls); i++) {
5509 			X509_CRL* crl = sk_X509_CRL_value(crls, i);
5510 
5511 			bio_out = BIO_new(BIO_s_mem());
5512 			if (bio_out && PEM_write_bio_X509_CRL(bio_out, crl)) {
5513 				BUF_MEM *bio_buf;
5514 				BIO_get_mem_ptr(bio_out, &bio_buf);
5515 				ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
5516 				add_index_zval(zout, i, &zcert);
5517 				BIO_free(bio_out);
5518 			}
5519 		}
5520 	}
5521 
5522 	RETVAL_TRUE;
5523 
5524 clean_exit:
5525 	if (bio_in != NULL) {
5526 		BIO_free(bio_in);
5527 	}
5528 
5529 	if (p7 != NULL) {
5530 		PKCS7_free(p7);
5531 	}
5532 }
5533 /* }}} */
5534 
5535 /* {{{ proto bool openssl_pkcs7_sign(string infile, string outfile, mixed signcert, mixed signkey, array headers [, int flags [, string extracertsfilename]])
5536    Signs the MIME message in the file named infile with signcert/signkey and output the result to file name outfile. headers lists plain text headers to exclude from the signed portion of the message, and should include to, from and subject as a minimum */
5537 
PHP_FUNCTION(openssl_pkcs7_sign)5538 PHP_FUNCTION(openssl_pkcs7_sign)
5539 {
5540 	zval * zcert, * zprivkey, * zheaders;
5541 	zval * hval;
5542 	X509 * cert = NULL;
5543 	EVP_PKEY * privkey = NULL;
5544 	zend_long flags = PKCS7_DETACHED;
5545 	PKCS7 * p7 = NULL;
5546 	BIO * infile = NULL, * outfile = NULL;
5547 	STACK_OF(X509) *others = NULL;
5548 	zend_resource *certresource = NULL, *keyresource = NULL;
5549 	zend_string * strindex;
5550 	char * infilename;
5551 	size_t infilename_len;
5552 	char * outfilename;
5553 	size_t outfilename_len;
5554 	char * extracertsfilename = NULL;
5555 	size_t extracertsfilename_len;
5556 
5557 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppzza!|lp!",
5558 				&infilename, &infilename_len, &outfilename, &outfilename_len,
5559 				&zcert, &zprivkey, &zheaders, &flags, &extracertsfilename,
5560 				&extracertsfilename_len) == FAILURE) {
5561 		return;
5562 	}
5563 
5564 	RETVAL_FALSE;
5565 
5566 	if (extracertsfilename) {
5567 		others = php_openssl_load_all_certs_from_file(extracertsfilename);
5568 		if (others == NULL) {
5569 			goto clean_exit;
5570 		}
5571 	}
5572 
5573 	privkey = php_openssl_evp_from_zval(zprivkey, 0, "", 0, 0, &keyresource);
5574 	if (privkey == NULL) {
5575 		php_error_docref(NULL, E_WARNING, "error getting private key");
5576 		goto clean_exit;
5577 	}
5578 
5579 	cert = php_openssl_x509_from_zval(zcert, 0, &certresource);
5580 	if (cert == NULL) {
5581 		php_error_docref(NULL, E_WARNING, "error getting cert");
5582 		goto clean_exit;
5583 	}
5584 
5585 	if (php_openssl_open_base_dir_chk(infilename) || php_openssl_open_base_dir_chk(outfilename)) {
5586 		goto clean_exit;
5587 	}
5588 
5589 	infile = BIO_new_file(infilename, PHP_OPENSSL_BIO_MODE_R(flags));
5590 	if (infile == NULL) {
5591 		php_openssl_store_errors();
5592 		php_error_docref(NULL, E_WARNING, "error opening input file %s!", infilename);
5593 		goto clean_exit;
5594 	}
5595 
5596 	outfile = BIO_new_file(outfilename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5597 	if (outfile == NULL) {
5598 		php_openssl_store_errors();
5599 		php_error_docref(NULL, E_WARNING, "error opening output file %s!", outfilename);
5600 		goto clean_exit;
5601 	}
5602 
5603 	p7 = PKCS7_sign(cert, privkey, others, infile, (int)flags);
5604 	if (p7 == NULL) {
5605 		php_openssl_store_errors();
5606 		php_error_docref(NULL, E_WARNING, "error creating PKCS7 structure!");
5607 		goto clean_exit;
5608 	}
5609 
5610 	(void)BIO_reset(infile);
5611 
5612 	/* tack on extra headers */
5613 	if (zheaders) {
5614 		int ret;
5615 
5616 		ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, hval) {
5617 			zend_string *str = zval_try_get_string(hval);
5618 			if (UNEXPECTED(!str)) {
5619 				goto clean_exit;
5620 			}
5621 			if (strindex) {
5622 				ret = BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), ZSTR_VAL(str));
5623 			} else {
5624 				ret = BIO_printf(outfile, "%s\n", ZSTR_VAL(str));
5625 			}
5626 			zend_string_release(str);
5627 			if (ret < 0) {
5628 				php_openssl_store_errors();
5629 			}
5630 		} ZEND_HASH_FOREACH_END();
5631 	}
5632 	/* write the signed data */
5633 	if (!SMIME_write_PKCS7(outfile, p7, infile, (int)flags)) {
5634 		php_openssl_store_errors();
5635 		goto clean_exit;
5636 	}
5637 
5638 	RETVAL_TRUE;
5639 
5640 clean_exit:
5641 	PKCS7_free(p7);
5642 	BIO_free(infile);
5643 	BIO_free(outfile);
5644 	if (others) {
5645 		sk_X509_pop_free(others, X509_free);
5646 	}
5647 	if (privkey && keyresource == NULL) {
5648 		EVP_PKEY_free(privkey);
5649 	}
5650 	if (cert && certresource == NULL) {
5651 		X509_free(cert);
5652 	}
5653 }
5654 /* }}} */
5655 
5656 /* {{{ proto bool openssl_pkcs7_decrypt(string infilename, string outfilename, mixed recipcert [, mixed recipkey])
5657    Decrypts the S/MIME message in the file name infilename and output the results to the file name outfilename.  recipcert is a CERT for one of the recipients. recipkey specifies the private key matching recipcert, if recipcert does not include the key */
5658 
PHP_FUNCTION(openssl_pkcs7_decrypt)5659 PHP_FUNCTION(openssl_pkcs7_decrypt)
5660 {
5661 	zval * recipcert, * recipkey = NULL;
5662 	X509 * cert = NULL;
5663 	EVP_PKEY * key = NULL;
5664 	zend_resource *certresval, *keyresval;
5665 	BIO * in = NULL, * out = NULL, * datain = NULL;
5666 	PKCS7 * p7 = NULL;
5667 	char * infilename;
5668 	size_t infilename_len;
5669 	char * outfilename;
5670 	size_t outfilename_len;
5671 
5672 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppz|z", &infilename, &infilename_len,
5673 				&outfilename, &outfilename_len, &recipcert, &recipkey) == FAILURE) {
5674 		return;
5675 	}
5676 
5677 	RETVAL_FALSE;
5678 
5679 	cert = php_openssl_x509_from_zval(recipcert, 0, &certresval);
5680 	if (cert == NULL) {
5681 		php_error_docref(NULL, E_WARNING, "unable to coerce parameter 3 to x509 cert");
5682 		goto clean_exit;
5683 	}
5684 
5685 	key = php_openssl_evp_from_zval(recipkey ? recipkey : recipcert, 0, "", 0, 0, &keyresval);
5686 	if (key == NULL) {
5687 		php_error_docref(NULL, E_WARNING, "unable to get private key");
5688 		goto clean_exit;
5689 	}
5690 
5691 	if (php_openssl_open_base_dir_chk(infilename) || php_openssl_open_base_dir_chk(outfilename)) {
5692 		goto clean_exit;
5693 	}
5694 
5695 	in = BIO_new_file(infilename, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
5696 	if (in == NULL) {
5697 		php_openssl_store_errors();
5698 		goto clean_exit;
5699 	}
5700 	out = BIO_new_file(outfilename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5701 	if (out == NULL) {
5702 		php_openssl_store_errors();
5703 		goto clean_exit;
5704 	}
5705 
5706 	p7 = SMIME_read_PKCS7(in, &datain);
5707 
5708 	if (p7 == NULL) {
5709 		php_openssl_store_errors();
5710 		goto clean_exit;
5711 	}
5712 	if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED)) {
5713 		RETVAL_TRUE;
5714 	} else {
5715 		php_openssl_store_errors();
5716 	}
5717 clean_exit:
5718 	PKCS7_free(p7);
5719 	BIO_free(datain);
5720 	BIO_free(in);
5721 	BIO_free(out);
5722 	if (cert && certresval == NULL) {
5723 		X509_free(cert);
5724 	}
5725 	if (key && keyresval == NULL) {
5726 		EVP_PKEY_free(key);
5727 	}
5728 }
5729 /* }}} */
5730 
5731 /* }}} */
5732 
5733 /* {{{ proto bool openssl_private_encrypt(string data, string &crypted, mixed key [, int padding])
5734    Encrypts data with private key */
PHP_FUNCTION(openssl_private_encrypt)5735 PHP_FUNCTION(openssl_private_encrypt)
5736 {
5737 	zval *key, *crypted;
5738 	EVP_PKEY *pkey;
5739 	int cryptedlen;
5740 	zend_string *cryptedbuf = NULL;
5741 	int successful = 0;
5742 	zend_resource *keyresource = NULL;
5743 	char * data;
5744 	size_t data_len;
5745 	zend_long padding = RSA_PKCS1_PADDING;
5746 
5747 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
5748 		return;
5749 	}
5750 	RETVAL_FALSE;
5751 
5752 	pkey = php_openssl_evp_from_zval(key, 0, "", 0, 0, &keyresource);
5753 
5754 	if (pkey == NULL) {
5755 		php_error_docref(NULL, E_WARNING, "key param is not a valid private key");
5756 		RETURN_FALSE;
5757 	}
5758 
5759 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
5760 
5761 	cryptedlen = EVP_PKEY_size(pkey);
5762 	cryptedbuf = zend_string_alloc(cryptedlen, 0);
5763 
5764 	switch (EVP_PKEY_id(pkey)) {
5765 		case EVP_PKEY_RSA:
5766 		case EVP_PKEY_RSA2:
5767 			successful = (RSA_private_encrypt((int)data_len,
5768 						(unsigned char *)data,
5769 						(unsigned char *)ZSTR_VAL(cryptedbuf),
5770 						EVP_PKEY_get0_RSA(pkey),
5771 						(int)padding) == cryptedlen);
5772 			break;
5773 		default:
5774 			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
5775 	}
5776 
5777 	if (successful) {
5778 		ZSTR_VAL(cryptedbuf)[cryptedlen] = '\0';
5779 		ZEND_TRY_ASSIGN_REF_NEW_STR(crypted, cryptedbuf);
5780 		cryptedbuf = NULL;
5781 		RETVAL_TRUE;
5782 	} else {
5783 		php_openssl_store_errors();
5784 	}
5785 	if (cryptedbuf) {
5786 		zend_string_release_ex(cryptedbuf, 0);
5787 	}
5788 	if (keyresource == NULL) {
5789 		EVP_PKEY_free(pkey);
5790 	}
5791 }
5792 /* }}} */
5793 
5794 /* {{{ proto bool openssl_private_decrypt(string data, string &decrypted, mixed key [, int padding])
5795    Decrypts data with private key */
PHP_FUNCTION(openssl_private_decrypt)5796 PHP_FUNCTION(openssl_private_decrypt)
5797 {
5798 	zval *key, *crypted;
5799 	EVP_PKEY *pkey;
5800 	int cryptedlen;
5801 	zend_string *cryptedbuf = NULL;
5802 	unsigned char *crypttemp;
5803 	int successful = 0;
5804 	zend_long padding = RSA_PKCS1_PADDING;
5805 	zend_resource *keyresource = NULL;
5806 	char * data;
5807 	size_t data_len;
5808 
5809 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
5810 		return;
5811 	}
5812 	RETVAL_FALSE;
5813 
5814 	pkey = php_openssl_evp_from_zval(key, 0, "", 0, 0, &keyresource);
5815 	if (pkey == NULL) {
5816 		php_error_docref(NULL, E_WARNING, "key parameter is not a valid private key");
5817 		RETURN_FALSE;
5818 	}
5819 
5820 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
5821 
5822 	cryptedlen = EVP_PKEY_size(pkey);
5823 	crypttemp = emalloc(cryptedlen + 1);
5824 
5825 	switch (EVP_PKEY_id(pkey)) {
5826 		case EVP_PKEY_RSA:
5827 		case EVP_PKEY_RSA2:
5828 			cryptedlen = RSA_private_decrypt((int)data_len,
5829 					(unsigned char *)data,
5830 					crypttemp,
5831 					EVP_PKEY_get0_RSA(pkey),
5832 					(int)padding);
5833 			if (cryptedlen != -1) {
5834 				cryptedbuf = zend_string_alloc(cryptedlen, 0);
5835 				memcpy(ZSTR_VAL(cryptedbuf), crypttemp, cryptedlen);
5836 				successful = 1;
5837 			}
5838 			break;
5839 		default:
5840 			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
5841 	}
5842 
5843 	efree(crypttemp);
5844 
5845 	if (successful) {
5846 		ZSTR_VAL(cryptedbuf)[cryptedlen] = '\0';
5847 		ZEND_TRY_ASSIGN_REF_NEW_STR(crypted, cryptedbuf);
5848 		cryptedbuf = NULL;
5849 		RETVAL_TRUE;
5850 	} else {
5851 		php_openssl_store_errors();
5852 	}
5853 
5854 	if (keyresource == NULL) {
5855 		EVP_PKEY_free(pkey);
5856 	}
5857 	if (cryptedbuf) {
5858 		zend_string_release_ex(cryptedbuf, 0);
5859 	}
5860 }
5861 /* }}} */
5862 
5863 /* {{{ proto bool openssl_public_encrypt(string data, string &crypted, mixed key [, int padding])
5864    Encrypts data with public key */
PHP_FUNCTION(openssl_public_encrypt)5865 PHP_FUNCTION(openssl_public_encrypt)
5866 {
5867 	zval *key, *crypted;
5868 	EVP_PKEY *pkey;
5869 	int cryptedlen;
5870 	zend_string *cryptedbuf;
5871 	int successful = 0;
5872 	zend_resource *keyresource = NULL;
5873 	zend_long padding = RSA_PKCS1_PADDING;
5874 	char * data;
5875 	size_t data_len;
5876 
5877 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
5878 		return;
5879 	RETVAL_FALSE;
5880 
5881 	pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, 0, &keyresource);
5882 	if (pkey == NULL) {
5883 		php_error_docref(NULL, E_WARNING, "key parameter is not a valid public key");
5884 		RETURN_FALSE;
5885 	}
5886 
5887 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
5888 
5889 	cryptedlen = EVP_PKEY_size(pkey);
5890 	cryptedbuf = zend_string_alloc(cryptedlen, 0);
5891 
5892 	switch (EVP_PKEY_id(pkey)) {
5893 		case EVP_PKEY_RSA:
5894 		case EVP_PKEY_RSA2:
5895 			successful = (RSA_public_encrypt((int)data_len,
5896 						(unsigned char *)data,
5897 						(unsigned char *)ZSTR_VAL(cryptedbuf),
5898 						EVP_PKEY_get0_RSA(pkey),
5899 						(int)padding) == cryptedlen);
5900 			break;
5901 		default:
5902 			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
5903 
5904 	}
5905 
5906 	if (successful) {
5907 		ZSTR_VAL(cryptedbuf)[cryptedlen] = '\0';
5908 		ZEND_TRY_ASSIGN_REF_NEW_STR(crypted, cryptedbuf);
5909 		cryptedbuf = NULL;
5910 		RETVAL_TRUE;
5911 	} else {
5912 		php_openssl_store_errors();
5913 	}
5914 	if (keyresource == NULL) {
5915 		EVP_PKEY_free(pkey);
5916 	}
5917 	if (cryptedbuf) {
5918 		zend_string_release_ex(cryptedbuf, 0);
5919 	}
5920 }
5921 /* }}} */
5922 
5923 /* {{{ proto bool openssl_public_decrypt(string data, string &crypted, resource key [, int padding])
5924    Decrypts data with public key */
PHP_FUNCTION(openssl_public_decrypt)5925 PHP_FUNCTION(openssl_public_decrypt)
5926 {
5927 	zval *key, *crypted;
5928 	EVP_PKEY *pkey;
5929 	int cryptedlen;
5930 	zend_string *cryptedbuf = NULL;
5931 	unsigned char *crypttemp;
5932 	int successful = 0;
5933 	zend_resource *keyresource = NULL;
5934 	zend_long padding = RSA_PKCS1_PADDING;
5935 	char * data;
5936 	size_t data_len;
5937 
5938 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
5939 		return;
5940 	}
5941 	RETVAL_FALSE;
5942 
5943 	pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, 0, &keyresource);
5944 	if (pkey == NULL) {
5945 		php_error_docref(NULL, E_WARNING, "key parameter is not a valid public key");
5946 		RETURN_FALSE;
5947 	}
5948 
5949 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
5950 
5951 	cryptedlen = EVP_PKEY_size(pkey);
5952 	crypttemp = emalloc(cryptedlen + 1);
5953 
5954 	switch (EVP_PKEY_id(pkey)) {
5955 		case EVP_PKEY_RSA:
5956 		case EVP_PKEY_RSA2:
5957 			cryptedlen = RSA_public_decrypt((int)data_len,
5958 					(unsigned char *)data,
5959 					crypttemp,
5960 					EVP_PKEY_get0_RSA(pkey),
5961 					(int)padding);
5962 			if (cryptedlen != -1) {
5963 				cryptedbuf = zend_string_alloc(cryptedlen, 0);
5964 				memcpy(ZSTR_VAL(cryptedbuf), crypttemp, cryptedlen);
5965 				successful = 1;
5966 			}
5967 			break;
5968 
5969 		default:
5970 			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
5971 
5972 	}
5973 
5974 	efree(crypttemp);
5975 
5976 	if (successful) {
5977 		ZSTR_VAL(cryptedbuf)[cryptedlen] = '\0';
5978 		ZEND_TRY_ASSIGN_REF_NEW_STR(crypted, cryptedbuf);
5979 		cryptedbuf = NULL;
5980 		RETVAL_TRUE;
5981 	} else {
5982 		php_openssl_store_errors();
5983 	}
5984 
5985 	if (cryptedbuf) {
5986 		zend_string_release_ex(cryptedbuf, 0);
5987 	}
5988 	if (keyresource == NULL) {
5989 		EVP_PKEY_free(pkey);
5990 	}
5991 }
5992 /* }}} */
5993 
5994 /* {{{ proto mixed openssl_error_string(void)
5995    Returns a description of the last error, and alters the index of the error messages. Returns false when the are no more messages */
PHP_FUNCTION(openssl_error_string)5996 PHP_FUNCTION(openssl_error_string)
5997 {
5998 	char buf[256];
5999 	unsigned long val;
6000 
6001 	if (zend_parse_parameters_none() == FAILURE) {
6002 		return;
6003 	}
6004 
6005 	php_openssl_store_errors();
6006 
6007 	if (OPENSSL_G(errors) == NULL || OPENSSL_G(errors)->top == OPENSSL_G(errors)->bottom) {
6008 		RETURN_FALSE;
6009 	}
6010 
6011 	OPENSSL_G(errors)->bottom = (OPENSSL_G(errors)->bottom + 1) % ERR_NUM_ERRORS;
6012 	val = OPENSSL_G(errors)->buffer[OPENSSL_G(errors)->bottom];
6013 
6014 	if (val) {
6015 		ERR_error_string_n(val, buf, 256);
6016 		RETURN_STRING(buf);
6017 	} else {
6018 		RETURN_FALSE;
6019 	}
6020 }
6021 /* }}} */
6022 
6023 /* {{{ proto bool openssl_sign(string data, &string signature, mixed key[, mixed method])
6024    Signs data */
PHP_FUNCTION(openssl_sign)6025 PHP_FUNCTION(openssl_sign)
6026 {
6027 	zval *key, *signature;
6028 	EVP_PKEY *pkey;
6029 	unsigned int siglen;
6030 	zend_string *sigbuf;
6031 	zend_resource *keyresource = NULL;
6032 	char * data;
6033 	size_t data_len;
6034 	EVP_MD_CTX *md_ctx;
6035 	zval *method = NULL;
6036 	zend_long signature_algo = OPENSSL_ALGO_SHA1;
6037 	const EVP_MD *mdtype;
6038 
6039 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|z", &data, &data_len, &signature, &key, &method) == FAILURE) {
6040 		return;
6041 	}
6042 	pkey = php_openssl_evp_from_zval(key, 0, "", 0, 0, &keyresource);
6043 	if (pkey == NULL) {
6044 		php_error_docref(NULL, E_WARNING, "supplied key param cannot be coerced into a private key");
6045 		RETURN_FALSE;
6046 	}
6047 
6048 	if (method == NULL || Z_TYPE_P(method) == IS_LONG) {
6049 		if (method != NULL) {
6050 			signature_algo = Z_LVAL_P(method);
6051 		}
6052 		mdtype = php_openssl_get_evp_md_from_algo(signature_algo);
6053 	} else if (Z_TYPE_P(method) == IS_STRING) {
6054 		mdtype = EVP_get_digestbyname(Z_STRVAL_P(method));
6055 	} else {
6056 		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm.");
6057 		RETURN_FALSE;
6058 	}
6059 	if (!mdtype) {
6060 		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm.");
6061 		RETURN_FALSE;
6062 	}
6063 
6064 	siglen = EVP_PKEY_size(pkey);
6065 	sigbuf = zend_string_alloc(siglen, 0);
6066 
6067 	md_ctx = EVP_MD_CTX_create();
6068 	if (md_ctx != NULL &&
6069 			EVP_SignInit(md_ctx, mdtype) &&
6070 			EVP_SignUpdate(md_ctx, data, data_len) &&
6071 			EVP_SignFinal(md_ctx, (unsigned char*)ZSTR_VAL(sigbuf), &siglen, pkey)) {
6072 		ZSTR_VAL(sigbuf)[siglen] = '\0';
6073 		ZSTR_LEN(sigbuf) = siglen;
6074 		ZEND_TRY_ASSIGN_REF_NEW_STR(signature, sigbuf);
6075 		RETVAL_TRUE;
6076 	} else {
6077 		php_openssl_store_errors();
6078 		efree(sigbuf);
6079 		RETVAL_FALSE;
6080 	}
6081 	EVP_MD_CTX_destroy(md_ctx);
6082 	if (keyresource == NULL) {
6083 		EVP_PKEY_free(pkey);
6084 	}
6085 }
6086 /* }}} */
6087 
6088 /* {{{ proto int openssl_verify(string data, string signature, mixed key[, mixed method])
6089    Verifys data */
PHP_FUNCTION(openssl_verify)6090 PHP_FUNCTION(openssl_verify)
6091 {
6092 	zval *key;
6093 	EVP_PKEY *pkey;
6094 	int err = 0;
6095 	EVP_MD_CTX *md_ctx;
6096 	const EVP_MD *mdtype;
6097 	zend_resource *keyresource = NULL;
6098 	char * data;
6099 	size_t data_len;
6100 	char * signature;
6101 	size_t signature_len;
6102 	zval *method = NULL;
6103 	zend_long signature_algo = OPENSSL_ALGO_SHA1;
6104 
6105 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz|z", &data, &data_len, &signature, &signature_len, &key, &method) == FAILURE) {
6106 		return;
6107 	}
6108 
6109 	PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(signature_len, signature);
6110 
6111 	if (method == NULL || Z_TYPE_P(method) == IS_LONG) {
6112 		if (method != NULL) {
6113 			signature_algo = Z_LVAL_P(method);
6114 		}
6115 		mdtype = php_openssl_get_evp_md_from_algo(signature_algo);
6116 	} else if (Z_TYPE_P(method) == IS_STRING) {
6117 		mdtype = EVP_get_digestbyname(Z_STRVAL_P(method));
6118 	} else {
6119 		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm.");
6120 		RETURN_FALSE;
6121 	}
6122 	if (!mdtype) {
6123 		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm.");
6124 		RETURN_FALSE;
6125 	}
6126 
6127 	pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, 0, &keyresource);
6128 	if (pkey == NULL) {
6129 		php_error_docref(NULL, E_WARNING, "supplied key param cannot be coerced into a public key");
6130 		RETURN_FALSE;
6131 	}
6132 
6133 	md_ctx = EVP_MD_CTX_create();
6134 	if (md_ctx == NULL ||
6135 			!EVP_VerifyInit (md_ctx, mdtype) ||
6136 			!EVP_VerifyUpdate (md_ctx, data, data_len) ||
6137 			(err = EVP_VerifyFinal(md_ctx, (unsigned char *)signature, (unsigned int)signature_len, pkey)) < 0) {
6138 		php_openssl_store_errors();
6139 	}
6140 	EVP_MD_CTX_destroy(md_ctx);
6141 
6142 	if (keyresource == NULL) {
6143 		EVP_PKEY_free(pkey);
6144 	}
6145 	RETURN_LONG(err);
6146 }
6147 /* }}} */
6148 
6149 /* {{{ proto int openssl_seal(string data, &string sealdata, &array ekeys, array pubkeys [, string method [, &string iv]]))
6150    Seals data */
PHP_FUNCTION(openssl_seal)6151 PHP_FUNCTION(openssl_seal)
6152 {
6153 	zval *pubkeys, *pubkey, *sealdata, *ekeys, *iv = NULL;
6154 	HashTable *pubkeysht;
6155 	EVP_PKEY **pkeys;
6156 	zend_resource ** key_resources;	/* so we know what to cleanup */
6157 	int i, len1, len2, *eksl, nkeys, iv_len;
6158 	unsigned char iv_buf[EVP_MAX_IV_LENGTH + 1], *buf = NULL, **eks;
6159 	char * data;
6160 	size_t data_len;
6161 	char *method =NULL;
6162 	size_t method_len = 0;
6163 	const EVP_CIPHER *cipher;
6164 	EVP_CIPHER_CTX *ctx;
6165 
6166 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szza|sz", &data, &data_len,
6167 				&sealdata, &ekeys, &pubkeys, &method, &method_len, &iv) == FAILURE) {
6168 		return;
6169 	}
6170 	pubkeysht = Z_ARRVAL_P(pubkeys);
6171 	nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0;
6172 	if (!nkeys) {
6173 		php_error_docref(NULL, E_WARNING, "Fourth argument to openssl_seal() must be a non-empty array");
6174 		RETURN_FALSE;
6175 	}
6176 
6177 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
6178 
6179 	if (method) {
6180 		cipher = EVP_get_cipherbyname(method);
6181 		if (!cipher) {
6182 			php_error_docref(NULL, E_WARNING, "Unknown signature algorithm.");
6183 			RETURN_FALSE;
6184 		}
6185 	} else {
6186 		cipher = EVP_rc4();
6187 	}
6188 
6189 	iv_len = EVP_CIPHER_iv_length(cipher);
6190 	if (!iv && iv_len > 0) {
6191 		php_error_docref(NULL, E_WARNING,
6192 				"Cipher algorithm requires an IV to be supplied as a sixth parameter");
6193 		RETURN_FALSE;
6194 	}
6195 
6196 	pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0);
6197 	eksl = safe_emalloc(nkeys, sizeof(*eksl), 0);
6198 	eks = safe_emalloc(nkeys, sizeof(*eks), 0);
6199 	memset(eks, 0, sizeof(*eks) * nkeys);
6200 	key_resources = safe_emalloc(nkeys, sizeof(zend_resource*), 0);
6201 	memset(key_resources, 0, sizeof(zend_resource*) * nkeys);
6202 	memset(pkeys, 0, sizeof(*pkeys) * nkeys);
6203 
6204 	/* get the public keys we are using to seal this data */
6205 	i = 0;
6206 	ZEND_HASH_FOREACH_VAL(pubkeysht, pubkey) {
6207 		pkeys[i] = php_openssl_evp_from_zval(pubkey, 1, NULL, 0, 0, &key_resources[i]);
6208 		if (pkeys[i] == NULL) {
6209 			php_error_docref(NULL, E_WARNING, "not a public key (%dth member of pubkeys)", i+1);
6210 			RETVAL_FALSE;
6211 			goto clean_exit;
6212 		}
6213 		eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1);
6214 		i++;
6215 	} ZEND_HASH_FOREACH_END();
6216 
6217 	ctx = EVP_CIPHER_CTX_new();
6218 	if (ctx == NULL || !EVP_EncryptInit(ctx,cipher,NULL,NULL)) {
6219 		EVP_CIPHER_CTX_free(ctx);
6220 		php_openssl_store_errors();
6221 		RETVAL_FALSE;
6222 		goto clean_exit;
6223 	}
6224 
6225 	/* allocate one byte extra to make room for \0 */
6226 	buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(ctx));
6227 	EVP_CIPHER_CTX_reset(ctx);
6228 
6229 	if (EVP_SealInit(ctx, cipher, eks, eksl, &iv_buf[0], pkeys, nkeys) <= 0 ||
6230 			!EVP_SealUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) ||
6231 			!EVP_SealFinal(ctx, buf + len1, &len2)) {
6232 		efree(buf);
6233 		EVP_CIPHER_CTX_free(ctx);
6234 		php_openssl_store_errors();
6235 		RETVAL_FALSE;
6236 		goto clean_exit;
6237 	}
6238 
6239 	if (len1 + len2 > 0) {
6240 		ZEND_TRY_ASSIGN_REF_NEW_STR(sealdata, zend_string_init((char*)buf, len1 + len2, 0));
6241 		efree(buf);
6242 
6243 		ekeys = zend_try_array_init(ekeys);
6244 		if (!ekeys) {
6245 			EVP_CIPHER_CTX_free(ctx);
6246 			goto clean_exit;
6247 		}
6248 
6249 		for (i=0; i<nkeys; i++) {
6250 			eks[i][eksl[i]] = '\0';
6251 			add_next_index_stringl(ekeys, (const char*)eks[i], eksl[i]);
6252 			efree(eks[i]);
6253 			eks[i] = NULL;
6254 		}
6255 
6256 		if (iv) {
6257 			iv_buf[iv_len] = '\0';
6258 			ZEND_TRY_ASSIGN_REF_NEW_STR(iv, zend_string_init((char*)iv_buf, iv_len, 0));
6259 		}
6260 	} else {
6261 		efree(buf);
6262 	}
6263 	RETVAL_LONG(len1 + len2);
6264 	EVP_CIPHER_CTX_free(ctx);
6265 
6266 clean_exit:
6267 	for (i=0; i<nkeys; i++) {
6268 		if (key_resources[i] == NULL && pkeys[i] != NULL) {
6269 			EVP_PKEY_free(pkeys[i]);
6270 		}
6271 		if (eks[i]) {
6272 			efree(eks[i]);
6273 		}
6274 	}
6275 	efree(eks);
6276 	efree(eksl);
6277 	efree(pkeys);
6278 	efree(key_resources);
6279 }
6280 /* }}} */
6281 
6282 /* {{{ proto bool openssl_open(string data, &string opendata, string ekey, mixed privkey [, string method [, string iv]])
6283    Opens data */
PHP_FUNCTION(openssl_open)6284 PHP_FUNCTION(openssl_open)
6285 {
6286 	zval *privkey, *opendata;
6287 	EVP_PKEY *pkey;
6288 	int len1, len2, cipher_iv_len;
6289 	unsigned char *buf, *iv_buf;
6290 	zend_resource *keyresource = NULL;
6291 	EVP_CIPHER_CTX *ctx;
6292 	char * data;
6293 	size_t data_len;
6294 	char * ekey;
6295 	size_t ekey_len;
6296 	char *method = NULL, *iv = NULL;
6297 	size_t method_len = 0, iv_len = 0;
6298 	const EVP_CIPHER *cipher;
6299 
6300 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szsz|ss", &data, &data_len, &opendata,
6301 				&ekey, &ekey_len, &privkey, &method, &method_len, &iv, &iv_len) == FAILURE) {
6302 		return;
6303 	}
6304 
6305 	pkey = php_openssl_evp_from_zval(privkey, 0, "", 0, 0, &keyresource);
6306 	if (pkey == NULL) {
6307 		php_error_docref(NULL, E_WARNING, "unable to coerce parameter 4 into a private key");
6308 		RETURN_FALSE;
6309 	}
6310 
6311 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(ekey_len, ekey);
6312 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
6313 
6314 	if (method) {
6315 		cipher = EVP_get_cipherbyname(method);
6316 		if (!cipher) {
6317 			php_error_docref(NULL, E_WARNING, "Unknown signature algorithm.");
6318 			RETURN_FALSE;
6319 		}
6320 	} else {
6321 		cipher = EVP_rc4();
6322 	}
6323 
6324 	cipher_iv_len = EVP_CIPHER_iv_length(cipher);
6325 	if (cipher_iv_len > 0) {
6326 		if (!iv) {
6327 			php_error_docref(NULL, E_WARNING,
6328 					"Cipher algorithm requires an IV to be supplied as a sixth parameter");
6329 			RETURN_FALSE;
6330 		}
6331 		if ((size_t)cipher_iv_len != iv_len) {
6332 			php_error_docref(NULL, E_WARNING, "IV length is invalid");
6333 			RETURN_FALSE;
6334 		}
6335 		iv_buf = (unsigned char *)iv;
6336 	} else {
6337 		iv_buf = NULL;
6338 	}
6339 
6340 	buf = emalloc(data_len + 1);
6341 
6342 	ctx = EVP_CIPHER_CTX_new();
6343 	if (ctx != NULL && EVP_OpenInit(ctx, cipher, (unsigned char *)ekey, (int)ekey_len, iv_buf, pkey) &&
6344 			EVP_OpenUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) &&
6345 			EVP_OpenFinal(ctx, buf + len1, &len2) && (len1 + len2 > 0)) {
6346 		buf[len1 + len2] = '\0';
6347 		ZEND_TRY_ASSIGN_REF_NEW_STR(opendata, zend_string_init((char*)buf, len1 + len2, 0));
6348 		RETVAL_TRUE;
6349 	} else {
6350 		php_openssl_store_errors();
6351 		RETVAL_FALSE;
6352 	}
6353 
6354 	efree(buf);
6355 	if (keyresource == NULL) {
6356 		EVP_PKEY_free(pkey);
6357 	}
6358 	EVP_CIPHER_CTX_free(ctx);
6359 }
6360 /* }}} */
6361 
php_openssl_add_method_or_alias(const OBJ_NAME * name,void * arg)6362 static void php_openssl_add_method_or_alias(const OBJ_NAME *name, void *arg) /* {{{ */
6363 {
6364 	add_next_index_string((zval*)arg, (char*)name->name);
6365 }
6366 /* }}} */
6367 
php_openssl_add_method(const OBJ_NAME * name,void * arg)6368 static void php_openssl_add_method(const OBJ_NAME *name, void *arg) /* {{{ */
6369 {
6370 	if (name->alias == 0) {
6371 		add_next_index_string((zval*)arg, (char*)name->name);
6372 	}
6373 }
6374 /* }}} */
6375 
6376 /* {{{ proto array openssl_get_md_methods([bool aliases = false])
6377    Return array of available digest methods */
PHP_FUNCTION(openssl_get_md_methods)6378 PHP_FUNCTION(openssl_get_md_methods)
6379 {
6380 	zend_bool aliases = 0;
6381 
6382 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &aliases) == FAILURE) {
6383 		return;
6384 	}
6385 	array_init(return_value);
6386 	OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH,
6387 		aliases ? php_openssl_add_method_or_alias: php_openssl_add_method,
6388 		return_value);
6389 }
6390 /* }}} */
6391 
6392 /* {{{ proto array openssl_get_cipher_methods([bool aliases = false])
6393    Return array of available cipher methods */
PHP_FUNCTION(openssl_get_cipher_methods)6394 PHP_FUNCTION(openssl_get_cipher_methods)
6395 {
6396 	zend_bool aliases = 0;
6397 
6398 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &aliases) == FAILURE) {
6399 		return;
6400 	}
6401 	array_init(return_value);
6402 	OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
6403 		aliases ? php_openssl_add_method_or_alias: php_openssl_add_method,
6404 		return_value);
6405 }
6406 /* }}} */
6407 
6408 /* {{{ proto array openssl_get_curve_names()
6409    Return array of available elliptic curves */
6410 #ifdef HAVE_EVP_PKEY_EC
PHP_FUNCTION(openssl_get_curve_names)6411 PHP_FUNCTION(openssl_get_curve_names)
6412 {
6413 	EC_builtin_curve *curves = NULL;
6414 	const char *sname;
6415 	size_t i;
6416 	size_t len = EC_get_builtin_curves(NULL, 0);
6417 
6418 	curves = emalloc(sizeof(EC_builtin_curve) * len);
6419 	if (!EC_get_builtin_curves(curves, len)) {
6420 		RETURN_FALSE;
6421 	}
6422 
6423 	array_init(return_value);
6424 	for (i = 0; i < len; i++) {
6425 		sname = OBJ_nid2sn(curves[i].nid);
6426 		if (sname != NULL) {
6427 			add_next_index_string(return_value, sname);
6428 		}
6429 	}
6430 	efree(curves);
6431 }
6432 #endif
6433 /* }}} */
6434 
6435 /* {{{ proto string openssl_digest(string data, string method [, bool raw_output=false])
6436    Computes digest hash value for given data using given method, returns raw or binhex encoded string */
PHP_FUNCTION(openssl_digest)6437 PHP_FUNCTION(openssl_digest)
6438 {
6439 	zend_bool raw_output = 0;
6440 	char *data, *method;
6441 	size_t data_len, method_len;
6442 	const EVP_MD *mdtype;
6443 	EVP_MD_CTX *md_ctx;
6444 	unsigned int siglen;
6445 	zend_string *sigbuf;
6446 
6447 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) {
6448 		return;
6449 	}
6450 	mdtype = EVP_get_digestbyname(method);
6451 	if (!mdtype) {
6452 		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm");
6453 		RETURN_FALSE;
6454 	}
6455 
6456 	siglen = EVP_MD_size(mdtype);
6457 	sigbuf = zend_string_alloc(siglen, 0);
6458 
6459 	md_ctx = EVP_MD_CTX_create();
6460 	if (EVP_DigestInit(md_ctx, mdtype) &&
6461 			EVP_DigestUpdate(md_ctx, (unsigned char *)data, data_len) &&
6462 			EVP_DigestFinal (md_ctx, (unsigned char *)ZSTR_VAL(sigbuf), &siglen)) {
6463 		if (raw_output) {
6464 			ZSTR_VAL(sigbuf)[siglen] = '\0';
6465 			ZSTR_LEN(sigbuf) = siglen;
6466 			RETVAL_STR(sigbuf);
6467 		} else {
6468 			int digest_str_len = siglen * 2;
6469 			zend_string *digest_str = zend_string_alloc(digest_str_len, 0);
6470 
6471 			make_digest_ex(ZSTR_VAL(digest_str), (unsigned char*)ZSTR_VAL(sigbuf), siglen);
6472 			ZSTR_VAL(digest_str)[digest_str_len] = '\0';
6473 			zend_string_release_ex(sigbuf, 0);
6474 			RETVAL_NEW_STR(digest_str);
6475 		}
6476 	} else {
6477 		php_openssl_store_errors();
6478 		zend_string_release_ex(sigbuf, 0);
6479 		RETVAL_FALSE;
6480 	}
6481 
6482 	EVP_MD_CTX_destroy(md_ctx);
6483 }
6484 /* }}} */
6485 
6486 /* Cipher mode info */
6487 struct php_openssl_cipher_mode {
6488 	zend_bool is_aead;
6489 	zend_bool is_single_run_aead;
6490 	zend_bool set_tag_length_always;
6491 	zend_bool set_tag_length_when_encrypting;
6492 	int aead_get_tag_flag;
6493 	int aead_set_tag_flag;
6494 	int aead_ivlen_flag;
6495 };
6496 
php_openssl_load_cipher_mode(struct php_openssl_cipher_mode * mode,const EVP_CIPHER * cipher_type)6497 static void php_openssl_load_cipher_mode(struct php_openssl_cipher_mode *mode, const EVP_CIPHER *cipher_type) /* {{{ */
6498 {
6499 	int cipher_mode = EVP_CIPHER_mode(cipher_type);
6500 	memset(mode, 0, sizeof(struct php_openssl_cipher_mode));
6501 	switch (cipher_mode) {
6502 #ifdef EVP_CIPH_OCB_MODE
6503 		/* Since OpenSSL 1.1, all AEAD ciphers use a common framework. We check for
6504 		 * EVP_CIPH_OCB_MODE, because LibreSSL does not support it. */
6505 		case EVP_CIPH_GCM_MODE:
6506 		case EVP_CIPH_OCB_MODE:
6507 		case EVP_CIPH_CCM_MODE:
6508 			mode->is_aead = 1;
6509 			/* For OCB mode, explicitly set the tag length even when decrypting,
6510 			 * see https://github.com/openssl/openssl/issues/8331. */
6511 			mode->set_tag_length_always = cipher_mode == EVP_CIPH_OCB_MODE;
6512 			mode->set_tag_length_when_encrypting = cipher_mode == EVP_CIPH_CCM_MODE;
6513 			mode->is_single_run_aead = cipher_mode == EVP_CIPH_CCM_MODE;
6514 			mode->aead_get_tag_flag = EVP_CTRL_AEAD_GET_TAG;
6515 			mode->aead_set_tag_flag = EVP_CTRL_AEAD_SET_TAG;
6516 			mode->aead_ivlen_flag = EVP_CTRL_AEAD_SET_IVLEN;
6517 			break;
6518 #else
6519 # ifdef EVP_CIPH_GCM_MODE
6520 		case EVP_CIPH_GCM_MODE:
6521 			mode->is_aead = 1;
6522 			mode->aead_get_tag_flag = EVP_CTRL_GCM_GET_TAG;
6523 			mode->aead_set_tag_flag = EVP_CTRL_GCM_SET_TAG;
6524 			mode->aead_ivlen_flag = EVP_CTRL_GCM_SET_IVLEN;
6525 			break;
6526 # endif
6527 # ifdef EVP_CIPH_CCM_MODE
6528 		case EVP_CIPH_CCM_MODE:
6529 			mode->is_aead = 1;
6530 			mode->is_single_run_aead = 1;
6531 			mode->set_tag_length_when_encrypting = 1;
6532 			mode->aead_get_tag_flag = EVP_CTRL_CCM_GET_TAG;
6533 			mode->aead_set_tag_flag = EVP_CTRL_CCM_SET_TAG;
6534 			mode->aead_ivlen_flag = EVP_CTRL_CCM_SET_IVLEN;
6535 			break;
6536 # endif
6537 #endif
6538 	}
6539 }
6540 /* }}} */
6541 
php_openssl_validate_iv(char ** piv,size_t * piv_len,size_t iv_required_len,zend_bool * free_iv,EVP_CIPHER_CTX * cipher_ctx,struct php_openssl_cipher_mode * mode)6542 static int php_openssl_validate_iv(char **piv, size_t *piv_len, size_t iv_required_len,
6543 		zend_bool *free_iv, EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode) /* {{{ */
6544 {
6545 	char *iv_new;
6546 
6547 	if (mode->is_aead) {
6548 		if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_ivlen_flag, *piv_len, NULL) != 1) {
6549 			php_error_docref(NULL, E_WARNING, "Setting of IV length for AEAD mode failed");
6550 			return FAILURE;
6551 		}
6552 		return SUCCESS;
6553 	}
6554 
6555 	/* Best case scenario, user behaved */
6556 	if (*piv_len == iv_required_len) {
6557 		return SUCCESS;
6558 	}
6559 
6560 	iv_new = ecalloc(1, iv_required_len + 1);
6561 
6562 	if (*piv_len == 0) {
6563 		/* BC behavior */
6564 		*piv_len = iv_required_len;
6565 		*piv = iv_new;
6566 		*free_iv = 1;
6567 		return SUCCESS;
6568 
6569 	}
6570 
6571 	if (*piv_len < iv_required_len) {
6572 		php_error_docref(NULL, E_WARNING,
6573 				"IV passed is only %zd bytes long, cipher expects an IV of precisely %zd bytes, padding with \\0",
6574 				*piv_len, iv_required_len);
6575 		memcpy(iv_new, *piv, *piv_len);
6576 		*piv_len = iv_required_len;
6577 		*piv = iv_new;
6578 		*free_iv = 1;
6579 		return SUCCESS;
6580 	}
6581 
6582 	php_error_docref(NULL, E_WARNING,
6583 			"IV passed is %zd bytes long which is longer than the %zd expected by selected cipher, truncating",
6584 			*piv_len, iv_required_len);
6585 	memcpy(iv_new, *piv, iv_required_len);
6586 	*piv_len = iv_required_len;
6587 	*piv = iv_new;
6588 	*free_iv = 1;
6589 	return SUCCESS;
6590 
6591 }
6592 /* }}} */
6593 
php_openssl_cipher_init(const EVP_CIPHER * cipher_type,EVP_CIPHER_CTX * cipher_ctx,struct php_openssl_cipher_mode * mode,char ** ppassword,size_t * ppassword_len,zend_bool * free_password,char ** piv,size_t * piv_len,zend_bool * free_iv,char * tag,int tag_len,zend_long options,int enc)6594 static int php_openssl_cipher_init(const EVP_CIPHER *cipher_type,
6595 		EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
6596 		char **ppassword, size_t *ppassword_len, zend_bool *free_password,
6597 		char **piv, size_t *piv_len, zend_bool *free_iv,
6598 		char *tag, int tag_len, zend_long options, int enc)  /* {{{ */
6599 {
6600 	unsigned char *key;
6601 	int key_len, password_len;
6602 	size_t max_iv_len;
6603 
6604 	*free_password = 0;
6605 
6606 	max_iv_len = EVP_CIPHER_iv_length(cipher_type);
6607 	if (enc && *piv_len == 0 && max_iv_len > 0 && !mode->is_aead) {
6608 		php_error_docref(NULL, E_WARNING,
6609 				"Using an empty Initialization Vector (iv) is potentially insecure and not recommended");
6610 	}
6611 
6612 	if (!EVP_CipherInit_ex(cipher_ctx, cipher_type, NULL, NULL, NULL, enc)) {
6613 		php_openssl_store_errors();
6614 		return FAILURE;
6615 	}
6616 	if (php_openssl_validate_iv(piv, piv_len, max_iv_len, free_iv, cipher_ctx, mode) == FAILURE) {
6617 		return FAILURE;
6618 	}
6619 	if (mode->set_tag_length_always || (enc && mode->set_tag_length_when_encrypting)) {
6620 		if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, NULL)) {
6621 			php_error_docref(NULL, E_WARNING, "Setting tag length for AEAD cipher failed");
6622 			return FAILURE;
6623 		}
6624 	}
6625 	if (!enc && tag && tag_len > 0) {
6626 		if (!mode->is_aead) {
6627 			php_error_docref(NULL, E_WARNING, "The tag cannot be used because the cipher method does not support AEAD");
6628 		} else if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, (unsigned char *) tag)) {
6629 			php_error_docref(NULL, E_WARNING, "Setting tag for AEAD cipher decryption failed");
6630 			return FAILURE;
6631 		}
6632 	}
6633 	/* check and set key */
6634 	password_len = (int) *ppassword_len;
6635 	key_len = EVP_CIPHER_key_length(cipher_type);
6636 	if (key_len > password_len) {
6637 		if ((OPENSSL_DONT_ZERO_PAD_KEY & options) && !EVP_CIPHER_CTX_set_key_length(cipher_ctx, password_len)) {
6638 			php_openssl_store_errors();
6639 			php_error_docref(NULL, E_WARNING, "Key length cannot be set for the cipher method");
6640 			return FAILURE;
6641 		}
6642 		key = emalloc(key_len);
6643 		memset(key, 0, key_len);
6644 		memcpy(key, *ppassword, password_len);
6645 		*ppassword = (char *) key;
6646 		*ppassword_len = key_len;
6647 		*free_password = 1;
6648 	} else {
6649 		if (password_len > key_len && !EVP_CIPHER_CTX_set_key_length(cipher_ctx, password_len)) {
6650 			php_openssl_store_errors();
6651 		}
6652 		key = (unsigned char*)*ppassword;
6653 	}
6654 
6655 	if (!EVP_CipherInit_ex(cipher_ctx, NULL, NULL, key, (unsigned char *)*piv, enc)) {
6656 		php_openssl_store_errors();
6657 		return FAILURE;
6658 	}
6659 	if (options & OPENSSL_ZERO_PADDING) {
6660 		EVP_CIPHER_CTX_set_padding(cipher_ctx, 0);
6661 	}
6662 
6663 	return SUCCESS;
6664 }
6665 /* }}} */
6666 
php_openssl_cipher_update(const EVP_CIPHER * cipher_type,EVP_CIPHER_CTX * cipher_ctx,struct php_openssl_cipher_mode * mode,zend_string ** poutbuf,int * poutlen,char * data,size_t data_len,char * aad,size_t aad_len,int enc)6667 static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
6668 		EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
6669 		zend_string **poutbuf, int *poutlen, char *data, size_t data_len,
6670 		char *aad, size_t aad_len, int enc)  /* {{{ */
6671 {
6672 	int i = 0;
6673 
6674 	if (mode->is_single_run_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, NULL, (int)data_len)) {
6675 		php_openssl_store_errors();
6676 		php_error_docref(NULL, E_WARNING, "Setting of data length failed");
6677 		return FAILURE;
6678 	}
6679 
6680 	if (mode->is_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, (unsigned char *)aad, (int)aad_len)) {
6681 		php_openssl_store_errors();
6682 		php_error_docref(NULL, E_WARNING, "Setting of additional application data failed");
6683 		return FAILURE;
6684 	}
6685 
6686 	*poutbuf = zend_string_alloc((int)data_len + EVP_CIPHER_block_size(cipher_type), 0);
6687 
6688 	if (!EVP_CipherUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(*poutbuf),
6689 					&i, (unsigned char *)data, (int)data_len)) {
6690 		/* we don't show warning when we fail but if we ever do, then it should look like this:
6691 		if (mode->is_single_run_aead && !enc) {
6692 			php_error_docref(NULL, E_WARNING, "Tag verifycation failed");
6693 		} else {
6694 			php_error_docref(NULL, E_WARNING, enc ? "Encryption failed" : "Decryption failed");
6695 		}
6696 		*/
6697 		php_openssl_store_errors();
6698 		zend_string_release_ex(*poutbuf, 0);
6699 		return FAILURE;
6700 	}
6701 
6702 	*poutlen = i;
6703 
6704 	return SUCCESS;
6705 }
6706 /* }}} */
6707 
6708 
php_openssl_encrypt(char * data,size_t data_len,char * method,size_t method_len,char * password,size_t password_len,zend_long options,char * iv,size_t iv_len,zval * tag,zend_long tag_len,char * aad,size_t aad_len)6709 PHP_OPENSSL_API zend_string* php_openssl_encrypt(char *data, size_t data_len, char *method, size_t method_len, char *password, size_t password_len, zend_long options, char *iv, size_t iv_len, zval *tag, zend_long tag_len, char *aad, size_t aad_len)
6710 {
6711 	const EVP_CIPHER *cipher_type;
6712 	EVP_CIPHER_CTX *cipher_ctx;
6713 	struct php_openssl_cipher_mode mode;
6714 	int i = 0, outlen;
6715 	zend_bool free_iv = 0, free_password = 0;
6716 	zend_string *outbuf = NULL;
6717 
6718 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NORET(data_len, data);
6719 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NORET(password_len, password);
6720 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NORET(aad_len, aad);
6721 	PHP_OPENSSL_CHECK_LONG_TO_INT_NORET(tag_len, tag_len);
6722 
6723 
6724 	cipher_type = EVP_get_cipherbyname(method);
6725 	if (!cipher_type) {
6726 		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
6727 		return NULL;
6728 	}
6729 
6730 	cipher_ctx = EVP_CIPHER_CTX_new();
6731 	if (!cipher_ctx) {
6732 		php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
6733 		return NULL;
6734 	}
6735 
6736 	php_openssl_load_cipher_mode(&mode, cipher_type);
6737 
6738 	if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
6739 				&password, &password_len, &free_password,
6740 				&iv, &iv_len, &free_iv, NULL, tag_len, options, 1) == FAILURE ||
6741 			php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
6742 				data, data_len, aad, aad_len, 1) == FAILURE) {
6743 		outbuf = NULL;
6744 	} else if (EVP_EncryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) {
6745 		outlen += i;
6746 		if (options & OPENSSL_RAW_DATA) {
6747 			ZSTR_VAL(outbuf)[outlen] = '\0';
6748 			ZSTR_LEN(outbuf) = outlen;
6749 		} else {
6750 			zend_string *base64_str;
6751 
6752 			base64_str = php_base64_encode((unsigned char*)ZSTR_VAL(outbuf), outlen);
6753 			zend_string_release_ex(outbuf, 0);
6754 			outbuf = base64_str;
6755 		}
6756 		if (mode.is_aead && tag) {
6757 			zend_string *tag_str = zend_string_alloc(tag_len, 0);
6758 
6759 			if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode.aead_get_tag_flag, tag_len, ZSTR_VAL(tag_str)) == 1) {
6760 				ZSTR_VAL(tag_str)[tag_len] = '\0';
6761 				ZSTR_LEN(tag_str) = tag_len;
6762 				ZEND_TRY_ASSIGN_REF_NEW_STR(tag, tag_str);
6763 			} else {
6764 				php_error_docref(NULL, E_WARNING, "Retrieving verification tag failed");
6765 				zend_string_release_ex(tag_str, 0);
6766 				zend_string_release_ex(outbuf, 0);
6767 				outbuf = NULL;
6768 			}
6769 		} else if (tag) {
6770 			ZEND_TRY_ASSIGN_REF_NULL(tag);
6771 		} else if (mode.is_aead) {
6772 			php_error_docref(NULL, E_WARNING, "A tag should be provided when using AEAD mode");
6773 			zend_string_release_ex(outbuf, 0);
6774 			outbuf = NULL;
6775 		}
6776 	} else {
6777 		php_openssl_store_errors();
6778 		zend_string_release_ex(outbuf, 0);
6779 		outbuf = NULL;
6780 	}
6781 
6782 	if (free_password) {
6783 		efree(password);
6784 	}
6785 	if (free_iv) {
6786 		efree(iv);
6787 	}
6788 	EVP_CIPHER_CTX_reset(cipher_ctx);
6789 	EVP_CIPHER_CTX_free(cipher_ctx);
6790 	return outbuf;
6791 }
6792 
6793 /* {{{ proto string openssl_encrypt(string data, string method, string password [, int options=0 [, string $iv=''[, string &$tag = ''[, string $aad = ''[, int $tag_length = 16]]]]])
6794    Encrypts given data with given method and key, returns raw or base64 encoded string */
PHP_FUNCTION(openssl_encrypt)6795 PHP_FUNCTION(openssl_encrypt)
6796 {
6797 	zend_long options = 0, tag_len = 16;
6798 	char *data, *method, *password, *iv = "", *aad = "";
6799 	size_t data_len, method_len, password_len, iv_len = 0, aad_len = 0;
6800 	zend_string *ret;
6801 	zval *tag = NULL;
6802 
6803 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lszsl", &data, &data_len, &method, &method_len,
6804 					&password, &password_len, &options, &iv, &iv_len, &tag, &aad, &aad_len, &tag_len) == FAILURE) {
6805 		return;
6806 	}
6807 
6808 	if ((ret = php_openssl_encrypt(data, data_len, method, method_len, password, password_len, options, iv, iv_len, tag, tag_len, aad, aad_len))) {
6809 		RETVAL_STR(ret);
6810 	} else {
6811 		RETVAL_FALSE;
6812 	}
6813 }
6814 /* }}} */
6815 
php_openssl_decrypt(char * data,size_t data_len,char * method,size_t method_len,char * password,size_t password_len,zend_long options,char * iv,size_t iv_len,char * tag,zend_long tag_len,char * aad,size_t aad_len)6816 PHP_OPENSSL_API zend_string* php_openssl_decrypt(char *data, size_t data_len, char *method, size_t method_len, char *password, size_t password_len, zend_long options, char *iv, size_t iv_len, char *tag, zend_long tag_len, char *aad, size_t aad_len)
6817 {
6818 	const EVP_CIPHER *cipher_type;
6819 	EVP_CIPHER_CTX *cipher_ctx;
6820 	struct php_openssl_cipher_mode mode;
6821 	int i = 0, outlen;
6822 	zend_string *base64_str = NULL;
6823 	zend_bool free_iv = 0, free_password = 0;
6824 	zend_string *outbuf = NULL;
6825 
6826 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NORET(data_len, data);
6827 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NORET(password_len, password);
6828 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NORET(aad_len, aad);
6829 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NORET(tag_len, tag);
6830 
6831 
6832 	cipher_type = EVP_get_cipherbyname(method);
6833 	if (!cipher_type) {
6834 		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
6835 		return NULL;
6836 	}
6837 
6838 	cipher_ctx = EVP_CIPHER_CTX_new();
6839 	if (!cipher_ctx) {
6840 		php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
6841 		return NULL;
6842 	}
6843 
6844 	php_openssl_load_cipher_mode(&mode, cipher_type);
6845 
6846 	if (!(options & OPENSSL_RAW_DATA)) {
6847 		base64_str = php_base64_decode((unsigned char*)data, data_len);
6848 		if (!base64_str) {
6849 			php_error_docref(NULL, E_WARNING, "Failed to base64 decode the input");
6850 			EVP_CIPHER_CTX_free(cipher_ctx);
6851 			return NULL;
6852 		}
6853 		data_len = ZSTR_LEN(base64_str);
6854 		data = ZSTR_VAL(base64_str);
6855 	}
6856 
6857 	if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
6858 				&password, &password_len, &free_password,
6859 				&iv, &iv_len, &free_iv, tag, tag_len, options, 0) == FAILURE ||
6860 			php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
6861 				data, data_len, aad, aad_len, 0) == FAILURE) {
6862 		outbuf = NULL;
6863 	} else if (mode.is_single_run_aead ||
6864 			EVP_DecryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) {
6865 		outlen += i;
6866 		ZSTR_VAL(outbuf)[outlen] = '\0';
6867 		ZSTR_LEN(outbuf) = outlen;
6868 	} else {
6869 		php_openssl_store_errors();
6870 		zend_string_release_ex(outbuf, 0);
6871 		outbuf = NULL;
6872 	}
6873 
6874 	if (free_password) {
6875 		efree(password);
6876 	}
6877 	if (free_iv) {
6878 		efree(iv);
6879 	}
6880 	if (base64_str) {
6881 		zend_string_release_ex(base64_str, 0);
6882 	}
6883 	EVP_CIPHER_CTX_reset(cipher_ctx);
6884 	EVP_CIPHER_CTX_free(cipher_ctx);
6885 	return outbuf;
6886 }
6887 
6888 /* {{{ proto string openssl_decrypt(string data, string method, string password [, int options=0 [, string $iv = ''[, string $tag = ''[, string $aad = '']]]])
6889    Takes raw or base64 encoded string and decrypts it using given method and key */
PHP_FUNCTION(openssl_decrypt)6890 PHP_FUNCTION(openssl_decrypt)
6891 {
6892 	zend_long options = 0;
6893 	char *data, *method, *password, *iv = "", *tag = NULL, *aad = "";
6894 	size_t data_len, method_len, password_len, iv_len = 0, tag_len = 0, aad_len = 0;
6895 	zend_string *ret;
6896 
6897 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lsss", &data, &data_len, &method, &method_len,
6898 					&password, &password_len, &options, &iv, &iv_len, &tag, &tag_len, &aad, &aad_len) == FAILURE) {
6899 		return;
6900 	}
6901 
6902 	if (!method_len) {
6903 		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
6904 		RETURN_FALSE;
6905 	}
6906 
6907 	if ((ret = php_openssl_decrypt(data, data_len, method, method_len, password, password_len, options, iv, iv_len, tag, tag_len, aad, aad_len))) {
6908 		RETVAL_STR(ret);
6909 	} else {
6910 		RETVAL_FALSE;
6911 	}
6912 }
6913 /* }}} */
6914 
php_openssl_cipher_iv_length(char * method)6915 PHP_OPENSSL_API zend_long php_openssl_cipher_iv_length(char *method)
6916 {
6917 	const EVP_CIPHER *cipher_type;
6918 
6919 	cipher_type = EVP_get_cipherbyname(method);
6920 	if (!cipher_type) {
6921 		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
6922 		return -1;
6923 	}
6924 
6925 	return EVP_CIPHER_iv_length(cipher_type);
6926 }
6927 
6928 /* {{{ proto int openssl_cipher_iv_length(string $method) */
PHP_FUNCTION(openssl_cipher_iv_length)6929 PHP_FUNCTION(openssl_cipher_iv_length)
6930 {
6931 	char *method;
6932 	size_t method_len;
6933 	zend_long ret;
6934 
6935 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &method, &method_len) == FAILURE) {
6936 		return;
6937 	}
6938 
6939 	if (!method_len) {
6940 		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
6941 		RETURN_FALSE;
6942 	}
6943 
6944 	if ((ret = php_openssl_cipher_iv_length(method)) == -1) {
6945 		RETURN_FALSE;
6946 	}
6947 
6948 	RETURN_LONG(ret);
6949 }
6950 /* }}} */
6951 
6952 
php_openssl_random_pseudo_bytes(zend_long buffer_length)6953 PHP_OPENSSL_API zend_string* php_openssl_random_pseudo_bytes(zend_long buffer_length)
6954 {
6955 	zend_string *buffer = NULL;
6956 	if (buffer_length <= 0
6957 #ifndef PHP_WIN32
6958 		|| ZEND_LONG_INT_OVFL(buffer_length)
6959 #endif
6960 			) {
6961 		zend_throw_exception(zend_ce_error, "Length must be greater than 0", 0);
6962 		return NULL;
6963 	}
6964 	buffer = zend_string_alloc(buffer_length, 0);
6965 
6966 #ifdef PHP_WIN32
6967 	/* random/urandom equivalent on Windows */
6968 	if (php_win32_get_random_bytes((unsigned char*)(buffer)->val, (size_t) buffer_length) == FAILURE){
6969 		zend_string_release_ex(buffer, 0);
6970 		zend_throw_exception(zend_ce_exception, "Error reading from source device", 0);
6971 		return NULL;
6972 	}
6973 #else
6974 
6975 	PHP_OPENSSL_CHECK_LONG_TO_INT_NORET(buffer_length, length);
6976 	PHP_OPENSSL_RAND_ADD_TIME();
6977 	/* FIXME loop if requested size > INT_MAX */
6978 	if (RAND_bytes((unsigned char*)ZSTR_VAL(buffer), (int)buffer_length) <= 0) {
6979 		zend_string_release_ex(buffer, 0);
6980 		zend_throw_exception(zend_ce_exception, "Error reading from source device", 0);
6981 		return NULL;
6982 	} else {
6983 		php_openssl_store_errors();
6984 	}
6985 #endif
6986 	return buffer;
6987 }
6988 
6989 /* {{{ proto string openssl_random_pseudo_bytes(int length [, &bool returned_strong_result])
6990    Returns a string of the length specified filled with random pseudo bytes */
PHP_FUNCTION(openssl_random_pseudo_bytes)6991 PHP_FUNCTION(openssl_random_pseudo_bytes)
6992 {
6993 	zend_string *buffer = NULL;
6994 	zend_long buffer_length;
6995 	zval *zstrong_result_returned = NULL;
6996 
6997 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|z", &buffer_length, &zstrong_result_returned) == FAILURE) {
6998 		return;
6999 	}
7000 
7001 	if (zstrong_result_returned) {
7002 		ZEND_TRY_ASSIGN_REF_FALSE(zstrong_result_returned);
7003 	}
7004 
7005 	if ((buffer = php_openssl_random_pseudo_bytes(buffer_length))) {
7006 		ZSTR_VAL(buffer)[buffer_length] = 0;
7007 		RETVAL_NEW_STR(buffer);
7008 	}
7009 
7010 	if (zstrong_result_returned) {
7011 		ZEND_TRY_ASSIGN_REF_TRUE(zstrong_result_returned);
7012 	}
7013 }
7014 /* }}} */
7015