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