php / php/php-src

check_cert() uses system CA store even if user provided own cert to be used as CA

Aperta
#20,586 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Bug Extension: openssl Status: Needs Triage
Lingua principale
C
Stelle
40.4k
Fork
8.1k
Merge medio
2g 13h
PR unite (30g)
96

Descrizione

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

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia in ext/openssl/openssl_backend_common.c, in php_openssl_setup_verify() e nel percorso check_cert(). Riproduci l’esempio di openssl_x509_checkpurpose() con valori di ca_info contenenti solo file, solo directory e una combinazione dei due. Il lavoro è completato quando file o directory CA validi forniti dall’utente impediscono il caricamento parziale dell’archivio CA di sistema.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
c, php
Ambito
backend, security
Tipo di issue
Bug
Difficoltà
3/5
Tempo stimato
1-2 giorni
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
55/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.