aboutsummaryrefslogtreecommitdiff
blob: 171c46cfd64f7f1e5539a6de92a92137267599e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * Copyright 2015-2024 Gentoo Foundation
 * Distributed under the terms of the GNU General Public License v2
 *
 * Copyright 2015-2024 Mike Frysinger  - <vapier@gentoo.org>
 */

#include "paxinc.h"
#include "seccomp-bpf.h"

#ifdef __linux__

/* Older versions of Linux might not have these. */
#ifndef CLONE_NEWIPC
#define CLONE_NEWIPC 0
#endif
#ifndef CLONE_NEWNET
#define CLONE_NEWNET 0
#endif
#ifndef CLONE_NEWNS
#define CLONE_NEWNS 0
#endif
#ifndef CLONE_NEWPID
#define CLONE_NEWPID 0
#endif
#ifndef CLONE_NEWUTS
#define CLONE_NEWUTS 0
#endif

#ifndef PR_SET_SECCOMP
#define PR_SET_SECCOMP 22
#endif
#ifndef SECCOMP_MODE_FILTER
#define SECCOMP_MODE_FILTER 2
#endif

#ifdef __SANITIZE_ADDRESS__
/* ASAN does some weird stuff. */
# define ALLOW_PIDNS 0
# undef WANT_SECCOMP
#else
# define ALLOW_PIDNS 1
#endif

#ifndef SECCOMP_BPF_AVAILABLE
# undef WANT_SECCOMP
#endif

#if PAX_UTILS_LIBFUZZ
# undef WANT_SECCOMP
#endif

static int ns_unshare(int flags)
{
	int flag, ret = 0;

	/* Try to oneshot it.  Maybe we'll get lucky! */
	if (unshare(flags) == 0)
		return flags;
	/* No access at all, so don't waste time below. */
	else if (errno == EPERM)
		return ret;

	/*
	 * We have to run these one permission at a time because if any are
	 * not supported (too old a kernel, or it's disabled), then all of
	 * them will be rejected and we won't know which one is a problem.
	 */

	/* First the ones that work against the current process.  */
	flag = 1;
	while (flags) {
		if (flags & flag) {
			if (unshare(flag) == 0)
				ret |= flag;
			flags &= ~flag;
		}
		flag <<= 1;
	}

	return ret;
}

void security_init_pid(void)
{
	int flags;

	if (!ALLOW_PIDNS || CLONE_NEWPID == 0)
		return;

	flags = ns_unshare(CLONE_NEWPID);
	if (USE_SLOW_SECURITY) {
		if (flags & CLONE_NEWPID)
			if (vfork() == 0)
				_exit(0);
	}
}

void security_init(bool allow_forking)
{
	(void) allow_forking;
	int flags;

	if (!ALLOW_PIDNS)
		allow_forking = true;

	/* Drop all possible caps for us and our children.  */
#ifdef PR_SET_NO_NEW_PRIVS /* New to linux-3.5 */
	prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
#endif
#ifdef PR_SET_SECUREBITS /* New to linux-2.6.26 */
# ifdef SECBIT_KEEP_CAPS_LOCKED /* New to linux-2.6.33 (all SECBIT_xxx) */
	prctl(PR_SET_SECUREBITS,
		SECBIT_KEEP_CAPS_LOCKED |
		SECBIT_NO_SETUID_FIXUP |
		SECBIT_NO_SETUID_FIXUP_LOCKED |
		SECBIT_NOROOT |
		SECBIT_NOROOT_LOCKED, 0, 0, 0);
# endif
#endif

	/* None of the pax tools need access to these features. */
	flags = CLONE_NEWIPC | CLONE_NEWUTS;
	/* Would be nice to leverage mount/net ns, but they're just way too slow. */
	if (USE_SLOW_SECURITY)
		flags |= CLONE_NEWNET | CLONE_NEWNS;
	if (!allow_forking)
		flags |= CLONE_NEWPID;
	flags = ns_unshare(flags);

	if (USE_SLOW_SECURITY) {
		/* We spawn one child and kill it so the kernel will fail in the future. */
		if (flags & CLONE_NEWPID)
			if (vfork() == 0)
				_exit(0);
	}

#ifdef WANT_SECCOMP
	{
	int ret;

	if (allow_forking)
		ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &seccomp_bpf_program_fork);
	else
		ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &seccomp_bpf_program_base);

	if (ret)
		warn("enabling seccomp failed");
	}
#endif
}

#endif