2 * Copyright (C) 2012 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 "imv_os_state.h"
17 #include "imv_os_database.h"
19 #include <imv/imv_agent.h>
20 #include <imv/imv_msg.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_pa_tnc_error.h>
29 #include <ietf/ietf_attr_product_info.h>
30 #include <ietf/ietf_attr_remediation_instr.h>
31 #include <ietf/ietf_attr_string_version.h>
32 #include <ita/ita_attr.h>
33 #include <ita/ita_attr_get_settings.h>
34 #include <ita/ita_attr_settings.h>
35 #include <ita/ita_attr_angel.h>
37 #include <tncif_names.h>
38 #include <tncif_pa_subtypes.h>
41 #include <collections/linked_list.h>
42 #include <utils/debug.h>
43 #include <utils/lexparser.h>
47 static const char imv_name
[] = "OS";
49 static pen_type_t msg_types
[] = {
50 { PEN_IETF
, PA_SUBTYPE_IETF_OPERATING_SYSTEM
}
53 static imv_agent_t
*imv_os
;
58 static imv_os_database_t
*os_db
;
61 * see section 3.8.1 of TCG TNC IF-IMV Specification 1.3
63 TNC_Result
TNC_IMV_Initialize(TNC_IMVID imv_id
,
64 TNC_Version min_version
,
65 TNC_Version max_version
,
66 TNC_Version
*actual_version
)
72 DBG1(DBG_IMV
, "IMV \"%s\" has already been initialized", imv_name
);
73 return TNC_RESULT_ALREADY_INITIALIZED
;
75 imv_os
= imv_agent_create(imv_name
, msg_types
, countof(msg_types
),
76 imv_id
, actual_version
);
79 return TNC_RESULT_FATAL
;
81 if (min_version
> TNC_IFIMV_VERSION_1
|| max_version
< TNC_IFIMV_VERSION_1
)
83 DBG1(DBG_IMV
, "no common IF-IMV version");
84 return TNC_RESULT_NO_COMMON_VERSION
;
87 /* attach OS database */
88 uri
= lib
->settings
->get_str(lib
->settings
,
89 "libimcv.plugins.imv-os.database", NULL
);
92 os_db
= imv_os_database_create(uri
);
95 return TNC_RESULT_SUCCESS
;
99 * see section 3.8.2 of TCG TNC IF-IMV Specification 1.3
101 TNC_Result
TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id
,
102 TNC_ConnectionID connection_id
,
103 TNC_ConnectionState new_state
)
109 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
110 return TNC_RESULT_NOT_INITIALIZED
;
114 case TNC_CONNECTION_STATE_CREATE
:
115 state
= imv_os_state_create(connection_id
);
116 return imv_os
->create_state(imv_os
, state
);
117 case TNC_CONNECTION_STATE_DELETE
:
118 return imv_os
->delete_state(imv_os
, connection_id
);
120 return imv_os
->change_state(imv_os
, connection_id
,
125 static TNC_Result
receive_message(imv_state_t
*state
, imv_msg_t
*in_msg
)
128 imv_os_state_t
*os_state
;
129 enumerator_t
*enumerator
;
133 chunk_t os_name
= chunk_empty
;
134 chunk_t os_version
= chunk_empty
;
135 bool fatal_error
= FALSE
, assessment
= FALSE
;
136 char non_market_apps_str
[] = "install_non_market_apps";
137 char android_id_str
[] = "android_id";
138 char machine_id_str
[] = "/var/lib/dbus/machine-id";
140 os_state
= (imv_os_state_t
*)state
;
142 /* parse received PA-TNC message and handle local and remote errors */
143 result
= in_msg
->receive(in_msg
, &fatal_error
);
144 if (result
!= TNC_RESULT_SUCCESS
)
149 out_msg
= imv_msg_create_as_reply(in_msg
);
151 /* analyze PA-TNC attributes */
152 enumerator
= in_msg
->create_attribute_enumerator(in_msg
);
153 while (enumerator
->enumerate(enumerator
, &attr
))
155 type
= attr
->get_type(attr
);
157 if (type
.vendor_id
== PEN_IETF
)
161 case IETF_ATTR_PRODUCT_INFORMATION
:
163 ietf_attr_product_info_t
*attr_cast
;
166 attr_cast
= (ietf_attr_product_info_t
*)attr
;
167 os_name
= attr_cast
->get_info(attr_cast
, &vendor_id
, NULL
);
168 if (vendor_id
!= PEN_IETF
)
170 DBG1(DBG_IMV
, "operating system name is '%.*s' "
171 "from vendor %N", os_name
.len
, os_name
.ptr
,
172 pen_names
, vendor_id
);
176 DBG1(DBG_IMV
, "operating system name is '%.*s'",
177 os_name
.len
, os_name
.ptr
);
181 case IETF_ATTR_STRING_VERSION
:
183 ietf_attr_string_version_t
*attr_cast
;
185 attr_cast
= (ietf_attr_string_version_t
*)attr
;
186 os_version
= attr_cast
->get_version(attr_cast
, NULL
, NULL
);
189 DBG1(DBG_IMV
, "operating system version is '%.*s'",
190 os_version
.len
, os_version
.ptr
);
194 case IETF_ATTR_NUMERIC_VERSION
:
196 ietf_attr_numeric_version_t
*attr_cast
;
197 u_int32_t major
, minor
;
199 attr_cast
= (ietf_attr_numeric_version_t
*)attr
;
200 attr_cast
->get_version(attr_cast
, &major
, &minor
);
201 DBG1(DBG_IMV
, "operating system numeric version is %d.%d",
205 case IETF_ATTR_OPERATIONAL_STATUS
:
207 ietf_attr_op_status_t
*attr_cast
;
208 op_status_t op_status
;
209 op_result_t op_result
;
212 attr_cast
= (ietf_attr_op_status_t
*)attr
;
213 op_status
= attr_cast
->get_status(attr_cast
);
214 op_result
= attr_cast
->get_result(attr_cast
);
215 last_boot
= attr_cast
->get_last_use(attr_cast
);
216 DBG1(DBG_IMV
, "operational status: %N, result: %N",
217 op_status_names
, op_status
, op_result_names
, op_result
);
218 DBG1(DBG_IMV
, "last boot: %T", &last_boot
, TRUE
);
221 case IETF_ATTR_FORWARDING_ENABLED
:
223 ietf_attr_fwd_enabled_t
*attr_cast
;
224 os_fwd_status_t fwd_status
;
226 attr_cast
= (ietf_attr_fwd_enabled_t
*)attr
;
227 fwd_status
= attr_cast
->get_status(attr_cast
);
228 DBG1(DBG_IMV
, "IPv4 forwarding status: %N",
229 os_fwd_status_names
, fwd_status
);
230 if (fwd_status
== OS_FWD_ENABLED
)
232 os_state
->set_os_settings(os_state
,
233 OS_SETTINGS_FWD_ENABLED
);
237 case IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED
:
239 ietf_attr_default_pwd_enabled_t
*attr_cast
;
240 bool default_pwd_status
;
242 attr_cast
= (ietf_attr_default_pwd_enabled_t
*)attr
;
243 default_pwd_status
= attr_cast
->get_status(attr_cast
);
244 DBG1(DBG_IMV
, "factory default password: %sabled",
245 default_pwd_status ?
"en":"dis");
246 if (default_pwd_status
)
248 os_state
->set_os_settings(os_state
,
249 OS_SETTINGS_DEFAULT_PWD_ENABLED
);
253 case IETF_ATTR_INSTALLED_PACKAGES
:
255 ietf_attr_installed_packages_t
*attr_cast
;
259 /* Received at least one Installed Packages attribute */
260 os_state
->set_package_request(os_state
, FALSE
);
266 attr_cast
= (ietf_attr_installed_packages_t
*)attr
;
268 e
= attr_cast
->create_enumerator(attr_cast
);
269 status
= os_db
->check_packages(os_db
, os_state
, e
);
272 if (status
== FAILED
)
274 state
->set_recommendation(state
,
275 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
276 TNC_IMV_EVALUATION_RESULT_ERROR
);
285 else if (type
.vendor_id
== PEN_ITA
)
289 case ITA_ATTR_SETTINGS
:
291 ita_attr_settings_t
*attr_cast
;
296 attr_cast
= (ita_attr_settings_t
*)attr
;
297 e
= attr_cast
->create_enumerator(attr_cast
);
298 while (e
->enumerate(e
, &name
, &value
))
300 if (streq(name
, non_market_apps_str
) &&
301 chunk_equals(value
, chunk_from_chars('1')))
303 os_state
->set_os_settings(os_state
,
304 OS_SETTINGS_NON_MARKET_APPS
);
306 else if ((streq(name
, android_id_str
) ||
307 streq(name
, machine_id_str
)) && os_db
)
309 os_state
->set_device_id(os_state
,
310 os_db
->get_device_id(os_db
, value
));
312 DBG1(DBG_IMV
, "setting '%s'\n %.*s",
313 name
, value
.len
, value
.ptr
);
318 case ITA_ATTR_START_ANGEL
:
319 os_state
->set_angel_count(os_state
, TRUE
);
321 case ITA_ATTR_STOP_ANGEL
:
322 os_state
->set_angel_count(os_state
, FALSE
);
329 enumerator
->destroy(enumerator
);
331 if (os_name
.len
&& os_version
.len
)
334 ita_attr_get_settings_t
*attr_cast
;
336 /* set the OS type, name and version */
337 os_type
= os_type_from_name(os_name
);
338 os_state
->set_info(os_state
,os_type
, os_name
, os_version
);
340 /* requesting installed packages */
341 os_state
->set_package_request(os_state
, TRUE
);
342 attr
= ietf_attr_attr_request_create(PEN_IETF
,
343 IETF_ATTR_INSTALLED_PACKAGES
);
344 out_msg
->add_attribute(out_msg
, attr
);
346 /* requesting Android or Linux settings */
347 attr
= ita_attr_get_settings_create();
348 attr_cast
= (ita_attr_get_settings_t
*)attr
;
350 if (os_type
== OS_TYPE_ANDROID
)
352 attr_cast
->add(attr_cast
, android_id_str
);
353 attr_cast
->add(attr_cast
, non_market_apps_str
);
357 attr_cast
->add(attr_cast
, machine_id_str
);
358 attr_cast
->add(attr_cast
, "/proc/sys/kernel/tainted");
360 out_msg
->add_attribute(out_msg
, attr
);
365 state
->set_recommendation(state
,
366 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
367 TNC_IMV_EVALUATION_RESULT_ERROR
);
371 /* If all Installed Packages attributes were received, go to assessment */
373 !os_state
->get_package_request(os_state
) &&
374 !os_state
->get_angel_count(os_state
))
376 int device_id
, count
, count_update
, count_blacklist
, count_ok
;
379 os_settings
= os_state
->get_os_settings(os_state
);
380 os_state
->get_count(os_state
, &count
, &count_update
, &count_blacklist
,
382 DBG1(DBG_IMV
, "processed %d packages: %d not updated, %d blacklisted, "
383 "%d ok, %d not found", count
, count_update
, count_blacklist
,
384 count_ok
, count
- count_update
- count_blacklist
- count_ok
);
386 /* Store device information in database */
387 device_id
= os_state
->get_device_id(os_state
);
388 if (os_db
&& device_id
)
390 os_db
->set_device_info(os_db
, device_id
,
391 os_state
->get_info(os_state
, NULL
, NULL
, NULL
),
392 count
, count_update
, count_blacklist
, os_settings
);
395 if (count_update
|| count_blacklist
|| os_settings
)
397 state
->set_recommendation(state
,
398 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
,
399 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR
);
403 state
->set_recommendation(state
,
404 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
405 TNC_IMV_EVALUATION_RESULT_COMPLIANT
);
412 result
= out_msg
->send_assessment(out_msg
);
413 out_msg
->destroy(out_msg
);
414 if (result
!= TNC_RESULT_SUCCESS
)
418 return imv_os
->provide_recommendation(imv_os
, state
);
421 /* send PA-TNC message with excl flag set */
422 result
= out_msg
->send(out_msg
, TRUE
);
423 out_msg
->destroy(out_msg
);
429 * see section 3.8.4 of TCG TNC IF-IMV Specification 1.3
431 TNC_Result
TNC_IMV_ReceiveMessage(TNC_IMVID imv_id
,
432 TNC_ConnectionID connection_id
,
433 TNC_BufferReference msg
,
435 TNC_MessageType msg_type
)
443 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
444 return TNC_RESULT_NOT_INITIALIZED
;
446 if (!imv_os
->get_state(imv_os
, connection_id
, &state
))
448 return TNC_RESULT_FATAL
;
450 in_msg
= imv_msg_create_from_data(imv_os
, state
, connection_id
, msg_type
,
451 chunk_create(msg
, msg_len
));
452 result
= receive_message(state
, in_msg
);
453 in_msg
->destroy(in_msg
);
459 * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3
461 TNC_Result
TNC_IMV_ReceiveMessageLong(TNC_IMVID imv_id
,
462 TNC_ConnectionID connection_id
,
463 TNC_UInt32 msg_flags
,
464 TNC_BufferReference msg
,
466 TNC_VendorID msg_vid
,
467 TNC_MessageSubtype msg_subtype
,
468 TNC_UInt32 src_imc_id
,
469 TNC_UInt32 dst_imv_id
)
477 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
478 return TNC_RESULT_NOT_INITIALIZED
;
480 if (!imv_os
->get_state(imv_os
, connection_id
, &state
))
482 return TNC_RESULT_FATAL
;
484 in_msg
= imv_msg_create_from_long_data(imv_os
, state
, connection_id
,
485 src_imc_id
, dst_imv_id
, msg_vid
, msg_subtype
,
486 chunk_create(msg
, msg_len
));
487 result
=receive_message(state
, in_msg
);
488 in_msg
->destroy(in_msg
);
494 * see section 3.8.7 of TCG TNC IF-IMV Specification 1.3
496 TNC_Result
TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id
,
497 TNC_ConnectionID connection_id
)
503 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
504 return TNC_RESULT_NOT_INITIALIZED
;
506 if (!imv_os
->get_state(imv_os
, connection_id
, &state
))
508 return TNC_RESULT_FATAL
;
510 return imv_os
->provide_recommendation(imv_os
, state
);
514 * see section 3.8.8 of TCG TNC IF-IMV Specification 1.3
516 TNC_Result
TNC_IMV_BatchEnding(TNC_IMVID imv_id
,
517 TNC_ConnectionID connection_id
)
520 imv_os_state_t
*os_state
;
521 TNC_Result result
= TNC_RESULT_SUCCESS
;
525 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
526 return TNC_RESULT_NOT_INITIALIZED
;
528 if (!imv_os
->get_state(imv_os
, connection_id
, &state
))
530 return TNC_RESULT_FATAL
;
532 os_state
= (imv_os_state_t
*)state
;
534 if (os_state
->get_info(os_state
, NULL
, NULL
, NULL
) == NULL
)
538 ietf_attr_attr_request_t
*attr_cast
;
540 out_msg
= imv_msg_create(imv_os
, state
, connection_id
, imv_id
,
541 TNC_IMCID_ANY
, msg_types
[0]);
542 attr
= ietf_attr_attr_request_create(PEN_IETF
,
543 IETF_ATTR_PRODUCT_INFORMATION
);
544 attr_cast
= (ietf_attr_attr_request_t
*)attr
;
545 attr_cast
->add(attr_cast
, PEN_IETF
, IETF_ATTR_STRING_VERSION
);
546 attr_cast
->add(attr_cast
, PEN_IETF
, IETF_ATTR_NUMERIC_VERSION
);
547 attr_cast
->add(attr_cast
, PEN_IETF
, IETF_ATTR_OPERATIONAL_STATUS
);
548 attr_cast
->add(attr_cast
, PEN_IETF
, IETF_ATTR_FORWARDING_ENABLED
);
549 attr_cast
->add(attr_cast
, PEN_IETF
, IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED
);
550 out_msg
->add_attribute(out_msg
, attr
);
552 /* send PA-TNC message with excl flag not set */
553 result
= out_msg
->send(out_msg
, FALSE
);
554 out_msg
->destroy(out_msg
);
561 * see section 3.8.9 of TCG TNC IF-IMV Specification 1.3
563 TNC_Result
TNC_IMV_Terminate(TNC_IMVID imv_id
)
567 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
568 return TNC_RESULT_NOT_INITIALIZED
;
572 imv_os
->destroy(imv_os
);
575 return TNC_RESULT_SUCCESS
;
579 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.3
581 TNC_Result
TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id
,
582 TNC_TNCS_BindFunctionPointer bind_function
)
586 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
587 return TNC_RESULT_NOT_INITIALIZED
;
589 return imv_os
->bind_functions(imv_os
, bind_function
);