diff --git a/CMakeLists.txt b/CMakeLists.txt index 463da8c..7f11183 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,7 @@ option(WITH-SOFTWARE-DBUS ${OPTDESC-SOFTWARE-DBUS} OFF) option(WITH-JOURNALD "Build journald provider" ON) option(WITH-LOCALE "Build locale provider" ON) option(WITH-SSSD "Build SSSD provider" ON) +option(WITH-SELINUX "Build SELinux provider" ON) option(WITH-DEVASSISTANT "Install Developer Assistant templates" OFF) diff --git a/mof/60_LMI_SELinux.mof b/mof/60_LMI_SELinux.mof new file mode 100644 index 0000000..f2fb443 --- /dev/null +++ b/mof/60_LMI_SELinux.mof @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + */ + +[ Version("0.0.1"), + Description("Common superclass for all SELinux classes.") ] +class LMI_SELinuxElement : CIM_ManagedElement +{ + [ Key, Override("InstanceID") ] + string InstanceID; +}; + +[ Version("0.0.1"), + Description("Class representing an SELinux policy boolean.") ] +class LMI_SELinuxBoolean : LMI_SELinuxElement +{ + [ Description("Current state.") ] + boolean State; + + [ Description("State on next system boot.") ] + boolean DefaultState; +}; + +[ Version("0.0.1"), + Description( + "Class representing an SELinux port. It can encompass multiple " + "individual network ports, or even their ranges.") ] +class LMI_SELinuxPort : LMI_SELinuxElement +{ + [ Description("SELinux context.") ] + string SELinuxContext; + + [ Description("Protocol type. Only UDP and TCP are supported."), + ValueMap {"0", "1"}, + Values {"UDP", "TCP"} ] + uint16 Protocol; + + [ Description( + "Array of open ports that the SELinux port corresponds to.\n" + "Individual values can be specified either as a single number, or a range.\n" + "The range would be represented as '-', e.g. '1024-2048'." + "Note that a network port can be labeled with multiple labels at the same time.") ] + string Ports[]; +}; + +[ Version("0.0.1"), + Description( + "SELinux on the managed system.\n" + "SELinux can be in the following states:\n" + " Enforcing - SELinux security policy is enforced.\n" + " Permissive - SELinux prints warnings instead of enforcing.\n" + " Disabled - No SELinux policy is loaded.\n") ] +class LMI_SELinuxService : CIM_Service +{ + [ Description("Current system-wide state of SELinux."), + ValueMap {"0", "1", "2"}, + Values {"Disabled", "Permissive", "Enforcing"} ] + uint16 SELinuxState; + + [ Description("SELinux system-wide state on next system boot."), + ValueMap {"0", "1", "2"}, + Values {"Disabled", "Permissive", "Enforcing"} ] + uint16 SELinuxDefaultState; + + [ Description("SELinux policy type.") ] + string PolicyType; + + [ Description("Current version of the SELinux system policy.") ] + uint32 PolicyVersion; + + [ Description( + "Set SELinux state."), + ValueMap {"0", "1", "2", "3", "4", "5", "6", "4096"}, + Values {"Job Completed with No Error", "Not Supported", + "Unknown", "Timeout", "Failed", "Invalid Parameter", + "In Use", "Method Parameters Checked - Job Started"} ] + uint32 SetSELinuxState( + [ IN, Description("New state value."), + ValueMap {"0", "1", "2"}, + Values {"Disabled", "Permissive", "Enforcing"} ] + uint16 NewState, + + [ IN, Description("If set to True, makes the new state persistent.") ] + boolean MakeDefault, + + [ IN(false), OUT ] + LMI_SELinuxJob REF Job + ); + + [ Description( + "Set label on an SELinux port."), + ValueMap {"0", "1", "2", "3", "4", "5", "6", "4096"}, + Values {"Job Completed with No Error", "Not Supported", + "Unknown", "Timeout", "Failed", "Invalid Parameter", + "In Use", "Method Parameters Checked - Job Started"} ] + uint32 SetPortLabel( + [ IN, Description("An SELinux port to change.") ] + LMI_SELinuxPort REF Target, + + [ IN, Description( + "Network ports to change. Can be specified as a single " + "port or as range, for example 1024-2048'.") ] + string PortRange, + + [ IN(false), OUT ] + LMI_SELinuxJob REF Job + ); + + [ Description( + "Set label on an SELinux file."), + ValueMap {"0", "1", "2", "3", "4", "5", "6", "4096"}, + Values {"Job Completed with No Error", "Not Supported", + "Unknown", "Timeout", "Failed", "Invalid Parameter", + "In Use", "Method Parameters Checked - Job Started"} ] + uint32 SetFileLabel( + [ IN, Description("An SELinux file to change.") ] + LMI_UnixFile REF Target, + + [ IN, Description("New label.") ] + string Label, + + [ IN(false), OUT ] + LMI_SELinuxJob REF Job + ); + + [ Description( + "Set a new value of an SELinux boolean."), + ValueMap {"0", "1", "2", "3", "4", "5", "6", "4096"}, + Values {"Job Completed with No Error", "Not Supported", + "Unknown", "Timeout", "Failed", "Invalid Parameter", + "In Use", "Method Parameters Checked - Job Started"} ] + uint32 SetBoolean( + [ IN, Description("An SELinux boolean to change.") ] + LMI_SELinuxBoolean REF Target, + + [ IN, Description("New value.") ] + boolean Value, + + [ IN, Description("If True, makes the new state persistent.") ] + boolean MakeDefault, + + [ IN(false), OUT ] + LMI_SELinuxJob REF Job + ); + + [ Description( + "Restore default SELinux security contexts on files.\n" + "There are two actions that can be taken on the specified files:\n" + " Report: List files whose SELinux label is different than the one specified by the policy.\n" + " Restore: Restore SELinux label on files to the respective values specified by the policy.\n"), + ValueMap {"0", "1", "2", "3", "4", "5", "6", "4096"}, + Values {"Job Completed with No Error", "Not Supported", + "Unknown", "Timeout", "Failed", "Invalid Parameter", + "In Use", "Method Parameters Checked - Job Started"} ] + uint32 RestoreLabels( + [ IN, OUT, Description("SELinux file(s) to change. On output, all files that have unexpected SELinux context") ] + LMI_UnixFile REF Target, + + [ IN, Description("Action to take on mislabeled files."), + ValueMap {"0", "1", ".."}, + Values {"Report", "Restore", "OpenLMI Reserved"} ] + uint16 Action, + + [ IN, Description( + "If True, restore labels recursively in case Target is a directory. " + "If Target is not a directory, this value is ignored.") ] + boolean Recursively, + + [ IN(false), OUT ] + LMI_SELinuxJob REF Job + ); +}; + +[ Version("0.0.1"), + Description("Association class the connects the SELinux system service with its elements."), + Association ] +class LMI_SELinuxServiceHasElement : CIM_Dependency +{ + [ Description("The SELinux element.") ] + LMI_SELinuxElement REF Antecedent; + + [ Description("The SELinux system service.") ] + LMI_SELinuxService REF Dependent; +}; + +[ Version("0.0.1"), + Association ] +class LMI_HostedSELinuxService : CIM_HostedService +{ +}; + + +/* Jobs. */ + +[ Version("0.0.1"), + Description("Generic SELinux provider job.") ] +class LMI_SELinuxJob : LMI_ConcreteJob +{ +}; + +[ Version("0.0.1"), + Association ] +class LMI_AffectedSELinuxJobElement : LMI_AffectedJobElement +{ +}; + +[ Version("0.0.1") ] +class LMI_SELinuxMethodResult : LMI_MethodResult { +}; + + +[ Version("0.0.1"), + Association ] +class LMI_AssociatedSELinuxJobMethodResult : LMI_AssociatedJobMethodResult { + [ Override ("Job") ] + LMI_SELinuxJob REF Job; + + [ Override ("JobParameters") ] + LMI_SELinuxMethodResult REF JobParameters; +}; + +[ Version("0.0.1"), + Indication ] +class LMI_SELinuxInstModification : CIM_InstModification { +}; diff --git a/mof/60_LMI_SELinux.reg b/mof/60_LMI_SELinux.reg new file mode 100644 index 0000000..e0b9cad --- /dev/null +++ b/mof/60_LMI_SELinux.reg @@ -0,0 +1,71 @@ +[LMI_SELinuxElement] + provider: LMI_SELinuxElement + location: cmpiLMI_SELinux + type: instance method + namespace: root/cimv2 + +[LMI_SELinuxBoolean] + provider: LMI_SELinuxBoolean + location: cmpiLMI_SELinux + type: instance method + namespace: root/cimv2 + +[LMI_SELinuxPort] + provider: LMI_SELinuxPort + location: cmpiLMI_SELinux + type: instance method + namespace: root/cimv2 + +[LMI_SELinuxService] + provider: LMI_SELinuxService + location: cmpiLMI_SELinux + type: instance method + namespace: root/cimv2 + +[LMI_SELinuxServiceHasElement] + provider: LMI_SELinuxServiceHasElement + location: cmpiLMI_SELinux + type: instance association + namespace: root/cimv2 + +[LMI_AffectedSELinuxJobElement] + provider: LMI_AffectedSELinuxJobElement + location: cmpiLMI_SELinux + type: instance association + namespace: root/cimv2 + +[LMI_HostedSELinuxService] + provider: LMI_HostedSELinuxService + location: cmpiLMI_SELinux + type: instance association + namespace: root/cimv2 + +[LMI_SELinuxJob] + provider: LMI_SELinuxJob + location: cmpiLMI_SELinux + type: instance method + namespace: root/cimv2 + +[LMI_AffectedSELinuxJobElement] + provider: LMI_AffectedSELinuxJobElement + location: cmpiLMI_SELinux + type: instance association + namespace: root/cimv2 + +[LMI_SELinuxMethodResult] + provider: LMI_SELinuxMethodResult + location: cmpiLMI_SELinux + type: instance method + namespace: root/cimv2 + +[LMI_AssociatedSELinuxJobMethodResult] + provider: LMI_AssociatedSELinuxJobMethodResult + location: cmpiLMI_SELinux + type: instance association + namespace: root/cimv2 + +[LMI_SELinuxInstModification] + provider: LMI_SELinuxInstModification + location: cmpiLMI_SELinux + type: instance indication + namespace: root/cimv2 diff --git a/mof/60_LMI_SELinux_MethodParameters.mof b/mof/60_LMI_SELinux_MethodParameters.mof new file mode 100644 index 0000000..191fc07 --- /dev/null +++ b/mof/60_LMI_SELinux_MethodParameters.mof @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + */ + +[ Version("0.0.1"), + Association, + Experimental ] +class __MethodParameters_SetSELinuxState +{ + uint16 NewState; + boolean MakeDefault; +}; + +[ Version("0.0.1"), + Association, + Experimental ] +class __MethodParameters_SetSELinuxState_Result: __MethodParameters_SetSELinuxState +{ + uint32 __ReturnValue; +}; + +[ Version("0.0.1"), + Association, + Experimental ] +class __MethodParameters_RestoreLabels +{ + LMI_UnixFile REF Target; + uint16 Action; + boolean Recursively; +}; + +[ Version("0.0.1"), + Association, + Experimental ] +class __MethodParameters_RestoreLabels_Result: __MethodParameters_RestoreLabels +{ + uint32 __ReturnValue; +}; + +[ Version("0.0.1"), + Association, + Experimental ] +class __MethodParameters_SetBoolean +{ + LMI_SELinuxBoolean REF Target; + boolean Value; + boolean MakeDefault; +}; + +[ Version("0.0.1"), + Association, + Experimental ] +class __MethodParameters_SetBoolean_Result: __MethodParameters_SetBoolean +{ + uint32 __ReturnValue; +}; + +[ Version("0.0.1"), + Association, + Experimental ] +class __MethodParameters_SetFileLabel +{ + LMI_UnixFile REF Target; + string Label; +}; + +[ Version("0.0.1"), + Association, + Experimental ] +class __MethodParameters_SetFileLabel_Result: __MethodParameters_SetFileLabel +{ + uint32 __ReturnValue; +}; + +[ Version("0.0.1"), + Association, + Experimental ] +class __MethodParameters_SetPortLabel +{ + LMI_SELinuxPort REF Target; + string PortRange; +}; + +[ Version("0.0.1"), + Association, + Experimental ] +class __MethodParameters_SetPortLabel_Result: __MethodParameters_SetPortLabel +{ + uint32 __ReturnValue; +}; \ No newline at end of file diff --git a/mof/CMakeLists.txt b/mof/CMakeLists.txt index 5e7aaa7..a53caf1 100644 --- a/mof/CMakeLists.txt +++ b/mof/CMakeLists.txt @@ -65,3 +65,7 @@ endif (WITH-LOCALE) if (WITH-SSSD) install(FILES 60_LMI_SSSD.mof DESTINATION share/openlmi-providers/) endif (WITH-SSSD) + +if (WITH-SELINUX) + install(FILES 60_LMI_SELinux.mof DESTINATION share/openlmi-providers/) +endif (WITH-SELINUX) diff --git a/src/.dir-locals.el b/src/.dir-locals.el new file mode 100644 index 0000000..c4aea32 --- /dev/null +++ b/src/.dir-locals.el @@ -0,0 +1,3 @@ +((c-mode + (c-basic-offset . 4) + (indent-tabs-mode))) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 58d72f5..59ff321 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,4 @@ include_directories( - ${CMAKE_CURRENT_SOURCE_DIR} libs/libopenlmi ${CMPI_INCLUDE_DIR} ${GLIB_INCLUDE_DIRS}) @@ -78,6 +77,10 @@ if (WITH-SSSD) add_subdirectory(sssd) endif (WITH-SSSD) +if (WITH-SELINUX) + add_subdirectory(selinux) +endif (WITH-SELINUX) + file(GLOB INIT_SKELS "*/lmi/*/__init__.skel") foreach(SKEL ${INIT_SKELS}) #TODO: find a way how to do it in cmake diff --git a/src/libs/jobmanager/lmi_job.c b/src/libs/jobmanager/lmi_job.c index 004e6a7..4943fa7 100644 --- a/src/libs/jobmanager/lmi_job.c +++ b/src/libs/jobmanager/lmi_job.c @@ -1680,7 +1680,7 @@ gboolean lmi_job_set_state(LmiJob *job, LmiJobStateEnum state, LmiJobStatusCodeEnum status_code, GVariant *result, - const gchar *error, ...) + const gchar *error) { GVariant *vold, *vnew; gchar *jobid = NULL; @@ -1749,12 +1749,7 @@ gboolean lmi_job_set_state(LmiJob *job, if (state == LMI_JOB_STATE_ENUM_COMPLETED && result) { job->priv->result = g_variant_ref_sink(result); } else if (state == LMI_JOB_STATE_ENUM_EXCEPTION && error) { - gchar *verror = NULL; - va_list args; - va_start(args, error); - vasprintf(&verror, error, args); - va_end(args); - job->priv->error = verror; + job->priv->error = g_strdup(error); } if (state == LMI_JOB_STATE_ENUM_COMPLETED) { job->priv->percent_complete = 100; @@ -1865,8 +1860,15 @@ gboolean lmi_job_finish_exception(LmiJob *job, LmiJobStatusCodeEnum status_code, const gchar *error, ...) { + gchar verror[BUFSIZ + 1]; + va_list args; + + va_start(args, error); + vsnprintf(verror, BUFSIZ, error, args); + va_end(args); + return lmi_job_set_state(job, LMI_JOB_STATE_ENUM_EXCEPTION, - status_code, NULL, error); + status_code, NULL, verror); } gboolean lmi_job_finish_terminate(LmiJob *job) diff --git a/src/libs/libopenlmi/openlmi.c b/src/libs/libopenlmi/openlmi.c index da660a5..55d96fd 100644 --- a/src/libs/libopenlmi/openlmi.c +++ b/src/libs/libopenlmi/openlmi.c @@ -783,6 +783,25 @@ err: return status; } +gchar *lmi_strip_str(gchar *str, gchar c) +{ + size_t len = strlen(str); + size_t i = 0; + size_t off = 0; + + while (i < len) { + if (str[i] == c) { + ++off; + } + else { + str[i - off] = str[i]; + } + ++i; + } + str[i - off] = '\0'; + return str; +} + /* vi: set et: */ /* Local Variables: */ /* indent-tabs-mode: nil */ diff --git a/src/libs/libopenlmi/openlmi.h b/src/libs/libopenlmi/openlmi.h index 8f4770c..38c8a07 100644 --- a/src/libs/libopenlmi/openlmi.h +++ b/src/libs/libopenlmi/openlmi.h @@ -262,6 +262,9 @@ void _lmi_debug(int level, const char *file, int line, const char *format, ...); +#define lmi_objectpath_to_string(o) \ + KChars((o)->ft->toString((o), NULL)) + #define lmi_return_with_chars(b, code, ...) \ do { \ char errmsg[BUFLEN]; \ @@ -291,6 +294,7 @@ void _lmi_debug(int level, const char *file, int line, const char *format, ...); return *(st); \ } while(0) +#define LMI_PARAM_ERROR_STR(what) "No " #what " has been specified" /** * Check required properties and their values. currently, only computer system @@ -328,6 +332,16 @@ const char *lmi_get_string_property_from_instance(const CMPIInstance *i, const c */ CMPIStatus lmi_data_to_string(const CMPIData *data, gchar **result); +/** + * Remove all occurrences of a character in a string. + * Works in place, therefore the input string must be allocated. + * + * @param str String to be modified. + * @param c All occurences of this character will be removed from the string. + * @retval Pointer to the modified string. + */ +gchar *lmi_strip_str(gchar *str, gchar c); + #endif /* OPENLMI_H */ /* vi: set et: */ diff --git a/src/logicalfile/CMakeLists.txt b/src/logicalfile/CMakeLists.txt index 0448f1e..755fe59 100644 --- a/src/logicalfile/CMakeLists.txt +++ b/src/logicalfile/CMakeLists.txt @@ -1,4 +1,3 @@ - set(PROVIDER_NAME LogicalFile) set(LIBRARY_NAME cmpiLMI_${PROVIDER_NAME}) set(MOF 50_LMI_LogicalFile.mof) diff --git a/src/logicalfile/file.h b/src/logicalfile/file.h index 4caf84d..00ba715 100644 --- a/src/logicalfile/file.h +++ b/src/logicalfile/file.h @@ -91,5 +91,4 @@ CMPIStatus stat_logicalfile_and_fill(const CMPIBroker *, logicalfile_t *, mode_t /* vi: set et: */ /* Local Variables: */ /* indent-tabs-mode: nil */ -/* c-backslash-max-column: 78 */ /* End: */ diff --git a/src/selinux/90_LMI_SELinux_Profile.mof.skel b/src/selinux/90_LMI_SELinux_Profile.mof.skel new file mode 100644 index 0000000..4252886 --- /dev/null +++ b/src/selinux/90_LMI_SELinux_Profile.mof.skel @@ -0,0 +1,22 @@ +instance of PG_ProviderProfileCapabilities +{ + CapabilityID = "@CLASS@"; + + ProviderModuleName = "cmpiLMI_SELinux"; + + ProviderName = "@CLASS@"; + + RegisteredProfile = 0; + + OtherRegisteredProfile = "OpenLMI-SELinux"; + OtherProfileOrganization = "OpenLMI"; + + ProfileVersion = "@VERSION@"; + + RegisteredSubProfiles = {}; + + ConformingElements = { + "@CLASS@" + }; +}; + diff --git a/src/selinux/CMakeLists.txt b/src/selinux/CMakeLists.txt new file mode 100644 index 0000000..5885af5 --- /dev/null +++ b/src/selinux/CMakeLists.txt @@ -0,0 +1,58 @@ +set(PROVIDER_NAME SELinux) +set(LIBRARY_NAME cmpiLMI_${PROVIDER_NAME}) +set(MOF 60_LMI_SELinux.mof) +# set(CONFIG conf/selinux.conf) +set(CIMPROVAGT_SCRIPT cmpiLMI_${PROVIDER_NAME}-cimprovagt) +set(LOGICALFILE_MOF "${CMAKE_SOURCE_DIR}/mof/50_LMI_LogicalFile.mof") + +set(provider_SRCS + selinux.c +) + +konkretcmpi_generate(${MOF} + CIM_PROVIDERS + CIM_HEADERS + CIM_CLASSES + ${OPENLMI_QUALIFIERS_MOF} + ${OPENLMI_JOBS_MOF} + ${LOGICALFILE_MOF} +) + +add_library(${LIBRARY_NAME} SHARED + ${provider_SRCS} + ${CIM_PROVIDERS} + ${CIM_HEADERS} +) + +pkg_check_modules(LIBSELINUX REQUIRED libselinux) +pkg_check_modules(LIBMANAGE REQUIRED libsemanage) +pkg_check_modules(LIBXML2 REQUIRED libxml-2.0) + +include_directories(${CMAKE_CURRENT_BINARY_DIR} + ${CMPI_INCLUDE_DIR} + ${LIBSELINUX_INCLUDE_DIRS} + ${LIBSEMANAGE_INCLUDE_DIRS} + ${LIBXML2_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/src/libs/indsender + ${CMAKE_SOURCE_DIR}/src/libs/jobmanager) + + +target_link_libraries(${LIBRARY_NAME} openlmicommon + ${KONKRETCMPI_LIBRARIES} + ${LIBSELINUX_LIBRARIES} + ${LIBMANAGE_LIBRARIES} + ${LIBXML2_LIBRARIES} + openlmijobmanager + openlmiindsender) + +# Create registration file +set(TARGET_MOF "${CMAKE_BINARY_DIR}/mof/90_LMI_SELinux_Profile.mof") +profile_mof_generate("90_LMI_SELinux_Profile.mof.skel" "${TARGET_MOF}" "${CIM_CLASSES}") + +cim_registration(${PROVIDER_NAME} ${LIBRARY_NAME} ${MOF} share/openlmi-providers "${TARGET_MOF}") + +install(TARGETS ${LIBRARY_NAME} DESTINATION lib${LIB_SUFFIX}/cmpi) +install(PROGRAMS ${CIMPROVAGT_SCRIPT} DESTINATION libexec/pegasus) +install(FILES ${TARGET_MOF} DESTINATION share/openlmi-providers/) +# install(FILES ${CONFIG} DESTINATION ${SYSCONF_INSTALL_DIR}/openlmi/selinux/) +install(FILES DESTINATION ${SYSCONF_INSTALL_DIR}/openlmi/selinux/) diff --git a/src/selinux/LMI_AffectedSELinuxJobElementProvider.c b/src/selinux/LMI_AffectedSELinuxJobElementProvider.c new file mode 100644 index 0000000..f5d6eca --- /dev/null +++ b/src/selinux/LMI_AffectedSELinuxJobElementProvider.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + * + */ + +#include +#include "LMI_AffectedSELinuxJobElement.h" + +static const CMPIBroker *_cb; + +static void LMI_AffectedSELinuxJobElementInitialize() +{ +} + +static CMPIStatus LMI_AffectedSELinuxJobElementCleanup( + CMPIInstanceMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_AffectedSELinuxJobElementEnumInstanceNames( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + return KDefaultEnumerateInstanceNames( + _cb, mi, cc, cr, cop); +} + +static CMPIStatus LMI_AffectedSELinuxJobElementEnumInstances( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_AffectedSELinuxJobElementGetInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + return KDefaultGetInstance( + _cb, mi, cc, cr, cop, properties); +} + +static CMPIStatus LMI_AffectedSELinuxJobElementCreateInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_AffectedSELinuxJobElementModifyInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci, + const char **properties) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_AffectedSELinuxJobElementDeleteInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_AffectedSELinuxJobElementExecQuery( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *lang, + const char *query) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_AffectedSELinuxJobElementAssociationCleanup( + CMPIAssociationMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_AffectedSELinuxJobElementAssociators( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *resultClass, + const char *role, + const char *resultRole, + const char **properties) +{ + return KDefaultAssociators( + _cb, + mi, + cc, + cr, + cop, + LMI_AffectedSELinuxJobElement_ClassName, + assocClass, + resultClass, + role, + resultRole, + properties); +} + +static CMPIStatus LMI_AffectedSELinuxJobElementAssociatorNames( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *resultClass, + const char *role, + const char *resultRole) +{ + return KDefaultAssociatorNames( + _cb, + mi, + cc, + cr, + cop, + LMI_AffectedSELinuxJobElement_ClassName, + assocClass, + resultClass, + role, + resultRole); +} + +static CMPIStatus LMI_AffectedSELinuxJobElementReferences( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *role, + const char **properties) +{ + return KDefaultReferences( + _cb, + mi, + cc, + cr, + cop, + LMI_AffectedSELinuxJobElement_ClassName, + assocClass, + role, + properties); +} + +static CMPIStatus LMI_AffectedSELinuxJobElementReferenceNames( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *role) +{ + return KDefaultReferenceNames( + _cb, + mi, + cc, + cr, + cop, + LMI_AffectedSELinuxJobElement_ClassName, + assocClass, + role); +} + +CMInstanceMIStub( + LMI_AffectedSELinuxJobElement, + LMI_AffectedSELinuxJobElement, + _cb, + LMI_AffectedSELinuxJobElementInitialize()) + +CMAssociationMIStub( + LMI_AffectedSELinuxJobElement, + LMI_AffectedSELinuxJobElement, + _cb, + LMI_AffectedSELinuxJobElementInitialize()) + +KONKRET_REGISTRATION( + "root/cimv2", + "LMI_AffectedSELinuxJobElement", + "LMI_AffectedSELinuxJobElement", + "instance association") diff --git a/src/selinux/LMI_AssociatedSELinuxJobMethodResultProvider.c b/src/selinux/LMI_AssociatedSELinuxJobMethodResultProvider.c new file mode 100644 index 0000000..f5d31e6 --- /dev/null +++ b/src/selinux/LMI_AssociatedSELinuxJobMethodResultProvider.c @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + * + */ + +#include +#include "LMI_AssociatedSELinuxJobMethodResult.h" +#include "selinux.h" + +static const CMPIBroker *_cb; + +static void LMI_AssociatedSELinuxJobMethodResultInitialize(const CMPIContext *ctx) +{ + selinux_provider_init(LMI_AssociatedSELinuxJobMethodResult_ClassName, FALSE, provider_config_defaults, _cb, ctx); +} + +static CMPIStatus LMI_AssociatedSELinuxJobMethodResultCleanup( + CMPIInstanceMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + return jobmgr_cleanup(LMI_AssociatedSELinuxJobMethodResult_ClassName); +} + +static CMPIStatus LMI_AssociatedSELinuxJobMethodResultEnumInstanceNames( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + return KDefaultEnumerateInstanceNames( + _cb, mi, cc, cr, cop); +} + +static CMPIStatus LMI_AssociatedSELinuxJobMethodResultEnumInstances( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + CMPIStatus status = {CMPI_RC_OK, NULL}; + CMPIObjectPath *jop, *mrop; + LmiJob *job = NULL; + guint *jobnum; + gchar *jobid = NULL; + guint *jobnums = jobmgr_get_job_numbers(NULL); + + if (jobnums == NULL) + goto err; + + for (jobnum = jobnums; *jobnum != 0; ++jobnum) { + job = jobmgr_get_job_by_number(*jobnum); + if (!job) { + /* job may have been deleted since numbers query */ + continue; + } + + if (lmi_job_get_method_name(job) == NULL) { + /* Method result can not be created for job with MethodName unset. + * Such a job is too new. */ + goto next; + } + + jobid = lmi_job_get_jobid(job); + + status = jobmgr_job_to_cim_op(job, &jop); + if (status.rc) { + if (status.msg) { + lmi_warn("Failed to make job object path out of job \"%s\": %s", + jobid, CMGetCharsPtr(status.msg, NULL)); + } else { + lmi_warn("Failed to make job object path out of job \"%s\"", jobid); + } + goto next; + } + + status = jobmgr_job_to_method_result_op(job, NULL, &mrop); + if (status.rc) { + if (status.msg) { + lmi_warn("Failed to make method result object path" + " out of job \"%s\": %s", jobid, + CMGetCharsPtr(status.msg, NULL)); + } else { + lmi_warn("Failed to make method result object path" + " out of job \"%s\"", jobid); + } + CMRelease(jop); + goto next; + } + + LMI_AssociatedSELinuxJobMethodResult w; + LMI_AssociatedSELinuxJobMethodResult_Init(&w, _cb, KNameSpace(cop)); + + LMI_AssociatedSELinuxJobMethodResult_SetObjectPath_Job(&w, jop); + LMI_AssociatedSELinuxJobMethodResult_SetObjectPath_JobParameters(&w, mrop); + + KReturnInstance(cr, w); +next: + g_free(jobid); + jobid = NULL; + g_clear_object(&job); + } + g_free(jobnums); + + return status; + +err: + if (!status.rc) { + lmi_error("Memory allocation failed"); + KSetStatus(&status, ERR_FAILED); + } + return status; +} + +static CMPIStatus LMI_AssociatedSELinuxJobMethodResultGetInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + return KDefaultGetInstance( + _cb, mi, cc, cr, cop, properties); +} + +static CMPIStatus LMI_AssociatedSELinuxJobMethodResultCreateInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_AssociatedSELinuxJobMethodResultModifyInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci, + const char**properties) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_AssociatedSELinuxJobMethodResultDeleteInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_AssociatedSELinuxJobMethodResultExecQuery( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *lang, + const char *query) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_AssociatedSELinuxJobMethodResultAssociationCleanup( + CMPIAssociationMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_AssociatedSELinuxJobMethodResultAssociators( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *resultClass, + const char *role, + const char *resultRole, + const char **properties) +{ + return KDefaultAssociators( + _cb, + mi, + cc, + cr, + cop, + LMI_AssociatedSELinuxJobMethodResult_ClassName, + assocClass, + resultClass, + role, + resultRole, + properties); +} + +static CMPIStatus LMI_AssociatedSELinuxJobMethodResultAssociatorNames( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *resultClass, + const char *role, + const char *resultRole) +{ + return KDefaultAssociatorNames( + _cb, + mi, + cc, + cr, + cop, + LMI_AssociatedSELinuxJobMethodResult_ClassName, + assocClass, + resultClass, + role, + resultRole); +} + +static CMPIStatus LMI_AssociatedSELinuxJobMethodResultReferences( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *role, + const char **properties) +{ + return KDefaultReferences( + _cb, + mi, + cc, + cr, + cop, + LMI_AssociatedSELinuxJobMethodResult_ClassName, + assocClass, + role, + properties); +} + +static CMPIStatus LMI_AssociatedSELinuxJobMethodResultReferenceNames( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *role) +{ + return KDefaultReferenceNames( + _cb, + mi, + cc, + cr, + cop, + LMI_AssociatedSELinuxJobMethodResult_ClassName, + assocClass, + role); +} + +CMInstanceMIStub( + LMI_AssociatedSELinuxJobMethodResult, + LMI_AssociatedSELinuxJobMethodResult, + _cb, + LMI_AssociatedSELinuxJobMethodResultInitialize(ctx)) + +CMAssociationMIStub( + LMI_AssociatedSELinuxJobMethodResult, + LMI_AssociatedSELinuxJobMethodResult, + _cb, + LMI_AssociatedSELinuxJobMethodResultInitialize(ctx)) + +KONKRET_REGISTRATION( + "root/cimv2", + "LMI_AssociatedSELinuxJobMethodResult", + "LMI_AssociatedSELinuxJobMethodResult", + "instance association") diff --git a/src/selinux/LMI_HostedSELinuxServiceProvider.c b/src/selinux/LMI_HostedSELinuxServiceProvider.c new file mode 100644 index 0000000..1bc5b9c --- /dev/null +++ b/src/selinux/LMI_HostedSELinuxServiceProvider.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + * + */ + +#include +#include "LMI_HostedSELinuxService.h" +#include "selinux.h" + +static const CMPIBroker *_cb; + +static void LMI_HostedSELinuxServiceInitialize(const CMPIContext *ctx) +{ + lmi_init(provider_name, _cb, ctx, provider_config_defaults); +} + +static CMPIStatus LMI_HostedSELinuxServiceCleanup( + CMPIInstanceMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_HostedSELinuxServiceEnumInstanceNames( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + return KDefaultEnumerateInstanceNames( + _cb, mi, cc, cr, cop); +} + +static CMPIStatus LMI_HostedSELinuxServiceEnumInstances( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + const char *ns = KNameSpace(cop); + CMPIStatus st; + LMI_HostedSELinuxService lmi_hosted; + LMI_SELinuxService lmi_service; + CMPIObjectPath *o; + + LMI_HostedSELinuxService_Init(&lmi_hosted, _cb, ns); + LMI_HostedSELinuxService_SetObjectPath_Antecedent(&lmi_hosted, lmi_get_computer_system_safe(cc)); + + init_selinux_service(&lmi_service, _cb, ns); + + o = LMI_SELinuxService_ToObjectPath(&lmi_service, &st); + lmi_return_if_status_not_ok(st); + LMI_HostedSELinuxService_SetObjectPath_Dependent(&lmi_hosted, o); + + KReturnInstance(cr, lmi_hosted); + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_HostedSELinuxServiceGetInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + return KDefaultGetInstance( + _cb, mi, cc, cr, cop, properties); +} + +static CMPIStatus LMI_HostedSELinuxServiceCreateInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_HostedSELinuxServiceModifyInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci, + const char**properties) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_HostedSELinuxServiceDeleteInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_HostedSELinuxServiceExecQuery( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *lang, + const char *query) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_HostedSELinuxServiceAssociationCleanup( + CMPIAssociationMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_HostedSELinuxServiceAssociators( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *resultClass, + const char *role, + const char *resultRole, + const char **properties) +{ + return KDefaultAssociators( + _cb, + mi, + cc, + cr, + cop, + LMI_HostedSELinuxService_ClassName, + assocClass, + resultClass, + role, + resultRole, + properties); +} + +static CMPIStatus LMI_HostedSELinuxServiceAssociatorNames( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *resultClass, + const char *role, + const char *resultRole) +{ + return KDefaultAssociatorNames( + _cb, + mi, + cc, + cr, + cop, + LMI_HostedSELinuxService_ClassName, + assocClass, + resultClass, + role, + resultRole); +} + +static CMPIStatus LMI_HostedSELinuxServiceReferences( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *role, + const char **properties) +{ + return KDefaultReferences( + _cb, + mi, + cc, + cr, + cop, + LMI_HostedSELinuxService_ClassName, + assocClass, + role, + properties); +} + +static CMPIStatus LMI_HostedSELinuxServiceReferenceNames( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *role) +{ + return KDefaultReferenceNames( + _cb, + mi, + cc, + cr, + cop, + LMI_HostedSELinuxService_ClassName, + assocClass, + role); +} + +CMInstanceMIStub( + LMI_HostedSELinuxService, + LMI_HostedSELinuxService, + _cb, + LMI_HostedSELinuxServiceInitialize(ctx)) + +CMAssociationMIStub( + LMI_HostedSELinuxService, + LMI_HostedSELinuxService, + _cb, + LMI_HostedSELinuxServiceInitialize(ctx)) + +KONKRET_REGISTRATION( + "root/cimv2", + "LMI_HostedSELinuxService", + "LMI_HostedSELinuxService", + "instance association") diff --git a/src/selinux/LMI_SELinuxBooleanProvider.c b/src/selinux/LMI_SELinuxBooleanProvider.c new file mode 100644 index 0000000..1b7576c --- /dev/null +++ b/src/selinux/LMI_SELinuxBooleanProvider.c @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + * + * Implementation note: + * + * Casts to/from char/gchar to/from xmlChar are necessary, because xmlChar is + * defined as an unsigned char. + */ + +#include +#include +#include +#include +#include "LMI_SELinuxBoolean.h" +#include "selinux.h" + + + +/** + * Populate boolean description hash with descriptions of all SELinux booleans + * specified by an XPath expression. + * + * This function parses /usr/share/selinux/devel/policy.xml to get boolean descriptions. + * There's currently no way how to get this information via libselinux. + * + * @param desc_hash GHashTable pointer to the boolean description hash. + * @param xpath_str XPath expression. + * @retval 0 on success, otherwise -1. + */ +static int boolean_desc_hash_add_from_xpath(GHashTable *desc_hash, const xmlChar *xpath_str) +{ + xmlDoc *doc = NULL; + xmlXPathContextPtr xpath_ctx = NULL; + xmlXPathObjectPtr xpath_obj = NULL; + xmlNodeSetPtr nodes; + xmlNodePtr p_node; + + lmi_info("Initializing XML parser."); + xmlInitParser(); + + doc = xmlParseFile("/usr/share/selinux/devel/policy.xml"); + if (doc == NULL) { + lmi_warn("Couldn't read SELinux policy"); + return -1; + } + + xpath_ctx = xmlXPathNewContext(doc); + if(xpath_ctx == NULL) { + lmi_warn("Unable to create new XPath context"); + xmlFreeDoc(doc); + return -1; + } + + xpath_obj = xmlXPathEvalExpression(xpath_str, xpath_ctx); + if(xpath_obj == NULL) { + lmi_warn("Unable to evaluate xpath expression: %s", xpath_str); + xmlXPathFreeContext(xpath_ctx); + xmlFreeDoc(doc); + return -1; + } + + xmlChar *parent_name; + xmlChar *content; + nodes = xpath_obj->nodesetval; + for(int i = 0; i < nodes->nodeNr; ++i) { + if(nodes->nodeTab[i]->type == XML_ELEMENT_NODE) { + p_node = nodes->nodeTab[i]; + parent_name = xmlGetProp(p_node->parent->parent, (const xmlChar *)"name"); + content = p_node->children->content; + lmi_strip_str((gchar *)content, '\n'); + + lmi_info("Adding SELinux boolean to hash: %s = %s", parent_name, content); + g_hash_table_insert(desc_hash, + g_strdup((gchar *)parent_name), + g_strdup((gchar *)content)); + + xmlFree(parent_name); + } + } + + xmlXPathFreeObject(xpath_obj); + xmlXPathFreeContext(xpath_ctx); + xmlFreeDoc(doc); + lmi_info("Cleaning up XML parser."); + xmlCleanupParser(); + return 0; +} + +/** + * Look up a SELinux boolean description. + * + * @param desc_hash GHashTable pointer to the boolean description hash. + * @param boolean Boolean name, description of which will be returned. + * @retval Boolean description. + */ +static const char *boolean_desc_hash_lookup(GHashTable *desc_hash, const char *boolean) +{ + return g_hash_table_lookup(desc_hash, boolean); +} + +/** + * Initialize a hash table containing SELinux boolean descriptions. + * If anything fails, NULL is returned. + * + * @retval GHashTable pointer to the initialized hash table, or NULL. + */ +static GHashTable *boolean_desc_hash_init() +{ + int rc; + GHashTable *desc_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + if (desc_hash == NULL) return NULL; + + rc = boolean_desc_hash_add_from_xpath(desc_hash, (const xmlChar *)"/policy/layer/module/tunable/desc/p"); + if (rc < 0) return NULL; + rc = boolean_desc_hash_add_from_xpath(desc_hash, (const xmlChar *)"/policy/layer/module/bool/desc/p"); + if (rc < 0) return NULL; + rc = boolean_desc_hash_add_from_xpath(desc_hash, (const xmlChar *)"/policy/layer/bool/desc/p"); + if (rc < 0) return NULL; + rc = boolean_desc_hash_add_from_xpath(desc_hash, (const xmlChar *)"/policy/tunable/desc/p"); + if (rc < 0) return NULL; + + return desc_hash; +} + +/** + * Free SELinux boolean description hash table. + * + * @param desc_hash GHashTable pointer to the boolean description hash. + */ +static void boolean_desc_hash_free(GHashTable *desc_hash) +{ + g_hash_table_unref(desc_hash); +} + + + +static const CMPIBroker *_cb = NULL; + +static void LMI_SELinuxBooleanInitialize(const CMPIContext *ctx) +{ + lmi_init(provider_name, _cb, ctx, provider_config_defaults); +} + +static CMPIStatus LMI_SELinuxBooleanCleanup( + CMPIInstanceMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxBooleanEnumInstanceNames( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + return KDefaultEnumerateInstanceNames( + _cb, mi, cc, cr, cop); +} + + +static CMPIStatus LMI_SELinuxBooleanEnumInstances( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + const char *ns = KNameSpace(cop); + int rc; + int len; + char **booleans; + + GHashTable *b_hash = boolean_desc_hash_init(); + const char *b_val; + + rc = security_get_boolean_names(&booleans, &len); + /* this function is useless without available booleans, so make error fatal */ + if (rc < 0) { + if (b_hash) { + boolean_desc_hash_free(b_hash); + } + lmi_return_with_chars(_cb, CMPI_RC_ERR_FAILED, "Could not get SELinux booleans"); + } + + for (int i = 0; i < len; i++) { + LMI_SELinuxBoolean lmi_bool; + + char instance_id[BUFLEN]; + snprintf(instance_id, BUFLEN, + LMI_ORGID ":" LMI_SELinuxBoolean_ClassName ":%s", booleans[i]); + + LMI_SELinuxBoolean_Init(&lmi_bool, _cb, ns); + LMI_SELinuxBoolean_Set_InstanceID(&lmi_bool, instance_id); + LMI_SELinuxBoolean_Set_ElementName(&lmi_bool, booleans[i]); + LMI_SELinuxBoolean_Set_State(&lmi_bool, security_get_boolean_active(booleans[i])); + LMI_SELinuxBoolean_Set_DefaultState(&lmi_bool, security_get_boolean_pending(booleans[i])); + b_val = (b_hash) ? boolean_desc_hash_lookup(b_hash, booleans[i]) : "N/A"; + LMI_SELinuxBoolean_Set_Description(&lmi_bool, b_val); + + KReturnInstance(cr, lmi_bool); + } + + if (b_hash) { + boolean_desc_hash_free(b_hash); + } + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxBooleanGetInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + return KDefaultGetInstance( + _cb, mi, cc, cr, cop, properties); +} + +static CMPIStatus LMI_SELinuxBooleanCreateInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxBooleanModifyInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci, + const char **properties) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxBooleanDeleteInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxBooleanExecQuery( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *lang, + const char *query) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +CMInstanceMIStub( + LMI_SELinuxBoolean, + LMI_SELinuxBoolean, + _cb, + LMI_SELinuxBooleanInitialize(ctx)) + +static CMPIStatus LMI_SELinuxBooleanMethodCleanup( + CMPIMethodMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxBooleanInvokeMethod( + CMPIMethodMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *meth, + const CMPIArgs *in, + CMPIArgs *out) +{ + return LMI_SELinuxBoolean_DispatchMethod( + _cb, mi, cc, cr, cop, meth, in, out); +} + +CMMethodMIStub( + LMI_SELinuxBoolean, + LMI_SELinuxBoolean, + _cb, + LMI_SELinuxBooleanInitialize(ctx)) + +KONKRET_REGISTRATION( + "root/cimv2", + "LMI_SELinuxBoolean", + "LMI_SELinuxBoolean", + "instance method") diff --git a/src/selinux/LMI_SELinuxInstModificationProvider.c b/src/selinux/LMI_SELinuxInstModificationProvider.c new file mode 100644 index 0000000..577f64c --- /dev/null +++ b/src/selinux/LMI_SELinuxInstModificationProvider.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + * + */ + +#include +#include "LMI_SELinuxInstModification.h" +#include "ind_sender.h" +#include "selinux.h" + +static const CMPIBroker *_cb = NULL; + +static void LMI_SELinuxInstModificationInitialize(const CMPIContext *ctx) +{ + selinux_provider_init(LMI_SELinuxInstModification_ClassName, FALSE, provider_config_defaults, _cb, ctx); +} + +static CMPIStatus LMI_SELinuxInstModificationIndicationCleanup( + CMPIIndicationMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + return jobmgr_cleanup(LMI_SELinuxInstModification_ClassName); +} + +static CMPIStatus LMI_SELinuxInstModificationAuthorizeFilter( + CMPIIndicationMI *mi, + const CMPIContext *cc, + const CMPISelectExp *se, + const char *ns, + const CMPIObjectPath *op, + const char *user) +{ + CMPIStatus status = {CMPI_RC_OK, NULL}; + + if (!ind_sender_authorize_filter(se, LMI_SELinuxJob_ClassName, op, user)) + KSetStatus2(_cb, &status, ERR_FAILED, "Failed to authorize filter!"); + return status; +} + +static CMPIStatus LMI_SELinuxInstModificationMustPoll( + CMPIIndicationMI *mi, + const CMPIContext *cc, + const CMPISelectExp *se, + const char *ns, + const CMPIObjectPath *op) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxInstModificationActivateFilter( + CMPIIndicationMI *mi, + const CMPIContext *cc, + const CMPISelectExp *se, + const char *ns, + const CMPIObjectPath *op, + CMPIBoolean firstActivation) +{ + return ind_sender_activate_filter(se, LMI_SELinuxJob_ClassName, op, firstActivation); +} + +static CMPIStatus LMI_SELinuxInstModificationDeActivateFilter( + CMPIIndicationMI *mi, + const CMPIContext *cc, + const CMPISelectExp *se, + const char *ns, + const CMPIObjectPath *op, + CMPIBoolean lastActivation) +{ + return ind_sender_deactivate_filter(se, LMI_SELinuxJob_ClassName, op, lastActivation); +} + +static CMPIStatus LMI_SELinuxInstModificationEnableIndications( + CMPIIndicationMI *mi, + const CMPIContext *cc) +{ + enable_indications(); + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxInstModificationDisableIndications( + CMPIIndicationMI *mi, + const CMPIContext *cc) +{ + disable_indications(); + CMReturn(CMPI_RC_OK); +} + +CMIndicationMIStub( + LMI_SELinuxInstModification, + LMI_SELinuxInstModification, + _cb, + LMI_SELinuxInstModificationInitialize(ctx)) + +KONKRET_REGISTRATION( + "root/cimv2", + "LMI_SELinuxInstModification", + "LMI_SELinuxInstModification", + "indication") diff --git a/src/selinux/LMI_SELinuxJobProvider.c b/src/selinux/LMI_SELinuxJobProvider.c new file mode 100644 index 0000000..945eadf --- /dev/null +++ b/src/selinux/LMI_SELinuxJobProvider.c @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + * + */ + +#include +#include "LMI_SELinuxJob.h" +#include "selinux.h" +#include "job_manager.h" + +static const CMPIBroker *_cb = NULL; + +static void LMI_SELinuxJobInitialize(const CMPIContext *ctx) +{ + selinux_provider_init(LMI_SELinuxJob_ClassName, FALSE, provider_config_defaults, _cb, ctx); +} + +static CMPIStatus LMI_SELinuxJobCleanup( + CMPIInstanceMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + return jobmgr_cleanup(LMI_SELinuxJob_ClassName); +} + +static CMPIStatus LMI_SELinuxJobEnumInstanceNames( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + return KDefaultEnumerateInstanceNames( + _cb, mi, cc, cr, cop); +} + +static CMPIStatus LMI_SELinuxJobEnumInstances( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + CMPIStatus status = {CMPI_RC_OK, NULL}; + gchar *jobid; + LmiJob *job; + CMPIInstance *inst; + guint *jobnum; + guint *jobnums = jobmgr_get_job_numbers(NULL); + + if (jobnums == NULL) + CMReturn(CMPI_RC_ERR_FAILED); + jobnum = jobnums; + + while (*jobnum) { + job = jobmgr_get_job_by_number(*jobnum); + if (!job) + /* job may have been deleted since numbers query */ + continue; /* TODO this won't work, will cycle forever */ + + if (!LMI_IS_JOB(job)) { + g_object_unref(job); + continue; /* TODO this won't work, will cycle forever */ + } + + + status = jobmgr_job_to_cim_instance(job, &inst); + g_object_unref(job); + if (status.rc != CMPI_RC_OK) { + jobid = lmi_job_get_jobid(job); + if (status.msg) { + lmi_error("Failed to make cim instance out of job \"%s\": %s", + jobid, CMGetCharsPtr(status.msg, NULL)); + } else { + lmi_error("Failed to make cim instance out of job \"%s\"", jobid); + } + g_free(jobid); + ++jobnum; + continue; + } + + CMReturnInstance(cr, inst); + ++jobnum; + } + + g_free(jobnums); + + KSetStatus(&status, OK); + return status; +} + +static CMPIStatus LMI_SELinuxJobGetInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + return KDefaultGetInstance( + _cb, mi, cc, cr, cop, properties); +} + +static CMPIStatus LMI_SELinuxJobCreateInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxJobModifyInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci, + const char **properties) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxJobDeleteInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxJobExecQuery( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *lang, + const char *query) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +CMInstanceMIStub( + LMI_SELinuxJob, + LMI_SELinuxJob, + _cb, + LMI_SELinuxJobInitialize(ctx)) + +static CMPIStatus LMI_SELinuxJobMethodCleanup( + CMPIMethodMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxJobInvokeMethod( + CMPIMethodMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *meth, + const CMPIArgs *in, + CMPIArgs *out) +{ + return LMI_SELinuxJob_DispatchMethod( + _cb, mi, cc, cr, cop, meth, in, out); +} + +CMMethodMIStub( + LMI_SELinuxJob, + LMI_SELinuxJob, + _cb, + LMI_SELinuxJobInitialize(ctx)) + +KUint32 LMI_SELinuxJob_KillJob( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxJobRef *self, + const KBoolean *DeleteOnKill, + CMPIStatus *status) +{ + KUint32 result = KUINT32_INIT; + + KSetStatus(status, ERR_NOT_SUPPORTED); + return result; +} + +KUint32 LMI_SELinuxJob_RequestStateChange( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxJobRef *self, + const KUint16 *RequestedState, + const KDateTime *TimeoutPeriod, + CMPIStatus *status) +{ + KUint32 result = KUINT32_INIT; + + KSetStatus(status, ERR_NOT_SUPPORTED); + return result; +} + +KUint32 LMI_SELinuxJob_GetError( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxJobRef *self, + KInstance *Error, + CMPIStatus *status) +{ + CMPIObjectPath *cop; + LmiJob *job = NULL; + CMPIInstance *inst = NULL; + KUint32 result = KUINT32_INIT; + + if ((cop = LMI_SELinuxJobRef_ToObjectPath(self, status)) == NULL) + goto err; + *status = jobmgr_get_job_matching_op(cop, &job); + CMRelease(cop); + if (status->rc) { + KSetStatus(status, ERR_NOT_FOUND); + goto done; + } + JOB_CRITICAL_BEGIN(job); + if (lmi_job_get_state(job) == LMI_JOB_STATE_ENUM_EXCEPTION) { + *status = jobmgr_job_to_cim_error(job, &inst); + if (status->rc) + goto critical_end_err; + KInstance_Set(Error, inst); + } + JOB_CRITICAL_END(job); + g_clear_object(&job); + + KUint32_Set(&result, 0); + goto done; + +critical_end_err: + JOB_CRITICAL_END(job); + g_clear_object(&job); +err: + if (!status->rc) { + lmi_error("Memory allocation failed"); + KSetStatus(status, ERR_FAILED); + } +done: + return result; +} + +KUint32 LMI_SELinuxJob_GetErrors( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxJobRef *self, + KInstanceA *errors, + CMPIStatus *status) +{ + KUint32 result = KUINT32_INIT; + KInstance error; + + KInstance_Null(&error); + LMI_SELinuxJob_GetError(cb, mi, context, self, &error, status); + if (status->rc) + return result; + if (KHasValue(&error)) { + KInstanceA_Init(errors, cb, 1); + KInstanceA_Set(errors, 0, (CMPIInstance *) error.value); + } else { + KInstanceA_Init(errors, cb, 0); + } + KUint32_Set(&result, 0); + return result; +} + +KUint32 LMI_SELinuxJob_ResumeWithAction( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxJobRef *self, + CMPIStatus *status) +{ + KUint32 result = KUINT32_INIT; + + KSetStatus(status, ERR_NOT_SUPPORTED); + return result; +} + +KUint32 LMI_SELinuxJob_ResumeWithInput( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxJobRef *self, + const KStringA *Inputs, + CMPIStatus *status) +{ + KUint32 result = KUINT32_INIT; + + KSetStatus(status, ERR_NOT_SUPPORTED); + return result; +} + +KONKRET_REGISTRATION( + "root/cimv2", + "LMI_SELinuxJob", + "LMI_SELinuxJob", + "instance method") diff --git a/src/selinux/LMI_SELinuxMethodResultProvider.c b/src/selinux/LMI_SELinuxMethodResultProvider.c new file mode 100644 index 0000000..5bbf310 --- /dev/null +++ b/src/selinux/LMI_SELinuxMethodResultProvider.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + * + */ + +#include +#include "LMI_SELinuxMethodResult.h" +#include "selinux.h" +#include "job_manager.h" + +static const CMPIBroker *_cb = NULL; + +static void LMI_SELinuxMethodResultInitialize(const CMPIContext *ctx) +{ + selinux_provider_init(LMI_SELinuxMethodResult_ClassName, FALSE, provider_config_defaults, _cb, ctx); +} + +static CMPIStatus LMI_SELinuxMethodResultCleanup( + CMPIInstanceMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + return jobmgr_cleanup(LMI_SELinuxMethodResult_ClassName); +} + +static CMPIStatus LMI_SELinuxMethodResultEnumInstanceNames( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + return KDefaultEnumerateInstanceNames( + _cb, mi, cc, cr, cop); +} + +static CMPIStatus LMI_SELinuxMethodResultEnumInstances( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + CMPIStatus status = {CMPI_RC_OK, NULL}; + CMPIInstance *inst; + LmiJob *job; + guint *jobnum; + guint *jobnums = jobmgr_get_job_numbers(NULL); + + if (jobnums == NULL) + CMReturn(CMPI_RC_ERR_FAILED); + + for (jobnum = jobnums; *jobnum != 0; ++jobnum) { + job = jobmgr_get_job_by_number(*jobnum); + if (!job) + /* job may have been deleted since numbers query */ + continue; + + status = jobmgr_job_to_method_result_instance(job, NULL, &inst); + if (status.rc) { + gchar *jobid = lmi_job_get_jobid(job); + if (status.msg) { + lmi_warn("Failed to make method result instance out of job" + " \"%s\": %s", jobid, CMGetCharsPtr(status.msg, NULL)); + } else { + lmi_warn("Failed to make method result instance out of job" + " \"%s\"", jobid); + } + g_free(jobid); + } else { + CMReturnInstance(cr, inst); + } + g_clear_object(&job); + } + + g_free(jobnums); + + return status; +} + +static CMPIStatus LMI_SELinuxMethodResultGetInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + return KDefaultGetInstance( + _cb, mi, cc, cr, cop, properties); +} + +static CMPIStatus LMI_SELinuxMethodResultCreateInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxMethodResultModifyInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci, + const char **properties) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxMethodResultDeleteInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxMethodResultExecQuery( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *lang, + const char *query) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +CMInstanceMIStub( + LMI_SELinuxMethodResult, + LMI_SELinuxMethodResult, + _cb, + LMI_SELinuxMethodResultInitialize(ctx)) + +static CMPIStatus LMI_SELinuxMethodResultMethodCleanup( + CMPIMethodMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxMethodResultInvokeMethod( + CMPIMethodMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *meth, + const CMPIArgs *in, + CMPIArgs *out) +{ + return LMI_SELinuxMethodResult_DispatchMethod( + _cb, mi, cc, cr, cop, meth, in, out); +} + +CMMethodMIStub( + LMI_SELinuxMethodResult, + LMI_SELinuxMethodResult, + _cb, + LMI_SELinuxMethodResultInitialize(ctx)) + +KONKRET_REGISTRATION( + "root/cimv2", + "LMI_SELinuxMethodResult", + "LMI_SELinuxMethodResult", + "instance method") diff --git a/src/selinux/LMI_SELinuxPortProvider.c b/src/selinux/LMI_SELinuxPortProvider.c new file mode 100644 index 0000000..0a0582f --- /dev/null +++ b/src/selinux/LMI_SELinuxPortProvider.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + * + */ + +#include +#include "LMI_SELinuxPort.h" +#include "selinux.h" + +static const CMPIBroker *_cb = NULL; + + + +/** + * Contruct a new string from a port instance. + * + * Libsemanage returns port ranges as ints. + * + * @param port Libsemanage port. + * @retval Newly allocated string with a port range. Must be freed by the caller. + */ +static char *get_port_range_str(const semanage_port_t *port) +{ + int portnum_l, portnum_h; + char *result; + + portnum_l = semanage_port_get_low(port); + portnum_h = semanage_port_get_high(port); + if (portnum_l == portnum_h) { + if (asprintf(&result, "%d", portnum_l) < 0) { + return NULL; + } + } + else { + if (asprintf(&result, "%d-%d", portnum_l, portnum_h) < 0) { + return NULL; + } + } + + return result; +} + +/** + * Handler to be executed for each port within the port hash. + * + * Creates LMI_SELinuxPort instances and sends them to the broker. + * + * @param key InstanceID of soon-to-be LMI_SELinuxPort. + * @param value Linked list containing all libsemanage ports associated with the InstanceID. + * @param user_data CMPI context data. + */ +static void port_hash_foreach_handler(gpointer key, gpointer value, gpointer user_data) +{ + const struct cmpi_data *data = (const struct cmpi_data *)user_data; + const CMPIResult *cr = data->cr; + const char *ns = data->namespace; + + semanage_context_t *ctx; + const char *type, *role, *user; + char contextstr[BUFLEN]; + + const char *instance_id = (const char *)key; + GSList *ports = (GSList *)value; + semanage_port_t *port = (semanage_port_t *)g_slist_nth_data(ports, 0); + int n_ports = g_slist_length(ports); + + ctx = semanage_port_get_con(port); + type = semanage_context_get_type(ctx); + role = semanage_context_get_role(ctx); + user = semanage_context_get_user(ctx); + snprintf(contextstr, BUFLEN, "%s:%s:%s", user, role, type); + + LMI_SELinuxPort lmi_port; + LMI_SELinuxPort_Init(&lmi_port, _cb, ns); + LMI_SELinuxPort_Set_InstanceID(&lmi_port, instance_id); + LMI_SELinuxPort_Set_ElementName(&lmi_port, type); + LMI_SELinuxPort_Set_Protocol(&lmi_port, semanage_port_get_proto(port)); + LMI_SELinuxPort_Set_SELinuxContext(&lmi_port, contextstr); + + LMI_SELinuxPort_Init_Ports(&lmi_port, n_ports); + for (int i = 0; i < n_ports; i++) { + semanage_port_t *port = (semanage_port_t *)g_slist_nth_data(ports, i); + char *p_str = get_port_range_str(port); + LMI_SELinuxPort_Set_Ports(&lmi_port, i, p_str); + free(p_str); + } + + CMReturnInstance(cr, LMI_SELinuxPort_ToInstance(&lmi_port, NULL)); +} + + + +static void LMI_SELinuxPortInitialize(const CMPIContext *ctx) +{ + lmi_init(provider_name, _cb, ctx, provider_config_defaults); +} + +static CMPIStatus LMI_SELinuxPortCleanup( + CMPIInstanceMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxPortEnumInstanceNames( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + return KDefaultEnumerateInstanceNames( + _cb, mi, cc, cr, cop); +} + +static CMPIStatus LMI_SELinuxPortEnumInstances( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + const char *ns = KNameSpace(cop); + int rc; + + semanage_handle_t *manage_hnd = NULL; + manage_hnd = semanage_handle_create(); + if (manage_hnd == NULL) { + lmi_return_with_chars(_cb, CMPI_RC_ERR_FAILED, "Could not create semanage handle"); + } + + rc = semanage_connect(manage_hnd); + if (rc < 0) { + lmi_return_with_chars(_cb, CMPI_RC_ERR_FAILED, "Could not connect to semanage"); + } + + struct cmpi_data data = {cr, ns}; + semanage_port_t **ports = NULL; + unsigned int n_ports; + unsigned int i = 0; + rc = semanage_port_list(manage_hnd, &ports, &n_ports); + if (rc < 0) { + lmi_return_with_chars(_cb, CMPI_RC_ERR_FAILED, "Could not list SELinux ports"); + } + /* + Libsemanage returns ports in the following manner: + ... + system_u:object_r:clockspeed_port_t 4041/udp + system_u:object_r:cluster_port_t 5149/tcp + system_u:object_r:cluster_port_t 5149/udp + system_u:object_r:cluster_port_t 40040/tcp + system_u:object_r:cluster_port_t 50006-50008/tcp + system_u:object_r:cluster_port_t 50006-50008/udp + system_u:object_r:cma_port_t 1050/tcp + system_u:object_r:cma_port_t 1050/udp + ... + + It is currently not possible to get port ranges for particular SELinux + ports in a sensible way, therefore the complex code below exists. + + It works in 3 passes. First, ports are iterated one by one (as is the only + possible way to go over them) and a hash is built, using port instance id + as a key, and a list of port instances as its value. The port instances + differ only in the port range values, which is why it is necessary to + process them like this. Second pass processes the hash and builds and + returns class instances to the broken. Third pass frees the allocated + ports. This step is, in theory, not necessary, but it's cleaner to free to + ports in a dedicated step, and not as a part of the previous algorithm. + + On my testing system, this makes 556 (ports returned by libsemanage) + 381 + (unique ports from the hash) + 556 (ports to be freed) iterations, which + makes it roughly 3 times slower than it could be, if libsemanage returned + its data in a sensible way. + */ + + /* First pass: reorganize in a sensible way. */ + GHashTable *ports_hash; + /* The values (lists containing the libsemanage port structs) don't have to + * be freed, as the ports will be freed separately in the third pass. */ + ports_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + for (; i < n_ports; i++) { + semanage_context_t *ctx = semanage_port_get_con(ports[i]); + const char *p_type = semanage_context_get_type(ctx); + const char *p_proto = PORT_PROTO(ports[i]); + GSList *ranges; + + char instance_id[BUFLEN]; + snprintf(instance_id, BUFLEN, + LMI_ORGID ":" LMI_SELinuxPort_ClassName ":%s:%s", p_proto, p_type); + + /* Lookup the value first. If it is found, the list can be extended and + * the original pointer replaced. If not found, the NULL will be used, + * indicating an empty list. */ + ranges = g_hash_table_lookup(ports_hash, instance_id); + ranges = g_slist_append(ranges, ports[i]); + g_hash_table_insert(ports_hash, g_strdup(instance_id), ranges); + } + + /* Second pass: feed the broker. */ + g_hash_table_foreach(ports_hash, port_hash_foreach_handler, &data); + + /* Third pass: free ports. */ + for (i = 0; i < n_ports; i++) { + semanage_port_free(ports[i]); + } + + g_hash_table_unref(ports_hash); + semanage_handle_destroy(manage_hnd); + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxPortGetInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + return KDefaultGetInstance( + _cb, mi, cc, cr, cop, properties); +} + +static CMPIStatus LMI_SELinuxPortCreateInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxPortModifyInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci, + const char **properties) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxPortDeleteInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxPortExecQuery( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *lang, + const char *query) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +CMInstanceMIStub( + LMI_SELinuxPort, + LMI_SELinuxPort, + _cb, + LMI_SELinuxPortInitialize(ctx)) + +static CMPIStatus LMI_SELinuxPortMethodCleanup( + CMPIMethodMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxPortInvokeMethod( + CMPIMethodMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *meth, + const CMPIArgs *in, + CMPIArgs *out) +{ + return LMI_SELinuxPort_DispatchMethod( + _cb, mi, cc, cr, cop, meth, in, out); +} + +CMMethodMIStub( + LMI_SELinuxPort, + LMI_SELinuxPort, + _cb, + LMI_SELinuxPortInitialize(ctx)) + +KONKRET_REGISTRATION( + "root/cimv2", + "LMI_SELinuxPort", + "LMI_SELinuxPort", + "instance method") diff --git a/src/selinux/LMI_SELinuxServiceHasElementProvider.c b/src/selinux/LMI_SELinuxServiceHasElementProvider.c new file mode 100644 index 0000000..3a36128 --- /dev/null +++ b/src/selinux/LMI_SELinuxServiceHasElementProvider.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + * + */ + +#include +#include "LMI_SELinuxServiceHasElement.h" +#include "LMI_SELinuxBoolean.h" +#include "LMI_SELinuxPort.h" +#include "selinux.h" + +static const CMPIBroker *_cb; + +static void LMI_SELinuxServiceHasElementInitialize(const CMPIContext *ctx) +{ + lmi_init(provider_name, _cb, ctx, provider_config_defaults); +} + +static CMPIStatus LMI_SELinuxServiceHasElementCleanup( + CMPIInstanceMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxServiceHasElementEnumInstanceNames( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + return KDefaultEnumerateInstanceNames( + _cb, mi, cc, cr, cop); +} + +static CMPIStatus LMI_SELinuxServiceHasElementEnumInstances( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + const char *ns = KNameSpace(cop); + CMPIStatus st; + + LMI_SELinuxServiceHasElement lmi_sshe; + LMI_SELinuxServiceHasElement_Init(&lmi_sshe, _cb, ns); + + LMI_SELinuxService lmi_service; + init_selinux_service(&lmi_service, _cb, ns); + + CMPIObjectPath *o = LMI_SELinuxService_ToObjectPath(&lmi_service, &st); + lmi_return_if_status_not_ok(st); + LMI_SELinuxServiceHasElement_SetObjectPath_Dependent(&lmi_sshe, o); + + CMPIEnumeration *e; + CMPIObjectPath *aux_op; + CMPIData data; + + /* booleans */ + aux_op = CMNewObjectPath(_cb, ns, LMI_SELinuxBoolean_ClassName, &st); + e = _cb->bft->enumerateInstances(_cb, cc, aux_op, NULL, &st); + CMRelease(aux_op); + lmi_return_if_status_not_ok(st); + while (CMHasNext(e, &st)) { + data = CMGetNext(e, &st); + LMI_SELinuxBoolean lmi_bool; + LMI_SELinuxBoolean_InitFromInstance(&lmi_bool, _cb, data.value.inst); + LMI_SELinuxServiceHasElement_SetObjectPath_Antecedent(&lmi_sshe, LMI_SELinuxBoolean_ToObjectPath(&lmi_bool, &st)); + + KReturnInstance(cr, lmi_sshe); + } + + /* ports */ + aux_op = CMNewObjectPath(_cb, ns, LMI_SELinuxPort_ClassName, &st); + e = _cb->bft->enumerateInstances(_cb, cc, aux_op, NULL, &st); + CMRelease(aux_op); + lmi_return_if_status_not_ok(st); + while (CMHasNext(e, &st)) { + data = CMGetNext(e, &st); + LMI_SELinuxPort lmi_bool; + LMI_SELinuxPort_InitFromInstance(&lmi_bool, _cb, data.value.inst); + LMI_SELinuxServiceHasElement_SetObjectPath_Antecedent(&lmi_sshe, LMI_SELinuxPort_ToObjectPath(&lmi_bool, &st)); + + KReturnInstance(cr, lmi_sshe); + } + + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxServiceHasElementGetInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + return KDefaultGetInstance( + _cb, mi, cc, cr, cop, properties); +} + +static CMPIStatus LMI_SELinuxServiceHasElementCreateInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxServiceHasElementModifyInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci, + const char**properties) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxServiceHasElementDeleteInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxServiceHasElementExecQuery( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *lang, + const char *query) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxServiceHasElementAssociationCleanup( + CMPIAssociationMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxServiceHasElementAssociators( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *resultClass, + const char *role, + const char *resultRole, + const char **properties) +{ + return KDefaultAssociators( + _cb, + mi, + cc, + cr, + cop, + LMI_SELinuxServiceHasElement_ClassName, + assocClass, + resultClass, + role, + resultRole, + properties); +} + +static CMPIStatus LMI_SELinuxServiceHasElementAssociatorNames( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *resultClass, + const char *role, + const char *resultRole) +{ + return KDefaultAssociatorNames( + _cb, + mi, + cc, + cr, + cop, + LMI_SELinuxServiceHasElement_ClassName, + assocClass, + resultClass, + role, + resultRole); +} + +static CMPIStatus LMI_SELinuxServiceHasElementReferences( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *role, + const char **properties) +{ + return KDefaultReferences( + _cb, + mi, + cc, + cr, + cop, + LMI_SELinuxServiceHasElement_ClassName, + assocClass, + role, + properties); +} + +static CMPIStatus LMI_SELinuxServiceHasElementReferenceNames( + CMPIAssociationMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *assocClass, + const char *role) +{ + return KDefaultReferenceNames( + _cb, + mi, + cc, + cr, + cop, + LMI_SELinuxServiceHasElement_ClassName, + assocClass, + role); +} + +CMInstanceMIStub( + LMI_SELinuxServiceHasElement, + LMI_SELinuxServiceHasElement, + _cb, + LMI_SELinuxServiceHasElementInitialize(ctx)) + +CMAssociationMIStub( + LMI_SELinuxServiceHasElement, + LMI_SELinuxServiceHasElement, + _cb, + LMI_SELinuxServiceHasElementInitialize(ctx)) + +KONKRET_REGISTRATION( + "root/cimv2", + "LMI_SELinuxServiceHasElement", + "LMI_SELinuxServiceHasElement", + "instance association") diff --git a/src/selinux/LMI_SELinuxServiceProvider.c b/src/selinux/LMI_SELinuxServiceProvider.c new file mode 100644 index 0000000..17d4e7b --- /dev/null +++ b/src/selinux/LMI_SELinuxServiceProvider.c @@ -0,0 +1,727 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + * + */ + +#include +#include "LMI_SELinuxService.h" +#include "selinux.h" +#include + +static const CMPIBroker *_cb = NULL; + + + +/** + * Parse SELinux policy type from the SELinux configuration. + * + * @param policy_type SELinux policy type. Must be freed by the caller. + */ +static void parse_selinux_sysconf(char **policy_type) +{ + const char *conf_path = "/etc/selinux/config"; + const char *fake_group = "fake"; + const char *fake_group_str = "[fake]\n"; + const int fake_group_str_len = strlen(fake_group_str); + const int CONTENT_SIZE = 25 * BUFLEN; + char content[CONTENT_SIZE + 1]; + content[0] = '\0'; + + lmi_debug("==> parse_selinux_sysconf (file=%s)", conf_path); + strcat(content, fake_group_str); + FILE *f = fopen(conf_path, "r"); + if (f == NULL) { + lmi_error("Can't read SELinux config: %s", conf_path); + return; + } + fread(content + fake_group_str_len, CONTENT_SIZE - fake_group_str_len, 1, f); + fclose(f); + + GError *error; + GKeyFile *keyfile = g_key_file_new(); + + if (!g_key_file_load_from_data(keyfile, content, strlen(content), + G_KEY_FILE_NONE, &error)) + { + lmi_error("Can't read SELinux config: %s", error->message); + g_key_file_free(keyfile); + return; + } + + *policy_type = g_key_file_get_value(keyfile, fake_group, "SELINUXTYPE", &error); + + g_key_file_free(keyfile); + lmi_debug("<== parse_selinux_sysconf"); +} + + + +static void LMI_SELinuxServiceInitialize(const CMPIContext *ctx) +{ + selinux_provider_init(LMI_SELinuxService_ClassName, FALSE, provider_config_defaults, _cb, ctx); +} + + +static CMPIStatus LMI_SELinuxServiceCleanup( + CMPIInstanceMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + return jobmgr_cleanup(LMI_SELinuxService_ClassName); +} + +static CMPIStatus LMI_SELinuxServiceEnumInstanceNames( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + return KDefaultEnumerateInstanceNames( + _cb, mi, cc, cr, cop); +} + +static CMPIStatus LMI_SELinuxServiceEnumInstances( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + const char *ns = KNameSpace(cop); + char *pt = NULL; + int ds; + LMI_SELinuxService lmi_service; + + init_selinux_service(&lmi_service, _cb, ns); + LMI_SELinuxService_Set_PolicyVersion(&lmi_service, security_policyvers()); + /* security_genenforce() / security_getenforcemode() return -1 (disabled), 0 + * (permissive) and 1 (enforcing). If +1 is added to the value, the correct + * value from the CMPI value map is obtained (see MOF for details). */ + LMI_SELinuxService_Set_SELinuxState(&lmi_service, security_getenforce() + 1); + + parse_selinux_sysconf(&pt); + selinux_getenforcemode(&ds); + LMI_SELinuxService_Set_PolicyType(&lmi_service, pt); + LMI_SELinuxService_Set_SELinuxDefaultState(&lmi_service, ds + 1); + + if (pt) + free(pt); + KReturnInstance(cr, lmi_service); + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxServiceGetInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char **properties) +{ + return KDefaultGetInstance( + _cb, mi, cc, cr, cop, properties); +} + +static CMPIStatus LMI_SELinuxServiceCreateInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxServiceModifyInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const CMPIInstance *ci, + const char **properties) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxServiceDeleteInstance( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +static CMPIStatus LMI_SELinuxServiceExecQuery( + CMPIInstanceMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *lang, + const char *query) +{ + CMReturn(CMPI_RC_ERR_NOT_SUPPORTED); +} + +CMInstanceMIStub( + LMI_SELinuxService, + LMI_SELinuxService, + _cb, + LMI_SELinuxServiceInitialize(ctx)) + +static CMPIStatus LMI_SELinuxServiceMethodCleanup( + CMPIMethodMI *mi, + const CMPIContext *cc, + CMPIBoolean term) +{ + CMReturn(CMPI_RC_OK); +} + +static CMPIStatus LMI_SELinuxServiceInvokeMethod( + CMPIMethodMI *mi, + const CMPIContext *cc, + const CMPIResult *cr, + const CMPIObjectPath *cop, + const char *meth, + const CMPIArgs *in, + CMPIArgs *out) +{ + return LMI_SELinuxService_DispatchMethod( + _cb, mi, cc, cr, cop, meth, in, out); +} + +CMMethodMIStub( + LMI_SELinuxService, + LMI_SELinuxService, + _cb, + LMI_SELinuxServiceInitialize(ctx)) + +KUint32 LMI_SELinuxService_RequestStateChange( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxServiceRef *self, + const KUint16 *RequestedState, + KRef *Job, + const KDateTime *TimeoutPeriod, + CMPIStatus *__status) +{ + KUint32 result = KUINT32_INIT; + + KSetStatus(__status, ERR_NOT_SUPPORTED); + return result; +} + +KUint32 LMI_SELinuxService_StartService( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxServiceRef *self, + CMPIStatus *__status) +{ + KUint32 result = KUINT32_INIT; + + KSetStatus(__status, ERR_NOT_SUPPORTED); + return result; +} + +KUint32 LMI_SELinuxService_StopService( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxServiceRef *self, + CMPIStatus *__status) +{ + KUint32 result = KUINT32_INIT; + + KSetStatus(__status, ERR_NOT_SUPPORTED); + return result; +} + +KUint32 LMI_SELinuxService_ChangeAffectedElementsAssignedSequence( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxServiceRef *self, + const KRefA *ManagedElements, + const KUint16A *AssignedSequence, + KRef *Job, + CMPIStatus *__status) +{ + KUint32 result = KUINT32_INIT; + + KSetStatus(__status, ERR_NOT_SUPPORTED); + return result; +} + +KUint32 LMI_SELinuxService_SetSELinuxState( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxServiceRef *self, + const KUint16 *NewState, + const KBoolean *MakeDefault, + KRef *Job, + CMPIStatus *__status) +{ + KUint32 result = KUINT32_INIT; + CMPIObjectPath *cop; + LmiJob *job; + GVariant *v; + + if (!KHasValue(NewState)) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, LMI_PARAM_ERROR_STR(NewState)); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + if (NewState->value > 2) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, "NewState out of range"); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + /* MakeDefault defaults to False */ + + /* setenforce doesn't allow to disable SELinux in runtime, the provider shouldn't either */ + if (KHasValue(MakeDefault) && MakeDefault->value == 0 && NewState->value == 0) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, "Disabling SELinux in runtime is not allowed"); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + job = jobmgr_new_job(LMI_TYPE_JOB); + if (job == NULL) { + KSetStatus2(cb, __status, ERR_FAILED, "Could not create job instance"); + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + lmi_job_set_method_name(job, "SetSELinuxState"); + + v = g_variant_new_int16(NewState->value); + lmi_job_set_in_param(job, "NewState", v); + v = g_variant_new_boolean(MakeDefault->value); + lmi_job_set_in_param(job, "MakeDefault", v); + v = g_variant_new_byte(JOB_SET_SELINUX_STATE); + lmi_job_set_in_param(job, PARAM_TYPE, v); + + *__status = jobmgr_run_job(job); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + + *__status = jobmgr_job_to_cim_op(job, &cop); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + + KRef_SetObjectPath(Job, cop); + KUint32_Set(&result, JOB_RC_STARTED); + return result; +} + +static CMPIStatus parse_port_instance_id(const gchar *instance_id, gint *type, gchar **name) +{ + CMPIStatus st = {0, NULL}; + gchar **parts; + + parts = g_strsplit(instance_id, ":", 0); + if (!parts) { + KSetStatus2(_cb, &st, ERR_INVALID_PARAMETER, "Port InstanceID could not be parsed"); + return st; + } + + if (g_strv_length(parts) != 4) { + KSetStatus2(_cb, &st, ERR_INVALID_PARAMETER, "Port InstanceID is not valid"); + g_strfreev(parts); + return st; + } + + *type = PORT_STR_TO_ENUM(parts[2]); + *name = parts[3]; + + g_free(parts[0]); + g_free(parts[1]); + g_free(parts[2]); + return st; +} + +static CMPIStatus parse_port_range(const gchar *range, gint *low, gint *high) +{ + CMPIStatus st = {0, NULL}; + gchar **parts; + gint parts_len; + + lmi_debug("==> parse_port_range (range=%s)", range); + parts = g_strsplit(range, "-", 0); + if (!parts) { + KSetStatus2(_cb, &st, ERR_INVALID_PARAMETER, "PortRange could not be parsed"); + return st; + } + + parts_len = g_strv_length(parts); + if (parts_len > 2) { + KSetStatus2(_cb, &st, ERR_INVALID_PARAMETER, "Invalid PortRange"); + goto out; + } + + *low = atoi(parts[0]); + *high = (parts_len == 2) ? atoi(parts[1]) : *low; + if (*low == 0 || *high == 0) { + KSetStatus2(_cb, &st, ERR_INVALID_PARAMETER, "Invalid PortRange"); + goto out; + } +out: + g_strfreev(parts); + lmi_debug("<== parse_port_range (low=%d, high=%d)", *low, *high); + return st; +} + +KUint32 LMI_SELinuxService_SetPortLabel( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxServiceRef *self, + const KRef *Target, + const KString *PortRange, + KRef *Job, + CMPIStatus *__status) +{ + KUint32 result = KUINT32_INIT; + CMPIObjectPath *cop; + LmiJob *job; + GVariant *v; + LMI_SELinuxPortRef lmi_portr; + gchar *port_name; + gint port_type, range_low, range_high; + + if (!KHasValue(PortRange)) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, LMI_PARAM_ERROR_STR(PortRange)); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + if (!KHasValue(Target)) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, LMI_PARAM_ERROR_STR(Target)); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + job = jobmgr_new_job(LMI_TYPE_JOB); + if (job == NULL) { + KSetStatus2(cb, __status, ERR_FAILED, "Could not create job instance"); + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + + *__status = LMI_SELinuxPortRef_InitFromObjectPath(&lmi_portr, cb, Target->value); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + if (!lmi_portr.InstanceID.chars) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, "The Target misses InstanceID"); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + *__status = parse_port_instance_id(lmi_portr.InstanceID.chars, &port_type, &port_name); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + v = g_variant_new_int32(port_type); + lmi_job_set_in_param(job, "Type", v); + v = g_variant_new_string(port_name); + lmi_job_set_in_param(job, "Name", v); + + g_free(port_name); + + *__status = parse_port_range(PortRange->value->ft->getCharPtr(PortRange->value, NULL), &range_low, &range_high); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + + v = g_variant_new_int32(range_low); + lmi_job_set_in_param(job, "RangeLow", v); + v = g_variant_new_int32(range_high); + lmi_job_set_in_param(job, "RangeHigh", v); + v = g_variant_new_byte(JOB_SET_PORT_LABEL); + lmi_job_set_in_param(job, PARAM_TYPE, v); + + *__status = jobmgr_run_job(job); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + + *__status = jobmgr_job_to_cim_op(job, &cop); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + + KRef_SetObjectPath(Job, cop); + KUint32_Set(&result, JOB_RC_STARTED); + return result; +} + +KUint32 LMI_SELinuxService_SetFileLabel( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxServiceRef *self, + const KRef *Target, + const KString *Label, + KRef *Job, + CMPIStatus *__status) +{ + KUint32 result = KUINT32_INIT; + CMPIObjectPath *cop; + LmiJob *job; + GVariant *v; + LMI_UnixFileRef lmi_ufr; + CMPIString *str; + const gchar *cstr; + + if (!KHasValue(Target)) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, LMI_PARAM_ERROR_STR(Target)); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + if (!KHasValue(Label)) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, LMI_PARAM_ERROR_STR(Label)); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + job = jobmgr_new_job(LMI_TYPE_JOB); + if (job == NULL) { + KSetStatus2(cb, __status, ERR_FAILED, "Could not create job instance"); + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + lmi_job_set_method_name(job, "SetFileLabel"); + + *__status = LMI_UnixFileRef_InitFromObjectPath(&lmi_ufr, cb, Target->value); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + if (!lmi_ufr.LFName.chars) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, "The Target misses LFName"); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + str = Label->value; + cstr = str->ft->getCharPtr(str, NULL); + if (!cstr || strcmp(cstr, "") == 0) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, "The Label cannot be empty"); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + v = g_variant_new_string(lmi_ufr.LFName.chars); + lmi_job_set_in_param(job, "Path", v); + v = g_variant_new_string(cstr); + lmi_job_set_in_param(job, "Label", v); + v = g_variant_new_byte(JOB_SET_FILE_LABEL); + lmi_job_set_in_param(job, PARAM_TYPE, v); + + *__status = jobmgr_run_job(job); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + + *__status = jobmgr_job_to_cim_op(job, &cop); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + + KRef_SetObjectPath(Job, cop); + KUint32_Set(&result, JOB_RC_STARTED); + return result; +} + +KUint32 LMI_SELinuxService_SetBoolean( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxServiceRef *self, + const KRef *Target, + const KBoolean *Value, + const KBoolean *MakeDefault, + KRef *Job, + CMPIStatus *__status) +{ + KUint32 result = KUINT32_INIT; + CMPIObjectPath *cop; + LmiJob *job; + GVariant *v; + LMI_SELinuxBooleanRef lmi_boolr; + gchar **parts; + + if (!KHasValue(Target)) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, LMI_PARAM_ERROR_STR(Target)); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + if (!KHasValue(Value)) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, LMI_PARAM_ERROR_STR(Value)); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + /* MakeDefault defaults to False */ + + *__status = LMI_SELinuxBooleanRef_InitFromObjectPath(&lmi_boolr, cb, Target->value); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + + parts = g_strsplit(lmi_boolr.InstanceID.chars, ":", 0); + if (!parts) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, "Invalid InstanceID"); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + job = jobmgr_new_job(LMI_TYPE_JOB); + if (job == NULL) { + KSetStatus2(cb, __status, ERR_FAILED, "Could not create job instance"); + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + lmi_job_set_method_name(job, "SetBoolean"); + + v = g_variant_new_string(parts[2]); + g_strfreev(parts); + lmi_job_set_in_param(job, "BoolName", v); + v = g_variant_new_boolean(Value->value); + lmi_job_set_in_param(job, "Value", v); + v = g_variant_new_boolean(MakeDefault->value); + lmi_job_set_in_param(job, "MakeDefault", v); + v = g_variant_new_byte(JOB_SET_BOOLEAN); + lmi_job_set_in_param(job, PARAM_TYPE, v); + + *__status = jobmgr_run_job(job); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + + *__status = jobmgr_job_to_cim_op(job, &cop); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + + KRef_SetObjectPath(Job, cop); + KUint32_Set(&result, JOB_RC_STARTED); + return result; +} + +KUint32 LMI_SELinuxService_RestoreLabels( + const CMPIBroker *cb, + CMPIMethodMI *mi, + const CMPIContext *context, + const LMI_SELinuxServiceRef *self, + KRef *Target, + const KUint16 *Action, + const KBoolean *Recursively, + KRef *Job, + CMPIStatus *__status) +{ + KUint32 result = KUINT32_INIT; + CMPIObjectPath *cop; + LmiJob *job; + GVariant *v; + LMI_UnixFileRef lmi_ufr; + + if (!KHasValue(Target)) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, LMI_PARAM_ERROR_STR(Target)); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + if (!KHasValue(Action)) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, LMI_PARAM_ERROR_STR(Action)); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + /* Recursively defaults to False */ + + job = jobmgr_new_job(LMI_TYPE_JOB); + if (job == NULL) { + KSetStatus2(cb, __status, ERR_FAILED, "Could not create job instance"); + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + lmi_job_set_method_name(job, "RestoreLabels"); + + *__status = LMI_UnixFileRef_InitFromObjectPath(&lmi_ufr, cb, Target->value); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + if (!lmi_ufr.LFName.chars) { + KSetStatus2(cb, __status, ERR_INVALID_PARAMETER, "The Target misses LFName"); + KUint32_Set(&result, JOB_RC_INVALID_PARAMETER); + return result; + } + + v = g_variant_new_string(lmi_ufr.LFName.chars); + lmi_job_set_in_param(job, "Path", v); + v = g_variant_new_int16(Action->value); + lmi_job_set_in_param(job, "Action", v); + v = g_variant_new_boolean(Recursively->value); + lmi_job_set_in_param(job, "Recursively", v); + v = g_variant_new_byte(JOB_RESTORE_LABELS); + lmi_job_set_in_param(job, PARAM_TYPE, v); + + *__status = jobmgr_run_job(job); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + + *__status = jobmgr_job_to_cim_op(job, &cop); + if (__status->rc) { + KUint32_Set(&result, JOB_RC_FAILED); + return result; + } + + KRef_SetObjectPath(Job, cop); + KUint32_Set(&result, JOB_RC_STARTED); + return result; +} + +KONKRET_REGISTRATION( + "root/cimv2", + "LMI_SELinuxService", + "LMI_SELinuxService", + "instance method") diff --git a/src/selinux/cmpiLMI_SELinux-cimprovagt b/src/selinux/cmpiLMI_SELinux-cimprovagt new file mode 100755 index 0000000..d8056ef --- /dev/null +++ b/src/selinux/cmpiLMI_SELinux-cimprovagt @@ -0,0 +1,21 @@ +#!/bin/sh +# +# Copyright (C) 2014 Red Hat, Inc. All rights reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Authors: Jan Synacek + +/usr/libexec/pegasus/cimprovagt "$@" diff --git a/src/selinux/selinux.c b/src/selinux/selinux.c new file mode 100644 index 0000000..e74bdbe --- /dev/null +++ b/src/selinux/selinux.c @@ -0,0 +1,696 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + * + */ + +#include "selinux.h" + +const ConfigEntry *provider_config_defaults = NULL; +const char *provider_name = "selinux"; + +void init_selinux_service(LMI_SELinuxService *service, const CMPIBroker *b, const char *ns) +{ + LMI_SELinuxService_Init(service, b, ns); + LMI_SELinuxService_Set_CreationClassName(service, LMI_SELinuxService_ClassName); + LMI_SELinuxService_Set_Name(service, LMI_SELinuxService_ClassName); + LMI_SELinuxService_Set_SystemCreationClassName(service, lmi_get_system_creation_class_name()); + LMI_SELinuxService_Set_SystemName(service, lmi_get_system_name()); + LMI_SELinuxService_Set_InstanceID(service, LMI_ORGID ":" LMI_SELinuxService_ClassName); +} + +CMPIStatus selinux_provider_init(const char *class, + gboolean is_indication_provider, + const ConfigEntry *provider_config_defaults, + const CMPIBroker *cb, + const CMPIContext *ctx) +{ + lmi_init(provider_name, cb, ctx, provider_config_defaults); + lmi_debug("==> selinux_provider_init (class=%s)", class); + + jobmgr_register_job_type(LMI_TYPE_JOB, LMI_SELinuxJob_ClassName, + NULL, /* don't need convert_func */ + job_make_params_callback, + METHOD_RESULT_VALUE_TYPE_UINT32, + job_process_callback, + FALSE, + FALSE); + + return jobmgr_init(cb, ctx, "SELinux", class, is_indication_provider, FALSE, NULL, 0); +} + +static gint set_selinux_default_state(gint newstate) +{ + const gchar *CONF_PATH = "/etc/selinux/config"; + const gint CONTENT_SIZE = 25 * BUFLEN; + gchar content[CONTENT_SIZE + 1]; + size_t content_len = 0; + gchar *line = NULL; + size_t line_len = BUFLEN; + ssize_t read; + GRegex *re; + GMatchInfo *mi; + FILE *f; + + lmi_debug("==> set_setlinux_default_state (newstate=%d)", newstate); + f = fopen(CONF_PATH, "r"); + if (f == NULL) { + lmi_error("Can't open SELinux sysconfig for reading: %s", CONF_PATH); + return -1; + } + + /* + * Read the original config line by line, find the relevant config option + * and if it's there, change it. + */ + content[0] = '\0'; + re = g_regex_new("^\\s*SELINUX\\s*=", 0, 0, NULL); + while((read = getline (&line, &line_len, f)) != -1) { + if (g_regex_match(re, line, 0, &mi)) { + read = snprintf(line, BUFLEN - 1, "SELINUX=%s\n", (newstate == 0) ? "disabled" : + (newstate == 1) ? "permissive" : + (newstate == 2) ? "enforcing" : + "unknown"); + lmi_debug("SELINUX default state changed to %s", line); + } + g_strlcat(content, line, CONTENT_SIZE); + content_len += read; + } + + g_match_info_free(mi); + g_regex_unref(re); + g_free(line); + fclose(f); + + /* write new state */ + f = fopen(CONF_PATH, "w"); + if (f == NULL) { + lmi_error("Can't open SELinux sysconfig for writing: %s", CONF_PATH); + return -1; + } + fwrite(content, 1, content_len, f); + + fclose(f); + lmi_debug("<== set_setlinux_default_state"); + return 0; +} + +/* Callback: LMI_SELinuxService.SetSELinuxState() */ +gint setselinuxstate(LmiJob *job) +{ + GVariant *v; + gint rc; + gint newstate; + gboolean makedefault; + + lmi_debug("==> setselinuxstate"); + + v = lmi_job_get_in_param(job, "NewState"); + newstate = g_variant_get_int16(v); + v = lmi_job_get_in_param(job, "MakeDefault"); + makedefault = g_variant_get_boolean(v); + + lmi_debug("LMI_SELinuxService.SetSELinuxState: NewState: %d, MakeDefault: %d", newstate, makedefault); + + if (makedefault) { + rc = set_selinux_default_state(newstate); + if (rc < 0) + lmi_job_finish_exception(job, LMI_JOB_STATUS_CODE_ENUM_FAILED, "Could not set default SELinux state"); + } else { + rc = security_setenforce(newstate - 1); + if (rc < 0) + lmi_job_finish_exception(job, LMI_JOB_STATUS_CODE_ENUM_FAILED, "Could not set SELinux state"); + } + + lmi_debug("<== setselinuxstate (rc=%d)", rc); + return rc; +} + +/* Callback: LMI_SELinuxService.SetFileLabel() */ +gint setfilelabel(LmiJob *job) +{ + GVariant *v; + const gchar *path; + const gchar *label; + gint rc; + + lmi_debug("==> setfilelabel"); + + v = lmi_job_get_in_param(job, "Path"); + path = g_variant_get_string(v, NULL); + v = lmi_job_get_in_param(job, "Label"); + label = g_variant_get_string(v, NULL); + + lmi_debug("LMI_SELinuxService.SetFileLabel: Target: %s, Label: %s", path, label); + + security_context_t con; + con = g_strdup(label); + rc = setfilecon(path, con); + freecon(con); + + if (rc < 0) + lmi_job_finish_exception(job, LMI_JOB_STATUS_CODE_ENUM_FAILED, + "Could not set file label: %s -> %s", path, label); + + lmi_debug("<== setfilelabel (rc=%d)", rc); + return rc; +} + +/* Callback: LMI_SELinuxService.SetPortLabel() */ +gint setportlabel(LmiJob *job) +{ + gint rc = -1; + GVariant *v; + const gchar *name; + gint type, range_low, range_high; + semanage_handle_t *hnd = NULL; + semanage_port_t *port = NULL; + semanage_port_key_t *pkey = NULL; + semanage_context_t *ctx = NULL; + + lmi_debug("==> setportlabel"); + + hnd = semanage_handle_create(); + if (hnd == NULL) { + lmi_error("semanage_handle_create failed"); + return rc; + } + + rc = semanage_connect(hnd); + if (rc < 0) { + lmi_error("semanage_connect failed"); + goto err; + } + + v = lmi_job_get_in_param(job, "Name"); + name = g_variant_get_string(v, NULL); + v = lmi_job_get_in_param(job, "Type"); + type = g_variant_get_int32(v); + v = lmi_job_get_in_param(job, "RangeLow"); + range_low = g_variant_get_int32(v); + v = lmi_job_get_in_param(job, "RangeHigh"); + range_high = g_variant_get_int32(v); + + lmi_debug("LMI_SELinuxService.SetPortLabel: Name: %s, Type: %d, RangeLow: %d, RangeHigh: %d", + name, type, range_low, range_high); + + rc = semanage_port_key_create(hnd, range_low, range_high, type, &pkey); + if (rc < 0) { + lmi_error("semanage_port_key_create failed"); + goto err; + } + + rc = semanage_port_query(hnd, pkey, &port); + if (rc < 0) { + lmi_error("semanage_port_query failed"); + goto err; + } + if (port == NULL) { + lmi_error("%s port %d-%d does not exist", PORT_ENUM_TO_STR(type), range_low, range_high); + rc = -1; + goto err; + } + + ctx = semanage_port_get_con(port); + if (ctx == NULL) { + lmi_error("Port context is not valid"); + rc = -1; + goto err; + } + + lmi_debug("Found '%s' label on that port", semanage_context_get_type(ctx)); + + rc = semanage_context_set_type(hnd, ctx, name); + if (rc < 0) { + lmi_error("semanage_context_set_type failed"); + goto err; + } + + rc = semanage_port_set_con(hnd, port, ctx); + if (rc < 0) { + lmi_error("semanage_port_set_con failed"); + goto err; + } + + /* modification */ + rc = semanage_begin_transaction(hnd); + if (rc < 0) { + lmi_error("semanage_begin_transaction failed"); + goto err; + } + rc = semanage_port_modify_local(hnd, pkey, port); + if (rc < 0) { + lmi_error("semanage_port_modify_local failed"); + goto err; + } + rc = semanage_commit(hnd); + if (rc < 0) { + lmi_error("semanage_commit failed"); + } +err: + semanage_port_key_free(pkey); + semanage_port_free(port); + semanage_disconnect(hnd); + semanage_handle_destroy(hnd); + if (rc < 0) + lmi_job_finish_exception(job, LMI_JOB_STATUS_CODE_ENUM_FAILED, + "Could not label %s port %d-%d with %s", + PORT_ENUM_TO_STR(type), range_low, range_high, name); + + lmi_debug("<== setportlabel (rc=%d)", rc); + return rc; +} + +/* Callback: LMI_SELinuxService.SetBoolean() */ +gint setboolean(LmiJob *job) +{ + GVariant *v; + gchar *boolname; + gboolean value; + gboolean makedefault; + int exists = 0; + semanage_handle_t *hnd = NULL; + semanage_bool_key_t *bk = NULL; + semanage_bool_t *b = NULL; + gint rc = -1; + + lmi_debug("==> setboolean"); + + v = lmi_job_get_in_param(job, "BoolName"); + boolname = (gchar *) g_variant_get_string(v, NULL); + v = lmi_job_get_in_param(job, "Value"); + value = g_variant_get_boolean(v); + v = lmi_job_get_in_param(job, "MakeDefault"); + makedefault = g_variant_get_boolean(v); + + lmi_debug("LMI_SELinuxService.SetBoolean: Target: %s, Value: %d, MakeDefault: %d", + boolname, value, makedefault); + + /* set permanent boolean value */ + /* For some reason, security_set_boolean_list() called with permanent = 1 doesn't work, + * so semanage has to be used. */ + if (makedefault) { + hnd = semanage_handle_create(); + if (!hnd) { + lmi_error("semanage_handle_create failed"); + return rc; + } + + rc = semanage_connect(hnd); + if (rc < 0) { + lmi_error("semanage_connect failed"); + goto err; + } + + /* gets a semanage lock internally */ + rc = semanage_begin_transaction(hnd); + if (rc < 0) { + lmi_error("semanage_begin_transaction failed"); + goto err; + } + + rc = semanage_bool_create(hnd, &b); + if (rc < 0) { + lmi_error("semanage_bool_create failed"); + goto err; + } + + rc = semanage_bool_set_name(hnd, b, boolname); + if (rc < 0) { + lmi_error("semanage_bool_set_name failed"); + goto err; + } + semanage_bool_set_value(b, value); + + rc = semanage_bool_key_extract(hnd, b, &bk); + if (rc < 0) { + lmi_error("semanage_bool_key_extract failed"); + goto err; + } + + rc = semanage_bool_exists(hnd, bk, &exists); + if (rc < 0) { + lmi_error("semanage_bool_exists failed"); + goto err; + } + if (!exists) { + rc = semanage_bool_exists_local(hnd, bk, &exists); + if (rc < 0) { + lmi_error("semanage_bool_exists_local failed"); + goto err; + } + if (!exists) { + lmi_error("Boolean %s does not exist", boolname); + goto err; + } + } + + rc = semanage_bool_modify_local(hnd, bk, b); + if (rc < 0) { + lmi_error("semanage_bool_modify_local failed"); + goto err; + } + + rc = semanage_bool_set_active(hnd, bk, b); + if (rc < 0) { + lmi_error("semanage_bool_set_active failed"); + goto err; + } + + semanage_set_reload(hnd, 1); + + rc = semanage_commit(hnd); + if (rc < 0) { + lmi_error("semanage_commit failed"); + goto err; + } + + semanage_disconnect(hnd); + } else { /* makedefault == false */ + /* TODO acquire a selinux lock? */ + /* set current boolean value */ + SELboolean boollist = {boolname, value}; + rc = security_set_boolean_list(1, &boollist, 0); + if (rc < 0) { + lmi_debug("Setting boolean %s to %d failed", boolname, value); + return rc; + } + } + +err: + /* semanage*_free functions handle NULLs fine */ + semanage_bool_key_free(bk); + semanage_bool_free(b); + semanage_handle_destroy(hnd); + if (rc < 0) + lmi_job_finish_exception(job, LMI_JOB_STATUS_CODE_ENUM_FAILED, "Could not set boolean %s", boolname); + + lmi_debug("<== setboolean (rc=%d)", rc); + return rc; +} + +void job_process_callback(LmiJob *job, GCancellable *cancellable) +{ + GVariant *v; + guchar _type; + gint rc = 0; + const gchar *err; + + lmi_debug("==> job_process_callback"); + + if (cancellable && g_cancellable_is_cancelled(cancellable)) { + return; + } + + v = lmi_job_get_in_param(job, PARAM_TYPE); + _type = g_variant_get_byte(v); + switch (_type) { + case JOB_SET_SELINUX_STATE: + rc = setselinuxstate(job); + if (rc < 0) + err = "Setting SELinux state failed"; + break; + case JOB_RESTORE_LABELS: + rc = restore_labels(job); + if (rc < 0) + err = "Restoring SELinux labels failed"; + break; + case JOB_SET_FILE_LABEL: + rc = setfilelabel(job); + if (rc < 0) + err = "Setting file label failed"; + break; + case JOB_SET_PORT_LABEL: + rc = setportlabel(job); + if (rc < 0) + err = "Setting port label failed"; + break; + case JOB_SET_BOOLEAN: + rc = setboolean(job); + if (rc < 0) + err = "Setting SELinux boolean failed"; + break; + default: + rc = -1; + lmi_job_finish_exception(job, LMI_JOB_STATUS_CODE_ENUM_FAILED, "Fatal: Unknown internal job type"); + } + + if (!lmi_job_is_finished(job)) { + if (rc < 0) + lmi_job_finish_exception(job, LMI_JOB_STATUS_CODE_ENUM_FAILED, err); + else + lmi_job_finish_ok(job, JOB_RC_COMPLETED); + } + lmi_debug("<== job_process_callback"); +} + +CMPIStatus job_make_params_callback(const CMPIBroker *cb, + const CMPIContext *ctx, + const LmiJob *job, + gboolean include_input, + gboolean include_output, + CMPIInstance *instance) +{ + CMPIStatus st = {CMPI_RC_OK, NULL}; + CMPIValue val; + CMPIObjectPath *o; + LMI_UnixFileRef lmi_ufr; + const char *ns; + GVariant *v; + struct restore_labels_data *rldata; + guchar type; + + lmi_debug("==> job_make_params_callback"); + + if (include_input) + /* not interested in input parameters */ + return st; + + v = lmi_job_get_in_param(job, PARAM_TYPE); + type = g_variant_get_byte(v); + + lmi_error("XXX type: %d", type); /* TODO */ + + if (type == JOB_RESTORE_LABELS) { + rldata = (struct restore_labels_data *) lmi_job_get_data(job); + o = CMGetObjectPath(instance, NULL); + val.string = CMGetNameSpace(o, NULL); + ns = val.string->ft->getCharPtr(val.string, NULL); + LMI_UnixFileRef_Init(&lmi_ufr, cb, ns); + LMI_UnixFileRef_Set_LFName(&lmi_ufr, rldata->path); + LMI_UnixFileRef_Set_CSCreationClassName(&lmi_ufr, lmi_get_system_creation_class_name()); + LMI_UnixFileRef_Set_CSName(&lmi_ufr, lmi_get_system_name_safe(ctx)); + + o = LMI_UnixFileRef_ToObjectPath(&lmi_ufr, &st); + if (st.rc) + return st; + val.ref = o; + st = CMSetProperty(instance, "Target", &val, CMPI_ref); + if (st.rc) + return st; + + val.uint32 = JOB_RC_COMPLETED; + st = CMSetProperty(instance, "__ReturnValue", &val, CMPI_uint32); + } else if (type == JOB_SET_SELINUX_STATE) { + val.uint32 = JOB_RC_COMPLETED; + st = CMSetProperty(instance, "__ReturnValue", &val, CMPI_uint32); + } else if (type == JOB_SET_FILE_LABEL) { + val.uint32 = JOB_RC_COMPLETED; + st = CMSetProperty(instance, "__ReturnValue", &val, CMPI_uint32); + } else if (type == JOB_SET_BOOLEAN) { + val.uint32 = JOB_RC_COMPLETED; + st = CMSetProperty(instance, "__ReturnValue", &val, CMPI_uint32); + } + lmi_debug("<== job_make_params_callback"); + return st; +} + +/* LMI_SELinuxService.RestoreLabels */ + +gint restore_label_handler(const gchar *path, struct selabel_handle *hnd, void *data) +{ + security_context_t c_current, c_expected; + struct restore_labels_data *rldata = (struct restore_labels_data *)data; + gint rc, mismatch; + + lmi_debug("==> restore_label_handler (file:%s)", path); + + rc = getfilecon(path, &c_current); + if (rc < 0) { + lmi_error("getfilecon() failed on '%s'\n", path); + return rc; + } + + rc = selabel_lookup(hnd, &c_expected, path, 0); + if (rc < 0) { + lmi_error("selabel_lookup() failed on '%s'\n", path); + freecon(c_current); + return rc; + } + + mismatch = strcmp(c_current, c_expected); + if (mismatch) { + lmi_info("Context mismatch on '%s': current:%s, expected:%s", path, c_current, c_expected); + } + + if (mismatch && rldata->action == ACTION_REPORT) { + /* TODO how to actually report? special return code? */ + } else if (mismatch && rldata->action == ACTION_RESTORE) { + lmi_info("Restoring context on '%s'", path); + rc = selinux_lsetfilecon_default(path); + if (rc) { + lmi_error("Could not restore file context on '%s'", path); + } + } + + freecon(c_expected); + freecon(c_current); + lmi_debug("<== restore_label_handler (rc=%d)", rc); + return rc; +} + +gint restore_label_pvt(struct restore_labels_data *rldata) +{ + GError *gerror = NULL; + GDir *gdir = NULL; + const gchar *gde; + const gchar *path = rldata->path; + gchar *dirname; + gchar *filepath; + GStatBuf sb; + struct selabel_handle *label_hnd; + gint rc; + + lmi_debug("==> restore_label_pvt (path=%s)", path); + + label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); + if (label_hnd == NULL) { + lmi_error("selabel_open() failed\n"); + return -1; + } + + if ((rc = g_stat(path, &sb)) < 0) { + lmi_error("Stat on '%s' failed", path); + goto out; + } + + if (!S_ISDIR(sb.st_mode)) { + rc = rldata->callback(path, label_hnd, (void *)rldata); + goto out; + } + + gdir = g_dir_open(path, 0, &gerror); + if (gerror) { + lmi_error("Opening '%s' failed", path); + rc = -1; + goto out; + } + + while ((gde = g_dir_read_name(gdir))) { + dirname = g_path_get_dirname(path); + filepath = g_build_filename(path, gde, NULL); + gerror = NULL; + + /* normalize file path in case of symlink or '..' here */ + { + gchar *actualpath = realpath(filepath, NULL); + /* in case of some dynamic files, the file doesn't have to exist */ + if (actualpath == NULL && errno == ENOENT) { + lmi_info("File disappeared: %s\n", filepath); + /* TODO */ + /* g_free(filepath); */ + /* g_free(dirname); */ + continue; + } + g_free(filepath); + filepath = actualpath; + } + + g_stat(filepath, &sb); + + if (g_hash_table_add(rldata->paths_htable, g_strdup(filepath)) == FALSE) { + /* already exists, throw warning and continue; */ + /* printf("already been there, skipping: %s\n", filepath); */ + g_free(filepath); + g_free(dirname); + continue; + } + + if (rldata->recursively && S_ISDIR(sb.st_mode)) { + /* XXX: Another problem: if there's a cyclic structure -- symlinks or bind mounts, + this will cycle forever. Possible solutions: 1) Keep paths that we traversed + in a hash table and check if we were there already. 2) Limit recursion depth. */ + /* UPDATE: For now, keep a hash with already visited paths. */ + restore_label_pvt(rldata); + } + + rc = rldata->callback(filepath, label_hnd, (void *)rldata); + + if (rc < 0) { + goto out; + } + + g_free(filepath); + g_free(dirname); + } + +out: + g_dir_close(gdir); + selabel_close(label_hnd); + lmi_debug("<== restore_label_pvt (rc=%d)", rc); + return rc; +} + +/* Callback: LMI_SELinuxService.RestoreLabels() */ +gint restore_labels(LmiJob *job) +{ + GVariant *v; + gchar *path; + gint action; + gboolean recursively; + gint rc; + + lmi_debug("==> restore_labels"); + + v = lmi_job_get_in_param(job, "Path"); + path = (gchar *) g_variant_get_string(v, NULL); + v = lmi_job_get_in_param(job, "Action"); + action = g_variant_get_int16(v); + v = lmi_job_get_in_param(job, "Recursively"); + recursively = g_variant_get_boolean(v); + + lmi_debug("LMI_SELinuxService.RestoreLabels: Target: %s, Action: %d, Recursively: %d", + path, action, recursively); + + GHashTable *paths_htable = NULL; + paths_htable = g_hash_table_new(g_str_hash, g_str_equal); + + struct restore_labels_data *rldata = g_malloc(sizeof(struct restore_labels_data)); + rldata->path = path; + rldata->action = action; + rldata->recursively = recursively; + rldata->job = job; + rldata->callback = restore_label_handler; + rldata->paths_htable = paths_htable; + lmi_job_set_data(job, (struct restore_labels_data *) rldata, NULL); /* TODO specify destroy callback */ + + rc = restore_label_pvt(rldata); + + lmi_debug("<== restore_labels (rc=%d)", rc); + return rc; +} diff --git a/src/selinux/selinux.h b/src/selinux/selinux.h new file mode 100644 index 0000000..4613c08 --- /dev/null +++ b/src/selinux/selinux.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Jan Synacek + * + */ + +#ifndef _SELINUX_PROVIDER_H +#define _SELINUX_PROVIDER_H + +#include +#include +#include +#include +#include /* GStatBuf */ + +#include "LMI_SELinuxService.h" +#include "openlmi.h" +#include "job_manager.h" + +enum { + JOB_SET_SELINUX_STATE = 0x10, + JOB_RESTORE_LABELS, + JOB_SET_FILE_LABEL, + JOB_SET_PORT_LABEL, + JOB_SET_BOOLEAN, + JOB_NUM +}; + +enum { + ACTION_REPORT, + ACTION_RESTORE, +}; + +#define JOB_RC_COMPLETED 0 +#define JOB_RC_FAILED 4 +#define JOB_RC_INVALID_PARAMETER 5 +#define JOB_RC_STARTED 4096 + +/* Special job parameter that specifies the type of action that the job is supposed to do. + * Used when job "inheritance' is deemed to be overkill. */ +#define PARAM_TYPE "_Type" + +#define PORT_STR_TO_ENUM(pstr) ((strcmp(pstr, "TCP") == 0) ? SEMANAGE_PROTO_TCP : SEMANAGE_PROTO_UDP) +#define PORT_ENUM_TO_STR(e) ((e == SEMANAGE_PROTO_TCP) ? "TCP" : "UDP") +#define PORT_PROTO(port) (PORT_ENUM_TO_STR(semanage_port_get_proto(port))) + +struct cmpi_data { + const CMPIResult *cr; + const char *namespace; +}; + +struct restore_labels_data { + gint action; + gboolean recursively; + gchar *path; + LmiJob *job; + gint (*callback)(const gchar *, struct selabel_handle *hnd, void *); + GHashTable *paths_htable; +}; + +struct mislabeled_file { + security_context_t sec_ctx_current; + security_context_t sec_ctx_expected; + gchar *path; +}; + +const ConfigEntry *provider_config_defaults; +const char *provider_name; + +/** + * Initialize an instance of LMI_SELinuxService. + * + * @param service Instance of LMI_SELinuxService. + * @param b CMPIBroker. + * @param ns Namespace. + */ +void init_selinux_service(LMI_SELinuxService *service, const CMPIBroker *b, const char *ns); + +/** + * comment + * + * @param p + */ +CMPIStatus selinux_provider_init(const char *class, + gboolean is_indication_provider, + const ConfigEntry *provider_config_defaults, + const CMPIBroker *cb, + const CMPIContext *ctx); + +/** + * comment + * + * @param p + */ +CMPIStatus job_make_params_callback(const CMPIBroker *cb, + const CMPIContext *ctx, + const LmiJob *job, + gboolean include_input, + gboolean include_output, + CMPIInstance *instance); + +/** + * comment + * + * @param p + */ +void job_process_callback(LmiJob *job, GCancellable *cancellable); + +gint setselinuxstate(LmiJob *job); +gint restore_labels(LmiJob *job); +gint setfilelabel(LmiJob *job); +gint setboolean(LmiJob *job); + +#endif diff --git a/src/selinux/test/README b/src/selinux/test/README new file mode 100644 index 0000000..ae10951 --- /dev/null +++ b/src/selinux/test/README @@ -0,0 +1,22 @@ +SELinux provider tests +====================== +Basic tests for the SELinux provider. + +Dependencies +------------ +$ yum install libselinux-python + +Usage +----- +The tests must be run on the same machine as the CIMOM! + +Set following environment variables: +LMI_CIMOM_URL - URL to the CIMOM, e.g. 'https://localhost:5989'. +LMI_CIMOM_USERNAME - Name of the user on CIMOM the test will use, e.g. 'root'. +LMI_CIMOM_PASSWORD - Password of the user, e.g. 'opensesame'. + +Running the tests: + export LMI_CIMOM_URL="https://my-machine" + export LMI_CIMOM_PASSWORD="password" + nosetests -v test_selinux.py + diff --git a/src/selinux/test/__init__.py b/src/selinux/test/__init__.py new file mode 100644 index 0000000..db9a637 --- /dev/null +++ b/src/selinux/test/__init__.py @@ -0,0 +1,17 @@ +# Copyright (C) 2014 Red Hat, Inc. All rights reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Authors: Jan Synacek diff --git a/src/selinux/test/test_selinux.py b/src/selinux/test/test_selinux.py new file mode 100644 index 0000000..3a3966b --- /dev/null +++ b/src/selinux/test/test_selinux.py @@ -0,0 +1,346 @@ +#!/usr/bin/python +# +# Copyright (C) 2014 Red Hat, Inc. All rights reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Authors: Jan Synacek + +import lmiwbem +from lmi.test.lmibase import LmiTestCase +from lmi.shell.LMIExceptions import CIMError + +import re +import selinux +import subprocess + +# Example output: +# nfs_export_all_ro (on , on) Description. +BOOLEAN_REGEXP = r"(\w+)\s+\((\w+)\s+,\s+(\w+)\)" + +def boolean_state_equal(state_str, state): + return (state_str == "on" and state is True or + state_str == "off" and state is False) + +class SELinuxProviderTestCase(LmiTestCase): + """ + Base class for SELinux provider tests. + """ + USE_EXCEPTIONS = True + + @classmethod + def setUpClass(cls): + LmiTestCase.setUpClass.im_func(cls) + + +class TestSELinuxBoolean(SELinuxProviderTestCase): + """ + SELinux boolean tests. + """ + CLASS_NAME = "LMI_SELinuxBoolean" + + def _build_booleans(self): + """ + Build a dictionary o SELinux booleans from semanage output. + """ + out = subprocess.check_output(["semanage", "boolean", "--list"]) + res = {} + for line in out.split("\n"): + # example output + # nfs_export_all_ro (on , on) Description. + m = re.match(BOOLEAN_REGEXP, line) + if m is None: continue # skip headers and blank lines + res[m.group(1)] = (m.group(2), m.group(3)) + return res + + def test_enum_instances(self): + """ + Test LMI_SELinuxBoolean.EnumerateInstances(). + """ + instances = self.cim_class.instances() + booleans = self._build_booleans() + self.assertEqual(len(instances), len(booleans.keys())) + + for inst in instances: + name = inst.InstanceID.split(":")[2] + self.assertTrue(booleans.has_key(name)) + self.assertTrue(boolean_state_equal(booleans[name][0], + inst.State)) + self.assertTrue(boolean_state_equal(booleans[name][1], + inst.DefaultState)) + self.assertEqual(inst.ElementName, name) + +class TestSELinuxPort(SELinuxProviderTestCase): + """ + SELinux port tests. + """ + CLASS_NAME = "LMI_SELinuxPort" + + def _build_ports(self): + """ + Build a dictionary o SELinux ports from semanage output. + """ + out = subprocess.check_output(["semanage", "port", "--list"]) + res = {} + for line in out.split("\n"): + # example output: + # zebra_port_t tcp 8021, 2600-2604, 2606, 2608-2609 + m = re.match(r"(\w+)\s+(\w+)\s+(.+)", line) + if m is None: continue # skip headers and blank lines + key = m.group(2).upper() + ":" + m.group(1) + res[key] = re.split(",\s+", m.group(3)) + return res + + + def test_enum_instances(self): + """ + Test LMI_SELinuxPort.EnumerateInstances(). + + For some reason, semanage doesn't list 'reserved_port_t' as one of the + ports. This test takes that into account. + """ + instances = self.cim_class.instances() + ports = self._build_ports() + # +1 for reserved_port_t + self.assertEquals(len(instances), len(ports.keys()) + 1) + icut = len("LMI:LMI_SELinuxPort:") + for inst in instances: + key = inst.InstanceID[icut:] + # skip reserved_port_t + if key == "TCP:reserved_port_t" or key == "UDP:reserved_port_t": + continue + self.assertTrue(ports.has_key(key)) + type = key.split(":")[1] + self.assertEqual(type, inst.ElementName) + self.assertEqual(type, inst.SELinuxContext.split(":")[2]) + # this test assumes that libsemanage always returns ports in the same order + self.assertEqual(ports[key], inst.Ports) + +class TestSELinuxService(SELinuxProviderTestCase): + """ + SELinux service class tests. + """ + CLASS_NAME = "LMI_SELinuxService" + + SELINUX_REGEXP = r"^\s*SELINUX\s*=\s*(\w*)" + SELINUXTYPE_REGEXP = r"^\s*SELINUXTYPE\s*=\s*(\w*)" + SELINUX_CONFIG_PATH = "/etc/selinux/config" + + def _get_selinux_sysconf(self): + """ + Parse SELinux configuration and return (selinux, selinuxtype). + """ + se, setype = (None, None) + + f = open(self.SELINUX_CONFIG_PATH, "r") + content = f.read() + + m = re.search(self.SELINUX_REGEXP, content, re.MULTILINE) + se = unicode(m.group(1).strip()) if m else None + m = re.search(self.SELINUXTYPE_REGEXP, content, re.MULTILINE) + setype = unicode(m.group(1).strip()) if m else None + + f.close() + return (se, setype) + + def _set_selinux_sysconf(self, selinux, selinuxtype): + f = open(self.SELINUX_CONFIG_PATH, "r") + content = f.read() + content = re.sub(self.SELINUX_REGEXP, "SELINUX="+selinux, content, flags=re.MULTILINE) + content = re.sub(self.SELINUXTYPE_REGEXP, "SELINUXTYPE="+selinuxtype, content, flags=re.MULTILINE) + f.close() + + f = open(self.SELINUX_CONFIG_PATH, "w") + f.write(content) + f.close() + + def _get_instance(self): + """ + Get the only instance of LMI_SELinuxService in the system. + Also check its basic properties. + """ + instances = self.cim_class.instances() + self.assertEquals(len(instances), 1) + inst = instances[0] + self.assertEquals(inst.InstanceID, "LMI:" + self.CLASS_NAME) + self.assertEquals(inst.CreationClassName, self.CLASS_NAME) + self.assertEquals(inst.Name, self.CLASS_NAME) + return inst + + def _verify_selinux_states(self, inst): + # security_genenforce() / security_getenforcemode() return -1 (disabled), 0 + # (permissive) and 1 (enforcing). If +1 is added to the value, the correct + # value from the value map is obtained (see MOF for details). + st, dst = selinux.selinux_getenforcemode() + self.assertEquals(inst.SELinuxDefaultState, dst + 1) + self.assertEquals(inst.SELinuxState, st + 1) + + def _get_boolean_values(self, boolname): + p1 = subprocess.Popen(["semanage", "boolean", "--list"], stdout=subprocess.PIPE) + p2 = subprocess.Popen(["grep", boolname], stdin=p1.stdout, stdout=subprocess.PIPE) + out = p2.communicate()[0] + m = re.match(BOOLEAN_REGEXP, out) + return m.groups() + + def test_enum_instances(self): + """ + Test LMI_SELinuxService.EnumerateInstances(). + """ + inst = self._get_instance() + + se, setype = self._get_selinux_sysconf() + self.assertEquals(inst.PolicyType, setype) + self.assertEquals(inst.PolicyVersion, selinux.security_policyvers()) + self._verify_selinux_states(inst) + + def test_set_selinux_state(self): + """ + Test LMI_SELinuxService.SetSELinuxState() + """ + old_selinux, old_selinuxtype = self._get_selinux_sysconf() + makedefault = True + inst = self._get_instance() + for state in [0, 1, 2]: + res = inst.SyncSetSELinuxState({"NewState": state, "MakeDefault": makedefault}) + self.assertEquals(res[0], 0) + inst = self._get_instance() + self._verify_selinux_states(inst) + + # Setting selinux to enforcing (state == 2) would only work with a valid + # selinux policy for this script and the provider, otherwise the tests fail. + # So, don't test with state == 2. + makedefault = False + inst = self._get_instance() + res = inst.SyncSetSELinuxState({"NewState": 1, "MakeDefault": makedefault}) + self.assertEquals(res[0], 0) + inst = self._get_instance() + self._verify_selinux_states(inst) + + # TODO this should be called in some kind of a cleanup, because any assertion above could fail + # restore + self._set_selinux_sysconf(old_selinux, old_selinuxtype) + + ## wrong cases + self.assertRaisesRegexp(CIMError, "Disabling SELinux in runtime is not allowed", + inst.SyncSetSELinuxState, + NewState = 0, + MakeDefault = makedefault) + + self.assertRaisesRegexp(CIMError, "NewState out of range", + inst.SyncSetSELinuxState, + NewState = 3, + MakeDefault = makedefault) + + def _prepare_restore_labels(self): + testdir = "/tmp" + + def _cleanup_restore_labels(self): + pass + + def test_restore_labels(self): + pass + + def test_set_file_label(self): + testfile = "/tmp/.test_set_file_label" + new_label = "openlmi_test_u:openlmi_test_r:openlmi_test_t:s999" + subprocess.check_call(["touch", testfile]) + + props = {"CSName":self.SYSTEM_NAME, + "CSCreationClassName":self.system_cs_name, + "FSCreationClassName":"", + "FSName":"", + "LFCreationClassName":"", + "LFName":testfile} + target = self.ns.LMI_UnixFile.new_instance_name(props) + + inst = self._get_instance() + res = inst.SyncSetFileLabel({"Target":target, "Label":new_label}) + self.assertEquals(res[0], 0) + + out = subprocess.check_output(["ls", "-Z", testfile]) + label = out.split()[0] + self.assertEquals(label, new_label) + + ## wrong cases + self.assertRaisesRegexp(CIMError, "The Label cannot be empty", + inst.SyncSetFileLabel, + Target = target, + Label = "") + + props["LFName"] = "/nowhere/.to/be./found" + target = self.ns.LMI_UnixFile.new_instance_name(props) + self.assertRaisesRegexp(CIMError, "Could not set file label", + inst.SyncSetFileLabel, + Target = target, + Label = new_label) + + + # TODO this should be called in some kind of a cleanup, because any assertion above could fail + subprocess.check_call(["rm", "-f", testfile]) + + def test_set_port_label(self): + pass + + def test_set_boolean(self): + """ + Test LMI_SELinuxService.SetBoolean(). + """ + boolname = "gluster_anon_write" + bool_instanceid = "LMI:LMI_SELinuxBoolean:" + boolname + + # save for restoration + _, old_state, old_defaultstate = self._get_boolean_values(boolname) + + target = self.ns.LMI_SELinuxBoolean.new_instance_name({"InstanceID":bool_instanceid}) + for makedefault in [True, False]: + for value in [True, False]: + inst = self._get_instance() + res = inst.SyncSetBoolean({"Target":target, + "Value":value, + "MakeDefault":makedefault}) + self.assertEquals(res[0], 0) + + name, state, defaultstate = self._get_boolean_values(boolname) + self.assertEquals(name, boolname) + self.assertTrue(boolean_state_equal(state, value)) + if makedefault: + self.assertTrue(boolean_state_equal(defaultstate, value)) + + + # TODO this should be called in some kind of a cleanup, because any assertion above could fail + # in case old values are different, the order in which the setsebool + # commands are is called matters + subprocess.check_call(["setsebool", boolname, old_defaultstate, "-P"]) + subprocess.check_call(["setsebool", boolname, old_state]) + + ## wrong uses + inst = self._get_instance() + self.assertRaisesRegexp(CIMError, "No Target has been specified", + inst.SyncSetBoolean, + Target = None, + Value = False, + MakeDefault = False) + self.assertRaisesRegexp(CIMError, "No Value has been specified", + inst.SyncSetBoolean, + Target = target, + Value = 1, + MakeDefault = False) + # MakeDefault not tested, as it defaults to False + target = self.ns.LMI_SELinuxBoolean.new_instance_name({"InstanceID":""}) + self.assertRaisesRegexp(CIMError, "Invalid InstanceID", + inst.SyncSetBoolean, + Target = target, + Value = False, + MakeDefault = False)