From ad0e05a1123a341be95a06419dc6b029e84a2a31 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Wed, 9 Nov 2011 11:37:12 +0100 Subject: [PATCH] TLS app layer: Add tls.issuerdn keyword. --- src/app-layer-ssl.h | 1 + src/app-layer-tls-handshake.c | 17 ++- src/detect-tls.c | 211 +++++++++++++++++++++++++++++++++- src/detect-tls.h | 1 + src/detect.h | 1 + 5 files changed, 221 insertions(+), 10 deletions(-) diff --git a/src/app-layer-ssl.h b/src/app-layer-ssl.h index 55ef5a9574..c8aaed1f9f 100644 --- a/src/app-layer-ssl.h +++ b/src/app-layer-ssl.h @@ -98,6 +98,7 @@ typedef struct SSLState_ { uint8_t compressionmethod; char *cert0_subject; + char *cert0_issuerdn; /* buffer for the tls record. * We use a malloced buffer, if the record is fragmented */ diff --git a/src/app-layer-tls-handshake.c b/src/app-layer-tls-handshake.c index fc596f82dd..8e057a1815 100644 --- a/src/app-layer-tls-handshake.c +++ b/src/app-layer-tls-handshake.c @@ -93,7 +93,7 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uin uint32_t certificates_length, cur_cert_length; int i; Asn1Generic *cert; - char subject[256]; + char buffer[256]; int rc; if (input_len < 3) @@ -118,13 +118,22 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uin SCLogWarning(SC_ERR_ALPARSER, "decoding ASN.1 structure for X509 certificate failed\n"); } if (cert != NULL) { - rc = Asn1DerGetSubjectDN(cert, subject, sizeof(subject)); + rc = Asn1DerGetSubjectDN(cert, buffer, sizeof(buffer)); if (rc != 0) { SCLogWarning(SC_ERR_ALPARSER, "X509: could not get subject\n"); } else { - //SCLogInfo("TLS Cert %d: %s\n", i, subject); + //SCLogInfo("TLS Cert %d: %s\n", i, buffer); if (i==0) { - ssl_state->cert0_subject = SCStrdup(subject); + ssl_state->cert0_subject = SCStrdup(buffer); + } + } + rc = Asn1DerGetIssuerDN(cert, buffer, sizeof(buffer)); + if (rc != 0) { + SCLogWarning(SC_ERR_ALPARSER, "X509: could not get issuerdn\n"); + } else { + //SCLogInfo("TLS IssuerDN %d: %s\n", i, buffer); + if (i==0) { + ssl_state->cert0_issuerdn = SCStrdup(buffer); } } DerFree(cert); diff --git a/src/detect-tls.c b/src/detect-tls.c index 01ccab74f5..13098956f5 100644 --- a/src/detect-tls.c +++ b/src/detect-tls.c @@ -65,13 +65,19 @@ */ #define PARSE_REGEX "^\\s*([A-z0-9\\.]+|\"[A-z0-9\\.]+\")\\s*$" -static pcre *parse_regex; -static pcre_extra *parse_regex_study; +static pcre *subject_parse_regex; +static pcre_extra *subject_parse_regex_study; +static pcre *issuerdn_parse_regex; +static pcre_extra *issuerdn_parse_regex_study; static int DetectTlsSubjectMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *); static int DetectTlsSubjectSetup (DetectEngineCtx *, Signature *, char *); static void DetectTlsSubjectRegisterTests(void); static void DetectTlsSubjectFree(void *); +static int DetectTlsIssuerDNMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *); +static int DetectTlsIssuerDNSetup (DetectEngineCtx *, Signature *, char *); +static void DetectTlsIssuerDNRegisterTests(void); +static void DetectTlsIssuerDNFree(void *); /** * \brief Registration function for keyword: tls.version @@ -85,20 +91,43 @@ void DetectTlsRegister (void) { sigmatch_table[DETECT_AL_TLS_SUBJECT].Free = DetectTlsSubjectFree; sigmatch_table[DETECT_AL_TLS_SUBJECT].RegisterTests = DetectTlsSubjectRegisterTests; + sigmatch_table[DETECT_AL_TLS_ISSUERDN].name = "tls.issuerdn"; + sigmatch_table[DETECT_AL_TLS_ISSUERDN].Match = NULL; + sigmatch_table[DETECT_AL_TLS_ISSUERDN].AppLayerMatch = DetectTlsIssuerDNMatch; + sigmatch_table[DETECT_AL_TLS_ISSUERDN].alproto = ALPROTO_TLS; + sigmatch_table[DETECT_AL_TLS_ISSUERDN].Setup = DetectTlsIssuerDNSetup; + sigmatch_table[DETECT_AL_TLS_ISSUERDN].Free = DetectTlsIssuerDNFree; + sigmatch_table[DETECT_AL_TLS_ISSUERDN].RegisterTests = DetectTlsIssuerDNRegisterTests; + const char *eb; int eo; int opts = 0; SCLogDebug("registering tls.subject rule option"); - parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL); - if (parse_regex == NULL) { + subject_parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL); + if (subject_parse_regex == NULL) { SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb); goto error; } - parse_regex_study = pcre_study(parse_regex, 0, &eb); + subject_parse_regex_study = pcre_study(subject_parse_regex, 0, &eb); + if (eb != NULL) { + SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb); + goto error; + } + + SCLogDebug("registering tls.issuerdn rule option"); + + issuerdn_parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL); + if (issuerdn_parse_regex == NULL) { + SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s", + PARSE_REGEX, eo, eb); + goto error; + } + + issuerdn_parse_regex_study = pcre_study(issuerdn_parse_regex, 0, &eb); if (eb != NULL) { SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb); goto error; @@ -162,7 +191,7 @@ static DetectTlsData *DetectTlsSubjectParse (char *str) int ret = 0, res = 0; int ov[MAX_SUBSTRINGS]; - ret = pcre_exec(parse_regex, parse_regex_study, str, strlen(str), 0, 0, + ret = pcre_exec(subject_parse_regex, subject_parse_regex_study, str, strlen(str), 0, 0, ov, MAX_SUBSTRINGS); if (ret < 1 || ret > 3) { @@ -282,3 +311,173 @@ static void DetectTlsSubjectFree(void *ptr) { */ static void DetectTlsSubjectRegisterTests(void) { } + +/** + * \brief match the specified IssuerDN on a tls session + * + * \param t pointer to thread vars + * \param det_ctx pointer to the pattern matcher thread + * \param p pointer to the current packet + * \param m pointer to the sigmatch that we will cast into DetectTlsData + * + * \retval 0 no match + * \retval 1 match + */ +static int DetectTlsIssuerDNMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m) +{ + SCEnter(); + + DetectTlsData *tls_data = (DetectTlsData *)m->ctx; + SSLState *ssl_state = (SSLState *)state; + if (ssl_state == NULL) { + SCLogDebug("no tls state, no match"); + SCReturnInt(0); + } + + int ret = 0; + SCMutexLock(&f->m); + + if (ssl_state->cert0_issuerdn != NULL) { + SCLogDebug("TLS: IssuerDN is [%s], looking for [%s]\n", ssl_state->cert0_issuerdn, tls_data->issuerdn); + + if (strstr(ssl_state->cert0_issuerdn, tls_data->issuerdn) != NULL) { + ret = 1; + } + } + + SCMutexUnlock(&f->m); + + SCReturnInt(ret); +} + +/** + * \brief This function is used to parse IPV4 ip_id passed via keyword: "id" + * + * \param idstr Pointer to the user provided id option + * + * \retval id_d pointer to DetectTlsData on success + * \retval NULL on failure + */ +static DetectTlsData *DetectTlsIssuerDNParse(char *str) +{ + DetectTlsData *tls = NULL; +#define MAX_SUBSTRINGS 30 + int ret = 0, res = 0; + int ov[MAX_SUBSTRINGS]; + + ret = pcre_exec(issuerdn_parse_regex, issuerdn_parse_regex_study, str, strlen(str), 0, 0, + ov, MAX_SUBSTRINGS); + + if (ret < 1 || ret > 3) { + SCLogError(SC_ERR_PCRE_MATCH, "invalid tls.issuerdn option"); + goto error; + } + + if (ret > 1) { + const char *str_ptr; + char *orig; + char *tmp_str; + res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + /* We have a correct id option */ + tls = SCMalloc(sizeof(DetectTlsData)); + if (tls == NULL) + goto error; + tls->issuerdn = NULL; + + orig = SCStrdup((char*)str_ptr); + tmp_str=orig; + if (tmp_str == NULL) { + goto error; + } + + /* Let's see if we need to escape "'s */ + if (tmp_str[0] == '"') + { + tmp_str[strlen(tmp_str) - 1] = '\0'; + tmp_str += 1; + } + + tls->issuerdn = SCStrdup(tmp_str); + + SCFree(orig); + + SCLogDebug("will look for TLS issuerdn %s", tls->issuerdn); + } + + return tls; + +error: + if (tls != NULL) + DetectTlsIssuerDNFree(tls); + return NULL; + +} + +/** + * \brief this function is used to add the parsed "id" option + * \brief into the current signature + * + * \param de_ctx pointer to the Detection Engine Context + * \param s pointer to the Current Signature + * \param idstr pointer to the user provided "id" option + * + * \retval 0 on Success + * \retval -1 on Failure + */ +static int DetectTlsIssuerDNSetup (DetectEngineCtx *de_ctx, Signature *s, char *str) +{ + DetectTlsData *tls = NULL; + SigMatch *sm = NULL; + + tls = DetectTlsIssuerDNParse(str); + if (tls == NULL) goto error; + + /* Okay so far so good, lets get this into a SigMatch + * and put it in the Signature. */ + sm = SigMatchAlloc(); + if (sm == NULL) + goto error; + + sm->type = DETECT_AL_TLS_ISSUERDN; + sm->ctx = (void *)tls; + + SigMatchAppendAppLayer(s, sm); + + if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS) { + SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords."); + goto error; + } + + s->alproto = ALPROTO_TLS; + return 0; + +error: + if (tls != NULL) DetectTlsIssuerDNFree(tls); + if (sm != NULL) SCFree(sm); + return -1; + +} + +/** + * \brief this function will free memory associated with DetectTlsData + * + * \param id_d pointer to DetectTlsData + */ +static void DetectTlsIssuerDNFree(void *ptr) +{ + DetectTlsData *id_d = (DetectTlsData *)ptr; + SCFree(id_d->issuerdn); + SCFree(id_d); +} + +/** + * \brief this function registers unit tests for DetectTlsIssuerDN + */ +static void DetectTlsIssuerDNRegisterTests(void) +{ +} diff --git a/src/detect-tls.h b/src/detect-tls.h index cc57cbed92..11e47129d4 100644 --- a/src/detect-tls.h +++ b/src/detect-tls.h @@ -37,6 +37,7 @@ typedef struct DetectTlsData_ { uint16_t ver; /** tls version to match */ char * subject; /** tls certificate subject substring to match */ + char * issuerdn; /** tls certificate issuerDN substring to match */ } DetectTlsData; /* prototypes */ diff --git a/src/detect.h b/src/detect.h index eefd897060..a89df367e6 100644 --- a/src/detect.h +++ b/src/detect.h @@ -988,6 +988,7 @@ enum { DETECT_AL_TLS_VERSION, DETECT_AL_TLS_SUBJECT, + DETECT_AL_TLS_ISSUERDN, DETECT_AL_HTTP_COOKIE, DETECT_AL_HTTP_METHOD,