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