check_cert() uses system CA store even if user provided own cert to be used as CA
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 40.4k
- Forks
- 8.1k
- Avg merge
- 2d 13h
- Merged PRs (30d)
- 96
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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in ext/openssl/openssl_backend_common.c at php_openssl_setup_verify() and the check_cert() path. Reproduce the openssl_x509_checkpurpose() example with file-only, directory-only, and mixed ca_info values. Done means valid user-provided CA files or directories prevent the system CA store from being partially loaded.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, php
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100