mirror of
https://github.com/OISF/suricata.git
synced 2026-10-12 02:07:10 +00:00
detect/dataset: add match subdomain option
Ticket: 8385 Add a new match subdomain option that enables blocking a domain and all its subdomains using datasets.
This commit is contained in:
committed by
Victor Julien
parent
899eb38691
commit
a3ab00e920
+72
-2
@@ -49,6 +49,8 @@
|
||||
#define DETECT_DATASET_CMD_ISNOTSET 2
|
||||
#define DETECT_DATASET_CMD_ISSET 3
|
||||
|
||||
#define DATASET_SUBDOMAIN_MAX_LOOKUPS 126
|
||||
|
||||
static int DetectDatasetSetup (DetectEngineCtx *, Signature *, const char *);
|
||||
void DetectDatasetFree (DetectEngineCtx *, void *);
|
||||
|
||||
@@ -62,6 +64,26 @@ void DetectDatasetRegister (void)
|
||||
sigmatch_table[DETECT_DATASET].flags = SIGMATCH_SUPPORT_FIREWALL;
|
||||
}
|
||||
|
||||
/** \brief walk up the domain hierarchy looking for a match in a JSON dataset */
|
||||
static DataJsonResultType DatajsonLookupSubdomain(
|
||||
Dataset *set, const uint8_t *data, const uint32_t data_len)
|
||||
{
|
||||
DataJsonResultType r = {
|
||||
.found = false, .json = { .value = NULL, .len = 0 }, .hashdata = NULL
|
||||
};
|
||||
int lookups = 0;
|
||||
for (uint32_t i = 1; i < data_len; i++) {
|
||||
if (data[i] == '.' && data[i - 1] != '.') {
|
||||
if (++lookups > DATASET_SUBDOMAIN_MAX_LOOKUPS)
|
||||
break;
|
||||
r = DatajsonLookup(set, data + i, data_len - i);
|
||||
if (r.found)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
1 match
|
||||
0 no match
|
||||
@@ -77,6 +99,9 @@ static int DetectDatajsonBufferMatch(DetectEngineThreadCtx *det_ctx, const Detec
|
||||
// PrintRawDataFp(stdout, data, data_len);
|
||||
DataJsonResultType r = DatajsonLookup(sd->set, data, data_len);
|
||||
SCLogDebug("r found: %d, len: %u", r.found, r.json.len);
|
||||
if (!r.found && sd->match_subdomain) {
|
||||
r = DatajsonLookupSubdomain(sd->set, data, data_len);
|
||||
}
|
||||
if (!r.found)
|
||||
return 0;
|
||||
if (r.json.len > 0) {
|
||||
@@ -102,6 +127,9 @@ static int DetectDatajsonBufferMatch(DetectEngineThreadCtx *det_ctx, const Detec
|
||||
// PrintRawDataFp(stdout, data, data_len);
|
||||
DataJsonResultType r = DatajsonLookup(sd->set, data, data_len);
|
||||
SCLogDebug("r found: %d, len: %u", r.found, r.json.len);
|
||||
if (!r.found && sd->match_subdomain) {
|
||||
r = DatajsonLookupSubdomain(sd->set, data, data_len);
|
||||
}
|
||||
if (r.found) {
|
||||
DatajsonUnlockElt(&r);
|
||||
return 0;
|
||||
@@ -114,6 +142,22 @@ static int DetectDatajsonBufferMatch(DetectEngineThreadCtx *det_ctx, const Detec
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** \brief walk up the domain hierarchy looking for a match in a dataset */
|
||||
static int DatasetLookupSubdomain(Dataset *set, const uint8_t *data, const uint32_t data_len)
|
||||
{
|
||||
int lookups = 0;
|
||||
for (uint32_t i = 1; i < data_len; i++) {
|
||||
if (data[i] == '.' && data[i - 1] != '.') {
|
||||
if (++lookups > DATASET_SUBDOMAIN_MAX_LOOKUPS)
|
||||
break;
|
||||
int r = DatasetLookup(set, data + i, data_len - i);
|
||||
if (r == 1)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
1 match
|
||||
0 no match
|
||||
@@ -133,6 +177,9 @@ int DetectDatasetBufferMatch(DetectEngineThreadCtx *det_ctx,
|
||||
case DETECT_DATASET_CMD_ISSET: {
|
||||
//PrintRawDataFp(stdout, data, data_len);
|
||||
int r = DatasetLookup(sd->set, data, data_len);
|
||||
if (r != 1 && sd->match_subdomain) {
|
||||
r = DatasetLookupSubdomain(sd->set, data, data_len);
|
||||
}
|
||||
SCLogDebug("r %d", r);
|
||||
if (r == 1)
|
||||
return 1;
|
||||
@@ -141,6 +188,9 @@ int DetectDatasetBufferMatch(DetectEngineThreadCtx *det_ctx,
|
||||
case DETECT_DATASET_CMD_ISNOTSET: {
|
||||
//PrintRawDataFp(stdout, data, data_len);
|
||||
int r = DatasetLookup(sd->set, data, data_len);
|
||||
if (r != 1 && sd->match_subdomain) {
|
||||
r = DatasetLookupSubdomain(sd->set, data, data_len);
|
||||
}
|
||||
SCLogDebug("r %d", r);
|
||||
if (r < 1)
|
||||
return 1;
|
||||
@@ -169,7 +219,7 @@ static int DetectDatasetParse(const char *str, char *cmd, int cmd_len, char *nam
|
||||
enum DatasetTypes *type, char *load, size_t load_size, char *save, size_t save_size,
|
||||
uint64_t *memcap, uint32_t *hashsize, DatasetFormats *format, char *value_key,
|
||||
size_t value_key_size, char *array_key, size_t array_key_size, char *enrichment_key,
|
||||
size_t enrichment_key_size, bool *remove_key)
|
||||
size_t enrichment_key_size, bool *remove_key, bool *match_subdomain)
|
||||
{
|
||||
bool cmd_set = false;
|
||||
bool name_set = false;
|
||||
@@ -221,6 +271,13 @@ static int DetectDatasetParse(const char *str, char *cmd, int cmd_len, char *nam
|
||||
*remove_key = true;
|
||||
} else
|
||||
return -1;
|
||||
} else if (strcmp(key, "match") == 0) {
|
||||
if (strcmp(val, "subdomain") == 0) {
|
||||
*match_subdomain = true;
|
||||
} else {
|
||||
SCLogError("unknown match mode '%s'", val);
|
||||
return -1;
|
||||
}
|
||||
} else if (strcmp(key, "type") == 0) {
|
||||
SCLogDebug("type %s", val);
|
||||
|
||||
@@ -474,6 +531,7 @@ int DetectDatasetSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawst
|
||||
char array_key[SIG_JSON_CONTENT_KEY_LEN] = "";
|
||||
char enrichment_key[SIG_JSON_CONTENT_KEY_LEN] = "";
|
||||
bool remove_key = false;
|
||||
bool match_subdomain = false;
|
||||
|
||||
if (DetectBufferGetActiveList(de_ctx, s) == -1) {
|
||||
SCLogError("datasets are only supported for sticky buffers");
|
||||
@@ -489,7 +547,7 @@ int DetectDatasetSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawst
|
||||
if (!DetectDatasetParse(rawstr, cmd_str, sizeof(cmd_str), name, sizeof(name), &type, load,
|
||||
sizeof(load), save, sizeof(save), &memcap, &hashsize, &format, value_key,
|
||||
sizeof(value_key), array_key, sizeof(array_key), enrichment_key,
|
||||
sizeof(enrichment_key), &remove_key)) {
|
||||
sizeof(enrichment_key), &remove_key, &match_subdomain)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -514,6 +572,17 @@ int DetectDatasetSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawst
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (match_subdomain) {
|
||||
if (cmd != DETECT_DATASET_CMD_ISSET && cmd != DETECT_DATASET_CMD_ISNOTSET) {
|
||||
SCLogError("'match subdomain' only supports isset/isnotset commands");
|
||||
return -1;
|
||||
}
|
||||
if (type != DATASET_TYPE_STRING) {
|
||||
SCLogError("'match subdomain' only supports type string");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((format == DATASET_FORMAT_JSON) || (format == DATASET_FORMAT_NDJSON)) {
|
||||
if (strlen(save) != 0) {
|
||||
SCLogError("json format is not supported with 'save' or 'state' option");
|
||||
@@ -579,6 +648,7 @@ int DetectDatasetSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawst
|
||||
cd->set = set;
|
||||
cd->cmd = cmd;
|
||||
cd->format = format;
|
||||
cd->match_subdomain = match_subdomain;
|
||||
if ((format == DATASET_FORMAT_JSON) || (format == DATASET_FORMAT_NDJSON)) {
|
||||
strlcpy(cd->json_key, enrichment_key, sizeof(cd->json_key));
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
typedef struct DetectDatasetData_ {
|
||||
Dataset *set;
|
||||
uint8_t cmd;
|
||||
bool match_subdomain;
|
||||
DatasetFormats format;
|
||||
DataJsonType json;
|
||||
char json_key[SIG_JSON_CONTENT_KEY_LEN];
|
||||
|
||||
Reference in New Issue
Block a user