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