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.
pull/15601/head
Antoine Abou Faysal 4 months ago committed by Victor Julien
parent 899eb38691
commit a3ab00e920

@ -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];

Loading…
Cancel
Save