Skip to content

Commit e7e4435

Browse files
committed
fix(crl): detect distribution points across extension key variants
Signed-off-by: Vitor Mattos <[email protected]>
1 parent 514ae2c commit e7e4435

4 files changed

Lines changed: 95 additions & 20 deletions

File tree

lib/Handler/CertificateEngine/AEngineHandler.php

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -182,23 +182,76 @@ private function parseX509(string $x509): array {
182182
}
183183

184184
private function addCrlValidationInfo(array &$certData, string $certPem): void {
185-
if (isset($certData['extensions']['crlDistributionPoints'])) {
186-
preg_match_all('/URI:([^\s,\n]+)/', $certData['extensions']['crlDistributionPoints'], $matches);
187-
$extractedUrls = $matches[1] ?? [];
188-
189-
$certData['crl_urls'] = $extractedUrls;
190-
$crlDetails = $this->crlRevocationChecker->validate($extractedUrls, $certPem);
191-
$certData['crl_validation'] = $crlDetails['status'];
192-
if (!empty($crlDetails['revoked_at'])) {
193-
$certData['crl_revoked_at'] = $crlDetails['revoked_at'];
185+
$extensions = $certData['extensions'] ?? [];
186+
if (is_array($extensions)) {
187+
['hasExtension' => $hasCrlExtension, 'urls' => $extractedUrls] = $this->extractCrlUrlsFromExtensions($extensions);
188+
if ($hasCrlExtension) {
189+
$certData['crl_urls'] = $extractedUrls;
190+
if (empty($extractedUrls)) {
191+
$certData['crl_validation'] = CrlValidationStatus::NO_URLS;
192+
return;
193+
}
194+
195+
$crlDetails = $this->crlRevocationChecker->validate($extractedUrls, $certPem);
196+
$certData['crl_validation'] = $crlDetails['status'];
197+
if (!empty($crlDetails['revoked_at'])) {
198+
$certData['crl_revoked_at'] = $crlDetails['revoked_at'];
199+
}
200+
return;
194201
}
195-
} else {
202+
}
203+
196204
$externalValidationEnabled = $this->appConfig->getValueBool(Application::APP_ID, 'crl_external_validation_enabled', true);
197205
$certData['crl_validation'] = $externalValidationEnabled
198206
? CrlValidationStatus::MISSING
199207
: CrlValidationStatus::DISABLED;
200208
$certData['crl_urls'] = [];
209+
}
210+
211+
/**
212+
* @return array{hasExtension: bool, urls: array<int, string>}
213+
*/
214+
private function extractCrlUrlsFromExtensions(array $extensions): array {
215+
$values = [];
216+
foreach ($extensions as $extensionName => $extensionValue) {
217+
if (!is_string($extensionName)) {
218+
continue;
219+
}
220+
221+
$normalizedName = strtolower(trim($extensionName));
222+
$isCrlDistributionPoints =
223+
$normalizedName === 'crldistributionpoints'
224+
|| $normalizedName === 'x509v3 crl distribution points'
225+
|| $normalizedName === '2.5.29.31'
226+
|| str_contains($normalizedName, 'crl distribution points');
227+
228+
if (!$isCrlDistributionPoints) {
229+
continue;
230+
}
231+
232+
if (is_string($extensionValue)) {
233+
$values[] = $extensionValue;
234+
} elseif (is_array($extensionValue)) {
235+
$values[] = implode("\n", array_filter($extensionValue, 'is_string'));
236+
}
237+
}
238+
239+
if (empty($values)) {
240+
return ['hasExtension' => false, 'urls' => []];
201241
}
242+
243+
$urls = [];
244+
foreach ($values as $value) {
245+
preg_match_all('/URI\s*:\s*([^\s,\n]+)/i', $value, $matches);
246+
if (!empty($matches[1])) {
247+
$urls = [...$urls, ...$matches[1]];
248+
}
249+
}
250+
251+
return [
252+
'hasExtension' => true,
253+
'urls' => array_values(array_unique($urls)),
254+
];
202255
}
203256

204257
private static function convertArrayToUtf8($array) {

lib/Service/IdentifyMethod/SignatureMethod/Password.php

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,6 @@ private function validateCertificateRevocation(array $certificateData): void {
5959
if ($status === CrlValidationStatus::DISABLED) {
6060
return;
6161
}
62-
// Backward compatibility for legacy certificates issued before CRL metadata existed.
63-
if ($status === CrlValidationStatus::MISSING) {
64-
$this->identifyService->getLogger()->warning('Signing allowed for certificate without revocation metadata', [
65-
'status' => $status->value,
66-
'signer_uid' => $this->userSession->getUser()?->getUID(),
67-
]);
68-
return;
69-
}
7062
$this->logRevocationBlockedSigning($status);
7163
throw new LibresignException($this->getRevocationErrorMessage($status), 422);
7264
}

tests/php/Unit/Handler/CertificateEngine/OpenSslHandlerTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,34 @@ public function testUniqueSerialNumbers(): void {
468468
$this->assertCount($numCertificates, array_unique($serialNumbers), 'All serial numbers should be unique');
469469
}
470470

471+
public function testExtractCrlUrlsFromOidExtensionName(): void {
472+
$handler = $this->getInstance();
473+
474+
$method = new \ReflectionMethod('OCA\\Libresign\\Handler\\CertificateEngine\\AEngineHandler', 'extractCrlUrlsFromExtensions');
475+
$method->setAccessible(true);
476+
477+
$result = $method->invoke($handler, [
478+
'2.5.29.31' => "Full Name:\nURI:https://example.org/crl/root.crl",
479+
]);
480+
481+
$this->assertTrue($result['hasExtension']);
482+
$this->assertSame(['https://example.org/crl/root.crl'], $result['urls']);
483+
}
484+
485+
public function testExtractCrlUrlsFromX509LabelExtensionName(): void {
486+
$handler = $this->getInstance();
487+
488+
$method = new \ReflectionMethod('OCA\\Libresign\\Handler\\CertificateEngine\\AEngineHandler', 'extractCrlUrlsFromExtensions');
489+
$method->setAccessible(true);
490+
491+
$result = $method->invoke($handler, [
492+
'X509v3 CRL Distribution Points' => "Full Name:\n URI : https://example.org/crl/issuer.crl",
493+
]);
494+
495+
$this->assertTrue($result['hasExtension']);
496+
$this->assertSame(['https://example.org/crl/issuer.crl'], $result['urls']);
497+
}
498+
471499
public function testRealCertificateRevocationInCrl(): void {
472500
$this->caIdentifierService->generateCaId('openssl');
473501

tests/php/Unit/Service/IdentifyMethod/PasswordTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,8 @@ public static function providerValidateToSignWithCertificateData(): array {
290290
'validTo_time_t' => $futureTimestamp,
291291
'crl_validation' => CrlValidationStatus::MISSING,
292292
],
293-
'shouldThrow' => false,
293+
'shouldThrow' => true,
294+
'expectedCode' => 422,
294295
],
295296
'revoked and expired certificate' => [
296297
'certificateData' => [
@@ -305,7 +306,8 @@ public static function providerValidateToSignWithCertificateData(): array {
305306
'validTo_time_t' => $futureTimestamp,
306307
'crl_validation' => CrlValidationStatus::MISSING,
307308
],
308-
'shouldThrow' => false,
309+
'shouldThrow' => true,
310+
'expectedCode' => 422,
309311
],
310312
'valid certificate - old date but valid (1970s timestamp)' => [
311313
'certificateData' => [

0 commit comments

Comments
 (0)