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