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