diff options
Diffstat (limited to 'src/libimcv/plugins/imv_os/imv_os.c')
-rw-r--r-- | src/libimcv/plugins/imv_os/imv_os.c | 590 |
1 files changed, 590 insertions, 0 deletions
diff --git a/src/libimcv/plugins/imv_os/imv_os.c b/src/libimcv/plugins/imv_os/imv_os.c new file mode 100644 index 000000000..65538df07 --- /dev/null +++ b/src/libimcv/plugins/imv_os/imv_os.c @@ -0,0 +1,590 @@ +/* + * Copyright (C) 2012 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "imv_os_state.h" +#include "imv_os_database.h" + +#include <imv/imv_agent.h> +#include <imv/imv_msg.h> +#include <ietf/ietf_attr.h> +#include <ietf/ietf_attr_attr_request.h> +#include <ietf/ietf_attr_default_pwd_enabled.h> +#include <ietf/ietf_attr_fwd_enabled.h> +#include <ietf/ietf_attr_installed_packages.h> +#include <ietf/ietf_attr_numeric_version.h> +#include <ietf/ietf_attr_op_status.h> +#include <ietf/ietf_attr_pa_tnc_error.h> +#include <ietf/ietf_attr_product_info.h> +#include <ietf/ietf_attr_remediation_instr.h> +#include <ietf/ietf_attr_string_version.h> +#include <ita/ita_attr.h> +#include <ita/ita_attr_get_settings.h> +#include <ita/ita_attr_settings.h> +#include <ita/ita_attr_angel.h> + +#include <tncif_names.h> +#include <tncif_pa_subtypes.h> + +#include <pen/pen.h> +#include <collections/linked_list.h> +#include <utils/debug.h> +#include <utils/lexparser.h> + +/* IMV definitions */ + +static const char imv_name[] = "OS"; + +static pen_type_t msg_types[] = { + { PEN_IETF, PA_SUBTYPE_IETF_OPERATING_SYSTEM } +}; + +static imv_agent_t *imv_os; + +/** + * IMV OS database + */ +static imv_os_database_t *os_db; + +/* + * see section 3.8.1 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id, + TNC_Version min_version, + TNC_Version max_version, + TNC_Version *actual_version) +{ + char *uri; + + if (imv_os) + { + DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name); + return TNC_RESULT_ALREADY_INITIALIZED; + } + imv_os = imv_agent_create(imv_name, msg_types, countof(msg_types), + imv_id, actual_version); + if (!imv_os) + { + return TNC_RESULT_FATAL; + } + if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1) + { + DBG1(DBG_IMV, "no common IF-IMV version"); + return TNC_RESULT_NO_COMMON_VERSION; + } + + /* attach OS database */ + uri = lib->settings->get_str(lib->settings, + "libimcv.plugins.imv-os.database", NULL); + if (uri) + { + os_db = imv_os_database_create(uri); + } + + return TNC_RESULT_SUCCESS; +} + +/** + * see section 3.8.2 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id, + TNC_ConnectionID connection_id, + TNC_ConnectionState new_state) +{ + imv_state_t *state; + + if (!imv_os) + { + DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); + return TNC_RESULT_NOT_INITIALIZED; + } + switch (new_state) + { + case TNC_CONNECTION_STATE_CREATE: + state = imv_os_state_create(connection_id); + return imv_os->create_state(imv_os, state); + case TNC_CONNECTION_STATE_DELETE: + return imv_os->delete_state(imv_os, connection_id); + default: + return imv_os->change_state(imv_os, connection_id, + new_state, NULL); + } +} + +static TNC_Result receive_message(imv_state_t *state, imv_msg_t *in_msg) +{ + imv_msg_t *out_msg; + imv_os_state_t *os_state; + enumerator_t *enumerator; + pa_tnc_attr_t *attr; + pen_type_t type; + TNC_Result result; + chunk_t os_name = chunk_empty; + chunk_t os_version = chunk_empty; + bool fatal_error = FALSE, assessment = FALSE; + char non_market_apps_str[] = "install_non_market_apps"; + char android_id_str[] = "android_id"; + char machine_id_str[] = "/var/lib/dbus/machine-id"; + + os_state = (imv_os_state_t*)state; + + /* parse received PA-TNC message and handle local and remote errors */ + result = in_msg->receive(in_msg, &fatal_error); + if (result != TNC_RESULT_SUCCESS) + { + return result; + } + + out_msg = imv_msg_create_as_reply(in_msg); + + /* analyze PA-TNC attributes */ + enumerator = in_msg->create_attribute_enumerator(in_msg); + while (enumerator->enumerate(enumerator, &attr)) + { + type = attr->get_type(attr); + + if (type.vendor_id == PEN_IETF) + { + switch (type.type) + { + case IETF_ATTR_PRODUCT_INFORMATION: + { + ietf_attr_product_info_t *attr_cast; + pen_t vendor_id; + + attr_cast = (ietf_attr_product_info_t*)attr; + os_name = attr_cast->get_info(attr_cast, &vendor_id, NULL); + if (vendor_id != PEN_IETF) + { + DBG1(DBG_IMV, "operating system name is '%.*s' " + "from vendor %N", os_name.len, os_name.ptr, + pen_names, vendor_id); + } + else + { + DBG1(DBG_IMV, "operating system name is '%.*s'", + os_name.len, os_name.ptr); + } + break; + } + case IETF_ATTR_STRING_VERSION: + { + ietf_attr_string_version_t *attr_cast; + + attr_cast = (ietf_attr_string_version_t*)attr; + os_version = attr_cast->get_version(attr_cast, NULL, NULL); + if (os_version.len) + { + DBG1(DBG_IMV, "operating system version is '%.*s'", + os_version.len, os_version.ptr); + } + break; + } + case IETF_ATTR_NUMERIC_VERSION: + { + ietf_attr_numeric_version_t *attr_cast; + u_int32_t major, minor; + + attr_cast = (ietf_attr_numeric_version_t*)attr; + attr_cast->get_version(attr_cast, &major, &minor); + DBG1(DBG_IMV, "operating system numeric version is %d.%d", + major, minor); + break; + } + case IETF_ATTR_OPERATIONAL_STATUS: + { + ietf_attr_op_status_t *attr_cast; + op_status_t op_status; + op_result_t op_result; + time_t last_boot; + + attr_cast = (ietf_attr_op_status_t*)attr; + op_status = attr_cast->get_status(attr_cast); + op_result = attr_cast->get_result(attr_cast); + last_boot = attr_cast->get_last_use(attr_cast); + DBG1(DBG_IMV, "operational status: %N, result: %N", + op_status_names, op_status, op_result_names, op_result); + DBG1(DBG_IMV, "last boot: %T", &last_boot, TRUE); + break; + } + case IETF_ATTR_FORWARDING_ENABLED: + { + ietf_attr_fwd_enabled_t *attr_cast; + os_fwd_status_t fwd_status; + + attr_cast = (ietf_attr_fwd_enabled_t*)attr; + fwd_status = attr_cast->get_status(attr_cast); + DBG1(DBG_IMV, "IPv4 forwarding status: %N", + os_fwd_status_names, fwd_status); + if (fwd_status == OS_FWD_ENABLED) + { + os_state->set_os_settings(os_state, + OS_SETTINGS_FWD_ENABLED); + } + break; + } + case IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED: + { + ietf_attr_default_pwd_enabled_t *attr_cast; + bool default_pwd_status; + + attr_cast = (ietf_attr_default_pwd_enabled_t*)attr; + default_pwd_status = attr_cast->get_status(attr_cast); + DBG1(DBG_IMV, "factory default password: %sabled", + default_pwd_status ? "en":"dis"); + if (default_pwd_status) + { + os_state->set_os_settings(os_state, + OS_SETTINGS_DEFAULT_PWD_ENABLED); + } + break; + } + case IETF_ATTR_INSTALLED_PACKAGES: + { + ietf_attr_installed_packages_t *attr_cast; + enumerator_t *e; + status_t status; + + /* Received at least one Installed Packages attribute */ + os_state->set_package_request(os_state, FALSE); + + if (!os_db) + { + break; + } + attr_cast = (ietf_attr_installed_packages_t*)attr; + + e = attr_cast->create_enumerator(attr_cast); + status = os_db->check_packages(os_db, os_state, e); + e->destroy(e); + + if (status == FAILED) + { + state->set_recommendation(state, + TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, + TNC_IMV_EVALUATION_RESULT_ERROR); + assessment = TRUE; + } + break; + } + default: + break; + } + } + else if (type.vendor_id == PEN_ITA) + { + switch (type.type) + { + case ITA_ATTR_SETTINGS: + { + ita_attr_settings_t *attr_cast; + enumerator_t *e; + char *name; + chunk_t value; + + attr_cast = (ita_attr_settings_t*)attr; + e = attr_cast->create_enumerator(attr_cast); + while (e->enumerate(e, &name, &value)) + { + if (streq(name, non_market_apps_str) && + chunk_equals(value, chunk_from_chars('1'))) + { + os_state->set_os_settings(os_state, + OS_SETTINGS_NON_MARKET_APPS); + } + else if ((streq(name, android_id_str) || + streq(name, machine_id_str)) && os_db) + { + os_state->set_device_id(os_state, + os_db->get_device_id(os_db, value)); + } + DBG1(DBG_IMV, "setting '%s'\n %.*s", + name, value.len, value.ptr); + } + e->destroy(e); + break; + } + case ITA_ATTR_START_ANGEL: + os_state->set_angel_count(os_state, TRUE); + break; + case ITA_ATTR_STOP_ANGEL: + os_state->set_angel_count(os_state, FALSE); + break; + default: + break; + } + } + } + enumerator->destroy(enumerator); + + if (os_name.len && os_version.len) + { + os_type_t os_type; + ita_attr_get_settings_t *attr_cast; + + /* set the OS type, name and version */ + os_type = os_type_from_name(os_name); + os_state->set_info(os_state,os_type, os_name, os_version); + + /* requesting installed packages */ + os_state->set_package_request(os_state, TRUE); + attr = ietf_attr_attr_request_create(PEN_IETF, + IETF_ATTR_INSTALLED_PACKAGES); + out_msg->add_attribute(out_msg, attr); + + /* requesting Android or Linux settings */ + attr = ita_attr_get_settings_create(); + attr_cast = (ita_attr_get_settings_t*)attr; + + if (os_type == OS_TYPE_ANDROID) + { + attr_cast->add(attr_cast, android_id_str); + attr_cast->add(attr_cast, non_market_apps_str); + } + else + { + attr_cast->add(attr_cast, machine_id_str); + attr_cast->add(attr_cast, "/proc/sys/kernel/tainted"); + } + out_msg->add_attribute(out_msg, attr); + } + + if (fatal_error) + { + state->set_recommendation(state, + TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, + TNC_IMV_EVALUATION_RESULT_ERROR); + assessment = TRUE; + } + + /* If all Installed Packages attributes were received, go to assessment */ + if (!assessment && + !os_state->get_package_request(os_state) && + !os_state->get_angel_count(os_state)) + { + int device_id, count, count_update, count_blacklist, count_ok; + u_int os_settings; + + os_settings = os_state->get_os_settings(os_state); + os_state->get_count(os_state, &count, &count_update, &count_blacklist, + &count_ok); + DBG1(DBG_IMV, "processed %d packages: %d not updated, %d blacklisted, " + "%d ok, %d not found", count, count_update, count_blacklist, + count_ok, count - count_update - count_blacklist - count_ok); + + /* Store device information in database */ + device_id = os_state->get_device_id(os_state); + if (os_db && device_id) + { + os_db->set_device_info(os_db, device_id, + os_state->get_info(os_state, NULL, NULL, NULL), + count, count_update, count_blacklist, os_settings); + } + + if (count_update || count_blacklist || os_settings) + { + state->set_recommendation(state, + TNC_IMV_ACTION_RECOMMENDATION_ISOLATE, + TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR); + } + else + { + state->set_recommendation(state, + TNC_IMV_ACTION_RECOMMENDATION_ALLOW, + TNC_IMV_EVALUATION_RESULT_COMPLIANT); + } + assessment = TRUE; + } + + if (assessment) + { + result = out_msg->send_assessment(out_msg); + out_msg->destroy(out_msg); + if (result != TNC_RESULT_SUCCESS) + { + return result; + } + return imv_os->provide_recommendation(imv_os, state); + } + + /* send PA-TNC message with excl flag set */ + result = out_msg->send(out_msg, TRUE); + out_msg->destroy(out_msg); + + return result; + } + +/** + * see section 3.8.4 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, + TNC_ConnectionID connection_id, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_MessageType msg_type) +{ + imv_state_t *state; + imv_msg_t *in_msg; + TNC_Result result; + + if (!imv_os) + { + DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); + return TNC_RESULT_NOT_INITIALIZED; + } + if (!imv_os->get_state(imv_os, connection_id, &state)) + { + return TNC_RESULT_FATAL; + } + in_msg = imv_msg_create_from_data(imv_os, state, connection_id, msg_type, + chunk_create(msg, msg_len)); + result = receive_message(state, in_msg); + in_msg->destroy(in_msg); + + return result; +} + +/** + * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_ReceiveMessageLong(TNC_IMVID imv_id, + TNC_ConnectionID connection_id, + TNC_UInt32 msg_flags, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imc_id, + TNC_UInt32 dst_imv_id) +{ + imv_state_t *state; + imv_msg_t *in_msg; + TNC_Result result; + + if (!imv_os) + { + DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); + return TNC_RESULT_NOT_INITIALIZED; + } + if (!imv_os->get_state(imv_os, connection_id, &state)) + { + return TNC_RESULT_FATAL; + } + in_msg = imv_msg_create_from_long_data(imv_os, state, connection_id, + src_imc_id, dst_imv_id, msg_vid, msg_subtype, + chunk_create(msg, msg_len)); + result =receive_message(state, in_msg); + in_msg->destroy(in_msg); + + return result; +} + +/** + * see section 3.8.7 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id, + TNC_ConnectionID connection_id) +{ + imv_state_t *state; + + if (!imv_os) + { + DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); + return TNC_RESULT_NOT_INITIALIZED; + } + if (!imv_os->get_state(imv_os, connection_id, &state)) + { + return TNC_RESULT_FATAL; + } + return imv_os->provide_recommendation(imv_os, state); +} + +/** + * see section 3.8.8 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id, + TNC_ConnectionID connection_id) +{ + imv_state_t *state; + imv_os_state_t *os_state; + TNC_Result result = TNC_RESULT_SUCCESS; + + if (!imv_os) + { + DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); + return TNC_RESULT_NOT_INITIALIZED; + } + if (!imv_os->get_state(imv_os, connection_id, &state)) + { + return TNC_RESULT_FATAL; + } + os_state = (imv_os_state_t*)state; + + if (os_state->get_info(os_state, NULL, NULL, NULL) == NULL) + { + imv_msg_t *out_msg; + pa_tnc_attr_t *attr; + ietf_attr_attr_request_t *attr_cast; + + out_msg = imv_msg_create(imv_os, state, connection_id, imv_id, + TNC_IMCID_ANY, msg_types[0]); + attr = ietf_attr_attr_request_create(PEN_IETF, + IETF_ATTR_PRODUCT_INFORMATION); + attr_cast = (ietf_attr_attr_request_t*)attr; + attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_STRING_VERSION); + attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_NUMERIC_VERSION); + attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_OPERATIONAL_STATUS); + attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_FORWARDING_ENABLED); + attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED); + out_msg->add_attribute(out_msg, attr); + + /* send PA-TNC message with excl flag not set */ + result = out_msg->send(out_msg, FALSE); + out_msg->destroy(out_msg); + } + + return result; +} + +/** + * see section 3.8.9 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id) +{ + if (!imv_os) + { + DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); + return TNC_RESULT_NOT_INITIALIZED; + } + DESTROY_IF(os_db); + + imv_os->destroy(imv_os); + imv_os = NULL; + + return TNC_RESULT_SUCCESS; +} + +/** + * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id, + TNC_TNCS_BindFunctionPointer bind_function) +{ + if (!imv_os) + { + DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); + return TNC_RESULT_NOT_INITIALIZED; + } + return imv_os->bind_functions(imv_os, bind_function); +} |