41b9d0bef9c1c780bfa61c4697bef482dcb66c98
[strongswan.git] / src / libimcv / plugins / imv_os / imv_os_agent.c
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 #define _GNU_SOURCE
17 #include <stdio.h>
18
19 #include "imv_os_agent.h"
20 #include "imv_os_state.h"
21 #include "imv_os_database.h"
22
23 #include <imcv.h>
24 #include <imv/imv_agent.h>
25 #include <imv/imv_msg.h>
26 #include <generic/generic_attr_bool.h>
27 #include <ietf/ietf_attr.h>
28 #include <ietf/ietf_attr_attr_request.h>
29 #include <ietf/ietf_attr_installed_packages.h>
30 #include <ietf/ietf_attr_numeric_version.h>
31 #include <ietf/ietf_attr_op_status.h>
32 #include <ietf/ietf_attr_pa_tnc_error.h>
33 #include <ietf/ietf_attr_product_info.h>
34 #include <ietf/ietf_attr_remediation_instr.h>
35 #include <ietf/ietf_attr_string_version.h>
36 #include <ita/ita_attr.h>
37 #include <ita/ita_attr_get_settings.h>
38 #include <ita/ita_attr_settings.h>
39 #include <ita/ita_attr_device_id.h>
40 #include "tcg/seg/tcg_seg_attr_max_size.h"
41 #include "tcg/seg/tcg_seg_attr_seg_env.h"
42
43 #include <tncif_names.h>
44 #include <tncif_pa_subtypes.h>
45
46 #include <pen/pen.h>
47 #include <utils/debug.h>
48
49 #define INSTALLED_PACKAGES_MAX_ATTR_SIZE 100000000
50
51 typedef struct private_imv_os_agent_t private_imv_os_agent_t;
52 typedef enum imv_os_attr_t imv_os_attr_t;
53
54 /* Subscribed PA-TNC message subtypes */
55 static pen_type_t msg_types[] = {
56 { PEN_IETF, PA_SUBTYPE_IETF_OPERATING_SYSTEM }
57 };
58
59 static char unknown_source_str[] = "install_non_market_apps";
60
61 /**
62 * Flag set when corresponding attribute has been received
63 */
64 enum imv_os_attr_t {
65 IMV_OS_ATTR_PRODUCT_INFORMATION = (1<<0),
66 IMV_OS_ATTR_STRING_VERSION = (1<<1),
67 IMV_OS_ATTR_NUMERIC_VERSION = (1<<2),
68 IMV_OS_ATTR_OPERATIONAL_STATUS = (1<<3),
69 IMV_OS_ATTR_FORWARDING_ENABLED = (1<<4),
70 IMV_OS_ATTR_FACTORY_DEFAULT_PWD_ENABLED = (1<<5),
71 IMV_OS_ATTR_DEVICE_ID = (1<<6),
72 IMV_OS_ATTR_MUST = (1<<7)-1,
73 IMV_OS_ATTR_INSTALLED_PACKAGES = (1<<7),
74 IMV_OS_ATTR_SETTINGS = (1<<8)
75 };
76
77 /**
78 * Private data of an imv_os_agent_t object.
79 */
80 struct private_imv_os_agent_t {
81
82 /**
83 * Public members of imv_os_agent_t
84 */
85 imv_agent_if_t public;
86
87 /**
88 * IMV agent responsible for generic functions
89 */
90 imv_agent_t *agent;
91
92 /**
93 * IMV OS database
94 */
95 imv_os_database_t *db;
96
97 };
98
99 METHOD(imv_agent_if_t, bind_functions, TNC_Result,
100 private_imv_os_agent_t *this, TNC_TNCS_BindFunctionPointer bind_function)
101 {
102 return this->agent->bind_functions(this->agent, bind_function);
103 }
104
105 METHOD(imv_agent_if_t, notify_connection_change, TNC_Result,
106 private_imv_os_agent_t *this, TNC_ConnectionID id,
107 TNC_ConnectionState new_state)
108 {
109 TNC_IMV_Action_Recommendation rec;
110 imv_state_t *state;
111 imv_session_t *session;
112
113 switch (new_state)
114 {
115 case TNC_CONNECTION_STATE_CREATE:
116 state = imv_os_state_create(id);
117 return this->agent->create_state(this->agent, state);
118 case TNC_CONNECTION_STATE_DELETE:
119 return this->agent->delete_state(this->agent, id);
120 case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
121 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
122 case TNC_CONNECTION_STATE_ACCESS_NONE:
123 if (this->agent->get_state(this->agent, id, &state) && imcv_db)
124 {
125 session = state->get_session(state);
126
127 if (session->get_policy_started(session))
128 {
129 switch (new_state)
130 {
131 case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
132 rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
133 break;
134 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
135 rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
136 break;
137 case TNC_CONNECTION_STATE_ACCESS_NONE:
138 default:
139 rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
140 }
141 imcv_db->add_recommendation(imcv_db, session, rec);
142 if (!imcv_db->policy_script(imcv_db, session, FALSE))
143 {
144 DBG1(DBG_IMV, "error in policy script stop");
145 }
146 }
147 }
148 /* fall through to default state */
149 default:
150 return this->agent->change_state(this->agent, id, new_state, NULL);
151 }
152 }
153
154 /**
155 * Process a received message
156 */
157 static TNC_Result receive_msg(private_imv_os_agent_t *this, imv_state_t *state,
158 imv_msg_t *in_msg)
159 {
160 imv_msg_t *out_msg;
161 imv_os_state_t *os_state;
162 imv_session_t *session;
163 imv_os_info_t *os_info = NULL;
164 enumerator_t *enumerator;
165 pa_tnc_attr_t *attr;
166 pen_type_t type;
167 TNC_Result result;
168 chunk_t os_name = chunk_empty;
169 chunk_t os_version = chunk_empty;
170 bool fatal_error = FALSE, assessment = FALSE;
171 uint16_t missing;
172
173 os_state = (imv_os_state_t*)state;
174 session = state->get_session(state);
175 os_info = session->get_os_info(session);
176
177 /* generate an outgoing PA-TNC message - we might need it */
178 out_msg = imv_msg_create_as_reply(in_msg);
179
180 /* parse received PA-TNC message and handle local and remote errors */
181 result = in_msg->receive(in_msg,out_msg, &fatal_error);
182 if (result != TNC_RESULT_SUCCESS)
183 {
184 out_msg->destroy(out_msg);
185 return result;
186 }
187
188 /* analyze PA-TNC attributes */
189 enumerator = in_msg->create_attribute_enumerator(in_msg);
190 while (enumerator->enumerate(enumerator, &attr))
191 {
192 type = attr->get_type(attr);
193
194 if (type.vendor_id == PEN_IETF)
195 {
196 switch (type.type)
197 {
198 case IETF_ATTR_PRODUCT_INFORMATION:
199 {
200 ietf_attr_product_info_t *attr_cast;
201 pen_t vendor_id;
202
203 state->set_action_flags(state,
204 IMV_OS_ATTR_PRODUCT_INFORMATION);
205 attr_cast = (ietf_attr_product_info_t*)attr;
206 os_name = attr_cast->get_info(attr_cast, &vendor_id, NULL);
207 os_info->set_name(os_info, os_name);
208
209 if (vendor_id != PEN_IETF)
210 {
211 DBG1(DBG_IMV, "operating system name is '%.*s' "
212 "from vendor %N", os_name.len, os_name.ptr,
213 pen_names, vendor_id);
214 }
215 else
216 {
217 DBG1(DBG_IMV, "operating system name is '%.*s'",
218 os_name.len, os_name.ptr);
219 }
220 break;
221 }
222 case IETF_ATTR_STRING_VERSION:
223 {
224 ietf_attr_string_version_t *attr_cast;
225
226 state->set_action_flags(state,
227 IMV_OS_ATTR_STRING_VERSION);
228 attr_cast = (ietf_attr_string_version_t*)attr;
229 os_version = attr_cast->get_version(attr_cast, NULL, NULL);
230 os_info->set_version(os_info, os_version);
231
232 if (os_version.len)
233 {
234 DBG1(DBG_IMV, "operating system version is '%.*s'",
235 os_version.len, os_version.ptr);
236 }
237 break;
238 }
239 case IETF_ATTR_NUMERIC_VERSION:
240 {
241 ietf_attr_numeric_version_t *attr_cast;
242 uint32_t major, minor;
243
244 state->set_action_flags(state,
245 IMV_OS_ATTR_NUMERIC_VERSION);
246 attr_cast = (ietf_attr_numeric_version_t*)attr;
247 attr_cast->get_version(attr_cast, &major, &minor);
248 DBG1(DBG_IMV, "operating system numeric version is %d.%d",
249 major, minor);
250 break;
251 }
252 case IETF_ATTR_OPERATIONAL_STATUS:
253 {
254 ietf_attr_op_status_t *attr_cast;
255 op_status_t op_status;
256 op_result_t op_result;
257 time_t last_boot;
258
259 state->set_action_flags(state,
260 IMV_OS_ATTR_OPERATIONAL_STATUS);
261 attr_cast = (ietf_attr_op_status_t*)attr;
262 op_status = attr_cast->get_status(attr_cast);
263 op_result = attr_cast->get_result(attr_cast);
264 last_boot = attr_cast->get_last_use(attr_cast);
265 DBG1(DBG_IMV, "operational status: %N, result: %N",
266 op_status_names, op_status, op_result_names, op_result);
267 DBG1(DBG_IMV, "last boot: %T", &last_boot, TRUE);
268 break;
269 }
270 case IETF_ATTR_FORWARDING_ENABLED:
271 {
272 generic_attr_bool_t *attr_cast;
273 os_fwd_status_t fwd_status;
274
275 state->set_action_flags(state,
276 IMV_OS_ATTR_FORWARDING_ENABLED);
277 attr_cast = (generic_attr_bool_t*)attr;
278 fwd_status = attr_cast->get_status(attr_cast);
279 DBG1(DBG_IMV, "IPv4 forwarding is %N",
280 os_fwd_status_names, fwd_status);
281 if (fwd_status == OS_FWD_ENABLED)
282 {
283 os_state->set_os_settings(os_state,
284 OS_SETTINGS_FWD_ENABLED);
285 }
286 break;
287 }
288 case IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED:
289 {
290 generic_attr_bool_t *attr_cast;
291 bool default_pwd_status;
292
293 state->set_action_flags(state,
294 IMV_OS_ATTR_FACTORY_DEFAULT_PWD_ENABLED);
295 attr_cast = (generic_attr_bool_t*)attr;
296 default_pwd_status = attr_cast->get_status(attr_cast);
297 DBG1(DBG_IMV, "factory default password is %sabled",
298 default_pwd_status ? "en":"dis");
299 if (default_pwd_status)
300 {
301 os_state->set_os_settings(os_state,
302 OS_SETTINGS_DEFAULT_PWD_ENABLED);
303 }
304 break;
305 }
306 case IETF_ATTR_INSTALLED_PACKAGES:
307 {
308 ietf_attr_installed_packages_t *attr_cast;
309 enumerator_t *e;
310 status_t status;
311
312 state->set_action_flags(state,
313 IMV_OS_ATTR_INSTALLED_PACKAGES);
314 if (!this->db)
315 {
316 break;
317 }
318 attr_cast = (ietf_attr_installed_packages_t*)attr;
319
320 e = attr_cast->create_enumerator(attr_cast);
321 status = this->db->check_packages(this->db, os_state, e);
322 e->destroy(e);
323
324 if (status == FAILED)
325 {
326 state->set_recommendation(state,
327 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
328 TNC_IMV_EVALUATION_RESULT_ERROR);
329 assessment = TRUE;
330 }
331 missing = attr_cast->get_count(attr_cast);
332 os_state->set_missing(os_state, missing);
333 attr_cast->clear_packages(attr_cast);
334 break;
335 }
336 default:
337 break;
338 }
339 }
340 else if (type.vendor_id == PEN_ITA)
341 {
342 switch (type.type)
343 {
344 case ITA_ATTR_SETTINGS:
345 {
346 ita_attr_settings_t *attr_cast;
347 enumerator_t *e;
348 char *name;
349 chunk_t value;
350
351 state->set_action_flags(state, IMV_OS_ATTR_SETTINGS);
352
353 attr_cast = (ita_attr_settings_t*)attr;
354 e = attr_cast->create_enumerator(attr_cast);
355 while (e->enumerate(e, &name, &value))
356 {
357 if (streq(name, unknown_source_str) &&
358 chunk_equals(value, chunk_from_chars('1')))
359 {
360 os_state->set_os_settings(os_state,
361 OS_SETTINGS_UNKNOWN_SOURCE);
362 }
363 DBG1(DBG_IMV, "setting '%s'\n %.*s",
364 name, value.len, value.ptr);
365 }
366 e->destroy(e);
367 break;
368 }
369 case ITA_ATTR_DEVICE_ID:
370 {
371 chunk_t value;
372
373 state->set_action_flags(state, IMV_OS_ATTR_DEVICE_ID);
374
375 value = attr->get_value(attr);
376 DBG1(DBG_IMV, "device ID is %.*s", value.len, value.ptr);
377 session->set_device_id(session, value);
378 break;
379 }
380 default:
381 break;
382 }
383 }
384 }
385 enumerator->destroy(enumerator);
386
387 if (fatal_error)
388 {
389 state->set_recommendation(state,
390 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
391 TNC_IMV_EVALUATION_RESULT_ERROR);
392 assessment = TRUE;
393 }
394
395 if (assessment)
396 {
397 os_state->set_handshake_state(os_state, IMV_OS_STATE_END);
398 result = out_msg->send_assessment(out_msg);
399 if (result == TNC_RESULT_SUCCESS)
400 {
401 result = this->agent->provide_recommendation(this->agent, state);
402 }
403 }
404 else
405 {
406 /* send PA-TNC message with the EXCL flag set */
407 result = out_msg->send(out_msg, TRUE);
408 }
409 out_msg->destroy(out_msg);
410
411 return result;
412 }
413
414 METHOD(imv_agent_if_t, receive_message, TNC_Result,
415 private_imv_os_agent_t *this, TNC_ConnectionID id,
416 TNC_MessageType msg_type, chunk_t msg)
417 {
418 imv_state_t *state;
419 imv_msg_t *in_msg;
420 TNC_Result result;
421
422 if (!this->agent->get_state(this->agent, id, &state))
423 {
424 return TNC_RESULT_FATAL;
425 }
426 in_msg = imv_msg_create_from_data(this->agent, state, id, msg_type, msg);
427 result = receive_msg(this, state, in_msg);
428 in_msg->destroy(in_msg);
429
430 return result;
431 }
432
433 METHOD(imv_agent_if_t, receive_message_long, TNC_Result,
434 private_imv_os_agent_t *this, TNC_ConnectionID id,
435 TNC_UInt32 src_imc_id, TNC_UInt32 dst_imv_id,
436 TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype, chunk_t msg)
437 {
438 imv_state_t *state;
439 imv_msg_t *in_msg;
440 TNC_Result result;
441
442 if (!this->agent->get_state(this->agent, id, &state))
443 {
444 return TNC_RESULT_FATAL;
445 }
446 in_msg = imv_msg_create_from_long_data(this->agent, state, id,
447 src_imc_id, dst_imv_id, msg_vid, msg_subtype, msg);
448 result = receive_msg(this, state, in_msg);
449 in_msg->destroy(in_msg);
450
451 return result;
452
453 }
454
455 /**
456 * Build an IETF Attribute Request attribute for missing attributes
457 */
458 static pa_tnc_attr_t* build_attr_request(uint32_t received)
459 {
460 pa_tnc_attr_t *attr;
461 ietf_attr_attr_request_t *attr_cast;
462
463 attr = ietf_attr_attr_request_create(PEN_RESERVED, 0);
464 attr_cast = (ietf_attr_attr_request_t*)attr;
465
466 if (!(received & IMV_OS_ATTR_PRODUCT_INFORMATION) ||
467 !(received & IMV_OS_ATTR_STRING_VERSION))
468 {
469 attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_PRODUCT_INFORMATION);
470 attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_STRING_VERSION);
471 }
472 if (!(received & IMV_OS_ATTR_NUMERIC_VERSION))
473 {
474 attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_NUMERIC_VERSION);
475 }
476 if (!(received & IMV_OS_ATTR_OPERATIONAL_STATUS))
477 {
478 attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_OPERATIONAL_STATUS);
479 }
480 if (!(received & IMV_OS_ATTR_FORWARDING_ENABLED))
481 {
482 attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_FORWARDING_ENABLED);
483 }
484 if (!(received & IMV_OS_ATTR_FACTORY_DEFAULT_PWD_ENABLED))
485 {
486 attr_cast->add(attr_cast, PEN_IETF,
487 IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED);
488 }
489 if (!(received & IMV_OS_ATTR_DEVICE_ID))
490 {
491 attr_cast->add(attr_cast, PEN_ITA, ITA_ATTR_DEVICE_ID);
492 }
493
494 return attr;
495 }
496
497 METHOD(imv_agent_if_t, batch_ending, TNC_Result,
498 private_imv_os_agent_t *this, TNC_ConnectionID id)
499 {
500 imv_msg_t *out_msg;
501 imv_state_t *state;
502 imv_session_t *session;
503 imv_workitem_t *workitem;
504 imv_os_state_t *os_state;
505 imv_os_handshake_state_t handshake_state;
506 pa_tnc_attr_t *attr;
507 TNC_IMVID imv_id;
508 TNC_Result result = TNC_RESULT_SUCCESS;
509 bool no_workitems = TRUE;
510 enumerator_t *enumerator;
511 uint32_t received;
512
513 if (!this->agent->get_state(this->agent, id, &state))
514 {
515 return TNC_RESULT_FATAL;
516 }
517 os_state = (imv_os_state_t*)state;
518 handshake_state = os_state->get_handshake_state(os_state);
519 received = state->get_action_flags(state);
520 session = state->get_session(state);
521 imv_id = this->agent->get_id(this->agent);
522
523 if (handshake_state == IMV_OS_STATE_END)
524 {
525 return TNC_RESULT_SUCCESS;
526 }
527
528 /* create an empty out message - we might need it */
529 out_msg = imv_msg_create(this->agent, state, id, imv_id, TNC_IMCID_ANY,
530 msg_types[0]);
531
532 if (handshake_state == IMV_OS_STATE_INIT)
533 {
534 size_t max_attr_size = INSTALLED_PACKAGES_MAX_ATTR_SIZE;
535 size_t max_seg_size;
536 seg_contract_t *contract;
537 seg_contract_manager_t *contracts;
538 char buf[BUF_LEN];
539
540 /* Determine maximum PA-TNC attribute segment size */
541 max_seg_size = state->get_max_msg_len(state)
542 - PA_TNC_HEADER_SIZE
543 - PA_TNC_ATTR_HEADER_SIZE
544 - TCG_SEG_ATTR_SEG_ENV_HEADER;
545
546 /* Announce support of PA-TNC segmentation to IMC */
547 contract = seg_contract_create(msg_types[0], max_attr_size,
548 max_seg_size, TRUE, imv_id, FALSE);
549 contract->get_info_string(contract, buf, BUF_LEN, TRUE);
550 DBG2(DBG_IMV, "%s", buf);
551 contracts = state->get_contracts(state);
552 contracts->add_contract(contracts, contract);
553 attr = tcg_seg_attr_max_size_create(max_attr_size, max_seg_size, TRUE);
554 out_msg->add_attribute(out_msg, attr);
555
556 if ((received & IMV_OS_ATTR_MUST) != IMV_OS_ATTR_MUST)
557 {
558 /* create attribute request for missing mandatory attributes */
559 out_msg->add_attribute(out_msg, build_attr_request(received));
560 }
561 }
562
563 if (handshake_state < IMV_OS_STATE_POLICY_START)
564 {
565 if (session->get_policy_started(session))
566 {
567 /* the policy script has already been started by another IMV */
568 handshake_state = IMV_OS_STATE_POLICY_START;
569 }
570 else
571 {
572 if (((received & IMV_OS_ATTR_PRODUCT_INFORMATION) &&
573 (received & IMV_OS_ATTR_STRING_VERSION)) &&
574 ((received & IMV_OS_ATTR_DEVICE_ID) ||
575 (handshake_state == IMV_OS_STATE_ATTR_REQ)))
576 {
577 if (!session->get_device_id(session, NULL))
578 {
579 session->set_device_id(session, chunk_empty);
580 }
581 if (imcv_db)
582 {
583 /* start the policy script */
584 if (!imcv_db->policy_script(imcv_db, session, TRUE))
585 {
586 DBG1(DBG_IMV, "error in policy script start");
587 }
588 }
589 else
590 {
591 DBG2(DBG_IMV, "no workitems available - "
592 "no evaluation possible");
593 state->set_recommendation(state,
594 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
595 TNC_IMV_EVALUATION_RESULT_DONT_KNOW);
596 session->set_policy_started(session, TRUE);
597 }
598 handshake_state = IMV_OS_STATE_POLICY_START;
599 }
600 else if (handshake_state == IMV_OS_STATE_ATTR_REQ)
601 {
602 /**
603 * both the IETF Product Information and IETF String Version
604 * attribute should have been present
605 */
606 state->set_recommendation(state,
607 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
608 TNC_IMV_EVALUATION_RESULT_ERROR);
609
610 /* send assessment */
611 result = out_msg->send_assessment(out_msg);
612 out_msg->destroy(out_msg);
613
614 if (result != TNC_RESULT_SUCCESS)
615 {
616 return result;
617 }
618 return this->agent->provide_recommendation(this->agent, state);
619 }
620 else
621 {
622 handshake_state = IMV_OS_STATE_ATTR_REQ;
623 }
624 }
625 os_state->set_handshake_state(os_state, handshake_state);
626 }
627
628 if (handshake_state == IMV_OS_STATE_POLICY_START)
629 {
630 enumerator = session->create_workitem_enumerator(session);
631 if (enumerator)
632 {
633 while (enumerator->enumerate(enumerator, &workitem))
634 {
635 if (workitem->get_imv_id(workitem) != TNC_IMVID_ANY)
636 {
637 continue;
638 }
639
640 switch (workitem->get_type(workitem))
641 {
642 case IMV_WORKITEM_PACKAGES:
643 attr = ietf_attr_attr_request_create(PEN_IETF,
644 IETF_ATTR_INSTALLED_PACKAGES);
645 out_msg->add_attribute(out_msg, attr);
646 break;
647 case IMV_WORKITEM_UNKNOWN_SOURCE:
648 attr = ita_attr_get_settings_create(unknown_source_str);
649 out_msg->add_attribute(out_msg, attr);
650 break;
651 case IMV_WORKITEM_FORWARDING:
652 case IMV_WORKITEM_DEFAULT_PWD:
653 break;
654 default:
655 continue;
656 }
657 workitem->set_imv_id(workitem, imv_id);
658 no_workitems = FALSE;
659 }
660 enumerator->destroy(enumerator);
661
662 if (no_workitems)
663 {
664 DBG2(DBG_IMV, "IMV %d has no workitems - "
665 "no evaluation requested", imv_id);
666 state->set_recommendation(state,
667 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
668 TNC_IMV_EVALUATION_RESULT_DONT_KNOW);
669 }
670 handshake_state = IMV_OS_STATE_WORKITEMS;
671 os_state->set_handshake_state(os_state, handshake_state);
672 }
673 }
674
675 if (handshake_state == IMV_OS_STATE_WORKITEMS)
676 {
677 TNC_IMV_Evaluation_Result eval;
678 TNC_IMV_Action_Recommendation rec;
679 char result_str[BUF_LEN];
680 bool fail;
681
682 enumerator = session->create_workitem_enumerator(session);
683 while (enumerator->enumerate(enumerator, &workitem))
684 {
685 if (workitem->get_imv_id(workitem) != imv_id)
686 {
687 continue;
688 }
689 eval = TNC_IMV_EVALUATION_RESULT_DONT_KNOW;
690
691 switch (workitem->get_type(workitem))
692 {
693 case IMV_WORKITEM_PACKAGES:
694 {
695 int count, count_update, count_blacklist, count_ok;
696
697 if (!(received & IMV_OS_ATTR_INSTALLED_PACKAGES) ||
698 os_state->get_missing(os_state) > 0)
699 {
700 continue;
701 }
702 os_state->get_count(os_state, &count, &count_update,
703 &count_blacklist, &count_ok);
704 fail = count_update || count_blacklist;
705 eval = fail ? TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR :
706 TNC_IMV_EVALUATION_RESULT_COMPLIANT;
707 snprintf(result_str, BUF_LEN, "processed %d packages: "
708 "%d not updated, %d blacklisted, %d ok, "
709 "%d unknown",
710 count, count_update, count_blacklist, count_ok,
711 count - count_update - count_blacklist - count_ok);
712 break;
713 }
714 case IMV_WORKITEM_UNKNOWN_SOURCE:
715 if (!(received & IMV_OS_ATTR_SETTINGS))
716 {
717 continue;
718 }
719 fail = os_state->get_os_settings(os_state) &
720 OS_SETTINGS_UNKNOWN_SOURCE;
721 eval = fail ? TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR :
722 TNC_IMV_EVALUATION_RESULT_COMPLIANT;
723 snprintf(result_str, BUF_LEN, "unknown sources%s enabled",
724 fail ? "" : " not");
725 break;
726 case IMV_WORKITEM_FORWARDING:
727 if (!(received & IMV_OS_ATTR_FORWARDING_ENABLED))
728 {
729 continue;
730 }
731 fail = os_state->get_os_settings(os_state) &
732 OS_SETTINGS_FWD_ENABLED;
733 eval = fail ? TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR :
734 TNC_IMV_EVALUATION_RESULT_COMPLIANT;
735 snprintf(result_str, BUF_LEN, "forwarding%s enabled",
736 fail ? "" : " not");
737 break;
738 case IMV_WORKITEM_DEFAULT_PWD:
739 if (!(received & IMV_OS_ATTR_FACTORY_DEFAULT_PWD_ENABLED))
740 {
741 continue;
742 }
743 fail = os_state->get_os_settings(os_state) &
744 OS_SETTINGS_DEFAULT_PWD_ENABLED;
745 eval = fail ? TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR :
746 TNC_IMV_EVALUATION_RESULT_COMPLIANT;
747 snprintf(result_str, BUF_LEN, "factory default password%s enabled",
748 fail ? "" : " not");
749 break;
750 default:
751 continue;
752 }
753 if (eval != TNC_IMV_EVALUATION_RESULT_DONT_KNOW)
754 {
755 session->remove_workitem(session, enumerator);
756 rec = workitem->set_result(workitem, result_str, eval);
757 state->update_recommendation(state, rec, eval);
758 imcv_db->finalize_workitem(imcv_db, workitem);
759 workitem->destroy(workitem);
760 }
761 }
762 enumerator->destroy(enumerator);
763
764 /* finalized all workitems ? */
765 if (session->get_workitem_count(session, imv_id) == 0)
766 {
767 os_state->set_handshake_state(os_state, IMV_OS_STATE_END);
768
769 result = out_msg->send_assessment(out_msg);
770 out_msg->destroy(out_msg);
771 if (result != TNC_RESULT_SUCCESS)
772 {
773 return result;
774 }
775 return this->agent->provide_recommendation(this->agent, state);
776 }
777 }
778
779 /* send non-empty PA-TNC message with excl flag not set */
780 if (out_msg->get_attribute_count(out_msg))
781 {
782 result = out_msg->send(out_msg, FALSE);
783 }
784 out_msg->destroy(out_msg);
785
786 return result;
787 }
788
789 METHOD(imv_agent_if_t, solicit_recommendation, TNC_Result,
790 private_imv_os_agent_t *this, TNC_ConnectionID id)
791 {
792 imv_state_t *state;
793
794 if (!this->agent->get_state(this->agent, id, &state))
795 {
796 return TNC_RESULT_FATAL;
797 }
798 return this->agent->provide_recommendation(this->agent, state);
799 }
800
801 METHOD(imv_agent_if_t, destroy, void,
802 private_imv_os_agent_t *this)
803 {
804 DESTROY_IF(this->agent);
805 DESTROY_IF(this->db);
806 free(this);
807 }
808
809 /**
810 * Described in header.
811 */
812 imv_agent_if_t *imv_os_agent_create(const char *name, TNC_IMVID id,
813 TNC_Version *actual_version)
814 {
815 private_imv_os_agent_t *this;
816 imv_agent_t *agent;
817
818 agent = imv_agent_create(name, msg_types, countof(msg_types), id,
819 actual_version);
820 if (!agent)
821 {
822 return NULL;
823 }
824
825 INIT(this,
826 .public = {
827 .bind_functions = _bind_functions,
828 .notify_connection_change = _notify_connection_change,
829 .receive_message = _receive_message,
830 .receive_message_long = _receive_message_long,
831 .batch_ending = _batch_ending,
832 .solicit_recommendation = _solicit_recommendation,
833 .destroy = _destroy,
834 },
835 .agent = agent,
836 .db = imv_os_database_create(imcv_db),
837 );
838
839 return &this->public;
840 }
841