2 * Copyright (C) 2011-2015 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
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>.
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
16 #include "imc_os_state.h"
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>
34 #include <tncif_pa_subtypes.h>
37 #include <utils/debug.h>
41 static const char imc_name
[] = "OS";
43 static pen_type_t msg_types
[] = {
44 { PEN_IETF
, PA_SUBTYPE_IETF_OPERATING_SYSTEM
}
47 static imc_agent_t
*imc_os
;
48 static imc_os_info_t
*os
;
51 * see section 3.8.1 of TCG TNC IF-IMC Specification 1.3
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
)
60 DBG1(DBG_IMC
, "IMC \"%s\" has already been initialized", imc_name
);
61 return TNC_RESULT_ALREADY_INITIALIZED
;
63 imc_os
= imc_agent_create(imc_name
, msg_types
, countof(msg_types
),
64 imc_id
, actual_version
);
67 return TNC_RESULT_FATAL
;
70 os
= imc_os_info_create();
73 imc_os
->destroy(imc_os
);
76 return TNC_RESULT_FATAL
;
79 if (min_version
> TNC_IFIMC_VERSION_1
|| max_version
< TNC_IFIMC_VERSION_1
)
81 DBG1(DBG_IMC
, "no common IF-IMC version");
82 return TNC_RESULT_NO_COMMON_VERSION
;
84 return TNC_RESULT_SUCCESS
;
88 * see section 3.8.2 of TCG TNC IF-IMC Specification 1.3
90 TNC_Result TNC_IMC_API
TNC_IMC_NotifyConnectionChange(TNC_IMCID imc_id
,
91 TNC_ConnectionID connection_id
, TNC_ConnectionState new_state
)
97 DBG1(DBG_IMC
, "IMC \"%s\" has not been initialized", imc_name
);
98 return TNC_RESULT_NOT_INITIALIZED
;
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
)
109 return TNC_RESULT_FATAL
;
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
);
117 return imc_os
->change_state(imc_os
, connection_id
,
123 * Add IETF Product Information attribute to the send queue
125 static void add_product_info(imc_msg_t
*msg
)
129 pen_t vendor_id
= PEN_IETF
;
132 typedef struct vendor_pen_t
{
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
}
145 os_type
= os
->get_type(os
);
146 for (i
= 0; i
< countof(vendor_pens
); i
++)
148 if (os_type
== vendor_pens
[i
].os_type
)
150 vendor_id
= vendor_pens
[i
].pen
;
154 attr
= ietf_attr_product_info_create(vendor_id
, 0, os
->get_name(os
));
155 msg
->add_attribute(msg
, attr
);
159 * Add IETF Numeric Version attribute to the send queue
161 static void add_numeric_version(imc_msg_t
*msg
)
164 u_int32_t major
, minor
;
166 os
->get_numeric_version(os
, &major
, &minor
);
167 DBG1(DBG_IMC
, "operating system numeric version is %d.%d",
170 attr
= ietf_attr_numeric_version_create(major
, minor
, 0, 0, 0);
171 msg
->add_attribute(msg
, attr
);
175 * Add IETF String Version attribute to the send queue
177 static void add_string_version(imc_msg_t
*msg
)
181 attr
= ietf_attr_string_version_create(os
->get_version(os
),
182 chunk_empty
, chunk_empty
);
183 msg
->add_attribute(msg
, attr
);
187 * Add IETF Operational Status attribute to the send queue
189 static void add_op_status(imc_msg_t
*msg
)
192 time_t uptime
, last_boot
;
194 uptime
= os
->get_uptime(os
);
195 last_boot
= uptime ?
time(NULL
) - uptime
: UNDEFINED_TIME
;
196 if (last_boot
!= UNDEFINED_TIME
)
198 DBG1(DBG_IMC
, "last boot: %T, %u s ago", &last_boot
, TRUE
, uptime
);
200 attr
= ietf_attr_op_status_create(OP_STATUS_OPERATIONAL
,
201 OP_RESULT_SUCCESSFUL
, last_boot
);
202 msg
->add_attribute(msg
, attr
);
206 * Add IETF Forwarding Enabled attribute to the send queue
208 static void add_fwd_enabled(imc_msg_t
*msg
)
211 os_fwd_status_t fwd_status
;
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
);
222 * Add IETF Factory Default Password Enabled attribute to the send queue
224 static void add_default_pwd_enabled(imc_msg_t
*msg
)
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
);
235 * Add ITA Device ID attribute to the send queue
237 static void add_device_id(imc_msg_t
*msg
)
240 chunk_t value
= chunk_empty
, keyid
;
241 char *name
, *device_id
, *cert_path
;
242 certificate_t
*cert
= NULL
;
243 public_key_t
*pubkey
;
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
);
250 value
= chunk_clone(chunk_from_str(device_id
));
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
);
260 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
,
261 CERT_TRUSTED_PUBKEY
, BUILD_FROM_FILE
,
262 cert_path
, BUILD_END
);
265 DBG2(DBG_IMC
, "loaded device public key from '%s'", cert_path
);
269 DBG1(DBG_IMC
, "loading device public key from '%s' failed",
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
);
281 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
,
282 CERT_X509
, BUILD_FROM_FILE
,
283 cert_path
, BUILD_END
);
286 DBG2(DBG_IMC
, "loaded device certificate from '%s'", cert_path
);
290 DBG1(DBG_IMC
, "loading device certificate from '%s' failed",
296 /* Compute the SHA-1 keyid of the retrieved device public key */
299 pubkey
= cert
->get_public_key(cert
);
302 if (pubkey
->get_fingerprint(pubkey
, KEYID_PUBKEY_INFO_SHA1
,
305 value
= chunk_to_hex(keyid
, NULL
, FALSE
);
307 pubkey
->destroy(pubkey
);
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
);
320 /* Trim trailing newline character */
321 if (value
.len
> 0 && value
.ptr
[value
.len
- 1] == '\n')
329 DBG1(DBG_IMC
, "no device ID available");
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
);
340 * Add an IETF Installed Packages attribute to the send queue
342 static void add_installed_packages(imc_state_t
*state
, imc_msg_t
*msg
)
345 ietf_attr_installed_packages_t
*attr_cast
;
346 enumerator_t
*enumerator
;
347 chunk_t name
, version
;
349 enumerator
= os
->create_package_enumerator(os
);
354 attr
= ietf_attr_installed_packages_create();
356 while (enumerator
->enumerate(enumerator
, &name
, &version
))
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
);
363 enumerator
->destroy(enumerator
);
365 msg
->add_attribute(msg
, attr
);
369 * Add ITA Settings attribute to the send queue
371 static void add_settings(enumerator_t
*enumerator
, imc_msg_t
*msg
)
373 pa_tnc_attr_t
*attr
= NULL
;
374 ita_attr_settings_t
*attr_cast
;
379 while (enumerator
->enumerate(enumerator
, &name
))
381 DBG1(DBG_IMC
, "setting '%s'", name
);
383 value
= os
->get_setting(os
, name
);
390 attr
= ita_attr_settings_create();
393 attr_cast
= (ita_attr_settings_t
*)attr
;
394 attr_cast
->add(attr_cast
, name
, value
);
400 msg
->add_attribute(msg
, attr
);
405 * see section 3.8.3 of TCG TNC IF-IMC Specification 1.3
407 TNC_Result TNC_IMC_API
TNC_IMC_BeginHandshake(TNC_IMCID imc_id
,
408 TNC_ConnectionID connection_id
)
412 TNC_Result result
= TNC_RESULT_SUCCESS
;
416 DBG1(DBG_IMC
, "IMC \"%s\" has not been initialized", imc_name
);
417 return TNC_RESULT_NOT_INITIALIZED
;
419 if (!imc_os
->get_state(imc_os
, connection_id
, &state
))
421 return TNC_RESULT_FATAL
;
423 if (lib
->settings
->get_bool(lib
->settings
,
424 "%s.plugins.imc-os.push_info", TRUE
, lib
->ns
))
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
);
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
);
444 static TNC_Result
receive_message(imc_state_t
*state
, imc_msg_t
*in_msg
)
447 enumerator_t
*enumerator
;
451 bool fatal_error
= FALSE
;
453 /* generate an outgoing PA-TNC message - we might need it */
454 out_msg
= imc_msg_create_as_reply(in_msg
);
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
)
460 out_msg
->destroy(out_msg
);
464 /* analyze PA-TNC attributes */
465 enumerator
= in_msg
->create_attribute_enumerator(in_msg
);
466 while (enumerator
->enumerate(enumerator
, &attr
))
468 type
= attr
->get_type(attr
);
470 if (type
.vendor_id
== PEN_IETF
)
472 if (type
.type
== IETF_ATTR_ATTRIBUTE_REQUEST
)
474 ietf_attr_attr_request_t
*attr_cast
;
478 attr_cast
= (ietf_attr_attr_request_t
*)attr
;
480 e
= attr_cast
->create_enumerator(attr_cast
);
481 while (e
->enumerate(e
, &entry
))
483 if (entry
->vendor_id
== PEN_IETF
)
487 case IETF_ATTR_PRODUCT_INFORMATION
:
488 add_product_info(out_msg
);
490 case IETF_ATTR_STRING_VERSION
:
491 add_string_version(out_msg
);
493 case IETF_ATTR_NUMERIC_VERSION
:
494 add_numeric_version(out_msg
);
496 case IETF_ATTR_OPERATIONAL_STATUS
:
497 add_op_status(out_msg
);
499 case IETF_ATTR_FORWARDING_ENABLED
:
500 add_fwd_enabled(out_msg
);
502 case IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED
:
503 add_default_pwd_enabled(out_msg
);
505 case IETF_ATTR_INSTALLED_PACKAGES
:
506 add_installed_packages(state
, out_msg
);
512 else if (entry
->vendor_id
== PEN_ITA
)
516 case ITA_ATTR_DEVICE_ID
:
517 add_device_id(out_msg
);
527 else if (type
.vendor_id
== PEN_ITA
&& type
.type
== ITA_ATTR_GET_SETTINGS
)
529 ita_attr_get_settings_t
*attr_cast
;
532 attr_cast
= (ita_attr_get_settings_t
*)attr
;
534 e
= attr_cast
->create_enumerator(attr_cast
);
535 add_settings(e
, out_msg
);
539 enumerator
->destroy(enumerator
);
543 result
= TNC_RESULT_FATAL
;
547 /* send PA-TNC message with the EXCL flag set */
548 result
= out_msg
->send(out_msg
, TRUE
);
550 out_msg
->destroy(out_msg
);
556 * see section 3.8.4 of TCG TNC IF-IMC Specification 1.3
559 TNC_Result TNC_IMC_API
TNC_IMC_ReceiveMessage(TNC_IMCID imc_id
,
560 TNC_ConnectionID connection_id
,
561 TNC_BufferReference msg
,
563 TNC_MessageType msg_type
)
571 DBG1(DBG_IMC
, "IMC \"%s\" has not been initialized", imc_name
);
572 return TNC_RESULT_NOT_INITIALIZED
;
574 if (!imc_os
->get_state(imc_os
, connection_id
, &state
))
576 return TNC_RESULT_FATAL
;
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
);
587 * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3
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
,
594 TNC_VendorID msg_vid
,
595 TNC_MessageSubtype msg_subtype
,
596 TNC_UInt32 src_imv_id
,
597 TNC_UInt32 dst_imc_id
)
605 DBG1(DBG_IMC
, "IMC \"%s\" has not been initialized", imc_name
);
606 return TNC_RESULT_NOT_INITIALIZED
;
608 if (!imc_os
->get_state(imc_os
, connection_id
, &state
))
610 return TNC_RESULT_FATAL
;
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
);
622 * see section 3.8.7 of TCG TNC IF-IMC Specification 1.3
624 TNC_Result TNC_IMC_API
TNC_IMC_BatchEnding(TNC_IMCID imc_id
,
625 TNC_ConnectionID connection_id
)
629 DBG1(DBG_IMC
, "IMC \"%s\" has not been initialized", imc_name
);
630 return TNC_RESULT_NOT_INITIALIZED
;
632 return TNC_RESULT_SUCCESS
;
636 * see section 3.8.8 of TCG TNC IF-IMC Specification 1.3
638 TNC_Result TNC_IMC_API
TNC_IMC_Terminate(TNC_IMCID imc_id
)
642 DBG1(DBG_IMC
, "IMC \"%s\" has not been initialized", imc_name
);
643 return TNC_RESULT_NOT_INITIALIZED
;
645 imc_os
->destroy(imc_os
);
651 return TNC_RESULT_SUCCESS
;
655 * see section 4.2.8.1 of TCG TNC IF-IMC Specification 1.3
657 TNC_Result TNC_IMC_API
TNC_IMC_ProvideBindFunction(TNC_IMCID imc_id
,
658 TNC_TNCC_BindFunctionPointer bind_function
)
662 DBG1(DBG_IMC
, "IMC \"%s\" has not been initialized", imc_name
);
663 return TNC_RESULT_NOT_INITIALIZED
;
665 return imc_os
->bind_functions(imc_os
, bind_function
);