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