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