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 count
, count_update
, count_blacklist
, count_ok
;
378 os_state
->get_count(os_state
, &count
, &count_update
, &count_blacklist
,
380 DBG1(DBG_IMV
, "processed %d packages: %d not updated, %d blacklisted, "
381 "%d ok, %d not found", count
, count_update
, count_blacklist
,
382 count_ok
, count
- count_update
- count_blacklist
- count_ok
);
384 if (count_update
|| count_blacklist
||
385 os_state
->get_os_settings(os_state
))
387 state
->set_recommendation(state
,
388 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
,
389 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR
);
393 state
->set_recommendation(state
,
394 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
395 TNC_IMV_EVALUATION_RESULT_COMPLIANT
);
402 result
= out_msg
->send_assessment(out_msg
);
403 out_msg
->destroy(out_msg
);
404 if (result
!= TNC_RESULT_SUCCESS
)
408 return imv_os
->provide_recommendation(imv_os
, state
);
411 /* send PA-TNC message with excl flag set */
412 result
= out_msg
->send(out_msg
, TRUE
);
413 out_msg
->destroy(out_msg
);
419 * see section 3.8.4 of TCG TNC IF-IMV Specification 1.3
421 TNC_Result
TNC_IMV_ReceiveMessage(TNC_IMVID imv_id
,
422 TNC_ConnectionID connection_id
,
423 TNC_BufferReference msg
,
425 TNC_MessageType msg_type
)
433 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
434 return TNC_RESULT_NOT_INITIALIZED
;
436 if (!imv_os
->get_state(imv_os
, connection_id
, &state
))
438 return TNC_RESULT_FATAL
;
440 in_msg
= imv_msg_create_from_data(imv_os
, state
, connection_id
, msg_type
,
441 chunk_create(msg
, msg_len
));
442 result
= receive_message(state
, in_msg
);
443 in_msg
->destroy(in_msg
);
449 * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3
451 TNC_Result
TNC_IMV_ReceiveMessageLong(TNC_IMVID imv_id
,
452 TNC_ConnectionID connection_id
,
453 TNC_UInt32 msg_flags
,
454 TNC_BufferReference msg
,
456 TNC_VendorID msg_vid
,
457 TNC_MessageSubtype msg_subtype
,
458 TNC_UInt32 src_imc_id
,
459 TNC_UInt32 dst_imv_id
)
467 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
468 return TNC_RESULT_NOT_INITIALIZED
;
470 if (!imv_os
->get_state(imv_os
, connection_id
, &state
))
472 return TNC_RESULT_FATAL
;
474 in_msg
= imv_msg_create_from_long_data(imv_os
, state
, connection_id
,
475 src_imc_id
, dst_imv_id
, msg_vid
, msg_subtype
,
476 chunk_create(msg
, msg_len
));
477 result
=receive_message(state
, in_msg
);
478 in_msg
->destroy(in_msg
);
484 * see section 3.8.7 of TCG TNC IF-IMV Specification 1.3
486 TNC_Result
TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id
,
487 TNC_ConnectionID connection_id
)
493 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
494 return TNC_RESULT_NOT_INITIALIZED
;
496 if (!imv_os
->get_state(imv_os
, connection_id
, &state
))
498 return TNC_RESULT_FATAL
;
500 return imv_os
->provide_recommendation(imv_os
, state
);
504 * see section 3.8.8 of TCG TNC IF-IMV Specification 1.3
506 TNC_Result
TNC_IMV_BatchEnding(TNC_IMVID imv_id
,
507 TNC_ConnectionID connection_id
)
510 imv_os_state_t
*os_state
;
511 TNC_Result result
= TNC_RESULT_SUCCESS
;
515 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
516 return TNC_RESULT_NOT_INITIALIZED
;
518 if (!imv_os
->get_state(imv_os
, connection_id
, &state
))
520 return TNC_RESULT_FATAL
;
522 os_state
= (imv_os_state_t
*)state
;
524 if (os_state
->get_info(os_state
, NULL
, NULL
, NULL
) == NULL
)
528 ietf_attr_attr_request_t
*attr_cast
;
530 out_msg
= imv_msg_create(imv_os
, state
, connection_id
, imv_id
,
531 TNC_IMCID_ANY
, msg_types
[0]);
532 attr
= ietf_attr_attr_request_create(PEN_IETF
,
533 IETF_ATTR_PRODUCT_INFORMATION
);
534 attr_cast
= (ietf_attr_attr_request_t
*)attr
;
535 attr_cast
->add(attr_cast
, PEN_IETF
, IETF_ATTR_STRING_VERSION
);
536 attr_cast
->add(attr_cast
, PEN_IETF
, IETF_ATTR_NUMERIC_VERSION
);
537 attr_cast
->add(attr_cast
, PEN_IETF
, IETF_ATTR_OPERATIONAL_STATUS
);
538 attr_cast
->add(attr_cast
, PEN_IETF
, IETF_ATTR_FORWARDING_ENABLED
);
539 attr_cast
->add(attr_cast
, PEN_IETF
, IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED
);
540 out_msg
->add_attribute(out_msg
, attr
);
542 /* send PA-TNC message with excl flag not set */
543 result
= out_msg
->send(out_msg
, FALSE
);
544 out_msg
->destroy(out_msg
);
551 * see section 3.8.9 of TCG TNC IF-IMV Specification 1.3
553 TNC_Result
TNC_IMV_Terminate(TNC_IMVID imv_id
)
557 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
558 return TNC_RESULT_NOT_INITIALIZED
;
562 imv_os
->destroy(imv_os
);
565 return TNC_RESULT_SUCCESS
;
569 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.3
571 TNC_Result
TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id
,
572 TNC_TNCS_BindFunctionPointer bind_function
)
576 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
577 return TNC_RESULT_NOT_INITIALIZED
;
579 return imv_os
->bind_functions(imv_os
, bind_function
);