diff --git a/src/app-layer-ssl.h b/src/app-layer-ssl.h index 3253ce46d5..31ca913e74 100644 --- a/src/app-layer-ssl.h +++ b/src/app-layer-ssl.h @@ -147,6 +147,8 @@ typedef struct SSLStateConnp_ { char *cert0_subject; char *cert0_issuerdn; + time_t cert0_not_before; + time_t cert0_not_after; char *cert0_fingerprint; /* ssl server name indication extension */ diff --git a/src/app-layer-tls-handshake.c b/src/app-layer-tls-handshake.c index 663471e831..9ae3a10801 100644 --- a/src/app-layer-tls-handshake.c +++ b/src/app-layer-tls-handshake.c @@ -87,6 +87,7 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, int i; Asn1Generic *cert; char buffer[256]; + time_t not_before, not_after; int rc; int parsed; uint8_t *start_data; @@ -176,6 +177,16 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, } } + rc = Asn1DerGetValidity(cert, ¬_before, ¬_after, &errcode); + if (rc != 0) { + TLSCertificateErrCodeToWarning(ssl_state, errcode); + } else { + if (i == 0) { + ssl_state->server_connp.cert0_not_before = not_before; + ssl_state->server_connp.cert0_not_after = not_after; + } + } + DerFree(cert); if (i == 0 && ssl_state->server_connp.cert0_fingerprint == NULL) { diff --git a/src/util-decode-der-get.c b/src/util-decode-der-get.c index 1c556c837d..413c431581 100644 --- a/src/util-decode-der-get.c +++ b/src/util-decode-der-get.c @@ -38,6 +38,7 @@ #include "util-decode-der-get.h" static const uint8_t SEQ_IDX_ISSUER[] = { 0, 2 }; +static const uint8_t SEQ_IDX_VALIDITY[] = { 0, 3 }; static const uint8_t SEQ_IDX_SUBJECT[] = { 0, 4 }; static const char *Oid2ShortStr(const char *oid) @@ -72,6 +73,68 @@ static const char *Oid2ShortStr(const char *oid) return "unknown"; } +static time_t GentimeToTime(char *gentime) +{ + time_t time; + struct tm tm; + + /* GeneralizedTime values MUST be expressed in Greenwich Mean Time + * (Zulu) and MUST include seconds (rfc5280 4.1.2.5.2). It MUST NOT + * include fractional seconds. It should therefore be on the format + * YYYYmmddHHMMSSZ. */ + if (strlen(gentime) != 15) + goto error; + + tm.tm_gmtoff = 0; + strptime(gentime, "%Y%m%d%H%M%SZ", &tm); + time = SCMkTimeUtc(&tm); + + if (time < 0) + goto error; + + return time; + +error: + return -1; +} + +static time_t UtctimeToTime(char *utctime) +{ + time_t time; + unsigned int year; + char yy[2]; + char buf[20]; + + /* UTCTime values MUST be expressed in Greenwich Mean Time (Zulu) + * and MUST include seconds (rfc5280 4.1.2.5.1). It should + * therefore be on the format YYmmddHHMMSSZ. */ + if (strlen(utctime) != 13) + goto error; + + /* UTCTime use two digits to represent the year. The year field (YY) + * should be interpreted as 19YY when it is greater than or equal to + * 50. If it is less than 50 it should be interpreted as 20YY. + * Because of this, GeneralizedTime must be used for dates in the + * year 2050 or later. */ + strlcpy(yy, utctime, sizeof(yy)); + year = strtol(yy, NULL, 10); + if (year >= 50) + snprintf(buf, sizeof(buf), "%i%s", 19, utctime); + else if (year < 50) + snprintf(buf, sizeof(buf), "%i%s", 20, utctime); + else + goto error; + + time = GentimeToTime(buf); + if (time == -1) + goto error; + + return time; + +error: + return -1; +} + /** * \brief Iterate through an ASN.1 structure, following the index sequence. * Context specific elements are skipped. @@ -129,6 +192,57 @@ const Asn1Generic * Asn1DerGet(const Asn1Generic *top, const uint8_t *seq_index, return node; } +int Asn1DerGetValidity(const Asn1Generic *cert, time_t *not_before, + time_t *not_after, uint32_t *errcode) +{ + const Asn1Generic *node, *it; + int rc = -1; + + if (errcode) + *errcode = ERR_DER_MISSING_ELEMENT; + + node = Asn1DerGet(cert, SEQ_IDX_VALIDITY, sizeof(SEQ_IDX_VALIDITY), errcode); + if ((node == NULL) || node->type != ASN1_SEQUENCE) + goto validity_error; + + it = node->data; + if (it == NULL || it->str == NULL) + goto validity_error; + + if (it->type == ASN1_UTCTIME) + *not_before = UtctimeToTime(it->str); + else if (it->type == ASN1_GENERALIZEDTIME) + *not_before = GentimeToTime(it->str); + else + goto validity_error; + + if (*not_before == -1) + goto validity_error; + + if (node->next == NULL) + goto validity_error; + + it = node->next->data; + + if (it == NULL || it->str == NULL) + goto validity_error; + + if (it->type == ASN1_UTCTIME) + *not_after = UtctimeToTime(it->str); + else if (it->type == ASN1_GENERALIZEDTIME) + *not_after = GentimeToTime(it->str); + else + goto validity_error; + + if (*not_after == -1) + goto validity_error; + + rc = 0; + +validity_error: + return rc; +} + int Asn1DerGetIssuerDN(const Asn1Generic *cert, char *buffer, uint32_t length, uint32_t *errcode) { diff --git a/src/util-decode-der-get.h b/src/util-decode-der-get.h index 79ecd7879c..2d2168aeb3 100644 --- a/src/util-decode-der-get.h +++ b/src/util-decode-der-get.h @@ -38,6 +38,7 @@ const Asn1Generic * Asn1DerGet(const Asn1Generic *top, const uint8_t *seq_index, const uint32_t seqsz, uint32_t *errcode); int Asn1DerGetIssuerDN(const Asn1Generic *cert, char *buffer, uint32_t length, uint32_t *errcode); +int Asn1DerGetValidity(const Asn1Generic *cert, time_t *not_before, time_t *not_after, uint32_t *errcode); int Asn1DerGetSubjectDN(const Asn1Generic *cert, char *buffer, uint32_t length, uint32_t *errcode); #endif /* __UTIL_DECODE_DER_GET_H__ */