xref: /PHP-5.3/ext/openssl/openssl.c (revision 0fe07a0e)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 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    +----------------------------------------------------------------------+
21  */
22 
23 /* $Id$ */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include "php.h"
30 #include "php_openssl.h"
31 
32 /* PHP Includes */
33 #include "ext/standard/file.h"
34 #include "ext/standard/info.h"
35 #include "ext/standard/php_fopen_wrappers.h"
36 #include "ext/standard/md5.h"
37 #include "ext/standard/base64.h"
38 
39 /* OpenSSL includes */
40 #include <openssl/evp.h>
41 #include <openssl/x509.h>
42 #include <openssl/x509v3.h>
43 #include <openssl/crypto.h>
44 #include <openssl/pem.h>
45 #include <openssl/err.h>
46 #include <openssl/conf.h>
47 #include <openssl/rand.h>
48 #include <openssl/ssl.h>
49 #include <openssl/pkcs12.h>
50 
51 /* Common */
52 #include <time.h>
53 
54 #ifdef NETWARE
55 #define timezone _timezone	/* timezone is called _timezone in LibC */
56 #endif
57 
58 #define DEFAULT_KEY_LENGTH	512
59 #define MIN_KEY_LENGTH		384
60 
61 #define OPENSSL_ALGO_SHA1 	1
62 #define OPENSSL_ALGO_MD5	2
63 #define OPENSSL_ALGO_MD4	3
64 #ifdef HAVE_OPENSSL_MD2_H
65 #define OPENSSL_ALGO_MD2	4
66 #endif
67 #define OPENSSL_ALGO_DSS1	5
68 
69 #define DEBUG_SMIME	0
70 
71 /* FIXME: Use the openssl constants instead of
72  * enum. It is now impossible to match real values
73  * against php constants. Also sorry to break the
74  * enum principles here, BC...
75  */
76 enum php_openssl_key_type {
77 	OPENSSL_KEYTYPE_RSA,
78 	OPENSSL_KEYTYPE_DSA,
79 	OPENSSL_KEYTYPE_DH,
80 	OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA,
81 #ifdef EVP_PKEY_EC
82 	OPENSSL_KEYTYPE_EC = OPENSSL_KEYTYPE_DH +1
83 #endif
84 };
85 
86 enum php_openssl_cipher_type {
87 	PHP_OPENSSL_CIPHER_RC2_40,
88 	PHP_OPENSSL_CIPHER_RC2_128,
89 	PHP_OPENSSL_CIPHER_RC2_64,
90 	PHP_OPENSSL_CIPHER_DES,
91 	PHP_OPENSSL_CIPHER_3DES,
92 
93 	PHP_OPENSSL_CIPHER_DEFAULT = PHP_OPENSSL_CIPHER_RC2_40
94 };
95 
96 PHP_FUNCTION(openssl_get_md_methods);
97 PHP_FUNCTION(openssl_get_cipher_methods);
98 
99 PHP_FUNCTION(openssl_digest);
100 PHP_FUNCTION(openssl_encrypt);
101 PHP_FUNCTION(openssl_decrypt);
102 PHP_FUNCTION(openssl_cipher_iv_length);
103 
104 PHP_FUNCTION(openssl_dh_compute_key);
105 PHP_FUNCTION(openssl_random_pseudo_bytes);
106 
107 /* {{{ arginfo */
108 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 0, 2)
109     ZEND_ARG_INFO(0, x509)
110     ZEND_ARG_INFO(0, outfilename)
111     ZEND_ARG_INFO(0, notext)
112 ZEND_END_ARG_INFO()
113 
114 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_export, 0, 0, 2)
115     ZEND_ARG_INFO(0, x509)
116     ZEND_ARG_INFO(1, out)
117     ZEND_ARG_INFO(0, notext)
118 ZEND_END_ARG_INFO()
119 
120 ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_check_private_key, 0)
121     ZEND_ARG_INFO(0, cert)
122     ZEND_ARG_INFO(0, key)
123 ZEND_END_ARG_INFO()
124 
125 ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_parse, 0)
126     ZEND_ARG_INFO(0, x509)
127     ZEND_ARG_INFO(0, shortname)
128 ZEND_END_ARG_INFO()
129 
130 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_checkpurpose, 0, 0, 3)
131     ZEND_ARG_INFO(0, x509cert)
132     ZEND_ARG_INFO(0, purpose)
133     ZEND_ARG_INFO(0, cainfo) /* array */
134     ZEND_ARG_INFO(0, untrustedfile)
135 ZEND_END_ARG_INFO()
136 
137 ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_read, 0)
138     ZEND_ARG_INFO(0, cert)
139 ZEND_END_ARG_INFO()
140 
141 ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_free, 0)
142     ZEND_ARG_INFO(0, x509)
143 ZEND_END_ARG_INFO()
144 
145 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs12_export_to_file, 0, 0, 4)
146     ZEND_ARG_INFO(0, x509)
147     ZEND_ARG_INFO(0, filename)
148     ZEND_ARG_INFO(0, priv_key)
149     ZEND_ARG_INFO(0, pass)
150     ZEND_ARG_INFO(0, args) /* array */
151 ZEND_END_ARG_INFO()
152 
153 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkcs12_export, 0)
154     ZEND_ARG_INFO(0, x509)
155     ZEND_ARG_INFO(1, out)
156     ZEND_ARG_INFO(0, priv_key)
157     ZEND_ARG_INFO(0, pass)
158     ZEND_ARG_INFO(0, args) /* array */
159 ZEND_END_ARG_INFO()
160 
161 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkcs12_read, 0)
162     ZEND_ARG_INFO(0, PKCS12)
163     ZEND_ARG_INFO(1, certs) /* array */
164     ZEND_ARG_INFO(0, pass)
165 ZEND_END_ARG_INFO()
166 
167 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_export_to_file, 0, 0, 2)
168     ZEND_ARG_INFO(0, csr)
169     ZEND_ARG_INFO(0, outfilename)
170     ZEND_ARG_INFO(0, notext)
171 ZEND_END_ARG_INFO()
172 
173 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_export, 0, 0, 2)
174     ZEND_ARG_INFO(0, csr)
175     ZEND_ARG_INFO(1, out)
176     ZEND_ARG_INFO(0, notext)
177 ZEND_END_ARG_INFO()
178 
179 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_sign, 0, 0, 4)
180     ZEND_ARG_INFO(0, csr)
181     ZEND_ARG_INFO(0, x509)
182     ZEND_ARG_INFO(0, priv_key)
183     ZEND_ARG_INFO(0, days)
184     ZEND_ARG_INFO(0, config_args) /* array */
185     ZEND_ARG_INFO(0, serial)
186 ZEND_END_ARG_INFO()
187 
188 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_new, 0, 0, 2)
189     ZEND_ARG_INFO(0, dn) /* array */
190     ZEND_ARG_INFO(1, privkey)
191     ZEND_ARG_INFO(0, configargs)
192     ZEND_ARG_INFO(0, extraattribs)
193 ZEND_END_ARG_INFO()
194 
195 ZEND_BEGIN_ARG_INFO(arginfo_openssl_csr_get_subject, 0)
196     ZEND_ARG_INFO(0, csr)
197 ZEND_END_ARG_INFO()
198 
199 ZEND_BEGIN_ARG_INFO(arginfo_openssl_csr_get_public_key, 0)
200     ZEND_ARG_INFO(0, csr)
201 ZEND_END_ARG_INFO()
202 
203 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_new, 0, 0, 0)
204     ZEND_ARG_INFO(0, configargs) /* array */
205 ZEND_END_ARG_INFO()
206 
207 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_export_to_file, 0, 0, 2)
208     ZEND_ARG_INFO(0, key)
209     ZEND_ARG_INFO(0, outfilename)
210     ZEND_ARG_INFO(0, passphrase)
211     ZEND_ARG_INFO(0, config_args) /* array */
212 ZEND_END_ARG_INFO()
213 
214 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_export, 0, 0, 2)
215     ZEND_ARG_INFO(0, key)
216     ZEND_ARG_INFO(1, out)
217     ZEND_ARG_INFO(0, passphrase)
218     ZEND_ARG_INFO(0, config_args) /* array */
219 ZEND_END_ARG_INFO()
220 
221 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_public, 0)
222     ZEND_ARG_INFO(0, cert)
223 ZEND_END_ARG_INFO()
224 
225 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_free, 0)
226     ZEND_ARG_INFO(0, key)
227 ZEND_END_ARG_INFO()
228 
229 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_get_private, 0, 0, 1)
230     ZEND_ARG_INFO(0, key)
231     ZEND_ARG_INFO(0, passphrase)
232 ZEND_END_ARG_INFO()
233 
234 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_details, 0)
235     ZEND_ARG_INFO(0, key)
236 ZEND_END_ARG_INFO()
237 
238 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_verify, 0, 0, 2)
239     ZEND_ARG_INFO(0, filename)
240     ZEND_ARG_INFO(0, flags)
241     ZEND_ARG_INFO(0, signerscerts)
242     ZEND_ARG_INFO(0, cainfo) /* array */
243     ZEND_ARG_INFO(0, extracerts)
244     ZEND_ARG_INFO(0, content)
245 ZEND_END_ARG_INFO()
246 
247 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_encrypt, 0, 0, 4)
248     ZEND_ARG_INFO(0, infile)
249     ZEND_ARG_INFO(0, outfile)
250     ZEND_ARG_INFO(0, recipcerts)
251     ZEND_ARG_INFO(0, headers) /* array */
252     ZEND_ARG_INFO(0, flags)
253     ZEND_ARG_INFO(0, cipher)
254 ZEND_END_ARG_INFO()
255 
256 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_sign, 0, 0, 5)
257     ZEND_ARG_INFO(0, infile)
258     ZEND_ARG_INFO(0, outfile)
259     ZEND_ARG_INFO(0, signcert)
260     ZEND_ARG_INFO(0, signkey)
261     ZEND_ARG_INFO(0, headers) /* array */
262     ZEND_ARG_INFO(0, flags)
263     ZEND_ARG_INFO(0, extracertsfilename)
264 ZEND_END_ARG_INFO()
265 
266 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_decrypt, 0, 0, 3)
267     ZEND_ARG_INFO(0, infilename)
268     ZEND_ARG_INFO(0, outfilename)
269     ZEND_ARG_INFO(0, recipcert)
270     ZEND_ARG_INFO(0, recipkey)
271 ZEND_END_ARG_INFO()
272 
273 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_private_encrypt, 0, 0, 3)
274     ZEND_ARG_INFO(0, data)
275     ZEND_ARG_INFO(1, crypted)
276     ZEND_ARG_INFO(0, key)
277     ZEND_ARG_INFO(0, padding)
278 ZEND_END_ARG_INFO()
279 
280 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_private_decrypt, 0, 0, 3)
281     ZEND_ARG_INFO(0, data)
282     ZEND_ARG_INFO(1, crypted)
283     ZEND_ARG_INFO(0, key)
284     ZEND_ARG_INFO(0, padding)
285 ZEND_END_ARG_INFO()
286 
287 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_public_encrypt, 0, 0, 3)
288     ZEND_ARG_INFO(0, data)
289     ZEND_ARG_INFO(1, crypted)
290     ZEND_ARG_INFO(0, key)
291     ZEND_ARG_INFO(0, padding)
292 ZEND_END_ARG_INFO()
293 
294 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_public_decrypt, 0, 0, 3)
295     ZEND_ARG_INFO(0, data)
296     ZEND_ARG_INFO(1, crypted)
297     ZEND_ARG_INFO(0, key)
298     ZEND_ARG_INFO(0, padding)
299 ZEND_END_ARG_INFO()
300 
301 ZEND_BEGIN_ARG_INFO(arginfo_openssl_error_string, 0)
302 ZEND_END_ARG_INFO()
303 
304 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_sign, 0, 0, 3)
305     ZEND_ARG_INFO(0, data)
306     ZEND_ARG_INFO(1, signature)
307     ZEND_ARG_INFO(0, key)
308     ZEND_ARG_INFO(0, method)
309 ZEND_END_ARG_INFO()
310 
311 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_verify, 0, 0, 3)
312     ZEND_ARG_INFO(0, data)
313     ZEND_ARG_INFO(0, signature)
314     ZEND_ARG_INFO(0, key)
315     ZEND_ARG_INFO(0, method)
316 ZEND_END_ARG_INFO()
317 
318 ZEND_BEGIN_ARG_INFO(arginfo_openssl_seal, 0)
319     ZEND_ARG_INFO(0, data)
320     ZEND_ARG_INFO(1, sealdata)
321     ZEND_ARG_INFO(1, ekeys) /* arary */
322     ZEND_ARG_INFO(0, pubkeys) /* array */
323 ZEND_END_ARG_INFO()
324 
325 ZEND_BEGIN_ARG_INFO(arginfo_openssl_open, 0)
326     ZEND_ARG_INFO(0, data)
327     ZEND_ARG_INFO(1, opendata)
328     ZEND_ARG_INFO(0, ekey)
329     ZEND_ARG_INFO(0, privkey)
330 ZEND_END_ARG_INFO()
331 
332 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_md_methods, 0, 0, 0)
333     ZEND_ARG_INFO(0, aliases)
334 ZEND_END_ARG_INFO()
335 
336 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_cipher_methods, 0, 0, 0)
337     ZEND_ARG_INFO(0, aliases)
338 ZEND_END_ARG_INFO()
339 
340 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_digest, 0, 0, 2)
341     ZEND_ARG_INFO(0, data)
342     ZEND_ARG_INFO(0, method)
343     ZEND_ARG_INFO(0, raw_output)
344 ZEND_END_ARG_INFO()
345 
346 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_encrypt, 0, 0, 3)
347     ZEND_ARG_INFO(0, data)
348     ZEND_ARG_INFO(0, method)
349     ZEND_ARG_INFO(0, password)
350     ZEND_ARG_INFO(0, raw_output)
351     ZEND_ARG_INFO(0, iv)
352 ZEND_END_ARG_INFO()
353 
354 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_decrypt, 0, 0, 3)
355     ZEND_ARG_INFO(0, data)
356     ZEND_ARG_INFO(0, method)
357     ZEND_ARG_INFO(0, password)
358     ZEND_ARG_INFO(0, raw_input)
359     ZEND_ARG_INFO(0, iv)
360 ZEND_END_ARG_INFO()
361 
362 ZEND_BEGIN_ARG_INFO(arginfo_openssl_cipher_iv_length, 0)
363     ZEND_ARG_INFO(0, method)
364 ZEND_END_ARG_INFO()
365 
366 ZEND_BEGIN_ARG_INFO(arginfo_openssl_dh_compute_key, 0)
367     ZEND_ARG_INFO(0, pub_key)
368     ZEND_ARG_INFO(0, dh_key)
369 ZEND_END_ARG_INFO()
370 
371 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_random_pseudo_bytes, 0, 0, 1)
372     ZEND_ARG_INFO(0, length)
373     ZEND_ARG_INFO(1, result_is_strong)
374 ZEND_END_ARG_INFO()
375 /* }}} */
376 
377 /* {{{ openssl_functions[]
378  */
379 const zend_function_entry openssl_functions[] = {
380 /* public/private key functions */
381 	PHP_FE(openssl_pkey_free,			arginfo_openssl_pkey_free)
382 	PHP_FE(openssl_pkey_new,			arginfo_openssl_pkey_new)
383 	PHP_FE(openssl_pkey_export,			arginfo_openssl_pkey_export)
384 	PHP_FE(openssl_pkey_export_to_file,	arginfo_openssl_pkey_export_to_file)
385 	PHP_FE(openssl_pkey_get_private,	arginfo_openssl_pkey_get_private)
386 	PHP_FE(openssl_pkey_get_public,		arginfo_openssl_pkey_get_public)
387 	PHP_FE(openssl_pkey_get_details,	arginfo_openssl_pkey_get_details)
388 
389 	PHP_FALIAS(openssl_free_key,		openssl_pkey_free, 			arginfo_openssl_pkey_free)
390 	PHP_FALIAS(openssl_get_privatekey,	openssl_pkey_get_private,	arginfo_openssl_pkey_get_private)
391 	PHP_FALIAS(openssl_get_publickey,	openssl_pkey_get_public,	arginfo_openssl_pkey_get_public)
392 
393 /* x.509 cert funcs */
394 	PHP_FE(openssl_x509_read,				arginfo_openssl_x509_read)
395 	PHP_FE(openssl_x509_free,          		arginfo_openssl_x509_free)
396 	PHP_FE(openssl_x509_parse,			 	arginfo_openssl_x509_parse)
397 	PHP_FE(openssl_x509_checkpurpose,		arginfo_openssl_x509_checkpurpose)
398 	PHP_FE(openssl_x509_check_private_key,	arginfo_openssl_x509_check_private_key)
399 	PHP_FE(openssl_x509_export,				arginfo_openssl_x509_export)
400 	PHP_FE(openssl_x509_export_to_file,		arginfo_openssl_x509_export_to_file)
401 
402 /* PKCS12 funcs */
403 	PHP_FE(openssl_pkcs12_export,			arginfo_openssl_pkcs12_export)
404 	PHP_FE(openssl_pkcs12_export_to_file,	arginfo_openssl_pkcs12_export_to_file)
405 	PHP_FE(openssl_pkcs12_read,				arginfo_openssl_pkcs12_read)
406 
407 /* CSR funcs */
408 	PHP_FE(openssl_csr_new,				arginfo_openssl_csr_new)
409 	PHP_FE(openssl_csr_export,			arginfo_openssl_csr_export)
410 	PHP_FE(openssl_csr_export_to_file,	arginfo_openssl_csr_export_to_file)
411 	PHP_FE(openssl_csr_sign,			arginfo_openssl_csr_sign)
412 	PHP_FE(openssl_csr_get_subject,		arginfo_openssl_csr_get_subject)
413 	PHP_FE(openssl_csr_get_public_key,	arginfo_openssl_csr_get_public_key)
414 
415 	PHP_FE(openssl_digest,				arginfo_openssl_digest)
416 	PHP_FE(openssl_encrypt,				arginfo_openssl_encrypt)
417 	PHP_FE(openssl_decrypt,				arginfo_openssl_decrypt)
418 	PHP_FE(openssl_cipher_iv_length,	arginfo_openssl_cipher_iv_length)
419 	PHP_FE(openssl_sign,				arginfo_openssl_sign)
420 	PHP_FE(openssl_verify,				arginfo_openssl_verify)
421 	PHP_FE(openssl_seal,				arginfo_openssl_seal)
422 	PHP_FE(openssl_open,				arginfo_openssl_open)
423 
424 /* for S/MIME handling */
425 	PHP_FE(openssl_pkcs7_verify,		arginfo_openssl_pkcs7_verify)
426 	PHP_FE(openssl_pkcs7_decrypt,		arginfo_openssl_pkcs7_decrypt)
427 	PHP_FE(openssl_pkcs7_sign,			arginfo_openssl_pkcs7_sign)
428 	PHP_FE(openssl_pkcs7_encrypt,		arginfo_openssl_pkcs7_encrypt)
429 
430 	PHP_FE(openssl_private_encrypt,		arginfo_openssl_private_encrypt)
431 	PHP_FE(openssl_private_decrypt,		arginfo_openssl_private_decrypt)
432 	PHP_FE(openssl_public_encrypt,		arginfo_openssl_public_encrypt)
433 	PHP_FE(openssl_public_decrypt,		arginfo_openssl_public_decrypt)
434 
435 	PHP_FE(openssl_get_md_methods,		arginfo_openssl_get_md_methods)
436 	PHP_FE(openssl_get_cipher_methods,	arginfo_openssl_get_cipher_methods)
437 
438 	PHP_FE(openssl_dh_compute_key,      arginfo_openssl_dh_compute_key)
439 
440 	PHP_FE(openssl_random_pseudo_bytes,    arginfo_openssl_random_pseudo_bytes)
441 	PHP_FE(openssl_error_string, arginfo_openssl_error_string)
442 	PHP_FE_END
443 };
444 /* }}} */
445 
446 /* {{{ openssl_module_entry
447  */
448 zend_module_entry openssl_module_entry = {
449 	STANDARD_MODULE_HEADER,
450 	"openssl",
451 	openssl_functions,
452 	PHP_MINIT(openssl),
453 	PHP_MSHUTDOWN(openssl),
454 	NULL,
455 	NULL,
456 	PHP_MINFO(openssl),
457 	NO_VERSION_YET,
458 	STANDARD_MODULE_PROPERTIES
459 };
460 /* }}} */
461 
462 #ifdef COMPILE_DL_OPENSSL
463 ZEND_GET_MODULE(openssl)
464 #endif
465 
466 static int le_key;
467 static int le_x509;
468 static int le_csr;
469 static int ssl_stream_data_index;
470 
php_openssl_get_x509_list_id(void)471 int php_openssl_get_x509_list_id(void) /* {{{ */
472 {
473 	return le_x509;
474 }
475 /* }}} */
476 
477 /* {{{ resource destructors */
php_pkey_free(zend_rsrc_list_entry * rsrc TSRMLS_DC)478 static void php_pkey_free(zend_rsrc_list_entry *rsrc TSRMLS_DC)
479 {
480 	EVP_PKEY *pkey = (EVP_PKEY *)rsrc->ptr;
481 
482 	assert(pkey != NULL);
483 
484 	EVP_PKEY_free(pkey);
485 }
486 
php_x509_free(zend_rsrc_list_entry * rsrc TSRMLS_DC)487 static void php_x509_free(zend_rsrc_list_entry *rsrc TSRMLS_DC)
488 {
489 	X509 *x509 = (X509 *)rsrc->ptr;
490 	X509_free(x509);
491 }
492 
php_csr_free(zend_rsrc_list_entry * rsrc TSRMLS_DC)493 static void php_csr_free(zend_rsrc_list_entry *rsrc TSRMLS_DC)
494 {
495 	X509_REQ * csr = (X509_REQ*)rsrc->ptr;
496 	X509_REQ_free(csr);
497 }
498 /* }}} */
499 
500 /* {{{ openssl safe_mode & open_basedir checks */
php_openssl_safe_mode_chk(char * filename TSRMLS_DC)501 inline static int php_openssl_safe_mode_chk(char *filename TSRMLS_DC)
502 {
503 	if (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
504 		return -1;
505 	}
506 	if (php_check_open_basedir(filename TSRMLS_CC)) {
507 		return -1;
508 	}
509 
510 	return 0;
511 }
512 /* }}} */
513 
514 /* openssl -> PHP "bridging" */
515 /* true global; readonly after module startup */
516 static char default_ssl_conf_filename[MAXPATHLEN];
517 
518 struct php_x509_request { /* {{{ */
519 #if OPENSSL_VERSION_NUMBER >= 0x10000002L
520 	LHASH_OF(CONF_VALUE) * global_config;	/* Global SSL config */
521 	LHASH_OF(CONF_VALUE) * req_config;		/* SSL config for this request */
522 #else
523 	LHASH * global_config;  /* Global SSL config */
524 	LHASH * req_config;             /* SSL config for this request */
525 #endif
526 	const EVP_MD * md_alg;
527 	const EVP_MD * digest;
528 	char	* section_name,
529 			* config_filename,
530 			* digest_name,
531 			* extensions_section,
532 			* request_extensions_section;
533 	int priv_key_bits;
534 	int priv_key_type;
535 
536 	int priv_key_encrypt;
537 
538 	EVP_PKEY * priv_key;
539 };
540 /* }}} */
541 
542 static X509 * php_openssl_x509_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC);
543 static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC);
544 static int php_openssl_is_private_key(EVP_PKEY* pkey TSRMLS_DC);
545 static X509_STORE     * setup_verify(zval * calist TSRMLS_DC);
546 static STACK_OF(X509) * load_all_certs_from_file(char *certfile);
547 static X509_REQ * php_openssl_csr_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC);
548 static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req TSRMLS_DC);
549 
add_assoc_name_entry(zval * val,char * key,X509_NAME * name,int shortname TSRMLS_DC)550 static void add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname TSRMLS_DC) /* {{{ */
551 {
552 	zval *subitem, *subentries;
553 	int i, j = -1, last = -1, obj_cnt = 0;
554 	char *sname;
555 	int nid;
556 	X509_NAME_ENTRY * ne;
557 	ASN1_STRING * str = NULL;
558 	ASN1_OBJECT * obj;
559 
560 	if (key != NULL) {
561 		MAKE_STD_ZVAL(subitem);
562 		array_init(subitem);
563 	} else {
564 		subitem = val;
565 	}
566 
567 	for (i = 0; i < X509_NAME_entry_count(name); i++) {
568 		unsigned char *to_add;
569 		int to_add_len;
570 
571 
572 		ne  = X509_NAME_get_entry(name, i);
573 		obj = X509_NAME_ENTRY_get_object(ne);
574 		nid = OBJ_obj2nid(obj);
575 		obj_cnt = 0;
576 
577 		if (shortname) {
578 			sname = (char *) OBJ_nid2sn(nid);
579 		} else {
580 			sname = (char *) OBJ_nid2ln(nid);
581 		}
582 
583 		MAKE_STD_ZVAL(subentries);
584 		array_init(subentries);
585 
586 		last = -1;
587 		for (;;) {
588 			j = X509_NAME_get_index_by_OBJ(name, obj, last);
589 			if (j < 0) {
590 				if (last != -1) break;
591 			} else {
592 				obj_cnt++;
593 				ne  = X509_NAME_get_entry(name, j);
594 				str = X509_NAME_ENTRY_get_data(ne);
595 				if (ASN1_STRING_type(str) != V_ASN1_UTF8STRING) {
596 					to_add_len = ASN1_STRING_to_UTF8(&to_add, str);
597 					if (to_add_len != -1) {
598 						add_next_index_stringl(subentries, (char *)to_add, to_add_len, 1);
599 					}
600 				} else {
601 					to_add = ASN1_STRING_data(str);
602 					to_add_len = ASN1_STRING_length(str);
603 					add_next_index_stringl(subentries, (char *)to_add, to_add_len, 1);
604 				}
605 			}
606 			last = j;
607 		}
608 		i = last;
609 
610 		if (obj_cnt > 1) {
611 			add_assoc_zval_ex(subitem, sname, strlen(sname) + 1, subentries);
612 		} else {
613 			zval_dtor(subentries);
614 			FREE_ZVAL(subentries);
615 			if (obj_cnt && str && to_add_len > -1) {
616 				add_assoc_stringl(subitem, sname, (char *)to_add, to_add_len, 1);
617 			}
618 		}
619 	}
620 	if (key != NULL) {
621 		zend_hash_update(HASH_OF(val), key, strlen(key) + 1, (void *)&subitem, sizeof(subitem), NULL);
622 	}
623 }
624 /* }}} */
625 
add_assoc_asn1_string(zval * val,char * key,ASN1_STRING * str)626 static void add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str) /* {{{ */
627 {
628 	add_assoc_stringl(val, key, (char *)str->data, str->length, 1);
629 }
630 /* }}} */
631 
asn1_time_to_time_t(ASN1_UTCTIME * timestr TSRMLS_DC)632 static time_t asn1_time_to_time_t(ASN1_UTCTIME * timestr TSRMLS_DC) /* {{{ */
633 {
634 /*
635 	This is how the time string is formatted:
636 
637    snprintf(p, sizeof(p), "%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100,
638       ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
639 */
640 
641 	time_t ret;
642 	struct tm thetime;
643 	char * strbuf;
644 	char * thestr;
645 	long gmadjust = 0;
646 
647 	if (ASN1_STRING_type(timestr) != V_ASN1_UTCTIME) {
648 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "illegal ASN1 data type for timestamp");
649 		return (time_t)-1;
650 	}
651 
652 	if (ASN1_STRING_length(timestr) != strlen((char*)ASN1_STRING_data(timestr))) {
653 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "illegal length in timestamp");
654 		return (time_t)-1;
655 	}
656 
657 	if (ASN1_STRING_length(timestr) < 13) {
658 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to parse time string %s correctly", timestr->data);
659 		return (time_t)-1;
660 	}
661 
662 	strbuf = estrdup((char *)ASN1_STRING_data(timestr));
663 
664 	memset(&thetime, 0, sizeof(thetime));
665 
666 	/* we work backwards so that we can use atoi more easily */
667 
668 	thestr = strbuf + ASN1_STRING_length(timestr) - 3;
669 
670 	thetime.tm_sec = atoi(thestr);
671 	*thestr = '\0';
672 	thestr -= 2;
673 	thetime.tm_min = atoi(thestr);
674 	*thestr = '\0';
675 	thestr -= 2;
676 	thetime.tm_hour = atoi(thestr);
677 	*thestr = '\0';
678 	thestr -= 2;
679 	thetime.tm_mday = atoi(thestr);
680 	*thestr = '\0';
681 	thestr -= 2;
682 	thetime.tm_mon = atoi(thestr)-1;
683 	*thestr = '\0';
684 	thestr -= 2;
685 	thetime.tm_year = atoi(thestr);
686 
687 	if (thetime.tm_year < 68) {
688 		thetime.tm_year += 100;
689 	}
690 
691 	thetime.tm_isdst = -1;
692 	ret = mktime(&thetime);
693 
694 #if HAVE_TM_GMTOFF
695 	gmadjust = thetime.tm_gmtoff;
696 #else
697 	/*
698 	** If correcting for daylight savings time, we set the adjustment to
699 	** the value of timezone - 3600 seconds. Otherwise, we need to overcorrect and
700 	** set the adjustment to the main timezone + 3600 seconds.
701 	*/
702 	gmadjust = -(thetime.tm_isdst ? (long)timezone - 3600 : (long)timezone + 3600);
703 #endif
704 	ret += gmadjust;
705 
706 	efree(strbuf);
707 
708 	return ret;
709 }
710 /* }}} */
711 
712 #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 TSRMLS_DC)713 static inline int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, LHASH_OF(CONF_VALUE) * config TSRMLS_DC) /* {{{ */
714 #else
715 static inline int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, LHASH * config TSRMLS_DC)
716 #endif
717 {
718 	X509V3_CTX ctx;
719 
720 	X509V3_set_ctx_test(&ctx);
721 	X509V3_set_conf_lhash(&ctx, config);
722 	if (!X509V3_EXT_add_conf(config, &ctx, (char *)section, NULL)) {
723 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error loading %s section %s of %s",
724 				section_label,
725 				section,
726 				config_filename);
727 		return FAILURE;
728 	}
729 	return SUCCESS;
730 }
731 /* }}} */
732 
add_oid_section(struct php_x509_request * req TSRMLS_DC)733 static int add_oid_section(struct php_x509_request * req TSRMLS_DC) /* {{{ */
734 {
735 	char * str;
736 	STACK_OF(CONF_VALUE) * sktmp;
737 	CONF_VALUE * cnf;
738 	int i;
739 
740 	str = CONF_get_string(req->req_config, NULL, "oid_section");
741 	if (str == NULL) {
742 		return SUCCESS;
743 	}
744 	sktmp = CONF_get_section(req->req_config, str);
745 	if (sktmp == NULL) {
746 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "problem loading oid section %s", str);
747 		return FAILURE;
748 	}
749 	for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++) {
750 		cnf = sk_CONF_VALUE_value(sktmp, i);
751 		if (OBJ_create(cnf->value, cnf->name, cnf->name) == NID_undef) {
752 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "problem creating object %s=%s", cnf->name, cnf->value);
753 			return FAILURE;
754 		}
755 	}
756 	return SUCCESS;
757 }
758 /* }}} */
759 
760 #define PHP_SSL_REQ_INIT(req)		memset(req, 0, sizeof(*req))
761 #define PHP_SSL_REQ_DISPOSE(req)	php_openssl_dispose_config(req TSRMLS_CC)
762 #define PHP_SSL_REQ_PARSE(req, zval)	php_openssl_parse_config(req, zval TSRMLS_CC)
763 
764 #define PHP_SSL_CONFIG_SYNTAX_CHECK(var) if (req->var && php_openssl_config_check_syntax(#var, \
765 			req->config_filename, req->var, req->req_config TSRMLS_CC) == FAILURE) return FAILURE
766 
767 #define SET_OPTIONAL_STRING_ARG(key, varname, defval)	\
768 	if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), key, sizeof(key), (void**)&item) == SUCCESS && Z_TYPE_PP(item) == IS_STRING) \
769 		varname = Z_STRVAL_PP(item); \
770 	else \
771 		varname = defval
772 
773 #define SET_OPTIONAL_LONG_ARG(key, varname, defval)	\
774 	if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), key, sizeof(key), (void**)&item) == SUCCESS && Z_TYPE_PP(item) == IS_LONG) \
775 		varname = Z_LVAL_PP(item); \
776 	else \
777 		varname = defval
778 
php_openssl_parse_config(struct php_x509_request * req,zval * optional_args TSRMLS_DC)779 static int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args TSRMLS_DC) /* {{{ */
780 {
781 	char * str;
782 	zval ** item;
783 
784 	SET_OPTIONAL_STRING_ARG("config", req->config_filename, default_ssl_conf_filename);
785 	SET_OPTIONAL_STRING_ARG("config_section_name", req->section_name, "req");
786 	req->global_config = CONF_load(NULL, default_ssl_conf_filename, NULL);
787 	req->req_config = CONF_load(NULL, req->config_filename, NULL);
788 
789 	if (req->req_config == NULL) {
790 		return FAILURE;
791 	}
792 
793 	/* read in the oids */
794 	str = CONF_get_string(req->req_config, NULL, "oid_file");
795 	if (str && !php_openssl_safe_mode_chk(str TSRMLS_CC)) {
796 		BIO *oid_bio = BIO_new_file(str, "r");
797 		if (oid_bio) {
798 			OBJ_create_objects(oid_bio);
799 			BIO_free(oid_bio);
800 		}
801 	}
802 	if (add_oid_section(req TSRMLS_CC) == FAILURE) {
803 		return FAILURE;
804 	}
805 	SET_OPTIONAL_STRING_ARG("digest_alg", req->digest_name,
806 		CONF_get_string(req->req_config, req->section_name, "default_md"));
807 	SET_OPTIONAL_STRING_ARG("x509_extensions", req->extensions_section,
808 		CONF_get_string(req->req_config, req->section_name, "x509_extensions"));
809 	SET_OPTIONAL_STRING_ARG("req_extensions", req->request_extensions_section,
810 		CONF_get_string(req->req_config, req->section_name, "req_extensions"));
811 	SET_OPTIONAL_LONG_ARG("private_key_bits", req->priv_key_bits,
812 		CONF_get_number(req->req_config, req->section_name, "default_bits"));
813 
814 	SET_OPTIONAL_LONG_ARG("private_key_type", req->priv_key_type, OPENSSL_KEYTYPE_DEFAULT);
815 
816 	if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), "encrypt_key", sizeof("encrypt_key"), (void**)&item) == SUCCESS && Z_TYPE_PP(item) == IS_BOOL) {
817 		req->priv_key_encrypt = Z_BVAL_PP(item);
818 	} else {
819 		str = CONF_get_string(req->req_config, req->section_name, "encrypt_rsa_key");
820 		if (str == NULL) {
821 			str = CONF_get_string(req->req_config, req->section_name, "encrypt_key");
822 		}
823 		if (str && strcmp(str, "no") == 0) {
824 			req->priv_key_encrypt = 0;
825 		} else {
826 			req->priv_key_encrypt = 1;
827 		}
828 	}
829 
830 	/* digest alg */
831 	if (req->digest_name == NULL) {
832 		req->digest_name = CONF_get_string(req->req_config, req->section_name, "default_md");
833 	}
834 	if (req->digest_name) {
835 		req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name);
836 	}
837 	if (req->md_alg == NULL) {
838 		req->md_alg = req->digest = EVP_md5();
839 	}
840 
841 	PHP_SSL_CONFIG_SYNTAX_CHECK(extensions_section);
842 
843 	/* set the string mask */
844 	str = CONF_get_string(req->req_config, req->section_name, "string_mask");
845 	if (str && !ASN1_STRING_set_default_mask_asc(str)) {
846 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid global string mask setting %s", str);
847 		return FAILURE;
848 	}
849 
850 	PHP_SSL_CONFIG_SYNTAX_CHECK(request_extensions_section);
851 
852 	return SUCCESS;
853 }
854 /* }}} */
855 
php_openssl_dispose_config(struct php_x509_request * req TSRMLS_DC)856 static void php_openssl_dispose_config(struct php_x509_request * req TSRMLS_DC) /* {{{ */
857 {
858 	if (req->priv_key) {
859 		EVP_PKEY_free(req->priv_key);
860 		req->priv_key = NULL;
861 	}
862 	if (req->global_config) {
863 		CONF_free(req->global_config);
864 		req->global_config = NULL;
865 	}
866 	if (req->req_config) {
867 		CONF_free(req->req_config);
868 		req->req_config = NULL;
869 	}
870 }
871 /* }}} */
872 
php_openssl_load_rand_file(const char * file,int * egdsocket,int * seeded)873 static int php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded) /* {{{ */
874 {
875 	char buffer[MAXPATHLEN];
876 
877 	TSRMLS_FETCH();
878 
879 	*egdsocket = 0;
880 	*seeded = 0;
881 
882 	if (file == NULL) {
883 		file = RAND_file_name(buffer, sizeof(buffer));
884 	} else if (RAND_egd(file) > 0) {
885 		/* if the given filename is an EGD socket, don't
886 		 * write anything back to it */
887 		*egdsocket = 1;
888 		return SUCCESS;
889 	}
890 	if (file == NULL || !RAND_load_file(file, -1)) {
891 		if (RAND_status() == 0) {
892 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to load random state; not enough random data!");
893 			return FAILURE;
894 		}
895 		return FAILURE;
896 	}
897 	*seeded = 1;
898 	return SUCCESS;
899 }
900 /* }}} */
901 
php_openssl_write_rand_file(const char * file,int egdsocket,int seeded)902 static int php_openssl_write_rand_file(const char * file, int egdsocket, int seeded) /* {{{ */
903 {
904 	char buffer[MAXPATHLEN];
905 
906 	TSRMLS_FETCH();
907 
908 	if (egdsocket || !seeded) {
909 		/* if we did not manage to read the seed file, we should not write
910 		 * a low-entropy seed file back */
911 		return FAILURE;
912 	}
913 	if (file == NULL) {
914 		file = RAND_file_name(buffer, sizeof(buffer));
915 	}
916 	if (file == NULL || !RAND_write_file(file)) {
917 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to write random state");
918 		return FAILURE;
919 	}
920 	return SUCCESS;
921 }
922 /* }}} */
923 
php_openssl_get_evp_md_from_algo(long algo)924 static EVP_MD * php_openssl_get_evp_md_from_algo(long algo) { /* {{{ */
925 	EVP_MD *mdtype;
926 
927 	switch (algo) {
928 		case OPENSSL_ALGO_SHA1:
929 			mdtype = (EVP_MD *) EVP_sha1();
930 			break;
931 		case OPENSSL_ALGO_MD5:
932 			mdtype = (EVP_MD *) EVP_md5();
933 			break;
934 		case OPENSSL_ALGO_MD4:
935 			mdtype = (EVP_MD *) EVP_md4();
936 			break;
937 #ifdef HAVE_OPENSSL_MD2_H
938 		case OPENSSL_ALGO_MD2:
939 			mdtype = (EVP_MD *) EVP_md2();
940 			break;
941 #endif
942 		case OPENSSL_ALGO_DSS1:
943 			mdtype = (EVP_MD *) EVP_dss1();
944 			break;
945 		default:
946 			return NULL;
947 			break;
948 	}
949 	return mdtype;
950 }
951 /* }}} */
952 
php_openssl_get_evp_cipher_from_algo(long algo)953 static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(long algo) { /* {{{ */
954 	switch (algo) {
955 #ifndef OPENSSL_NO_RC2
956 		case PHP_OPENSSL_CIPHER_RC2_40:
957 			return EVP_rc2_40_cbc();
958 			break;
959 		case PHP_OPENSSL_CIPHER_RC2_64:
960 			return EVP_rc2_64_cbc();
961 			break;
962 		case PHP_OPENSSL_CIPHER_RC2_128:
963 			return EVP_rc2_cbc();
964 			break;
965 #endif
966 
967 #ifndef OPENSSL_NO_DES
968 		case PHP_OPENSSL_CIPHER_DES:
969 			return EVP_des_cbc();
970 			break;
971 		case PHP_OPENSSL_CIPHER_3DES:
972 			return EVP_des_ede3_cbc();
973 			break;
974 #endif
975 		default:
976 			return NULL;
977 			break;
978 	}
979 }
980 /* }}} */
981 
982 /* {{{ PHP_MINIT_FUNCTION
983  */
PHP_MINIT_FUNCTION(openssl)984 PHP_MINIT_FUNCTION(openssl)
985 {
986 	char * config_filename;
987 
988 	le_key = zend_register_list_destructors_ex(php_pkey_free, NULL, "OpenSSL key", module_number);
989 	le_x509 = zend_register_list_destructors_ex(php_x509_free, NULL, "OpenSSL X.509", module_number);
990 	le_csr = zend_register_list_destructors_ex(php_csr_free, NULL, "OpenSSL X.509 CSR", module_number);
991 
992 	SSL_library_init();
993 	OpenSSL_add_all_ciphers();
994 	OpenSSL_add_all_digests();
995 	OpenSSL_add_all_algorithms();
996 
997 	ERR_load_ERR_strings();
998 	ERR_load_crypto_strings();
999 	ERR_load_EVP_strings();
1000 
1001 	/* register a resource id number with OpenSSL so that we can map SSL -> stream structures in
1002 	 * OpenSSL callbacks */
1003 	ssl_stream_data_index = SSL_get_ex_new_index(0, "PHP stream index", NULL, NULL, NULL);
1004 
1005 	REGISTER_STRING_CONSTANT("OPENSSL_VERSION_TEXT", OPENSSL_VERSION_TEXT, CONST_CS|CONST_PERSISTENT);
1006 	REGISTER_LONG_CONSTANT("OPENSSL_VERSION_NUMBER", OPENSSL_VERSION_NUMBER, CONST_CS|CONST_PERSISTENT);
1007 
1008 	/* purposes for cert purpose checking */
1009 	REGISTER_LONG_CONSTANT("X509_PURPOSE_SSL_CLIENT", X509_PURPOSE_SSL_CLIENT, CONST_CS|CONST_PERSISTENT);
1010 	REGISTER_LONG_CONSTANT("X509_PURPOSE_SSL_SERVER", X509_PURPOSE_SSL_SERVER, CONST_CS|CONST_PERSISTENT);
1011 	REGISTER_LONG_CONSTANT("X509_PURPOSE_NS_SSL_SERVER", X509_PURPOSE_NS_SSL_SERVER, CONST_CS|CONST_PERSISTENT);
1012 	REGISTER_LONG_CONSTANT("X509_PURPOSE_SMIME_SIGN", X509_PURPOSE_SMIME_SIGN, CONST_CS|CONST_PERSISTENT);
1013 	REGISTER_LONG_CONSTANT("X509_PURPOSE_SMIME_ENCRYPT", X509_PURPOSE_SMIME_ENCRYPT, CONST_CS|CONST_PERSISTENT);
1014 	REGISTER_LONG_CONSTANT("X509_PURPOSE_CRL_SIGN", X509_PURPOSE_CRL_SIGN, CONST_CS|CONST_PERSISTENT);
1015 #ifdef X509_PURPOSE_ANY
1016 	REGISTER_LONG_CONSTANT("X509_PURPOSE_ANY", X509_PURPOSE_ANY, CONST_CS|CONST_PERSISTENT);
1017 #endif
1018 
1019 	/* signature algorithm constants */
1020 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA1", OPENSSL_ALGO_SHA1, CONST_CS|CONST_PERSISTENT);
1021 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD5", OPENSSL_ALGO_MD5, CONST_CS|CONST_PERSISTENT);
1022 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD4", OPENSSL_ALGO_MD4, CONST_CS|CONST_PERSISTENT);
1023 #ifdef HAVE_OPENSSL_MD2_H
1024 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD2", OPENSSL_ALGO_MD2, CONST_CS|CONST_PERSISTENT);
1025 #endif
1026 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_DSS1", OPENSSL_ALGO_DSS1, CONST_CS|CONST_PERSISTENT);
1027 
1028 	/* flags for S/MIME */
1029 	REGISTER_LONG_CONSTANT("PKCS7_DETACHED", PKCS7_DETACHED, CONST_CS|CONST_PERSISTENT);
1030 	REGISTER_LONG_CONSTANT("PKCS7_TEXT", PKCS7_TEXT, CONST_CS|CONST_PERSISTENT);
1031 	REGISTER_LONG_CONSTANT("PKCS7_NOINTERN", PKCS7_NOINTERN, CONST_CS|CONST_PERSISTENT);
1032 	REGISTER_LONG_CONSTANT("PKCS7_NOVERIFY", PKCS7_NOVERIFY, CONST_CS|CONST_PERSISTENT);
1033 	REGISTER_LONG_CONSTANT("PKCS7_NOCHAIN", PKCS7_NOCHAIN, CONST_CS|CONST_PERSISTENT);
1034 	REGISTER_LONG_CONSTANT("PKCS7_NOCERTS", PKCS7_NOCERTS, CONST_CS|CONST_PERSISTENT);
1035 	REGISTER_LONG_CONSTANT("PKCS7_NOATTR", PKCS7_NOATTR, CONST_CS|CONST_PERSISTENT);
1036 	REGISTER_LONG_CONSTANT("PKCS7_BINARY", PKCS7_BINARY, CONST_CS|CONST_PERSISTENT);
1037 	REGISTER_LONG_CONSTANT("PKCS7_NOSIGS", PKCS7_NOSIGS, CONST_CS|CONST_PERSISTENT);
1038 
1039 	REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_PADDING", RSA_PKCS1_PADDING, CONST_CS|CONST_PERSISTENT);
1040 	REGISTER_LONG_CONSTANT("OPENSSL_SSLV23_PADDING", RSA_SSLV23_PADDING, CONST_CS|CONST_PERSISTENT);
1041 	REGISTER_LONG_CONSTANT("OPENSSL_NO_PADDING", RSA_NO_PADDING, CONST_CS|CONST_PERSISTENT);
1042 	REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_OAEP_PADDING", RSA_PKCS1_OAEP_PADDING, CONST_CS|CONST_PERSISTENT);
1043 
1044 	/* Ciphers */
1045 #ifndef OPENSSL_NO_RC2
1046 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_40", PHP_OPENSSL_CIPHER_RC2_40, CONST_CS|CONST_PERSISTENT);
1047 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_128", PHP_OPENSSL_CIPHER_RC2_128, CONST_CS|CONST_PERSISTENT);
1048 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_64", PHP_OPENSSL_CIPHER_RC2_64, CONST_CS|CONST_PERSISTENT);
1049 #endif
1050 #ifndef OPENSSL_NO_DES
1051 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_DES", PHP_OPENSSL_CIPHER_DES, CONST_CS|CONST_PERSISTENT);
1052 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_3DES", PHP_OPENSSL_CIPHER_3DES, CONST_CS|CONST_PERSISTENT);
1053 #endif
1054 
1055 	/* Values for key types */
1056 	REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_RSA", OPENSSL_KEYTYPE_RSA, CONST_CS|CONST_PERSISTENT);
1057 #ifndef NO_DSA
1058 	REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DSA", OPENSSL_KEYTYPE_DSA, CONST_CS|CONST_PERSISTENT);
1059 #endif
1060 	REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DH", OPENSSL_KEYTYPE_DH, CONST_CS|CONST_PERSISTENT);
1061 #ifdef EVP_PKEY_EC
1062 	REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_EC", OPENSSL_KEYTYPE_EC, CONST_CS|CONST_PERSISTENT);
1063 #endif
1064 
1065 #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
1066 	/* SNI support included in OpenSSL >= 0.9.8j */
1067 	REGISTER_LONG_CONSTANT("OPENSSL_TLSEXT_SERVER_NAME", 1, CONST_CS|CONST_PERSISTENT);
1068 #endif
1069 
1070 	/* Determine default SSL configuration file */
1071 	config_filename = getenv("OPENSSL_CONF");
1072 	if (config_filename == NULL) {
1073 		config_filename = getenv("SSLEAY_CONF");
1074 	}
1075 
1076 	/* default to 'openssl.cnf' if no environment variable is set */
1077 	if (config_filename == NULL) {
1078 		snprintf(default_ssl_conf_filename, sizeof(default_ssl_conf_filename), "%s/%s",
1079 				X509_get_default_cert_area(),
1080 				"openssl.cnf");
1081 	} else {
1082 		strlcpy(default_ssl_conf_filename, config_filename, sizeof(default_ssl_conf_filename));
1083 	}
1084 
1085 	php_stream_xport_register("ssl", php_openssl_ssl_socket_factory TSRMLS_CC);
1086 	php_stream_xport_register("sslv3", php_openssl_ssl_socket_factory TSRMLS_CC);
1087 #ifndef OPENSSL_NO_SSL2
1088 	php_stream_xport_register("sslv2", php_openssl_ssl_socket_factory TSRMLS_CC);
1089 #endif
1090 	php_stream_xport_register("tls", php_openssl_ssl_socket_factory TSRMLS_CC);
1091 
1092 	/* override the default tcp socket provider */
1093 	php_stream_xport_register("tcp", php_openssl_ssl_socket_factory TSRMLS_CC);
1094 
1095 	php_register_url_stream_wrapper("https", &php_stream_http_wrapper TSRMLS_CC);
1096 	php_register_url_stream_wrapper("ftps", &php_stream_ftp_wrapper TSRMLS_CC);
1097 
1098 	return SUCCESS;
1099 }
1100 /* }}} */
1101 
1102 /* {{{ PHP_MINFO_FUNCTION
1103  */
PHP_MINFO_FUNCTION(openssl)1104 PHP_MINFO_FUNCTION(openssl)
1105 {
1106 	php_info_print_table_start();
1107 	php_info_print_table_row(2, "OpenSSL support", "enabled");
1108 	php_info_print_table_row(2, "OpenSSL Library Version", SSLeay_version(SSLEAY_VERSION));
1109 	php_info_print_table_row(2, "OpenSSL Header Version", OPENSSL_VERSION_TEXT);
1110 	php_info_print_table_end();
1111 }
1112 /* }}} */
1113 
1114 /* {{{ PHP_MSHUTDOWN_FUNCTION
1115  */
PHP_MSHUTDOWN_FUNCTION(openssl)1116 PHP_MSHUTDOWN_FUNCTION(openssl)
1117 {
1118 	EVP_cleanup();
1119 
1120 	php_unregister_url_stream_wrapper("https" TSRMLS_CC);
1121 	php_unregister_url_stream_wrapper("ftps" TSRMLS_CC);
1122 
1123 	php_stream_xport_unregister("ssl" TSRMLS_CC);
1124 #ifndef OPENSSL_NO_SSL2
1125 	php_stream_xport_unregister("sslv2" TSRMLS_CC);
1126 #endif
1127 	php_stream_xport_unregister("sslv3" TSRMLS_CC);
1128 	php_stream_xport_unregister("tls" TSRMLS_CC);
1129 
1130 	/* reinstate the default tcp handler */
1131 	php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC);
1132 
1133 	return SUCCESS;
1134 }
1135 /* }}} */
1136 
1137 /* {{{ x509 cert functions */
1138 
1139 /* {{{ php_openssl_x509_from_zval
1140 	Given a zval, coerce it into an X509 object.
1141 	The zval can be:
1142 		. X509 resource created using openssl_read_x509()
1143 		. if it starts with file:// then it will be interpreted as the path to that cert
1144 		. it will be interpreted as the cert data
1145 	If you supply makeresource, the result will be registered as an x509 resource and
1146 	it's value returned in makeresource.
1147 */
php_openssl_x509_from_zval(zval ** val,int makeresource,long * resourceval TSRMLS_DC)1148 static X509 * php_openssl_x509_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC)
1149 {
1150 	X509 *cert = NULL;
1151 
1152 	if (resourceval) {
1153 		*resourceval = -1;
1154 	}
1155 	if (Z_TYPE_PP(val) == IS_RESOURCE) {
1156 		/* is it an x509 resource ? */
1157 		void * what;
1158 		int type;
1159 
1160 		what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509", &type, 1, le_x509);
1161 		if (!what) {
1162 			return NULL;
1163 		}
1164 		/* this is so callers can decide if they should free the X509 */
1165 		if (resourceval) {
1166 			*resourceval = Z_LVAL_PP(val);
1167 		}
1168 		if (type == le_x509) {
1169 			return (X509*)what;
1170 		}
1171 		/* other types could be used here - eg: file pointers and read in the data from them */
1172 
1173 		return NULL;
1174 	}
1175 
1176 	if (!(Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_OBJECT)) {
1177 		return NULL;
1178 	}
1179 
1180 	/* force it to be a string and check if it refers to a file */
1181 	convert_to_string_ex(val);
1182 
1183 	if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) {
1184 		/* read cert from the named file */
1185 		BIO *in;
1186 
1187 		if (php_openssl_safe_mode_chk(Z_STRVAL_PP(val) + (sizeof("file://") - 1) TSRMLS_CC)) {
1188 			return NULL;
1189 		}
1190 
1191 		in = BIO_new_file(Z_STRVAL_PP(val) + (sizeof("file://") - 1), "r");
1192 		if (in == NULL) {
1193 			return NULL;
1194 		}
1195 		cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1196 		BIO_free(in);
1197 	} else {
1198 		BIO *in;
1199 
1200 		in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
1201 		if (in == NULL) {
1202 			return NULL;
1203 		}
1204 #ifdef TYPEDEF_D2I_OF
1205 		cert = (X509 *) PEM_ASN1_read_bio((d2i_of_void *)d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
1206 #else
1207 		cert = (X509 *) PEM_ASN1_read_bio((char *(*)())d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
1208 #endif
1209 		BIO_free(in);
1210 	}
1211 
1212 	if (cert && makeresource && resourceval) {
1213 		*resourceval = zend_list_insert(cert, le_x509);
1214 	}
1215 	return cert;
1216 }
1217 
1218 /* }}} */
1219 
1220 /* {{{ proto bool openssl_x509_export_to_file(mixed x509, string outfilename [, bool notext = true])
1221    Exports a CERT to file or a var */
PHP_FUNCTION(openssl_x509_export_to_file)1222 PHP_FUNCTION(openssl_x509_export_to_file)
1223 {
1224 	X509 * cert;
1225 	zval ** zcert;
1226 	zend_bool notext = 1;
1227 	BIO * bio_out;
1228 	long certresource;
1229 	char * filename;
1230 	int filename_len;
1231 
1232 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zs|b", &zcert, &filename, &filename_len, &notext) == FAILURE) {
1233 		return;
1234 	}
1235 	RETVAL_FALSE;
1236 
1237 	cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
1238 	if (cert == NULL) {
1239 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1");
1240 		return;
1241 	}
1242 
1243 	if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
1244 		return;
1245 	}
1246 
1247 	bio_out = BIO_new_file(filename, "w");
1248 	if (bio_out) {
1249 		if (!notext) {
1250 			X509_print(bio_out, cert);
1251 		}
1252 		PEM_write_bio_X509(bio_out, cert);
1253 
1254 		RETVAL_TRUE;
1255 	} else {
1256 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename);
1257 	}
1258 	if (certresource == -1 && cert) {
1259 		X509_free(cert);
1260 	}
1261 	BIO_free(bio_out);
1262 }
1263 /* }}} */
1264 
1265 /* {{{ proto bool openssl_x509_export(mixed x509, string &out [, bool notext = true])
1266    Exports a CERT to file or a var */
PHP_FUNCTION(openssl_x509_export)1267 PHP_FUNCTION(openssl_x509_export)
1268 {
1269 	X509 * cert;
1270 	zval ** zcert, *zout;
1271 	zend_bool notext = 1;
1272 	BIO * bio_out;
1273 	long certresource;
1274 
1275 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zz|b", &zcert, &zout, &notext) == FAILURE) {
1276 		return;
1277 	}
1278 	RETVAL_FALSE;
1279 
1280 	cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
1281 	if (cert == NULL) {
1282 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1");
1283 		return;
1284 	}
1285 
1286 	bio_out = BIO_new(BIO_s_mem());
1287 	if (!notext) {
1288 		X509_print(bio_out, cert);
1289 	}
1290 	if (PEM_write_bio_X509(bio_out, cert))  {
1291 		BUF_MEM *bio_buf;
1292 
1293 		zval_dtor(zout);
1294 		BIO_get_mem_ptr(bio_out, &bio_buf);
1295 		ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1);
1296 
1297 		RETVAL_TRUE;
1298 	}
1299 
1300 	if (certresource == -1 && cert) {
1301 		X509_free(cert);
1302 	}
1303 	BIO_free(bio_out);
1304 }
1305 /* }}} */
1306 
1307 /* {{{ proto bool openssl_x509_check_private_key(mixed cert, mixed key)
1308    Checks if a private key corresponds to a CERT */
PHP_FUNCTION(openssl_x509_check_private_key)1309 PHP_FUNCTION(openssl_x509_check_private_key)
1310 {
1311 	zval ** zcert, **zkey;
1312 	X509 * cert = NULL;
1313 	EVP_PKEY * key = NULL;
1314 	long certresource = -1, keyresource = -1;
1315 
1316 	RETVAL_FALSE;
1317 
1318 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ", &zcert, &zkey) == FAILURE) {
1319 		return;
1320 	}
1321 	cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
1322 	if (cert == NULL) {
1323 		RETURN_FALSE;
1324 	}
1325 	key = php_openssl_evp_from_zval(zkey, 0, "", 1, &keyresource TSRMLS_CC);
1326 	if (key) {
1327 		RETVAL_BOOL(X509_check_private_key(cert, key));
1328 	}
1329 
1330 	if (keyresource == -1 && key) {
1331 		EVP_PKEY_free(key);
1332 	}
1333 	if (certresource == -1 && cert) {
1334 		X509_free(cert);
1335 	}
1336 }
1337 /* }}} */
1338 
1339 /* Special handling of subjectAltName, see CVE-2013-4073
1340  * Christian Heimes
1341  */
1342 
openssl_x509v3_subjectAltName(BIO * bio,X509_EXTENSION * extension)1343 static int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)
1344 {
1345 	GENERAL_NAMES *names;
1346 	const X509V3_EXT_METHOD *method = NULL;
1347 	long i, length, num;
1348 	const unsigned char *p;
1349 
1350 	method = X509V3_EXT_get(extension);
1351 	if (method == NULL) {
1352 		return -1;
1353 	}
1354 
1355 	p = extension->value->data;
1356 	length = extension->value->length;
1357 	if (method->it) {
1358 		names = (GENERAL_NAMES*)(ASN1_item_d2i(NULL, &p, length,
1359 						       ASN1_ITEM_ptr(method->it)));
1360 	} else {
1361 		names = (GENERAL_NAMES*)(method->d2i(NULL, &p, length));
1362 	}
1363 	if (names == NULL) {
1364 		return -1;
1365 	}
1366 
1367 	num = sk_GENERAL_NAME_num(names);
1368 	for (i = 0; i < num; i++) {
1369 			GENERAL_NAME *name;
1370 			ASN1_STRING *as;
1371 			name = sk_GENERAL_NAME_value(names, i);
1372 			switch (name->type) {
1373 				case GEN_EMAIL:
1374 					BIO_puts(bio, "email:");
1375 					as = name->d.rfc822Name;
1376 					BIO_write(bio, ASN1_STRING_data(as),
1377 						  ASN1_STRING_length(as));
1378 					break;
1379 				case GEN_DNS:
1380 					BIO_puts(bio, "DNS:");
1381 					as = name->d.dNSName;
1382 					BIO_write(bio, ASN1_STRING_data(as),
1383 						  ASN1_STRING_length(as));
1384 					break;
1385 				case GEN_URI:
1386 					BIO_puts(bio, "URI:");
1387 					as = name->d.uniformResourceIdentifier;
1388 					BIO_write(bio, ASN1_STRING_data(as),
1389 						  ASN1_STRING_length(as));
1390 					break;
1391 				default:
1392 					/* use builtin print for GEN_OTHERNAME, GEN_X400,
1393 					 * GEN_EDIPARTY, GEN_DIRNAME, GEN_IPADD and GEN_RID
1394 					 */
1395 					GENERAL_NAME_print(bio, name);
1396 			}
1397 			/* trailing ', ' except for last element */
1398 			if (i < (num - 1)) {
1399 				BIO_puts(bio, ", ");
1400 			}
1401 	}
1402 	sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
1403 
1404 	return 0;
1405 }
1406 
1407 /* {{{ proto array openssl_x509_parse(mixed x509 [, bool shortnames=true])
1408    Returns an array of the fields/values of the CERT */
PHP_FUNCTION(openssl_x509_parse)1409 PHP_FUNCTION(openssl_x509_parse)
1410 {
1411 	zval ** zcert;
1412 	X509 * cert = NULL;
1413 	long certresource = -1;
1414 	int i;
1415 	zend_bool useshortnames = 1;
1416 	char * tmpstr;
1417 	zval * subitem;
1418 	X509_EXTENSION *extension;
1419 	char *extname;
1420 	BIO  *bio_out;
1421 	BUF_MEM *bio_buf;
1422 	char buf[256];
1423 
1424 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcert, &useshortnames) == FAILURE) {
1425 		return;
1426 	}
1427 	cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
1428 	if (cert == NULL) {
1429 		RETURN_FALSE;
1430 	}
1431 	array_init(return_value);
1432 
1433 	if (cert->name) {
1434 		add_assoc_string(return_value, "name", cert->name, 1);
1435 	}
1436 /*	add_assoc_bool(return_value, "valid", cert->valid); */
1437 
1438 	add_assoc_name_entry(return_value, "subject", 		X509_get_subject_name(cert), useshortnames TSRMLS_CC);
1439 	/* hash as used in CA directories to lookup cert by subject name */
1440 	{
1441 		char buf[32];
1442 		snprintf(buf, sizeof(buf), "%08lx", X509_subject_name_hash(cert));
1443 		add_assoc_string(return_value, "hash", buf, 1);
1444 	}
1445 
1446 	add_assoc_name_entry(return_value, "issuer", 		X509_get_issuer_name(cert), useshortnames TSRMLS_CC);
1447 	add_assoc_long(return_value, "version", 			X509_get_version(cert));
1448 
1449 	add_assoc_string(return_value, "serialNumber", i2s_ASN1_INTEGER(NULL, X509_get_serialNumber(cert)), 1);
1450 
1451 	add_assoc_asn1_string(return_value, "validFrom", 	X509_get_notBefore(cert));
1452 	add_assoc_asn1_string(return_value, "validTo", 		X509_get_notAfter(cert));
1453 
1454 	add_assoc_long(return_value, "validFrom_time_t", 	asn1_time_to_time_t(X509_get_notBefore(cert) TSRMLS_CC));
1455 	add_assoc_long(return_value, "validTo_time_t", 		asn1_time_to_time_t(X509_get_notAfter(cert) TSRMLS_CC));
1456 
1457 	tmpstr = (char *)X509_alias_get0(cert, NULL);
1458 	if (tmpstr) {
1459 		add_assoc_string(return_value, "alias", tmpstr, 1);
1460 	}
1461 /*
1462 	add_assoc_long(return_value, "signaturetypeLONG", X509_get_signature_type(cert));
1463 	add_assoc_string(return_value, "signaturetype", OBJ_nid2sn(X509_get_signature_type(cert)), 1);
1464 	add_assoc_string(return_value, "signaturetypeLN", OBJ_nid2ln(X509_get_signature_type(cert)), 1);
1465 */
1466 	MAKE_STD_ZVAL(subitem);
1467 	array_init(subitem);
1468 
1469 	/* NOTE: the purposes are added as integer keys - the keys match up to the X509_PURPOSE_SSL_XXX defines
1470 	   in x509v3.h */
1471 	for (i = 0; i < X509_PURPOSE_get_count(); i++) {
1472 		int id, purpset;
1473 		char * pname;
1474 		X509_PURPOSE * purp;
1475 		zval * subsub;
1476 
1477 		MAKE_STD_ZVAL(subsub);
1478 		array_init(subsub);
1479 
1480 		purp = X509_PURPOSE_get0(i);
1481 		id = X509_PURPOSE_get_id(purp);
1482 
1483 		purpset = X509_check_purpose(cert, id, 0);
1484 		add_index_bool(subsub, 0, purpset);
1485 
1486 		purpset = X509_check_purpose(cert, id, 1);
1487 		add_index_bool(subsub, 1, purpset);
1488 
1489 		pname = useshortnames ? X509_PURPOSE_get0_sname(purp) : X509_PURPOSE_get0_name(purp);
1490 		add_index_string(subsub, 2, pname, 1);
1491 
1492 		/* NOTE: if purpset > 1 then it's a warning - we should mention it ? */
1493 
1494 		add_index_zval(subitem, id, subsub);
1495 	}
1496 	add_assoc_zval(return_value, "purposes", subitem);
1497 
1498 	MAKE_STD_ZVAL(subitem);
1499 	array_init(subitem);
1500 
1501 
1502 	for (i = 0; i < X509_get_ext_count(cert); i++) {
1503 		int nid;
1504 		extension = X509_get_ext(cert, i);
1505 		nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
1506 		if (nid != NID_undef) {
1507 			extname = (char *)OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(extension)));
1508 		} else {
1509 			OBJ_obj2txt(buf, sizeof(buf)-1, X509_EXTENSION_get_object(extension), 1);
1510 			extname = buf;
1511 		}
1512 		bio_out = BIO_new(BIO_s_mem());
1513 		if (nid == NID_subject_alt_name) {
1514 			if (openssl_x509v3_subjectAltName(bio_out, extension) == 0) {
1515 				BIO_get_mem_ptr(bio_out, &bio_buf);
1516 				add_assoc_stringl(subitem, extname, bio_buf->data, bio_buf->length, 1);
1517 			} else {
1518 				zval_dtor(return_value);
1519 				if (certresource == -1 && cert) {
1520 					X509_free(cert);
1521 				}
1522 				BIO_free(bio_out);
1523 				RETURN_FALSE;
1524 			}
1525 		}
1526 		else if (X509V3_EXT_print(bio_out, extension, 0, 0)) {
1527 			BIO_get_mem_ptr(bio_out, &bio_buf);
1528 			add_assoc_stringl(subitem, extname, bio_buf->data, bio_buf->length, 1);
1529 		} else {
1530 			add_assoc_asn1_string(subitem, extname, X509_EXTENSION_get_data(extension));
1531 		}
1532 		BIO_free(bio_out);
1533 	}
1534 	add_assoc_zval(return_value, "extensions", subitem);
1535 
1536 	if (certresource == -1 && cert) {
1537 		X509_free(cert);
1538 	}
1539 }
1540 /* }}} */
1541 
1542 /* {{{ load_all_certs_from_file */
STACK_OF(X509)1543 static STACK_OF(X509) * load_all_certs_from_file(char *certfile)
1544 {
1545 	STACK_OF(X509_INFO) *sk=NULL;
1546 	STACK_OF(X509) *stack=NULL, *ret=NULL;
1547 	BIO *in=NULL;
1548 	X509_INFO *xi;
1549 	TSRMLS_FETCH();
1550 
1551 	if(!(stack = sk_X509_new_null())) {
1552 		php_error_docref(NULL TSRMLS_CC, E_ERROR, "memory allocation failure");
1553 		goto end;
1554 	}
1555 
1556 	if (php_openssl_safe_mode_chk(certfile TSRMLS_CC)) {
1557 		sk_X509_free(stack);
1558 		goto end;
1559 	}
1560 
1561 	if(!(in=BIO_new_file(certfile, "r"))) {
1562 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening the file, %s", certfile);
1563 		sk_X509_free(stack);
1564 		goto end;
1565 	}
1566 
1567 	/* This loads from a file, a stack of x509/crl/pkey sets */
1568 	if(!(sk=PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) {
1569 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "error reading the file, %s", certfile);
1570 		sk_X509_free(stack);
1571 		goto end;
1572 	}
1573 
1574 	/* scan over it and pull out the certs */
1575 	while (sk_X509_INFO_num(sk)) {
1576 		xi=sk_X509_INFO_shift(sk);
1577 		if (xi->x509 != NULL) {
1578 			sk_X509_push(stack,xi->x509);
1579 			xi->x509=NULL;
1580 		}
1581 		X509_INFO_free(xi);
1582 	}
1583 	if(!sk_X509_num(stack)) {
1584 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "no certificates in file, %s", certfile);
1585 		sk_X509_free(stack);
1586 		goto end;
1587 	}
1588 	ret=stack;
1589 end:
1590 	BIO_free(in);
1591 	sk_X509_INFO_free(sk);
1592 
1593 	return ret;
1594 }
1595 /* }}} */
1596 
1597 /* {{{ check_cert */
check_cert(X509_STORE * ctx,X509 * x,STACK_OF (X509)* untrustedchain,int purpose)1598 static int check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain, int purpose)
1599 {
1600 	int ret=0;
1601 	X509_STORE_CTX *csc;
1602 	TSRMLS_FETCH();
1603 
1604 	csc = X509_STORE_CTX_new();
1605 	if (csc == NULL) {
1606 		php_error_docref(NULL TSRMLS_CC, E_ERROR, "memory allocation failure");
1607 		return 0;
1608 	}
1609 	X509_STORE_CTX_init(csc, ctx, x, untrustedchain);
1610 	if(purpose >= 0) {
1611 		X509_STORE_CTX_set_purpose(csc, purpose);
1612 	}
1613 	ret = X509_verify_cert(csc);
1614 	X509_STORE_CTX_free(csc);
1615 
1616 	return ret;
1617 }
1618 /* }}} */
1619 
1620 /* {{{ proto int openssl_x509_checkpurpose(mixed x509cert, int purpose, array cainfo [, string untrustedfile])
1621    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)1622 PHP_FUNCTION(openssl_x509_checkpurpose)
1623 {
1624 	zval ** zcert, * zcainfo = NULL;
1625 	X509_STORE * cainfo = NULL;
1626 	X509 * cert = NULL;
1627 	long certresource = -1;
1628 	STACK_OF(X509) * untrustedchain = NULL;
1629 	long purpose;
1630 	char * untrusted = NULL;
1631 	int untrusted_len = 0, ret;
1632 
1633 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zl|a!s", &zcert, &purpose, &zcainfo, &untrusted, &untrusted_len) == FAILURE) {
1634 		return;
1635 	}
1636 
1637 	RETVAL_LONG(-1);
1638 
1639 	if (untrusted) {
1640 		untrustedchain = load_all_certs_from_file(untrusted);
1641 		if (untrustedchain == NULL) {
1642 			goto clean_exit;
1643 		}
1644 	}
1645 
1646 	cainfo = setup_verify(zcainfo TSRMLS_CC);
1647 	if (cainfo == NULL) {
1648 		goto clean_exit;
1649 	}
1650 	cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
1651 	if (cert == NULL) {
1652 		goto clean_exit;
1653 	}
1654 
1655 	ret = check_cert(cainfo, cert, untrustedchain, purpose);
1656 	if (ret != 0 && ret != 1) {
1657 		RETVAL_LONG(ret);
1658 	} else {
1659 		RETVAL_BOOL(ret);
1660 	}
1661 
1662 clean_exit:
1663 	if (certresource == 1 && cert) {
1664 		X509_free(cert);
1665 	}
1666 	if (cainfo) {
1667 		X509_STORE_free(cainfo);
1668 	}
1669 	if (untrustedchain) {
1670 		sk_X509_pop_free(untrustedchain, X509_free);
1671 	}
1672 }
1673 /* }}} */
1674 
1675 /* {{{ setup_verify
1676  * calist is an array containing file and directory names.  create a
1677  * certificate store and add those certs to it for use in verification.
1678 */
setup_verify(zval * calist TSRMLS_DC)1679 static X509_STORE * setup_verify(zval * calist TSRMLS_DC)
1680 {
1681 	X509_STORE *store;
1682 	X509_LOOKUP * dir_lookup, * file_lookup;
1683 	HashPosition pos;
1684 	int ndirs = 0, nfiles = 0;
1685 
1686 	store = X509_STORE_new();
1687 
1688 	if (store == NULL) {
1689 		return NULL;
1690 	}
1691 
1692 	if (calist && (Z_TYPE_P(calist) == IS_ARRAY)) {
1693 		zend_hash_internal_pointer_reset_ex(HASH_OF(calist), &pos);
1694 		for (;; zend_hash_move_forward_ex(HASH_OF(calist), &pos)) {
1695 			zval ** item;
1696 			struct stat sb;
1697 
1698 			if (zend_hash_get_current_data_ex(HASH_OF(calist), (void**)&item, &pos) == FAILURE) {
1699 				break;
1700 			}
1701 			convert_to_string_ex(item);
1702 
1703 			if (VCWD_STAT(Z_STRVAL_PP(item), &sb) == -1) {
1704 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to stat %s", Z_STRVAL_PP(item));
1705 				continue;
1706 			}
1707 
1708 			if ((sb.st_mode & S_IFREG) == S_IFREG) {
1709 				file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
1710 				if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM)) {
1711 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "error loading file %s", Z_STRVAL_PP(item));
1712 				} else {
1713 					nfiles++;
1714 				}
1715 				file_lookup = NULL;
1716 			} else {
1717 				dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
1718 				if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM)) {
1719 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "error loading directory %s", Z_STRVAL_PP(item));
1720 				} else {
1721 					ndirs++;
1722 				}
1723 				dir_lookup = NULL;
1724 			}
1725 		}
1726 	}
1727 	if (nfiles == 0) {
1728 		file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
1729 		if (file_lookup) {
1730 			X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT);
1731 		}
1732 	}
1733 	if (ndirs == 0) {
1734 		dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
1735 		if (dir_lookup) {
1736 			X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT);
1737 		}
1738 	}
1739 	return store;
1740 }
1741 /* }}} */
1742 
1743 /* {{{ proto resource openssl_x509_read(mixed cert)
1744    Reads X.509 certificates */
PHP_FUNCTION(openssl_x509_read)1745 PHP_FUNCTION(openssl_x509_read)
1746 {
1747 	zval **cert;
1748 	X509 *x509;
1749 
1750 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &cert) == FAILURE) {
1751 		return;
1752 	}
1753 	Z_TYPE_P(return_value) = IS_RESOURCE;
1754 	x509 = php_openssl_x509_from_zval(cert, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
1755 
1756 	if (x509 == NULL) {
1757 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied parameter cannot be coerced into an X509 certificate!");
1758 		RETURN_FALSE;
1759 	}
1760 }
1761 /* }}} */
1762 
1763 /* {{{ proto void openssl_x509_free(resource x509)
1764    Frees X.509 certificates */
PHP_FUNCTION(openssl_x509_free)1765 PHP_FUNCTION(openssl_x509_free)
1766 {
1767 	zval *x509;
1768 	X509 *cert;
1769 
1770 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &x509) == FAILURE) {
1771 		return;
1772 	}
1773 	ZEND_FETCH_RESOURCE(cert, X509 *, &x509, -1, "OpenSSL X.509", le_x509);
1774 	zend_list_delete(Z_LVAL_P(x509));
1775 }
1776 /* }}} */
1777 
1778 /* }}} */
1779 
1780 /* Pop all X509 from Stack and free them, free the stack afterwards */
php_sk_X509_free(STACK_OF (X509)* sk)1781 static void php_sk_X509_free(STACK_OF(X509) * sk) /* {{{ */
1782 {
1783 	for (;;) {
1784 		X509* x = sk_X509_pop(sk);
1785 		if (!x) break;
1786 		X509_free(x);
1787 	}
1788 	sk_X509_free(sk);
1789 }
1790 /* }}} */
1791 
STACK_OF(X509)1792 static STACK_OF(X509) * php_array_to_X509_sk(zval ** zcerts TSRMLS_DC) /* {{{ */
1793 {
1794 	HashPosition hpos;
1795 	zval ** zcertval;
1796 	STACK_OF(X509) * sk = NULL;
1797     X509 * cert;
1798     long certresource;
1799 
1800 	sk = sk_X509_new_null();
1801 
1802 	/* get certs */
1803 	if (Z_TYPE_PP(zcerts) == IS_ARRAY) {
1804 		zend_hash_internal_pointer_reset_ex(HASH_OF(*zcerts), &hpos);
1805 		while(zend_hash_get_current_data_ex(HASH_OF(*zcerts), (void**)&zcertval, &hpos) == SUCCESS) {
1806 
1807 			cert = php_openssl_x509_from_zval(zcertval, 0, &certresource TSRMLS_CC);
1808 			if (cert == NULL) {
1809 				goto clean_exit;
1810 			}
1811 
1812 			if (certresource != -1) {
1813 				cert = X509_dup(cert);
1814 
1815 				if (cert == NULL) {
1816 					goto clean_exit;
1817 				}
1818 
1819 			}
1820 			sk_X509_push(sk, cert);
1821 
1822 			zend_hash_move_forward_ex(HASH_OF(*zcerts), &hpos);
1823 		}
1824 	} else {
1825 		/* a single certificate */
1826 		cert = php_openssl_x509_from_zval(zcerts, 0, &certresource TSRMLS_CC);
1827 
1828 		if (cert == NULL) {
1829 			goto clean_exit;
1830 		}
1831 
1832 		if (certresource != -1) {
1833 			cert = X509_dup(cert);
1834 			if (cert == NULL) {
1835 				goto clean_exit;
1836 			}
1837 		}
1838 		sk_X509_push(sk, cert);
1839 	}
1840 
1841   clean_exit:
1842     return sk;
1843 }
1844 /* }}} */
1845 
1846 /* {{{ proto bool openssl_pkcs12_export_to_file(mixed x509, string filename, mixed priv_key, string pass[, array args])
1847    Creates and exports a PKCS to file */
PHP_FUNCTION(openssl_pkcs12_export_to_file)1848 PHP_FUNCTION(openssl_pkcs12_export_to_file)
1849 {
1850 	X509 * cert = NULL;
1851 	BIO * bio_out = NULL;
1852 	PKCS12 * p12 = NULL;
1853 	char * filename;
1854 	char * friendly_name = NULL;
1855 	int filename_len;
1856 	char * pass;
1857 	int pass_len;
1858 	zval **zcert = NULL, *zpkey = NULL, *args = NULL;
1859 	EVP_PKEY *priv_key = NULL;
1860 	long certresource, keyresource;
1861 	zval ** item;
1862 	STACK_OF(X509) *ca = NULL;
1863 
1864 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zszs|a", &zcert, &filename, &filename_len, &zpkey, &pass, &pass_len, &args) == FAILURE)
1865 		return;
1866 
1867 	RETVAL_FALSE;
1868 
1869 	if (strlen(filename) != filename_len) {
1870 		return;
1871 	}
1872 
1873 	cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
1874 	if (cert == NULL) {
1875 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1");
1876 		return;
1877 	}
1878 	priv_key = php_openssl_evp_from_zval(&zpkey, 0, "", 1, &keyresource TSRMLS_CC);
1879 	if (priv_key == NULL) {
1880 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3");
1881 		goto cleanup;
1882 	}
1883 	if (cert && !X509_check_private_key(cert, priv_key)) {
1884 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to cert");
1885 		goto cleanup;
1886 	}
1887 	if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
1888 		goto cleanup;
1889 	}
1890 
1891 	/* parse extra config from args array, promote this to an extra function */
1892 	if (args && zend_hash_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name"), (void**)&item) == SUCCESS && Z_TYPE_PP(item) == IS_STRING)
1893 		friendly_name = Z_STRVAL_PP(item);
1894 	/* certpbe (default RC2-40)
1895 	   keypbe (default 3DES)
1896 	   friendly_caname
1897 	*/
1898 
1899 	if (args && zend_hash_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts"), (void**)&item) == SUCCESS)
1900 		ca = php_array_to_X509_sk(item TSRMLS_CC);
1901 	/* end parse extra config */
1902 
1903 	/*PKCS12 *PKCS12_create(char *pass, char *name, EVP_PKEY *pkey, X509 *cert, STACK_OF(X509) *ca,
1904                                        int nid_key, int nid_cert, int iter, int mac_iter, int keytype);*/
1905 
1906 	p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
1907 
1908 	bio_out = BIO_new_file(filename, "w");
1909 	if (bio_out) {
1910 
1911 		i2d_PKCS12_bio(bio_out, p12);
1912 
1913 		RETVAL_TRUE;
1914 	} else {
1915 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename);
1916 	}
1917 
1918 	BIO_free(bio_out);
1919 	PKCS12_free(p12);
1920 	php_sk_X509_free(ca);
1921 
1922 cleanup:
1923 
1924 	if (keyresource == -1 && priv_key) {
1925 		EVP_PKEY_free(priv_key);
1926 	}
1927 	if (certresource == -1 && cert) {
1928 		X509_free(cert);
1929 	}
1930 }
1931 /* }}} */
1932 
1933 /* {{{ proto bool openssl_pkcs12_export(mixed x509, string &out, mixed priv_key, string pass[, array args])
1934    Creates and exports a PKCS12 to a var */
PHP_FUNCTION(openssl_pkcs12_export)1935 PHP_FUNCTION(openssl_pkcs12_export)
1936 {
1937 	X509 * cert = NULL;
1938 	BIO * bio_out;
1939 	PKCS12 * p12 = NULL;
1940 	zval * zcert = NULL, *zout = NULL, *zpkey, *args = NULL;
1941 	EVP_PKEY *priv_key = NULL;
1942 	long certresource, keyresource;
1943 	char * pass;
1944 	int pass_len;
1945 	char * friendly_name = NULL;
1946 	zval ** item;
1947 	STACK_OF(X509) *ca = NULL;
1948 
1949 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zzzs|a", &zcert, &zout, &zpkey, &pass, &pass_len, &args) == FAILURE)
1950 		return;
1951 
1952 	RETVAL_FALSE;
1953 
1954 	cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC);
1955 	if (cert == NULL) {
1956 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1");
1957 		return;
1958 	}
1959 	priv_key = php_openssl_evp_from_zval(&zpkey, 0, "", 1, &keyresource TSRMLS_CC);
1960 	if (priv_key == NULL) {
1961 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3");
1962 		goto cleanup;
1963 	}
1964 	if (cert && !X509_check_private_key(cert, priv_key)) {
1965 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to cert");
1966 		goto cleanup;
1967 	}
1968 
1969 	/* parse extra config from args array, promote this to an extra function */
1970 	if (args && zend_hash_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name"), (void**)&item) == SUCCESS && Z_TYPE_PP(item) == IS_STRING)
1971 		friendly_name = Z_STRVAL_PP(item);
1972 
1973 	if (args && zend_hash_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts"), (void**)&item) == SUCCESS)
1974 		ca = php_array_to_X509_sk(item TSRMLS_CC);
1975 	/* end parse extra config */
1976 
1977 	p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
1978 
1979 	bio_out = BIO_new(BIO_s_mem());
1980 	if (i2d_PKCS12_bio(bio_out, p12))  {
1981 		BUF_MEM *bio_buf;
1982 
1983 		zval_dtor(zout);
1984 		BIO_get_mem_ptr(bio_out, &bio_buf);
1985 		ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1);
1986 
1987 		RETVAL_TRUE;
1988 	}
1989 
1990 	BIO_free(bio_out);
1991 	PKCS12_free(p12);
1992 	php_sk_X509_free(ca);
1993 
1994 cleanup:
1995 
1996 	if (keyresource == -1 && priv_key) {
1997 		EVP_PKEY_free(priv_key);
1998 	}
1999 	if (certresource == -1 && cert) {
2000 		X509_free(cert);
2001 	}
2002 }
2003 /* }}} */
2004 
2005 /* {{{ proto bool openssl_pkcs12_read(string PKCS12, array &certs, string pass)
2006    Parses a PKCS12 to an array */
PHP_FUNCTION(openssl_pkcs12_read)2007 PHP_FUNCTION(openssl_pkcs12_read)
2008 {
2009 	zval *zout = NULL, *zextracerts, *zcert, *zpkey;
2010 	char *pass, *zp12;
2011 	int pass_len, zp12_len;
2012 	PKCS12 * p12 = NULL;
2013 	EVP_PKEY * pkey = NULL;
2014 	X509 * cert = NULL;
2015 	STACK_OF(X509) * ca = NULL;
2016 	BIO * bio_in = NULL;
2017 	int i;
2018 
2019 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szs", &zp12, &zp12_len, &zout, &pass, &pass_len) == FAILURE)
2020 		return;
2021 
2022 	RETVAL_FALSE;
2023 
2024 	bio_in = BIO_new(BIO_s_mem());
2025 
2026 	if(!BIO_write(bio_in, zp12, zp12_len))
2027 		goto cleanup;
2028 
2029 	if(d2i_PKCS12_bio(bio_in, &p12)) {
2030 		if(PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
2031 			BIO * bio_out;
2032 
2033 			zval_dtor(zout);
2034 			array_init(zout);
2035 
2036 			bio_out = BIO_new(BIO_s_mem());
2037 			if (PEM_write_bio_X509(bio_out, cert)) {
2038 				BUF_MEM *bio_buf;
2039 				BIO_get_mem_ptr(bio_out, &bio_buf);
2040 				MAKE_STD_ZVAL(zcert);
2041 				ZVAL_STRINGL(zcert, bio_buf->data, bio_buf->length, 1);
2042 				add_assoc_zval(zout, "cert", zcert);
2043 			}
2044 			BIO_free(bio_out);
2045 
2046 			bio_out = BIO_new(BIO_s_mem());
2047 			if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) {
2048 				BUF_MEM *bio_buf;
2049 				BIO_get_mem_ptr(bio_out, &bio_buf);
2050 				MAKE_STD_ZVAL(zpkey);
2051 				ZVAL_STRINGL(zpkey, bio_buf->data, bio_buf->length, 1);
2052 				add_assoc_zval(zout, "pkey", zpkey);
2053 			}
2054 			BIO_free(bio_out);
2055 
2056 			MAKE_STD_ZVAL(zextracerts);
2057 			array_init(zextracerts);
2058 
2059 			for (i=0;;i++) {
2060 				zval * zextracert;
2061 				X509* aCA = sk_X509_pop(ca);
2062 				if (!aCA) break;
2063 
2064 				bio_out = BIO_new(BIO_s_mem());
2065 				if (PEM_write_bio_X509(bio_out, aCA)) {
2066 					BUF_MEM *bio_buf;
2067 					BIO_get_mem_ptr(bio_out, &bio_buf);
2068 					MAKE_STD_ZVAL(zextracert);
2069 					ZVAL_STRINGL(zextracert, bio_buf->data, bio_buf->length, 1);
2070 					add_index_zval(zextracerts, i, zextracert);
2071 
2072 				}
2073 				BIO_free(bio_out);
2074 
2075 				X509_free(aCA);
2076 			}
2077 			if(ca) {
2078 				sk_X509_free(ca);
2079 				add_assoc_zval(zout, "extracerts", zextracerts);
2080 			} else {
2081 				zval_dtor(zextracerts);
2082 			}
2083 
2084 			RETVAL_TRUE;
2085 
2086 			PKCS12_free(p12);
2087 		}
2088 	}
2089 
2090   cleanup:
2091 	if (bio_in) {
2092 		BIO_free(bio_in);
2093 	}
2094 	if (pkey) {
2095 		EVP_PKEY_free(pkey);
2096 	}
2097 	if (cert) {
2098 		X509_free(cert);
2099 	}
2100 }
2101 /* }}} */
2102 
2103 /* {{{ x509 CSR functions */
2104 
2105 /* {{{ php_openssl_make_REQ */
php_openssl_make_REQ(struct php_x509_request * req,X509_REQ * csr,zval * dn,zval * attribs TSRMLS_DC)2106 static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, zval * dn, zval * attribs TSRMLS_DC)
2107 {
2108 	STACK_OF(CONF_VALUE) * dn_sk, *attr_sk = NULL;
2109 	char * str, *dn_sect, *attr_sect;
2110 
2111 	dn_sect = CONF_get_string(req->req_config, req->section_name, "distinguished_name");
2112 	if (dn_sect == NULL) {
2113 		return FAILURE;
2114 	}
2115 	dn_sk = CONF_get_section(req->req_config, dn_sect);
2116 	if (dn_sk == NULL) {
2117 		return FAILURE;
2118 	}
2119 	attr_sect = CONF_get_string(req->req_config, req->section_name, "attributes");
2120 	if (attr_sect == NULL) {
2121 		attr_sk = NULL;
2122 	} else {
2123 		attr_sk = CONF_get_section(req->req_config, attr_sect);
2124 		if (attr_sk == NULL) {
2125 			return FAILURE;
2126 		}
2127 	}
2128 	/* setup the version number: version 1 */
2129 	if (X509_REQ_set_version(csr, 0L)) {
2130 		int i, nid;
2131 		char * type;
2132 		CONF_VALUE * v;
2133 		X509_NAME * subj;
2134 		HashPosition hpos;
2135 		zval ** item;
2136 
2137 		subj = X509_REQ_get_subject_name(csr);
2138 		/* apply values from the dn hash */
2139 		zend_hash_internal_pointer_reset_ex(HASH_OF(dn), &hpos);
2140 		while(zend_hash_get_current_data_ex(HASH_OF(dn), (void**)&item, &hpos) == SUCCESS) {
2141 			char * strindex = NULL;
2142 			uint strindexlen = 0;
2143 			ulong intindex;
2144 
2145 			zend_hash_get_current_key_ex(HASH_OF(dn), &strindex, &strindexlen, &intindex, 0, &hpos);
2146 
2147 			convert_to_string_ex(item);
2148 
2149 			if (strindex) {
2150 				int nid;
2151 
2152 				nid = OBJ_txt2nid(strindex);
2153 				if (nid != NID_undef) {
2154 					if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_ASC,
2155 								(unsigned char*)Z_STRVAL_PP(item), -1, -1, 0))
2156 					{
2157 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "dn: add_entry_by_NID %d -> %s (failed)", nid, Z_STRVAL_PP(item));
2158 						return FAILURE;
2159 					}
2160 				} else {
2161 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "dn: %s is not a recognized name", strindex);
2162 				}
2163 			}
2164 			zend_hash_move_forward_ex(HASH_OF(dn), &hpos);
2165 		}
2166 
2167 		/* Finally apply defaults from config file */
2168 		for(i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) {
2169 			int len;
2170 			char buffer[200 + 1]; /*200 + \0 !*/
2171 
2172 			v = sk_CONF_VALUE_value(dn_sk, i);
2173 			type = v->name;
2174 
2175 			len = strlen(type);
2176 			if (len < sizeof("_default")) {
2177 				continue;
2178 			}
2179 			len -= sizeof("_default") - 1;
2180 			if (strcmp("_default", type + len) != 0) {
2181 				continue;
2182 			}
2183 			if (len > 200) {
2184 				len = 200;
2185 			}
2186 			memcpy(buffer, type, len);
2187 			buffer[len] = '\0';
2188 			type = buffer;
2189 
2190 			/* Skip past any leading X. X: X, etc to allow for multiple
2191 			 * instances */
2192 			for (str = type; *str; str++) {
2193 				if (*str == ':' || *str == ',' || *str == '.') {
2194 					str++;
2195 					if (*str) {
2196 						type = str;
2197 					}
2198 					break;
2199 				}
2200 			}
2201 			/* if it is already set, skip this */
2202 			nid = OBJ_txt2nid(type);
2203 			if (X509_NAME_get_index_by_NID(subj, nid, -1) >= 0) {
2204 				continue;
2205 			}
2206 			if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_ASC, (unsigned char*)v->value, -1, -1, 0)) {
2207 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value);
2208 				return FAILURE;
2209 			}
2210 			if (!X509_NAME_entry_count(subj)) {
2211 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "no objects specified in config file");
2212 				return FAILURE;
2213 			}
2214 		}
2215 		if (attribs) {
2216 			zend_hash_internal_pointer_reset_ex(HASH_OF(attribs), &hpos);
2217 			while(zend_hash_get_current_data_ex(HASH_OF(attribs), (void**)&item, &hpos) == SUCCESS) {
2218 				char *strindex = NULL;
2219 				uint strindexlen;
2220 				ulong intindex;
2221 
2222 				zend_hash_get_current_key_ex(HASH_OF(attribs), &strindex, &strindexlen, &intindex, 0, &hpos);
2223 				convert_to_string_ex(item);
2224 
2225 				if (strindex) {
2226 					int nid;
2227 
2228 					nid = OBJ_txt2nid(strindex);
2229 					if (nid != NID_undef) {
2230 						if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_ASC, (unsigned char*)Z_STRVAL_PP(item), -1, -1, 0)) {
2231 							php_error_docref(NULL TSRMLS_CC, E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, Z_STRVAL_PP(item));
2232 							return FAILURE;
2233 						}
2234 					} else {
2235 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "dn: %s is not a recognized name", strindex);
2236 					}
2237 				}
2238 				zend_hash_move_forward_ex(HASH_OF(attribs), &hpos);
2239 			}
2240 			for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) {
2241 				v = sk_CONF_VALUE_value(attr_sk, i);
2242 				/* if it is already set, skip this */
2243 				nid = OBJ_txt2nid(v->name);
2244 				if (X509_REQ_get_attr_by_NID(csr, nid, -1) >= 0) {
2245 					continue;
2246 				}
2247 				if (!X509_REQ_add1_attr_by_txt(csr, v->name, MBSTRING_ASC, (unsigned char*)v->value, -1)) {
2248 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "add1_attr_by_txt %s -> %s (failed)", v->name, v->value);
2249 					return FAILURE;
2250 				}
2251 			}
2252 		}
2253 	}
2254 
2255 	X509_REQ_set_pubkey(csr, req->priv_key);
2256 	return SUCCESS;
2257 }
2258 /* }}} */
2259 
2260 /* {{{ php_openssl_csr_from_zval */
php_openssl_csr_from_zval(zval ** val,int makeresource,long * resourceval TSRMLS_DC)2261 static X509_REQ * php_openssl_csr_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC)
2262 {
2263 	X509_REQ * csr = NULL;
2264 	char * filename = NULL;
2265 	BIO * in;
2266 
2267 	if (resourceval) {
2268 		*resourceval = -1;
2269 	}
2270 	if (Z_TYPE_PP(val) == IS_RESOURCE) {
2271 		void * what;
2272 		int type;
2273 
2274 		what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509 CSR", &type, 1, le_csr);
2275 		if (what) {
2276 			if (resourceval) {
2277 				*resourceval = Z_LVAL_PP(val);
2278 			}
2279 			return (X509_REQ*)what;
2280 		}
2281 		return NULL;
2282 	} else if (Z_TYPE_PP(val) != IS_STRING) {
2283 		return NULL;
2284 	}
2285 
2286 	if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) {
2287 		filename = Z_STRVAL_PP(val) + (sizeof("file://") - 1);
2288 	}
2289 	if (filename) {
2290 		if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
2291 			return NULL;
2292 		}
2293 		in = BIO_new_file(filename, "r");
2294 	} else {
2295 		in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
2296 	}
2297 	csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL);
2298 	BIO_free(in);
2299 
2300 	return csr;
2301 }
2302 /* }}} */
2303 
2304 /* {{{ proto bool openssl_csr_export_to_file(resource csr, string outfilename [, bool notext=true])
2305    Exports a CSR to file */
PHP_FUNCTION(openssl_csr_export_to_file)2306 PHP_FUNCTION(openssl_csr_export_to_file)
2307 {
2308 	X509_REQ * csr;
2309 	zval * zcsr = NULL;
2310 	zend_bool notext = 1;
2311 	char * filename = NULL; int filename_len;
2312 	BIO * bio_out;
2313 	long csr_resource;
2314 
2315 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|b", &zcsr, &filename, &filename_len, &notext) == FAILURE) {
2316 		return;
2317 	}
2318 	RETVAL_FALSE;
2319 
2320 	if (strlen(filename) != filename_len) {
2321 		return;
2322 	}
2323 
2324 	csr = php_openssl_csr_from_zval(&zcsr, 0, &csr_resource TSRMLS_CC);
2325 	if (csr == NULL) {
2326 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1");
2327 		return;
2328 	}
2329 
2330 	if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
2331 		return;
2332 	}
2333 
2334 	bio_out = BIO_new_file(filename, "w");
2335 	if (bio_out) {
2336 		if (!notext) {
2337 			X509_REQ_print(bio_out, csr);
2338 		}
2339 		PEM_write_bio_X509_REQ(bio_out, csr);
2340 		RETVAL_TRUE;
2341 	} else {
2342 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename);
2343 	}
2344 
2345 	if (csr_resource == -1 && csr) {
2346 		X509_REQ_free(csr);
2347 	}
2348 	BIO_free(bio_out);
2349 }
2350 /* }}} */
2351 
2352 /* {{{ proto bool openssl_csr_export(resource csr, string &out [, bool notext=true])
2353    Exports a CSR to file or a var */
PHP_FUNCTION(openssl_csr_export)2354 PHP_FUNCTION(openssl_csr_export)
2355 {
2356 	X509_REQ * csr;
2357 	zval * zcsr = NULL, *zout=NULL;
2358 	zend_bool notext = 1;
2359 	BIO * bio_out;
2360 
2361 	long csr_resource;
2362 
2363 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz|b", &zcsr, &zout, &notext) == FAILURE) {
2364 		return;
2365 	}
2366 	RETVAL_FALSE;
2367 
2368 	csr = php_openssl_csr_from_zval(&zcsr, 0, &csr_resource TSRMLS_CC);
2369 	if (csr == NULL) {
2370 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1");
2371 		return;
2372 	}
2373 
2374 	/* export to a var */
2375 
2376 	bio_out = BIO_new(BIO_s_mem());
2377 	if (!notext) {
2378 		X509_REQ_print(bio_out, csr);
2379 	}
2380 
2381 	if (PEM_write_bio_X509_REQ(bio_out, csr)) {
2382 		BUF_MEM *bio_buf;
2383 
2384 		BIO_get_mem_ptr(bio_out, &bio_buf);
2385 		zval_dtor(zout);
2386 		ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1);
2387 
2388 		RETVAL_TRUE;
2389 	}
2390 
2391 	if (csr_resource == -1 && csr) {
2392 		X509_REQ_free(csr);
2393 	}
2394 	BIO_free(bio_out);
2395 }
2396 /* }}} */
2397 
2398 /* {{{ proto resource openssl_csr_sign(mixed csr, mixed x509, mixed priv_key, long days [, array config_args [, long serial]])
2399    Signs a cert with another CERT */
PHP_FUNCTION(openssl_csr_sign)2400 PHP_FUNCTION(openssl_csr_sign)
2401 {
2402 	zval ** zcert = NULL, **zcsr, **zpkey, *args = NULL;
2403 	long num_days;
2404 	long serial = 0L;
2405 	X509 * cert = NULL, *new_cert = NULL;
2406 	X509_REQ * csr;
2407 	EVP_PKEY * key = NULL, *priv_key = NULL;
2408 	long csr_resource, certresource = 0, keyresource = -1;
2409 	int i;
2410 	struct php_x509_request req;
2411 
2412 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ!Zl|a!l", &zcsr, &zcert, &zpkey, &num_days, &args, &serial) == FAILURE)
2413 		return;
2414 
2415 	RETVAL_FALSE;
2416 	PHP_SSL_REQ_INIT(&req);
2417 
2418 	csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC);
2419 	if (csr == NULL) {
2420 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1");
2421 		return;
2422 	}
2423 	if (zcert) {
2424 		cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
2425 		if (cert == NULL) {
2426 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 2");
2427 			goto cleanup;
2428 		}
2429 	}
2430 	priv_key = php_openssl_evp_from_zval(zpkey, 0, "", 1, &keyresource TSRMLS_CC);
2431 	if (priv_key == NULL) {
2432 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3");
2433 		goto cleanup;
2434 	}
2435 	if (cert && !X509_check_private_key(cert, priv_key)) {
2436 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to signing cert");
2437 		goto cleanup;
2438 	}
2439 
2440 	if (PHP_SSL_REQ_PARSE(&req, args) == FAILURE) {
2441 		goto cleanup;
2442 	}
2443 	/* Check that the request matches the signature */
2444 	key = X509_REQ_get_pubkey(csr);
2445 	if (key == NULL) {
2446 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "error unpacking public key");
2447 		goto cleanup;
2448 	}
2449 	i = X509_REQ_verify(csr, key);
2450 
2451 	if (i < 0) {
2452 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Signature verification problems");
2453 		goto cleanup;
2454 	}
2455 	else if (i == 0) {
2456 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Signature did not match the certificate request");
2457 		goto cleanup;
2458 	}
2459 
2460 	/* Now we can get on with it */
2461 
2462 	new_cert = X509_new();
2463 	if (new_cert == NULL) {
2464 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "No memory");
2465 		goto cleanup;
2466 	}
2467 	/* Version 3 cert */
2468 	if (!X509_set_version(new_cert, 2))
2469 		goto cleanup;
2470 
2471 	ASN1_INTEGER_set(X509_get_serialNumber(new_cert), serial);
2472 
2473 	X509_set_subject_name(new_cert, X509_REQ_get_subject_name(csr));
2474 
2475 	if (cert == NULL) {
2476 		cert = new_cert;
2477 	}
2478 	if (!X509_set_issuer_name(new_cert, X509_get_subject_name(cert))) {
2479 		goto cleanup;
2480 	}
2481 	X509_gmtime_adj(X509_get_notBefore(new_cert), 0);
2482 	X509_gmtime_adj(X509_get_notAfter(new_cert), (long)60*60*24*num_days);
2483 	i = X509_set_pubkey(new_cert, key);
2484 	if (!i) {
2485 		goto cleanup;
2486 	}
2487 	if (req.extensions_section) {
2488 		X509V3_CTX ctx;
2489 
2490 		X509V3_set_ctx(&ctx, cert, new_cert, csr, NULL, 0);
2491 		X509V3_set_conf_lhash(&ctx, req.req_config);
2492 		if (!X509V3_EXT_add_conf(req.req_config, &ctx, req.extensions_section, new_cert)) {
2493 			goto cleanup;
2494 		}
2495 	}
2496 
2497 	/* Now sign it */
2498 	if (!X509_sign(new_cert, priv_key, req.digest)) {
2499 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to sign it");
2500 		goto cleanup;
2501 	}
2502 
2503 	/* Succeeded; lets return the cert */
2504 	RETVAL_RESOURCE(zend_list_insert(new_cert, le_x509));
2505 	new_cert = NULL;
2506 
2507 cleanup:
2508 
2509 	if (cert == new_cert) {
2510 		cert = NULL;
2511 	}
2512 	PHP_SSL_REQ_DISPOSE(&req);
2513 
2514 	if (keyresource == -1 && priv_key) {
2515 		EVP_PKEY_free(priv_key);
2516 	}
2517 	if (key) {
2518 		EVP_PKEY_free(key);
2519 	}
2520 	if (csr_resource == -1 && csr) {
2521 		X509_REQ_free(csr);
2522 	}
2523 	if (certresource == -1 && cert) {
2524 		X509_free(cert);
2525 	}
2526 	if (new_cert) {
2527 		X509_free(new_cert);
2528 	}
2529 }
2530 /* }}} */
2531 
2532 /* {{{ proto bool openssl_csr_new(array dn, resource &privkey [, array configargs [, array extraattribs]])
2533    Generates a privkey and CSR */
PHP_FUNCTION(openssl_csr_new)2534 PHP_FUNCTION(openssl_csr_new)
2535 {
2536 	struct php_x509_request req;
2537 	zval * args = NULL, * dn, *attribs = NULL;
2538 	zval * out_pkey;
2539 	X509_REQ * csr = NULL;
2540 	int we_made_the_key = 1;
2541 	long key_resource;
2542 
2543 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az|a!a!", &dn, &out_pkey, &args, &attribs) == FAILURE) {
2544 		return;
2545 	}
2546 	RETVAL_FALSE;
2547 
2548 	PHP_SSL_REQ_INIT(&req);
2549 
2550 	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
2551 		/* Generate or use a private key */
2552 		if (Z_TYPE_P(out_pkey) != IS_NULL) {
2553 			req.priv_key = php_openssl_evp_from_zval(&out_pkey, 0, NULL, 0, &key_resource TSRMLS_CC);
2554 			if (req.priv_key != NULL) {
2555 				we_made_the_key = 0;
2556 			}
2557 		}
2558 		if (req.priv_key == NULL) {
2559 			php_openssl_generate_private_key(&req TSRMLS_CC);
2560 		}
2561 		if (req.priv_key == NULL) {
2562 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to generate a private key");
2563 		} else {
2564 			csr = X509_REQ_new();
2565 			if (csr) {
2566 				if (php_openssl_make_REQ(&req, csr, dn, attribs TSRMLS_CC) == SUCCESS) {
2567 					X509V3_CTX ext_ctx;
2568 
2569 					X509V3_set_ctx(&ext_ctx, NULL, NULL, csr, NULL, 0);
2570 					X509V3_set_conf_lhash(&ext_ctx, req.req_config);
2571 
2572 					/* Add extensions */
2573 					if (req.request_extensions_section && !X509V3_EXT_REQ_add_conf(req.req_config,
2574 								&ext_ctx, req.request_extensions_section, csr))
2575 					{
2576 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error loading extension section %s", req.request_extensions_section);
2577 					} else {
2578 						RETVAL_TRUE;
2579 
2580 						if (X509_REQ_sign(csr, req.priv_key, req.digest)) {
2581 							RETVAL_RESOURCE(zend_list_insert(csr, le_csr));
2582 							csr = NULL;
2583 						} else {
2584 							php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error signing request");
2585 						}
2586 
2587 						if (we_made_the_key) {
2588 							/* and a resource for the private key */
2589 							zval_dtor(out_pkey);
2590 							ZVAL_RESOURCE(out_pkey, zend_list_insert(req.priv_key, le_key));
2591 							req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
2592 						} else if (key_resource != -1) {
2593 							req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
2594 						}
2595 					}
2596 				}
2597 				else {
2598 					if (!we_made_the_key) {
2599 						/* if we have not made the key we are not supposed to zap it by calling dispose! */
2600 						req.priv_key = NULL;
2601 					}
2602 				}
2603 			}
2604 		}
2605 	}
2606 	if (csr) {
2607 		X509_REQ_free(csr);
2608 	}
2609 	PHP_SSL_REQ_DISPOSE(&req);
2610 }
2611 /* }}} */
2612 
2613 /* {{{ proto mixed openssl_csr_get_subject(mixed csr)
2614    Returns the subject of a CERT or FALSE on error */
PHP_FUNCTION(openssl_csr_get_subject)2615 PHP_FUNCTION(openssl_csr_get_subject)
2616 {
2617 	zval ** zcsr;
2618 	zend_bool use_shortnames = 1;
2619 	long csr_resource;
2620 	X509_NAME * subject;
2621 	X509_REQ * csr;
2622 
2623 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcsr, &use_shortnames) == FAILURE) {
2624 		return;
2625 	}
2626 
2627 	csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC);
2628 
2629 	if (csr == NULL) {
2630 		RETURN_FALSE;
2631 	}
2632 
2633 	subject = X509_REQ_get_subject_name(csr);
2634 
2635 	array_init(return_value);
2636 	add_assoc_name_entry(return_value, NULL, subject, use_shortnames TSRMLS_CC);
2637 	return;
2638 }
2639 /* }}} */
2640 
2641 /* {{{ proto mixed openssl_csr_get_public_key(mixed csr)
2642 	Returns the subject of a CERT or FALSE on error */
PHP_FUNCTION(openssl_csr_get_public_key)2643 PHP_FUNCTION(openssl_csr_get_public_key)
2644 {
2645 	zval ** zcsr;
2646 	zend_bool use_shortnames = 1;
2647 	long csr_resource;
2648 
2649 	X509_REQ * csr;
2650 	EVP_PKEY *tpubkey;
2651 
2652 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcsr, &use_shortnames) == FAILURE) {
2653 		return;
2654 	}
2655 
2656 	csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC);
2657 
2658 	if (csr == NULL) {
2659 		RETURN_FALSE;
2660 	}
2661 
2662 	tpubkey=X509_REQ_get_pubkey(csr);
2663 	RETVAL_RESOURCE(zend_list_insert(tpubkey, le_key));
2664 	return;
2665 }
2666 /* }}} */
2667 
2668 /* }}} */
2669 
2670 /* {{{ EVP Public/Private key functions */
2671 
2672 /* {{{ php_openssl_evp_from_zval
2673    Given a zval, coerce it into a EVP_PKEY object.
2674 	It can be:
2675 		1. private key resource from openssl_get_privatekey()
2676 		2. X509 resource -> public key will be extracted from it
2677 		3. if it starts with file:// interpreted as path to key file
2678 		4. interpreted as the data from the cert/key file and interpreted in same way as openssl_get_privatekey()
2679 		5. an array(0 => [items 2..4], 1 => passphrase)
2680 		6. if val is a string (possibly starting with file:///) and it is not an X509 certificate, then interpret as public key
2681 	NOTE: If you are requesting a private key but have not specified a passphrase, you should use an
2682 	empty string rather than NULL for the passphrase - NULL causes a passphrase prompt to be emitted in
2683 	the Apache error log!
2684 */
php_openssl_evp_from_zval(zval ** val,int public_key,char * passphrase,int makeresource,long * resourceval TSRMLS_DC)2685 static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC)
2686 {
2687 	EVP_PKEY * key = NULL;
2688 	X509 * cert = NULL;
2689 	int free_cert = 0;
2690 	long cert_res = -1;
2691 	char * filename = NULL;
2692 	zval tmp;
2693 
2694 	Z_TYPE(tmp) = IS_NULL;
2695 
2696 #define TMP_CLEAN \
2697 	if (Z_TYPE(tmp) == IS_STRING) {\
2698 		zval_dtor(&tmp); \
2699 	} \
2700 	return NULL;
2701 
2702 	if (resourceval) {
2703 		*resourceval = -1;
2704 	}
2705 	if (Z_TYPE_PP(val) == IS_ARRAY) {
2706 		zval ** zphrase;
2707 
2708 		/* get passphrase */
2709 
2710 		if (zend_hash_index_find(HASH_OF(*val), 1, (void **)&zphrase) == FAILURE) {
2711 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)");
2712 			return NULL;
2713 		}
2714 
2715 		if (Z_TYPE_PP(zphrase) == IS_STRING) {
2716 			passphrase = Z_STRVAL_PP(zphrase);
2717 		} else {
2718 			tmp = **zphrase;
2719 			zval_copy_ctor(&tmp);
2720 			convert_to_string(&tmp);
2721 			passphrase = Z_STRVAL(tmp);
2722 		}
2723 
2724 		/* now set val to be the key param and continue */
2725 		if (zend_hash_index_find(HASH_OF(*val), 0, (void **)&val) == FAILURE) {
2726 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)");
2727 			TMP_CLEAN;
2728 		}
2729 	}
2730 
2731 	if (Z_TYPE_PP(val) == IS_RESOURCE) {
2732 		void * what;
2733 		int type;
2734 
2735 		what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509/key", &type, 2, le_x509, le_key);
2736 		if (!what) {
2737 			TMP_CLEAN;
2738 		}
2739 		if (resourceval) {
2740 			*resourceval = Z_LVAL_PP(val);
2741 		}
2742 		if (type == le_x509) {
2743 			/* extract key from cert, depending on public_key param */
2744 			cert = (X509*)what;
2745 			free_cert = 0;
2746 		} else if (type == le_key) {
2747 			int is_priv;
2748 
2749 			is_priv = php_openssl_is_private_key((EVP_PKEY*)what TSRMLS_CC);
2750 
2751 			/* check whether it is actually a private key if requested */
2752 			if (!public_key && !is_priv) {
2753 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param is a public key");
2754 				TMP_CLEAN;
2755 			}
2756 
2757 			if (public_key && is_priv) {
2758 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Don't know how to get public key from this private key");
2759 				TMP_CLEAN;
2760 			} else {
2761 				if (Z_TYPE(tmp) == IS_STRING) {
2762 					zval_dtor(&tmp);
2763 				}
2764 				/* got the key - return it */
2765 				return (EVP_PKEY*)what;
2766 			}
2767 		} else {
2768 			/* other types could be used here - eg: file pointers and read in the data from them */
2769 			TMP_CLEAN;
2770 		}
2771 	} else {
2772 		/* force it to be a string and check if it refers to a file */
2773 		/* passing non string values leaks, object uses toString, it returns NULL
2774 		 * See bug38255.phpt
2775 		 */
2776 		if (!(Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_OBJECT)) {
2777 			TMP_CLEAN;
2778 		}
2779 		convert_to_string_ex(val);
2780 
2781 		if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) {
2782 			filename = Z_STRVAL_PP(val) + (sizeof("file://") - 1);
2783 		}
2784 		/* it's an X509 file/cert of some kind, and we need to extract the data from that */
2785 		if (public_key) {
2786 			cert = php_openssl_x509_from_zval(val, 0, &cert_res TSRMLS_CC);
2787 			free_cert = (cert_res == -1);
2788 			/* actual extraction done later */
2789 			if (!cert) {
2790 				/* not a X509 certificate, try to retrieve public key */
2791 				BIO* in;
2792 				if (filename) {
2793 					in = BIO_new_file(filename, "r");
2794 				} else {
2795 					in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
2796 				}
2797 				if (in == NULL) {
2798 					TMP_CLEAN;
2799 				}
2800 				key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL);
2801 				BIO_free(in);
2802 			}
2803 		} else {
2804 			/* we want the private key */
2805 			BIO *in;
2806 
2807 			if (filename) {
2808 				if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
2809 					TMP_CLEAN;
2810 				}
2811 				in = BIO_new_file(filename, "r");
2812 			} else {
2813 				in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
2814 			}
2815 
2816 			if (in == NULL) {
2817 				TMP_CLEAN;
2818 			}
2819 			key = PEM_read_bio_PrivateKey(in, NULL,NULL, passphrase);
2820 			BIO_free(in);
2821 		}
2822 	}
2823 
2824 	if (public_key && cert && key == NULL) {
2825 		/* extract public key from X509 cert */
2826 		key = (EVP_PKEY *) X509_get_pubkey(cert);
2827 	}
2828 
2829 	if (free_cert && cert) {
2830 		X509_free(cert);
2831 	}
2832 	if (key && makeresource && resourceval) {
2833 		*resourceval = ZEND_REGISTER_RESOURCE(NULL, key, le_key);
2834 	}
2835 	if (Z_TYPE(tmp) == IS_STRING) {
2836 		zval_dtor(&tmp);
2837 	}
2838 	return key;
2839 }
2840 /* }}} */
2841 
2842 /* {{{ php_openssl_generate_private_key */
php_openssl_generate_private_key(struct php_x509_request * req TSRMLS_DC)2843 static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req TSRMLS_DC)
2844 {
2845 	char * randfile = NULL;
2846 	int egdsocket, seeded;
2847 	EVP_PKEY * return_val = NULL;
2848 
2849 	if (req->priv_key_bits < MIN_KEY_LENGTH) {
2850 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key length is too short; it needs to be at least %d bits, not %d",
2851 				MIN_KEY_LENGTH, req->priv_key_bits);
2852 		return NULL;
2853 	}
2854 
2855 	randfile = CONF_get_string(req->req_config, req->section_name, "RANDFILE");
2856 	php_openssl_load_rand_file(randfile, &egdsocket, &seeded);
2857 
2858 	if ((req->priv_key = EVP_PKEY_new()) != NULL) {
2859 		switch(req->priv_key_type) {
2860 			case OPENSSL_KEYTYPE_RSA:
2861 				if (EVP_PKEY_assign_RSA(req->priv_key, RSA_generate_key(req->priv_key_bits, 0x10001, NULL, NULL))) {
2862 					return_val = req->priv_key;
2863 				}
2864 				break;
2865 #if !defined(NO_DSA) && defined(HAVE_DSA_DEFAULT_METHOD)
2866 			case OPENSSL_KEYTYPE_DSA:
2867 				{
2868 					DSA *dsapar = DSA_generate_parameters(req->priv_key_bits, NULL, 0, NULL, NULL, NULL, NULL);
2869 					if (dsapar) {
2870 						DSA_set_method(dsapar, DSA_get_default_method());
2871 						if (DSA_generate_key(dsapar)) {
2872 							if (EVP_PKEY_assign_DSA(req->priv_key, dsapar)) {
2873 								return_val = req->priv_key;
2874 							}
2875 						} else {
2876 							DSA_free(dsapar);
2877 						}
2878 					}
2879 				}
2880 				break;
2881 #endif
2882 #if !defined(NO_DH)
2883 			case OPENSSL_KEYTYPE_DH:
2884 				{
2885 					DH *dhpar = DH_generate_parameters(req->priv_key_bits, 2, NULL, NULL);
2886 					int codes = 0;
2887 
2888 					if (dhpar) {
2889 						DH_set_method(dhpar, DH_get_default_method());
2890 						if (DH_check(dhpar, &codes) && codes == 0 && DH_generate_key(dhpar)) {
2891 							if (EVP_PKEY_assign_DH(req->priv_key, dhpar)) {
2892 								return_val = req->priv_key;
2893 							}
2894 						} else {
2895 							DH_free(dhpar);
2896 						}
2897 					}
2898 				}
2899 				break;
2900 #endif
2901 			default:
2902 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported private key type");
2903 		}
2904 	}
2905 
2906 	php_openssl_write_rand_file(randfile, egdsocket, seeded);
2907 
2908 	if (return_val == NULL) {
2909 		EVP_PKEY_free(req->priv_key);
2910 		req->priv_key = NULL;
2911 		return NULL;
2912 	}
2913 
2914 	return return_val;
2915 }
2916 /* }}} */
2917 
2918 /* {{{ php_openssl_is_private_key
2919 	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 TSRMLS_DC)2920 static int php_openssl_is_private_key(EVP_PKEY* pkey TSRMLS_DC)
2921 {
2922 	assert(pkey != NULL);
2923 
2924 	switch (pkey->type) {
2925 #ifndef NO_RSA
2926 		case EVP_PKEY_RSA:
2927 		case EVP_PKEY_RSA2:
2928 			assert(pkey->pkey.rsa != NULL);
2929 			if (pkey->pkey.rsa != NULL && (NULL == pkey->pkey.rsa->p || NULL == pkey->pkey.rsa->q)) {
2930 				return 0;
2931 			}
2932 			break;
2933 #endif
2934 #ifndef NO_DSA
2935 		case EVP_PKEY_DSA:
2936 		case EVP_PKEY_DSA1:
2937 		case EVP_PKEY_DSA2:
2938 		case EVP_PKEY_DSA3:
2939 		case EVP_PKEY_DSA4:
2940 			assert(pkey->pkey.dsa != NULL);
2941 
2942 			if (NULL == pkey->pkey.dsa->p || NULL == pkey->pkey.dsa->q || NULL == pkey->pkey.dsa->priv_key){
2943 				return 0;
2944 			}
2945 			break;
2946 #endif
2947 #ifndef NO_DH
2948 		case EVP_PKEY_DH:
2949 			assert(pkey->pkey.dh != NULL);
2950 
2951 			if (NULL == pkey->pkey.dh->p || NULL == pkey->pkey.dh->priv_key) {
2952 				return 0;
2953 			}
2954 			break;
2955 #endif
2956 		default:
2957 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
2958 			break;
2959 	}
2960 	return 1;
2961 }
2962 /* }}} */
2963 
2964 #define OPENSSL_PKEY_GET_BN(_type, _name) do {							\
2965 		if (pkey->pkey._type->_name != NULL) {							\
2966 			int len = BN_num_bytes(pkey->pkey._type->_name);			\
2967 			char *str = emalloc(len + 1);								\
2968 			BN_bn2bin(pkey->pkey._type->_name, (unsigned char*)str);	\
2969 			str[len] = 0;                                           	\
2970 			add_assoc_stringl(_type, #_name, str, len, 0);				\
2971 		}																\
2972 	} while (0)
2973 
2974 #define OPENSSL_PKEY_SET_BN(_ht, _type, _name) do {						\
2975 		zval **bn;														\
2976 		if (zend_hash_find(_ht, #_name, sizeof(#_name),	(void**)&bn) == SUCCESS && \
2977 				Z_TYPE_PP(bn) == IS_STRING) {							\
2978 			_type->_name = BN_bin2bn(									\
2979 				(unsigned char*)Z_STRVAL_PP(bn),						\
2980 	 			Z_STRLEN_PP(bn), NULL);									\
2981 	    }                                                               \
2982 	} while (0);
2983 
2984 
2985 /* {{{ proto resource openssl_pkey_new([array configargs])
2986    Generates a new private key */
PHP_FUNCTION(openssl_pkey_new)2987 PHP_FUNCTION(openssl_pkey_new)
2988 {
2989 	struct php_x509_request req;
2990 	zval * args = NULL;
2991 	zval **data;
2992 
2993 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &args) == FAILURE) {
2994 		return;
2995 	}
2996 	RETVAL_FALSE;
2997 
2998 	if (args && Z_TYPE_P(args) == IS_ARRAY) {
2999 		EVP_PKEY *pkey;
3000 
3001 		if (zend_hash_find(Z_ARRVAL_P(args), "rsa", sizeof("rsa"), (void**)&data) == SUCCESS &&
3002 		    Z_TYPE_PP(data) == IS_ARRAY) {
3003 		    pkey = EVP_PKEY_new();
3004 		    if (pkey) {
3005 				RSA *rsa = RSA_new();
3006 				if (rsa) {
3007 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, n);
3008 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, e);
3009 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, d);
3010 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, p);
3011 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, q);
3012 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, dmp1);
3013 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, dmq1);
3014 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, iqmp);
3015 					if (rsa->n && rsa->d) {
3016 						if (EVP_PKEY_assign_RSA(pkey, rsa)) {
3017 							RETURN_RESOURCE(zend_list_insert(pkey, le_key));
3018 						}
3019 					}
3020 					RSA_free(rsa);
3021 				}
3022 				EVP_PKEY_free(pkey);
3023 			}
3024 			RETURN_FALSE;
3025 		} else if (zend_hash_find(Z_ARRVAL_P(args), "dsa", sizeof("dsa"), (void**)&data) == SUCCESS &&
3026 		           Z_TYPE_PP(data) == IS_ARRAY) {
3027 		    pkey = EVP_PKEY_new();
3028 		    if (pkey) {
3029 				DSA *dsa = DSA_new();
3030 				if (dsa) {
3031 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, p);
3032 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, q);
3033 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, g);
3034 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, priv_key);
3035 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, pub_key);
3036 					if (dsa->p && dsa->q && dsa->g) {
3037 						if (!dsa->priv_key && !dsa->pub_key) {
3038 							DSA_generate_key(dsa);
3039 						}
3040 						if (EVP_PKEY_assign_DSA(pkey, dsa)) {
3041 							RETURN_RESOURCE(zend_list_insert(pkey, le_key));
3042 						}
3043 					}
3044 					DSA_free(dsa);
3045 				}
3046 				EVP_PKEY_free(pkey);
3047 			}
3048 			RETURN_FALSE;
3049 		} else if (zend_hash_find(Z_ARRVAL_P(args), "dh", sizeof("dh"), (void**)&data) == SUCCESS &&
3050 		           Z_TYPE_PP(data) == IS_ARRAY) {
3051 		    pkey = EVP_PKEY_new();
3052 		    if (pkey) {
3053 				DH *dh = DH_new();
3054 				if (dh) {
3055 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, p);
3056 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, g);
3057 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, priv_key);
3058 					OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, pub_key);
3059 					if (dh->p && dh->g) {
3060 						if (!dh->pub_key) {
3061 							DH_generate_key(dh);
3062 						}
3063 						if (EVP_PKEY_assign_DH(pkey, dh)) {
3064 							RETURN_RESOURCE(zend_list_insert(pkey, le_key));
3065 						}
3066 					}
3067 					DH_free(dh);
3068 				}
3069 				EVP_PKEY_free(pkey);
3070 			}
3071 			RETURN_FALSE;
3072 		}
3073 	}
3074 
3075 	PHP_SSL_REQ_INIT(&req);
3076 
3077 	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS)
3078 	{
3079 		if (php_openssl_generate_private_key(&req TSRMLS_CC)) {
3080 			/* pass back a key resource */
3081 			RETVAL_RESOURCE(zend_list_insert(req.priv_key, le_key));
3082 			/* make sure the cleanup code doesn't zap it! */
3083 			req.priv_key = NULL;
3084 		}
3085 	}
3086 	PHP_SSL_REQ_DISPOSE(&req);
3087 }
3088 /* }}} */
3089 
3090 /* {{{ proto bool openssl_pkey_export_to_file(mixed key, string outfilename [, string passphrase, array config_args)
3091    Gets an exportable representation of a key into a file */
PHP_FUNCTION(openssl_pkey_export_to_file)3092 PHP_FUNCTION(openssl_pkey_export_to_file)
3093 {
3094 	struct php_x509_request req;
3095 	zval ** zpkey, * args = NULL;
3096 	char * passphrase = NULL; int passphrase_len = 0;
3097 	char * filename = NULL; int filename_len = 0;
3098 	long key_resource = -1;
3099 	EVP_PKEY * key;
3100 	BIO * bio_out = NULL;
3101 	const EVP_CIPHER * cipher;
3102 
3103 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zs|s!a!", &zpkey, &filename, &filename_len, &passphrase, &passphrase_len, &args) == FAILURE) {
3104 		return;
3105 	}
3106 	RETVAL_FALSE;
3107 
3108 	if (strlen(filename) != filename_len) {
3109 		return;
3110 	}
3111 
3112 	key = php_openssl_evp_from_zval(zpkey, 0, passphrase, 0, &key_resource TSRMLS_CC);
3113 
3114 	if (key == NULL) {
3115 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get key from parameter 1");
3116 		RETURN_FALSE;
3117 	}
3118 
3119 	if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
3120 		RETURN_FALSE;
3121 	}
3122 
3123 	PHP_SSL_REQ_INIT(&req);
3124 
3125 	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
3126 		bio_out = BIO_new_file(filename, "w");
3127 
3128 		if (passphrase && req.priv_key_encrypt) {
3129 			cipher = (EVP_CIPHER *) EVP_des_ede3_cbc();
3130 		} else {
3131 			cipher = NULL;
3132 		}
3133 		if (PEM_write_bio_PrivateKey(bio_out, key, cipher, (unsigned char *)passphrase, passphrase_len, NULL, NULL)) {
3134 			/* Success!
3135 			 * If returning the output as a string, do so now */
3136 			RETVAL_TRUE;
3137 		}
3138 	}
3139 	PHP_SSL_REQ_DISPOSE(&req);
3140 
3141 	if (key_resource == -1 && key) {
3142 		EVP_PKEY_free(key);
3143 	}
3144 	if (bio_out) {
3145 		BIO_free(bio_out);
3146 	}
3147 }
3148 /* }}} */
3149 
3150 /* {{{ proto bool openssl_pkey_export(mixed key, &mixed out [, string passphrase [, array config_args]])
3151    Gets an exportable representation of a key into a string or file */
PHP_FUNCTION(openssl_pkey_export)3152 PHP_FUNCTION(openssl_pkey_export)
3153 {
3154 	struct php_x509_request req;
3155 	zval ** zpkey, * args = NULL, *out;
3156 	char * passphrase = NULL; int passphrase_len = 0;
3157 	long key_resource = -1;
3158 	EVP_PKEY * key;
3159 	BIO * bio_out = NULL;
3160 	const EVP_CIPHER * cipher;
3161 
3162 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zz|s!a!", &zpkey, &out, &passphrase, &passphrase_len, &args) == FAILURE) {
3163 		return;
3164 	}
3165 	RETVAL_FALSE;
3166 
3167 	key = php_openssl_evp_from_zval(zpkey, 0, passphrase, 0, &key_resource TSRMLS_CC);
3168 
3169 	if (key == NULL) {
3170 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get key from parameter 1");
3171 		RETURN_FALSE;
3172 	}
3173 
3174 	PHP_SSL_REQ_INIT(&req);
3175 
3176 	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
3177 		bio_out = BIO_new(BIO_s_mem());
3178 
3179 		if (passphrase && req.priv_key_encrypt) {
3180 			cipher = (EVP_CIPHER *) EVP_des_ede3_cbc();
3181 		} else {
3182 			cipher = NULL;
3183 		}
3184 		if (PEM_write_bio_PrivateKey(bio_out, key, cipher, (unsigned char *)passphrase, passphrase_len, NULL, NULL)) {
3185 			/* Success!
3186 			 * If returning the output as a string, do so now */
3187 
3188 			char * bio_mem_ptr;
3189 			long bio_mem_len;
3190 			RETVAL_TRUE;
3191 
3192 			bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
3193 			zval_dtor(out);
3194 			ZVAL_STRINGL(out, bio_mem_ptr, bio_mem_len, 1);
3195 		}
3196 	}
3197 	PHP_SSL_REQ_DISPOSE(&req);
3198 
3199 	if (key_resource == -1 && key) {
3200 		EVP_PKEY_free(key);
3201 	}
3202 	if (bio_out) {
3203 		BIO_free(bio_out);
3204 	}
3205 }
3206 /* }}} */
3207 
3208 /* {{{ proto int openssl_pkey_get_public(mixed cert)
3209    Gets public key from X.509 certificate */
PHP_FUNCTION(openssl_pkey_get_public)3210 PHP_FUNCTION(openssl_pkey_get_public)
3211 {
3212 	zval **cert;
3213 	EVP_PKEY *pkey;
3214 
3215 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &cert) == FAILURE) {
3216 		return;
3217 	}
3218 	Z_TYPE_P(return_value) = IS_RESOURCE;
3219 	pkey = php_openssl_evp_from_zval(cert, 1, NULL, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
3220 
3221 	if (pkey == NULL) {
3222 		RETURN_FALSE;
3223 	}
3224 }
3225 /* }}} */
3226 
3227 /* {{{ proto void openssl_pkey_free(int key)
3228    Frees a key */
PHP_FUNCTION(openssl_pkey_free)3229 PHP_FUNCTION(openssl_pkey_free)
3230 {
3231 	zval *key;
3232 	EVP_PKEY *pkey;
3233 
3234 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &key) == FAILURE) {
3235 		return;
3236 	}
3237 	ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key);
3238 	zend_list_delete(Z_LVAL_P(key));
3239 }
3240 /* }}} */
3241 
3242 /* {{{ proto int openssl_pkey_get_private(string key [, string passphrase])
3243    Gets private keys */
PHP_FUNCTION(openssl_pkey_get_private)3244 PHP_FUNCTION(openssl_pkey_get_private)
3245 {
3246 	zval **cert;
3247 	EVP_PKEY *pkey;
3248 	char * passphrase = "";
3249 	int passphrase_len = sizeof("")-1;
3250 
3251 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|s", &cert, &passphrase, &passphrase_len) == FAILURE) {
3252 		return;
3253 	}
3254 	Z_TYPE_P(return_value) = IS_RESOURCE;
3255 	pkey = php_openssl_evp_from_zval(cert, 0, passphrase, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
3256 
3257 	if (pkey == NULL) {
3258 		RETURN_FALSE;
3259 	}
3260 }
3261 
3262 /* }}} */
3263 
3264 /* {{{ proto resource openssl_pkey_get_details(resource key)
3265 	returns an array with the key details (bits, pkey, type)*/
PHP_FUNCTION(openssl_pkey_get_details)3266 PHP_FUNCTION(openssl_pkey_get_details)
3267 {
3268 	zval *key;
3269 	EVP_PKEY *pkey;
3270 	BIO *out;
3271 	unsigned int pbio_len;
3272 	char *pbio;
3273 	long ktype;
3274 
3275 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &key) == FAILURE) {
3276 		return;
3277 	}
3278 	ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key);
3279 	if (!pkey) {
3280 		RETURN_FALSE;
3281 	}
3282 	out = BIO_new(BIO_s_mem());
3283 	PEM_write_bio_PUBKEY(out, pkey);
3284 	pbio_len = BIO_get_mem_data(out, &pbio);
3285 
3286 	array_init(return_value);
3287 	add_assoc_long(return_value, "bits", EVP_PKEY_bits(pkey));
3288 	add_assoc_stringl(return_value, "key", pbio, pbio_len, 1);
3289 	/*TODO: Use the real values once the openssl constants are used
3290 	 * See the enum at the top of this file
3291 	 */
3292 	switch (EVP_PKEY_type(pkey->type)) {
3293 		case EVP_PKEY_RSA:
3294 		case EVP_PKEY_RSA2:
3295 			ktype = OPENSSL_KEYTYPE_RSA;
3296 
3297 			if (pkey->pkey.rsa != NULL) {
3298 				zval *rsa;
3299 
3300 				ALLOC_INIT_ZVAL(rsa);
3301 				array_init(rsa);
3302 				OPENSSL_PKEY_GET_BN(rsa, n);
3303 				OPENSSL_PKEY_GET_BN(rsa, e);
3304 				OPENSSL_PKEY_GET_BN(rsa, d);
3305 				OPENSSL_PKEY_GET_BN(rsa, p);
3306 				OPENSSL_PKEY_GET_BN(rsa, q);
3307 				OPENSSL_PKEY_GET_BN(rsa, dmp1);
3308 				OPENSSL_PKEY_GET_BN(rsa, dmq1);
3309 				OPENSSL_PKEY_GET_BN(rsa, iqmp);
3310 				add_assoc_zval(return_value, "rsa", rsa);
3311 			}
3312 
3313 			break;
3314 		case EVP_PKEY_DSA:
3315 		case EVP_PKEY_DSA2:
3316 		case EVP_PKEY_DSA3:
3317 		case EVP_PKEY_DSA4:
3318 			ktype = OPENSSL_KEYTYPE_DSA;
3319 
3320 			if (pkey->pkey.dsa != NULL) {
3321 				zval *dsa;
3322 
3323 				ALLOC_INIT_ZVAL(dsa);
3324 				array_init(dsa);
3325 				OPENSSL_PKEY_GET_BN(dsa, p);
3326 				OPENSSL_PKEY_GET_BN(dsa, q);
3327 				OPENSSL_PKEY_GET_BN(dsa, g);
3328 				OPENSSL_PKEY_GET_BN(dsa, priv_key);
3329 				OPENSSL_PKEY_GET_BN(dsa, pub_key);
3330 				add_assoc_zval(return_value, "dsa", dsa);
3331 			}
3332 			break;
3333 		case EVP_PKEY_DH:
3334 
3335 			ktype = OPENSSL_KEYTYPE_DH;
3336 
3337 			if (pkey->pkey.dh != NULL) {
3338 				zval *dh;
3339 
3340 				ALLOC_INIT_ZVAL(dh);
3341 				array_init(dh);
3342 				OPENSSL_PKEY_GET_BN(dh, p);
3343 				OPENSSL_PKEY_GET_BN(dh, g);
3344 				OPENSSL_PKEY_GET_BN(dh, priv_key);
3345 				OPENSSL_PKEY_GET_BN(dh, pub_key);
3346 				add_assoc_zval(return_value, "dh", dh);
3347 			}
3348 
3349 			break;
3350 #ifdef EVP_PKEY_EC
3351 		case EVP_PKEY_EC:
3352 			ktype = OPENSSL_KEYTYPE_EC;
3353 			break;
3354 #endif
3355 		default:
3356 			ktype = -1;
3357 			break;
3358 	}
3359 	add_assoc_long(return_value, "type", ktype);
3360 
3361 	BIO_free(out);
3362 }
3363 /* }}} */
3364 
3365 /* }}} */
3366 
3367 /* {{{ PKCS7 S/MIME functions */
3368 
3369 /* {{{ proto bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]])
3370    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)3371 PHP_FUNCTION(openssl_pkcs7_verify)
3372 {
3373 	X509_STORE * store = NULL;
3374 	zval * cainfo = NULL;
3375 	STACK_OF(X509) *signers= NULL;
3376 	STACK_OF(X509) *others = NULL;
3377 	PKCS7 * p7 = NULL;
3378 	BIO * in = NULL, * datain = NULL, * dataout = NULL;
3379 	long flags = 0;
3380 	char * filename; int filename_len;
3381 	char * extracerts = NULL; int extracerts_len = 0;
3382 	char * signersfilename = NULL; int signersfilename_len = 0;
3383 	char * datafilename = NULL; int datafilename_len = 0;
3384 
3385 	RETVAL_LONG(-1);
3386 
3387 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|sass", &filename, &filename_len,
3388 				&flags, &signersfilename, &signersfilename_len, &cainfo,
3389 				&extracerts, &extracerts_len, &datafilename, &datafilename_len) == FAILURE) {
3390 		return;
3391 	}
3392 
3393 	if (extracerts) {
3394 		others = load_all_certs_from_file(extracerts);
3395 		if (others == NULL) {
3396 			goto clean_exit;
3397 		}
3398 	}
3399 
3400 	flags = flags & ~PKCS7_DETACHED;
3401 
3402 	store = setup_verify(cainfo TSRMLS_CC);
3403 
3404 	if (!store) {
3405 		goto clean_exit;
3406 	}
3407 	if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
3408 		goto clean_exit;
3409 	}
3410 
3411 	in = BIO_new_file(filename, (flags & PKCS7_BINARY) ? "rb" : "r");
3412 	if (in == NULL) {
3413 		goto clean_exit;
3414 	}
3415 	p7 = SMIME_read_PKCS7(in, &datain);
3416 	if (p7 == NULL) {
3417 #if DEBUG_SMIME
3418 		zend_printf("SMIME_read_PKCS7 failed\n");
3419 #endif
3420 		goto clean_exit;
3421 	}
3422 
3423 	if (datafilename) {
3424 
3425 		if (php_openssl_safe_mode_chk(datafilename TSRMLS_CC)) {
3426 			goto clean_exit;
3427 		}
3428 
3429 		dataout = BIO_new_file(datafilename, "w");
3430 		if (dataout == NULL) {
3431 			goto clean_exit;
3432 		}
3433 	}
3434 #if DEBUG_SMIME
3435 	zend_printf("Calling PKCS7 verify\n");
3436 #endif
3437 
3438 	if (PKCS7_verify(p7, others, store, datain, dataout, flags)) {
3439 
3440 		RETVAL_TRUE;
3441 
3442 		if (signersfilename) {
3443 			BIO *certout;
3444 
3445 			if (php_openssl_safe_mode_chk(signersfilename TSRMLS_CC)) {
3446 				goto clean_exit;
3447 			}
3448 
3449 			certout = BIO_new_file(signersfilename, "w");
3450 			if (certout) {
3451 				int i;
3452 				signers = PKCS7_get0_signers(p7, NULL, flags);
3453 
3454 				for(i = 0; i < sk_X509_num(signers); i++) {
3455 					PEM_write_bio_X509(certout, sk_X509_value(signers, i));
3456 				}
3457 				BIO_free(certout);
3458 				sk_X509_free(signers);
3459 			} else {
3460 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "signature OK, but cannot open %s for writing", signersfilename);
3461 				RETVAL_LONG(-1);
3462 			}
3463 		}
3464 		goto clean_exit;
3465 	} else {
3466 		RETVAL_FALSE;
3467 	}
3468 clean_exit:
3469 	X509_STORE_free(store);
3470 	BIO_free(datain);
3471 	BIO_free(in);
3472 	BIO_free(dataout);
3473 	PKCS7_free(p7);
3474 	sk_X509_free(others);
3475 }
3476 /* }}} */
3477 
3478 /* {{{ proto bool openssl_pkcs7_encrypt(string infile, string outfile, mixed recipcerts, array headers [, long flags [, long cipher]])
3479    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)3480 PHP_FUNCTION(openssl_pkcs7_encrypt)
3481 {
3482 	zval ** zrecipcerts, * zheaders = NULL;
3483 	STACK_OF(X509) * recipcerts = NULL;
3484 	BIO * infile = NULL, * outfile = NULL;
3485 	long flags = 0;
3486 	PKCS7 * p7 = NULL;
3487 	HashPosition hpos;
3488 	zval ** zcertval;
3489 	X509 * cert;
3490 	const EVP_CIPHER *cipher = NULL;
3491 	long cipherid = PHP_OPENSSL_CIPHER_DEFAULT;
3492 	uint strindexlen;
3493 	ulong intindex;
3494 	char * strindex;
3495 	char * infilename = NULL;	int infilename_len;
3496 	char * outfilename = NULL;	int outfilename_len;
3497 
3498 	RETVAL_FALSE;
3499 
3500 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssZa!|ll", &infilename, &infilename_len,
3501 				&outfilename, &outfilename_len, &zrecipcerts, &zheaders, &flags, &cipherid) == FAILURE)
3502 		return;
3503 
3504 	if (strlen(infilename) != infilename_len) {
3505 		return;
3506 	}
3507 
3508 	if (strlen(outfilename) != outfilename_len) {
3509 		return;
3510 	}
3511 
3512 	if (php_openssl_safe_mode_chk(infilename TSRMLS_CC) || php_openssl_safe_mode_chk(outfilename TSRMLS_CC)) {
3513 		return;
3514 	}
3515 
3516 	infile = BIO_new_file(infilename, "r");
3517 	if (infile == NULL) {
3518 		goto clean_exit;
3519 	}
3520 
3521 	outfile = BIO_new_file(outfilename, "w");
3522 	if (outfile == NULL) {
3523 		goto clean_exit;
3524 	}
3525 
3526 	recipcerts = sk_X509_new_null();
3527 
3528 	/* get certs */
3529 	if (Z_TYPE_PP(zrecipcerts) == IS_ARRAY) {
3530 		zend_hash_internal_pointer_reset_ex(HASH_OF(*zrecipcerts), &hpos);
3531 		while(zend_hash_get_current_data_ex(HASH_OF(*zrecipcerts), (void**)&zcertval, &hpos) == SUCCESS) {
3532 			long certresource;
3533 
3534 			cert = php_openssl_x509_from_zval(zcertval, 0, &certresource TSRMLS_CC);
3535 			if (cert == NULL) {
3536 				goto clean_exit;
3537 			}
3538 
3539 			if (certresource != -1) {
3540 				/* we shouldn't free this particular cert, as it is a resource.
3541 					make a copy and push that on the stack instead */
3542 				cert = X509_dup(cert);
3543 				if (cert == NULL) {
3544 					goto clean_exit;
3545 				}
3546 			}
3547 			sk_X509_push(recipcerts, cert);
3548 
3549 			zend_hash_move_forward_ex(HASH_OF(*zrecipcerts), &hpos);
3550 		}
3551 	} else {
3552 		/* a single certificate */
3553 		long certresource;
3554 
3555 		cert = php_openssl_x509_from_zval(zrecipcerts, 0, &certresource TSRMLS_CC);
3556 		if (cert == NULL) {
3557 			goto clean_exit;
3558 		}
3559 
3560 		if (certresource != -1) {
3561 			/* we shouldn't free this particular cert, as it is a resource.
3562 				make a copy and push that on the stack instead */
3563 			cert = X509_dup(cert);
3564 			if (cert == NULL) {
3565 				goto clean_exit;
3566 			}
3567 		}
3568 		sk_X509_push(recipcerts, cert);
3569 	}
3570 
3571 	/* sanity check the cipher */
3572 	cipher = php_openssl_get_evp_cipher_from_algo(cipherid);
3573 	if (cipher == NULL) {
3574 		/* shouldn't happen */
3575 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get cipher");
3576 		goto clean_exit;
3577 	}
3578 
3579 	p7 = PKCS7_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, flags);
3580 
3581 	if (p7 == NULL) {
3582 		goto clean_exit;
3583 	}
3584 
3585 	/* tack on extra headers */
3586 	if (zheaders) {
3587 		zend_hash_internal_pointer_reset_ex(HASH_OF(zheaders), &hpos);
3588 		while(zend_hash_get_current_data_ex(HASH_OF(zheaders), (void**)&zcertval, &hpos) == SUCCESS) {
3589 			strindex = NULL;
3590 			zend_hash_get_current_key_ex(HASH_OF(zheaders), &strindex, &strindexlen, &intindex, 0, &hpos);
3591 
3592 			convert_to_string_ex(zcertval);
3593 
3594 			if (strindex) {
3595 				BIO_printf(outfile, "%s: %s\n", strindex, Z_STRVAL_PP(zcertval));
3596 			} else {
3597 				BIO_printf(outfile, "%s\n", Z_STRVAL_PP(zcertval));
3598 			}
3599 
3600 			zend_hash_move_forward_ex(HASH_OF(zheaders), &hpos);
3601 		}
3602 	}
3603 
3604 	(void)BIO_reset(infile);
3605 
3606 	/* write the encrypted data */
3607 	SMIME_write_PKCS7(outfile, p7, infile, flags);
3608 
3609 	RETVAL_TRUE;
3610 
3611 clean_exit:
3612 	PKCS7_free(p7);
3613 	BIO_free(infile);
3614 	BIO_free(outfile);
3615 	if (recipcerts) {
3616 		sk_X509_pop_free(recipcerts, X509_free);
3617 	}
3618 }
3619 /* }}} */
3620 
3621 /* {{{ proto bool openssl_pkcs7_sign(string infile, string outfile, mixed signcert, mixed signkey, array headers [, long flags [, string extracertsfilename]])
3622    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 */
3623 
PHP_FUNCTION(openssl_pkcs7_sign)3624 PHP_FUNCTION(openssl_pkcs7_sign)
3625 {
3626 	zval ** zcert, ** zprivkey, * zheaders;
3627 	zval ** hval;
3628 	X509 * cert = NULL;
3629 	EVP_PKEY * privkey = NULL;
3630 	long flags = PKCS7_DETACHED;
3631 	PKCS7 * p7 = NULL;
3632 	BIO * infile = NULL, * outfile = NULL;
3633 	STACK_OF(X509) *others = NULL;
3634 	long certresource = -1, keyresource = -1;
3635 	ulong intindex;
3636 	uint strindexlen;
3637 	HashPosition hpos;
3638 	char * strindex;
3639 	char * infilename;	int infilename_len;
3640 	char * outfilename;	int outfilename_len;
3641 	char * extracertsfilename = NULL; int extracertsfilename_len;
3642 
3643 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssZZa!|ls",
3644 				&infilename, &infilename_len, &outfilename, &outfilename_len,
3645 				&zcert, &zprivkey, &zheaders, &flags, &extracertsfilename,
3646 				&extracertsfilename_len) == FAILURE) {
3647 		return;
3648 	}
3649 	RETVAL_FALSE;
3650 
3651 	if (strlen(infilename) != infilename_len) {
3652 		return;
3653 	}
3654 
3655 	if (strlen(outfilename) != outfilename_len) {
3656 		return;
3657 	}
3658 
3659 	if (extracertsfilename) {
3660 		others = load_all_certs_from_file(extracertsfilename);
3661 		if (others == NULL) {
3662 			goto clean_exit;
3663 		}
3664 	}
3665 
3666 	privkey = php_openssl_evp_from_zval(zprivkey, 0, "", 0, &keyresource TSRMLS_CC);
3667 	if (privkey == NULL) {
3668 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "error getting private key");
3669 		goto clean_exit;
3670 	}
3671 
3672 	cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
3673 	if (cert == NULL) {
3674 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "error getting cert");
3675 		goto clean_exit;
3676 	}
3677 
3678 	if (php_openssl_safe_mode_chk(infilename TSRMLS_CC) || php_openssl_safe_mode_chk(outfilename TSRMLS_CC)) {
3679 		goto clean_exit;
3680 	}
3681 
3682 	infile = BIO_new_file(infilename, "r");
3683 	if (infile == NULL) {
3684 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening input file %s!", infilename);
3685 		goto clean_exit;
3686 	}
3687 
3688 	outfile = BIO_new_file(outfilename, "w");
3689 	if (outfile == NULL) {
3690 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening output file %s!", outfilename);
3691 		goto clean_exit;
3692 	}
3693 
3694 	p7 = PKCS7_sign(cert, privkey, others, infile, flags);
3695 	if (p7 == NULL) {
3696 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "error creating PKCS7 structure!");
3697 		goto clean_exit;
3698 	}
3699 
3700 	(void)BIO_reset(infile);
3701 
3702 	/* tack on extra headers */
3703 	if (zheaders) {
3704 		zend_hash_internal_pointer_reset_ex(HASH_OF(zheaders), &hpos);
3705 		while(zend_hash_get_current_data_ex(HASH_OF(zheaders), (void**)&hval, &hpos) == SUCCESS) {
3706 			strindex = NULL;
3707 			zend_hash_get_current_key_ex(HASH_OF(zheaders), &strindex, &strindexlen, &intindex, 0, &hpos);
3708 
3709 			convert_to_string_ex(hval);
3710 
3711 			if (strindex) {
3712 				BIO_printf(outfile, "%s: %s\n", strindex, Z_STRVAL_PP(hval));
3713 			} else {
3714 				BIO_printf(outfile, "%s\n", Z_STRVAL_PP(hval));
3715 			}
3716 			zend_hash_move_forward_ex(HASH_OF(zheaders), &hpos);
3717 		}
3718 	}
3719 	/* write the signed data */
3720 	SMIME_write_PKCS7(outfile, p7, infile, flags);
3721 
3722 	RETVAL_TRUE;
3723 
3724 clean_exit:
3725 	PKCS7_free(p7);
3726 	BIO_free(infile);
3727 	BIO_free(outfile);
3728 	if (others) {
3729 		sk_X509_pop_free(others, X509_free);
3730 	}
3731 	if (privkey && keyresource == -1) {
3732 		EVP_PKEY_free(privkey);
3733 	}
3734 	if (cert && certresource == -1) {
3735 		X509_free(cert);
3736 	}
3737 }
3738 /* }}} */
3739 
3740 /* {{{ proto bool openssl_pkcs7_decrypt(string infilename, string outfilename, mixed recipcert [, mixed recipkey])
3741    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 */
3742 
PHP_FUNCTION(openssl_pkcs7_decrypt)3743 PHP_FUNCTION(openssl_pkcs7_decrypt)
3744 {
3745 	zval ** recipcert, ** recipkey = NULL;
3746 	X509 * cert = NULL;
3747 	EVP_PKEY * key = NULL;
3748 	long certresval, keyresval;
3749 	BIO * in = NULL, * out = NULL, * datain = NULL;
3750 	PKCS7 * p7 = NULL;
3751 	char * infilename;	int infilename_len;
3752 	char * outfilename;	int outfilename_len;
3753 
3754 	RETVAL_FALSE;
3755 
3756 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssZ|Z", &infilename, &infilename_len,
3757 				&outfilename, &outfilename_len, &recipcert, &recipkey) == FAILURE) {
3758 		return;
3759 	}
3760 
3761 	if (strlen(infilename) != infilename_len) {
3762 		return;
3763 	}
3764 
3765 	if (strlen(outfilename) != outfilename_len) {
3766 		return;
3767 	}
3768 
3769 	cert = php_openssl_x509_from_zval(recipcert, 0, &certresval TSRMLS_CC);
3770 	if (cert == NULL) {
3771 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to coerce parameter 3 to x509 cert");
3772 		goto clean_exit;
3773 	}
3774 
3775 	key = php_openssl_evp_from_zval(recipkey ? recipkey : recipcert, 0, "", 0, &keyresval TSRMLS_CC);
3776 	if (key == NULL) {
3777 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to get private key");
3778 		goto clean_exit;
3779 	}
3780 
3781 	if (php_openssl_safe_mode_chk(infilename TSRMLS_CC) || php_openssl_safe_mode_chk(outfilename TSRMLS_CC)) {
3782 		goto clean_exit;
3783 	}
3784 
3785 	in = BIO_new_file(infilename, "r");
3786 	if (in == NULL) {
3787 		goto clean_exit;
3788 	}
3789 	out = BIO_new_file(outfilename, "w");
3790 	if (out == NULL) {
3791 		goto clean_exit;
3792 	}
3793 
3794 	p7 = SMIME_read_PKCS7(in, &datain);
3795 
3796 	if (p7 == NULL) {
3797 		goto clean_exit;
3798 	}
3799 	if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED)) {
3800 		RETVAL_TRUE;
3801 	}
3802 clean_exit:
3803 	PKCS7_free(p7);
3804 	BIO_free(datain);
3805 	BIO_free(in);
3806 	BIO_free(out);
3807 	if (cert && certresval == -1) {
3808 		X509_free(cert);
3809 	}
3810 	if (key && keyresval == -1) {
3811 		EVP_PKEY_free(key);
3812 	}
3813 }
3814 /* }}} */
3815 
3816 /* }}} */
3817 
3818 /* {{{ proto bool openssl_private_encrypt(string data, string &crypted, mixed key [, int padding])
3819    Encrypts data with private key */
PHP_FUNCTION(openssl_private_encrypt)3820 PHP_FUNCTION(openssl_private_encrypt)
3821 {
3822 	zval **key, *crypted;
3823 	EVP_PKEY *pkey;
3824 	int cryptedlen;
3825 	unsigned char *cryptedbuf = NULL;
3826 	int successful = 0;
3827 	long keyresource = -1;
3828 	char * data;
3829 	int data_len;
3830 	long padding = RSA_PKCS1_PADDING;
3831 
3832 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
3833 		return;
3834 	}
3835 	RETVAL_FALSE;
3836 
3837 	pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC);
3838 
3839 	if (pkey == NULL) {
3840 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "key param is not a valid private key");
3841 		RETURN_FALSE;
3842 	}
3843 
3844 	cryptedlen = EVP_PKEY_size(pkey);
3845 	cryptedbuf = emalloc(cryptedlen + 1);
3846 
3847 	switch (pkey->type) {
3848 		case EVP_PKEY_RSA:
3849 		case EVP_PKEY_RSA2:
3850 			successful =  (RSA_private_encrypt(data_len,
3851 						(unsigned char *)data,
3852 						cryptedbuf,
3853 						pkey->pkey.rsa,
3854 						padding) == cryptedlen);
3855 			break;
3856 		default:
3857 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
3858 	}
3859 
3860 	if (successful) {
3861 		zval_dtor(crypted);
3862 		cryptedbuf[cryptedlen] = '\0';
3863 		ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0);
3864 		cryptedbuf = NULL;
3865 		RETVAL_TRUE;
3866 	}
3867 	if (cryptedbuf) {
3868 		efree(cryptedbuf);
3869 	}
3870 	if (keyresource == -1) {
3871 		EVP_PKEY_free(pkey);
3872 	}
3873 }
3874 /* }}} */
3875 
3876 /* {{{ proto bool openssl_private_decrypt(string data, string &decrypted, mixed key [, int padding])
3877    Decrypts data with private key */
PHP_FUNCTION(openssl_private_decrypt)3878 PHP_FUNCTION(openssl_private_decrypt)
3879 {
3880 	zval **key, *crypted;
3881 	EVP_PKEY *pkey;
3882 	int cryptedlen;
3883 	unsigned char *cryptedbuf = NULL;
3884 	unsigned char *crypttemp;
3885 	int successful = 0;
3886 	long padding = RSA_PKCS1_PADDING;
3887 	long keyresource = -1;
3888 	char * data;
3889 	int data_len;
3890 
3891 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
3892 		return;
3893 	}
3894 	RETVAL_FALSE;
3895 
3896 	pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC);
3897 	if (pkey == NULL) {
3898 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid private key");
3899 		RETURN_FALSE;
3900 	}
3901 
3902 	cryptedlen = EVP_PKEY_size(pkey);
3903 	crypttemp = emalloc(cryptedlen + 1);
3904 
3905 	switch (pkey->type) {
3906 		case EVP_PKEY_RSA:
3907 		case EVP_PKEY_RSA2:
3908 			cryptedlen = RSA_private_decrypt(data_len,
3909 					(unsigned char *)data,
3910 					crypttemp,
3911 					pkey->pkey.rsa,
3912 					padding);
3913 			if (cryptedlen != -1) {
3914 				cryptedbuf = emalloc(cryptedlen + 1);
3915 				memcpy(cryptedbuf, crypttemp, cryptedlen);
3916 				successful = 1;
3917 			}
3918 			break;
3919 		default:
3920 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
3921 	}
3922 
3923 	efree(crypttemp);
3924 
3925 	if (successful) {
3926 		zval_dtor(crypted);
3927 		cryptedbuf[cryptedlen] = '\0';
3928 		ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0);
3929 		cryptedbuf = NULL;
3930 		RETVAL_TRUE;
3931 	}
3932 
3933 	if (keyresource == -1) {
3934 		EVP_PKEY_free(pkey);
3935 	}
3936 	if (cryptedbuf) {
3937 		efree(cryptedbuf);
3938 	}
3939 }
3940 /* }}} */
3941 
3942 /* {{{ proto bool openssl_public_encrypt(string data, string &crypted, mixed key [, int padding])
3943    Encrypts data with public key */
PHP_FUNCTION(openssl_public_encrypt)3944 PHP_FUNCTION(openssl_public_encrypt)
3945 {
3946 	zval **key, *crypted;
3947 	EVP_PKEY *pkey;
3948 	int cryptedlen;
3949 	unsigned char *cryptedbuf;
3950 	int successful = 0;
3951 	long keyresource = -1;
3952 	long padding = RSA_PKCS1_PADDING;
3953 	char * data;
3954 	int data_len;
3955 
3956 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
3957 		return;
3958 
3959 	RETVAL_FALSE;
3960 
3961 	pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC);
3962 	if (pkey == NULL) {
3963 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid public key");
3964 		RETURN_FALSE;
3965 	}
3966 
3967 	cryptedlen = EVP_PKEY_size(pkey);
3968 	cryptedbuf = emalloc(cryptedlen + 1);
3969 
3970 	switch (pkey->type) {
3971 		case EVP_PKEY_RSA:
3972 		case EVP_PKEY_RSA2:
3973 			successful = (RSA_public_encrypt(data_len,
3974 						(unsigned char *)data,
3975 						cryptedbuf,
3976 						pkey->pkey.rsa,
3977 						padding) == cryptedlen);
3978 			break;
3979 		default:
3980 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
3981 
3982 	}
3983 
3984 	if (successful) {
3985 		zval_dtor(crypted);
3986 		cryptedbuf[cryptedlen] = '\0';
3987 		ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0);
3988 		cryptedbuf = NULL;
3989 		RETVAL_TRUE;
3990 	}
3991 	if (keyresource == -1) {
3992 		EVP_PKEY_free(pkey);
3993 	}
3994 	if (cryptedbuf) {
3995 		efree(cryptedbuf);
3996 	}
3997 }
3998 /* }}} */
3999 
4000 /* {{{ proto bool openssl_public_decrypt(string data, string &crypted, resource key [, int padding])
4001    Decrypts data with public key */
PHP_FUNCTION(openssl_public_decrypt)4002 PHP_FUNCTION(openssl_public_decrypt)
4003 {
4004 	zval **key, *crypted;
4005 	EVP_PKEY *pkey;
4006 	int cryptedlen;
4007 	unsigned char *cryptedbuf = NULL;
4008 	unsigned char *crypttemp;
4009 	int successful = 0;
4010 	long keyresource = -1;
4011 	long padding = RSA_PKCS1_PADDING;
4012 	char * data;
4013 	int data_len;
4014 
4015 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
4016 		return;
4017 	}
4018 	RETVAL_FALSE;
4019 
4020 	pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC);
4021 	if (pkey == NULL) {
4022 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid public key");
4023 		RETURN_FALSE;
4024 	}
4025 
4026 	cryptedlen = EVP_PKEY_size(pkey);
4027 	crypttemp = emalloc(cryptedlen + 1);
4028 
4029 	switch (pkey->type) {
4030 		case EVP_PKEY_RSA:
4031 		case EVP_PKEY_RSA2:
4032 			cryptedlen = RSA_public_decrypt(data_len,
4033 					(unsigned char *)data,
4034 					crypttemp,
4035 					pkey->pkey.rsa,
4036 					padding);
4037 			if (cryptedlen != -1) {
4038 				cryptedbuf = emalloc(cryptedlen + 1);
4039 				memcpy(cryptedbuf, crypttemp, cryptedlen);
4040 				successful = 1;
4041 			}
4042 			break;
4043 
4044 		default:
4045 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
4046 
4047 	}
4048 
4049 	efree(crypttemp);
4050 
4051 	if (successful) {
4052 		zval_dtor(crypted);
4053 		cryptedbuf[cryptedlen] = '\0';
4054 		ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0);
4055 		cryptedbuf = NULL;
4056 		RETVAL_TRUE;
4057 	}
4058 
4059 	if (cryptedbuf) {
4060 		efree(cryptedbuf);
4061 	}
4062 	if (keyresource == -1) {
4063 		EVP_PKEY_free(pkey);
4064 	}
4065 }
4066 /* }}} */
4067 
4068 /* {{{ proto mixed openssl_error_string(void)
4069    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)4070 PHP_FUNCTION(openssl_error_string)
4071 {
4072 	char buf[512];
4073 	unsigned long val;
4074 
4075 	if (zend_parse_parameters_none() == FAILURE) {
4076 		return;
4077 	}
4078 
4079 	val = ERR_get_error();
4080 	if (val) {
4081 		RETURN_STRING(ERR_error_string(val, buf), 1);
4082 	} else {
4083 		RETURN_FALSE;
4084 	}
4085 }
4086 /* }}} */
4087 
4088 /* {{{ proto bool openssl_sign(string data, &string signature, mixed key[, mixed method])
4089    Signs data */
PHP_FUNCTION(openssl_sign)4090 PHP_FUNCTION(openssl_sign)
4091 {
4092 	zval **key, *signature;
4093 	EVP_PKEY *pkey;
4094 	int siglen;
4095 	unsigned char *sigbuf;
4096 	long keyresource = -1;
4097 	char * data;
4098 	int data_len;
4099 	EVP_MD_CTX md_ctx;
4100 	zval *method = NULL;
4101 	long signature_algo = OPENSSL_ALGO_SHA1;
4102 	const EVP_MD *mdtype;
4103 
4104 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|z", &data, &data_len, &signature, &key, &method) == FAILURE) {
4105 		return;
4106 	}
4107 	pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC);
4108 	if (pkey == NULL) {
4109 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param cannot be coerced into a private key");
4110 		RETURN_FALSE;
4111 	}
4112 
4113 	if (method == NULL || Z_TYPE_P(method) == IS_LONG) {
4114 		if (method != NULL) {
4115 			signature_algo = Z_LVAL_P(method);
4116 		}
4117 		mdtype = php_openssl_get_evp_md_from_algo(signature_algo);
4118 	} else if (Z_TYPE_P(method) == IS_STRING) {
4119 		mdtype = EVP_get_digestbyname(Z_STRVAL_P(method));
4120 	} else {
4121 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
4122 		RETURN_FALSE;
4123 	}
4124 	if (!mdtype) {
4125 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
4126 		RETURN_FALSE;
4127 	}
4128 
4129 	siglen = EVP_PKEY_size(pkey);
4130 	sigbuf = emalloc(siglen + 1);
4131 
4132 	EVP_SignInit(&md_ctx, mdtype);
4133 	EVP_SignUpdate(&md_ctx, data, data_len);
4134 	if (EVP_SignFinal (&md_ctx, sigbuf,(unsigned int *)&siglen, pkey)) {
4135 		zval_dtor(signature);
4136 		sigbuf[siglen] = '\0';
4137 		ZVAL_STRINGL(signature, (char *)sigbuf, siglen, 0);
4138 		RETVAL_TRUE;
4139 	} else {
4140 		efree(sigbuf);
4141 		RETVAL_FALSE;
4142 	}
4143 	EVP_MD_CTX_cleanup(&md_ctx);
4144 	if (keyresource == -1) {
4145 		EVP_PKEY_free(pkey);
4146 	}
4147 }
4148 /* }}} */
4149 
4150 /* {{{ proto int openssl_verify(string data, string signature, mixed key[, mixed method])
4151    Verifys data */
PHP_FUNCTION(openssl_verify)4152 PHP_FUNCTION(openssl_verify)
4153 {
4154 	zval **key;
4155 	EVP_PKEY *pkey;
4156 	int err;
4157 	EVP_MD_CTX     md_ctx;
4158 	const EVP_MD *mdtype;
4159 	long keyresource = -1;
4160 	char * data;	int data_len;
4161 	char * signature;	int signature_len;
4162 	zval *method = NULL;
4163 	long signature_algo = OPENSSL_ALGO_SHA1;
4164 
4165 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssZ|z", &data, &data_len, &signature, &signature_len, &key, &method) == FAILURE) {
4166 		return;
4167 	}
4168 
4169 	if (method == NULL || Z_TYPE_P(method) == IS_LONG) {
4170 		if (method != NULL) {
4171 			signature_algo = Z_LVAL_P(method);
4172 		}
4173 		mdtype = php_openssl_get_evp_md_from_algo(signature_algo);
4174 	} else if (Z_TYPE_P(method) == IS_STRING) {
4175 		mdtype = EVP_get_digestbyname(Z_STRVAL_P(method));
4176 	} else {
4177 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
4178 		RETURN_FALSE;
4179 	}
4180 	if (!mdtype) {
4181 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
4182 		RETURN_FALSE;
4183 	}
4184 
4185 	pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC);
4186 	if (pkey == NULL) {
4187 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param cannot be coerced into a public key");
4188 		RETURN_FALSE;
4189 	}
4190 
4191 	EVP_VerifyInit   (&md_ctx, mdtype);
4192 	EVP_VerifyUpdate (&md_ctx, data, data_len);
4193 	err = EVP_VerifyFinal (&md_ctx, (unsigned char *)signature, signature_len, pkey);
4194 	EVP_MD_CTX_cleanup(&md_ctx);
4195 
4196 	if (keyresource == -1) {
4197 		EVP_PKEY_free(pkey);
4198 	}
4199 	RETURN_LONG(err);
4200 }
4201 /* }}} */
4202 
4203 /* {{{ proto int openssl_seal(string data, &string sealdata, &array ekeys, array pubkeys)
4204    Seals data */
PHP_FUNCTION(openssl_seal)4205 PHP_FUNCTION(openssl_seal)
4206 {
4207 	zval *pubkeys, **pubkey, *sealdata, *ekeys;
4208 	HashTable *pubkeysht;
4209 	HashPosition pos;
4210 	EVP_PKEY **pkeys;
4211 	long * key_resources;	/* so we know what to cleanup */
4212 	int i, len1, len2, *eksl, nkeys;
4213 	unsigned char *buf = NULL, **eks;
4214 	char * data; int data_len;
4215 	char *method =NULL;
4216 	int method_len = 0;
4217 	const EVP_CIPHER *cipher;
4218 	EVP_CIPHER_CTX ctx;
4219 
4220 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szza/|s", &data, &data_len, &sealdata, &ekeys, &pubkeys, &method, &method_len) == FAILURE) {
4221 		return;
4222 	}
4223 
4224 	pubkeysht = HASH_OF(pubkeys);
4225 	nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0;
4226 	if (!nkeys) {
4227 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Fourth argument to openssl_seal() must be a non-empty array");
4228 		RETURN_FALSE;
4229 	}
4230 
4231 	if (method) {
4232 		cipher = EVP_get_cipherbyname(method);
4233 		if (!cipher) {
4234 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
4235 			RETURN_FALSE;
4236 		}
4237 	} else {
4238 		cipher = EVP_rc4();
4239 	}
4240 
4241 	pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0);
4242 	eksl = safe_emalloc(nkeys, sizeof(*eksl), 0);
4243 	eks = safe_emalloc(nkeys, sizeof(*eks), 0);
4244 	memset(eks, 0, sizeof(*eks) * nkeys);
4245 	key_resources = safe_emalloc(nkeys, sizeof(long), 0);
4246 	memset(key_resources, 0, sizeof(*key_resources) * nkeys);
4247 
4248 	/* get the public keys we are using to seal this data */
4249 	zend_hash_internal_pointer_reset_ex(pubkeysht, &pos);
4250 	i = 0;
4251 	while (zend_hash_get_current_data_ex(pubkeysht, (void **) &pubkey,
4252 				&pos) == SUCCESS) {
4253 		pkeys[i] = php_openssl_evp_from_zval(pubkey, 1, NULL, 0, &key_resources[i] TSRMLS_CC);
4254 		if (pkeys[i] == NULL) {
4255 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "not a public key (%dth member of pubkeys)", i+1);
4256 			RETVAL_FALSE;
4257 			goto clean_exit;
4258 		}
4259 		eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1);
4260 		zend_hash_move_forward_ex(pubkeysht, &pos);
4261 		i++;
4262 	}
4263 
4264 	if (!EVP_EncryptInit(&ctx,cipher,NULL,NULL)) {
4265 		RETVAL_FALSE;
4266 		goto clean_exit;
4267 	}
4268 
4269 #if 0
4270 	/* Need this if allow ciphers that require initialization vector */
4271 	ivlen = EVP_CIPHER_CTX_iv_length(&ctx);
4272 	iv = ivlen ? emalloc(ivlen + 1) : NULL;
4273 #endif
4274 	/* allocate one byte extra to make room for \0 */
4275 	buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(&ctx));
4276 
4277 	if (!EVP_SealInit(&ctx, cipher, eks, eksl, NULL, pkeys, nkeys) || !EVP_SealUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) {
4278 		RETVAL_FALSE;
4279 		efree(buf);
4280 		goto clean_exit;
4281 	}
4282 
4283 	EVP_SealFinal(&ctx, buf + len1, &len2);
4284 
4285 	if (len1 + len2 > 0) {
4286 		zval_dtor(sealdata);
4287 		buf[len1 + len2] = '\0';
4288 		buf = erealloc(buf, len1 + len2 + 1);
4289 		ZVAL_STRINGL(sealdata, (char *)buf, len1 + len2, 0);
4290 
4291 		zval_dtor(ekeys);
4292 		array_init(ekeys);
4293 		for (i=0; i<nkeys; i++) {
4294 			eks[i][eksl[i]] = '\0';
4295 			add_next_index_stringl(ekeys, erealloc(eks[i], eksl[i] + 1), eksl[i], 0);
4296 			eks[i] = NULL;
4297 		}
4298 #if 0
4299 		/* If allow ciphers that need IV, we need this */
4300 		zval_dtor(*ivec);
4301 		if (ivlen) {
4302 			iv[ivlen] = '\0';
4303 			ZVAL_STRINGL(*ivec, erealloc(iv, ivlen + 1), ivlen, 0);
4304 		} else {
4305 			ZVAL_EMPTY_STRING(*ivec);
4306 		}
4307 #endif
4308 	} else {
4309 		efree(buf);
4310 	}
4311 	RETVAL_LONG(len1 + len2);
4312 
4313 clean_exit:
4314 	for (i=0; i<nkeys; i++) {
4315 		if (key_resources[i] == -1) {
4316 			EVP_PKEY_free(pkeys[i]);
4317 		}
4318 		if (eks[i]) {
4319 			efree(eks[i]);
4320 		}
4321 	}
4322 	efree(eks);
4323 	efree(eksl);
4324 	efree(pkeys);
4325 	efree(key_resources);
4326 }
4327 /* }}} */
4328 
4329 /* {{{ proto bool openssl_open(string data, &string opendata, string ekey, mixed privkey)
4330    Opens data */
PHP_FUNCTION(openssl_open)4331 PHP_FUNCTION(openssl_open)
4332 {
4333 	zval **privkey, *opendata;
4334 	EVP_PKEY *pkey;
4335 	int len1, len2;
4336 	unsigned char *buf;
4337 	long keyresource = -1;
4338 	EVP_CIPHER_CTX ctx;
4339 	char * data;	int data_len;
4340 	char * ekey;	int ekey_len;
4341 	char *method =NULL;
4342 	int method_len = 0;
4343 	const EVP_CIPHER *cipher;
4344 
4345 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szsZ|s", &data, &data_len, &opendata, &ekey, &ekey_len, &privkey, &method, &method_len) == FAILURE) {
4346 		return;
4347 	}
4348 
4349 	pkey = php_openssl_evp_from_zval(privkey, 0, "", 0, &keyresource TSRMLS_CC);
4350 	if (pkey == NULL) {
4351 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to coerce parameter 4 into a private key");
4352 		RETURN_FALSE;
4353 	}
4354 
4355 	if (method) {
4356 		cipher = EVP_get_cipherbyname(method);
4357 		if (!cipher) {
4358 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
4359 			RETURN_FALSE;
4360 		}
4361 	} else {
4362 		cipher = EVP_rc4();
4363 	}
4364 
4365 	buf = emalloc(data_len + 1);
4366 
4367 	if (EVP_OpenInit(&ctx, cipher, (unsigned char *)ekey, ekey_len, NULL, pkey) && EVP_OpenUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) {
4368 		if (!EVP_OpenFinal(&ctx, buf + len1, &len2) || (len1 + len2 == 0)) {
4369 			efree(buf);
4370 			if (keyresource == -1) {
4371 				EVP_PKEY_free(pkey);
4372 			}
4373 			RETURN_FALSE;
4374 		}
4375 	} else {
4376 		efree(buf);
4377 		if (keyresource == -1) {
4378 			EVP_PKEY_free(pkey);
4379 		}
4380 		RETURN_FALSE;
4381 	}
4382 	if (keyresource == -1) {
4383 		EVP_PKEY_free(pkey);
4384 	}
4385 	zval_dtor(opendata);
4386 	buf[len1 + len2] = '\0';
4387 	ZVAL_STRINGL(opendata, erealloc(buf, len1 + len2 + 1), len1 + len2, 0);
4388 	RETURN_TRUE;
4389 }
4390 /* }}} */
4391 
4392 /* SSL verification functions */
4393 
4394 #define GET_VER_OPT(name)               (stream->context && SUCCESS == php_stream_context_get_option(stream->context, "ssl", name, &val))
4395 #define GET_VER_OPT_STRING(name, str)   if (GET_VER_OPT(name)) { convert_to_string_ex(val); str = Z_STRVAL_PP(val); }
4396 
verify_callback(int preverify_ok,X509_STORE_CTX * ctx)4397 static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) /* {{{ */
4398 {
4399 	php_stream *stream;
4400 	SSL *ssl;
4401 	X509 *err_cert;
4402 	int err, depth, ret;
4403 	zval **val;
4404 
4405 	ret = preverify_ok;
4406 
4407 	/* determine the status for the current cert */
4408 	err_cert = X509_STORE_CTX_get_current_cert(ctx);
4409 	err = X509_STORE_CTX_get_error(ctx);
4410 	depth = X509_STORE_CTX_get_error_depth(ctx);
4411 
4412 	/* conjure the stream & context to use */
4413 	ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
4414 	stream = (php_stream*)SSL_get_ex_data(ssl, ssl_stream_data_index);
4415 
4416 	/* if allow_self_signed is set, make sure that verification succeeds */
4417 	if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && GET_VER_OPT("allow_self_signed") && zval_is_true(*val)) {
4418 		ret = 1;
4419 	}
4420 
4421 	/* check the depth */
4422 	if (GET_VER_OPT("verify_depth")) {
4423 		convert_to_long_ex(val);
4424 
4425 		if (depth > Z_LVAL_PP(val)) {
4426 			ret = 0;
4427 			X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG);
4428 		}
4429 	}
4430 
4431 	return ret;
4432 
4433 }
4434 /* }}} */
4435 
php_openssl_apply_verification_policy(SSL * ssl,X509 * peer,php_stream * stream TSRMLS_DC)4436 int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stream TSRMLS_DC) /* {{{ */
4437 {
4438 	zval **val = NULL;
4439 	char *cnmatch = NULL;
4440 	X509_NAME *name;
4441 	char buf[1024];
4442 	int err;
4443 
4444 	/* verification is turned off */
4445 	if (!(GET_VER_OPT("verify_peer") && zval_is_true(*val))) {
4446 		return SUCCESS;
4447 	}
4448 
4449 	if (peer == NULL) {
4450 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not get peer certificate");
4451 		return FAILURE;
4452 	}
4453 
4454 	err = SSL_get_verify_result(ssl);
4455 	switch (err) {
4456 		case X509_V_OK:
4457 			/* fine */
4458 			break;
4459 		case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
4460 			if (GET_VER_OPT("allow_self_signed") && zval_is_true(*val)) {
4461 				/* allowed */
4462 				break;
4463 			}
4464 			/* not allowed, so fall through */
4465 		default:
4466 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not verify peer: code:%d %s", err, X509_verify_cert_error_string(err));
4467 			return FAILURE;
4468 	}
4469 
4470 	/* if the cert passed the usual checks, apply our own local policies now */
4471 
4472 	name = X509_get_subject_name(peer);
4473 
4474 	/* Does the common name match ? (used primarily for https://) */
4475 	GET_VER_OPT_STRING("CN_match", cnmatch);
4476 	if (cnmatch) {
4477 		int match = 0;
4478 		int name_len = X509_NAME_get_text_by_NID(name, NID_commonName, buf, sizeof(buf));
4479 
4480 		if (name_len == -1) {
4481 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to locate peer certificate CN");
4482 			return FAILURE;
4483 		} else if (name_len != strlen(buf)) {
4484 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' is malformed", name_len, buf);
4485 			return FAILURE;
4486 		}
4487 
4488 		match = strcmp(cnmatch, buf) == 0;
4489 		if (!match && strlen(buf) > 3 && buf[0] == '*' && buf[1] == '.') {
4490 			/* Try wildcard */
4491 
4492 			if (strchr(buf+2, '.')) {
4493 				char *tmp = strstr(cnmatch, buf+1);
4494 
4495 				match = tmp && strcmp(tmp, buf+2) && tmp == strchr(cnmatch, '.');
4496 			}
4497 		}
4498 
4499 		if (!match) {
4500 			/* didn't match */
4501 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' did not match expected CN=`%s'", name_len, buf, cnmatch);
4502 			return FAILURE;
4503 		}
4504 	}
4505 
4506 	return SUCCESS;
4507 }
4508 /* }}} */
4509 
passwd_callback(char * buf,int num,int verify,void * data)4510 static int passwd_callback(char *buf, int num, int verify, void *data) /* {{{ */
4511 {
4512     php_stream *stream = (php_stream *)data;
4513     zval **val = NULL;
4514     char *passphrase = NULL;
4515     /* TODO: could expand this to make a callback into PHP user-space */
4516 
4517     GET_VER_OPT_STRING("passphrase", passphrase);
4518 
4519     if (passphrase) {
4520         if (Z_STRLEN_PP(val) < num - 1) {
4521             memcpy(buf, Z_STRVAL_PP(val), Z_STRLEN_PP(val)+1);
4522             return Z_STRLEN_PP(val);
4523         }
4524     }
4525     return 0;
4526 }
4527 /* }}} */
4528 
php_SSL_new_from_context(SSL_CTX * ctx,php_stream * stream TSRMLS_DC)4529 SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC) /* {{{ */
4530 {
4531 	zval **val = NULL;
4532 	char *cafile = NULL;
4533 	char *capath = NULL;
4534 	char *certfile = NULL;
4535 	char *cipherlist = NULL;
4536 	int ok = 1;
4537 
4538 	ERR_clear_error();
4539 
4540 	/* look at context options in the stream and set appropriate verification flags */
4541 	if (GET_VER_OPT("verify_peer") && zval_is_true(*val)) {
4542 
4543 		/* turn on verification callback */
4544 		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
4545 
4546 		/* CA stuff */
4547 		GET_VER_OPT_STRING("cafile", cafile);
4548 		GET_VER_OPT_STRING("capath", capath);
4549 
4550 		if (cafile || capath) {
4551 			if (!SSL_CTX_load_verify_locations(ctx, cafile, capath)) {
4552 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set verify locations `%s' `%s'", cafile, capath);
4553 				return NULL;
4554 			}
4555 		}
4556 
4557 		if (GET_VER_OPT("verify_depth")) {
4558 			convert_to_long_ex(val);
4559 			SSL_CTX_set_verify_depth(ctx, Z_LVAL_PP(val));
4560 		}
4561 	} else {
4562 		SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
4563 	}
4564 
4565 	/* callback for the passphrase (for localcert) */
4566 	if (GET_VER_OPT("passphrase")) {
4567 		SSL_CTX_set_default_passwd_cb_userdata(ctx, stream);
4568 		SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
4569 	}
4570 
4571 	GET_VER_OPT_STRING("ciphers", cipherlist);
4572 	if (!cipherlist) {
4573 		cipherlist = "DEFAULT";
4574 	}
4575 	if (SSL_CTX_set_cipher_list(ctx, cipherlist) != 1) {
4576 		return NULL;
4577 	}
4578 
4579 	GET_VER_OPT_STRING("local_cert", certfile);
4580 	if (certfile) {
4581 		X509 *cert = NULL;
4582 		EVP_PKEY *key = NULL;
4583 		SSL *tmpssl;
4584 		char resolved_path_buff[MAXPATHLEN];
4585 		const char * private_key = NULL;
4586 
4587 		if (VCWD_REALPATH(certfile, resolved_path_buff)) {
4588 			/* a certificate to use for authentication */
4589 			if (SSL_CTX_use_certificate_chain_file(ctx, resolved_path_buff) != 1) {
4590 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set local cert chain file `%s'; Check that your cafile/capath settings include details of your certificate and its issuer", certfile);
4591 				return NULL;
4592 			}
4593 			GET_VER_OPT_STRING("local_pk", private_key);
4594 
4595 			if (private_key) {
4596 				char resolved_path_buff_pk[MAXPATHLEN];
4597 				if (VCWD_REALPATH(private_key, resolved_path_buff_pk)) {
4598 					if (SSL_CTX_use_PrivateKey_file(ctx, resolved_path_buff_pk, SSL_FILETYPE_PEM) != 1) {
4599 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set private key file `%s'", resolved_path_buff_pk);
4600 						return NULL;
4601 					}
4602 				}
4603 			} else {
4604 				if (SSL_CTX_use_PrivateKey_file(ctx, resolved_path_buff, SSL_FILETYPE_PEM) != 1) {
4605 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set private key file `%s'", resolved_path_buff);
4606 					return NULL;
4607 				}
4608 			}
4609 
4610 			tmpssl = SSL_new(ctx);
4611 			cert = SSL_get_certificate(tmpssl);
4612 
4613 			if (cert) {
4614 				key = X509_get_pubkey(cert);
4615 				EVP_PKEY_copy_parameters(key, SSL_get_privatekey(tmpssl));
4616 				EVP_PKEY_free(key);
4617 			}
4618 			SSL_free(tmpssl);
4619 
4620 			if (!SSL_CTX_check_private_key(ctx)) {
4621 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Private key does not match certificate!");
4622 			}
4623 		}
4624 	}
4625 	if (ok) {
4626 		SSL *ssl = SSL_new(ctx);
4627 
4628 		if (ssl) {
4629 			/* map SSL => stream */
4630 			SSL_set_ex_data(ssl, ssl_stream_data_index, stream);
4631 		}
4632 		return ssl;
4633 	}
4634 
4635 	return NULL;
4636 }
4637 /* }}} */
4638 
openssl_add_method_or_alias(const OBJ_NAME * name,void * arg)4639 static void openssl_add_method_or_alias(const OBJ_NAME *name, void *arg) /* {{{ */
4640 {
4641 	add_next_index_string((zval*)arg, (char*)name->name, 1);
4642 }
4643 /* }}} */
4644 
openssl_add_method(const OBJ_NAME * name,void * arg)4645 static void openssl_add_method(const OBJ_NAME *name, void *arg) /* {{{ */
4646 {
4647 	if (name->alias == 0) {
4648 		add_next_index_string((zval*)arg, (char*)name->name, 1);
4649 	}
4650 }
4651 /* }}} */
4652 
4653 /* {{{ proto array openssl_get_md_methods([bool aliases = false])
4654    Return array of available digest methods */
PHP_FUNCTION(openssl_get_md_methods)4655 PHP_FUNCTION(openssl_get_md_methods)
4656 {
4657 	zend_bool aliases = 0;
4658 
4659 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &aliases) == FAILURE) {
4660 		return;
4661 	}
4662 	array_init(return_value);
4663 	OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH,
4664 		aliases ? openssl_add_method_or_alias: openssl_add_method,
4665 		return_value);
4666 }
4667 /* }}} */
4668 
4669 /* {{{ proto array openssl_get_cipher_methods([bool aliases = false])
4670    Return array of available cipher methods */
PHP_FUNCTION(openssl_get_cipher_methods)4671 PHP_FUNCTION(openssl_get_cipher_methods)
4672 {
4673 	zend_bool aliases = 0;
4674 
4675 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &aliases) == FAILURE) {
4676 		return;
4677 	}
4678 	array_init(return_value);
4679 	OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
4680 		aliases ? openssl_add_method_or_alias: openssl_add_method,
4681 		return_value);
4682 }
4683 /* }}} */
4684 
4685 /* {{{ proto string openssl_digest(string data, string method [, bool raw_output=false])
4686    Computes digest hash value for given data using given method, returns raw or binhex encoded string */
PHP_FUNCTION(openssl_digest)4687 PHP_FUNCTION(openssl_digest)
4688 {
4689 	zend_bool raw_output = 0;
4690 	char *data, *method;
4691 	int data_len, method_len;
4692 	const EVP_MD *mdtype;
4693 	EVP_MD_CTX md_ctx;
4694 	int siglen;
4695 	unsigned char *sigbuf;
4696 
4697 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) {
4698 		return;
4699 	}
4700 	mdtype = EVP_get_digestbyname(method);
4701 	if (!mdtype) {
4702 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm");
4703 		RETURN_FALSE;
4704 	}
4705 
4706 	siglen = EVP_MD_size(mdtype);
4707 	sigbuf = emalloc(siglen + 1);
4708 
4709 	EVP_DigestInit(&md_ctx, mdtype);
4710 	EVP_DigestUpdate(&md_ctx, (unsigned char *)data, data_len);
4711 	if (EVP_DigestFinal (&md_ctx, (unsigned char *)sigbuf, (unsigned int *)&siglen)) {
4712 		if (raw_output) {
4713 			sigbuf[siglen] = '\0';
4714 			RETVAL_STRINGL((char *)sigbuf, siglen, 0);
4715 		} else {
4716 			int digest_str_len = siglen * 2;
4717 			char *digest_str = emalloc(digest_str_len + 1);
4718 
4719 			make_digest_ex(digest_str, sigbuf, siglen);
4720 			efree(sigbuf);
4721 			RETVAL_STRINGL(digest_str, digest_str_len, 0);
4722 		}
4723 	} else {
4724 		efree(sigbuf);
4725 		RETVAL_FALSE;
4726 	}
4727 }
4728 /* }}} */
4729 
php_openssl_validate_iv(char ** piv,int * piv_len,int iv_required_len TSRMLS_DC)4730 static zend_bool php_openssl_validate_iv(char **piv, int *piv_len, int iv_required_len TSRMLS_DC)
4731 {
4732 	char *iv_new;
4733 
4734 	/* Best case scenario, user behaved */
4735 	if (*piv_len == iv_required_len) {
4736 		return 0;
4737 	}
4738 
4739 	iv_new = ecalloc(1, iv_required_len + 1);
4740 
4741 	if (*piv_len <= 0) {
4742 		/* BC behavior */
4743 		*piv_len = iv_required_len;
4744 		*piv     = iv_new;
4745 		return 1;
4746 	}
4747 
4748 	if (*piv_len < iv_required_len) {
4749 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "IV passed is only %d bytes long, cipher expects an IV of precisely %d bytes, padding with \\0", *piv_len, iv_required_len);
4750 		memcpy(iv_new, *piv, *piv_len);
4751 		*piv_len = iv_required_len;
4752 		*piv     = iv_new;
4753 		return 1;
4754 	}
4755 
4756 	php_error_docref(NULL TSRMLS_CC, E_WARNING, "IV passed is %d bytes long which is longer than the %d expected by selected cipher, truncating", *piv_len, iv_required_len);
4757 	memcpy(iv_new, *piv, iv_required_len);
4758 	*piv_len = iv_required_len;
4759 	*piv     = iv_new;
4760 	return 1;
4761 
4762 }
4763 
4764 /* {{{ proto string openssl_encrypt(string data, string method, string password [, bool raw_output=false [, string $iv='']])
4765    Encrypts given data with given method and key, returns raw or base64 encoded string */
PHP_FUNCTION(openssl_encrypt)4766 PHP_FUNCTION(openssl_encrypt)
4767 {
4768 	zend_bool raw_output = 0;
4769 	char *data, *method, *password, *iv = "";
4770 	int data_len, method_len, password_len, iv_len = 0, max_iv_len;
4771 	const EVP_CIPHER *cipher_type;
4772 	EVP_CIPHER_CTX cipher_ctx;
4773 	int i = 0, outlen, keylen;
4774 	unsigned char *outbuf, *key;
4775 	zend_bool free_iv;
4776 
4777 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|bs", &data, &data_len, &method, &method_len, &password, &password_len, &raw_output, &iv, &iv_len) == FAILURE) {
4778 		return;
4779 	}
4780 	cipher_type = EVP_get_cipherbyname(method);
4781 	if (!cipher_type) {
4782 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
4783 		RETURN_FALSE;
4784 	}
4785 
4786 	keylen = EVP_CIPHER_key_length(cipher_type);
4787 	if (keylen > password_len) {
4788 		key = emalloc(keylen);
4789 		memset(key, 0, keylen);
4790 		memcpy(key, password, password_len);
4791 	} else {
4792 		key = (unsigned char*)password;
4793 	}
4794 
4795 	max_iv_len = EVP_CIPHER_iv_length(cipher_type);
4796 	if (iv_len <= 0 && max_iv_len > 0) {
4797 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Using an empty Initialization Vector (iv) is potentially insecure and not recommended");
4798 	}
4799 	free_iv = php_openssl_validate_iv(&iv, &iv_len, max_iv_len TSRMLS_CC);
4800 
4801 	outlen = data_len + EVP_CIPHER_block_size(cipher_type);
4802 	outbuf = emalloc(outlen + 1);
4803 
4804 	EVP_EncryptInit(&cipher_ctx, cipher_type, NULL, NULL);
4805 	if (password_len > keylen) {
4806 		EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len);
4807 	}
4808 	EVP_EncryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
4809 	if (data_len > 0) {
4810 		EVP_EncryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len);
4811 	}
4812 	outlen = i;
4813 	if (EVP_EncryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) {
4814 		outlen += i;
4815 		if (raw_output) {
4816 			outbuf[outlen] = '\0';
4817 			RETVAL_STRINGL((char *)outbuf, outlen, 0);
4818 		} else {
4819 			int base64_str_len;
4820 			char *base64_str;
4821 
4822 			base64_str = (char*)php_base64_encode(outbuf, outlen, &base64_str_len);
4823 			efree(outbuf);
4824 			RETVAL_STRINGL(base64_str, base64_str_len, 0);
4825 		}
4826 	} else {
4827 		efree(outbuf);
4828 		RETVAL_FALSE;
4829 	}
4830 	if (key != (unsigned char*)password) {
4831 		efree(key);
4832 	}
4833 	if (free_iv) {
4834 		efree(iv);
4835 	}
4836 	EVP_CIPHER_CTX_cleanup(&cipher_ctx);
4837 }
4838 /* }}} */
4839 
4840 /* {{{ proto string openssl_decrypt(string data, string method, string password [, bool raw_input=false [, string $iv = '']])
4841    Takes raw or base64 encoded string and dectupt it using given method and key */
PHP_FUNCTION(openssl_decrypt)4842 PHP_FUNCTION(openssl_decrypt)
4843 {
4844 	zend_bool raw_input = 0;
4845 	char *data, *method, *password, *iv = "";
4846 	int data_len, method_len, password_len, iv_len = 0;
4847 	const EVP_CIPHER *cipher_type;
4848 	EVP_CIPHER_CTX cipher_ctx;
4849 	int i, outlen, keylen;
4850 	unsigned char *outbuf, *key;
4851 	int base64_str_len;
4852 	char *base64_str = NULL;
4853 	zend_bool free_iv;
4854 
4855 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|bs", &data, &data_len, &method, &method_len, &password, &password_len, &raw_input, &iv, &iv_len) == FAILURE) {
4856 		return;
4857 	}
4858 
4859 	if (!method_len) {
4860 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
4861 		RETURN_FALSE;
4862 	}
4863 
4864 	cipher_type = EVP_get_cipherbyname(method);
4865 	if (!cipher_type) {
4866 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
4867 		RETURN_FALSE;
4868 	}
4869 
4870 	if (!raw_input) {
4871 		base64_str = (char*)php_base64_decode((unsigned char*)data, data_len, &base64_str_len);
4872 		if (!base64_str) {
4873 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to base64 decode the input");
4874 			RETURN_FALSE;
4875 		}
4876 		data_len = base64_str_len;
4877 		data = base64_str;
4878 	}
4879 
4880 	keylen = EVP_CIPHER_key_length(cipher_type);
4881 	if (keylen > password_len) {
4882 		key = emalloc(keylen);
4883 		memset(key, 0, keylen);
4884 		memcpy(key, password, password_len);
4885 	} else {
4886 		key = (unsigned char*)password;
4887 	}
4888 
4889 	free_iv = php_openssl_validate_iv(&iv, &iv_len, EVP_CIPHER_iv_length(cipher_type) TSRMLS_CC);
4890 
4891 	outlen = data_len + EVP_CIPHER_block_size(cipher_type);
4892 	outbuf = emalloc(outlen + 1);
4893 
4894 	EVP_DecryptInit(&cipher_ctx, cipher_type, NULL, NULL);
4895 	if (password_len > keylen) {
4896 		EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len);
4897 	}
4898 	EVP_DecryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
4899 	EVP_DecryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len);
4900 	outlen = i;
4901 	if (EVP_DecryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) {
4902 		outlen += i;
4903 		outbuf[outlen] = '\0';
4904 		RETVAL_STRINGL((char *)outbuf, outlen, 0);
4905 	} else {
4906 		efree(outbuf);
4907 		RETVAL_FALSE;
4908 	}
4909 	if (key != (unsigned char*)password) {
4910 		efree(key);
4911 	}
4912 	if (free_iv) {
4913 		efree(iv);
4914 	}
4915 	if (base64_str) {
4916 		efree(base64_str);
4917 	}
4918  	EVP_CIPHER_CTX_cleanup(&cipher_ctx);
4919 }
4920 /* }}} */
4921 
4922 /* {{{ proto int openssl_cipher_iv_length(string $method) */
PHP_FUNCTION(openssl_cipher_iv_length)4923 PHP_FUNCTION(openssl_cipher_iv_length)
4924 {
4925 	char *method;
4926 	int method_len;
4927 	const EVP_CIPHER *cipher_type;
4928 
4929 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len) == FAILURE) {
4930 		return;
4931 	}
4932 
4933 	if (!method_len) {
4934 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
4935 		RETURN_FALSE;
4936 	}
4937 
4938 	cipher_type = EVP_get_cipherbyname(method);
4939 	if (!cipher_type) {
4940 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
4941 		RETURN_FALSE;
4942 	}
4943 
4944 	RETURN_LONG(EVP_CIPHER_iv_length(cipher_type));
4945 }
4946 /* }}} */
4947 
4948 
4949 /* {{{ proto string openssl_dh_compute_key(string pub_key, resource dh_key)
4950    Computes shared sicret for public value of remote DH key and local DH key */
PHP_FUNCTION(openssl_dh_compute_key)4951 PHP_FUNCTION(openssl_dh_compute_key)
4952 {
4953 	zval *key;
4954 	char *pub_str;
4955 	int pub_len;
4956 	EVP_PKEY *pkey;
4957 	BIGNUM *pub;
4958 	char *data;
4959 	int len;
4960 
4961 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sr", &pub_str, &pub_len, &key) == FAILURE) {
4962 		return;
4963 	}
4964 	ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key);
4965 	if (!pkey || EVP_PKEY_type(pkey->type) != EVP_PKEY_DH || !pkey->pkey.dh) {
4966 		RETURN_FALSE;
4967 	}
4968 
4969 	pub = BN_bin2bn((unsigned char*)pub_str, pub_len, NULL);
4970 
4971 	data = emalloc(DH_size(pkey->pkey.dh) + 1);
4972 	len = DH_compute_key((unsigned char*)data, pub, pkey->pkey.dh);
4973 
4974 	if (len >= 0) {
4975 		data[len] = 0;
4976 		RETVAL_STRINGL(data, len, 0);
4977 	} else {
4978 		efree(data);
4979 		RETVAL_FALSE;
4980 	}
4981 
4982 	BN_free(pub);
4983 }
4984 /* }}} */
4985 
4986 /* {{{ proto string openssl_random_pseudo_bytes(integer length [, &bool returned_strong_result])
4987    Returns a string of the length specified filled with random pseudo bytes */
PHP_FUNCTION(openssl_random_pseudo_bytes)4988 PHP_FUNCTION(openssl_random_pseudo_bytes)
4989 {
4990 	long buffer_length;
4991 	unsigned char *buffer = NULL;
4992 	zval *zstrong_result_returned = NULL;
4993 	int strong_result = 0;
4994 
4995 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|z", &buffer_length, &zstrong_result_returned) == FAILURE) {
4996 		return;
4997 	}
4998 
4999 	if (buffer_length <= 0) {
5000 		RETURN_FALSE;
5001 	}
5002 
5003 	if (zstrong_result_returned) {
5004 		zval_dtor(zstrong_result_returned);
5005 		ZVAL_BOOL(zstrong_result_returned, 0);
5006 	}
5007 
5008 	buffer = emalloc(buffer_length + 1);
5009 
5010 	if ((strong_result = RAND_pseudo_bytes(buffer, buffer_length)) < 0) {
5011 		efree(buffer);
5012 		RETURN_FALSE;
5013 	}
5014 
5015 	buffer[buffer_length] = 0;
5016 	RETVAL_STRINGL((char *)buffer, buffer_length, 0);
5017 
5018 	if (zstrong_result_returned) {
5019 		ZVAL_BOOL(zstrong_result_returned, strong_result);
5020 	}
5021 }
5022 /* }}} */
5023 
5024 /*
5025  * Local variables:
5026  * tab-width: 8
5027  * c-basic-offset: 8
5028  * End:
5029  * vim600: sw=4 ts=4 fdm=marker
5030  * vim<600: sw=4 ts=4
5031  */
5032