matteocorti/check_ssl_cert

crash with expired cert when using floating point numbers

Closed this issue · 7 comments

Describe the bug

When the certificate actually expired, but -w or -c specifies a floating point number, the script crashes.

To Reproduce

lukas@htznr2:~$ /home/lukas/check_ssl_cert -w 30.4 -c 10 -H expired.badssl.com
/home/lukas/check_ssl_cert: 2236: Illegal number: -2930.31
lukas@htznr2:~$ /home/lukas/check_ssl_cert -w 30 -c 10.2 -H expired.badssl.com
/home/lukas/check_ssl_cert: 2236: Illegal number: -2930.31
lukas@htznr2:~$ /home/lukas/check_ssl_cert -w 0.01 -c 0 -H expired.badssl.com
/home/lukas/check_ssl_cert: 2236: Illegal number: -2930.32
lukas@htznr2:~$

Expected behavior

  • floating point numbers in -c or -w should not crash when the cert is expired
  • script should allow -w and -c to be the same, to disable warnings
  • I want to use -w 0 -c 0 for some external services, because in a lot of cases the problem is only actionable when it's actually too late (for external services not under my administrative control)

System (please complete the following information):

  • OS: Ubuntu
  • OS version: 20.04.6 LTS
  • check_ssl_cert version: 2.64.0
  • OpenSSL version (openssl version): 1.1.1f

Additional context/output

$ /home/lukas/check_ssl_cert -w 0.01 -c 0 -H expired.badssl.com -d -v
[DBG] check_ssl_cert version: 2.64.0
[DBG] System info: Linux htznr2 5.4.0-146-generic #163-Ubuntu SMP Fri Mar 17 18:26:02 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
[DBG] /etc/os-release:
[DBG]   NAME="Ubuntu"
[DBG]   VERSION="20.04.6 LTS (Focal Fossa)"
[DBG]   ID=ubuntu
[DBG]   ID_LIKE=debian
[DBG]   PRETTY_NAME="Ubuntu 20.04.6 LTS"
[DBG]   VERSION_ID="20.04"
[DBG]   HOME_URL="https://www.ubuntu.com/"
[DBG]   SUPPORT_URL="https://help.ubuntu.com/"
[DBG]   BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
[DBG]   PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
[DBG]   VERSION_CODENAME=focal
[DBG]   UBUNTU_CODENAME=focal
[DBG] User: lukas
[DBG] Shell: /bin/bash
[DBG]   GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
[DBG]   Copyright (C) 2019 Free Software Foundation, Inc.
[DBG]   License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
[DBG]
[DBG]   This is free software; you are free to change and redistribute it.
[DBG]   There is NO WARRANTY, to the extent permitted by law.
[DBG] grep: /bin/grep
[DBG]   grep (GNU grep) 3.4
[DBG]   Copyright (C) 2020 Free Software Foundation, Inc.
[DBG]   License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
[DBG]   This is free software: you are free to change and redistribute it.
[DBG]   There is NO WARRANTY, to the extent permitted by law.
[DBG]
[DBG]   Written by Mike Haertel and others; see
[DBG]   <https://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>.
[DBG] hostname: /bin/hostname
[DBG] $PATH: /home/lukas/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
[DBG] Command line arguments: -w 0.01 -c 0 -H expired.badssl.com -d -v
[DBG]   TMPDIR = /tmp
[DBG] Required HTTP headers:
[DBG] Unrequired HTTP headers:
[DBG] Checking if the host (expired.badssl.com) exists
[DBG] HOST = expired.badssl.com
[DBG] SNI                 =
[DBG] HOST_NAME           = expired.badssl.com
[DBG] HOST_ADDR           = expired.badssl.com
[DBG] NAMES_TO_BE_CHECKED = __HOST__
[DBG] Checking if expired.badssl.com is an IP address
[DBG] expired.badssl.com is not an IP address
[DBG] HOST_IS_IP.         = 0
[DBG] Checking if expired.badssl.com is an IP address
[DBG] expired.badssl.com is not an IP address
[DBG] Adding expired.badssl.com to NAMES_TO_BE_CHECKED
[DBG] NAMES_TO_BE_CHECKED = expired.badssl.com
[DBG] curl binary needed. SSL Labs = , OCSP = 1, CURL = , IGNORE_CONNECTION_STATE=, FILE_URI=
[DBG] curl binary not specified
[DBG] curl available: /usr/bin/curl
[DBG] curl 7.68.0 (x86_64-pc-linux-gnu) libcurl/7.68.0 OpenSSL/1.1.1f zlib/1.2.11 brotli/1.0.7 libidn2/2.2.0 libpsl/0.21.0 (+libidn2/2.2.0) libssh/0.9.3/openssl/zlib nghttp2/1.40.0 librtmp/2.3
[DBG] Release-Date: 2020-01-08
[DBG] Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp
[DBG] Features: AsynchDNS brotli GSS-API HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM NTLM_WB PSL SPNEGO SSL TLS-SRP UnixSockets
[DBG] -c specified: 0
[DBG] -w specified: 0.01
[DBG] Executing comparison '864 <= 0'
[DBG]   bc result = 0
[DBG]   returning 1
[DBG] ROOT_CA =
[DBG] mktemp available: /bin/mktemp
[DBG] file version: file-5.38
[DBG] magic file from /etc/magic:/usr/share/misc/magic
[DBG] nmap binary not specified
[DBG] nmap available: /usr/bin/nmap
[DBG] Checking IPs: host expired.badssl.com
[DBG] perl available: /usr/bin/perl
[DBG] date available: /bin/date
[DBG] checking date version
[DBG] date computation type: GNU
[DBG] OpenSSL binary: /usr/bin/openssl
[DBG] OpenSSL info:
[DBG] OpenSSL 1.1.1f  31 Mar 2020
[DBG] built on: Mon Feb  6 17:57:17 2023 UTC
[DBG] platform: debian-amd64
[DBG] options:  bn(64,64) rc4(16x,int) des(int) blowfish(ptr)
[DBG] compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -fdebug-prefix-map=/build/openssl-7mt7n4/openssl-1.1.1f=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_TLS_SECURITY_LEVEL=2 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAESNI_ASM -DVPAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPOLY1305_ASM -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2
[DBG] OPENSSLDIR: "/usr/lib/ssl"
[DBG] ENGINESDIR: "/usr/lib/x86_64-linux-gnu/engines-1.1"
[DBG] Seeding source: os-specific
[DBG] OpenSSL configuration directory: /usr/lib/ssl
[DBG] 0 root certificates installed by default
[DBG] Date computation: GNU
[DBG] '/usr/bin/openssl s_client' supports '-servername': using -servername expired.badssl.com
[DBG] Proxy settings (before):
[DBG]   http_proxy  =
[DBG]   https_proxy =
[DBG]   HTTP_PROXY  =
[DBG]   HTTPS_PROXY =
[DBG] Proxy settings (after):
[DBG]   http_proxy  =
[DBG]   https_proxy =
[DBG]   HTTP_PROXY  =
[DBG]   HTTPS_PROXY =
[DBG]   s_client    =
[DBG]   curl        =
[DBG] '/usr/bin/openssl s_client' supports '-name': using htznr2
[DBG] '/usr/bin/openssl s_client' supports '-xmpphost': using -xmpphost expired.badssl.com
[DBG] HOST_HEADER = expired.badssl.com
[DBG] Testing connection with expired.badssl.com:443
[DBG] Executing: '/usr/bin/nmap  --unprivileged -Pn -p 443 expired.badssl.com'
[DBG] Sanity checks: OK
[DBG] temporary file /tmp/sXWE17 created
[DBG] temporary file /tmp/XR15xV created
[DBG] temporary file /tmp/wNsKJF created
[DBG] temporary file /tmp/435JZZ created
[DBG] temporary file /tmp/1enVPS created
[DBG] temporary file /tmp/jltOVI created
[DBG] temporary file /tmp/9QtOF3 created
[DBG] Temporary files created
[DBG] expired.badssl.com is not an IP address
[DBG] fetch_certificate: PROTOCOL =
[DBG] exec_with_timeout printf 'HEAD / HTTP/1.1
[DBG] Host: expired.badssl.com
[DBG] User-Agent: check_ssl_cert/2.64.0
[DBG] Connection: close
[DBG]
[DBG] ' | /usr/bin/openssl s_client     -crlf  -connect expired.badssl.com:443 -servername expired.badssl.com   -showcerts -verify 6        2> /tmp/XR15xV 1> /tmp/sXWE17
[DBG]   TIMEOUT_REASON = fetching certificate
[DBG] executing with timeout (120s): printf 'HEAD / HTTP/1.1
[DBG] Host: expired.badssl.com
[DBG] User-Agent: check_ssl_cert/2.64.0
[DBG] Connection: close
[DBG]
[DBG] ' | /usr/bin/openssl s_client     -crlf  -connect expired.badssl.com:443 -servername expired.badssl.com   -showcerts -verify 6        2> /tmp/XR15xV 1> /tmp/sXWE17
[DBG]   start time = 1682062215
[DBG] /usr/bin/timeout 120 /bin/sh -c "printf 'HEAD / HTTP/1.1
[DBG] Host: expired.badssl.com
[DBG] User-Agent: check_ssl_cert/2.64.0
[DBG] Connection: close
[DBG]
[DBG] ' | /usr/bin/openssl s_client     -crlf  -connect expired.badssl.com:443 -servername expired.badssl.com   -showcerts -verify 6        2> /tmp/XR15xV 1> /tmp/sXWE17"
[DBG]   end time = 1682062216
[DBG]   new timeout = 119
[DBG] Return value of the command = 0
[DBG] Negotiated protocol:
[DBG] openssl_version 3.0.0
[DBG] Checking if OpenSSL version is at least 3.0.0 ( '3' '0' '0' ':0' )
[DBG] openssl version: OpenSSL 1.1.1f  31 Mar 2020
[DBG] Current version 1.1.1f ( '1' '1' '1' 'f:102' )
[DBG]   false
[DBG] checking TLS renegotiation
[DBG] exec_with_timeout printf 'R
[DBG] ' | /usr/bin/openssl s_client   -crlf -connect expired.badssl.com:443 -servername expired.badssl.com   2>&1 | /bin/grep -F -q err
[DBG]   TIMEOUT_REASON = checking TLS renegotiation
[DBG] executing with timeout (119s): printf 'R
[DBG] ' | /usr/bin/openssl s_client   -crlf -connect expired.badssl.com:443 -servername expired.badssl.com   2>&1 | /bin/grep -F -q err
[DBG]   start time = 1682062216
[DBG] /usr/bin/timeout 119 /bin/sh -c "printf 'R
[DBG] ' | /usr/bin/openssl s_client   -crlf -connect expired.badssl.com:443 -servername expired.badssl.com   2>&1 | /bin/grep -F -q err"
[DBG]   end time = 1682062216
[DBG]   new timeout = 119
TLS renegotiation OK
[DBG] extracting cert attribute enddate
[DBG] extracting cert attribute startdate
[DBG] extracting cert attribute cn
[DBG] extracting cert attribute subject
[DBG] SUBJECT = subject=OU = Domain Control Validated, OU = PositiveSSL Wildcard, CN = *.badssl.com
[DBG] extracting cert attribute serial
[DBG] SERIAL = 4AE79549FA9ABE3F100F17A478E16909
[DBG] extracting cert attribute version
[DBG] X509_VERSION = 3 (0x2)
[DBG] extracting cert attribute fingerprint
[DBG] FINGERPRINT = 40:4B:BD:2F:1F:4C:C2:FD:EE:F1:3A:AB:DD:52:3E:F6:1F:1C:71:F3
[DBG] Checking if x509 supports the -ext option
[DBG] extracting cert attribute keyUsage
[DBG] Certificate purpose is not defined as critical
[DBG] extracting cert attribute oscp_uri_single
[DBG] extracting cert attribute oscp_uri
[DBG] OCSP_URI = http://ocsp.comodoca.com
[DBG] Extracting issuers
[DBG]   Number of certificates in the chain: 3
[DBG] Checking certificate chain
[DBG]     extracting issuer for element 1
[DBG] extracting cert attribute issuer
[DBG] ELEMENT_ISSUER=COMODO CA Limited
[DBG] ELEMENT_ISSUER=COMODO RSA Domain Validation Secure Server CA
[DBG] ISSUERS=COMODO CA Limited
[DBG] ISSUERS=COMODO RSA Domain Validation Secure Server CA
[DBG]     extracting issuer for element 2
[DBG] extracting cert attribute issuer
[DBG] ELEMENT_ISSUER=COMODO CA Limited
[DBG] ELEMENT_ISSUER=COMODO RSA Certification Authority
[DBG] ISSUERS=COMODO CA Limited
[DBG] ISSUERS=COMODO RSA Domain Validation Secure Server CA
[DBG] ISSUERS=COMODO CA Limited
[DBG] ISSUERS=COMODO RSA Certification Authority
[DBG]     extracting issuer for element 3
[DBG] extracting cert attribute issuer
[DBG] ELEMENT_ISSUER=AddTrust AB
[DBG] ELEMENT_ISSUER=AddTrust External CA Root
[DBG] ISSUERS=COMODO CA Limited
[DBG] ISSUERS=COMODO RSA Domain Validation Secure Server CA
[DBG] ISSUERS=COMODO CA Limited
[DBG] ISSUERS=COMODO RSA Certification Authority
[DBG] ISSUERS=AddTrust AB
[DBG] ISSUERS=AddTrust External CA Root
[DBG] Certificate chain check finished
[DBG] ISSUERS =
[DBG] COMODO CA Limited
[DBG] COMODO RSA Domain Validation Secure Server CA
[DBG] COMODO CA Limited
[DBG] COMODO RSA Certification Authority
[DBG] AddTrust AB
[DBG] AddTrust External CA Root
[DBG] extracting cert attribute issuer_uri_single
[DBG] extracting cert attribute issuer_uri
[DBG] extracting cert attribute sig_algo
[DBG] subject=OU = Domain Control Validated, OU = PositiveSSL Wildcard, CN = *.badssl.com
[DBG] CN         = *.badssl.com
[DBG] CA         = COMODO CA Limited
[DBG] CA         = COMODO RSA Domain Validation Secure Server CA
[DBG] CA         = COMODO CA Limited
[DBG] CA         = COMODO RSA Certification Authority
[DBG] CA         = AddTrust AB
[DBG] CA         = AddTrust External CA Root
[DBG] SERIAL     = 4AE79549FA9ABE3F100F17A478E16909
[DBG] FINGERPRINT= 40:4B:BD:2F:1F:4C:C2:FD:EE:F1:3A:AB:DD:52:3E:F6:1F:1C:71:F3
[DBG] OCSP_URI   = http://ocsp.comodoca.com
[DBG] ISSUER_URI = http://crt.comodoca.com/COMODORSADomainValidationSecureServerCA.crt
[DBG] rsaEncryption (2048 bit)
[DBG] extracting cert attribute subjectAlternativeName
[DBG] subjectAlternativeName = *.badssl.com badssl.com
The certificate for this site contains a Subject Alternative Name extension
[DBG] Check the common name and alternative names
[DBG] CN                       = *.badssl.com
[DBG] SUBJECT_ALTERNATIVE_NAME = *.badssl.com
[DBG] SUBJECT_ALTERNATIVE_NAME = badssl.com
[DBG] ALTNAMES                 = 1
[DBG] NAMES_TO_BE_CHECKED      = expired.badssl.com
[DBG]   checking 'expired.badssl.com'
[DBG]     common name
[DBG]         checking (1) if expired.badssl.com matches ^[A-Za-z0-9_-]*[.]badssl[.]com$
[DBG]         expired.badssl.com matches ^[A-Za-z0-9_-]*[.]badssl[.]com$
[DBG]       checking (2) if the expired.badssl.com matches ^*.badssl.com$
[DBG]     alternative names
[DBG]       check altname: *.badssl.com
[DBG]         the altname *.badssl.com begins with a '*'
[DBG]         checking if (4) expired.badssl.com matches ^badssl.com$
[DBG]       checking if (5) expired.badssl.com matches ^[A-Za-z0-9_-]*[.]badssl[.]com$
[DBG]         expired.badssl.com matches ^[A-Za-z0-9_-]*[.]badssl[.]com$
[DBG]         checking if (6) expired.badssl.com matches ^*.badssl.com$
[DBG]       check altname: badssl.com
[DBG]  CN check finished
[DBG] Checking expiration date
[DBG] Number of certificates in CA chain: 3
[DBG] ------------------------------------------------------------------------------
[DBG] -- Checking element 1
[DBG] extracting cert attribute cn
[DBG] Checking expiration date of element 1 (*.badssl.com)
[DBG] extracting cert attribute enddate
[DBG] Validity date on cert element 1 (*.badssl.com) is Apr 12 23:59:59 2015 GMT
[DBG] Date computations: GNU
[DBG] Computing number of hours until 'Apr 12 23:59:59 2015 GMT' with GNU
[DBG] Computing '(1428883199-1682062217)/3600' (precision 2)
[DBG] Hours until Apr 12 23:59:59 2015 GMT: -70327.50
[DBG] Computing '-70327.50/24' (precision 2)
[DBG] Computing '-70327.50 * 3600' (precision 2)
[DBG] Adding line to prometheus days output: cert_days_chain_elem{cn="*.badssl.com", element="1"} -2930.31
[DBG]   valid for -2930.31 days
[DBG] executing: /usr/bin/openssl x509 -noout -checkend 0 on cert element 1 (*.badssl.com)
[DBG] Executing comparison '-2930.31 >= 0'
[DBG]   bc result = 0
[DBG]   returning 1
/home/lukas/check_ssl_cert: 2236: Illegal number: -2930.31
$

Thanks
I can confirm the problem. Floats work if the cert is not expired ...

The compare() function is not using the scale parameter

Could you also allow -w 0 -c 0 to disable warnings altogether? Really I'm using -w 0.1 -c 0 to workaround the fact that I can't disable warnings.

Could you also allow -w 0 -c 0 to disable warnings altogether? Really I'm using -w 0.1 -c 0 to workaround the fact that I can't disable warnings.

Why not :-) I'll first fix the bug then look at it. You can also try --ignore-exp

Ok thanks.

I don't want to ignore the expiration date though (--ignore-exp) - which also requires --ignore-sct.

I want a critical error when the certificate is expired, but not earlier. I also don't want warnings.

The use-case is that in certain cases I want to know when a certificate is actually expired, but I don't want any errors or warnings when the certificate is still valid, even if its only for 1 hour.

Ok thanks.

I don't want to ignore the expiration date though (--ignore-exp) - which also requires --ignore-sct.

I want a critical error when the certificate is expired, but not earlier. I also don't want warnings.

The use-case is that in certain cases I want to know when a certificate is actually expired, but I don't want any errors or warnings when the certificate is still valid, even if its only for 1 hour.

Makes sense. I removed the check in the last commit.

Perfect, thank you!