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