Skip to content

Commit 10584e2

Browse files
committed
utils: SELinux and SELinux file context
Implement SELinux and SELinux file context classes to check the SELinux context of a file. Signed-off-by: Iker Pedrosa <[email protected]>
1 parent 53c4c4e commit 10584e2

File tree

1 file changed

+103
-0
lines changed

1 file changed

+103
-0
lines changed

pytest_mh/utils/selinux.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""SELinux utilities."""
2+
3+
from __future__ import annotations
4+
5+
import re
6+
7+
from .. import MultihostHost, MultihostUtility
8+
from ..conn import ProcessLogLevel
9+
10+
__all__ = [
11+
"SELinuxContext",
12+
"SELinux",
13+
]
14+
15+
16+
class SELinuxContext(object):
17+
"""
18+
Result of ``ls -Z``
19+
"""
20+
21+
def __init__(self, user: str, role: str, type: str, sensitivity: str, categories: str | None) -> None:
22+
self.user: str = user
23+
"""
24+
SELinux user.
25+
"""
26+
27+
self.role: str = role
28+
"""
29+
SELinux role.
30+
"""
31+
32+
self.type: str = type
33+
"""
34+
SELinux type.
35+
"""
36+
37+
self.sensitivity: str = sensitivity
38+
"""
39+
SELinux sensitivity level.
40+
"""
41+
42+
self.categories: str | None = categories
43+
"""
44+
SELinux categories.
45+
"""
46+
47+
def __str__(self) -> str:
48+
return f"({self.user}:{self.role}:{self.type}:{self.sensitivity}:{self.categories})"
49+
50+
def __repr__(self) -> str:
51+
return str(self)
52+
53+
def __eq__(self, other) -> bool:
54+
if not isinstance(other, SELinuxContext):
55+
return False
56+
57+
return (
58+
self.user == other.user
59+
and self.role == other.role
60+
and self.type == other.type
61+
and self.sensitivity == other.sensitivity
62+
and self.categories == other.categories
63+
)
64+
65+
@classmethod
66+
def FromOutput(cls, stdout: str) -> SELinuxContext:
67+
match = re.match(r"([^ ]+)\s+", stdout)
68+
69+
if not match:
70+
raise ValueError("Unexpected value: expecting space separated string")
71+
72+
selinux_context = match.group(1)
73+
labels = selinux_context.split(":")
74+
75+
if len(labels) == 5:
76+
return cls(labels[0], labels[1], labels[2], labels[3], labels[4])
77+
elif len(labels) == 4:
78+
return cls(labels[0], labels[1], labels[2], labels[3], None)
79+
else:
80+
raise ValueError(f"Unexpected value: got {len(labels)} labels, expecting 4 or 5")
81+
82+
83+
class SELinux(MultihostUtility[MultihostHost]):
84+
"""
85+
SELinux utilities
86+
"""
87+
88+
def get_file_context(self, path: str) -> SELinuxContext | None:
89+
"""
90+
Gets SELinux file context.
91+
92+
:param path: File path.
93+
:type path: str
94+
:return: SELinux file context
95+
:rtype: SELinuxContext or None
96+
"""
97+
self.logger.info(f'Getting SELinux context for "{path}"')
98+
result = self.host.conn.exec(["ls", "-Z", path], log_level=ProcessLogLevel.Error, raise_on_error=False)
99+
100+
if result.rc != 0:
101+
return None
102+
103+
return SELinuxContext.FromOutput(result.stdout)

0 commit comments

Comments
 (0)