9d3980195bbed366e62f9cb11a4e2c70ff11d741
[strongswan.git] / src / libimcv / plugins / imv_hcd / imv_hcd_agent.c
1 /*
2 * Copyright (C) 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 #define _GNU_SOURCE
17 #include <stdio.h>
18
19 #include "imv_hcd_agent.h"
20 #include "imv_hcd_state.h"
21
22 #include <imcv.h>
23 #include <imv/imv_agent.h>
24 #include <imv/imv_msg.h>
25 #include <generic/generic_attr_bool.h>
26 #include <generic/generic_attr_chunk.h>
27 #include <generic/generic_attr_string.h>
28 #include <ietf/ietf_attr.h>
29 #include <ietf/ietf_attr_attr_request.h>
30 #include <pwg/pwg_attr.h>
31 #include <pwg/pwg_attr_vendor_smi_code.h>
32 #include "tcg/seg/tcg_seg_attr_max_size.h"
33 #include "tcg/seg/tcg_seg_attr_seg_env.h"
34
35 #include <tncif_names.h>
36 #include <tncif_pa_subtypes.h>
37
38 #include <pen/pen.h>
39 #include <utils/debug.h>
40
41 #define HCD_MAX_ATTR_SIZE 10000000
42
43 typedef struct private_imv_hcd_agent_t private_imv_hcd_agent_t;
44 typedef enum imv_hcd_attr_t imv_hcd_attr_t;
45
46 /* Subscribed PA-TNC message subtypes */
47 static pen_type_t msg_types[] = {
48 { PEN_IETF, PA_SUBTYPE_IETF_OPERATING_SYSTEM },
49 { PEN_PWG, PA_SUBTYPE_PWG_HCD_SYSTEM },
50 { PEN_PWG, PA_SUBTYPE_PWG_HCD_CONSOLE },
51 { PEN_PWG, PA_SUBTYPE_PWG_HCD_MARKER },
52 { PEN_PWG, PA_SUBTYPE_PWG_HCD_FINISHER },
53 { PEN_PWG, PA_SUBTYPE_PWG_HCD_INTERFACE },
54 { PEN_PWG, PA_SUBTYPE_PWG_HCD_SCANNER }
55 };
56
57 /**
58 * Flag set when corresponding attribute has been received
59 */
60 enum imv_hcd_attr_t {
61 IMV_HCD_ATTR_NONE = 0,
62 IMV_HCD_ATTR_NATURAL_LANG = (1<<0),
63 IMV_HCD_ATTR_DEFAULT_PWD_ENABLED = (1<<1),
64 IMV_HCD_ATTR_FIREWALL_SETTING = (1<<2),
65 IMV_HCD_ATTR_FIRMWARE_NAME = (1<<3),
66 IMV_HCD_ATTR_FORWARDING_ENABLED = (1<<4),
67 IMV_HCD_ATTR_MACHINE_TYPE_MODEL = (1<<5),
68 IMV_HCD_ATTR_PSTN_FAX_ENABLED = (1<<6),
69 IMV_HCD_ATTR_RESIDENT_APP_NAME = (1<<7),
70 IMV_HCD_ATTR_TIME_SOURCE = (1<<8),
71 IMV_HCD_ATTR_USER_APP_ENABLED = (1<<9),
72 IMV_HCD_ATTR_USER_APP_PERSIST_ENABLED = (1<<10),
73 IMV_HCD_ATTR_USER_APP_NAME = (1<<11),
74 IMV_HCD_ATTR_VENDOR_NAME = (1<<12),
75 IMV_HCD_ATTR_VENDOR_SMI_CODE = (1<<13),
76 IMV_HCD_ATTR_MUST = (1<<14)-1
77 };
78
79 static imv_hcd_attr_t attr_type_to_flag(pwg_attr_t attr_type)
80 {
81 switch (attr_type)
82 {
83 case PWG_HCD_ATTRS_NATURAL_LANG:
84 return IMV_HCD_ATTR_NATURAL_LANG;
85 case PWG_HCD_DEFAULT_PWD_ENABLED:
86 return IMV_HCD_ATTR_DEFAULT_PWD_ENABLED;
87 case PWG_HCD_FIREWALL_SETTING:
88 return IMV_HCD_ATTR_FIREWALL_SETTING;
89 case PWG_HCD_FIRMWARE_NAME:
90 return IMV_HCD_ATTR_FIRMWARE_NAME;
91 case PWG_HCD_FORWARDING_ENABLED:
92 return IMV_HCD_ATTR_FORWARDING_ENABLED;
93 case PWG_HCD_MACHINE_TYPE_MODEL:
94 return IMV_HCD_ATTR_MACHINE_TYPE_MODEL;
95 case PWG_HCD_PSTN_FAX_ENABLED:
96 return IMV_HCD_ATTR_PSTN_FAX_ENABLED;
97 case PWG_HCD_RESIDENT_APP_NAME:
98 return IMV_HCD_ATTR_RESIDENT_APP_NAME;
99 case PWG_HCD_TIME_SOURCE:
100 return IMV_HCD_ATTR_TIME_SOURCE;
101 case PWG_HCD_USER_APP_ENABLED:
102 return IMV_HCD_ATTR_USER_APP_ENABLED;
103 case PWG_HCD_USER_APP_PERSIST_ENABLED:
104 return IMV_HCD_ATTR_USER_APP_PERSIST_ENABLED;
105 case PWG_HCD_USER_APP_NAME:
106 return IMV_HCD_ATTR_USER_APP_NAME;
107 case PWG_HCD_VENDOR_NAME:
108 return IMV_HCD_ATTR_VENDOR_NAME;
109 case PWG_HCD_VENDOR_SMI_CODE:
110 return IMV_HCD_ATTR_VENDOR_SMI_CODE;
111 default:
112 return IMV_HCD_ATTR_NONE;
113 }
114 }
115
116 /**
117 * Private data of an imv_hcd_agent_t object.
118 */
119 struct private_imv_hcd_agent_t {
120
121 /**
122 * Public members of imv_hcd_agent_t
123 */
124 imv_agent_if_t public;
125
126 /**
127 * IMV agent responsible for generic functions
128 */
129 imv_agent_t *agent;
130
131 };
132
133 METHOD(imv_agent_if_t, bind_functions, TNC_Result,
134 private_imv_hcd_agent_t *this, TNC_TNCS_BindFunctionPointer bind_function)
135 {
136 return this->agent->bind_functions(this->agent, bind_function);
137 }
138
139 METHOD(imv_agent_if_t, notify_connection_change, TNC_Result,
140 private_imv_hcd_agent_t *this, TNC_ConnectionID id,
141 TNC_ConnectionState new_state)
142 {
143 TNC_IMV_Action_Recommendation rec;
144 imv_state_t *state;
145 imv_session_t *session;
146
147 switch (new_state)
148 {
149 case TNC_CONNECTION_STATE_CREATE:
150 state = imv_hcd_state_create(id);
151 return this->agent->create_state(this->agent, state);
152 case TNC_CONNECTION_STATE_DELETE:
153 return this->agent->delete_state(this->agent, id);
154 case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
155 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
156 case TNC_CONNECTION_STATE_ACCESS_NONE:
157 if (this->agent->get_state(this->agent, id, &state) && imcv_db)
158 {
159 session = state->get_session(state);
160
161 if (session->get_policy_started(session))
162 {
163 switch (new_state)
164 {
165 case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
166 rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
167 break;
168 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
169 rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
170 break;
171 case TNC_CONNECTION_STATE_ACCESS_NONE:
172 default:
173 rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
174 }
175 imcv_db->add_recommendation(imcv_db, session, rec);
176 if (!imcv_db->policy_script(imcv_db, session, FALSE))
177 {
178 DBG1(DBG_IMV, "error in policy script stop");
179 }
180 }
181 }
182 /* fall through to default state */
183 default:
184 return this->agent->change_state(this->agent, id, new_state, NULL);
185 }
186 }
187
188 /**
189 * Process a received message
190 */
191 static TNC_Result receive_msg(private_imv_hcd_agent_t *this, imv_state_t *state,
192 imv_msg_t *in_msg)
193 {
194 imv_msg_t *out_msg;
195 imv_hcd_state_t *hcd_state;
196 pa_tnc_attr_t *attr;
197 pen_type_t type;
198 TNC_Result result;
199 bool fatal_error = FALSE, assessment = FALSE;
200 enumerator_t *enumerator;
201
202 hcd_state = (imv_hcd_state_t*)state;
203
204 /* generate an outgoing PA-TNC message - we might need it */
205 out_msg = imv_msg_create_as_reply(in_msg);
206
207 /* parse received PA-TNC message and handle local and remote errors */
208 result = in_msg->receive(in_msg,out_msg, &fatal_error);
209 if (result != TNC_RESULT_SUCCESS)
210 {
211 out_msg->destroy(out_msg);
212 return result;
213 }
214
215 /* analyze PA-TNC attributes */
216 enumerator = in_msg->create_attribute_enumerator(in_msg);
217 while (enumerator->enumerate(enumerator, &attr))
218 {
219 type = attr->get_type(attr);
220
221 if (type.vendor_id == PEN_PWG)
222 {
223 state->set_action_flags(state, attr_type_to_flag(type.type));
224
225 switch (type.type)
226 {
227 case PWG_HCD_ATTRS_NATURAL_LANG:
228 case PWG_HCD_MACHINE_TYPE_MODEL:
229 case PWG_HCD_VENDOR_NAME:
230 case PWG_HCD_TIME_SOURCE:
231 case PWG_HCD_FIRMWARE_NAME:
232 case PWG_HCD_FIRMWARE_PATCHES:
233 case PWG_HCD_FIRMWARE_STRING_VERSION:
234 case PWG_HCD_RESIDENT_APP_NAME:
235 case PWG_HCD_RESIDENT_APP_PATCHES:
236 case PWG_HCD_RESIDENT_APP_STRING_VERSION:
237 case PWG_HCD_USER_APP_NAME:
238 case PWG_HCD_USER_APP_PATCHES:
239 case PWG_HCD_USER_APP_STRING_VERSION:
240 {
241 chunk_t value;
242
243 value = attr->get_value(attr);
244 DBG2(DBG_IMV, "%N: %.*s", pwg_attr_names, type.type,
245 value.len, value.ptr);
246 break;
247 }
248 case PWG_HCD_FIRMWARE_VERSION:
249 case PWG_HCD_RESIDENT_APP_VERSION:
250 case PWG_HCD_USER_APP_VERSION:
251 {
252 chunk_t value;
253
254 value = attr->get_value(attr);
255 DBG2(DBG_IMV, "%N: %#B", pwg_attr_names, type.type, &value);
256 break;
257 }
258 case PWG_HCD_CERTIFICATION_STATE:
259 case PWG_HCD_CONFIGURATION_STATE:
260 {
261 chunk_t value;
262
263 value = attr->get_value(attr);
264 DBG2(DBG_IMV, "%N: %B", pwg_attr_names, type.type, &value);
265 break;
266 }
267 case PWG_HCD_DEFAULT_PWD_ENABLED:
268 case PWG_HCD_FORWARDING_ENABLED:
269 case PWG_HCD_PSTN_FAX_ENABLED:
270 case PWG_HCD_USER_APP_ENABLED:
271 case PWG_HCD_USER_APP_PERSIST_ENABLED:
272 {
273 generic_attr_bool_t *attr_cast;
274 bool status;
275
276 attr_cast = (generic_attr_bool_t*)attr;
277 status = attr_cast->get_status(attr_cast);
278 DBG2(DBG_IMV, "%N: %s", pwg_attr_names, type.type,
279 status ? "yes" : "no");
280
281 if (type.type == PWG_HCD_USER_APP_ENABLED && !status)
282 {
283 /* do not request user applications */
284 state->set_action_flags(state,
285 IMV_HCD_ATTR_USER_APP_NAME);
286 }
287 break;
288 }
289 case PWG_HCD_VENDOR_SMI_CODE:
290 {
291 pwg_attr_vendor_smi_code_t *attr_cast;
292 uint32_t smi_code;
293
294 attr_cast = (pwg_attr_vendor_smi_code_t*)attr;
295 smi_code = attr_cast->get_vendor_smi_code(attr_cast);
296 DBG2(DBG_IMV, "%N: 0x%06x (%u)", pwg_attr_names, type.type,
297 smi_code, smi_code);
298 break;
299 }
300 default:
301 break;
302 }
303 }
304 }
305 enumerator->destroy(enumerator);
306
307 if (fatal_error)
308 {
309 state->set_recommendation(state,
310 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
311 TNC_IMV_EVALUATION_RESULT_ERROR);
312 assessment = TRUE;
313 }
314
315 if (assessment)
316 {
317 hcd_state->set_handshake_state(hcd_state, IMV_HCD_STATE_END);
318 result = out_msg->send_assessment(out_msg);
319 if (result == TNC_RESULT_SUCCESS)
320 {
321 result = this->agent->provide_recommendation(this->agent, state);
322 }
323 }
324 else
325 {
326 /* send PA-TNC message with the EXCL flag set */
327 result = out_msg->send(out_msg, TRUE);
328 }
329 out_msg->destroy(out_msg);
330
331 return result;
332 }
333
334 METHOD(imv_agent_if_t, receive_message, TNC_Result,
335 private_imv_hcd_agent_t *this, TNC_ConnectionID id,
336 TNC_MessageType msg_type, chunk_t msg)
337 {
338 imv_state_t *state;
339 imv_msg_t *in_msg;
340 TNC_Result result;
341
342 if (!this->agent->get_state(this->agent, id, &state))
343 {
344 return TNC_RESULT_FATAL;
345 }
346 in_msg = imv_msg_create_from_data(this->agent, state, id, msg_type, msg);
347 result = receive_msg(this, state, in_msg);
348 in_msg->destroy(in_msg);
349
350 return result;
351 }
352
353 METHOD(imv_agent_if_t, receive_message_long, TNC_Result,
354 private_imv_hcd_agent_t *this, TNC_ConnectionID id,
355 TNC_UInt32 src_imc_id, TNC_UInt32 dst_imv_id,
356 TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype, chunk_t msg)
357 {
358 imv_state_t *state;
359 imv_msg_t *in_msg;
360 TNC_Result result;
361
362 if (!this->agent->get_state(this->agent, id, &state))
363 {
364 return TNC_RESULT_FATAL;
365 }
366 in_msg = imv_msg_create_from_long_data(this->agent, state, id,
367 src_imc_id, dst_imv_id, msg_vid, msg_subtype, msg);
368 result = receive_msg(this, state, in_msg);
369 in_msg->destroy(in_msg);
370
371 return result;
372
373 }
374
375 /**
376 * Build an IETF Attribute Request attribute for missing attributes
377 */
378 static pa_tnc_attr_t* build_attr_request(uint32_t received)
379 {
380 pa_tnc_attr_t *attr;
381 ietf_attr_attr_request_t *attr_cast;
382
383 attr = ietf_attr_attr_request_create(PEN_RESERVED, 0);
384 attr_cast = (ietf_attr_attr_request_t*)attr;
385
386 if (!(received & IMV_HCD_ATTR_NATURAL_LANG))
387 {
388 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_ATTRS_NATURAL_LANG);
389 }
390 if (!(received & IMV_HCD_ATTR_DEFAULT_PWD_ENABLED))
391 {
392 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_DEFAULT_PWD_ENABLED);
393 }
394 if (!(received & IMV_HCD_ATTR_FIREWALL_SETTING))
395 {
396 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_FIREWALL_SETTING);
397 }
398 if (!(received & IMV_HCD_ATTR_FIRMWARE_NAME))
399 {
400 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_FIRMWARE_NAME);
401 }
402 if (!(received & IMV_HCD_ATTR_FORWARDING_ENABLED))
403 {
404 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_FORWARDING_ENABLED);
405 }
406 if (!(received & IMV_HCD_ATTR_MACHINE_TYPE_MODEL))
407 {
408 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_MACHINE_TYPE_MODEL);
409 }
410 if (!(received & IMV_HCD_ATTR_PSTN_FAX_ENABLED))
411 {
412 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_PSTN_FAX_ENABLED);
413 }
414 if (!(received & IMV_HCD_ATTR_RESIDENT_APP_NAME))
415 {
416 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_RESIDENT_APP_NAME);
417 }
418 if (!(received & IMV_HCD_ATTR_TIME_SOURCE))
419 {
420 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_TIME_SOURCE);
421 }
422 if (!(received & IMV_HCD_ATTR_USER_APP_ENABLED))
423 {
424 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_USER_APP_ENABLED);
425 }
426 if (!(received & IMV_HCD_ATTR_USER_APP_PERSIST_ENABLED))
427 {
428 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_USER_APP_PERSIST_ENABLED);
429 }
430 if (!(received & IMV_HCD_ATTR_USER_APP_NAME))
431 {
432 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_USER_APP_NAME);
433 }
434 if (!(received & IMV_HCD_ATTR_VENDOR_NAME))
435 {
436 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_VENDOR_NAME);
437 }
438 if (!(received & IMV_HCD_ATTR_VENDOR_SMI_CODE))
439 {
440 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_VENDOR_SMI_CODE);
441 }
442 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_CERTIFICATION_STATE);
443 attr_cast->add(attr_cast, PEN_PWG, PWG_HCD_CONFIGURATION_STATE);
444
445 return attr;
446 }
447
448 METHOD(imv_agent_if_t, batch_ending, TNC_Result,
449 private_imv_hcd_agent_t *this, TNC_ConnectionID id)
450 {
451 imv_msg_t *out_msg;
452 imv_state_t *state;
453 imv_hcd_state_t *hcd_state;
454 imv_hcd_handshake_state_t handshake_state;
455 pa_tnc_attr_t *attr;
456 TNC_IMVID imv_id;
457 TNC_Result result = TNC_RESULT_SUCCESS;
458 uint32_t received;
459
460 if (!this->agent->get_state(this->agent, id, &state))
461 {
462 return TNC_RESULT_FATAL;
463 }
464 hcd_state = (imv_hcd_state_t*)state;
465 handshake_state = hcd_state->get_handshake_state(hcd_state);
466 received = state->get_action_flags(state);
467 imv_id = this->agent->get_id(this->agent);
468
469 if (handshake_state == IMV_HCD_STATE_END)
470 {
471 return TNC_RESULT_SUCCESS;
472 }
473
474 /* create an empty out message - we might need it */
475 out_msg = imv_msg_create(this->agent, state, id, imv_id, TNC_IMCID_ANY,
476 msg_types[0]);
477
478 if (handshake_state == IMV_HCD_STATE_INIT)
479 {
480 size_t max_attr_size = HCD_MAX_ATTR_SIZE;
481 size_t max_seg_size;
482 seg_contract_t *contract;
483 seg_contract_manager_t *contracts;
484 char buf[BUF_LEN];
485
486 /* Determine maximum PA-TNC attribute segment size */
487 max_seg_size = state->get_max_msg_len(state)
488 - PA_TNC_HEADER_SIZE
489 - PA_TNC_ATTR_HEADER_SIZE
490 - TCG_SEG_ATTR_SEG_ENV_HEADER
491 - PA_TNC_ATTR_HEADER_SIZE
492 - TCG_SEG_ATTR_MAX_SIZE_SIZE;
493
494 /* Announce support of PA-TNC segmentation to IMC */
495 contract = seg_contract_create(msg_types[0], max_attr_size,
496 max_seg_size, TRUE, imv_id, FALSE);
497 contract->get_info_string(contract, buf, BUF_LEN, TRUE);
498 DBG2(DBG_IMV, "%s", buf);
499 contracts = state->get_contracts(state);
500 contracts->add_contract(contracts, contract);
501 attr = tcg_seg_attr_max_size_create(max_attr_size, max_seg_size, TRUE);
502 out_msg->add_attribute(out_msg, attr);
503
504 if ((received & IMV_HCD_ATTR_MUST) != IMV_HCD_ATTR_MUST)
505 {
506 /* create attribute request for missing mandatory attributes */
507 out_msg->add_attribute(out_msg, build_attr_request(received));
508 }
509 hcd_state->set_handshake_state(hcd_state, IMV_HCD_STATE_ATTR_REQ);
510 }
511
512 /* send non-empty PA-TNC message with excl flag not set */
513 if (out_msg->get_attribute_count(out_msg))
514 {
515 result = out_msg->send(out_msg, FALSE);
516 }
517 out_msg->destroy(out_msg);
518
519 return result;
520 }
521
522 METHOD(imv_agent_if_t, solicit_recommendation, TNC_Result,
523 private_imv_hcd_agent_t *this, TNC_ConnectionID id)
524 {
525 imv_state_t *state;
526
527 if (!this->agent->get_state(this->agent, id, &state))
528 {
529 return TNC_RESULT_FATAL;
530 }
531 return this->agent->provide_recommendation(this->agent, state);
532 }
533
534 METHOD(imv_agent_if_t, destroy, void,
535 private_imv_hcd_agent_t *this)
536 {
537 DESTROY_IF(this->agent);
538 free(this);
539 }
540
541 /**
542 * Described in header.
543 */
544 imv_agent_if_t *imv_hcd_agent_create(const char *name, TNC_IMVID id,
545 TNC_Version *actual_version)
546 {
547 private_imv_hcd_agent_t *this;
548 imv_agent_t *agent;
549
550 agent = imv_agent_create(name, msg_types, countof(msg_types), id,
551 actual_version);
552 if (!agent)
553 {
554 return NULL;
555 }
556
557 INIT(this,
558 .public = {
559 .bind_functions = _bind_functions,
560 .notify_connection_change = _notify_connection_change,
561 .receive_message = _receive_message,
562 .receive_message_long = _receive_message_long,
563 .batch_ending = _batch_ending,
564 .solicit_recommendation = _solicit_recommendation,
565 .destroy = _destroy,
566 },
567 .agent = agent,
568 );
569
570 return &this->public;
571 }
572