Skip to content

Commit 04f8230

Browse files
libselinux: add selinux_unshare()
Add a selinux_unshare() interface to unshare the SELinux namespace, and a unshareselinux utility to run a shell or command in its own SELinux and mount namespaces. The selinux_unshare() interface expects the caller to have already unshared its mount namespace and created a MS_REC|MS_PRIVATE mount of / prior to invoking it so that it can freely modify the selinuxfs mount as needed by the unshare operation. The unshareselinux utility demonstrates how to do this prior to calling selinux_unshare(). Upon a successful return from selinux_unshare(), the SELinux namespace will be unshared and there will be no selinuxfs mount on /sys/fs/selinux. The caller can then proceed to mount a new selinuxfs filesystem private to the new namespace, load a policy, set enforcing mode, etc, as is commonly handled by init/systemd during boot. If/when this gets merged, the new selinux_unshare() symbol should be moved to its own version in libselinux.map. Signed-off-by: Stephen Smalley <[email protected]>
1 parent a5763ea commit 04f8230

File tree

5 files changed

+145
-0
lines changed

5 files changed

+145
-0
lines changed

libselinux/include/selinux/selinux.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,19 @@ extern int selinux_lsetfilecon_default(const char *path);
760760
*/
761761
extern void selinux_reset_config(void);
762762

763+
/*
764+
* Unshare the SELinux namespace.
765+
* Prior to invoking this API, the caller must have unshared the
766+
* mount namespace (CLONE_NEWNS) and mounted a MS_REC|MS_PRIVATE
767+
* / filesystem so that selinux_unshare() can freely modify any
768+
* existing selinuxfs mount as needed for the unshare.
769+
* Returns 0 on success, in which case the SELinux namespace has
770+
* been unshared and any old selinuxfs mount will have been unmounted.
771+
* The caller can then proceed to mount a new selinuxfs filesystem
772+
* for the new namespace, load a policy, set enforcing mode, etc.
773+
*/
774+
extern int selinux_unshare(void);
775+
763776
#ifdef __cplusplus
764777
}
765778
#endif

libselinux/src/libselinux.map

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ LIBSELINUX_3.5 {
251251
global:
252252
getpidprevcon;
253253
getpidprevcon_raw;
254+
selinux_unshare;
254255
} LIBSELINUX_3.4;
255256

256257
LIBSELINUX_3.8 {

libselinux/src/unshare.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#ifndef _GNU_SOURCE
2+
#define _GNU_SOURCE
3+
#endif
4+
5+
#include <sched.h>
6+
#include <stdint.h>
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include <fcntl.h>
10+
#include <unistd.h>
11+
#include <errno.h>
12+
#include <sys/mount.h>
13+
#include <sys/vfs.h>
14+
#include <sys/statvfs.h>
15+
16+
#include "selinux_internal.h"
17+
#include "policy.h"
18+
19+
/*
20+
* Precondition: caller must have already done unshare(CLONE_NEWNS) or
21+
* been created via clone(CLONE_NEWNS) and mounted a MS_REC|MS_PRIVATE
22+
* / filesystem so that any pre-existing selinuxfs mount can be
23+
* modified freely by selinux_unshare(). See ../utils/unshareselinux.c
24+
* for an example.
25+
*/
26+
int selinux_unshare(void)
27+
{
28+
struct statfs fs;
29+
int ret, fd;
30+
char buf[3] = "1\n";
31+
32+
ret = statfs(SELINUXMNT, &fs);
33+
if (ret < 0) {
34+
/*
35+
* Should we try to handle this gracefully?
36+
* If it fails due to /sys not being mounted, then we could
37+
* mount sysfs and try to continue here.
38+
*/
39+
return ret;
40+
}
41+
42+
if ((uint32_t) fs.f_type == (uint32_t) SELINUX_MAGIC) {
43+
if (fs.f_flags & ST_RDONLY) {
44+
/*
45+
* selinuxfs is mounted read-only; try re-mounting
46+
* read-write temporarily so that we can unshare it.
47+
*/
48+
ret = mount(SELINUXFS, SELINUXMNT, SELINUXFS,
49+
MS_REMOUNT | MS_BIND | MS_NOEXEC |
50+
MS_NOSUID, 0);
51+
if (ret < 0)
52+
return -1;
53+
}
54+
} else {
55+
/*
56+
* selinuxfs is not mounted; try mounting it temporarily so
57+
* that we can unshare it.
58+
*/
59+
ret = mount(SELINUXFS, SELINUXMNT, SELINUXFS,
60+
MS_NOEXEC | MS_NOSUID, 0);
61+
if (ret < 0)
62+
return -1;
63+
}
64+
65+
/*
66+
* Unshare SELinux namespace
67+
*/
68+
fd = open(SELINUXMNT "/unshare", O_WRONLY);
69+
if (fd < 0)
70+
return -1;
71+
ret = write(fd, buf, sizeof(buf));
72+
if (ret != sizeof(buf)) {
73+
int sv_errno = errno;
74+
close(fd);
75+
errno = sv_errno;
76+
return -1;
77+
}
78+
close(fd);
79+
80+
/*
81+
* Now unmount the old selinuxfs which refers to the old/parent namespace
82+
*/
83+
ret = umount(SELINUXMNT);
84+
if (ret < 0)
85+
return ret;
86+
87+
return 0;
88+
}

libselinux/utils/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ setfilecon
3030
togglesebool
3131
selinux_check_access
3232
validatetrans
33+
unshareselinux

libselinux/utils/unshareselinux.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#ifndef _GNU_SOURCE
2+
#define _GNU_SOURCE
3+
#endif
4+
5+
#include <sched.h>
6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
#include <unistd.h>
9+
#include <errno.h>
10+
#include <sys/mount.h>
11+
#include <selinux/selinux.h>
12+
13+
int main(int argc, char **argv)
14+
{
15+
int ret;
16+
17+
ret = unshare(CLONE_NEWNS);
18+
if (ret < 0) {
19+
perror("unshare(CLONE_NEWNS)");
20+
exit(1);
21+
}
22+
23+
ret = mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL);
24+
if (ret < 0) {
25+
perror("mount(/)");
26+
exit(1);
27+
}
28+
29+
ret = selinux_unshare();
30+
if (ret < 0) {
31+
perror("selinux_unshare");
32+
exit(1);
33+
}
34+
35+
if (argc < 2) {
36+
fprintf(stderr, "usage: %s command args...\n", argv[0]);
37+
exit(1);
38+
}
39+
40+
execvp(argv[1], &argv[1]);
41+
perror(argv[1]);
42+
}

0 commit comments

Comments
 (0)