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