priv.c revision 1.6
1/* $OpenBSD: priv.c,v 1.6 2017/03/02 07:33:37 reyk Exp $ */ 2 3/* 4 * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/param.h> /* nitems */ 20#include <sys/queue.h> 21#include <sys/stat.h> 22#include <sys/socket.h> 23#include <sys/un.h> 24#include <sys/ioctl.h> 25#include <sys/tree.h> 26 27#include <net/if.h> 28#include <netinet/in.h> 29#include <netinet/if_ether.h> 30#include <net/if_bridge.h> 31 32#include <errno.h> 33#include <event.h> 34#include <fcntl.h> 35#include <stdlib.h> 36#include <stdio.h> 37#include <string.h> 38#include <unistd.h> 39#include <signal.h> 40#include <ctype.h> 41 42#include "proc.h" 43#include "vmd.h" 44 45int priv_dispatch_parent(int, struct privsep_proc *, struct imsg *); 46void priv_run(struct privsep *, struct privsep_proc *, void *); 47 48static struct privsep_proc procs[] = { 49 { "parent", PROC_PARENT, priv_dispatch_parent } 50}; 51 52void 53priv(struct privsep *ps, struct privsep_proc *p) 54{ 55 proc_run(ps, p, procs, nitems(procs), priv_run, NULL); 56} 57 58void 59priv_run(struct privsep *ps, struct privsep_proc *p, void *arg) 60{ 61 struct vmd *env = ps->ps_env; 62 63 /* 64 * no pledge(2) in the "priv" process: 65 * write ioctls are not permitted by pledge. 66 */ 67 68 /* Open our own socket for generic interface ioctls */ 69 if ((env->vmd_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 70 fatal("socket"); 71} 72 73int 74priv_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) 75{ 76 const char *desct[] = { "tap", "switch", "bridge", NULL }; 77 struct privsep *ps = p->p_ps; 78 struct vmop_ifreq vfr; 79 struct vmd *env = ps->ps_env; 80 struct ifreq ifr; 81 struct ifbreq ifbr; 82 struct ifgroupreq ifgr; 83 char type[IF_NAMESIZE]; 84 85 switch (imsg->hdr.type) { 86 case IMSG_VMDOP_PRIV_IFDESCR: 87 case IMSG_VMDOP_PRIV_IFCREATE: 88 case IMSG_VMDOP_PRIV_IFADD: 89 case IMSG_VMDOP_PRIV_IFUP: 90 case IMSG_VMDOP_PRIV_IFDOWN: 91 case IMSG_VMDOP_PRIV_IFGROUP: 92 IMSG_SIZE_CHECK(imsg, &vfr); 93 memcpy(&vfr, imsg->data, sizeof(vfr)); 94 95 /* We should not get malicious requests from the parent */ 96 if (priv_getiftype(vfr.vfr_name, type, NULL) == -1 || 97 priv_findname(type, desct) == -1) 98 fatalx("%s: rejected priv operation on interface: %s", 99 __func__, vfr.vfr_name); 100 break; 101 default: 102 return (-1); 103 } 104 105 switch (imsg->hdr.type) { 106 case IMSG_VMDOP_PRIV_IFDESCR: 107 /* Set the interface description */ 108 strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name)); 109 ifr.ifr_data = (caddr_t)vfr.vfr_value; 110 if (ioctl(env->vmd_fd, SIOCSIFDESCR, &ifr) < 0) 111 log_warn("SIOCSIFDESCR"); 112 break; 113 case IMSG_VMDOP_PRIV_IFCREATE: 114 /* Create the bridge if it doesn't exist */ 115 strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name)); 116 if (ioctl(env->vmd_fd, SIOCIFCREATE, &ifr) < 0 && 117 errno != EEXIST) 118 log_warn("SIOCIFCREATE"); 119 break; 120 case IMSG_VMDOP_PRIV_IFADD: 121 if (priv_getiftype(vfr.vfr_value, type, NULL) == -1) 122 fatalx("%s: rejected to add interface: %s", 123 __func__, vfr.vfr_value); 124 125 /* Attach the device to the bridge */ 126 strlcpy(ifbr.ifbr_name, vfr.vfr_name, 127 sizeof(ifbr.ifbr_name)); 128 strlcpy(ifbr.ifbr_ifsname, vfr.vfr_value, 129 sizeof(ifbr.ifbr_ifsname)); 130 if (ioctl(env->vmd_fd, SIOCBRDGADD, &ifbr) < 0 && 131 errno != EEXIST) 132 log_warn("SIOCBRDGADD"); 133 break; 134 case IMSG_VMDOP_PRIV_IFUP: 135 case IMSG_VMDOP_PRIV_IFDOWN: 136 /* Set the interface status */ 137 strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name)); 138 if (ioctl(env->vmd_fd, SIOCGIFFLAGS, &ifr) < 0) { 139 log_warn("SIOCGIFFLAGS"); 140 break; 141 } 142 if (imsg->hdr.type == IMSG_VMDOP_PRIV_IFUP) 143 ifr.ifr_flags |= IFF_UP; 144 else 145 ifr.ifr_flags &= ~IFF_UP; 146 if (ioctl(env->vmd_fd, SIOCSIFFLAGS, &ifr) < 0) 147 log_warn("SIOCSIFFLAGS"); 148 break; 149 case IMSG_VMDOP_PRIV_IFGROUP: 150 if (priv_validgroup(vfr.vfr_value) == -1) 151 fatalx("%s: invalid group name", __func__); 152 153 if (strlcpy(ifgr.ifgr_name, vfr.vfr_name, 154 sizeof(ifgr.ifgr_name)) >= sizeof(ifgr.ifgr_name) || 155 strlcpy(ifgr.ifgr_group, vfr.vfr_value, 156 sizeof(ifgr.ifgr_group)) >= sizeof(ifgr.ifgr_group)) 157 fatalx("%s: group name too long", __func__); 158 159 if (ioctl(env->vmd_fd, SIOCAIFGROUP, &ifgr) < 0 && 160 errno != EEXIST) 161 log_warn("SIOCAIFGROUP"); 162 break; 163 default: 164 return (-1); 165 } 166 167 return (0); 168} 169 170int 171priv_getiftype(char *ifname, char *type, unsigned int *unitptr) 172{ 173 const char *errstr; 174 size_t span; 175 unsigned int unit; 176 177 /* Extract the name part */ 178 span = strcspn(ifname, "0123456789"); 179 if (span == 0 || span >= strlen(ifname) || span >= (IF_NAMESIZE - 1)) 180 return (-1); 181 memcpy(type, ifname, span); 182 type[span] = 0; 183 184 /* Now parse the unit (we don't strictly validate the format here) */ 185 unit = strtonum(ifname + span, 0, UINT_MAX, &errstr); 186 if (errstr != NULL) 187 return (-1); 188 if (unitptr != NULL) 189 *unitptr = unit; 190 191 return (0); 192} 193 194int 195priv_findname(const char *name, const char **names) 196{ 197 unsigned int i; 198 199 for (i = 0; names[i] != NULL; i++) { 200 if (strcmp(name, names[i]) == 0) 201 return (0); 202 } 203 204 return (-1); 205} 206 207int 208priv_validgroup(const char *name) 209{ 210 if (strlen(name) >= IF_NAMESIZE) 211 return (-1); 212 /* Group can not end with a digit */ 213 if (name[0] && isdigit(name[strlen(name) - 1])) 214 return (-1); 215 return (0); 216} 217 218/* 219 * Called from the process peer 220 */ 221 222int 223vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm) 224{ 225 struct vm_create_params *vcp = &vm->vm_params.vmc_params; 226 struct vmd_if *vif; 227 struct vmd_switch *vsw; 228 unsigned int i; 229 struct vmop_ifreq vfr, vfbr; 230 231 for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) { 232 vif = &vm->vm_ifs[i]; 233 234 if (vif->vif_name == NULL) 235 break; 236 237 if (strlcpy(vfr.vfr_name, vif->vif_name, 238 sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name)) 239 return (-1); 240 241 /* Description can be truncated */ 242 (void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value), 243 "vm%u-if%u-%s", vm->vm_vmid, i, vcp->vcp_name); 244 245 log_debug("%s: interface %s description %s", __func__, 246 vfr.vfr_name, vfr.vfr_value); 247 248 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR, 249 &vfr, sizeof(vfr)); 250 251 /* Add interface to bridge/switch */ 252 if ((vsw = switch_getbyname(vif->vif_switch)) != NULL) { 253 if (strlcpy(vfbr.vfr_name, vsw->sw_ifname, 254 sizeof(vfbr.vfr_name)) >= sizeof(vfbr.vfr_name)) 255 return (-1); 256 if (strlcpy(vfbr.vfr_value, vif->vif_name, 257 sizeof(vfbr.vfr_value)) >= sizeof(vfbr.vfr_value)) 258 return (-1); 259 260 log_debug("%s: interface %s add %s", __func__, 261 vfbr.vfr_name, vfbr.vfr_value); 262 263 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFCREATE, 264 &vfbr, sizeof(vfbr)); 265 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD, 266 &vfbr, sizeof(vfbr)); 267 } else if (vif->vif_switch != NULL) 268 log_warnx("switch %s not found", vif->vif_switch); 269 270 /* First group is defined per-interface */ 271 if (vif->vif_group) { 272 if (strlcpy(vfr.vfr_value, vif->vif_group, 273 sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value)) 274 return (-1); 275 276 log_debug("%s: interface %s group %s", __func__, 277 vfr.vfr_name, vfr.vfr_value); 278 279 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFGROUP, 280 &vfr, sizeof(vfr)); 281 } 282 283 /* The second group is defined per-switch */ 284 if (vsw != NULL && vsw->sw_group != NULL) { 285 if (strlcpy(vfr.vfr_value, vsw->sw_group, 286 sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value)) 287 return (-1); 288 289 log_debug("%s: interface %s group %s switch %s", 290 __func__, vfr.vfr_name, vfr.vfr_value, 291 vsw->sw_name); 292 293 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFGROUP, 294 &vfr, sizeof(vfr)); 295 } 296 297 /* Set the new interface status to up or down */ 298 proc_compose(ps, PROC_PRIV, (vif->vif_flags & VMIFF_UP) ? 299 IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN, 300 &vfr, sizeof(vfr)); 301 } 302 303 return (0); 304} 305 306int 307vm_priv_brconfig(struct privsep *ps, struct vmd_switch *vsw) 308{ 309 struct vmd_if *vif; 310 struct vmop_ifreq vfr; 311 312 if (strlcpy(vfr.vfr_name, vsw->sw_ifname, 313 sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name)) 314 return (-1); 315 316 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFCREATE, 317 &vfr, sizeof(vfr)); 318 319 /* Description can be truncated */ 320 (void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value), 321 "switch%u-%s", vsw->sw_id, vsw->sw_name); 322 323 log_debug("%s: interface %s description %s", __func__, 324 vfr.vfr_name, vfr.vfr_value); 325 326 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR, 327 &vfr, sizeof(vfr)); 328 329 TAILQ_FOREACH(vif, &vsw->sw_ifs, vif_entry) { 330 if (strlcpy(vfr.vfr_value, vif->vif_name, 331 sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value)) 332 return (-1); 333 334 log_debug("%s: interface %s add %s", __func__, 335 vfr.vfr_name, vfr.vfr_value); 336 337 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD, 338 &vfr, sizeof(vfr)); 339 } 340 341 /* Set the new interface status to up or down */ 342 proc_compose(ps, PROC_PRIV, (vsw->sw_flags & VMIFF_UP) ? 343 IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN, 344 &vfr, sizeof(vfr)); 345 346 vsw->sw_running = 1; 347 return (0); 348} 349