/* Privileged: called by monitor_loop. */ static void m_priv_setsockopt(void) { int sock, level, optname, v; int err = 0; char *optval = 0; socklen_t optlen; sock = mm_receive_fd(m_state.s); if (sock < 0) { log_print("m_priv_setsockopt: read/write error"); return; } must_read(&level, sizeof level); must_read(&optname, sizeof optname); must_read(&optlen, sizeof optlen); optval = (char *)malloc(optlen); if (!optval) { log_print("m_priv_setsockopt: malloc failed"); close(sock); return; } must_read(optval, optlen); if (m_priv_check_sockopt(level, optname) != 0) { err = EACCES; v = -1; } else { v = setsockopt(sock, level, optname, optval, optlen); if (v < 0) err = errno; } close(sock); sock = -1; must_write(&err, sizeof err); must_write(&v, sizeof v); free(optval); return; }
/* Proxies */ static void priv_ping() { int rc; enum priv_cmd cmd = PRIV_PING; must_write(&cmd, sizeof(enum priv_cmd)); must_read(&rc, sizeof(int)); log_debug("privsep", "monitor ready"); }
static void asroot_iface_promisc() { char name[IFNAMSIZ]; int rc; must_read(PRIV_PRIVILEGED, &name, sizeof(name)); name[sizeof(name) - 1] = '\0'; rc = asroot_iface_promisc_os(name); must_write(PRIV_PRIVILEGED, &rc, sizeof(rc)); }
/* Privileged: called by monitor_loop. */ static void m_priv_bind(void) { int sock, v, err = 0; struct sockaddr *name = 0; socklen_t namelen; sock = mm_receive_fd(m_state.s); if (sock < 0) { log_print("m_priv_bind: read/write error"); return; } must_read(&namelen, sizeof namelen); name = (struct sockaddr *)malloc(namelen); if (!name) { log_print("m_priv_bind: malloc failed"); close(sock); return; } must_read((char *)name, namelen); if (m_priv_check_bind(name, namelen) != 0) { err = EACCES; v = -1; } else { v = bind(sock, name, namelen); if (v < 0) { log_error("m_priv_bind: bind(%d,%p,%d) returned %d", sock, name, namelen, v); err = errno; } } close(sock); sock = -1; must_write(&err, sizeof err); must_write(&v, sizeof v); free(name); return; }
/* Proxies */ static void priv_ping() { int rc; enum priv_cmd cmd = PRIV_PING; must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd)); priv_wait(); must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int)); log_debug("privsep", "monitor ready"); }
int monitor_req_readdir(const char *filename) { int cmd, err; size_t len; cmd = MONITOR_REQ_READDIR; must_write(&cmd, sizeof cmd); len = strlen(filename); must_write(&len, sizeof len); must_write(filename, len); must_read(&err, sizeof err); if (err == -1) must_read(&errno, sizeof errno); return err; }
/* * send the snaplength to privileged process */ int priv_set_snaplen(int snaplen) { int cmd, ret; if (priv_fd < 0) errx(1, "%s: called from privileged portion", __func__); cmd = PRIV_SET_SNAPLEN; must_write(priv_fd, &cmd, sizeof(int)); must_write(priv_fd, &snaplen, sizeof(int)); must_read(priv_fd, &ret, sizeof(int)); /* also set hpcap->snapshot in child */ if (ret == 0) hpcap->snapshot = snaplen; return (ret); }
/* Proxy for gethostbyname */ char * priv_gethostbyname() { static char *buf = NULL; int rc; enum priv_cmd cmd = PRIV_GET_HOSTNAME; must_write(&cmd, sizeof(enum priv_cmd)); must_read(&rc, sizeof(int)); if ((buf = (char*)realloc(buf, rc+1)) == NULL) fatal("privsep", NULL); must_read(buf, rc+1); return buf; }
static void asroot_gethostbyname() { struct utsname un; struct hostent *hp; int len; if (uname(&un) < 0) fatal("privsep", "failed to get system information"); if ((hp = gethostbyname(un.nodename)) == NULL) { log_info("privsep", "unable to get system name"); #ifdef HAVE_RES_INIT res_init(); #endif len = strlen(un.nodename); must_write(&len, sizeof(int)); must_write(un.nodename, len + 1); } else { len = strlen(hp->h_name); must_write(&len, sizeof(int)); must_write(hp->h_name, len + 1); } }
/* Move-away and reopen log-file */ int priv_move_log(void) { int cmd, ret; if (priv_fd < 0) errx(1, "%s: called from privileged portion\n", __func__); cmd = PRIV_MOVE_LOG; must_write(priv_fd, &cmd, sizeof(int)); must_read(priv_fd, &ret, sizeof(int)); return (ret); }
/* Bind to allowed privileged ports using privsep, or try to bind locally */ int isc_priv_bind(int fd, struct sockaddr *sa, socklen_t salen) { int status, er; if (priv_fd < 0) errx(1, "%s called from privileged portion", __func__); if (check_bind(sa, salen)) { status = bind(fd, sa, salen); } else { logmsg(LOG_DEBUG, "Binding privsep"); write_command(priv_fd, PRIV_BIND); send_fd(priv_fd, fd); must_write(priv_fd, &salen, sizeof(salen)); must_write(priv_fd, sa, salen); must_read(priv_fd, &er, sizeof(er)); must_read(priv_fd, &status, sizeof(status)); errno = er; } return (status); }
/* Open log-file */ int priv_open_log(void) { int cmd, fd; if (priv_fd < 0) errx(1, "%s: called from privileged portion", __func__); cmd = PRIV_OPEN_LOG; must_write(priv_fd, &cmd, sizeof(int)); fd = receive_fd(priv_fd); return (fd); }
static void asroot_iface_init() { int rc = -1, fd = -1; int ifindex; char name[IFNAMSIZ]; must_read(&ifindex, sizeof(ifindex)); must_read(&name, sizeof(name)); name[sizeof(name) - 1] = '\0'; rc = asroot_iface_init_os(ifindex, name, &fd); must_write(&rc, sizeof(rc)); if (rc == 0 && fd >=0) send_fd(fd); if (fd >= 0) close(fd); }
/* Proxy for gethostname */ char * priv_gethostname() { static char *buf = NULL; int rc; enum priv_cmd cmd = PRIV_GET_HOSTNAME; must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd)); priv_wait(); must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int)); if ((buf = (char*)realloc(buf, rc+1)) == NULL) fatal("privsep", NULL); must_read(PRIV_UNPRIVILEGED, buf, rc); buf[rc] = '\0'; return buf; }
static void asroot_snmp_socket() { int sock, rc; static struct sockaddr_un *addr = NULL; struct sockaddr_un bogus; if (!addr) { addr = (struct sockaddr_un *)malloc(sizeof(struct sockaddr_un)); must_read(addr, sizeof(struct sockaddr_un)); } else /* We have already been asked to connect to a socket. We will * connect to the same socket. */ must_read(&bogus, sizeof(struct sockaddr_un)); if (addr->sun_family != AF_UNIX) fatal("privsep", "someone is trying to trick me"); addr->sun_path[sizeof(addr->sun_path)-1] = '\0'; if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { log_warn("privsep", "cannot open socket"); must_write(&sock, sizeof(int)); return; } if ((rc = connect(sock, (struct sockaddr *) addr, sizeof(struct sockaddr_un))) != 0) { log_info("privsep", "cannot connect to %s: %s", addr->sun_path, strerror(errno)); close(sock); rc = -1; must_write(&rc, sizeof(int)); return; } must_write(&rc, sizeof(int)); send_fd(sock); close(sock); }
static void asroot_iface_init() { int rc = -1, fd = -1; int ifindex; char name[IFNAMSIZ]; must_read(PRIV_PRIVILEGED, &ifindex, sizeof(ifindex)); must_read(PRIV_PRIVILEGED, &name, sizeof(name)); name[sizeof(name) - 1] = '\0'; TRACE(LLDPD_PRIV_INTERFACE_INIT(name)); rc = asroot_iface_init_os(ifindex, name, &fd); must_write(PRIV_PRIVILEGED, &rc, sizeof(rc)); if (rc == 0 && fd >=0) send_fd(PRIV_PRIVILEGED, fd); if (fd >= 0) close(fd); }
static void asroot_iface_description() { char name[IFNAMSIZ]; char *description; int len, rc; must_read(&name, sizeof(name)); name[sizeof(name) - 1] = '\0'; must_read(&len, sizeof(int)); if ((description = (char*)malloc(len+1)) == NULL) fatal("description", NULL); must_read(description, len); description[len] = 0; rc = asroot_iface_description_os(name, description); must_write(&rc, sizeof(rc)); }
/* Privileged: called by monitor_loop. */ static void m_priv_pfkey_open(void) { int fd, err = 0; fd = pf_key_v2_open(); if (fd < 0) err = -1; must_write(&err, sizeof err); if (fd > 0 && mm_send_fd(m_state.s, fd)) { log_error("m_priv_pfkey_open: read/write operation failed"); close(fd); return; } close(fd); }
static void asroot_iface_description() { char name[IFNAMSIZ]; char *description; int len, rc; must_read(PRIV_PRIVILEGED, &name, sizeof(name)); name[sizeof(name) - 1] = '\0'; must_read(PRIV_PRIVILEGED, &len, sizeof(int)); if ((description = (char*)malloc(len+1)) == NULL) fatal("description", NULL); must_read(PRIV_PRIVILEGED, description, len); description[len] = 0; TRACE(LLDPD_PRIV_INTERFACE_DESCRIPTION(name, description)); rc = asroot_iface_description_os(name, description); must_write(PRIV_PRIVILEGED, &rc, sizeof(rc)); free(description); }
static void asroot_ctl_cleanup() { int len; char *ctlname; int rc = 0; must_read(PRIV_PRIVILEGED, &len, sizeof(int)); if ((ctlname = (char*)malloc(len+1)) == NULL) fatal("ctlname", NULL); must_read(PRIV_PRIVILEGED, ctlname, len); ctlname[len] = 0; ctl_cleanup(ctlname); free(ctlname); /* Ack */ must_write(PRIV_PRIVILEGED, &rc, sizeof(int)); }
/* Privileged: called by monitor_loop. */ static void m_priv_getfd(void) { char path[MAXPATHLEN]; size_t len; int v, flags, ret; int err = 0; mode_t mode; must_read(&len, sizeof len); if (len == 0 || len >= sizeof path) log_fatal("m_priv_getfd: invalid pathname length"); must_read(path, len); path[len] = '\0'; if (strlen(path) != len) log_fatal("m_priv_getfd: invalid pathname"); must_read(&flags, sizeof flags); must_read(&mode, sizeof mode); if ((ret = m_priv_local_sanitize_path(path, sizeof path, flags)) != 0) { if (ret == 1) log_print("m_priv_getfd: illegal path \"%s\"", path); err = EACCES; v = -1; } else { if ((v = open(path, flags, mode)) == -1) err = errno; } must_write(&err, sizeof err); if (v != -1) { if (mm_send_fd(m_state.s, v) == -1) log_error("m_priv_getfd: sending fd failed"); close(v); } }
static void asroot_iface_multicast() { int sock = -1, add, rc = 0; struct ifreq ifr = { .ifr_name = {} }; must_read(PRIV_PRIVILEGED, ifr.ifr_name, IFNAMSIZ); #if defined HOST_OS_LINUX must_read(PRIV_PRIVILEGED, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN); #elif defined HOST_OS_FREEBSD || defined HOST_OS_OSX || defined HOST_OS_DRAGONFLY /* Black magic from mtest.c */ struct sockaddr_dl *dlp = ALIGNED_CAST(struct sockaddr_dl *, &ifr.ifr_addr); dlp->sdl_len = sizeof(struct sockaddr_dl); dlp->sdl_family = AF_LINK; dlp->sdl_index = 0; dlp->sdl_nlen = 0; dlp->sdl_alen = ETHER_ADDR_LEN; dlp->sdl_slen = 0; must_read(PRIV_PRIVILEGED, LLADDR(dlp), ETHER_ADDR_LEN); #elif defined HOST_OS_OPENBSD || defined HOST_OS_NETBSD || defined HOST_OS_SOLARIS struct sockaddr *sap = (struct sockaddr *)&ifr.ifr_addr; #if ! defined HOST_OS_SOLARIS sap->sa_len = sizeof(struct sockaddr); #endif sap->sa_family = AF_UNSPEC; must_read(PRIV_PRIVILEGED, sap->sa_data, ETHER_ADDR_LEN); #else #error Unsupported OS #endif must_read(PRIV_PRIVILEGED, &add, sizeof(int)); if (((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) || ((ioctl(sock, (add)?SIOCADDMULTI:SIOCDELMULTI, &ifr) < 0) && (errno != EADDRINUSE))) rc = errno; if (sock != -1) close(sock); must_write(PRIV_PRIVILEGED, &rc, sizeof(rc)); }
static void read_client_x11_packet_cb(EV_P_ ev_io *w, int revents) { struct connstate *connstate = (struct connstate *)w->data; void *request = smalloc(sizeof(generic_x11_request_t)); must_read(readall_into(request, sizeof(generic_x11_request_t), connstate->clientw->fd)); const size_t len = (((generic_x11_request_t *)request)->length * 4); if (len > sizeof(generic_x11_request_t)) { request = srealloc(request, len); must_read(readall_into(request + sizeof(generic_x11_request_t), len - sizeof(generic_x11_request_t), connstate->clientw->fd)); } // XXX: sequence counter wrapping is not implemented, but should not be // necessary given that this tool is scoped for test cases. connstate->sequence++; /* BEGIN RandR 1.5 specific */ const uint8_t opcode = ((generic_x11_request_t *)request)->opcode; if (opcode == XCB_QUERY_EXTENSION) { xcb_query_extension_request_t *req = request; const char *name = request + sizeof(xcb_query_extension_request_t); if (req->name_len == strlen("RANDR") && strncmp(name, "RANDR", strlen("RANDR")) == 0) { connstate->getext_randr = connstate->sequence; } } else if (opcode == connstate->randr_major_opcode) { const uint8_t randr_opcode = ((generic_x11_request_t *)request)->pad0; if (randr_opcode == XCB_RANDR_GET_MONITORS) { connstate->getmonitors = connstate->sequence; } } /* END RandR 1.5 specific */ must_write(writeall(connstate->serverw->fd, request, len)); free(request); }
static void m_priv_req_readdir() { size_t len; char path[MAXPATHLEN]; DIR *dp; struct dirent *file; struct stat sb; int off, size, fd, ret, serrno; must_read(&len, sizeof len); if (len == 0 || len >= sizeof path) log_fatal("m_priv_req_readdir: invalid pathname length"); must_read(path, len); path[len] = '\0'; if (strlen(path) != len) log_fatal("m_priv_req_readdir: invalid pathname"); off = strlen(path); size = sizeof path - off; if ((dp = opendir(path)) == NULL) { serrno = errno; ret = -1; must_write(&ret, sizeof ret); must_write(&serrno, sizeof serrno); return; } /* report opendir() success */ ret = 0; must_write(&ret, sizeof ret); while ((file = readdir(dp)) != NULL) { strlcpy(path + off, file->d_name, size); if (m_priv_local_sanitize_path(path, sizeof path, O_RDONLY) != 0) continue; fd = open(path, O_RDONLY, 0); if (fd == -1) { log_error("m_priv_req_readdir: open " "(\"%s\", O_RDONLY, 0) failed", path); continue; } if ((fstat(fd, &sb) == -1) || !(S_ISREG(sb.st_mode) || S_ISLNK(sb.st_mode))) { close(fd); continue; } len = strlen(path); must_write(&len, sizeof len); must_write(path, len); mm_send_fd(m_state.s, fd); close(fd); } closedir(dp); len = 0; must_write(&len, sizeof len); }
static void asroot_ping() { int rc = 1; must_write(&rc, sizeof(int)); }
/* * XXX reimplement pcap_open_live with privsep, this is the * unprivileged part. */ pcap_t * priv_pcap_live(const char *dev, int slen, int prom, int to_ms, char *ebuf, u_int dlt, u_int dirfilt) { int fd, err; struct bpf_version bv; u_int v; pcap_t *p; if (priv_fd < 0) errx(1, "%s: called from privileged portion", __func__); if (dev == NULL) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "No interface specified"); return (NULL); } p = malloc(sizeof(*p)); if (p == NULL) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); return (NULL); } bzero(p, sizeof(*p)); write_command(priv_fd, PRIV_OPEN_BPF); must_write(priv_fd, &slen, sizeof(int)); must_write(priv_fd, &prom, sizeof(int)); must_write(priv_fd, &dlt, sizeof(u_int)); must_write(priv_fd, &dirfilt, sizeof(u_int)); write_string(priv_fd, dev); fd = receive_fd(priv_fd); must_read(priv_fd, &err, sizeof(int)); if (fd < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "Failed to open bpf device for %s: %s", dev, strerror(err)); goto bad; } /* fd is locked, can only use 'safe' ioctls */ if (ioctl(fd, BIOCVERSION, &bv) < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s", pcap_strerror(errno)); goto bad; } if (bv.bv_major != BPF_MAJOR_VERSION || bv.bv_minor < BPF_MINOR_VERSION) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "kernel bpf filter out of date"); goto bad; } p->fd = fd; p->snapshot = slen; /* Get the data link layer type. */ if (ioctl(fd, BIOCGDLT, &v) < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s", pcap_strerror(errno)); goto bad; } p->linktype = v; /* XXX hack */ if (p->linktype == DLT_PFLOG && p->snapshot < 160) p->snapshot = 160; /* set timeout */ if (to_ms != 0) { struct timeval to; to.tv_sec = to_ms / 1000; to.tv_usec = (to_ms * 1000) % 1000000; if (ioctl(p->fd, BIOCSRTIMEOUT, &to) < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s", pcap_strerror(errno)); goto bad; } } if (ioctl(fd, BIOCGBLEN, &v) < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s", pcap_strerror(errno)); goto bad; } p->bufsize = v; p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); goto bad; } return (p); bad: if (fd >= 0) close(fd); free(p); return (NULL); }
/* based on syslogd privsep */ int priv_init(void) { int i, fd, socks[2], cmd; int snaplen, ret, olderrno; struct passwd *pw; #ifdef __FreeBSD__ for (i = 1; i < NSIG; i++) #else for (i = 1; i < _NSIG; i++) #endif signal(i, SIG_DFL); /* Create sockets */ if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) err(1, "socketpair() failed"); pw = getpwnam("_pflogd"); if (pw == NULL) errx(1, "unknown user _pflogd"); endpwent(); child_pid = fork(); if (child_pid < 0) err(1, "fork() failed"); if (!child_pid) { gid_t gidset[1]; /* Child - drop privileges and return */ if (chroot(pw->pw_dir) != 0) err(1, "unable to chroot"); if (chdir("/") != 0) err(1, "unable to chdir"); gidset[0] = pw->pw_gid; if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) err(1, "setresgid() failed"); if (setgroups(1, gidset) == -1) err(1, "setgroups() failed"); if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) err(1, "setresuid() failed"); close(socks[0]); priv_fd = socks[1]; return 0; } /* Father */ /* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */ signal(SIGALRM, sig_pass_to_chld); signal(SIGTERM, sig_pass_to_chld); signal(SIGHUP, sig_pass_to_chld); signal(SIGINT, sig_pass_to_chld); signal(SIGQUIT, sig_pass_to_chld); signal(SIGCHLD, sig_chld); setproctitle("[priv]"); close(socks[1]); while (!gotsig_chld) { if (may_read(socks[0], &cmd, sizeof(int))) break; switch (cmd) { case PRIV_SET_SNAPLEN: logmsg(LOG_DEBUG, "[priv]: msg PRIV_SET_SNAPLENGTH received"); must_read(socks[0], &snaplen, sizeof(int)); ret = set_snaplen(snaplen); if (ret) { logmsg(LOG_NOTICE, "[priv]: set_snaplen failed for snaplen %d", snaplen); } must_write(socks[0], &ret, sizeof(int)); break; case PRIV_OPEN_LOG: logmsg(LOG_DEBUG, "[priv]: msg PRIV_OPEN_LOG received"); /* create or append logs but do not follow symlinks */ fd = open(filename, O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW, 0600); olderrno = errno; send_fd(socks[0], fd); if (fd < 0) logmsg(LOG_NOTICE, "[priv]: failed to open %s: %s", filename, strerror(olderrno)); else close(fd); break; case PRIV_MOVE_LOG: logmsg(LOG_DEBUG, "[priv]: msg PRIV_MOVE_LOG received"); ret = move_log(filename); must_write(socks[0], &ret, sizeof(int)); break; default: logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); _exit(1); /* NOTREACHED */ } } _exit(1); }
/* write a command to the peer */ static void write_command(int fd, int cmd) { must_write(fd, &cmd, sizeof(cmd)); }
static void asroot_ping() { int rc = 1; must_write(PRIV_PRIVILEGED, &rc, sizeof(int)); }