Skip to content

Commit

Permalink
Add openssl_pkcs7_read and output P7B in openssl_pkcs7_verify
Browse files Browse the repository at this point in the history
Add an optional argument to openssl_pkcs7_verify to save the P7B
structure which can contain extra CA intermediate certificates send
along with an S/MIME signed email.

Introduce a new function called openssl_pkcs7_read, which can read a
PKCS#7 structure passed as a string and returns by reference an array
with PEM certificates formatted as a string.
  • Loading branch information
jelly authored and bukka committed Jun 22, 2017
1 parent d09edf7 commit 787a18a
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 4 deletions.
138 changes: 134 additions & 4 deletions ext/openssl/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_verify, 0, 0, 2)
ZEND_ARG_INFO(0, cainfo) /* array */
ZEND_ARG_INFO(0, extracerts)
ZEND_ARG_INFO(0, content)
ZEND_ARG_INFO(0, pk7)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_encrypt, 0, 0, 4)
Expand Down Expand Up @@ -318,6 +319,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_decrypt, 0, 0, 3)
ZEND_ARG_INFO(0, recipkey)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_read, 0, 0, 2)
ZEND_ARG_INFO(0, infilename)
ZEND_ARG_INFO(1, certs)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_private_encrypt, 0, 0, 3)
ZEND_ARG_INFO(0, data)
ZEND_ARG_INFO(1, crypted)
Expand Down Expand Up @@ -519,6 +525,7 @@ const zend_function_entry openssl_functions[] = {
PHP_FE(openssl_pkcs7_decrypt, arginfo_openssl_pkcs7_decrypt)
PHP_FE(openssl_pkcs7_sign, arginfo_openssl_pkcs7_sign)
PHP_FE(openssl_pkcs7_encrypt, arginfo_openssl_pkcs7_encrypt)
PHP_FE(openssl_pkcs7_read, arginfo_openssl_pkcs7_read)

PHP_FE(openssl_private_encrypt, arginfo_openssl_private_encrypt)
PHP_FE(openssl_private_decrypt, arginfo_openssl_private_decrypt)
Expand Down Expand Up @@ -4968,7 +4975,7 @@ PHP_FUNCTION(openssl_pbkdf2)

/* {{{ PKCS7 S/MIME functions */

/* {{{ proto bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]])
/* {{{ proto bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content [, string pk7]]]]])
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)
{
Expand All @@ -4977,7 +4984,7 @@ PHP_FUNCTION(openssl_pkcs7_verify)
STACK_OF(X509) *signers= NULL;
STACK_OF(X509) *others = NULL;
PKCS7 * p7 = NULL;
BIO * in = NULL, * datain = NULL, * dataout = NULL;
BIO * in = NULL, * datain = NULL, * dataout = NULL, * p7bout = NULL;
zend_long flags = 0;
char * filename;
size_t filename_len;
Expand All @@ -4987,12 +4994,14 @@ PHP_FUNCTION(openssl_pkcs7_verify)
size_t signersfilename_len = 0;
char * datafilename = NULL;
size_t datafilename_len = 0;
char * p7bfilename = NULL;
size_t p7bfilename_len = 0;

RETVAL_LONG(-1);

if (zend_parse_parameters(ZEND_NUM_ARGS(), "pl|papp", &filename, &filename_len,
if (zend_parse_parameters(ZEND_NUM_ARGS(), "pl|pappp", &filename, &filename_len,
&flags, &signersfilename, &signersfilename_len, &cainfo,
&extracerts, &extracerts_len, &datafilename, &datafilename_len) == FAILURE) {
&extracerts, &extracerts_len, &datafilename, &datafilename_len, &p7bfilename, &p7bfilename_len) == FAILURE) {
return;
}

Expand Down Expand Up @@ -5040,6 +5049,19 @@ PHP_FUNCTION(openssl_pkcs7_verify)
goto clean_exit;
}
}

if (p7bfilename) {

if (php_openssl_open_base_dir_chk(p7bfilename)) {
goto clean_exit;
}

p7bout = BIO_new_file(p7bfilename, "w");
if (p7bout == NULL) {
php_openssl_store_errors();
goto clean_exit;
}
}
#if DEBUG_SMIME
zend_printf("Calling PKCS7 verify\n");
#endif
Expand Down Expand Up @@ -5081,12 +5103,19 @@ PHP_FUNCTION(openssl_pkcs7_verify)
php_error_docref(NULL, E_WARNING, "signature OK, but cannot open %s for writing", signersfilename);
RETVAL_LONG(-1);
}

if (p7bout) {
PEM_write_bio_PKCS7(p7bout, p7);
}
}
} else {
php_openssl_store_errors();
RETVAL_FALSE;
}
clean_exit:
if (p7bout) {
BIO_free(p7bout);
}
X509_STORE_free(store);
BIO_free(datain);
BIO_free(in);
Expand Down Expand Up @@ -5230,6 +5259,107 @@ PHP_FUNCTION(openssl_pkcs7_encrypt)
}
/* }}} */

/* {{{ proto bool openssl_pkcs7_read(string P7B, array &certs)
Exports the PKCS7 file to an array of PEM certificates */
PHP_FUNCTION(openssl_pkcs7_read)
{
zval * zout = NULL, zcert;
char *p7b;
size_t p7b_len;
STACK_OF(X509) *certs = NULL;
STACK_OF(X509_CRL) *crls = NULL;
BIO * bio_in = NULL, * bio_out = NULL;
PKCS7 * p7 = NULL;
int i;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/", &p7b, &p7b_len,
&zout) == FAILURE) {
return;
}

RETVAL_FALSE;

PHP_OPENSSL_CHECK_SIZE_T_TO_INT(p7b_len, p7b);

bio_in = BIO_new(BIO_s_mem());
if (bio_in == NULL) {
goto clean_exit;
}

if (0 >= BIO_write(bio_in, p7b, (int)p7b_len)) {
php_openssl_store_errors();
goto clean_exit;
}

p7 = PEM_read_bio_PKCS7(bio_in, NULL, NULL, NULL);
if (p7 == NULL) {
php_openssl_store_errors();
goto clean_exit;
}

switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_signed:
if (p7->d.sign != NULL) {
certs = p7->d.sign->cert;
crls = p7->d.sign->crl;
}
break;
case NID_pkcs7_signedAndEnveloped:
if (p7->d.signed_and_enveloped != NULL) {
certs = p7->d.signed_and_enveloped->cert;
crls = p7->d.signed_and_enveloped->crl;
}
break;
default:
break;
}

zval_dtor(zout);
array_init(zout);

if (certs != NULL) {
for (i = 0; i < sk_X509_num(certs); i++) {
X509* ca = sk_X509_value(certs, i);

bio_out = BIO_new(BIO_s_mem());
if (bio_out && PEM_write_bio_X509(bio_out, ca)) {
BUF_MEM *bio_buf;
BIO_get_mem_ptr(bio_out, &bio_buf);
ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
add_index_zval(zout, i, &zcert);
BIO_free(bio_out);
}
}
}

if (crls != NULL) {
for (i = 0; i < sk_X509_CRL_num(crls); i++) {
X509_CRL* crl = sk_X509_CRL_value(crls, i);

bio_out = BIO_new(BIO_s_mem());
if (bio_out && PEM_write_bio_X509_CRL(bio_out, crl)) {
BUF_MEM *bio_buf;
BIO_get_mem_ptr(bio_out, &bio_buf);
ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
add_index_zval(zout, i, &zcert);
BIO_free(bio_out);
}
}
}

RETVAL_TRUE;

clean_exit:
if (bio_in != NULL) {
BIO_free(bio_in);
}

if (p7 != NULL) {
PKCS7_free(p7);
}
}
/* }}} */

/* {{{ proto bool openssl_pkcs7_sign(string infile, string outfile, mixed signcert, mixed signkey, array headers [, long flags [, string extracertsfilename]])
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 */

Expand Down
1 change: 1 addition & 0 deletions ext/openssl/php_openssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ PHP_FUNCTION(openssl_pkcs7_verify);
PHP_FUNCTION(openssl_pkcs7_decrypt);
PHP_FUNCTION(openssl_pkcs7_sign);
PHP_FUNCTION(openssl_pkcs7_encrypt);
PHP_FUNCTION(openssl_pkcs7_read);

PHP_FUNCTION(openssl_error_string);

Expand Down
22 changes: 22 additions & 0 deletions ext/openssl/tests/cert.p7b
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-----BEGIN PKCS7-----
MIIDnQYJKoZIhvcNAQcCoIIDjjCCA4oCAQExADALBgkqhkiG9w0BBwGgggNwMIID
bDCCAtWgAwIBAgIJAK7FVsxyN1CiMA0GCSqGSIb3DQEBBQUAMIGBMQswCQYDVQQG
EwJCUjEaMBgGA1UECBMRUmlvIEdyYW5kZSBkbyBTdWwxFTATBgNVBAcTDFBvcnRv
IEFsZWdyZTEeMBwGA1UEAxMVSGVucmlxdWUgZG8gTi4gQW5nZWxvMR8wHQYJKoZI
hvcNAQkBFhBobmFuZ2Vsb0BwaHAubmV0MB4XDTA4MDYzMDEwMjg0M1oXDTA4MDcz
MDEwMjg0M1owgYExCzAJBgNVBAYTAkJSMRowGAYDVQQIExFSaW8gR3JhbmRlIGRv
IFN1bDEVMBMGA1UEBxMMUG9ydG8gQWxlZ3JlMR4wHAYDVQQDExVIZW5yaXF1ZSBk
byBOLiBBbmdlbG8xHzAdBgkqhkiG9w0BCQEWEGhuYW5nZWxvQHBocC5uZXQwgZ8w
DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMteno+QK1ulX4/WDAVBYfoTPRTze4SZ
Lwgael4jwWTytj+8c5nNllrFELD6WjJzfjaoIMhCF4w4I2bkWR6/PTqrvnv+iiiI
tHfKvJgYqIobUhkiKmWa2wL3mgqvNRIqTrTC4jWZuCkxQ/ksqL9O/F6zk+aRS1d+
KbPaqCR5Rw+lAgMBAAGjgekwgeYwHQYDVR0OBBYEFNt+QHK9XDWF7CkpgRLoYmhq
tz99MIG2BgNVHSMEga4wgauAFNt+QHK9XDWF7CkpgRLoYmhqtz99oYGHpIGEMIGB
MQswCQYDVQQGEwJCUjEaMBgGA1UECBMRUmlvIEdyYW5kZSBkbyBTdWwxFTATBgNV
BAcTDFBvcnRvIEFsZWdyZTEeMBwGA1UEAxMVSGVucmlxdWUgZG8gTi4gQW5nZWxv
MR8wHQYJKoZIhvcNAQkBFhBobmFuZ2Vsb0BwaHAubmV0ggkArsVWzHI3UKIwDAYD
VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCP1GUnStC0TBqngr3Kx+zSUW8K
utKO0ORc5R8aV/x9LlaJrzPyQJgiPpu5hXogLSKRIHxQS3X2+Y0VvIpW72LWPVKP
hYlNtO3oKnfoJGKin0eEhXRZMjfEW/kznY+ZZmNifV2r8s+KhNAqI4PbClvn4vh8
xF/9+eVEj+hM+0OflKEAMQA=
-----END PKCS7-----
52 changes: 52 additions & 0 deletions ext/openssl/tests/openssl_pkcs7_read_basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
--TEST--
openssl_pkcs7_read() tests
--SKIPIF--
<?php if (!extension_loaded("openssl")) print "skip"; ?>
--FILE--
<?php
$infile = file_get_contents(dirname(__FILE__) . "/cert.p7b");
$certfile = file_get_contents(dirname(__FILE__) . "/cert.crt");
$result = [];

var_dump(openssl_pkcs7_read());
var_dump(openssl_pkcs7_read(""));
var_dump(openssl_pkcs7_read("", $result));
var_dump(openssl_pkcs7_read($certfile, $result));
var_dump(openssl_pkcs7_read($infile, $result));
var_dump($result);
?>
--EXPECTF--

Warning: openssl_pkcs7_read() expects exactly 2 parameters, 0 given in %s on line %d
NULL

Warning: openssl_pkcs7_read() expects exactly 2 parameters, 1 given in %s on line %d
NULL
bool(false)
bool(false)
bool(true)
array(1) {
[0]=>
string(1249) "-----BEGIN CERTIFICATE-----
MIIDbDCCAtWgAwIBAgIJAK7FVsxyN1CiMA0GCSqGSIb3DQEBBQUAMIGBMQswCQYD
VQQGEwJCUjEaMBgGA1UECBMRUmlvIEdyYW5kZSBkbyBTdWwxFTATBgNVBAcTDFBv
cnRvIEFsZWdyZTEeMBwGA1UEAxMVSGVucmlxdWUgZG8gTi4gQW5nZWxvMR8wHQYJ
KoZIhvcNAQkBFhBobmFuZ2Vsb0BwaHAubmV0MB4XDTA4MDYzMDEwMjg0M1oXDTA4
MDczMDEwMjg0M1owgYExCzAJBgNVBAYTAkJSMRowGAYDVQQIExFSaW8gR3JhbmRl
IGRvIFN1bDEVMBMGA1UEBxMMUG9ydG8gQWxlZ3JlMR4wHAYDVQQDExVIZW5yaXF1
ZSBkbyBOLiBBbmdlbG8xHzAdBgkqhkiG9w0BCQEWEGhuYW5nZWxvQHBocC5uZXQw
gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMteno+QK1ulX4/WDAVBYfoTPRTz
e4SZLwgael4jwWTytj+8c5nNllrFELD6WjJzfjaoIMhCF4w4I2bkWR6/PTqrvnv+
iiiItHfKvJgYqIobUhkiKmWa2wL3mgqvNRIqTrTC4jWZuCkxQ/ksqL9O/F6zk+aR
S1d+KbPaqCR5Rw+lAgMBAAGjgekwgeYwHQYDVR0OBBYEFNt+QHK9XDWF7CkpgRLo
Ymhqtz99MIG2BgNVHSMEga4wgauAFNt+QHK9XDWF7CkpgRLoYmhqtz99oYGHpIGE
MIGBMQswCQYDVQQGEwJCUjEaMBgGA1UECBMRUmlvIEdyYW5kZSBkbyBTdWwxFTAT
BgNVBAcTDFBvcnRvIEFsZWdyZTEeMBwGA1UEAxMVSGVucmlxdWUgZG8gTi4gQW5n
ZWxvMR8wHQYJKoZIhvcNAQkBFhBobmFuZ2Vsb0BwaHAubmV0ggkArsVWzHI3UKIw
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCP1GUnStC0TBqngr3Kx+zS
UW8KutKO0ORc5R8aV/x9LlaJrzPyQJgiPpu5hXogLSKRIHxQS3X2+Y0VvIpW72LW
PVKPhYlNtO3oKnfoJGKin0eEhXRZMjfEW/kznY+ZZmNifV2r8s+KhNAqI4PbClvn
4vh8xF/9+eVEj+hM+0OflA==
-----END CERTIFICATE-----
"
}
46 changes: 46 additions & 0 deletions ext/openssl/tests/openssl_pkcs7_verify_basic.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ if ($contentfile === false) {
die("failed to get a temporary filename!");
}

$pkcsfile = dirname(__FILE__) . "/openssl_pkcs7_verify__pkcsfile.tmp";

$infile = dirname(__FILE__) . "/cert.crt";
$eml = dirname(__FILE__) . "/signed.eml";
$wrong = "wrong";
Expand All @@ -26,6 +28,8 @@ var_dump(openssl_pkcs7_verify($eml, 0));
var_dump(openssl_pkcs7_verify($eml, 0, $empty));
var_dump(openssl_pkcs7_verify($eml, PKCS7_NOVERIFY, $outfile));
var_dump(openssl_pkcs7_verify($eml, PKCS7_NOVERIFY, $outfile, $cainfo, $outfile, $contentfile));
var_dump(openssl_pkcs7_verify($eml, PKCS7_NOVERIFY, $outfile, $cainfo, $outfile, $contentfile, $pkcsfile));
var_dump(file_get_contents($pkcsfile));

if (file_exists($outfile)) {
echo "true\n";
Expand All @@ -37,12 +41,54 @@ if (file_exists($contentfile)) {
unlink($contentfile);
}
?>
--CLEAN--
<?php
if (file_exists($pkcsfile)) {
unlink($pkcsfile);
}
?>
--EXPECTF--
int(-1)
int(-1)
bool(false)
bool(false)
bool(true)
bool(true)
bool(true)
string(2062) "-----BEGIN PKCS7-----
MIIFzQYJKoZIhvcNAQcCoIIFvjCCBboCAQExDzANBglghkgBZQMEAgEFADALBgkq
hkiG9w0BBwGgggNwMIIDbDCCAtWgAwIBAgIJAK7FVsxyN1CiMA0GCSqGSIb3DQEB
BQUAMIGBMQswCQYDVQQGEwJCUjEaMBgGA1UECBMRUmlvIEdyYW5kZSBkbyBTdWwx
FTATBgNVBAcTDFBvcnRvIEFsZWdyZTEeMBwGA1UEAxMVSGVucmlxdWUgZG8gTi4g
QW5nZWxvMR8wHQYJKoZIhvcNAQkBFhBobmFuZ2Vsb0BwaHAubmV0MB4XDTA4MDYz
MDEwMjg0M1oXDTA4MDczMDEwMjg0M1owgYExCzAJBgNVBAYTAkJSMRowGAYDVQQI
ExFSaW8gR3JhbmRlIGRvIFN1bDEVMBMGA1UEBxMMUG9ydG8gQWxlZ3JlMR4wHAYD
VQQDExVIZW5yaXF1ZSBkbyBOLiBBbmdlbG8xHzAdBgkqhkiG9w0BCQEWEGhuYW5n
ZWxvQHBocC5uZXQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMteno+QK1ul
X4/WDAVBYfoTPRTze4SZLwgael4jwWTytj+8c5nNllrFELD6WjJzfjaoIMhCF4w4
I2bkWR6/PTqrvnv+iiiItHfKvJgYqIobUhkiKmWa2wL3mgqvNRIqTrTC4jWZuCkx
Q/ksqL9O/F6zk+aRS1d+KbPaqCR5Rw+lAgMBAAGjgekwgeYwHQYDVR0OBBYEFNt+
QHK9XDWF7CkpgRLoYmhqtz99MIG2BgNVHSMEga4wgauAFNt+QHK9XDWF7CkpgRLo
Ymhqtz99oYGHpIGEMIGBMQswCQYDVQQGEwJCUjEaMBgGA1UECBMRUmlvIEdyYW5k
ZSBkbyBTdWwxFTATBgNVBAcTDFBvcnRvIEFsZWdyZTEeMBwGA1UEAxMVSGVucmlx
dWUgZG8gTi4gQW5nZWxvMR8wHQYJKoZIhvcNAQkBFhBobmFuZ2Vsb0BwaHAubmV0
ggkArsVWzHI3UKIwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCP1GUn
StC0TBqngr3Kx+zSUW8KutKO0ORc5R8aV/x9LlaJrzPyQJgiPpu5hXogLSKRIHxQ
S3X2+Y0VvIpW72LWPVKPhYlNtO3oKnfoJGKin0eEhXRZMjfEW/kznY+ZZmNifV2r
8s+KhNAqI4PbClvn4vh8xF/9+eVEj+hM+0OflDGCAiEwggIdAgEBMIGPMIGBMQsw
CQYDVQQGEwJCUjEaMBgGA1UECBMRUmlvIEdyYW5kZSBkbyBTdWwxFTATBgNVBAcT
DFBvcnRvIEFsZWdyZTEeMBwGA1UEAxMVSGVucmlxdWUgZG8gTi4gQW5nZWxvMR8w
HQYJKoZIhvcNAQkBFhBobmFuZ2Vsb0BwaHAubmV0AgkArsVWzHI3UKIwDQYJYIZI
AWUDBAIBBQCggeQwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0B
CQUxDxcNMTcwNTIyMTAxMDU1WjAvBgkqhkiG9w0BCQQxIgQg37MSoxw91phVhwUO
MeurwtXAXK1ADEeYYl/7Bfmz4CsweQYJKoZIhvcNAQkPMWwwajALBglghkgBZQME
ASowCwYJYIZIAWUDBAEWMAsGCWCGSAFlAwQBAjAKBggqhkiG9w0DBzAOBggqhkiG
9w0DAgICAIAwDQYIKoZIhvcNAwICAUAwBwYFKw4DAgcwDQYIKoZIhvcNAwICASgw
DQYJKoZIhvcNAQEBBQAEgYAw4XcsQ4BIhEuRNspG8RqPE9ODCrTWwXPSQ4B9fzks
KUAsqcefO8AfifY+uuq3/k6Prhl23U5ILth/0fUAIGFLTcIZziaGTwbpgcmRSmNi
jBxatHyKVaGJNGqij5KRk8vhEpy5mwOzmkUzYa0r4teXjyfnKhI/h1vUrO3kKybC
5Q==
-----END PKCS7-----
"
true
true

0 comments on commit 787a18a

Please sign in to comment.