]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libimcv/plugins/imv_scanner/imv_scanner_agent.c
Update copyright headers after acquisition by secunet
[thirdparty/strongswan.git] / src / libimcv / plugins / imv_scanner / imv_scanner_agent.c
1 /*
2 * Copyright (C) 2013-2015 Andreas Steffen
3 *
4 * Copyright (C) secunet Security Networks AG
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "imv_scanner_agent.h"
18 #include "imv_scanner_state.h"
19
20 #include <imcv.h>
21 #include <imv/imv_agent.h>
22 #include <imv/imv_msg.h>
23 #include <ietf/ietf_attr.h>
24 #include <ietf/ietf_attr_attr_request.h>
25 #include <ietf/ietf_attr_pa_tnc_error.h>
26 #include <ietf/ietf_attr_port_filter.h>
27
28 #include <tncif_names.h>
29 #include <tncif_pa_subtypes.h>
30
31 #include <pen/pen.h>
32 #include <utils/debug.h>
33 #include <utils/lexparser.h>
34
35 typedef struct private_imv_scanner_agent_t private_imv_scanner_agent_t;
36
37 /* Subscribed PA-TNC message subtypes */
38 static pen_type_t msg_types[] = {
39 { PEN_IETF, PA_SUBTYPE_IETF_FIREWALL }
40 };
41
42 /**
43 * Private data of an imv_scanner_agent_t object.
44 */
45 struct private_imv_scanner_agent_t {
46
47 /**
48 * Public members of imv_scanner_agent_t
49 */
50 imv_agent_if_t public;
51
52 /**
53 * IMV agent responsible for generic functions
54 */
55 imv_agent_t *agent;
56
57 };
58
59 METHOD(imv_agent_if_t, bind_functions, TNC_Result,
60 private_imv_scanner_agent_t *this, TNC_TNCS_BindFunctionPointer bind_function)
61 {
62 return this->agent->bind_functions(this->agent, bind_function);
63 }
64
65 METHOD(imv_agent_if_t, notify_connection_change, TNC_Result,
66 private_imv_scanner_agent_t *this, TNC_ConnectionID id,
67 TNC_ConnectionState new_state)
68 {
69 imv_state_t *state;
70
71 switch (new_state)
72 {
73 case TNC_CONNECTION_STATE_CREATE:
74 state = imv_scanner_state_create(id);
75 return this->agent->create_state(this->agent, state);
76 case TNC_CONNECTION_STATE_DELETE:
77 return this->agent->delete_state(this->agent, id);
78 default:
79 return this->agent->change_state(this->agent, id, new_state, NULL);
80 }
81 }
82
83 /**
84 * Process a received message
85 */
86 static TNC_Result receive_msg(private_imv_scanner_agent_t *this,
87 imv_state_t *state, imv_msg_t *in_msg)
88 {
89 imv_msg_t *out_msg;
90 imv_scanner_state_t *scanner_state;
91 enumerator_t *enumerator;
92 pa_tnc_attr_t *attr;
93 pen_type_t type;
94 TNC_Result result;
95 ietf_attr_port_filter_t *port_filter_attr;
96 bool fatal_error = FALSE;
97
98 /* generate an outgoing PA-TNC message - we might need it */
99 out_msg = imv_msg_create_as_reply(in_msg);
100
101 /* parse received PA-TNC message and handle local and remote errors */
102 result = in_msg->receive(in_msg, out_msg, &fatal_error);
103 if (result != TNC_RESULT_SUCCESS)
104 {
105 out_msg->destroy(out_msg);
106 return result;
107 }
108
109 /* analyze PA-TNC attributes */
110 enumerator = in_msg->create_attribute_enumerator(in_msg);
111 while (enumerator->enumerate(enumerator, &attr))
112 {
113 type = attr->get_type(attr);
114
115 if (type.vendor_id == PEN_IETF && type.type == IETF_ATTR_PORT_FILTER)
116 {
117 scanner_state = (imv_scanner_state_t*)state;
118 port_filter_attr = (ietf_attr_port_filter_t*)attr->get_ref(attr);
119 scanner_state->set_port_filter_attr(scanner_state, port_filter_attr);
120 }
121 }
122 enumerator->destroy(enumerator);
123
124 if (fatal_error)
125 {
126 state->set_recommendation(state,
127 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
128 TNC_IMV_EVALUATION_RESULT_ERROR);
129 result = out_msg->send_assessment(out_msg);
130 if (result == TNC_RESULT_SUCCESS)
131 {
132 result = this->agent->provide_recommendation(this->agent, state);
133 }
134 }
135 else
136 {
137 /* send PA-TNC message with the EXCL flag set */
138 result = out_msg->send(out_msg, TRUE);
139 }
140 out_msg->destroy(out_msg);
141
142 return result;
143 }
144
145 METHOD(imv_agent_if_t, receive_message, TNC_Result,
146 private_imv_scanner_agent_t *this, TNC_ConnectionID id,
147 TNC_MessageType msg_type, chunk_t msg)
148 {
149 imv_state_t *state;
150 imv_msg_t *in_msg;
151 TNC_Result result;
152
153 if (!this->agent->get_state(this->agent, id, &state))
154 {
155 return TNC_RESULT_FATAL;
156 }
157 in_msg = imv_msg_create_from_data(this->agent, state, id, msg_type, msg);
158 result = receive_msg(this, state, in_msg);
159 in_msg->destroy(in_msg);
160
161 return result;
162 }
163
164 METHOD(imv_agent_if_t, receive_message_long, TNC_Result,
165 private_imv_scanner_agent_t *this, TNC_ConnectionID id,
166 TNC_UInt32 src_imc_id, TNC_UInt32 dst_imv_id,
167 TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype, chunk_t msg)
168 {
169 imv_state_t *state;
170 imv_msg_t *in_msg;
171 TNC_Result result;
172
173 if (!this->agent->get_state(this->agent, id, &state))
174 {
175 return TNC_RESULT_FATAL;
176 }
177 in_msg = imv_msg_create_from_long_data(this->agent, state, id,
178 src_imc_id, dst_imv_id, msg_vid, msg_subtype, msg);
179 result = receive_msg(this, state, in_msg);
180 in_msg->destroy(in_msg);
181
182 return result;
183
184 }
185
186 typedef struct port_range_t port_range_t;
187
188 struct port_range_t {
189 uint16_t start, stop;
190 };
191
192 /**
193 * Parse a TCP or UDP port list from an argument string
194 */
195 static linked_list_t* get_port_list(uint8_t protocol_family,
196 bool closed_port_policy, char *arg_str)
197 {
198 chunk_t port_list, port_item, port_start;
199 port_range_t *port_range;
200 linked_list_t *list;
201
202 list = linked_list_create();
203
204 port_list = chunk_from_str(arg_str);
205 DBG2(DBG_IMV, "list of %s ports that %s:",
206 (protocol_family == IPPROTO_TCP) ? "tcp" : "udp",
207 closed_port_policy ? "are allowed to be open" : "must be closed");
208
209 while (eat_whitespace(&port_list))
210 {
211 if (!extract_token(&port_item, ' ', &port_list))
212 {
213 /* reached last port item */
214 port_item = port_list;
215 port_list = chunk_empty;
216 }
217 port_range = malloc_thing(port_range_t);
218 port_range->start = atoi(port_item.ptr);
219
220 if (extract_token(&port_start, '-', &port_item) && port_item.len)
221 {
222 port_range->stop = atoi(port_item.ptr);
223 }
224 else
225 {
226 port_range->stop = port_range->start;
227 }
228 DBG2(DBG_IMV, "%5u - %5u", port_range->start, port_range->stop);
229 list->insert_last(list, port_range);
230 }
231
232 return list;
233 }
234
235 METHOD(imv_agent_if_t, batch_ending, TNC_Result,
236 private_imv_scanner_agent_t *this, TNC_ConnectionID id)
237 {
238 imv_msg_t *out_msg;
239 imv_state_t *state;
240 imv_session_t *session;
241 imv_workitem_t *workitem;
242 imv_scanner_state_t *scanner_state;
243 imv_scanner_handshake_state_t handshake_state;
244 pa_tnc_attr_t *attr;
245 ietf_attr_port_filter_t *port_filter_attr;
246 TNC_IMVID imv_id;
247 TNC_Result result = TNC_RESULT_SUCCESS;
248 bool no_workitems = TRUE;
249 enumerator_t *enumerator;
250
251 if (!this->agent->get_state(this->agent, id, &state))
252 {
253 return TNC_RESULT_FATAL;
254 }
255 scanner_state = (imv_scanner_state_t*)state;
256 handshake_state = scanner_state->get_handshake_state(scanner_state);
257 port_filter_attr = scanner_state->get_port_filter_attr(scanner_state);
258 session = state->get_session(state);
259 imv_id = this->agent->get_id(this->agent);
260
261 if (handshake_state == IMV_SCANNER_STATE_END)
262 {
263 return TNC_RESULT_SUCCESS;
264 }
265
266 /* create an empty out message - we might need it */
267 out_msg = imv_msg_create(this->agent, state, id, imv_id, TNC_IMCID_ANY,
268 msg_types[0]);
269
270 if (!imcv_db)
271 {
272 DBG2(DBG_IMV, "no workitems available - no evaluation possible");
273 state->set_recommendation(state,
274 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
275 TNC_IMV_EVALUATION_RESULT_DONT_KNOW);
276 result = out_msg->send_assessment(out_msg);
277 out_msg->destroy(out_msg);
278 scanner_state->set_handshake_state(scanner_state, IMV_SCANNER_STATE_END);
279
280 if (result != TNC_RESULT_SUCCESS)
281 {
282 return result;
283 }
284 return this->agent->provide_recommendation(this->agent, state);
285 }
286
287 if (handshake_state == IMV_SCANNER_STATE_INIT &&
288 session->get_policy_started(session))
289 {
290 enumerator = session->create_workitem_enumerator(session);
291 if (enumerator)
292 {
293 while (enumerator->enumerate(enumerator, &workitem))
294 {
295 if (workitem->get_imv_id(workitem) != TNC_IMVID_ANY)
296 {
297 continue;
298 }
299
300 switch (workitem->get_type(workitem))
301 {
302 case IMV_WORKITEM_TCP_PORT_OPEN:
303 case IMV_WORKITEM_TCP_PORT_BLOCK:
304 case IMV_WORKITEM_UDP_PORT_OPEN:
305 case IMV_WORKITEM_UDP_PORT_BLOCK:
306 if (!port_filter_attr &&
307 handshake_state != IMV_SCANNER_STATE_ATTR_REQ)
308 {
309 attr = ietf_attr_attr_request_create(PEN_IETF,
310 IETF_ATTR_PORT_FILTER);
311 out_msg->add_attribute(out_msg, attr);
312 handshake_state = IMV_SCANNER_STATE_ATTR_REQ;
313 }
314 break;
315 default:
316 continue;
317 }
318 workitem->set_imv_id(workitem, imv_id);
319 no_workitems = FALSE;
320 }
321 enumerator->destroy(enumerator);
322
323 if (no_workitems)
324 {
325 DBG2(DBG_IMV, "IMV %d has no workitems - "
326 "no evaluation requested", imv_id);
327 state->set_recommendation(state,
328 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
329 TNC_IMV_EVALUATION_RESULT_DONT_KNOW);
330 }
331 handshake_state = IMV_SCANNER_STATE_WORKITEMS;
332 scanner_state->set_handshake_state(scanner_state, handshake_state);
333 }
334 }
335
336 if (handshake_state == IMV_SCANNER_STATE_WORKITEMS && port_filter_attr)
337 {
338 TNC_IMV_Evaluation_Result eval;
339 TNC_IMV_Action_Recommendation rec;
340 uint8_t protocol_family, protocol;
341 uint16_t port;
342 bool closed_port_policy, blocked, first;
343 char result_str[BUF_LEN], *pos, *protocol_str;
344 size_t len;
345 int written;
346 linked_list_t *port_list;
347 enumerator_t *e1, *e2;
348
349 enumerator = session->create_workitem_enumerator(session);
350 while (enumerator->enumerate(enumerator, &workitem))
351 {
352 if (workitem->get_imv_id(workitem) != imv_id)
353 {
354 continue;
355 }
356 eval = TNC_IMV_EVALUATION_RESULT_COMPLIANT;
357
358 switch (workitem->get_type(workitem))
359 {
360 case IMV_WORKITEM_TCP_PORT_OPEN:
361 protocol_family = IPPROTO_TCP;
362 closed_port_policy = TRUE;
363 break;
364 case IMV_WORKITEM_TCP_PORT_BLOCK:
365 protocol_family = IPPROTO_TCP;
366 closed_port_policy = FALSE;
367 break;
368 case IMV_WORKITEM_UDP_PORT_OPEN:
369 protocol_family = IPPROTO_UDP;
370 closed_port_policy = TRUE;
371 break;
372 case IMV_WORKITEM_UDP_PORT_BLOCK:
373 protocol_family = IPPROTO_UDP;
374 closed_port_policy = FALSE;
375 break;
376 default:
377 continue;
378 }
379 port_list = get_port_list(protocol_family, closed_port_policy,
380 workitem->get_arg_str(workitem));
381 protocol_str = (protocol_family == IPPROTO_TCP) ? "tcp" : "udp";
382 result_str[0] = '\0';
383 pos = result_str;
384 len = BUF_LEN;
385 first = TRUE;
386
387 e1 = port_filter_attr->create_port_enumerator(port_filter_attr);
388 while (e1->enumerate(e1, &blocked, &protocol, &port))
389 {
390 port_range_t *port_range;
391 bool passed, found = FALSE;
392 char buf[20];
393
394 if (blocked || protocol != protocol_family)
395 {
396 /* ignore closed ports or non-matching protocols */
397 continue;
398 }
399
400 e2 = port_list->create_enumerator(port_list);
401 while (e2->enumerate(e2, &port_range))
402 {
403 if (port >= port_range->start && port <= port_range->stop)
404 {
405 found = TRUE;
406 break;
407 }
408 }
409 e2->destroy(e2);
410
411 passed = (closed_port_policy == found);
412 DBG2(DBG_IMV, "%s port %5u open: %s", protocol_str, port,
413 passed ? "ok" : "fatal");
414 if (!passed)
415 {
416 eval = TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR;
417 snprintf(buf, sizeof(buf), "%s/%u", protocol_str, port);
418 scanner_state->add_violating_port(scanner_state, strdup(buf));
419 if (first)
420 {
421 written = snprintf(pos, len, "violating %s ports:",
422 protocol_str);
423 if (written > 0 && written < len)
424 {
425 pos += written;
426 len -= written;
427 }
428 first = FALSE;
429 }
430 written = snprintf(pos, len, " %u", port);
431 if (written < 0 || written >= len)
432 {
433 *pos = '\0';
434 }
435 else
436 {
437 pos += written;
438 len -= written;
439 }
440 }
441 }
442 e1->destroy(e1);
443
444 if (first)
445 {
446 snprintf(pos, len, "no violating %s ports", protocol_str);
447 }
448 port_list->destroy(port_list);
449
450 session->remove_workitem(session, enumerator);
451 rec = workitem->set_result(workitem, result_str, eval);
452 state->update_recommendation(state, rec, eval);
453 imcv_db->finalize_workitem(imcv_db, workitem);
454 workitem->destroy(workitem);
455 }
456 enumerator->destroy(enumerator);
457 }
458
459 /* finalized all workitems ? */
460 if (handshake_state == IMV_SCANNER_STATE_WORKITEMS &&
461 session->get_workitem_count(session, imv_id) == 0)
462 {
463 result = out_msg->send_assessment(out_msg);
464 out_msg->destroy(out_msg);
465 scanner_state->set_handshake_state(scanner_state, IMV_SCANNER_STATE_END);
466
467 if (result != TNC_RESULT_SUCCESS)
468 {
469 return result;
470 }
471 return this->agent->provide_recommendation(this->agent, state);
472 }
473
474 /* send non-empty PA-TNC message with excl flag not set */
475 if (out_msg->get_attribute_count(out_msg))
476 {
477 result = out_msg->send(out_msg, FALSE);
478 }
479 out_msg->destroy(out_msg);
480
481 return result;
482 }
483
484 METHOD(imv_agent_if_t, solicit_recommendation, TNC_Result,
485 private_imv_scanner_agent_t *this, TNC_ConnectionID id)
486 {
487 imv_state_t *state;
488
489 if (!this->agent->get_state(this->agent, id, &state))
490 {
491 return TNC_RESULT_FATAL;
492 }
493 return this->agent->provide_recommendation(this->agent, state);
494 }
495
496 METHOD(imv_agent_if_t, destroy, void,
497 private_imv_scanner_agent_t *this)
498 {
499 this->agent->destroy(this->agent);
500 free(this);
501 }
502
503 /**
504 * Described in header.
505 */
506 imv_agent_if_t *imv_scanner_agent_create(const char *name, TNC_IMVID id,
507 TNC_Version *actual_version)
508 {
509 private_imv_scanner_agent_t *this;
510 imv_agent_t *agent;
511
512 agent = imv_agent_create(name, msg_types, countof(msg_types), id,
513 actual_version);
514 if (!agent)
515 {
516 return NULL;
517 }
518
519 INIT(this,
520 .public = {
521 .bind_functions = _bind_functions,
522 .notify_connection_change = _notify_connection_change,
523 .receive_message = _receive_message,
524 .receive_message_long = _receive_message_long,
525 .batch_ending = _batch_ending,
526 .solicit_recommendation = _solicit_recommendation,
527 .destroy = _destroy,
528 },
529 .agent = agent,
530 );
531
532 return &this->public;
533 }
534