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