-
Notifications
You must be signed in to change notification settings - Fork 7.9k
OpenSSL output P7B structure in open_pkcs7_verify and add a openssl_pkcs7_read function #2563
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
ext/openssl/openssl.c
Outdated
int i; | ||
|
||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/", &p7b, &p7b_len, | ||
&zout) == FAILURE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use braces after if
(it's PHP core CS)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adjusted.
ext/openssl/openssl.c
Outdated
|
||
if (0 >= BIO_write(bio_in, p7b, (int)p7b_len)) { | ||
php_openssl_store_errors(); | ||
goto clean_exit; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't that mean that you try to free p7 that wasn't allocated yet. I don't think we should be doing that even it's NULL and some platform accept that (not sure how portable it is)...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm C89 denotes:
Quoting the C standard, 7.20.3.2/2 from [ISO-IEC 9899](http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf)
void free(void *ptr);
If ptr is a null pointer, no action occurs.
But I'm fine with changing it, since the rest of the code follows the same style guide.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep but some old compilers were failing on that. I don't actually thing we support any such system but it's still better to follow this rule...
|
||
PHP_OPENSSL_CHECK_SIZE_T_TO_INT(p7b_len, p7b); | ||
|
||
bio_in = BIO_new(BIO_s_mem()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you should probably check if it's not NULL..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be actually caught below as BIO_write will check that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, then I won't add a check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I say add the check anyway. We can't guarantee that OpenSSL won't suddenly start assuming bio_in is valid. Compared to the file access/crypto cost, one int branch won't be the perf killer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I agree with @sgolemon. It's better to add checks than relay on library.
p7 = PEM_read_bio_PKCS7(bio_in, NULL, NULL, NULL); | ||
if (p7 == NULL) { | ||
php_openssl_store_errors(); | ||
goto clean_exit; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again, it will free NULL...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adjusted, by checking the value in the exit label
@@ -0,0 +1,28 @@ | |||
--TEST-- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might be good to rename to openssl_pkcs7_read_basic.phpt
to keep it consistent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense will do.
@@ -36,6 +42,11 @@ if (file_exists($contentfile)) { | |||
echo "true\n"; | |||
unlink($contentfile); | |||
} | |||
|
|||
if (file_exists($pkcsfile)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pls move it to the --CLEAN--
section (see openssl_x509_export_to_file_basic.phpt for example...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, maybe the others should be moved there too. But I would rather see that in a separate PR.
@@ -14,6 +14,11 @@ if ($contentfile === false) { | |||
die("failed to get a temporary filename!"); | |||
} | |||
|
|||
$pkcsfile = tempnam(sys_get_temp_dir(), "ssl"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's better to not use temp for test files to keep it consistent (see openssl_x509_export_to_file_basic.phpt for example of the usual naming )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
ext/openssl/openssl.c
Outdated
if (certs != NULL) { | ||
for (i = 0; i < sk_X509_num(certs); i++) { | ||
X509* aCA = sk_X509_pop(certs); | ||
if (!aCA) break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
braces should be here and a new line...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
ext/openssl/openssl.c
Outdated
X509_CRL *crl; | ||
for (i = 0; i < sk_X509_CRL_num(crls); i++) { | ||
X509_CRL* crl = sk_X509_CRL_pop(crls); | ||
if (!crl) break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
braces here as well ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
ext/openssl/openssl.c
Outdated
add_index_zval(zout, i, &zcert); | ||
} | ||
|
||
BIO_free(bio_out); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if BIO_new fails, then it would be NULL...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved the free into the BIO_write if statement as it can't be NULL then.
@jelly It looks ok from the quick look now. Please squash it and will test it next week. If no one objects till then and all is fine, I will try to merge as well. |
} | ||
|
||
zval_dtor(zout); | ||
array_init(zout); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems wierd to return by ref through an arg when you're essentially not using the return value at all.
Why not write your structure to return_value
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've based the function on openssl_pkcs12_read() which does the same, and I think it's nice to keep the functions in php-openssl consistent. One exception is openssl_x509_read which returns a resource or false which makes my argument a bit weaker.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually how it works for most openssl ext function (this one is actually based on openssl_pkcs12_read
). I'm not saying that I like it but I think that the consistency is much more important in here so I would prefer to leave it as it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m'kay. Local consistency is preferable, just struck me as odd at first glance. Freely ignore this review comment. :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I will drop the commits. @sgolemon thanks for the review btw 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bukka reverted those changes, if everything looks fine I will squash the commits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jelly Looks good. Please squash the commits and I will test and possibly merge.
PKCS7 * p7 = NULL; | ||
int i; | ||
|
||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/", &p7b, &p7b_len, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nit; New code should prefer "S" (zend_string*) for string args. It makes it easier to share refs and pass data around to other APIs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please elaborate how it makes things easier in this particular case?
|
||
PHP_OPENSSL_CHECK_SIZE_T_TO_INT(p7b_len, p7b); | ||
|
||
bio_in = BIO_new(BIO_s_mem()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I say add the check anyway. We can't guarantee that OpenSSL won't suddenly start assuming bio_in is valid. Compared to the file access/crypto cost, one int branch won't be the perf killer.
ext/openssl/openssl.c
Outdated
|
||
if (certs != NULL) { | ||
for (i = 0; i < sk_X509_num(certs); i++) { | ||
X509* aCA = sk_X509_pop(certs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know the API here, but pop
would imply that it's removing the cert from the certs vector. If that's the case, then on the next loop sk_X509_num
will be one less while i
continues to increase and you'll end up with only the first half of your entries.
Have you tried this with more than one cert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sgolemon Good catch! pop
decreases the num
so the num should be first saved to the variable and the variable used in the loop. Alternatively sk_X509_value
could be used IIRC.
ext/openssl/openssl.c
Outdated
} | ||
|
||
bio_out = BIO_new(BIO_s_mem()); | ||
if (PEM_write_bio_X509(bio_out, aCA)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As with above, please code defensively.
if (bio_out && PEM_writre_bio_X509(...)) {
var_dump(openssl_pkcs7_read("", $result)); | ||
var_dump(openssl_pkcs7_read($certfile, $result)); | ||
var_dump(openssl_pkcs7_read($infile, $result)); | ||
var_dump(empty($result)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe dump the actual certificate contents? If it contains unpredictable components, you can use an EXPECTF
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm it doesn't contain unpredictable components and should match openssl pkcs7 -in cert.p7b -print_certs (but without the subject= and issuer=)
@@ -26,6 +28,7 @@ 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)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, let's verify something about the written contents. Whether that's a readfile
or even just a filesize
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a check to verify the contents of $pkcsfile matches the expected pk7b output.
ZEND_ARG_INFO(0, infilename) | ||
ZEND_ARG_INFO(1, certs) | ||
ZEND_END_ARG_INFO() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't forget to add the new param to arginfo_openssl_pkcs7_verify
&flags, &signersfilename, &signersfilename_len, &cainfo, | ||
&extracerts, &extracerts_len, &datafilename, &datafilename_len) == FAILURE) { | ||
&extracerts, &extracerts_len, &datafilename, &datafilename_len, &p7bfilename, &p7bfilename_len) == FAILURE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nit; As with below, prefer P
for pathnames as zend_string
rather than separate pieces. No need to change the existing args in the same commit, but let's do new stuff right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see much point in here as we need just an actual string to pass to the library. I see the point if it's in internal API but if it's just passed to the library, then I don't think we should be changing that. It will just bring addition of extra macro wrapping around zend_string values when calling the library func.
ext/openssl/openssl.c
Outdated
|
||
if (p7bout) { | ||
PEM_write_bio_PKCS7(p7bout, p7); | ||
BIO_free(p7bout); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like p7out can leak if the PKCS7_verify
call fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems right, thanks! I'll move it to the exit label.
ext/openssl/openssl.c
Outdated
goto clean_exit; | ||
} | ||
|
||
if (0 >= BIO_write(bio_in, ZSTR_VAL(p7b), (int)ZSTR_LEN(p7b))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is exactly what I don't like. What's the point of using zend_string
if the result is just extra macros. Please can you change it back to s
unless there is some specific reason why we should use zend_string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I'll drop the two commits.
ext/openssl/openssl.c
Outdated
goto clean_exit; | ||
} | ||
|
||
p7bout = BIO_new_file(ZSTR_VAL(p7bfilename), "w"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd really prefer not to use ZSTR_VAL
and rather use p
@bukka I think we will wait for your review of this ... please do merge if okay ... |
92742b2
to
4041dee
Compare
@bukka squashed the commits |
@jelly It actually segfaults for me with OpenSSL 1.0.2 when running |
Well it even fails on travis so you should be able to recreate it: |
Hmm it works for me with OpenSSL 1.1, but indeed on Debian Jessie fails with OpenSSL 1.0 so I will take a look at it. |
b23135d
to
3aa1ee6
Compare
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.
3aa1ee6
to
3b7dc1f
Compare
Thanks for the fix. I have tested on all supported version and all is good. So it's merged now! |
Comment on behalf of bukka at php.net: Merged via 787a18a |
This PR adds support for retrieving the P7B structure which may exists in a signed email, this structure usually contains intermediate certificates which are needed for OCSP validation.
Therefore openssl_pkcs7_verify has an extra parameter added to save this structure to a file.
Since PHP has no support to read PKCS#7 structure, add a new function openss_pkcs7_read, which is based on openssl_pkcs12_read.
There has been a request for reading a PKCS#7 structure for a while.
Background information, with openssl's command line utlity this can be achieved as following:
There is more information available in my mail to php-internals http://news.php.net/php.internals/99293