System: Trust: Certificates - link certificates by subject on import, closes https://github.com/opnsense/core/issues/7813

It looks like we only linked certificates on CA import, move that code into the Cert model and improve it a bit, also trigger when importing a new certificate manually now.
To improve the matching, we search for subjects that match best based on item length now (e.g. a dn ZH,NL should precede one with only NL)
This commit is contained in:
Ad Schellevis 2024-08-26 11:02:42 +02:00
parent db58e04ac3
commit 1be6303460
3 changed files with 56 additions and 9 deletions

View File

@ -127,15 +127,8 @@ class CaController extends ApiMutableModelControllerBase
}
}
$certmdl = new Cert();
foreach ($certmdl->cert->iterateItems() as $cert) {
$x509_2 = openssl_x509_parse((string)$cert->crt_payload);
if ($x509_2 !== false) {
if ($this->compare_issuer($x509_2['issuer'], $x509['subject'])) {
$cert->caref = (string)$node->refid;
}
}
}
$certmdl->serializeToConfig();
$certmdl->linkCaRefs(null, $this->getModel());
$certmdl->serializeToConfig(false, true); /* we need to force save to maintain integrity */
}
}
break;

View File

@ -110,6 +110,7 @@ class CertController extends ApiMutableModelControllerBase
$error = gettext('Invalid private key provided');
}
}
$this->getModel()->linkCaRefs($node->refid);
break;
case 'import_csr':
if (CertStore::parseX509((string)$node->crt_payload) === false) {

View File

@ -37,4 +37,57 @@ use OPNsense\Base\Messages\Message;
*/
class Cert extends BaseModel
{
/**
* compare subject to issuer (subject fits within issuer DN)
* @param array $subject to find
* @param array $issuer to match on
* @return bool
*/
private function compare_issuer(array $subject, array $issuer): bool
{
return empty(array_diff(
array_map('serialize', $subject),
array_map('serialize', $issuer)
));
}
/**
* link certificates to ca's in our trust store (based on issuer)
* @param string $refid optional certificate reference id to link
* @param null|Ca $ca_mdl optional Ca model to use, comstructs one when not offered
* @return void
*/
public function linkCaRefs(?string $refid=null, mixed $ca_mdl=null): void
{
$ca_subjects = [];
foreach ($this->cert->iterateItems() as $cert) {
if ($refid != null && $cert->refid != $refid) {
continue;
}
$cert_x509 = openssl_x509_parse((string)$cert->crt_payload);
if ($cert_x509 === false) {
continue;
}
if (empty($ca_subjects)) {
/* collect on first item for matching */
$mdl = $ca_mdl == null ? new Ca() : $ca_mdl;
foreach ($mdl->ca->iterateItems() as $ca) {
$x509 = openssl_x509_parse((string)$ca->crt_payload);
if ($x509 === false) {
continue;
}
/* add sort key, longer paths should match earlier. e.g. NL,ZH should precede NL */
$key = sprintf('%04d-%s', count($x509['subject']), $ca->refid);
$ca_subjects[$key] = ['subject' => $x509['subject'], 'caref' => (string)$ca->refid];
}
krsort($ca_subjects);
}
foreach ($ca_subjects as $caref => $item) {
if ($this->compare_issuer($item['subject'], $cert_x509['issuer'])) {
$cert->caref = $item['caref'];
break;
}
}
}
}
}