Skip to content

Commit 7872ab4

Browse files
committed
tlshd: Parse filter definitions
Administrators can define filters which specify a filter type (e.g., which x.509 field is to be checked) and filter arguments (e.g., what content is to be matched). A subsequent patch will add the ability to specify the tags which are added to a TLS session based on filter matches. Signed-off-by: Chuck Lever <[email protected]>
1 parent bbd4f5e commit 7872ab4

File tree

2 files changed

+286
-1
lines changed

2 files changed

+286
-1
lines changed

src/tlshd/etc/tls-session-tags.man

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,24 @@ derived SHA256 fingerrint.
222222
Filters of this type match when an incoming x.509 certificate's
223223
issuer and subject distinguished names are exactly equal.
224224
This filter type is not yet implemented.
225+
.SS Filters
226+
The definition of each filter is YAML mapping that specifies
227+
the unique name of the filter,
228+
its filter type,
229+
and
230+
zero or more specific match arguments to be used.
231+
For example:
232+
233+
filters:
234+
...
235+
monsters-university:
236+
type: "x509.tbs.issuer"
237+
pattern: "*,O=Monsters University,*"
238+
...
239+
240+
This blurb defines a filter named "monsters-university".
241+
It uses a wildcard match that looks for "O=Monsters Univerity"
242+
in the "issuer" field of each incoming x.509 certificate.
225243
.SH STANDARDS
226244
x.509
227245
.BR

src/tlshd/tags.c

Lines changed: 268 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ enum tlshd_tags_fsm_state_index {
6464
PS_DOCUMENT,
6565
PS_TOP_LEVEL,
6666

67+
PS_FILTERS,
68+
PS_FILTER,
69+
PS_FILTER_KEYS,
70+
PS_FILTER_KEY,
71+
PS_FILTER_TYPE_VALUE,
72+
PS_FILTER_PATTERN_VALUE,
73+
PS_FILTER_PURPOSE_LIST,
74+
PS_FILTER_KEY_USAGE,
75+
6776
PS_UNEXPECTED_INPUT_TOKEN,
6877
PS_FAILURE,
6978
};
@@ -79,10 +88,25 @@ struct tlshd_tags_filter_type {
7988

8089
static GHashTable *tlshd_tags_filter_type_hash;
8190

91+
struct tlshd_tags_filter {
92+
gchar *fi_name;
93+
struct tlshd_tags_filter_type *fi_filter_type;
94+
95+
/* filter arguments */
96+
gchar *fi_pattern;
97+
GPatternSpec *fi_pattern_spec;
98+
unsigned int fi_purpose_mask;
99+
time_t fi_time;
100+
};
101+
102+
static GHashTable *tlshd_tags_filter_hash;
103+
82104
struct tlshd_tags_parser_state {
83105
yaml_event_t ps_yaml_event;
84106

85107
enum tlshd_tags_fsm_state_index ps_fsm_state;
108+
109+
struct tlshd_tags_filter *ps_current_filter;
86110
};
87111

88112
static enum tlshd_tags_fsm_state_index
@@ -91,10 +115,205 @@ tlshd_tags_top_level(struct tlshd_tags_parser_state *current)
91115
const yaml_event_t *event = &current->ps_yaml_event;
92116
const char *mapping = (const char *)event->data.scalar.value;
93117

118+
if (strcmp(mapping, "filters") == 0)
119+
return PS_FILTERS;
120+
94121
tlshd_log_error("Unexpected mapping name: %s\n", mapping);
95122
return PS_UNEXPECTED_INPUT_TOKEN;
96123
}
97124

125+
/* --- Filters --- */
126+
127+
static void tlshd_tags_filter_free(struct tlshd_tags_filter *filter)
128+
{
129+
if (!filter)
130+
return;
131+
132+
if (tlshd_debug > 3)
133+
tlshd_log_debug("Removing filter '%s' from the filter hash",
134+
filter->fi_name);
135+
136+
if (filter->fi_pattern_spec)
137+
g_pattern_spec_free(filter->fi_pattern_spec);
138+
g_free(filter->fi_pattern);
139+
g_free(filter->fi_name);
140+
g_free(filter);
141+
}
142+
143+
static void tlshd_tags_filter_hash_destroy(void)
144+
{
145+
GHashTableIter iter;
146+
gpointer key, value;
147+
148+
if (!tlshd_tags_filter_hash)
149+
return;
150+
151+
g_hash_table_iter_init(&iter, tlshd_tags_filter_hash);
152+
while (g_hash_table_iter_next(&iter, &key, &value))
153+
tlshd_tags_filter_free((struct tlshd_tags_filter *)value);
154+
155+
g_hash_table_destroy(tlshd_tags_filter_hash);
156+
tlshd_tags_filter_hash = NULL;
157+
}
158+
159+
static bool
160+
tlshd_tags_filter_hash_init(void)
161+
{
162+
tlshd_tags_filter_hash = g_hash_table_new(g_str_hash, g_str_equal);
163+
return tlshd_tags_filter_hash != NULL;
164+
}
165+
166+
static enum tlshd_tags_fsm_state_index
167+
tlshd_tags_filter_create(struct tlshd_tags_parser_state *current)
168+
{
169+
const yaml_event_t *event = &current->ps_yaml_event;
170+
struct tlshd_tags_filter *filter;
171+
172+
filter = g_malloc0(sizeof(*filter));
173+
if (!filter) {
174+
tlshd_log_error("Failed to allocate new filter\n");
175+
return PS_FAILURE;
176+
}
177+
178+
filter->fi_name = g_strdup((const char *)event->data.scalar.value);
179+
if (!filter->fi_name) {
180+
free(filter);
181+
tlshd_log_error("Failed to allocate new filter\n");
182+
return PS_FAILURE;
183+
}
184+
185+
current->ps_current_filter = filter;
186+
return PS_FILTER_KEYS;
187+
}
188+
189+
static enum tlshd_tags_fsm_state_index
190+
tlshd_tags_filter_type_add(struct tlshd_tags_parser_state *current)
191+
{
192+
const yaml_event_t *event = &current->ps_yaml_event;
193+
const char *name = (const char *)event->data.scalar.value;
194+
195+
if (!current->ps_current_filter) {
196+
tlshd_log_error("No current filter\n");
197+
return PS_FAILURE;
198+
}
199+
200+
if (current->ps_current_filter->fi_filter_type) {
201+
tlshd_log_error("Filter type already set for filter '%s'\n",
202+
name);
203+
return PS_FAILURE;
204+
}
205+
206+
gconstpointer key = (gconstpointer)name;
207+
gpointer filter_type;
208+
209+
filter_type = g_hash_table_lookup(tlshd_tags_filter_type_hash, key);
210+
if (!filter_type) {
211+
tlshd_log_debug("Filter type '%s' is not supported", name);
212+
return PS_UNEXPECTED_INPUT_TOKEN;
213+
}
214+
215+
current->ps_current_filter->fi_filter_type = filter_type;
216+
return PS_FILTER_KEY;
217+
}
218+
219+
static enum tlshd_tags_fsm_state_index
220+
tlshd_tags_filter_key_set(struct tlshd_tags_parser_state *current)
221+
{
222+
const yaml_event_t *event = &current->ps_yaml_event;
223+
const char *key = (const char *)event->data.scalar.value;
224+
225+
if (strcmp(key, "type") == 0)
226+
return PS_FILTER_TYPE_VALUE;
227+
else if (strcmp(key, "pattern") == 0)
228+
return PS_FILTER_PATTERN_VALUE;
229+
else if (strcmp(key, "purpose") == 0)
230+
return PS_FILTER_PURPOSE_LIST;
231+
232+
tlshd_log_error("Unexpected token: %s\n", key);
233+
return PS_UNEXPECTED_INPUT_TOKEN;
234+
}
235+
236+
static enum tlshd_tags_fsm_state_index
237+
tlshd_tags_filter_pattern_set(struct tlshd_tags_parser_state *current)
238+
{
239+
const yaml_event_t *event = &current->ps_yaml_event;
240+
const char *pattern = (const char *)event->data.scalar.value;
241+
242+
if (!current->ps_current_filter) {
243+
tlshd_log_error("No current filter\n");
244+
return PS_FAILURE;
245+
}
246+
247+
current->ps_current_filter->fi_pattern = g_strdup(pattern);
248+
if (!current->ps_current_filter->fi_pattern) {
249+
tlshd_log_error("Failed to allocate filter pattern\n");
250+
return PS_FAILURE;
251+
}
252+
253+
return PS_FILTER_KEY;
254+
}
255+
256+
static enum tlshd_tags_fsm_state_index
257+
tlshd_tags_filter_key_usage_set(struct tlshd_tags_parser_state *current)
258+
{
259+
const yaml_event_t *event = &current->ps_yaml_event;
260+
const char *name = (const char *)event->data.scalar.value;
261+
unsigned int key_usage = 0;
262+
263+
if (strcmp(name, "digitalSignature") == 0)
264+
key_usage = GNUTLS_KEY_DIGITAL_SIGNATURE;
265+
else if (strcmp(name, "nonRepudiation") == 0)
266+
key_usage = GNUTLS_KEY_NON_REPUDIATION;
267+
else if (strcmp(name, "keyEncipherment") == 0)
268+
key_usage = GNUTLS_KEY_KEY_ENCIPHERMENT;
269+
else if (strcmp(name, "dataEncipherment") == 0)
270+
key_usage = GNUTLS_KEY_DATA_ENCIPHERMENT;
271+
else if (strcmp(name, "keyAgreement") == 0)
272+
key_usage = GNUTLS_KEY_KEY_AGREEMENT;
273+
else if (strcmp(name, "keyCertSign") == 0)
274+
key_usage = GNUTLS_KEY_KEY_CERT_SIGN;
275+
else if (strcmp(name, "cRLSign") == 0)
276+
key_usage = GNUTLS_KEY_CRL_SIGN;
277+
else if (strcmp(name, "encipherOnly") == 0)
278+
key_usage = GNUTLS_KEY_ENCIPHER_ONLY;
279+
else if (strcmp(name, "decipherOnly") == 0)
280+
key_usage = GNUTLS_KEY_DECIPHER_ONLY;
281+
else {
282+
tlshd_log_error("Unrecognized key usage: %s\n", name);
283+
return PS_UNEXPECTED_INPUT_TOKEN;
284+
}
285+
286+
current->ps_current_filter->fi_purpose_mask |= key_usage;
287+
return PS_FILTER_KEY_USAGE;
288+
}
289+
290+
static enum tlshd_tags_fsm_state_index
291+
tlshd_tags_filter_finalize(struct tlshd_tags_parser_state *current)
292+
{
293+
struct tlshd_tags_filter *filter = current->ps_current_filter;
294+
295+
if (!filter) {
296+
tlshd_log_error("No current filter\n");
297+
return PS_FAILURE;
298+
}
299+
300+
if (!filter->fi_filter_type->ft_validate) {
301+
tlshd_log_error("Filter '%s' filter type is not yet implemented.",
302+
filter->fi_name);
303+
return PS_FILTER;
304+
}
305+
if (!filter->fi_filter_type->ft_validate(filter))
306+
return PS_FAILURE;
307+
308+
if (tlshd_debug > 3)
309+
tlshd_log_debug("Adding filter '%s' to the filter hash",
310+
filter->fi_name);
311+
g_hash_table_insert(tlshd_tags_filter_hash, filter->fi_name,
312+
(gpointer)filter);
313+
current->ps_current_filter = NULL;
314+
return PS_FILTER;
315+
}
316+
98317
/* --- FSM states --- */
99318

100319
typedef enum tlshd_tags_fsm_state_index
@@ -137,6 +356,41 @@ static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_top_level[]
137356
NEXT_STATE(YAML_MAPPING_END_EVENT, PS_DOCUMENT),
138357
};
139358

359+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_filters[] = {
360+
NEXT_STATE(YAML_MAPPING_START_EVENT, PS_FILTER),
361+
};
362+
363+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_filter[] = {
364+
NEXT_ACTION(YAML_SCALAR_EVENT, tlshd_tags_filter_create),
365+
NEXT_STATE(YAML_MAPPING_END_EVENT, PS_TOP_LEVEL),
366+
};
367+
368+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_filter_keys[] = {
369+
NEXT_STATE(YAML_MAPPING_START_EVENT, PS_FILTER_KEY),
370+
};
371+
372+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_filter_key[] = {
373+
NEXT_ACTION(YAML_SCALAR_EVENT, tlshd_tags_filter_key_set),
374+
NEXT_ACTION(YAML_MAPPING_END_EVENT, tlshd_tags_filter_finalize),
375+
};
376+
377+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_filter_type_value[] = {
378+
NEXT_ACTION(YAML_SCALAR_EVENT, tlshd_tags_filter_type_add),
379+
};
380+
381+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_filter_pattern_value[] = {
382+
NEXT_ACTION(YAML_SCALAR_EVENT, tlshd_tags_filter_pattern_set),
383+
};
384+
385+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_filter_purpose_list[] = {
386+
NEXT_STATE(YAML_SEQUENCE_START_EVENT, PS_FILTER_KEY_USAGE),
387+
};
388+
389+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_filter_key_usage[] = {
390+
NEXT_ACTION(YAML_SCALAR_EVENT, tlshd_tags_filter_key_usage_set),
391+
NEXT_STATE(YAML_SEQUENCE_END_EVENT, PS_FILTER_KEY),
392+
};
393+
140394
struct tlshd_tags_fsm_state {
141395
const char *ts_name;
142396
const struct tlshd_tags_fsm_transition *ts_transitions;
@@ -162,6 +416,14 @@ static const struct tlshd_tags_fsm_state tlshd_tags_fsm_state_table[] = {
162416
FSM_STATE(PS_STREAM, tlshd_tags_transitions_stream),
163417
FSM_STATE(PS_DOCUMENT, tlshd_tags_transitions_document),
164418
FSM_STATE(PS_TOP_LEVEL, tlshd_tags_transitions_top_level),
419+
FSM_STATE(PS_FILTERS, tlshd_tags_transitions_filters),
420+
FSM_STATE(PS_FILTER, tlshd_tags_transitions_filter),
421+
FSM_STATE(PS_FILTER_KEYS, tlshd_tags_transitions_filter_keys),
422+
FSM_STATE(PS_FILTER_KEY, tlshd_tags_transitions_filter_key),
423+
FSM_STATE(PS_FILTER_TYPE_VALUE, tlshd_tags_transitions_filter_type_value),
424+
FSM_STATE(PS_FILTER_PATTERN_VALUE, tlshd_tags_transitions_filter_pattern_value),
425+
FSM_STATE(PS_FILTER_PURPOSE_LIST, tlshd_tags_transitions_filter_purpose_list),
426+
FSM_STATE(PS_FILTER_KEY_USAGE, tlshd_tags_transitions_filter_key_usage),
165427
TERMINAL_STATE(PS_UNEXPECTED_INPUT_TOKEN),
166428
TERMINAL_STATE(PS_FAILURE),
167429
};
@@ -396,12 +658,16 @@ void tlshd_tags_config_init(const char *tagsdir)
396658
{
397659
if (!tlshd_tags_filter_type_hash_init())
398660
return;
661+
if (!tlshd_tags_filter_hash_init())
662+
goto filter_type_hash;
399663

400664
if (!tlshd_tags_read_directory(tagsdir))
401-
goto filter_type_hash;
665+
goto filter_hash;
402666

403667
return;
404668

669+
filter_hash:
670+
tlshd_tags_filter_hash_destroy();
405671
filter_type_hash:
406672
tlshd_tags_filter_type_hash_destroy();
407673
}
@@ -412,5 +678,6 @@ void tlshd_tags_config_init(const char *tagsdir)
412678
*/
413679
void tlshd_tags_config_shutdown(void)
414680
{
681+
tlshd_tags_filter_hash_destroy();
415682
tlshd_tags_filter_type_hash_destroy();
416683
}

0 commit comments

Comments
 (0)