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 } 236
This page was automatically generated by LXR 0.3.1. • OpenWrt