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