trace: use standard POSIX header for basename()
[project/procd.git] / jail / capabilities.c
1 /*
2 * Copyright (C) 2015 Etienne CHAMPETIER <champetier.etienne@gmail.com>
3 * Copyright (C) 2020 Daniel Golle <daniel@makrotopia.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License version 2.1
7 * as published by the Free Software Foundation
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15 #define _GNU_SOURCE 1
16 #include <syslog.h>
17 #include <sys/prctl.h>
18 #include <libubox/blobmsg.h>
19 #include <libubox/blobmsg_json.h>
20
21 #include "log.h"
22 #include "../capabilities-names.h"
23 #include "capabilities.h"
24
25 #define JAIL_CAP_ERROR (1LLU << (CAP_LAST_CAP+1))
26 #define JAIL_CAP_ALL (0xffffffffffffffffLLU)
27
28 static int find_capabilities(const char *name)
29 {
30 int i;
31
32 for (i = 0; i <= CAP_LAST_CAP; i++)
33 if (capabilities_names[i] && !strcasecmp(capabilities_names[i], name))
34 return i;
35
36 return -1;
37 }
38
39 enum {
40 OCI_CAPABILITIES_BOUNDING,
41 OCI_CAPABILITIES_EFFECTIVE,
42 OCI_CAPABILITIES_INHERITABLE,
43 OCI_CAPABILITIES_PERMITTED,
44 OCI_CAPABILITIES_AMBIENT,
45 __OCI_CAPABILITIES_MAX
46 };
47
48 static const struct blobmsg_policy oci_capabilities_policy[] = {
49 [OCI_CAPABILITIES_BOUNDING] = { "bounding", BLOBMSG_TYPE_ARRAY },
50 [OCI_CAPABILITIES_EFFECTIVE] = { "effective", BLOBMSG_TYPE_ARRAY },
51 [OCI_CAPABILITIES_INHERITABLE] = { "inheritable", BLOBMSG_TYPE_ARRAY },
52 [OCI_CAPABILITIES_PERMITTED] = { "permitted", BLOBMSG_TYPE_ARRAY },
53 [OCI_CAPABILITIES_AMBIENT] = { "ambient", BLOBMSG_TYPE_ARRAY },
54 };
55
56 static uint64_t parseOCIcap(struct blob_attr *msg)
57 {
58 struct blob_attr *cur;
59 int rem;
60 uint64_t caps = 0;
61 int capnum;
62
63 /* each capset is optional, set all-1 mask if absent */
64 if (!msg)
65 return JAIL_CAP_ALL;
66
67 blobmsg_for_each_attr(cur, msg, rem) {
68 capnum = find_capabilities(blobmsg_get_string(cur));
69 if (capnum < 0)
70 return JAIL_CAP_ERROR;
71
72 caps |= (1LLU << capnum);
73 }
74
75 return caps;
76 }
77
78 int parseOCIcapabilities(struct jail_capset *capset, struct blob_attr *msg)
79 {
80 struct blob_attr *tb[__OCI_CAPABILITIES_MAX];
81 uint64_t caps;
82 blobmsg_parse(oci_capabilities_policy, __OCI_CAPABILITIES_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
83
84 caps = parseOCIcap(tb[OCI_CAPABILITIES_BOUNDING]);
85 if (caps == JAIL_CAP_ERROR)
86 return EINVAL;
87 else
88 capset->bounding = caps;
89
90 caps = parseOCIcap(tb[OCI_CAPABILITIES_EFFECTIVE]);
91 if (caps == JAIL_CAP_ERROR)
92 return EINVAL;
93 else
94 capset->effective = caps;
95
96 caps = parseOCIcap(tb[OCI_CAPABILITIES_INHERITABLE]);
97 if (caps == JAIL_CAP_ERROR)
98 return EINVAL;
99 else
100 capset->inheritable = caps;
101
102 caps = parseOCIcap(tb[OCI_CAPABILITIES_PERMITTED]);
103 if (caps == JAIL_CAP_ERROR)
104 return EINVAL;
105 else
106 capset->permitted = caps;
107
108 caps = parseOCIcap(tb[OCI_CAPABILITIES_AMBIENT]);
109 if (caps == JAIL_CAP_ERROR)
110 return EINVAL;
111 else
112 capset->ambient = caps;
113
114 capset->apply = 1;
115
116 return 0;
117 }
118
119
120 int applyOCIcapabilities(struct jail_capset ocicapset, uint64_t retain)
121 {
122 struct __user_cap_header_struct uh = {};
123 struct __user_cap_data_struct ud[2];
124 int cap;
125 int is_set;
126
127 if (!ocicapset.apply)
128 return 0;
129
130 /* drop from bounding set */
131 if (ocicapset.bounding != JAIL_CAP_ALL) {
132 for (cap = 0; cap <= CAP_LAST_CAP; cap++) {
133 if (!prctl(PR_CAPBSET_READ, cap, 0, 0, 0)) {
134 /* can't raise */
135 if (ocicapset.bounding & (1LLU << cap))
136 ERROR("capability %s (%d) is not in bounding set\n", capabilities_names[cap], cap);
137
138 continue;
139 }
140 if ( ((ocicapset.bounding | retain) & (1LLU << cap)) == 0) {
141 DEBUG("dropping capability %s (%d) from bounding set\n", capabilities_names[cap], cap);
142 if (prctl(PR_CAPBSET_DROP, cap, 0, 0, 0)) {
143 ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %m\n", cap);
144 return errno;
145 }
146 } else {
147 DEBUG("keeping capability %s (%d)\n", capabilities_names[cap], cap);
148 }
149 }
150 }
151
152 /* set effective, permitted and inheritable */
153 uh.version = _LINUX_CAPABILITY_VERSION_3;
154 uh.pid = getpid();
155
156 if (capget(&uh, ud)) {
157 ERROR("capget() failed\n");
158 return -1;
159 }
160
161 DEBUG("old capabilities: Pe=%016llx Pp=%016llx Pi=%016llx\n",
162 0LLU | ud[0].effective | (0LLU | ud[1].effective) << 32,
163 0LLU | ud[0].permitted | (0LLU | ud[1].permitted) << 32,
164 0LLU | ud[0].inheritable | (0LLU | ud[1].inheritable) << 32);
165
166 if (ocicapset.effective != JAIL_CAP_ALL) {
167 ud[0].effective = (ocicapset.effective | retain) & 0xFFFFFFFFU;
168 ud[1].effective = ((ocicapset.effective | retain) >> 32) & 0xFFFFFFFFU;
169 }
170
171 if (ocicapset.permitted != JAIL_CAP_ALL) {
172 ud[0].permitted = (ocicapset.permitted | retain) & 0xFFFFFFFFU;
173 ud[1].permitted = ((ocicapset.permitted | retain) >> 32) & 0xFFFFFFFFU;
174 }
175
176 if (ocicapset.inheritable != JAIL_CAP_ALL) {
177 ud[0].inheritable = (ocicapset.inheritable | retain) & 0xFFFFFFFFU;
178 ud[1].inheritable = ((ocicapset.inheritable | retain) >> 32) & 0xFFFFFFFFU;
179 }
180
181 DEBUG("new capabilities: Pe=%016llx Pp=%016llx Pi=%016llx\n",
182 0LLU | ud[0].effective | (0LLU | ud[1].effective) << 32,
183 0LLU | ud[0].permitted | (0LLU | ud[1].permitted) << 32,
184 0LLU | ud[0].inheritable | (0LLU | ud[1].inheritable) << 32);
185
186 if (capset(&uh, ud)) {
187 ERROR("capset() failed\n");
188 return -1;
189 }
190
191 /* edit ambient set */
192 if (ocicapset.ambient != JAIL_CAP_ALL) {
193 for (cap = 0; cap <= CAP_LAST_CAP; cap++) {
194 is_set = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, cap, 0, 0);
195 if ( (ocicapset.ambient & (1LLU << cap)) == 0) {
196 if (is_set) {
197 DEBUG("dropping capability %s (%d) from ambient set\n", capabilities_names[cap], cap);
198 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, cap, 0, 0)) {
199 ERROR("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, %d, 0, 0) failed: %m\n", cap);
200 return errno;
201 }
202 }
203 } else {
204 if (!is_set) {
205 DEBUG("raising capability %s (%d) to ambient set\n", capabilities_names[cap], cap);
206 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0)) {\
207 ERROR("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %d, 0, 0) failed: %m\n", cap);
208 return errno;
209 }
210 }
211 }
212 }
213 }
214
215 return 0;
216 }
217
218 int parseOCIcapabilities_from_file(struct jail_capset *capset, const char *file)
219 {
220 struct blob_buf b = { 0 };
221 int ret;
222
223 blob_buf_init(&b, 0);
224 ret = !blobmsg_add_json_from_file(&b, file);
225 if (ret) {
226 ERROR("failed to load %s\n", file);
227 goto err;
228 }
229
230 ret = parseOCIcapabilities(capset, b.head);
231
232 err:
233 blob_buf_free(&b);
234 return ret;
235 }