OpenLMI SELinux Design Document

Table of Contents

1 Model

The model is designed to be as close to the actual SELinux implementation as possible, without introducing additional layers of complexity that are usually found in the overly general CIM standards. Hopefully, this effort will make it clear and easier to implement.

1.1 UML

selinux.png

Figure 1: SELinux CIM model

1.2 MOF

/*
 * 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
{
};

[ Version("0.0.1"),
  Description("Common superclass for all SELinux classes which have a context.") ]
class LMI_SELinuxElementWithContext : LMI_SELinuxElement
{
  [ Description("Current SELinux context.") ]
    string CurrentContext;

  [ Description("Expected SELinux context.") ]
    string ExpectedContext;
};

[ Version("0.0.1"),
  Description("Class representing an SELinux policy boolean.") ]
class LMI_SELinuxBoolean : LMI_SELinuxElement
{
  [ Description("Currently set state.") ]
    boolean State;
  [ Description("State value that is set persistently across reboots.") ]
    boolean DefaultState;
};

[ Version("0.0.1"),
  Description(
    "Class representing an SELinux port. It can encompass multiple "
    "individual networking ports, or even their ranges.") ]
class LMI_SELinuxPort : LMI_SELinuxElementWithContext
{
  [ 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'.") ]
    string Ports[];
};

[ Version("0.0.1"),
  Description("Class ") ]
class LMI_SELinuxFile : LMI_SELinuxElementWithContext
{
  [ Description("Full path to the actual file.") ]
    string Name;
};

[ 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"
    "\n"
    "SELinux can have the following types:\n"
    "   Targeted - Targeted processes are protected.\n"
    "   Minimum - Modification of targeted policy. Only selected processes are protected.\n"
    "   MLS - Multi Level Security protection.") ]
class LMI_SELinuxService : CIM_Service
{
  [ Description("Current system-wide state of SELinux."),
    ValueMap {"1", "2"},
    Values {"Permissive", "Enforcing"} ]
    uint16 State;

  [ Description("SELinux system-wide state that is set persistently across reboots."),
    ValueMap {"0", "1", "2"},
    Values {"Disabled", "Permissive", "Enforcing"} ]
    uint16 DefaultState;

  [ Description("SELinux type."),
    ValueMap {"0", "1", "2"},
    Values {"Targeted", "Minimum", "MLS"} ]
    uint16 Type;

  [ Description("Current version of the SELinux system policy.") ]
    uint32 PolicyVersion;

  [ Description(
    "Set SELinux state."),
    ValueMap {"0", "1", "2", "3", "4", "5", "6"},
    Values {"Job Completed with No Error", "Not Supported",
            "Unknown", "Timeout", "Failed", "Invalid Parameter",
            "In Use"} ]
    uint32 SetState(
           [ 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 ]
           CIM_ConcreteJob REF Job
    );

  [ Description(
    "Set label on an SELinux port."),
    ValueMap {"0", "1", "2", "3", "4", "5", "6"},
    Values {"Job Completed with No Error", "Not Supported",
            "Unknown", "Timeout", "Failed", "Invalid Parameter",
            "In Use"} ]
    uint32 SetPortLabel(
           [ IN, OUT, Description("An SELinux port to change.") ]
           LMI_SELinuxPort REF Port,

           [ IN, Description("New label.") ]
           string Label,

           [ IN(false), OUT ]
           CIM_ConcreteJob REF Job
    );

  [ Description(
    "Set label on an SELinux file."),
    ValueMap {"0", "1", "2", "3", "4", "5", "6"},
    Values {"Job Completed with No Error", "Not Supported",
            "Unknown", "Timeout", "Failed", "Invalid Parameter",
            "In Use"} ]
    uint32 SetFileLabel(
           [ IN, OUT, Description("An SELinux file to change.") ]
           LMI_SELinuxFile REF Target,

           [ IN, Description("New label.") ]
           string Label,

           [ IN(false), OUT ]
           CIM_ConcreteJob REF Job
    );

  [ Description(
    "Set a new value of an SELinux boolean."),
    ValueMap {"0", "1", "2", "3", "4", "5", "6"},
    Values {"Job Completed with No Error", "Not Supported",
            "Unknown", "Timeout", "Failed", "Invalid Parameter",
            "In Use"} ]
    uint32 SetBoolean(
           [ IN, OUT, 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 ]
           CIM_ConcreteJob REF Job
    );

  [ Description(
    "Restore SELinux label on a file to the value specified by the policy."),
    ValueMap {"0", "1", "2", "3", "4", "5", "6"},
    Values {"Job Completed with No Error", "Not Supported",
            "Unknown", "Timeout", "Failed", "Invalid Parameter",
            "In Use"} ]
    uint32 RestoreLabel(
           [ IN, OUT, Description("An SELinux file to change.") ]
           LMI_SELinuxFile REF Target,

           [ 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 ]
           CIM_ConcreteJob REF Job
    );

  [ Description(
    ""),
    ValueMap {"0", "1", "2", "3", "4", "5", "6"},
    Values {"Job Completed with No Error", "Not Supported",
            "Unknown", "Timeout", "Failed", "Invalid Parameter",
            "In Use"} ]
    uint32 GetMislabeledFiles(
           [ IN, OUT, Description("An SELinux file to change.") ]
           LMI_SELinuxFile REF Target,

           [ 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 ]
           CIM_ConcreteJob 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_AffectedSELinuxJobElement : CIM_AffectedJobElement
{
};

[ Version("0.0.1"),
  Association ]
class LMI_HostedSELinuxService : CIM_HostedService
{
};

source

2 Example class instances

selinux-instances.png

Figure 2: SELinux class instances

3 Use cases

All the use cases are described using a pseudo language very similar to what the actual LMIShell looks like.

3.1 Booleans

3.1.1 get selinux boolean

LMI_SELinuxBoolean.first_instance({"InstanceID":"LMI:SELinuxBoolean:use_nfs_home_dirs"})

3.1.2 set selinux boolean

s = LMI_SELinuxService.first_instance()
i = LMI_SELinuxBoolean.first_instance({"InstanceID":"LMI:SELinuxBoolean:use_nfs_home_dirs"})

# set boolean without making it permanent
s.SetBoolean(ref i, True, False)

# set boolean permanently
s.SetBoolean(ref i, True, True)

3.2 Ports

3.2.1 show local ports and their labels

LMI_SELinuxPort.EnumerateInstance()

3.2.2 set label on a port

s = LMI_SELinuxService.first_instance()
i = LMI_SELinuxPort.first_instance({"InstanceID":"LMI:SELinuxPort:TCP:virt_port_t"})

s.SetPortLabel(ref i, "new_label_t")

3.3 Service

For all upcoming operations, the service instance is needed.

s = LMI_SELinuxService.first_instance()

Also, all service methods return job instances with affected elements associated to them.

3.3.1 get selinux state

# getenforce
s.State

# get persistent state (/etc/sysconfig/selinux)
s.DefaultState

3.3.2 set selinux state

# setenforce
s.SetState(Permissive)

# set persistent state (/etc/sysconfig/selinux)
s.SetState(Enforcing, True)

3.3.3 show policy version

s.PolicyVersion

3.3.4 show current mode (enforcing/permissive)

s.State

3.3.5 show default mode (from /etc/sysconfig/selinux; enforcing/permissive/disabled)

s.DefaultState

3.3.6 restore a file label (optionally with subdirectories)

# restore label on a file
# the 'Recursively' argument has no effect on non-directories
s.RestoreLabel(ref "/home/jsynacek/.bashrc")

# restore labels in  /etc recursively
# this may potentially take a while
s.RestoreLabel(ref "/etc", True)

3.3.7 scan a directory and report mislabelled files

# get mislabeled files in /var
s.GetMislabeledFiles(ref "/var")

# get mislabeled files in /var recursively
# this may potentially take a while
s.GetMislabeledFiles(ref "/var", True)

3.3.8 TODO send indication when file gets mislabelled

TBD

4 Additional use cases

If there are any, they will be documented here.

5 Design

Important questions and design desicions will be recorded here, together with a timestamp and version of this document, in which they were made.

Q Question
A Answer
D Desicion

5.1 Q: should LMI_SELinuxSystem properties by set via ModifyInstance() or by LMI_SELinuxService?   v1

A: Use LMI_SELinuxService. Additionally, a job should be spawned, because some selinux operations may take a while.

<2014-04-07 Mon>

5.2 D: put SELinux related methods and general properties under one class (LMI_SELinuxService)   v1

<2014-04-07 Mon>

5.3 D: SELinux files will be called LMI_SELinuxFile   v1

and will have an identity association with LMI_UnixFile.

<2014-04-07 Mon>

5.4 Q: how should be LMI_SELinuxPort handle InstanceIDs, labels and ports?   v1

Based on the original idea, the InstanceID would be specified as LMI:SELinuxPort:<type>. However, this brings some ambiguity, since there can be two ports open, both with the same label but using different protocols.

selinux-ports.png

Figure 3: SELinux ports

In Figure 3, SELinuxPort_original_1 and SELinuxPort_original_2 represent the original model instances. They have the same InstanceID, yet they represent two different objects. This is not acceptable, since InstanceID must be unique.

This can be resolved in multiple ways:

  1. Put protocol into InstanceID. (objects SELinuxPort_solution_1_1 and 1_2)

    pros: no other change needed

    cons: introduces some redundancy

  2. Leave InstanceID as it is and remodel SELinuxPort. (object SELinuxPort_solution_2)

    pros: only one instance is needed

    cons: brings unneeded complexity

  3. Put the whole label into InstanceID.

    In theory, if SELinux labels on ports were guaranteed to be unique, putting the whole label into InstanceID would solve the problem. But that doesn't have to hold true.

A: Solution 1 is, in my opinion, the best way to solve this problem, so I will go with that.

<2014-04-08 Tue>

5.5 D: there is no intention to enable the modification of policy   v1

<2014-04-14 Mon>

6 Older versions

Created by Jan Synáček <jsynacek@redhat.com>, 2014-04-14 Mon 09:29

source