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