2 * Copyright (C) 2015 Etienne CHAMPETIER <champetier.etienne@gmail.com>
3 * Copyright (C) 2020 Daniel Golle <daniel@makrotopia.org>
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
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.
17 #include <sys/prctl.h>
18 #include <libubox/blobmsg.h>
19 #include <libubox/blobmsg_json.h>
22 #include "../capabilities-names.h"
23 #include "capabilities.h"
25 #define JAIL_CAP_ERROR (1LLU << (CAP_LAST_CAP+1))
26 #define JAIL_CAP_ALL (0xffffffffffffffffLLU)
28 static int find_capabilities(const char *name
)
32 for (i
= 0; i
<= CAP_LAST_CAP
; i
++)
33 if (capabilities_names
[i
] && !strcasecmp(capabilities_names
[i
], name
))
40 OCI_CAPABILITIES_BOUNDING
,
41 OCI_CAPABILITIES_EFFECTIVE
,
42 OCI_CAPABILITIES_INHERITABLE
,
43 OCI_CAPABILITIES_PERMITTED
,
44 OCI_CAPABILITIES_AMBIENT
,
45 __OCI_CAPABILITIES_MAX
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
},
56 static uint64_t parseOCIcap(struct blob_attr
*msg
)
58 struct blob_attr
*cur
;
63 /* each capset is optional, set all-1 mask if absent */
67 blobmsg_for_each_attr(cur
, msg
, rem
) {
68 capnum
= find_capabilities(blobmsg_get_string(cur
));
70 return JAIL_CAP_ERROR
;
72 caps
|= (1LLU << capnum
);
78 int parseOCIcapabilities(struct jail_capset
*capset
, struct blob_attr
*msg
)
80 struct blob_attr
*tb
[__OCI_CAPABILITIES_MAX
];
82 blobmsg_parse(oci_capabilities_policy
, __OCI_CAPABILITIES_MAX
, tb
, blobmsg_data(msg
), blobmsg_len(msg
));
84 caps
= parseOCIcap(tb
[OCI_CAPABILITIES_BOUNDING
]);
85 if (caps
== JAIL_CAP_ERROR
)
88 capset
->bounding
= caps
;
90 caps
= parseOCIcap(tb
[OCI_CAPABILITIES_EFFECTIVE
]);
91 if (caps
== JAIL_CAP_ERROR
)
94 capset
->effective
= caps
;
96 caps
= parseOCIcap(tb
[OCI_CAPABILITIES_INHERITABLE
]);
97 if (caps
== JAIL_CAP_ERROR
)
100 capset
->inheritable
= caps
;
102 caps
= parseOCIcap(tb
[OCI_CAPABILITIES_PERMITTED
]);
103 if (caps
== JAIL_CAP_ERROR
)
106 capset
->permitted
= caps
;
108 caps
= parseOCIcap(tb
[OCI_CAPABILITIES_AMBIENT
]);
109 if (caps
== JAIL_CAP_ERROR
)
112 capset
->ambient
= caps
;
120 int applyOCIcapabilities(struct jail_capset ocicapset
)
122 struct __user_cap_header_struct uh
= {};
123 struct __user_cap_data_struct ud
;
127 if (!ocicapset
.apply
)
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)) {
135 if (ocicapset
.bounding
& (1LLU << cap
))
136 ERROR("capability %s (%d) is not in bounding set\n", capabilities_names
[cap
], cap
);
140 if ( (ocicapset
.bounding
& (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
);
147 DEBUG("keeping capability %s (%d)\n", capabilities_names
[cap
], cap
);
152 /* set effective, permitted and inheritable */
153 uh
.version
= _LINUX_CAPABILITY_VERSION_3
;
156 if (capget(&uh
, &ud
)) {
157 ERROR("capget() failed\n");
161 DEBUG("old capabilities: Pe=%08x Pp=%08x Pi=%08x\n", ud
.effective
, ud
.permitted
, ud
.inheritable
);
163 if (ocicapset
.effective
!= JAIL_CAP_ALL
)
164 ud
.effective
= ocicapset
.effective
;
166 if (ocicapset
.permitted
!= JAIL_CAP_ALL
)
167 ud
.permitted
= ocicapset
.permitted
;
169 if (ocicapset
.inheritable
!= JAIL_CAP_ALL
)
170 ud
.inheritable
= ocicapset
.inheritable
;
172 DEBUG("new capabilities: Pe=%08x Pp=%08x Pi=%08x\n", ud
.effective
, ud
.permitted
, ud
.inheritable
);
174 if (capset(&uh
, &ud
)) {
175 ERROR("capset() failed\n");
179 /* edit ambient set */
180 if (ocicapset
.ambient
!= JAIL_CAP_ALL
) {
181 for (cap
= 0; cap
<= CAP_LAST_CAP
; cap
++) {
182 is_set
= prctl(PR_CAP_AMBIENT
, PR_CAP_AMBIENT_IS_SET
, cap
, 0, 0);
183 if ( (ocicapset
.ambient
& (1LLU << cap
)) == 0) {
185 DEBUG("dropping capability %s (%d) from ambient set\n", capabilities_names
[cap
], cap
);
186 if (prctl(PR_CAP_AMBIENT
, PR_CAP_AMBIENT_LOWER
, cap
, 0, 0)) {
187 ERROR("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, %d, 0, 0) failed: %m\n", cap
);
193 DEBUG("raising capability %s (%d) to ambient set\n", capabilities_names
[cap
], cap
);
194 if (prctl(PR_CAP_AMBIENT
, PR_CAP_AMBIENT_RAISE
, cap
, 0, 0)) {\
195 ERROR("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %d, 0, 0) failed: %m\n", cap
);
206 int drop_capabilities(const char *file
)
213 static const struct blobmsg_policy policy
[__CAP_MAX
] = {
214 [CAP_KEEP
] = { .name
= "cap.keep", .type
= BLOBMSG_TYPE_ARRAY
},
215 [CAP_DROP
] = { .name
= "cap.drop", .type
= BLOBMSG_TYPE_ARRAY
},
217 struct blob_buf b
= { 0 };
218 struct blob_attr
*tb
[__CAP_MAX
];
219 struct blob_attr
*cur
;
222 uint64_t capdrop
= 0LLU;
224 DEBUG("dropping capabilities\n");
226 blob_buf_init(&b
, 0);
227 if (!blobmsg_add_json_from_file(&b
, file
)) {
228 ERROR("failed to load %s\n", file
);
232 blobmsg_parse(policy
, __CAP_MAX
, tb
, blob_data(b
.head
), blob_len(b
.head
));
233 if (!tb
[CAP_KEEP
] && !tb
[CAP_DROP
]) {
234 ERROR("failed to parse %s\n", file
);
238 blobmsg_for_each_attr(cur
, tb
[CAP_KEEP
], rem
) {
239 name
= blobmsg_get_string(cur
);
241 ERROR("invalid capability name in cap.keep\n");
244 cap
= find_capabilities(name
);
246 ERROR("unknown capability %s in cap.keep\n", name
);
249 capdrop
|= (1LLU << cap
);
252 if (capdrop
== 0LLU) {
253 DEBUG("cap.keep empty -> only dropping capabilities from cap.drop (blacklist)\n");
254 capdrop
= JAIL_CAP_ALL
;
256 DEBUG("cap.keep has at least one capability -> dropping every capabilities not in cap.keep (whitelist)\n");
259 blobmsg_for_each_attr(cur
, tb
[CAP_DROP
], rem
) {
260 name
= blobmsg_get_string(cur
);
262 ERROR("invalid capability name in cap.drop\n");
265 cap
= find_capabilities(name
);
267 ERROR("unknown capability %s in cap.drop\n", name
);
270 capdrop
&= ~(1LLU << cap
);
273 for (cap
= 0; cap
<= CAP_LAST_CAP
; cap
++) {
274 if ( (capdrop
& (1LLU << cap
)) == 0) {
275 DEBUG("dropping capability %s (%d)\n", capabilities_names
[cap
], cap
);
276 if (prctl(PR_CAPBSET_DROP
, cap
, 0, 0, 0)) {
277 ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %m\n", cap
);
281 DEBUG("keeping capability %s (%d)\n", capabilities_names
[cap
], cap
);