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
, uint64_t retain
)
122 struct __user_cap_header_struct uh
= {};
123 struct __user_cap_data_struct ud
[2];
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
| 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
);
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=%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);
166 if (ocicapset
.effective
!= JAIL_CAP_ALL
) {
167 ud
[0].effective
= (ocicapset
.effective
| retain
) & 0xFFFFFFFFU
;
168 ud
[1].effective
= ((ocicapset
.effective
| retain
) >> 32) & 0xFFFFFFFFU
;
171 if (ocicapset
.permitted
!= JAIL_CAP_ALL
) {
172 ud
[0].permitted
= (ocicapset
.permitted
| retain
) & 0xFFFFFFFFU
;
173 ud
[1].permitted
= ((ocicapset
.permitted
| retain
) >> 32) & 0xFFFFFFFFU
;
176 if (ocicapset
.inheritable
!= JAIL_CAP_ALL
) {
177 ud
[0].inheritable
= (ocicapset
.inheritable
| retain
) & 0xFFFFFFFFU
;
178 ud
[1].inheritable
= ((ocicapset
.inheritable
| retain
) >> 32) & 0xFFFFFFFFU
;
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);
186 if (capset(&uh
, ud
)) {
187 ERROR("capset() failed\n");
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) {
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
);
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
);
218 int parseOCIcapabilities_from_file(struct jail_capset
*capset
, const char *file
)
220 struct blob_buf b
= { 0 };
223 blob_buf_init(&b
, 0);
224 ret
= !blobmsg_add_json_from_file(&b
, file
);
226 ERROR("failed to load %s\n", file
);
230 ret
= parseOCIcapabilities(capset
, b
.head
);