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