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