diff options
Diffstat (limited to 'src/VBox/NetworkServices/NAT/mkrawsock.c')
-rw-r--r-- | src/VBox/NetworkServices/NAT/mkrawsock.c | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/src/VBox/NetworkServices/NAT/mkrawsock.c b/src/VBox/NetworkServices/NAT/mkrawsock.c new file mode 100644 index 00000000..50e9831f --- /dev/null +++ b/src/VBox/NetworkServices/NAT/mkrawsock.c @@ -0,0 +1,349 @@ +/* $Id: mkrawsock.c $ */ +/** @file + * Auxiliary server to create raw-sockets when debugging unprivileged. + */ + +/* + * Copyright (C) 2013-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#ifdef __linux__ +#define _GNU_SOURCE +#endif + +#ifdef __sun__ +#if __STDC_VERSION__ - 0 >= 199901L +#define _XOPEN_SOURCE 600 +#else +#define _XOPEN_SOURCE 500 +#endif +#define __EXTENSIONS__ 1 +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#ifdef __linux__ +#include <linux/icmp.h> /* for ICMP_FILTER */ +#endif +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +static void handler(int sig); +static void serve(int s); +static int mkrawsock(int family); + +volatile sig_atomic_t signaled = 0; + +int +main(int argc, char **argv) +{ + struct sigaction sa; + struct sockaddr_un sux; /* because solaris */ + struct passwd *pw; + size_t pathlen; + char *slash; + int s, client; + int status; + + memset(&sux, 0, sizeof(sux)); + sux.sun_family = AF_UNIX; + + if (getuid() == 0) { + if (argc != 2) { + fprintf(stderr, "username required when run as root\n"); + return EXIT_FAILURE; + } + + errno = 0; + pw = getpwnam(argv[1]); + if (pw == NULL) { + perror("getpwnam"); + return EXIT_FAILURE; + } + if (pw->pw_uid == 0) { + fprintf(stderr, "%s is superuser\n", pw->pw_name); + return EXIT_FAILURE; + } + } + else { + errno = 0; + pw = getpwuid(getuid()); + if (pw == NULL) { + perror("getpwuid"); + return EXIT_FAILURE; + } + } + + pathlen = snprintf(sux.sun_path, sizeof(sux.sun_path), + "/tmp/.vbox-%s-aux/mkrawsock", pw->pw_name); + if (pathlen > sizeof(sux.sun_path)) { + fprintf(stderr, "socket pathname truncated\n"); + return EXIT_FAILURE; + } + + slash = strrchr(sux.sun_path, '/'); + if (slash == NULL) { + fprintf(stderr, "%s: no directory separator\n", sux.sun_path); + return EXIT_FAILURE; + } + + *slash = '\0'; + + status = mkdir(sux.sun_path, 0700); + if (status == 0) { + status = chown(sux.sun_path, pw->pw_uid, pw->pw_gid); + if (status < 0) { + perror("chown"); + return EXIT_FAILURE; + } + } + else if (errno != EEXIST) { + perror("mkdir"); + return EXIT_FAILURE; + } + else { + int dirfd; + struct stat st; + + dirfd = open(sux.sun_path, O_RDONLY, O_DIRECTORY); + if (dirfd < 0) { + perror(sux.sun_path); + return EXIT_FAILURE; + } + + status = fstat(dirfd, &st); + close(dirfd); + + if (status < 0) { + perror(sux.sun_path); + return EXIT_FAILURE; + } + + if (st.st_uid != pw->pw_uid) { + fprintf(stderr, "%s: exists but not owned by %s\n", + sux.sun_path, pw->pw_name); + return EXIT_FAILURE; + } + + if ((st.st_mode & 0777) != 0700) { + fprintf(stderr, "%s: bad mode %04o\n", + sux.sun_path, (unsigned int)(st.st_mode & 0777)); + return EXIT_FAILURE; + } + } + + *slash = '/'; + +#if 0 + status = unlink(sux.sun_path); + if (status < 0 && errno != ENOENT) { + perror("unlink"); + } +#endif + + s = socket(PF_UNIX, SOCK_STREAM, 0); + if (s < 0) { + perror("socket"); + return EXIT_FAILURE; + } + + status = bind(s, (struct sockaddr *)&sux, + (sizeof(sux) - sizeof(sux.sun_path) + + strlen(sux.sun_path) + 1)); + if (status < 0) { + perror(sux.sun_path); + close(s); + return EXIT_FAILURE; + } + + status = chown(sux.sun_path, pw->pw_uid, pw->pw_gid); + if (status < 0) { + perror("chown"); + close(s); + return EXIT_FAILURE; + } + + status = chmod(sux.sun_path, 0600); + if (status < 0) { + perror("chmod"); + close(s); + return EXIT_FAILURE; + } + + status = listen(s, 1); + if (status < 0) { + perror("listen"); + close(s); + return EXIT_FAILURE; + } + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + while (!signaled) { + client = accept(s, NULL, 0); + if (client < 0) { + perror("accept"); + continue; + } + + serve(client); + close(client); + } + + close(s); + status = unlink(sux.sun_path); + if (status < 0) { + perror("unlink"); + } + + return EXIT_SUCCESS; +} + + +static void +handler(int sig) +{ + signaled = 1; +} + + +static void +serve(int client) +{ +#ifdef SO_PEERCRED + struct ucred cr; + socklen_t crlen; +#endif + ssize_t nread, nsent; + struct msghdr mh; + struct iovec iov[1]; + char buf[1]; + struct cmsghdr *cmh; + char cmsg[CMSG_SPACE(sizeof(int))]; + int fd; + int status; + +#ifdef SO_PEERCRED + crlen = sizeof(cr); + status = getsockopt(client, SOL_SOCKET, SO_PEERCRED, &cr, &crlen); + if (status < 0) { + perror("SO_PEERCRED"); + return; + } + + fprintf(stderr, "request from pid %lu uid %lu ", + (unsigned long)cr.pid, (unsigned long)cr.uid); +#endif + + nread = read(client, buf, 1); + if (nread < 0) { + perror("recv"); + return; + } + + fd = -1; + switch (buf[0]) { + + case '4': + fprintf(stderr, "for ICMPv4 socket\n"); + fd = mkrawsock(PF_INET); + break; + + case '6': + fprintf(stderr, "for ICMPv6 socket\n"); + fd = mkrawsock(PF_INET6); + break; + + default: + fprintf(stderr, "bad request 0x%02x\n", (unsigned int)buf[0]); + return; + } + + if (fd < 0) { + buf[0] = '\0'; /* NAK */ + nsent = write(client, buf, 1); + (void)nsent; + return; + } + + memset(&mh, 0, sizeof(mh)); + memset(cmsg, 0, sizeof(cmsg)); + + iov[0].iov_base = buf; + iov[0].iov_len = 1; + + mh.msg_iov = iov; + mh.msg_iovlen = 1; + mh.msg_control = cmsg; + mh.msg_controllen = sizeof(cmsg); + + cmh = CMSG_FIRSTHDR(&mh); + cmh->cmsg_level = SOL_SOCKET; + cmh->cmsg_type = SCM_RIGHTS; + cmh->cmsg_len = CMSG_LEN(sizeof(fd)); + *((int *) CMSG_DATA(cmh)) = fd; + + nsent = sendmsg(client, &mh, 0); + if (nsent < 0) { + perror("sendmsg"); + } + + close(fd); +} + + +static int +mkrawsock(int family) +{ + int fd; + + if (family == PF_INET) { + fd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); + if (fd < 0) { + perror("IPPROTO_ICMP"); + return -1; + } + } + else { + fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + if (fd < 0) { + perror("IPPROTO_ICMPV6"); + return -1; + } + } + + return fd; +} |