2 * Copyright (C) 2013-2015 Andreas Steffen
4 * Copyright (C) secunet Security Networks AG
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>.
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
17 #include "imv_scanner_agent.h"
18 #include "imv_scanner_state.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>
28 #include <tncif_names.h>
29 #include <tncif_pa_subtypes.h>
32 #include <utils/debug.h>
33 #include <utils/lexparser.h>
35 typedef struct private_imv_scanner_agent_t private_imv_scanner_agent_t
;
37 /* Subscribed PA-TNC message subtypes */
38 static pen_type_t msg_types
[] = {
39 { PEN_IETF
, PA_SUBTYPE_IETF_FIREWALL
}
43 * Private data of an imv_scanner_agent_t object.
45 struct private_imv_scanner_agent_t
{
48 * Public members of imv_scanner_agent_t
50 imv_agent_if_t
public;
53 * IMV agent responsible for generic functions
59 METHOD(imv_agent_if_t
, bind_functions
, TNC_Result
,
60 private_imv_scanner_agent_t
*this, TNC_TNCS_BindFunctionPointer bind_function
)
62 return this->agent
->bind_functions(this->agent
, bind_function
);
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
)
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
);
79 return this->agent
->change_state(this->agent
, id
, new_state
, NULL
);
84 * Process a received message
86 static TNC_Result
receive_msg(private_imv_scanner_agent_t
*this,
87 imv_state_t
*state
, imv_msg_t
*in_msg
)
90 imv_scanner_state_t
*scanner_state
;
91 enumerator_t
*enumerator
;
95 ietf_attr_port_filter_t
*port_filter_attr
;
96 bool fatal_error
= FALSE
;
98 /* generate an outgoing PA-TNC message - we might need it */
99 out_msg
= imv_msg_create_as_reply(in_msg
);
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
)
105 out_msg
->destroy(out_msg
);
109 /* analyze PA-TNC attributes */
110 enumerator
= in_msg
->create_attribute_enumerator(in_msg
);
111 while (enumerator
->enumerate(enumerator
, &attr
))
113 type
= attr
->get_type(attr
);
115 if (type
.vendor_id
== PEN_IETF
&& type
.type
== IETF_ATTR_PORT_FILTER
)
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
);
122 enumerator
->destroy(enumerator
);
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
)
132 result
= this->agent
->provide_recommendation(this->agent
, state
);
137 /* send PA-TNC message with the EXCL flag set */
138 result
= out_msg
->send(out_msg
, TRUE
);
140 out_msg
->destroy(out_msg
);
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
)
153 if (!this->agent
->get_state(this->agent
, id
, &state
))
155 return TNC_RESULT_FATAL
;
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
);
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
)
173 if (!this->agent
->get_state(this->agent
, id
, &state
))
175 return TNC_RESULT_FATAL
;
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
);
186 typedef struct port_range_t port_range_t
;
188 struct port_range_t
{
189 uint16_t start
, stop
;
193 * Parse a TCP or UDP port list from an argument string
195 static linked_list_t
* get_port_list(uint8_t protocol_family
,
196 bool closed_port_policy
, char *arg_str
)
198 chunk_t port_list
, port_item
, port_start
;
199 port_range_t
*port_range
;
202 list
= linked_list_create();
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");
209 while (eat_whitespace(&port_list
))
211 if (!extract_token(&port_item
, ' ', &port_list
))
213 /* reached last port item */
214 port_item
= port_list
;
215 port_list
= chunk_empty
;
217 port_range
= malloc_thing(port_range_t
);
218 port_range
->start
= atoi(port_item
.ptr
);
220 if (extract_token(&port_start
, '-', &port_item
) && port_item
.len
)
222 port_range
->stop
= atoi(port_item
.ptr
);
226 port_range
->stop
= port_range
->start
;
228 DBG2(DBG_IMV
, "%5u - %5u", port_range
->start
, port_range
->stop
);
229 list
->insert_last(list
, port_range
);
235 METHOD(imv_agent_if_t
, batch_ending
, TNC_Result
,
236 private_imv_scanner_agent_t
*this, TNC_ConnectionID id
)
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
;
245 ietf_attr_port_filter_t
*port_filter_attr
;
247 TNC_Result result
= TNC_RESULT_SUCCESS
;
248 bool no_workitems
= TRUE
;
249 enumerator_t
*enumerator
;
251 if (!this->agent
->get_state(this->agent
, id
, &state
))
253 return TNC_RESULT_FATAL
;
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
);
261 if (handshake_state
== IMV_SCANNER_STATE_END
)
263 return TNC_RESULT_SUCCESS
;
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
,
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
);
280 if (result
!= TNC_RESULT_SUCCESS
)
284 return this->agent
->provide_recommendation(this->agent
, state
);
287 if (handshake_state
== IMV_SCANNER_STATE_INIT
&&
288 session
->get_policy_started(session
))
290 enumerator
= session
->create_workitem_enumerator(session
);
293 while (enumerator
->enumerate(enumerator
, &workitem
))
295 if (workitem
->get_imv_id(workitem
) != TNC_IMVID_ANY
)
300 switch (workitem
->get_type(workitem
))
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
)
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
;
318 workitem
->set_imv_id(workitem
, imv_id
);
319 no_workitems
= FALSE
;
321 enumerator
->destroy(enumerator
);
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
);
331 handshake_state
= IMV_SCANNER_STATE_WORKITEMS
;
332 scanner_state
->set_handshake_state(scanner_state
, handshake_state
);
336 if (handshake_state
== IMV_SCANNER_STATE_WORKITEMS
&& port_filter_attr
)
338 TNC_IMV_Evaluation_Result eval
;
339 TNC_IMV_Action_Recommendation rec
;
340 uint8_t protocol_family
, protocol
;
342 bool closed_port_policy
, blocked
, first
;
343 char result_str
[BUF_LEN
], *pos
, *protocol_str
;
346 linked_list_t
*port_list
;
347 enumerator_t
*e1
, *e2
;
349 enumerator
= session
->create_workitem_enumerator(session
);
350 while (enumerator
->enumerate(enumerator
, &workitem
))
352 if (workitem
->get_imv_id(workitem
) != imv_id
)
356 eval
= TNC_IMV_EVALUATION_RESULT_COMPLIANT
;
358 switch (workitem
->get_type(workitem
))
360 case IMV_WORKITEM_TCP_PORT_OPEN
:
361 protocol_family
= IPPROTO_TCP
;
362 closed_port_policy
= TRUE
;
364 case IMV_WORKITEM_TCP_PORT_BLOCK
:
365 protocol_family
= IPPROTO_TCP
;
366 closed_port_policy
= FALSE
;
368 case IMV_WORKITEM_UDP_PORT_OPEN
:
369 protocol_family
= IPPROTO_UDP
;
370 closed_port_policy
= TRUE
;
372 case IMV_WORKITEM_UDP_PORT_BLOCK
:
373 protocol_family
= IPPROTO_UDP
;
374 closed_port_policy
= FALSE
;
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';
387 e1
= port_filter_attr
->create_port_enumerator(port_filter_attr
);
388 while (e1
->enumerate(e1
, &blocked
, &protocol
, &port
))
390 port_range_t
*port_range
;
391 bool passed
, found
= FALSE
;
394 if (blocked
|| protocol
!= protocol_family
)
396 /* ignore closed ports or non-matching protocols */
400 e2
= port_list
->create_enumerator(port_list
);
401 while (e2
->enumerate(e2
, &port_range
))
403 if (port
>= port_range
->start
&& port
<= port_range
->stop
)
411 passed
= (closed_port_policy
== found
);
412 DBG2(DBG_IMV
, "%s port %5u open: %s", protocol_str
, port
,
413 passed
? "ok" : "fatal");
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
));
421 written
= snprintf(pos
, len
, "violating %s ports:",
423 if (written
> 0 && written
< len
)
430 written
= snprintf(pos
, len
, " %u", port
);
431 if (written
< 0 || written
>= len
)
446 snprintf(pos
, len
, "no violating %s ports", protocol_str
);
448 port_list
->destroy(port_list
);
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
);
456 enumerator
->destroy(enumerator
);
459 /* finalized all workitems ? */
460 if (handshake_state
== IMV_SCANNER_STATE_WORKITEMS
&&
461 session
->get_workitem_count(session
, imv_id
) == 0)
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
);
467 if (result
!= TNC_RESULT_SUCCESS
)
471 return this->agent
->provide_recommendation(this->agent
, state
);
474 /* send non-empty PA-TNC message with excl flag not set */
475 if (out_msg
->get_attribute_count(out_msg
))
477 result
= out_msg
->send(out_msg
, FALSE
);
479 out_msg
->destroy(out_msg
);
484 METHOD(imv_agent_if_t
, solicit_recommendation
, TNC_Result
,
485 private_imv_scanner_agent_t
*this, TNC_ConnectionID id
)
489 if (!this->agent
->get_state(this->agent
, id
, &state
))
491 return TNC_RESULT_FATAL
;
493 return this->agent
->provide_recommendation(this->agent
, state
);
496 METHOD(imv_agent_if_t
, destroy
, void,
497 private_imv_scanner_agent_t
*this)
499 this->agent
->destroy(this->agent
);
504 * Described in header.
506 imv_agent_if_t
*imv_scanner_agent_create(const char *name
, TNC_IMVID id
,
507 TNC_Version
*actual_version
)
509 private_imv_scanner_agent_t
*this;
512 agent
= imv_agent_create(name
, msg_types
, countof(msg_types
), id
,
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
,
532 return &this->public;