0091e3e1c10016d63a04c5e0fe52c5532d4743f5
[strongswan.git] / src / libimcv / plugins / imv_os / imv_os.c
1 /*
2 * Copyright (C) 2012 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_os_state.h"
17 #include "imv_os_database.h"
18
19 #include <imv/imv_agent.h>
20 #include <imv/imv_msg.h>
21 #include <ietf/ietf_attr.h>
22 #include <ietf/ietf_attr_attr_request.h>
23 #include <ietf/ietf_attr_default_pwd_enabled.h>
24 #include <ietf/ietf_attr_fwd_enabled.h>
25 #include <ietf/ietf_attr_installed_packages.h>
26 #include <ietf/ietf_attr_numeric_version.h>
27 #include <ietf/ietf_attr_op_status.h>
28 #include <ietf/ietf_attr_pa_tnc_error.h>
29 #include <ietf/ietf_attr_product_info.h>
30 #include <ietf/ietf_attr_remediation_instr.h>
31 #include <ietf/ietf_attr_string_version.h>
32 #include <ita/ita_attr.h>
33 #include <ita/ita_attr_get_settings.h>
34 #include <ita/ita_attr_settings.h>
35 #include <ita/ita_attr_angel.h>
36
37 #include <tncif_names.h>
38 #include <tncif_pa_subtypes.h>
39
40 #include <pen/pen.h>
41 #include <collections/linked_list.h>
42 #include <utils/debug.h>
43 #include <utils/lexparser.h>
44
45 /* IMV definitions */
46
47 static const char imv_name[] = "OS";
48
49 static pen_type_t msg_types[] = {
50 { PEN_IETF, PA_SUBTYPE_IETF_OPERATING_SYSTEM }
51 };
52
53 static imv_agent_t *imv_os;
54
55 /**
56 * IMV OS database
57 */
58 static imv_os_database_t *os_db;
59
60 /*
61 * see section 3.8.1 of TCG TNC IF-IMV Specification 1.3
62 */
63 TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id,
64 TNC_Version min_version,
65 TNC_Version max_version,
66 TNC_Version *actual_version)
67 {
68 char *uri;
69
70 if (imv_os)
71 {
72 DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name);
73 return TNC_RESULT_ALREADY_INITIALIZED;
74 }
75 imv_os = imv_agent_create(imv_name, msg_types, countof(msg_types),
76 imv_id, actual_version);
77 if (!imv_os)
78 {
79 return TNC_RESULT_FATAL;
80 }
81 if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1)
82 {
83 DBG1(DBG_IMV, "no common IF-IMV version");
84 return TNC_RESULT_NO_COMMON_VERSION;
85 }
86
87 /* attach OS database */
88 uri = lib->settings->get_str(lib->settings,
89 "libimcv.plugins.imv-os.database", NULL);
90 if (uri)
91 {
92 os_db = imv_os_database_create(uri);
93 }
94
95 return TNC_RESULT_SUCCESS;
96 }
97
98 /**
99 * see section 3.8.2 of TCG TNC IF-IMV Specification 1.3
100 */
101 TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id,
102 TNC_ConnectionID connection_id,
103 TNC_ConnectionState new_state)
104 {
105 imv_state_t *state;
106
107 if (!imv_os)
108 {
109 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
110 return TNC_RESULT_NOT_INITIALIZED;
111 }
112 switch (new_state)
113 {
114 case TNC_CONNECTION_STATE_CREATE:
115 state = imv_os_state_create(connection_id);
116 return imv_os->create_state(imv_os, state);
117 case TNC_CONNECTION_STATE_DELETE:
118 return imv_os->delete_state(imv_os, connection_id);
119 default:
120 return imv_os->change_state(imv_os, connection_id,
121 new_state, NULL);
122 }
123 }
124
125 /**
126 * print multi-line values to debug output
127 */
128 static void dbg_imv_multi_line(chunk_t value)
129 {
130 chunk_t line;
131
132 while (extract_token(&line, '\n', &value))
133 {
134 DBG2(DBG_IMV, " %.*s", line.len, line.ptr);
135 }
136 if (value.len)
137 {
138 DBG2(DBG_IMV, " %.*s", value.len, value.ptr);
139 }
140 }
141
142 static TNC_Result receive_message(imv_state_t *state, imv_msg_t *in_msg)
143 {
144 imv_msg_t *out_msg;
145 imv_os_state_t *os_state;
146 enumerator_t *enumerator;
147 pa_tnc_attr_t *attr;
148 pen_type_t type;
149 TNC_Result result;
150 chunk_t os_name = chunk_empty;
151 chunk_t os_version = chunk_empty;
152 bool fatal_error = FALSE, assessment = FALSE;
153
154 os_state = (imv_os_state_t*)state;
155
156 /* parse received PA-TNC message and handle local and remote errors */
157 result = in_msg->receive(in_msg, &fatal_error);
158 if (result != TNC_RESULT_SUCCESS)
159 {
160 return result;
161 }
162
163 out_msg = imv_msg_create_as_reply(in_msg);
164
165 /* analyze PA-TNC attributes */
166 enumerator = in_msg->create_attribute_enumerator(in_msg);
167 while (enumerator->enumerate(enumerator, &attr))
168 {
169 type = attr->get_type(attr);
170
171 if (type.vendor_id == PEN_IETF)
172 {
173 switch (type.type)
174 {
175 case IETF_ATTR_PRODUCT_INFORMATION:
176 {
177 ietf_attr_product_info_t *attr_cast;
178 pen_t vendor_id;
179
180 attr_cast = (ietf_attr_product_info_t*)attr;
181 os_name = attr_cast->get_info(attr_cast, &vendor_id, NULL);
182 if (vendor_id != PEN_IETF)
183 {
184 DBG1(DBG_IMV, "operating system name is '%.*s' "
185 "from vendor %N", os_name.len, os_name.ptr,
186 pen_names, vendor_id);
187 }
188 else
189 {
190 DBG1(DBG_IMV, "operating system name is '%.*s'",
191 os_name.len, os_name.ptr);
192 }
193 break;
194 }
195 case IETF_ATTR_STRING_VERSION:
196 {
197 ietf_attr_string_version_t *attr_cast;
198
199 attr_cast = (ietf_attr_string_version_t*)attr;
200 os_version = attr_cast->get_version(attr_cast, NULL, NULL);
201 if (os_version.len)
202 {
203 DBG1(DBG_IMV, "operating system version is '%.*s'",
204 os_version.len, os_version.ptr);
205 }
206 break;
207 }
208 case IETF_ATTR_NUMERIC_VERSION:
209 {
210 ietf_attr_numeric_version_t *attr_cast;
211 u_int32_t major, minor;
212
213 attr_cast = (ietf_attr_numeric_version_t*)attr;
214 attr_cast->get_version(attr_cast, &major, &minor);
215 DBG1(DBG_IMV, "operating system numeric version is %d.%d",
216 major, minor);
217 break;
218 }
219 case IETF_ATTR_OPERATIONAL_STATUS:
220 {
221 ietf_attr_op_status_t *attr_cast;
222 op_status_t op_status;
223 op_result_t op_result;
224 time_t last_boot;
225
226 attr_cast = (ietf_attr_op_status_t*)attr;
227 op_status = attr_cast->get_status(attr_cast);
228 op_result = attr_cast->get_result(attr_cast);
229 last_boot = attr_cast->get_last_use(attr_cast);
230 DBG1(DBG_IMV, "operational status: %N, result: %N",
231 op_status_names, op_status, op_result_names, op_result);
232 DBG1(DBG_IMV, "last boot: %T", &last_boot, TRUE);
233 break;
234 }
235 case IETF_ATTR_FORWARDING_ENABLED:
236 {
237 ietf_attr_fwd_enabled_t *attr_cast;
238 os_fwd_status_t fwd_status;
239
240 attr_cast = (ietf_attr_fwd_enabled_t*)attr;
241 fwd_status = attr_cast->get_status(attr_cast);
242 DBG1(DBG_IMV, "IPv4 forwarding status: %N",
243 os_fwd_status_names, fwd_status);
244 break;
245 }
246 case IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED:
247 {
248 ietf_attr_default_pwd_enabled_t *attr_cast;
249 bool default_pwd_status;
250
251 attr_cast = (ietf_attr_default_pwd_enabled_t*)attr;
252 default_pwd_status = attr_cast->get_status(attr_cast);
253 DBG1(DBG_IMV, "factory default password: %sabled",
254 default_pwd_status ? "en":"dis");
255 break;
256 }
257 case IETF_ATTR_INSTALLED_PACKAGES:
258 {
259 ietf_attr_installed_packages_t *attr_cast;
260 enumerator_t *e;
261 status_t status;
262
263 /* Received at least one Installed Packages attribute */
264 os_state->set_package_request(os_state, FALSE);
265
266 if (!os_db)
267 {
268 break;
269 }
270 attr_cast = (ietf_attr_installed_packages_t*)attr;
271
272 e = attr_cast->create_enumerator(attr_cast);
273 status = os_db->check_packages(os_db, os_state, e);
274 e->destroy(e);
275
276 if (status == FAILED)
277 {
278 state->set_recommendation(state,
279 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
280 TNC_IMV_EVALUATION_RESULT_ERROR);
281 assessment = TRUE;
282 }
283 break;
284 }
285 default:
286 break;
287 }
288 }
289 else if (type.vendor_id == PEN_ITA)
290 {
291 switch (type.type)
292 {
293 case ITA_ATTR_SETTINGS:
294 {
295 ita_attr_settings_t *attr_cast;
296 enumerator_t *e;
297 char *name;
298 chunk_t value;
299
300 attr_cast = (ita_attr_settings_t*)attr;
301 e = attr_cast->create_enumerator(attr_cast);
302 while (e->enumerate(e, &name, &value))
303 {
304 DBG1(DBG_IMV, "setting '%s'", name);
305 dbg_imv_multi_line(value);
306 }
307 e->destroy(e);
308 break;
309 }
310 case ITA_ATTR_START_ANGEL:
311 os_state->set_angel_count(os_state, TRUE);
312 break;
313 case ITA_ATTR_STOP_ANGEL:
314 os_state->set_angel_count(os_state, FALSE);
315 break;
316 default:
317 break;
318 }
319 }
320 }
321 enumerator->destroy(enumerator);
322
323 if (os_name.len && os_version.len)
324 {
325 os_type_t os_type;
326 char *product_info;
327 char *uri = "http://remediation.strongswan.org/fix-it/";
328 char *string = "use a Linux operating system instead of Windows 1.2.3";
329 char *lang_code = "en";
330
331 os_type = os_type_from_name(os_name);
332 os_state->set_info(os_state,os_type, os_name, os_version);
333 product_info = os_state->get_info(os_state, NULL, NULL, NULL);
334
335 if (streq(product_info, "Windows 1.2.3"))
336 {
337 DBG1(DBG_IMV, "OS '%s' is not supported", product_info);
338
339 attr = ietf_attr_remediation_instr_create_from_string(
340 chunk_create(string, strlen(string)),
341 chunk_create(lang_code, strlen(lang_code)));
342 out_msg->add_attribute(out_msg, attr);
343 attr = ietf_attr_remediation_instr_create_from_uri(
344 chunk_create(uri, strlen(uri)));
345 out_msg->add_attribute(out_msg, attr);
346
347 state->set_recommendation(state,
348 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
349 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR);
350 assessment = TRUE;
351 }
352 else
353 {
354 ita_attr_get_settings_t *attr_cast;
355
356 /* requesting installed packages */
357 os_state->set_package_request(os_state, TRUE);
358 attr = ietf_attr_attr_request_create(PEN_IETF,
359 IETF_ATTR_INSTALLED_PACKAGES);
360 out_msg->add_attribute(out_msg, attr);
361
362 /* requesting Android or Linux settings */
363 attr = ita_attr_get_settings_create();
364 attr_cast = (ita_attr_get_settings_t*)attr;
365
366 if (os_type == OS_TYPE_ANDROID)
367 {
368 attr_cast->add(attr_cast, "android_id");
369 attr_cast->add(attr_cast, "install_non_market_apps");
370 }
371 else
372 {
373 attr_cast->add(attr_cast, "/proc/sys/kernel/random/boot_id");
374 attr_cast->add(attr_cast, "/proc/sys/kernel/tainted");
375 }
376 out_msg->add_attribute(out_msg, attr);
377 }
378 }
379
380 if (fatal_error)
381 {
382 state->set_recommendation(state,
383 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
384 TNC_IMV_EVALUATION_RESULT_ERROR);
385 assessment = TRUE;
386 }
387
388 /* If all Installed Packages attributes were received, go to assessment */
389 if (!assessment &&
390 !os_state->get_package_request(os_state) &&
391 !os_state->get_angel_count(os_state))
392 {
393 int count, count_update, count_blacklist, count_ok;
394
395 os_state->get_count(os_state, &count, &count_update, &count_blacklist,
396 &count_ok);
397 DBG1(DBG_IMV, "processed %d packages: %d not updated, %d blacklisted, "
398 "%d ok, %d not found", count, count_update, count_blacklist,
399 count_ok, count - count_update - count_blacklist - count_ok);
400
401 if (count_update || count_blacklist)
402 {
403 state->set_recommendation(state,
404 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
405 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR);
406 }
407 else
408 {
409 state->set_recommendation(state,
410 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
411 TNC_IMV_EVALUATION_RESULT_COMPLIANT);
412 }
413 assessment = TRUE;
414 }
415
416 if (assessment)
417 {
418 result = out_msg->send_assessment(out_msg);
419 out_msg->destroy(out_msg);
420 if (result != TNC_RESULT_SUCCESS)
421 {
422 return result;
423 }
424 return imv_os->provide_recommendation(imv_os, state);
425 }
426
427 /* send PA-TNC message with excl flag set */
428 result = out_msg->send(out_msg, TRUE);
429 out_msg->destroy(out_msg);
430
431 return result;
432 }
433
434 /**
435 * see section 3.8.4 of TCG TNC IF-IMV Specification 1.3
436 */
437 TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
438 TNC_ConnectionID connection_id,
439 TNC_BufferReference msg,
440 TNC_UInt32 msg_len,
441 TNC_MessageType msg_type)
442 {
443 imv_state_t *state;
444 imv_msg_t *in_msg;
445 TNC_Result result;
446
447 if (!imv_os)
448 {
449 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
450 return TNC_RESULT_NOT_INITIALIZED;
451 }
452 if (!imv_os->get_state(imv_os, connection_id, &state))
453 {
454 return TNC_RESULT_FATAL;
455 }
456 in_msg = imv_msg_create_from_data(imv_os, state, connection_id, msg_type,
457 chunk_create(msg, msg_len));
458 result = receive_message(state, in_msg);
459 in_msg->destroy(in_msg);
460
461 return result;
462 }
463
464 /**
465 * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3
466 */
467 TNC_Result TNC_IMV_ReceiveMessageLong(TNC_IMVID imv_id,
468 TNC_ConnectionID connection_id,
469 TNC_UInt32 msg_flags,
470 TNC_BufferReference msg,
471 TNC_UInt32 msg_len,
472 TNC_VendorID msg_vid,
473 TNC_MessageSubtype msg_subtype,
474 TNC_UInt32 src_imc_id,
475 TNC_UInt32 dst_imv_id)
476 {
477 imv_state_t *state;
478 imv_msg_t *in_msg;
479 TNC_Result result;
480
481 if (!imv_os)
482 {
483 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
484 return TNC_RESULT_NOT_INITIALIZED;
485 }
486 if (!imv_os->get_state(imv_os, connection_id, &state))
487 {
488 return TNC_RESULT_FATAL;
489 }
490 in_msg = imv_msg_create_from_long_data(imv_os, state, connection_id,
491 src_imc_id, dst_imv_id, msg_vid, msg_subtype,
492 chunk_create(msg, msg_len));
493 result =receive_message(state, in_msg);
494 in_msg->destroy(in_msg);
495
496 return result;
497 }
498
499 /**
500 * see section 3.8.7 of TCG TNC IF-IMV Specification 1.3
501 */
502 TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id,
503 TNC_ConnectionID connection_id)
504 {
505 imv_state_t *state;
506
507 if (!imv_os)
508 {
509 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
510 return TNC_RESULT_NOT_INITIALIZED;
511 }
512 if (!imv_os->get_state(imv_os, connection_id, &state))
513 {
514 return TNC_RESULT_FATAL;
515 }
516 return imv_os->provide_recommendation(imv_os, state);
517 }
518
519 /**
520 * see section 3.8.8 of TCG TNC IF-IMV Specification 1.3
521 */
522 TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id,
523 TNC_ConnectionID connection_id)
524 {
525 imv_state_t *state;
526 imv_os_state_t *os_state;
527 TNC_Result result = TNC_RESULT_SUCCESS;
528
529 if (!imv_os)
530 {
531 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
532 return TNC_RESULT_NOT_INITIALIZED;
533 }
534 if (!imv_os->get_state(imv_os, connection_id, &state))
535 {
536 return TNC_RESULT_FATAL;
537 }
538 os_state = (imv_os_state_t*)state;
539
540 if (os_state->get_info(os_state, NULL, NULL, NULL) == NULL)
541 {
542 imv_msg_t *out_msg;
543 pa_tnc_attr_t *attr;
544 ietf_attr_attr_request_t *attr_cast;
545
546 out_msg = imv_msg_create(imv_os, state, connection_id, imv_id,
547 TNC_IMCID_ANY, msg_types[0]);
548 attr = ietf_attr_attr_request_create(PEN_IETF,
549 IETF_ATTR_PRODUCT_INFORMATION);
550 attr_cast = (ietf_attr_attr_request_t*)attr;
551 attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_STRING_VERSION);
552 attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_NUMERIC_VERSION);
553 attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_OPERATIONAL_STATUS);
554 attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_FORWARDING_ENABLED);
555 attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED);
556 out_msg->add_attribute(out_msg, attr);
557
558 /* send PA-TNC message with excl flag not set */
559 result = out_msg->send(out_msg, FALSE);
560 out_msg->destroy(out_msg);
561 }
562
563 return result;
564 }
565
566 /**
567 * see section 3.8.9 of TCG TNC IF-IMV Specification 1.3
568 */
569 TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id)
570 {
571 if (!imv_os)
572 {
573 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
574 return TNC_RESULT_NOT_INITIALIZED;
575 }
576 DESTROY_IF(os_db);
577
578 imv_os->destroy(imv_os);
579 imv_os = NULL;
580
581 return TNC_RESULT_SUCCESS;
582 }
583
584 /**
585 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.3
586 */
587 TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id,
588 TNC_TNCS_BindFunctionPointer bind_function)
589 {
590 if (!imv_os)
591 {
592 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
593 return TNC_RESULT_NOT_INITIALIZED;
594 }
595 return imv_os->bind_functions(imv_os, bind_function);
596 }