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