priv.c revision 1.2
1/* $OpenBSD: priv.c,v 1.2 2016/10/05 17:30:13 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 41#include "proc.h" 42#include "vmd.h" 43 44int priv_dispatch_parent(int, struct privsep_proc *, struct imsg *); 45void priv_run(struct privsep *, struct privsep_proc *, void *); 46 47static struct privsep_proc procs[] = { 48 { "parent", PROC_PARENT, priv_dispatch_parent } 49}; 50 51void 52priv(struct privsep *ps, struct privsep_proc *p) 53{ 54 proc_run(ps, p, procs, nitems(procs), priv_run, NULL); 55} 56 57void 58priv_run(struct privsep *ps, struct privsep_proc *p, void *arg) 59{ 60 struct vmd *env = ps->ps_env; 61 62 /* 63 * no pledge(2) in the "priv" process: 64 * write ioctls are not permitted by pledge. 65 */ 66 67 /* Open our own socket for generic interface ioctls */ 68 if ((env->vmd_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 69 fatal("socket"); 70} 71 72int 73priv_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) 74{ 75 const char *desct[] = { "tap", "switch", "bridge", NULL }; 76 struct privsep *ps = p->p_ps; 77 struct vmop_ifreq vfr; 78 struct vmd *env = ps->ps_env; 79 struct ifreq ifr; 80 struct ifbreq ifbr; 81 char type[IF_NAMESIZE]; 82 83 switch (imsg->hdr.type) { 84 case IMSG_VMDOP_PRIV_IFDESCR: 85 case IMSG_VMDOP_PRIV_IFCREATE: 86 case IMSG_VMDOP_PRIV_IFADD: 87 case IMSG_VMDOP_PRIV_IFUP: 88 case IMSG_VMDOP_PRIV_IFDOWN: 89 IMSG_SIZE_CHECK(imsg, &vfr); 90 memcpy(&vfr, imsg->data, sizeof(vfr)); 91 92 /* We should not get malicious requests from the parent */ 93 if (priv_getiftype(vfr.vfr_name, type, NULL) == -1 || 94 priv_findname(type, desct) == -1) 95 fatalx("%s: rejected priv operation on interface: %s", 96 __func__, vfr.vfr_name); 97 break; 98 default: 99 return (-1); 100 } 101 102 switch (imsg->hdr.type) { 103 case IMSG_VMDOP_PRIV_IFDESCR: 104 /* Set the interface description */ 105 strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name)); 106 ifr.ifr_data = (caddr_t)vfr.vfr_value; 107 if (ioctl(env->vmd_fd, SIOCSIFDESCR, &ifr) < 0) 108 log_warn("SIOCSIFDESCR"); 109 break; 110 case IMSG_VMDOP_PRIV_IFCREATE: 111 /* Create the bridge if it doesn't exist */ 112 strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name)); 113 if (ioctl(env->vmd_fd, SIOCIFCREATE, &ifr) < 0 && 114 errno != EEXIST) 115 log_warn("SIOCIFCREATE"); 116 break; 117 case IMSG_VMDOP_PRIV_IFADD: 118 if (priv_getiftype(vfr.vfr_value, type, NULL) == -1) 119 fatalx("%s: rejected to add interface: %s", 120 __func__, vfr.vfr_value); 121 122 /* Attach the device to the bridge */ 123 strlcpy(ifbr.ifbr_name, vfr.vfr_name, 124 sizeof(ifbr.ifbr_name)); 125 strlcpy(ifbr.ifbr_ifsname, vfr.vfr_value, 126 sizeof(ifbr.ifbr_ifsname)); 127 if (ioctl(env->vmd_fd, SIOCBRDGADD, &ifbr) < 0 && 128 errno != EEXIST) 129 log_warn("SIOCBRDGADD"); 130 break; 131 case IMSG_VMDOP_PRIV_IFUP: 132 case IMSG_VMDOP_PRIV_IFDOWN: 133 /* Set the interface status */ 134 strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name)); 135 if (ioctl(env->vmd_fd, SIOCGIFFLAGS, &ifr) < 0) { 136 log_warn("SIOCGIFFLAGS"); 137 break; 138 } 139 if (imsg->hdr.type == IMSG_VMDOP_PRIV_IFUP) 140 ifr.ifr_flags |= IFF_UP; 141 else 142 ifr.ifr_flags &= ~IFF_UP; 143 if (ioctl(env->vmd_fd, SIOCSIFFLAGS, &ifr) < 0) 144 log_warn("SIOCSIFFLAGS"); 145 break; 146 default: 147 return (-1); 148 } 149 150 return (0); 151} 152 153int 154priv_getiftype(char *ifname, char *type, unsigned int *unitptr) 155{ 156 const char *errstr; 157 size_t span; 158 unsigned int unit; 159 160 /* Extract the name part */ 161 span = strcspn(ifname, "0123456789"); 162 if (span == 0 || span >= strlen(ifname) || span >= (IF_NAMESIZE - 1)) 163 return (-1); 164 memcpy(type, ifname, span); 165 type[span] = 0; 166 167 /* Now parse the unit (we don't strictly validate the format here) */ 168 unit = strtonum(ifname + span, 0, UINT_MAX, &errstr); 169 if (errstr != NULL) 170 return (-1); 171 if (unitptr != NULL) 172 *unitptr = unit; 173 174 return (0); 175} 176 177int 178priv_findname(const char *name, const char **names) 179{ 180 unsigned int i; 181 182 for (i = 0; names[i] != NULL; i++) { 183 if (strcmp(name, names[i]) == 0) 184 return (0); 185 } 186 187 return (-1); 188} 189 190/* 191 * Called from the process peer 192 */ 193 194int 195vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm) 196{ 197 struct vm_create_params *vcp = &vm->vm_params; 198 struct vmd_if *vif; 199 struct vmd_switch *vsw; 200 unsigned int i; 201 struct vmop_ifreq vfr, vfbr; 202 203 for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) { 204 vif = &vm->vm_ifs[i]; 205 206 if (vif->vif_name == NULL) 207 break; 208 209 if (strlcpy(vfr.vfr_name, vif->vif_name, 210 sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name)) 211 return (-1); 212 213 /* Description can be truncated */ 214 (void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value), 215 "vm%u-if%u-%s", vm->vm_vmid, i, vcp->vcp_name); 216 217 log_debug("%s: interface %s description %s", __func__, 218 vfr.vfr_name, vfr.vfr_value); 219 220 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR, 221 &vfr, sizeof(vfr)); 222 223 /* Add interface to bridge/switch */ 224 if ((vsw = switch_getbyname(vif->vif_switch)) != NULL) { 225 if (strlcpy(vfbr.vfr_name, vsw->sw_ifname, 226 sizeof(vfbr.vfr_name)) >= sizeof(vfbr.vfr_name)) 227 return (-1); 228 if (strlcpy(vfbr.vfr_value, vif->vif_name, 229 sizeof(vfbr.vfr_value)) >= sizeof(vfbr.vfr_value)) 230 return (-1); 231 232 log_debug("%s: interface %s add %s", __func__, 233 vfbr.vfr_name, vfbr.vfr_value); 234 235 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFCREATE, 236 &vfbr, sizeof(vfbr)); 237 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD, 238 &vfbr, sizeof(vfbr)); 239 } else if (vif->vif_switch != NULL) 240 log_warnx("switch %s not found", vif->vif_switch); 241 242 /* Set the new interface status to up or down */ 243 proc_compose(ps, PROC_PRIV, (vif->vif_flags & IFF_UP) ? 244 IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN, 245 &vfr, sizeof(vfr)); 246 } 247 248 return (0); 249} 250 251int 252vm_priv_brconfig(struct privsep *ps, struct vmd_switch *vsw) 253{ 254 struct vmd_if *vif; 255 struct vmop_ifreq vfr; 256 257 if (strlcpy(vfr.vfr_name, vsw->sw_ifname, 258 sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name)) 259 return (-1); 260 261 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFCREATE, 262 &vfr, sizeof(vfr)); 263 264 /* Description can be truncated */ 265 (void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value), 266 "switch%u-%s", vsw->sw_id, vsw->sw_name); 267 268 log_debug("%s: interface %s description %s", __func__, 269 vfr.vfr_name, vfr.vfr_value); 270 271 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR, 272 &vfr, sizeof(vfr)); 273 274 TAILQ_FOREACH(vif, &vsw->sw_ifs, vif_entry) { 275 if (strlcpy(vfr.vfr_value, vif->vif_name, 276 sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value)) 277 return (-1); 278 279 log_debug("%s: interface %s add %s", __func__, 280 vfr.vfr_name, vfr.vfr_value); 281 282 proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD, 283 &vfr, sizeof(vfr)); 284 } 285 286 /* Set the new interface status to up or down */ 287 proc_compose(ps, PROC_PRIV, (vsw->sw_flags & IFF_UP) ? 288 IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN, 289 &vfr, sizeof(vfr)); 290 291 return (0); 292} 293