Separated IMV session management from IMV policy database
[strongswan.git] / src / libimcv / plugins / imc_os / imc_os.c
1 /*
2 * Copyright (C) 2011-2014 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 "imc_os_state.h"
17
18 #include <imc/imc_agent.h>
19 #include <imc/imc_msg.h>
20 #include <imc/imc_os_info.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_product_info.h>
29 #include <ietf/ietf_attr_string_version.h>
30 #include <ita/ita_attr.h>
31 #include <ita/ita_attr_get_settings.h>
32 #include <ita/ita_attr_settings.h>
33 #include <ita/ita_attr_angel.h>
34 #include <ita/ita_attr_device_id.h>
35
36 #include <tncif_pa_subtypes.h>
37
38 #include <pen/pen.h>
39 #include <utils/debug.h>
40
41 /* IMC definitions */
42
43 static const char imc_name[] = "OS";
44
45 static pen_type_t msg_types[] = {
46 { PEN_IETF, PA_SUBTYPE_IETF_OPERATING_SYSTEM }
47 };
48
49 static imc_agent_t *imc_os;
50 static imc_os_info_t *os;
51
52 /**
53 * see section 3.8.1 of TCG TNC IF-IMC Specification 1.3
54 */
55 TNC_Result TNC_IMC_Initialize(TNC_IMCID imc_id,
56 TNC_Version min_version,
57 TNC_Version max_version,
58 TNC_Version *actual_version)
59 {
60 if (imc_os)
61 {
62 DBG1(DBG_IMC, "IMC \"%s\" has already been initialized", imc_name);
63 return TNC_RESULT_ALREADY_INITIALIZED;
64 }
65 imc_os = imc_agent_create(imc_name, msg_types, countof(msg_types),
66 imc_id, actual_version);
67 if (!imc_os)
68 {
69 return TNC_RESULT_FATAL;
70 }
71
72 os = imc_os_info_create();
73 if (!os)
74 {
75 imc_os->destroy(imc_os);
76 imc_os = NULL;
77
78 return TNC_RESULT_FATAL;
79 }
80
81 if (min_version > TNC_IFIMC_VERSION_1 || max_version < TNC_IFIMC_VERSION_1)
82 {
83 DBG1(DBG_IMC, "no common IF-IMC version");
84 return TNC_RESULT_NO_COMMON_VERSION;
85 }
86 return TNC_RESULT_SUCCESS;
87 }
88
89 /**
90 * see section 3.8.2 of TCG TNC IF-IMC Specification 1.3
91 */
92 TNC_Result TNC_IMC_NotifyConnectionChange(TNC_IMCID imc_id,
93 TNC_ConnectionID connection_id,
94 TNC_ConnectionState new_state)
95 {
96 imc_state_t *state;
97
98 if (!imc_os)
99 {
100 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
101 return TNC_RESULT_NOT_INITIALIZED;
102 }
103 switch (new_state)
104 {
105 case TNC_CONNECTION_STATE_CREATE:
106 state = imc_os_state_create(connection_id);
107 return imc_os->create_state(imc_os, state);
108 case TNC_CONNECTION_STATE_HANDSHAKE:
109 if (imc_os->change_state(imc_os, connection_id, new_state,
110 &state) != TNC_RESULT_SUCCESS)
111 {
112 return TNC_RESULT_FATAL;
113 }
114 state->set_result(state, imc_id,
115 TNC_IMV_EVALUATION_RESULT_DONT_KNOW);
116 return TNC_RESULT_SUCCESS;
117 case TNC_CONNECTION_STATE_DELETE:
118 return imc_os->delete_state(imc_os, connection_id);
119 default:
120 return imc_os->change_state(imc_os, connection_id,
121 new_state, NULL);
122 }
123 }
124
125 /**
126 * Add IETF Product Information attribute to the send queue
127 */
128 static void add_product_info(imc_msg_t *msg)
129 {
130 pa_tnc_attr_t *attr;
131 os_type_t os_type;
132 pen_t vendor_id = PEN_IETF;
133 int i;
134
135 typedef struct vendor_pen_t {
136 os_type_t os_type;
137 pen_t pen;
138 } vendor_pen_t;
139
140 vendor_pen_t vendor_pens[] = {
141 { OS_TYPE_DEBIAN, PEN_DEBIAN },
142 { OS_TYPE_UBUNTU, PEN_CANONICAL },
143 { OS_TYPE_FEDORA, PEN_FEDORA },
144 { OS_TYPE_REDHAT, PEN_REDHAT },
145 { OS_TYPE_ANDROID, PEN_GOOGLE }
146 };
147
148 os_type = os->get_type(os);
149 for (i = 0; i < countof(vendor_pens); i++)
150 {
151 if (os_type == vendor_pens[i].os_type)
152 {
153 vendor_id = vendor_pens[i].pen;
154 break;
155 }
156 }
157 attr = ietf_attr_product_info_create(vendor_id, 0, os->get_name(os));
158 msg->add_attribute(msg, attr);
159 }
160
161 /**
162 * Add IETF Numeric Version attribute to the send queue
163 */
164 static void add_numeric_version(imc_msg_t *msg)
165 {
166 pa_tnc_attr_t *attr;
167 u_int32_t major, minor;
168
169 os->get_numeric_version(os, &major, &minor);
170 DBG1(DBG_IMC, "operating system numeric version is %d.%d",
171 major, minor);
172
173 attr = ietf_attr_numeric_version_create(major, minor, 0, 0, 0);
174 msg->add_attribute(msg, attr);
175 }
176
177 /**
178 * Add IETF String Version attribute to the send queue
179 */
180 static void add_string_version(imc_msg_t *msg)
181 {
182 pa_tnc_attr_t *attr;
183
184 attr = ietf_attr_string_version_create(os->get_version(os),
185 chunk_empty, chunk_empty);
186 msg->add_attribute(msg, attr);
187 }
188
189 /**
190 * Add IETF Operational Status attribute to the send queue
191 */
192 static void add_op_status(imc_msg_t *msg)
193 {
194 pa_tnc_attr_t *attr;
195 time_t uptime, last_boot;
196
197 uptime = os->get_uptime(os);
198 last_boot = uptime ? time(NULL) - uptime : UNDEFINED_TIME;
199 if (last_boot != UNDEFINED_TIME)
200 {
201 DBG1(DBG_IMC, "last boot: %T, %u s ago", &last_boot, TRUE, uptime);
202 }
203 attr = ietf_attr_op_status_create(OP_STATUS_OPERATIONAL,
204 OP_RESULT_SUCCESSFUL, last_boot);
205 msg->add_attribute(msg, attr);
206 }
207
208 /**
209 * Add IETF Forwarding Enabled attribute to the send queue
210 */
211 static void add_fwd_enabled(imc_msg_t *msg)
212 {
213 pa_tnc_attr_t *attr;
214 os_fwd_status_t fwd_status;
215
216 fwd_status = os->get_fwd_status(os);
217 DBG1(DBG_IMC, "IPv4 forwarding is %N",
218 os_fwd_status_names, fwd_status);
219 attr = ietf_attr_fwd_enabled_create(fwd_status);
220 msg->add_attribute(msg, attr);
221 }
222
223 /**
224 * Add IETF Factory Default Password Enabled attribute to the send queue
225 */
226 static void add_default_pwd_enabled(imc_msg_t *msg)
227 {
228 pa_tnc_attr_t *attr;
229
230 DBG1(DBG_IMC, "factory default password is disabled");
231 attr = ietf_attr_default_pwd_enabled_create(FALSE);
232 msg->add_attribute(msg, attr);
233 }
234
235 /**
236 * Add ITA Device ID attribute to the send queue
237 */
238 static void add_device_id(imc_msg_t *msg)
239 {
240 pa_tnc_attr_t *attr;
241 chunk_t value = chunk_empty, keyid;
242 char *name, *device_id, *cert_path;
243 certificate_t *cert = NULL;
244 public_key_t *pubkey;
245
246 /* Get the device ID as a character string */
247 device_id = lib->settings->get_str(lib->settings,
248 "%s.plugins.imc-os.device_id", NULL, lib->ns);
249 if (device_id)
250 {
251 value = chunk_clone(chunk_from_str(device_id));
252 }
253
254 if (value.len == 0)
255 {
256 /* Derive the device ID from a raw public key */
257 cert_path = lib->settings->get_str(lib->settings,
258 "%s.plugins.imc-os.device_pubkey", NULL, lib->ns);
259 if (cert_path)
260 {
261 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
262 CERT_TRUSTED_PUBKEY, BUILD_FROM_FILE,
263 cert_path, BUILD_END);
264 if (cert)
265 {
266 DBG2(DBG_IMC, "loaded device public key from '%s'", cert_path);
267 }
268 else
269 {
270 DBG1(DBG_IMC, "loading device public key from '%s' failed",
271 cert_path);
272 }
273 }
274
275 if (!cert)
276 {
277 /* Derive the device ID from the public key contained in a certificate */
278 cert_path = lib->settings->get_str(lib->settings,
279 "%s.plugins.imc-os.device_cert", NULL, lib->ns);
280 if (cert_path)
281 {
282 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
283 CERT_X509, BUILD_FROM_FILE,
284 cert_path, BUILD_END);
285 if (cert)
286 {
287 DBG2(DBG_IMC, "loaded device certificate from '%s'", cert_path);
288 }
289 else
290 {
291 DBG1(DBG_IMC, "loading device certificate from '%s' failed",
292 cert_path);
293 }
294 }
295 }
296
297 /* Compute the SHA-1 keyid of the retrieved device public key */
298 if (cert)
299 {
300 pubkey = cert->get_public_key(cert);
301 if (pubkey)
302 {
303 if (pubkey->get_fingerprint(pubkey, KEYID_PUBKEY_INFO_SHA1,
304 &keyid))
305 {
306 value = chunk_to_hex(keyid, NULL, FALSE);
307 }
308 pubkey->destroy(pubkey);
309 }
310 cert->destroy(cert);
311 }
312 }
313
314 if (value.len == 0)
315 {
316 /* Derive the device ID from some unique OS settings */
317 name = os->get_type(os) == OS_TYPE_ANDROID ?
318 "android_id" : "/var/lib/dbus/machine-id";
319 value = os->get_setting(os, name);
320
321 /* Trim trailing newline character */
322 if (value.len > 0 && value.ptr[value.len - 1] == '\n')
323 {
324 value.len--;
325 }
326 }
327
328 if (value.len == 0)
329 {
330 DBG1(DBG_IMC, "no device ID available");
331 return;
332 }
333
334 DBG1(DBG_IMC, "device ID is %.*s", value.len, value.ptr);
335 attr = ita_attr_device_id_create(value);
336 msg->add_attribute(msg, attr);
337 free(value.ptr);
338 }
339
340 /**
341 * Add an IETF Installed Packages attribute to the send queue
342 */
343 static void add_installed_packages(imc_state_t *state, imc_msg_t *msg)
344 {
345 pa_tnc_attr_t *attr = NULL, *attr_angel;
346 ietf_attr_installed_packages_t *attr_cast;
347 enumerator_t *enumerator;
348 chunk_t name, version;
349 size_t max_attr_size, attr_size, entry_size;
350 bool first = TRUE;
351
352 /**
353 * Compute the maximum IETF Installed Packages attribute size
354 * leaving space for an additional ITA Angel attribute
355 */
356 max_attr_size = state->get_max_msg_len(state) - 8 - 12;
357
358 /* At least one IETF Installed Packages attribute is sent */
359 attr = ietf_attr_installed_packages_create();
360 attr_size = 12 + 4;
361
362 enumerator = os->create_package_enumerator(os);
363 if (enumerator)
364 {
365 while (enumerator->enumerate(enumerator, &name, &version))
366 {
367 DBG2(DBG_IMC, "package '%.*s' (%.*s)",
368 name.len, name.ptr, version.len, version.ptr);
369
370 entry_size = 2 + name.len + version.len;
371 if (attr_size + entry_size > max_attr_size)
372 {
373 if (first)
374 {
375 /**
376 * Send an ITA Start Angel attribute to the IMV signalling
377 * that multiple ITA Installed Package attributes follow.
378 */
379 attr_angel = ita_attr_angel_create(TRUE);
380 msg->add_attribute(msg, attr_angel);
381 first = FALSE;
382 }
383 msg->add_attribute(msg, attr);
384
385 /* create the next IETF Installed Packages attribute */
386 attr = ietf_attr_installed_packages_create();
387 attr_size = 12 + 4;
388 }
389 attr_cast = (ietf_attr_installed_packages_t*)attr;
390 attr_cast->add(attr_cast, name, version);
391 attr_size += entry_size;
392 }
393 enumerator->destroy(enumerator);
394 }
395 msg->add_attribute(msg, attr);
396
397 if (!first)
398 {
399 /**
400 * If we sent an ITA Start Angel attribute in the first place,
401 * terminate by appending a matching ITA Stop Angel attribute.
402 */
403 attr_angel = ita_attr_angel_create(FALSE);
404 msg->add_attribute(msg, attr_angel);
405 }
406 }
407
408 /**
409 * Add ITA Settings attribute to the send queue
410 */
411 static void add_settings(enumerator_t *enumerator, imc_msg_t *msg)
412 {
413 pa_tnc_attr_t *attr = NULL;
414 ita_attr_settings_t *attr_cast;
415 chunk_t value;
416 char *name;
417 bool first = TRUE;
418
419 while (enumerator->enumerate(enumerator, &name))
420 {
421 DBG1(DBG_IMC, "setting '%s'", name);
422
423 value = os->get_setting(os, name);
424 if (!value.ptr)
425 {
426 continue;
427 }
428 if (first)
429 {
430 attr = ita_attr_settings_create();
431 first = FALSE;
432 }
433 attr_cast = (ita_attr_settings_t*)attr;
434 attr_cast->add(attr_cast, name, value);
435 chunk_free(&value);
436 }
437
438 if (attr)
439 {
440 msg->add_attribute(msg, attr);
441 }
442 }
443
444 /**
445 * see section 3.8.3 of TCG TNC IF-IMC Specification 1.3
446 */
447 TNC_Result TNC_IMC_BeginHandshake(TNC_IMCID imc_id,
448 TNC_ConnectionID connection_id)
449 {
450 imc_state_t *state;
451 imc_msg_t *out_msg;
452 TNC_Result result = TNC_RESULT_SUCCESS;
453
454 if (!imc_os)
455 {
456 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
457 return TNC_RESULT_NOT_INITIALIZED;
458 }
459 if (!imc_os->get_state(imc_os, connection_id, &state))
460 {
461 return TNC_RESULT_FATAL;
462 }
463 if (lib->settings->get_bool(lib->settings,
464 "%s.plugins.imc-os.push_info", TRUE, lib->ns))
465 {
466 out_msg = imc_msg_create(imc_os, state, connection_id, imc_id,
467 TNC_IMVID_ANY, msg_types[0]);
468 add_product_info(out_msg);
469 add_string_version(out_msg);
470 add_numeric_version(out_msg);
471 add_op_status(out_msg);
472 add_fwd_enabled(out_msg);
473 add_default_pwd_enabled(out_msg);
474 add_device_id(out_msg);
475
476 /* send PA-TNC message with the excl flag not set */
477 result = out_msg->send(out_msg, FALSE);
478 out_msg->destroy(out_msg);
479 }
480
481 return result;
482 }
483
484 static TNC_Result receive_message(imc_state_t *state, imc_msg_t *in_msg)
485 {
486 imc_msg_t *out_msg;
487 enumerator_t *enumerator;
488 pa_tnc_attr_t *attr;
489 pen_type_t type;
490 TNC_Result result;
491 bool fatal_error = FALSE;
492
493 /* parse received PA-TNC message and handle local and remote errors */
494 result = in_msg->receive(in_msg, &fatal_error);
495 if (result != TNC_RESULT_SUCCESS)
496 {
497 return result;
498 }
499 out_msg = imc_msg_create_as_reply(in_msg);
500
501 /* analyze PA-TNC attributes */
502 enumerator = in_msg->create_attribute_enumerator(in_msg);
503 while (enumerator->enumerate(enumerator, &attr))
504 {
505 type = attr->get_type(attr);
506
507 if (type.vendor_id == PEN_IETF)
508 {
509 if (type.type == IETF_ATTR_ATTRIBUTE_REQUEST)
510 {
511 ietf_attr_attr_request_t *attr_cast;
512 pen_type_t *entry;
513 enumerator_t *e;
514
515 attr_cast = (ietf_attr_attr_request_t*)attr;
516
517 e = attr_cast->create_enumerator(attr_cast);
518 while (e->enumerate(e, &entry))
519 {
520 if (entry->vendor_id == PEN_IETF)
521 {
522 switch (entry->type)
523 {
524 case IETF_ATTR_PRODUCT_INFORMATION:
525 add_product_info(out_msg);
526 break;
527 case IETF_ATTR_STRING_VERSION:
528 add_string_version(out_msg);
529 break;
530 case IETF_ATTR_NUMERIC_VERSION:
531 add_numeric_version(out_msg);
532 break;
533 case IETF_ATTR_OPERATIONAL_STATUS:
534 add_op_status(out_msg);
535 break;
536 case IETF_ATTR_FORWARDING_ENABLED:
537 add_fwd_enabled(out_msg);
538 break;
539 case IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED:
540 add_default_pwd_enabled(out_msg);
541 break;
542 case IETF_ATTR_INSTALLED_PACKAGES:
543 add_installed_packages(state, out_msg);
544 break;
545 default:
546 break;
547 }
548 }
549 else if (entry->vendor_id == PEN_ITA)
550 {
551 switch (entry->type)
552 {
553 case ITA_ATTR_DEVICE_ID:
554 add_device_id(out_msg);
555 break;
556 default:
557 break;
558 }
559 }
560 }
561 e->destroy(e);
562 }
563 }
564 else if (type.vendor_id == PEN_ITA && type.type == ITA_ATTR_GET_SETTINGS)
565 {
566 ita_attr_get_settings_t *attr_cast;
567 enumerator_t *e;
568
569 attr_cast = (ita_attr_get_settings_t*)attr;
570
571 e = attr_cast->create_enumerator(attr_cast);
572 add_settings(e, out_msg);
573 e->destroy(e);
574 }
575 }
576 enumerator->destroy(enumerator);
577
578 if (fatal_error)
579 {
580 result = TNC_RESULT_FATAL;
581 }
582 else
583 {
584 result = out_msg->send(out_msg, TRUE);
585 }
586 out_msg->destroy(out_msg);
587
588 return result;
589 }
590
591 /**
592 * see section 3.8.4 of TCG TNC IF-IMC Specification 1.3
593
594 */
595 TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id,
596 TNC_ConnectionID connection_id,
597 TNC_BufferReference msg,
598 TNC_UInt32 msg_len,
599 TNC_MessageType msg_type)
600 {
601 imc_state_t *state;
602 imc_msg_t *in_msg;
603 TNC_Result result;
604
605 if (!imc_os)
606 {
607 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
608 return TNC_RESULT_NOT_INITIALIZED;
609 }
610 if (!imc_os->get_state(imc_os, connection_id, &state))
611 {
612 return TNC_RESULT_FATAL;
613 }
614 in_msg = imc_msg_create_from_data(imc_os, state, connection_id, msg_type,
615 chunk_create(msg, msg_len));
616 result = receive_message(state, in_msg);
617 in_msg->destroy(in_msg);
618
619 return result;
620 }
621
622 /**
623 * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3
624 */
625 TNC_Result TNC_IMC_ReceiveMessageLong(TNC_IMCID imc_id,
626 TNC_ConnectionID connection_id,
627 TNC_UInt32 msg_flags,
628 TNC_BufferReference msg,
629 TNC_UInt32 msg_len,
630 TNC_VendorID msg_vid,
631 TNC_MessageSubtype msg_subtype,
632 TNC_UInt32 src_imv_id,
633 TNC_UInt32 dst_imc_id)
634 {
635 imc_state_t *state;
636 imc_msg_t *in_msg;
637 TNC_Result result;
638
639 if (!imc_os)
640 {
641 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
642 return TNC_RESULT_NOT_INITIALIZED;
643 }
644 if (!imc_os->get_state(imc_os, connection_id, &state))
645 {
646 return TNC_RESULT_FATAL;
647 }
648 in_msg = imc_msg_create_from_long_data(imc_os, state, connection_id,
649 src_imv_id, dst_imc_id,msg_vid, msg_subtype,
650 chunk_create(msg, msg_len));
651 result =receive_message(state, in_msg);
652 in_msg->destroy(in_msg);
653
654 return result;
655 }
656
657 /**
658 * see section 3.8.7 of TCG TNC IF-IMC Specification 1.3
659 */
660 TNC_Result TNC_IMC_BatchEnding(TNC_IMCID imc_id,
661 TNC_ConnectionID connection_id)
662 {
663 if (!imc_os)
664 {
665 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
666 return TNC_RESULT_NOT_INITIALIZED;
667 }
668 return TNC_RESULT_SUCCESS;
669 }
670
671 /**
672 * see section 3.8.8 of TCG TNC IF-IMC Specification 1.3
673 */
674 TNC_Result TNC_IMC_Terminate(TNC_IMCID imc_id)
675 {
676 if (!imc_os)
677 {
678 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
679 return TNC_RESULT_NOT_INITIALIZED;
680 }
681 imc_os->destroy(imc_os);
682 imc_os = NULL;
683
684 os->destroy(os);
685 os = NULL;
686
687 return TNC_RESULT_SUCCESS;
688 }
689
690 /**
691 * see section 4.2.8.1 of TCG TNC IF-IMC Specification 1.3
692 */
693 TNC_Result TNC_IMC_ProvideBindFunction(TNC_IMCID imc_id,
694 TNC_TNCC_BindFunctionPointer bind_function)
695 {
696 if (!imc_os)
697 {
698 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
699 return TNC_RESULT_NOT_INITIALIZED;
700 }
701 return imc_os->bind_functions(imc_os, bind_function);
702 }