From: "M. Mohan Kumar" <mo...@in.ibm.com> Provide root privilege access to QEMU 9p proxy filesystem using socket communication.
Proxy helper is started by root user as: ~ # virtfs-proxy-helper -f|--fd <socket descriptor> -p|--path <path-to-share> Signed-off-by: M. Mohan Kumar <mo...@in.ibm.com> --- Makefile | 3 + configure | 19 +++ fsdev/virtfs-proxy-helper.c | 280 +++++++++++++++++++++++++++++++++++++++++++ hw/9pfs/virtio-9p-proxy.h | 7 +- 4 files changed, 308 insertions(+), 1 deletions(-) create mode 100644 fsdev/virtfs-proxy-helper.c diff --git a/Makefile b/Makefile index ba8d738..19b481a 100644 --- a/Makefile +++ b/Makefile @@ -153,6 +153,9 @@ qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y) qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y) qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y) +fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o +fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap + qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@") diff --git a/configure b/configure index a6cf6d6..a2b55e8 100755 --- a/configure +++ b/configure @@ -1873,6 +1873,22 @@ else fi ########################################## +# libcap probe + +if test "$cap" != "no" ; then + cat > $TMPC <<EOF +#include <stdio.h> +#include <sys/capability.h> +int main(void) { cap_t caps; caps = cap_init(); } +EOF + if compile_prog "" "-lcap" ; then + cap=yes + else + cap=no + fi +fi + +########################################## # pthread probe PTHREADLIBS_LIST="-pthread -lpthread -lpthreadGC2" @@ -2662,6 +2678,9 @@ confdir=$sysconfdir$confsuffix tools= if test "$softmmu" = yes ; then tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools" + if [ "$cap" = "yes" -a "$linux" = "yes" ] ; then + tools="$tools fsdev/virtfs-proxy-helper\$(EXESUF)" + fi if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then tools="qemu-nbd\$(EXESUF) $tools" if [ "$guest_agent" = "yes" ]; then diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c new file mode 100644 index 0000000..dc222d4 --- /dev/null +++ b/fsdev/virtfs-proxy-helper.c @@ -0,0 +1,280 @@ +/* + * Helper for QEMU Proxy FS Driver + * Copyright IBM, Corp. 2011 + * + * Authors: + * M. Mohan Kumar <mo...@in.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ +#include <stdio.h> +#include <string.h> +#include <sys/un.h> +#include <limits.h> +#include <signal.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <getopt.h> +#include <unistd.h> +#include <syslog.h> +#include <sys/prctl.h> +#include <sys/capability.h> +#include <sys/fsuid.h> +#include <stdarg.h> +#include <stdbool.h> +#include "qemu-common.h" +#include "virtio-9p-marshal.h" +#include "hw/9pfs/virtio-9p-proxy.h" + +#define PROGNAME "virtfs-proxy-helper" + +#define do_log(loglevel, fmt, args...) \ + do { \ + if (is_daemon) {\ + syslog(loglevel, fmt, ##args); \ + } else { \ + fprintf(stderr, fmt, ##args); \ + } \ + } while (0) + +#define do_perror(string) \ + do { \ + if (is_daemon) {\ + syslog(LOG_CRIT, string ":%s", strerror(errno)); \ + } else { \ + fprintf(stderr, string ":%s\n", strerror(errno)); \ + } \ + } while (0) + +static struct option helper_opts[] = { + {"fd", required_argument, NULL, 'f'}, + {"path", required_argument, NULL, 'p'}, + {"nodaemon", no_argument, NULL, 'n'}, +}; + +static bool is_daemon; + +static int cap_set(void) +{ + cap_t caps; + cap_value_t cap_list[10]; + + /* helper needs following capbabilities only */ + cap_list[0] = CAP_CHOWN; + cap_list[1] = CAP_DAC_OVERRIDE; + cap_list[2] = CAP_DAC_READ_SEARCH; + cap_list[3] = CAP_FOWNER; + cap_list[4] = CAP_FSETID; + cap_list[5] = CAP_SETGID; + cap_list[6] = CAP_MKNOD; + cap_list[7] = CAP_SETUID; + + caps = cap_init(); + if (caps == NULL) { + do_perror("cap_init"); + return -1; + } + if (cap_set_flag(caps, CAP_PERMITTED, 8, cap_list, CAP_SET) < 0) { + do_perror("cap_set_flag"); + goto error; + } + if (cap_set_proc(caps) < 0) { + do_perror("cap_set_proc"); + goto error; + } + if (cap_set_flag(caps, CAP_EFFECTIVE, 8, cap_list, CAP_SET)) { + do_perror("cap_set_flag"); + goto error; + } + if (cap_set_proc(caps) < 0) { + do_perror("cap_set_proc"); + goto error; + } + cap_free(caps); + return 0; + +error: + cap_free(caps); + return -1; +} + +static int init_capabilities(void) +{ + if (prctl(PR_SET_KEEPCAPS, 1) < 0) { + do_perror("prctl"); + return -1; + } + if (cap_set() < 0) { + return -1; + } + return 0; +} + +static int socket_read(int sockfd, void *buff, ssize_t size) +{ + ssize_t retval, total = 0; + + while (size) { + retval = read(sockfd, buff, size); + if (retval == 0) { + break; + } + if (retval < 0) { + if (errno == EINTR) { + continue; + } + break; + } + size -= retval; + buff += retval; + total += retval; + } + return total; +} + +static int socket_write(int sockfd, void *buff, ssize_t size) +{ + int retval; + + do { + retval = write(sockfd, buff, size); + } while (retval < 0 && errno == EINTR); + if (retval != size) { + return -errno; + } + return retval; +} + +static int read_request(int sockfd, struct iovec *iovec) +{ + int retval; + ProxyHeader header; + + /* read the header */ + retval = socket_read(sockfd, iovec->iov_base, sizeof(header)); + if (retval != sizeof(header)) { + return -EIO; + } + /* unmarshal header */ + proxy_unmarshal(iovec, 1, 0, "dd", &header.type, &header.size); + if (header.size > BUFF_SZ) { + do_log(LOG_CRIT, "header.size exceeds maximum size\n"); + return -E2BIG; + } + /* read the request */ + retval = socket_read(sockfd, iovec->iov_base + sizeof(header), header.size); + if (retval != header.size) { + return -EIO; + } + return header.type; +} + +static void usage(char *prog) +{ + fprintf(stderr, "usage: %s\n" + " -p|--path <path> 9p path to export\n" + " {-f|--fd <socket-descriptor>} socket file descriptor to be used\n" + " [-n|--nodaemon] Run as a normal program\n", + basename(prog)); +} + +static int process_requests(int sock) +{ + int type; + struct iovec iovec; + + iovec.iov_base = g_malloc(BUFF_SZ); + iovec.iov_len = BUFF_SZ; + while (1) { + type = read_request(sock, &iovec); + if (type <= 0) { + goto error; + } + } + (void)socket_write; +error: + g_free(iovec.iov_base); + return -1; +} + +int main(int argc, char **argv) +{ + int sock; + char *rpath = NULL; + struct stat stbuf; + int c, option_index; + + is_daemon = true; + sock = -1; + while (1) { + option_index = 0; + c = getopt_long(argc, argv, "p:nh?f:", helper_opts, + &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'p': + rpath = optarg; + break; + case 'n': + is_daemon = false; + break; + case 'f': + sock = atoi(optarg); + break; + case '?': + case 'h': + default: + usage(argv[0]); + exit(EXIT_FAILURE); + } + } + + /* Parameter validation */ + if (sock == -1 || rpath[0] == '\0') { + fprintf(stderr, "socket descriptor or path not specified\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + if (lstat(rpath, &stbuf) < 0) { + fprintf(stderr, "invalid path \"%s\" specified, %s\n", + rpath, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (!S_ISDIR(stbuf.st_mode)) { + fprintf(stderr, "specified path \"%s\" is not directory\n", rpath); + exit(EXIT_FAILURE); + } + + if (is_daemon) { + if (daemon(0, 0) < 0) { + fprintf(stderr, "daemon call failed\n"); + exit(EXIT_FAILURE); + } + openlog(PROGNAME, LOG_PID, LOG_DAEMON); + } + + do_log(LOG_INFO, "Started\n"); + + if (chroot(rpath) < 0) { + do_perror("chroot"); + goto error; + } + umask(0); + + if (init_capabilities() < 0) { + goto error; + } + + process_requests(sock); +error: + do_log(LOG_INFO, "Done\n"); + closelog(); + return 0; +} diff --git a/hw/9pfs/virtio-9p-proxy.h b/hw/9pfs/virtio-9p-proxy.h index f5e1a02..7be44bd 100644 --- a/hw/9pfs/virtio-9p-proxy.h +++ b/hw/9pfs/virtio-9p-proxy.h @@ -1,7 +1,12 @@ #ifndef _QEMU_VIRTIO_9P_PROXY_H #define _QEMU_VIRTIO_9P_PROXY_H -#define BUFF_SZ (4 * 1024) +#define BUFF_SZ (64 * 1024) + +#define proxy_unmarshal(in_sg, in_elem, offset, fmt, args...) \ + v9fs_unmarshal(in_sg, in_elem, offset, 0, fmt, ##args) +#define proxy_marshal(out_sg, out_elem, offset, fmt, args...) \ + v9fs_marshal(out_sg, out_elem, offset, 0, fmt, ##args) typedef struct { int type; -- 1.7.6