Defined generic boolean PA-TNC attribute
[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 <ietf/ietf_attr.h>
23 #include <ietf/ietf_attr_attr_request.h>
24 #include <ietf/ietf_attr_installed_packages.h>
25 #include <ietf/ietf_attr_numeric_version.h>
26 #include <ietf/ietf_attr_op_status.h>
27 #include <ietf/ietf_attr_product_info.h>
28 #include <ietf/ietf_attr_string_version.h>
29 #include <ita/ita_attr.h>
30 #include <ita/ita_attr_get_settings.h>
31 #include <ita/ita_attr_settings.h>
32 #include <ita/ita_attr_device_id.h>
33
34 #include <tncif_pa_subtypes.h>
35
36 #include <pen/pen.h>
37 #include <utils/debug.h>
38
39 /* IMC definitions */
40
41 static const char imc_name[] = "OS";
42
43 static pen_type_t msg_types[] = {
44 { PEN_IETF, PA_SUBTYPE_IETF_OPERATING_SYSTEM }
45 };
46
47 static imc_agent_t *imc_os;
48 static imc_os_info_t *os;
49
50 /**
51 * see section 3.8.1 of TCG TNC IF-IMC Specification 1.3
52 */
53 TNC_Result TNC_IMC_API TNC_IMC_Initialize(TNC_IMCID imc_id,
54 TNC_Version min_version,
55 TNC_Version max_version,
56 TNC_Version *actual_version)
57 {
58 if (imc_os)
59 {
60 DBG1(DBG_IMC, "IMC \"%s\" has already been initialized", imc_name);
61 return TNC_RESULT_ALREADY_INITIALIZED;
62 }
63 imc_os = imc_agent_create(imc_name, msg_types, countof(msg_types),
64 imc_id, actual_version);
65 if (!imc_os)
66 {
67 return TNC_RESULT_FATAL;
68 }
69
70 os = imc_os_info_create();
71 if (!os)
72 {
73 imc_os->destroy(imc_os);
74 imc_os = NULL;
75
76 return TNC_RESULT_FATAL;
77 }
78
79 if (min_version > TNC_IFIMC_VERSION_1 || max_version < TNC_IFIMC_VERSION_1)
80 {
81 DBG1(DBG_IMC, "no common IF-IMC version");
82 return TNC_RESULT_NO_COMMON_VERSION;
83 }
84 return TNC_RESULT_SUCCESS;
85 }
86
87 /**
88 * see section 3.8.2 of TCG TNC IF-IMC Specification 1.3
89 */
90 TNC_Result TNC_IMC_API TNC_IMC_NotifyConnectionChange(TNC_IMCID imc_id,
91 TNC_ConnectionID connection_id, TNC_ConnectionState new_state)
92 {
93 imc_state_t *state;
94
95 if (!imc_os)
96 {
97 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
98 return TNC_RESULT_NOT_INITIALIZED;
99 }
100 switch (new_state)
101 {
102 case TNC_CONNECTION_STATE_CREATE:
103 state = imc_os_state_create(connection_id);
104 return imc_os->create_state(imc_os, state);
105 case TNC_CONNECTION_STATE_HANDSHAKE:
106 if (imc_os->change_state(imc_os, connection_id, new_state,
107 &state) != TNC_RESULT_SUCCESS)
108 {
109 return TNC_RESULT_FATAL;
110 }
111 state->set_result(state, imc_id,
112 TNC_IMV_EVALUATION_RESULT_DONT_KNOW);
113 return TNC_RESULT_SUCCESS;
114 case TNC_CONNECTION_STATE_DELETE:
115 return imc_os->delete_state(imc_os, connection_id);
116 default:
117 return imc_os->change_state(imc_os, connection_id,
118 new_state, NULL);
119 }
120 }
121
122 /**
123 * Add IETF Product Information attribute to the send queue
124 */
125 static void add_product_info(imc_msg_t *msg)
126 {
127 pa_tnc_attr_t *attr;
128 os_type_t os_type;
129 pen_t vendor_id = PEN_IETF;
130 int i;
131
132 typedef struct vendor_pen_t {
133 os_type_t os_type;
134 pen_t pen;
135 } vendor_pen_t;
136
137 vendor_pen_t vendor_pens[] = {
138 { OS_TYPE_DEBIAN, PEN_DEBIAN },
139 { OS_TYPE_UBUNTU, PEN_CANONICAL },
140 { OS_TYPE_FEDORA, PEN_FEDORA },
141 { OS_TYPE_REDHAT, PEN_REDHAT },
142 { OS_TYPE_ANDROID, PEN_GOOGLE }
143 };
144
145 os_type = os->get_type(os);
146 for (i = 0; i < countof(vendor_pens); i++)
147 {
148 if (os_type == vendor_pens[i].os_type)
149 {
150 vendor_id = vendor_pens[i].pen;
151 break;
152 }
153 }
154 attr = ietf_attr_product_info_create(vendor_id, 0, os->get_name(os));
155 msg->add_attribute(msg, attr);
156 }
157
158 /**
159 * Add IETF Numeric Version attribute to the send queue
160 */
161 static void add_numeric_version(imc_msg_t *msg)
162 {
163 pa_tnc_attr_t *attr;
164 u_int32_t major, minor;
165
166 os->get_numeric_version(os, &major, &minor);
167 DBG1(DBG_IMC, "operating system numeric version is %d.%d",
168 major, minor);
169
170 attr = ietf_attr_numeric_version_create(major, minor, 0, 0, 0);
171 msg->add_attribute(msg, attr);
172 }
173
174 /**
175 * Add IETF String Version attribute to the send queue
176 */
177 static void add_string_version(imc_msg_t *msg)
178 {
179 pa_tnc_attr_t *attr;
180
181 attr = ietf_attr_string_version_create(os->get_version(os),
182 chunk_empty, chunk_empty);
183 msg->add_attribute(msg, attr);
184 }
185
186 /**
187 * Add IETF Operational Status attribute to the send queue
188 */
189 static void add_op_status(imc_msg_t *msg)
190 {
191 pa_tnc_attr_t *attr;
192 time_t uptime, last_boot;
193
194 uptime = os->get_uptime(os);
195 last_boot = uptime ? time(NULL) - uptime : UNDEFINED_TIME;
196 if (last_boot != UNDEFINED_TIME)
197 {
198 DBG1(DBG_IMC, "last boot: %T, %u s ago", &last_boot, TRUE, uptime);
199 }
200 attr = ietf_attr_op_status_create(OP_STATUS_OPERATIONAL,
201 OP_RESULT_SUCCESSFUL, last_boot);
202 msg->add_attribute(msg, attr);
203 }
204
205 /**
206 * Add IETF Forwarding Enabled attribute to the send queue
207 */
208 static void add_fwd_enabled(imc_msg_t *msg)
209 {
210 pa_tnc_attr_t *attr;
211 os_fwd_status_t fwd_status;
212
213 fwd_status = os->get_fwd_status(os);
214 DBG1(DBG_IMC, "IPv4 forwarding is %N",
215 os_fwd_status_names, fwd_status);
216 attr = generic_attr_bool_create(fwd_status, pen_type_create(PEN_IETF,
217 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
228 DBG1(DBG_IMC, "factory default password is disabled");
229 attr = generic_attr_bool_create(FALSE, pen_type_create(PEN_IETF,
230 IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED));
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;
345 ietf_attr_installed_packages_t *attr_cast;
346 enumerator_t *enumerator;
347 chunk_t name, version;
348
349 enumerator = os->create_package_enumerator(os);
350 if (!enumerator)
351 {
352 return;
353 }
354 attr = ietf_attr_installed_packages_create();
355
356 while (enumerator->enumerate(enumerator, &name, &version))
357 {
358 DBG2(DBG_IMC, "package '%.*s' (%.*s)",
359 name.len, name.ptr, version.len, version.ptr);
360 attr_cast = (ietf_attr_installed_packages_t*)attr;
361 attr_cast->add(attr_cast, name, version);
362 }
363 enumerator->destroy(enumerator);
364
365 msg->add_attribute(msg, attr);
366 }
367
368 /**
369 * Add ITA Settings attribute to the send queue
370 */
371 static void add_settings(enumerator_t *enumerator, imc_msg_t *msg)
372 {
373 pa_tnc_attr_t *attr = NULL;
374 ita_attr_settings_t *attr_cast;
375 chunk_t value;
376 char *name;
377 bool first = TRUE;
378
379 while (enumerator->enumerate(enumerator, &name))
380 {
381 DBG1(DBG_IMC, "setting '%s'", name);
382
383 value = os->get_setting(os, name);
384 if (!value.ptr)
385 {
386 continue;
387 }
388 if (first)
389 {
390 attr = ita_attr_settings_create();
391 first = FALSE;
392 }
393 attr_cast = (ita_attr_settings_t*)attr;
394 attr_cast->add(attr_cast, name, value);
395 chunk_free(&value);
396 }
397
398 if (attr)
399 {
400 msg->add_attribute(msg, attr);
401 }
402 }
403
404 /**
405 * see section 3.8.3 of TCG TNC IF-IMC Specification 1.3
406 */
407 TNC_Result TNC_IMC_API TNC_IMC_BeginHandshake(TNC_IMCID imc_id,
408 TNC_ConnectionID connection_id)
409 {
410 imc_state_t *state;
411 imc_msg_t *out_msg;
412 TNC_Result result = TNC_RESULT_SUCCESS;
413
414 if (!imc_os)
415 {
416 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
417 return TNC_RESULT_NOT_INITIALIZED;
418 }
419 if (!imc_os->get_state(imc_os, connection_id, &state))
420 {
421 return TNC_RESULT_FATAL;
422 }
423 if (lib->settings->get_bool(lib->settings,
424 "%s.plugins.imc-os.push_info", TRUE, lib->ns))
425 {
426 out_msg = imc_msg_create(imc_os, state, connection_id, imc_id,
427 TNC_IMVID_ANY, msg_types[0]);
428 add_product_info(out_msg);
429 add_string_version(out_msg);
430 add_numeric_version(out_msg);
431 add_op_status(out_msg);
432 add_fwd_enabled(out_msg);
433 add_default_pwd_enabled(out_msg);
434 add_device_id(out_msg);
435
436 /* send PA-TNC message with the excl flag not set */
437 result = out_msg->send(out_msg, FALSE);
438 out_msg->destroy(out_msg);
439 }
440
441 return result;
442 }
443
444 static TNC_Result receive_message(imc_state_t *state, imc_msg_t *in_msg)
445 {
446 imc_msg_t *out_msg;
447 enumerator_t *enumerator;
448 pa_tnc_attr_t *attr;
449 pen_type_t type;
450 TNC_Result result;
451 bool fatal_error = FALSE;
452
453 /* generate an outgoing PA-TNC message - we might need it */
454 out_msg = imc_msg_create_as_reply(in_msg);
455
456 /* parse received PA-TNC message and handle local and remote errors */
457 result = in_msg->receive(in_msg, out_msg, &fatal_error);
458 if (result != TNC_RESULT_SUCCESS)
459 {
460 out_msg->destroy(out_msg);
461 return result;
462 }
463
464 /* analyze PA-TNC attributes */
465 enumerator = in_msg->create_attribute_enumerator(in_msg);
466 while (enumerator->enumerate(enumerator, &attr))
467 {
468 type = attr->get_type(attr);
469
470 if (type.vendor_id == PEN_IETF)
471 {
472 if (type.type == IETF_ATTR_ATTRIBUTE_REQUEST)
473 {
474 ietf_attr_attr_request_t *attr_cast;
475 pen_type_t *entry;
476 enumerator_t *e;
477
478 attr_cast = (ietf_attr_attr_request_t*)attr;
479
480 e = attr_cast->create_enumerator(attr_cast);
481 while (e->enumerate(e, &entry))
482 {
483 if (entry->vendor_id == PEN_IETF)
484 {
485 switch (entry->type)
486 {
487 case IETF_ATTR_PRODUCT_INFORMATION:
488 add_product_info(out_msg);
489 break;
490 case IETF_ATTR_STRING_VERSION:
491 add_string_version(out_msg);
492 break;
493 case IETF_ATTR_NUMERIC_VERSION:
494 add_numeric_version(out_msg);
495 break;
496 case IETF_ATTR_OPERATIONAL_STATUS:
497 add_op_status(out_msg);
498 break;
499 case IETF_ATTR_FORWARDING_ENABLED:
500 add_fwd_enabled(out_msg);
501 break;
502 case IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED:
503 add_default_pwd_enabled(out_msg);
504 break;
505 case IETF_ATTR_INSTALLED_PACKAGES:
506 add_installed_packages(state, out_msg);
507 break;
508 default:
509 break;
510 }
511 }
512 else if (entry->vendor_id == PEN_ITA)
513 {
514 switch (entry->type)
515 {
516 case ITA_ATTR_DEVICE_ID:
517 add_device_id(out_msg);
518 break;
519 default:
520 break;
521 }
522 }
523 }
524 e->destroy(e);
525 }
526 }
527 else if (type.vendor_id == PEN_ITA && type.type == ITA_ATTR_GET_SETTINGS)
528 {
529 ita_attr_get_settings_t *attr_cast;
530 enumerator_t *e;
531
532 attr_cast = (ita_attr_get_settings_t*)attr;
533
534 e = attr_cast->create_enumerator(attr_cast);
535 add_settings(e, out_msg);
536 e->destroy(e);
537 }
538 }
539 enumerator->destroy(enumerator);
540
541 if (fatal_error)
542 {
543 result = TNC_RESULT_FATAL;
544 }
545 else
546 {
547 /* send PA-TNC message with the EXCL flag set */
548 result = out_msg->send(out_msg, TRUE);
549 }
550 out_msg->destroy(out_msg);
551
552 return result;
553 }
554
555 /**
556 * see section 3.8.4 of TCG TNC IF-IMC Specification 1.3
557
558 */
559 TNC_Result TNC_IMC_API TNC_IMC_ReceiveMessage(TNC_IMCID imc_id,
560 TNC_ConnectionID connection_id,
561 TNC_BufferReference msg,
562 TNC_UInt32 msg_len,
563 TNC_MessageType msg_type)
564 {
565 imc_state_t *state;
566 imc_msg_t *in_msg;
567 TNC_Result result;
568
569 if (!imc_os)
570 {
571 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
572 return TNC_RESULT_NOT_INITIALIZED;
573 }
574 if (!imc_os->get_state(imc_os, connection_id, &state))
575 {
576 return TNC_RESULT_FATAL;
577 }
578 in_msg = imc_msg_create_from_data(imc_os, state, connection_id, msg_type,
579 chunk_create(msg, msg_len));
580 result = receive_message(state, in_msg);
581 in_msg->destroy(in_msg);
582
583 return result;
584 }
585
586 /**
587 * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3
588 */
589 TNC_Result TNC_IMC_API TNC_IMC_ReceiveMessageLong(TNC_IMCID imc_id,
590 TNC_ConnectionID connection_id,
591 TNC_UInt32 msg_flags,
592 TNC_BufferReference msg,
593 TNC_UInt32 msg_len,
594 TNC_VendorID msg_vid,
595 TNC_MessageSubtype msg_subtype,
596 TNC_UInt32 src_imv_id,
597 TNC_UInt32 dst_imc_id)
598 {
599 imc_state_t *state;
600 imc_msg_t *in_msg;
601 TNC_Result result;
602
603 if (!imc_os)
604 {
605 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
606 return TNC_RESULT_NOT_INITIALIZED;
607 }
608 if (!imc_os->get_state(imc_os, connection_id, &state))
609 {
610 return TNC_RESULT_FATAL;
611 }
612 in_msg = imc_msg_create_from_long_data(imc_os, state, connection_id,
613 src_imv_id, dst_imc_id,msg_vid, msg_subtype,
614 chunk_create(msg, msg_len));
615 result =receive_message(state, in_msg);
616 in_msg->destroy(in_msg);
617
618 return result;
619 }
620
621 /**
622 * see section 3.8.7 of TCG TNC IF-IMC Specification 1.3
623 */
624 TNC_Result TNC_IMC_API TNC_IMC_BatchEnding(TNC_IMCID imc_id,
625 TNC_ConnectionID connection_id)
626 {
627 if (!imc_os)
628 {
629 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
630 return TNC_RESULT_NOT_INITIALIZED;
631 }
632 return TNC_RESULT_SUCCESS;
633 }
634
635 /**
636 * see section 3.8.8 of TCG TNC IF-IMC Specification 1.3
637 */
638 TNC_Result TNC_IMC_API TNC_IMC_Terminate(TNC_IMCID imc_id)
639 {
640 if (!imc_os)
641 {
642 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
643 return TNC_RESULT_NOT_INITIALIZED;
644 }
645 imc_os->destroy(imc_os);
646 imc_os = NULL;
647
648 os->destroy(os);
649 os = NULL;
650
651 return TNC_RESULT_SUCCESS;
652 }
653
654 /**
655 * see section 4.2.8.1 of TCG TNC IF-IMC Specification 1.3
656 */
657 TNC_Result TNC_IMC_API TNC_IMC_ProvideBindFunction(TNC_IMCID imc_id,
658 TNC_TNCC_BindFunctionPointer bind_function)
659 {
660 if (!imc_os)
661 {
662 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
663 return TNC_RESULT_NOT_INITIALIZED;
664 }
665 return imc_os->bind_functions(imc_os, bind_function);
666 }