-
Notifications
You must be signed in to change notification settings - Fork 8k
Description
Description
I was trying to figure out why this code
<?php
$cert = file_get_contents("single-letsencrypt-domain-cert.pem")
print_r(openssl_x509_checkpurpose($cert, X509_PURPOSE_SSL_SERVER, array('letsencrypt-intermediate.pem')));worked (returned True) on some systems while on other returned False. Turns out the key difference is that one system had CA certificates in a bundle ONLY:
/etc/pki/tls/certs/ca-bundle.crt
while the other system had above bundle BUT also individual hashed CA certs in /etc/openssl/certs/ directory.
/etc/pki/tls/certs/ca-bundle.crt
/etc/openssl/certs/*.0 (bunch of hashes as symlinks to actual individual CA cert files)
(note this is on PLD/Linux, so on your system layout can be different)
Now openssl_x509_checkpurpose() third parameter is "ca_info should be an array of trusted CA files/dirs as described in Certificate Verification."
I assume the goal is to override system CA certs. (My assumption is also based on how/when check_cert() tries to load system CA certs)
openssl_x509_checkpurpose() then uses check_cert() to load CA certs.
Unfortunately the code is like this
[code from php 8.5 git]
parse what user has specified in ca_info (calist), if some files then "nfiles++", if dirs then "ndirs++",
load what user specified (if possible) and then handle system CA certs but do it in such way:
832 if (nfiles == 0) {
833 file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
834 if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT)) {
835 php_openssl_store_errors();
836 }
837 }
838 if (ndirs == 0) {
839 dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
840 if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT)) {
841 php_openssl_store_errors();
842 }
843 }
Which means:
- if user specified (in ca_info) any files and only files then sytem CA files won't be loaded BUT sytem directories will (due to ndirs==0)!
- if user specified (in ca_info) any directories and only directories then sytem CA directories won't be loaded BUT sytem files will (due to nfiles==0)!
- if user specified (in ca_info) any files and any directories then no CA files and no CA directories will be used (due to ndirs > 0 and nfiles > 0).
That makes no sense. Partial system CA is getting kind of "randomly" loaded only based on file vs directory type.
I would expect only user provided CA files/dirs to be used and no system CA store if user uses anything valid as ca_info parameter of openssl_x509_checkpurpose()
So the fix - use system CA certs in form of files and directories only if user didn't override these with own ca_info (calist) parameter.
diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c
index c21e64a1306..1f4509f40f0 100644
--- a/ext/openssl/openssl_backend_common.c
+++ b/ext/openssl/openssl_backend_common.c
@@ -829,13 +829,11 @@ X509_STORE *php_openssl_setup_verify(zval *calist, uint32_t arg_num)
}
} ZEND_HASH_FOREACH_END();
}
- if (nfiles == 0) {
+ if (nfiles == 0 && ndirs == 0) {
file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT)) {
php_openssl_store_errors();
}
- }
- if (ndirs == 0) {
dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT)) {
php_openssl_store_errors();
PHP Version
$ php84 --version
PHP 8.4.13 (cli) (built: Sep 27 2025 01:01:24) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.4.13, Copyright (c) Zend Technologies
(tests were done on this php)
Operating System
PLD/Linux current Th