matteocorti / check_ssl_cert

A shell script (that can be used as a Nagios/Icinga plugin) to check an SSL/TLS connection.
GNU General Public License v3.0
370 stars 132 forks source link

Loop over all CA Issuers #496

Open rlueckl opened 10 months ago

rlueckl commented 10 months ago

Is your feature request related to a problem? Please describe.

Similar to #258 could we also have a feature that the OCSP check loops over all "CA Issuers" found in the certificate? Currently it only looks at the first issuer and if that doesn't match the certificate, the OCSP validation fails.

Our internal CA writes 3 different issuers in the certificate (one old one and two new ones), but only one of them (the last one) matches the certificate.

Describe the solution you'd like

check_ssl_cert should loop over all "CA Issuers" and only throw an error if the certificate can't be validated against any of them.

Additional context

Certificate contains:

        X509v3 extensions:
            Authority Information Access: 
                CA Issuers - URI:http://company.pki.infrastructure/pub/internalissuer_old.cer
                CA Issuers - URI:http://company.pki.infrastructure/pub/internalissuer_new_a.cer
                CA Issuers - URI:http://company.pki.infrastructure/pub/internalissuer_new_b.cer
                OCSP - URI:http://company.ocsp.infrastructure/ocsp

The certificate issuer is internalissuer_new_b.

check_ssl_cert only looks at internalissuer_old. Debug log from testing:

[DBG 1s] ------------------------------------------------------------------------------
[DBG 1s] Checking OCSP status of element 1
[DBG 1s] temporary file /tmp/QkBALW created
[DBG 1s] Storing the chain element in /tmp/QkBALW
[DBG 1s] Checking revocation via OCSP
[DBG 1s] extracting cert attribute issuer_hash
[DBG 1s] Issuer hash: f31059ce
[DBG 1s] extracting cert attribute issuer_uri
[DBG 1s] Chain element issuer URIs: http://company.pki.infrastructure/pub/internalissuer_old.cer
[DBG 1s] http://company.pki.infrastructure/pub/internalissuer_new_a.cer
[DBG 1s] http://company.pki.infrastructure/pub/internalissuer_new_b.cer
[DBG 1s] checking issuer URIs: http://company.pki.infrastructure/pub/internalissuer_old.cer
[DBG 1s] OCSP: fetching issuer certificate http://company.pki.infrastructure/pub/internalissuer_old.cer to /tmp/VfkrIY
[DBG 1s] exec_with_timeout: TIMEOUT=120, CURRENT_TIMEOUT=119, ELAPSED=1
[DBG 1s] exec_with_timeout /usr/bin/curl     --silent --user-agent 'check_ssl_cert/2.78.0' --location \"http://company.pki.infrastructure/pub/internalissuer_old.cer\" > /tmp/VfkrIY
[DBG 1s] executing with timeout (119s): /usr/bin/curl     --silent --user-agent 'check_ssl_cert/2.78.0' --location \"http://company.pki.infrastructure/pub/internalissuer_old.cer\" > /tmp/VfkrIY
[DBG 1s] /usr/bin/timeout 119 /bin/sh -c "/usr/bin/curl     --silent --user-agent 'check_ssl_cert/2.78.0' --location \"http://company.pki.infrastructure/pub/internalissuer_old.cer\" > /tmp/VfkrIY"
[DBG 1s] OCSP: issuer certificate type (1): Certificate, Version=3
[DBG 1s] OCSP: issuer certificate type (2): Certificate, Version=3
[DBG 1s] OCSP: converting issuer certificate from DER to PEM
[DBG 1s] OCSP: issuer certificate type (3): PEM certificate
[DBG 1s] checking issuer URIs: http://company.pki.infrastructure/pub/internalissuer_new_a.cer
[DBG 1s] checking issuer URIs: http://company.pki.infrastructure/pub/internalissuer_new_b.cer
[DBG 1s] extracting cert attribute oscp_uri
[DBG 1s] OCSP: URIs = http://company.ocsp.infrastructure/ocsp
[DBG 1s] OCSP: URI = http://company.ocsp.infrastructure/ocsp
[DBG 1s] OCSP: host = company.ocsp.infrastructure
[DBG 1s] openssl ocsp supports the -header option
[DBG 1s] /usr/bin/openssl ocsp -header requires 'key=value'
[DBG 1s] executing (5) /usr/bin/openssl ocsp -timeout "119" -no_nonce -issuer /tmp/VfkrIY -cert /tmp/QkBALW  -url http://company.ocsp.infrastructure/ocsp  -header HOST=company.ocsp.infrastructure
[DBG 1s] OCSP: response = Responder Error: unauthorized (6)
[DBG 1s] OCSP: not good. HTTP_PROXY = 
[DBG 1s] executing /usr/bin/openssl ocsp -timeout "119" -no_nonce -issuer "/tmp/VfkrIY" -cert "/tmp/QkBALW" -url "http://company.ocsp.infrastructure/ocsp" "" 2>&1
[DBG 1s] Responder Error: unauthorized (6)
CRITICAL error: OCSP error (Responder Error: unauthorized (6))
[DBG 1s] CRITICAL ----------------------------------------
[DBG 1s] prepend_critical_message: new message    = OCSP error (Responder Error: unauthorized (6))
[DBG 1s] prepend_critical_message: CRITICAL_MSG   =
[DBG 1s] prepend_critical_message: ALL_MSG 1      =
[DBG 1s] prepend_critical_message: MSG 2          = SSL_CERT CRITICAL servername01.lan:8443: OCSP error (Responder Error: unauthorized (6))
[DBG 1s] prepend_critical_message: CRITICAL_MSG 2 = SSL_CERT CRITICAL servername01.lan:8443: OCSP error (Responder Error: unauthorized (6))
[DBG 1s] prepend_critical_message: ALL_MSG 2      = \n    SSL_CERT CRITICAL servername01.lan:8443: OCSP error (Responder Error: unauthorized (6))
[DBG 1s] CRITICAL ----------------------------------------

Testing manually:

LIVE root@servername01 ~ # /usr/bin/openssl ocsp -timeout "119" -no_nonce -issuer internalissuer_old.pem -cert servername01.pem  -url http://company.ocsp.infrastructure/ocsp  -header HOST=company.ocsp.infrastructure
Responder Error: unauthorized (6)

LIVE root@servername01 ~ # /usr/bin/openssl ocsp -timeout "119" -no_nonce -issuer internalissuer_new_b.pem -cert servername01.pem  -url http://company.ocsp.infrastructure/ocsp  -header HOST=company.ocsp.infrastructure
Response verify OK
servername01.pem: good
    This Update: Jan 10 12:55:13 2024 GMT
matteocorti commented 10 months ago

I would first check if this really makes sense. Why more then one issuer? Is there a use case? I didn't find any other examples...

But on the other end, the "s" at the end of "Issuers" seems to indicate that more then one should be possible

matteocorti commented 10 months ago

Just as info

Authority Information Access (AIA) is a special extension in SSL certificates that contains information about the issuer of the certificate. This extension helps fetch intermediate certificates from the issuing certification authority. In case if server does not provide intermediate certificates, they could be downloaded from the link contained in the AIA field. This approach allows saving the certificate chain and performing an SSL certificate check, even if the server incorrectly configured. If user's client supports AIA Fetching, then user does not even aware of server configuration errors.

matteocorti commented 10 months ago

Since I do not have host to test. Do you (@rlueckl) have a host I could use for debugging? You could send it to me privately and I would not disclose it.

lukastribus commented 10 months ago

I just had check_ssl_cert correctly return CRITICAL for a revoked certificate due to OCSP a few days ago, so I do think it works currently.

matteocorti commented 10 months ago

I just had check_ssl_cert correctly return CRITICAL for a revoked certificate due to OCSP a few days ago, so I do think it works currently.

I just checked, and it works (I deleted my earlier comments, as the implementation seems correct).

The OCSP client needs the intermediate certificate to perform the check. What should be done if multiple are specified and if one is not OK is not really clear to me.

The extension is there to permit a check even if the server does not deliver the intermediate certificate. On a first thought, it seems to me that an invalid intermediate certificate should not be accepted. On the other hand, since this attribute is there to help in case of a misconfigured server, I do not see a lot of problems by checking all of them.

matteocorti commented 10 months ago

Reference to check

https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1

matteocorti commented 10 months ago

Seems that multiple certifcates are possible:

An authorityInfoAccess extension may include multiple instances of the id-ad-caIssuers accessMethod. The different instances may specify different methods for accessing the same information or may point to different information. When the id-ad-caIssuers accessMethod is used, at least one instance SHOULD specify an accessLocation that is an HTTP [RFC2616] or LDAP [RFC4516] URI.

matteocorti commented 10 months ago

And again: https://security.stackexchange.com/questions/26577/is-an-aia-or-crl-useful-required-at-the-root-ca-if-that-root-is-used-to-cross

The AIA extensions are used to help in path building, by pointing to potential certificates for the issuer. Use of these certificate is not mandatory; mass deployment of intermediate CA certificates is a viable alternative (at least in organized networks which offer facilities for that, e.g. Active Directory servers).

In this case the issue is valid and is a bug

lukastribus commented 10 months ago

I'm not sure if I understand this ticket, but if this is about hiding server configuration issues by downloading intermediate certificates from AIA, I strongly disagree, as previously discussed:

https://github.com/matteocorti/check_ssl_cert/issues/366#issuecomment-1074138086

matteocorti commented 10 months ago

I'm not sure if I understand this ticket, but if this is about hiding server configuration issues by downloading intermediate certificates from AIA, I strongly disagree, as previously discussed:

#366 (comment)

I agree with you. I am not using the information from AIA to validate the chain. A missing intermediate certificate is an error.

According to the RFC the information in the AIA is there to perform the OCSP check.

I still have to check, but I could completely ignore the certificate specified with AIA and send the intermediate from the chain.

I need some time to perform some checks.

rlueckl commented 10 months ago

Sorry, this goes a little bit above my head. :smile:

As far as I understand https://datatracker.ietf.org/doc/html/rfc5280#section-5.2.7 multiple certificates are possible and can be used to validate the chain (as you already wrote). I think the intention of multiple entries was to have a fallback in case one URL isn't reachable.

According to the RFC the information in the AIA is there to perform the OCSP check.

I still have to check, but I could completely ignore the certificate specified with AIA and send the intermediate from the chain.

This would also work for us. Our servers offer the complete chain, no need to download the intermediate from the AIA.

Unfortunately I can't send you a host (even privately), because all the information is confidential. I can give you debug logs with the hostnames, filenames, etc. altered like in the initial message.

matteocorti commented 10 months ago

Sorry, this goes a little bit above my head. 😄

As far as I understand https://datatracker.ietf.org/doc/html/rfc5280#section-5.2.7 multiple certificates are possible and can be used to validate the chain (as you already wrote). I think the intention of multiple entries was to have a fallback in case one URL isn't reachable.

According to the RFC the information in the AIA is there to perform the OCSP check. I still have to check, but I could completely ignore the certificate specified with AIA and send the intermediate from the chain.

This would also work for us. Our servers offer the complete chain, no need to download the intermediate from the AIA.

Unfortunately I can't send you a host (even privately), because all the information is confidential. I can give you debug logs with the hostnames, filenames, etc. altered like in the initial message.

I'll have to craft a local test certificate then ...

grimz-ly commented 6 months ago

maybe wrong to comment here, but I feel it's related.

if local CA root certificate is expired, the certificate chain validation doesn't happen at all and the check returns OK status. Should the test return CRIT if the local CA root cert is expired given it leverages it for these checks?