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 <jsynacek@redhat.com>
+ */
+
+[ 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 '<port_low>-<port_high>', 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 <jsynacek@redhat.com>
+ */
+
+[ 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 <jsynacek@redhat.com>
+ *
+ */
+
+#include <konkret/konkret.h>
+#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 <jsynacek@redhat.com>
+ *
+ */
+
+#include <konkret/konkret.h>
+#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 <jsynacek@redhat.com>
+ *
+ */
+
+#include <konkret/konkret.h>
+#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 <jsynacek@redhat.com>
+ *
+ * Implementation note:
+ *
+ * Casts to/from char/gchar to/from xmlChar are necessary, because xmlChar is
+ * defined as an unsigned char.
+ */
+
+#include <konkret/konkret.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#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 <jsynacek@redhat.com>
+ *
+ */
+
+#include <konkret/konkret.h>
+#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 <jsynacek@redhat.com>
+ *
+ */
+
+#include <konkret/konkret.h>
+#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 <jsynacek@redhat.com>
+ *
+ */
+
+#include <konkret/konkret.h>
+#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 <jsynacek@redhat.com>
+ *
+ */
+
+#include <konkret/konkret.h>
+#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 <jsynacek@redhat.com>
+ *
+ */
+
+#include <konkret/konkret.h>
+#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..7a1ac77
--- /dev/null
+++ b/src/selinux/LMI_SELinuxServiceProvider.c
@@ -0,0 +1,737 @@
+/*
+ * 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 <jsynacek@redhat.com>
+ *
+ */
+
+#include <konkret/konkret.h>
+#include "LMI_SELinuxService.h"
+#include "selinux.h"
+#include <assert.h>
+
+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 (wrong format)");
+        g_strfreev(parts);
+        return st;
+    }
+    if (!PORT_STR_IS_VALID(parts[2])) {
+        KSetStatus2(_cb, &st, ERR_INVALID_PARAMETER, "Port InstanceID is not valid (invalid protocol)");
+        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;
+    }
+
+    *__status = LMI_SELinuxPortRef_InitFromObjectPath(&lmi_portr, cb, Target->value);
+    if (__status->rc) {
+        KUint32_Set(&result, JOB_RC_FAILED);
+        return result;
+    }
+    lmi_error("InstanceID: _%s_", lmi_portr.InstanceID.chars);
+    if (!lmi_portr.InstanceID.chars || strcmp(lmi_portr.InstanceID.chars, "") == 0) {
+        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;
+    }
+
+    *__status = parse_port_range(PortRange->value->ft->getCharPtr(PortRange->value, NULL), &range_low, &range_high);
+    if (__status->rc) {
+        g_free(port_name);
+        KUint32_Set(&result, JOB_RC_FAILED);
+        return result;
+    }
+
+    job = jobmgr_new_job(LMI_TYPE_JOB);
+    if (job == NULL) {
+        g_free(port_name);
+        KSetStatus2(cb, __status, ERR_FAILED, "Could not create job instance");
+        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);
+
+    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;
+    }
+    lmi_job_set_method_name(job, "SetPortLabel");
+
+    *__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 <jsynacek@redhat.com>
+
+/usr/libexec/pegasus/cimprovagt "$@"
diff --git a/src/selinux/selinux.c b/src/selinux/selinux.c
new file mode 100644
index 0000000..9eebcd8
--- /dev/null
+++ b/src/selinux/selinux.c
@@ -0,0 +1,699 @@
+/*
+ * 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 <jsynacek@redhat.com>
+ *
+ */
+
+#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_PORT_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..458ed03
--- /dev/null
+++ b/src/selinux/selinux.h
@@ -0,0 +1,130 @@
+/*
+ * 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 <jsynacek@redhat.com>
+ *
+ */
+
+#ifndef _SELINUX_PROVIDER_H
+#define _SELINUX_PROVIDER_H
+
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+#include <semanage/semanage.h>
+#include <glib.h>
+#include <glib/gstdio.h>	/* 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)))
+#define PORT_STR_IS_VALID(pstr) ((strcmp(pstr, "TCP") == 0) || (strcmp(pstr, "UDP") == 0))
+
+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 <jsynacek@redhat.com>
diff --git a/src/selinux/test/test_selinux.py b/src/selinux/test/test_selinux.py
new file mode 100644
index 0000000..309e378
--- /dev/null
+++ b/src/selinux/test/test_selinux.py
@@ -0,0 +1,418 @@
+#!/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 <jsynacek@redhat.com>
+
+import lmiwbem
+from lmi.shell.LMIExceptions import CIMError
+from lmi.test.lmibase import LmiTestCase
+from lmi.test.util import mark_dangerous
+
+import os
+import re
+import selinux
+import subprocess
+
+# Example output:
+# nfs_export_all_ro    (on   ,   on)  Description.
+BOOLEAN_REGEXP = r"(\w+)\s+\((\w+)\s+,\s+(\w+)\)"
+# Example output:
+# zebra_port_t     tcp   8021, 2600-2604, 2606, 2608-2609
+PORT_REGEXP = r"(\w+)\s+(\w+)\s+(.+)"
+
+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"):
+            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"):
+            m = re.match(PORT_REGEXP, 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_CONFIG_PATH = "/etc/selinux/config"
+    SELINUX_REGEXP      = r"^\s*SELINUX\s*=\s*(\w*)"
+    SELINUXTYPE_REGEXP  = r"^\s*SELINUXTYPE\s*=\s*(\w*)"
+
+    TESTDIR   = "/tmp/"
+    TESTFILE  = TESTDIR + ".test_set_file_label"
+    BOOLNAME  = "gluster_anon_write"
+    PORTNAME  = "zebra_port_t"
+    PORT      = "1229"    # zented_port_t
+    PORTRANGE = "161-162" # snmp_port_t
+
+    def setUp(self):
+        print "SETTING UP"
+        ## test_set_selinux_state
+        self.old_selinux, self.old_selinuxtype = self._get_selinux_sysconf()
+        ## test_restore_labels
+
+        ## test_set_file_label
+        subprocess.check_call(["touch", self.TESTFILE])
+        ## test_set_port_label (no init needed)
+        ## test_set_boolean
+        _, self.old_state, self.old_defaultstate = self._get_boolean_values(self.BOOLNAME)
+
+    def tearDown(self):
+        print "TEARING DOWN"
+        ## test_set_selinux_state
+        self._set_selinux_sysconf(self.old_selinux, self.old_selinuxtype)
+        ## test_restore_labels
+        ## test set_file_label
+        subprocess.check_call(["rm", "-f", self.TESTFILE])
+        ## test_set_port_label (TODO SLOOOOW: call only once)
+        null = open(os.devnull, "w")
+        for protocol in ["tcp", "udp"]:
+            subprocess.call(["semanage", "port", "-d", "-t", self.PORTNAME, "-p", protocol, self.PORT],
+                            stdout=null, stderr=null)
+            subprocess.call(["semanage", "port", "-d", "-t", self.PORTNAME, "-p", protocol, self.PORTRANGE],
+                            stdout=null, stderr=null)
+        null.close()
+        ## test_set_boolean
+        # the order in which the setsebool commands are is called matters
+        subprocess.check_call(["setsebool", self.BOOLNAME, self.old_defaultstate, "-P"])
+        subprocess.check_call(["setsebool", self.BOOLNAME, self.old_state])
+
+    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 _get_port_values(self, portname, protocol):
+        p1 = subprocess.Popen(["semanage", "port", "--list"], stdout=subprocess.PIPE)
+        p2 = subprocess.Popen(["grep", "-i", portname], stdin=p1.stdout, stdout=subprocess.PIPE)
+        p3 = subprocess.Popen(["grep", "-i", protocol], stdin=p2.stdout, stdout=subprocess.PIPE)
+        out = p3.communicate()[0]
+        m = re.match(PORT_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()
+        """
+        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)
+
+        ## 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 test_restore_labels(self):
+        pass
+
+    def test_set_file_label(self):
+        new_label = "openlmi_test_u:openlmi_test_r:openlmi_test_t:s999"
+
+        props = {"CSName":self.SYSTEM_NAME,
+                 "CSCreationClassName":self.system_cs_name,
+                 "FSCreationClassName":"",
+                 "FSName":"",
+                 "LFCreationClassName":"",
+                 "LFName":self.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", self.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)
+
+    def test_set_port_label(self):
+        """
+        Test LMI_SELinuxService.SetPortLabel"
+        """
+        inst = self._get_instance()
+
+        def _set_port_label_and_assert(portname, protocol, range):
+            port_instanceid = "LMI:LMI_SELinuxPort:%s:%s" % (protocol, portname)
+            target = self.ns.LMI_SELinuxPort.new_instance_name({"InstanceID":port_instanceid})
+
+            res = inst.SyncSetPortLabel({"Target":target,
+                                         "PortRange":range})
+            self.assertEquals(res[0], 0)
+            (portname_out, protocol_out, ranges_out) = self._get_port_values(portname, protocol)
+            self.assertTrue(range in ranges_out.split(', '))
+
+        for protocol in ["TCP", "UDP"]:
+            port_instanceid = "LMI:LMI_SELinuxPort:%s:%s" % (protocol, self.PORTNAME)
+
+            # put zebra_port_t on port 1229 (zented_port_t)
+            _set_port_label_and_assert(self.PORTNAME, protocol, "1229")
+
+            # put zebra_port_t on ports 161-162 (snmp_port_t)
+            _set_port_label_and_assert(self.PORTNAME, protocol, "161-162")
+
+        ## wrong case
+        target = self.ns.LMI_SELinuxPort.new_instance_name({"InstanceID":"LMI:LMI_SELinuxPort:TCP:" + self.PORTNAME})
+        self.assertRaisesRegexp(CIMError, "Could not label ... port",
+                                inst.SyncSetPortLabel,
+                                Target = target,
+                                PortRange = "1-65535")
+
+        target = self.ns.LMI_SELinuxPort.new_instance_name({"InstanceID":"LMI:LMI_SELinuxPort:TCP:no_such_port_exists_t"})
+        self.assertRaisesRegexp(CIMError, "Could not label ... port",
+                                inst.SyncSetPortLabel,
+                                Target = target,
+                                PortRange = "1229")
+
+        target = self.ns.LMI_SELinuxPort.new_instance_name({"InstanceID":"LMI:LMI_SELinuxPort:InvalidProtocol:zebra_port_t"})
+        self.assertRaisesRegexp(CIMError, "Port InstanceID is not valid",
+                                inst.SyncSetPortLabel,
+                                Target = target,
+                                PortRange = "N/A")
+
+        target = self.ns.LMI_SELinuxPort.new_instance_name({"InstanceID":"Pure.Garbage_TM"})
+        self.assertRaisesRegexp(CIMError, "Port InstanceID is not valid",
+                                inst.SyncSetPortLabel,
+                                Target = target,
+                                PortRange = "N/A")
+
+        target = self.ns.LMI_SELinuxPort.new_instance_name({"InstanceID":""})
+        self.assertRaisesRegexp(CIMError, "The Target misses InstanceID",
+                                inst.SyncSetPortLabel,
+                                Target = target,
+                                PortRange = "N/A")
+
+    def test_set_boolean(self):
+        """
+        Test LMI_SELinuxService.SetBoolean().
+        """
+        bool_instanceid = "LMI:LMI_SELinuxBoolean:" + self.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(self.BOOLNAME)
+                self.assertEquals(name, self.BOOLNAME)
+                self.assertTrue(boolean_state_equal(state, value))
+                if makedefault:
+                    self.assertTrue(boolean_state_equal(defaultstate, value))
+
+        ## 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)
