OpenLMI SELinux Design Document
Table of Contents
- 1. Model
- 2. Example class instances
- 3. Use cases
- 3.1. Booleans
- 3.2. Ports
- 3.3. Service
- 3.3.1. get selinux state
- 3.3.2. set selinux state
- 3.3.3. show policy version
- 3.3.4. show current mode (enforcing/permissive)
- 3.3.5. show default mode (from /etc/sysconfig/selinux; enforcing/permissive/disabled)
- 3.3.6. restore a file label (optionally with subdirectories)
- 3.3.7. scan a directory and report mislabelled files
- 3.3.8. TODO send indication when file gets mislabelled
- 4. Additional use cases
- 5. Design
- 5.1. Q: Should LMI_SELinuxSystem properties by set via ModifyInstance() or by LMI_SELinuxService? v1
- 5.2. D: Put SELinux related methods and general properties under one class (LMI_SELinuxService). v1
- 5.3. D: SELinux files will be called LMI_SELinuxFile. v1
- 5.4. Q: How should be LMI_SELinuxPort handle InstanceIDs, labels and ports? v1
- 5.5. D: There is no intention to enable the modification of policy. v1
- 5.6. Q: There is already LMI_UnixFile with context properties. Why additional SELinux class for that? v2
- 5.7. D: LMI_SELinuxService will use a string for its policy type property. v2
- 5.8. Q: Why not make LMI_SElinuxService.SetSELinuxState() take NewState and NewDefaultState instead? v2
- 6. Older versions
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
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("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_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'." "Note that a network port can be labeled with multiple labels at the same time.") ] string Ports[]; }; [ Version("0.0.1"), Description( "Class representing an SELinux file. Instances of this class " "should be connected with existing LMI_UnixFile instances with " "an identity association class. See LMI_SELinuxFileIdentity.") ] 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") ] 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"}, Values {"Job Completed with No Error", "Not Supported", "Unknown", "Timeout", "Failed", "Invalid Parameter", "In Use"} ] 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"}, 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 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"}, 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 ] LMI_SELinuxJob 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 ] LMI_SELinuxJob REF Job ); [ Description( "Take action on an SELinux file.\n" "Possible actions are:\n" " ListMislabeledFiles : List files whose SELinux label is different than the one specified by the policy.\n" " RestoreMislabeledFiles : Restore SELinux label on files to the respective values specified by the policy.\n"), ValueMap {"0", "1", "2", "3", "4", "5", "6"}, Values {"Job Completed with No Error", "Not Supported", "Unknown", "Timeout", "Failed", "Invalid Parameter", "In Use"} ] uint32 ProcessTarget( [ IN, OUT, Description("An SELinux file to change.") ] LMI_SELinuxFile REF Target, [ IN, Description(""), ValueMap {"0", "1", ".."}, Values {"ListMislabeledFiles", "RestoreMislabeledFiles", "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_AffectedSELinuxJobElement : CIM_AffectedJobElement { }; [ Version("0.0.1"), Association ] class LMI_HostedSELinuxService : CIM_HostedService { };
2 Example class instances
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.SetSELinuxState(Permissive) # set persistent state (/etc/sysconfig/selinux) s.SetSELinuxState(Enforcing, True)
3.3.3 show policy version
s.PolicyVersion
3.3.4 show current mode (enforcing/permissive)
s.SELinuxState
3.3.5 show default mode (from /etc/sysconfig/selinux; enforcing/permissive/disabled)
s.SELinuxDefaultState
3.3.6 restore a file label (optionally with subdirectories)
# restore label on a file # the 'Recursively' argument has no effect on non-directories i = LMI_SELinuxFile.get_instance({"InstanceID":"LMI:SELinuxFile:/home/jsynacek/.bashrc"}) s.ProcessTarget(ref i, 1<"RestoreMislabeledFiles">) # restore labels in /etc recursively # this may potentially take a while i = LMI_SELinuxFile.get_instance({"InstanceID":"LMI:SELinuxFile:/etc"}) s.ProcessTarget(ref i, 1<"RestoreMislabeledFiles">, True)
3.3.7 scan a directory and report mislabelled files
# get mislabeled files in /var i = LMI_SELinuxFile.get_instance({"InstanceID":"LMI:SELinuxFile:/var"}) s.ProcessTarget(ref i, 0<"ListMislabeledFiles">, False) # get mislabeled files in /var recursively # this may potentially take a while s.ProcessTarget(ref i, 0<"ListMislabeledFiles">, 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.
5.2 D: Put SELinux related methods and general properties under one class (LMI_SELinuxService). v1
5.3 D: SELinux files will be called LMI_SELinuxFile. v1
and will have an identity association with LMI_UnixFile.
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.
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:
- Put protocol into InstanceID. (objects SELinuxPort_solution_1_1 and 1_2)
pros: no other change needed
cons: introduces some redundancy
- Leave InstanceID as it is and remodel SELinuxPort. (object SELinuxPort_solution_2)
pros: only one instance is needed
cons: brings unneeded complexity
- 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.
5.5 D: There is no intention to enable the modification of policy. v1
5.6 Q: There is already LMI_UnixFile with context properties. Why additional SELinux class for that? v2
A: I wanted the file class to fit into the whole model – inherit from LMI_SELinuxElement. I know about the contexts in LMI_UnixFile and they should be removed from there. The ideal solution would probably be if LMI_UnixFile inherited from LMI_SELinuxElement, which would be multiple inheritance and that is not allowed.
Since the path to a file is a primary key (we care about Unixy systems), it is common sense and the simplest solution to use paths to get references. InstanceID can be used for that. It may seem like a hack at first, and certainly not the CIM way, but, in my opinion, is the best solution.
As a bonus, if there is no SELinux on the managed machine, no SELinux* classes will be present and everything will fit.
5.7 D: LMI_SELinuxService will use a string for its policy type property. v2
Although there are some common names for policy types, like Targeted, Strict, MLS, it is probably better to use simple string, instead of an enumeration type. It isn't clear whether the common names may change or custom names can be used.
5.8 Q: Why not make LMI_SElinuxService.SetSELinuxState() take NewState and NewDefaultState instead? v2
A: It may appear as a better solution, but if the method is called with both set, and for some reason, setting of only one of them fails, it is not clear what should happen. Should the state be rolled back? Should the method return a special return code saying that it was possible to set only one them?
It is more simple to just call the current method twice and have the operation "atomic".