summaryrefslogtreecommitdiff
path: root/qemu-bridge-helper.c
diff options
context:
space:
mode:
authorbiao716.wang <biao716.wang@samsung.com>2023-08-28 14:40:25 +0900
committerbiao716.wang <biao716.wang@samsung.com>2023-08-28 14:40:25 +0900
commitd0ba767e7dc0fded6daae31c11d13b538ed8d53f (patch)
treeeaaffe8ef043ee1803a7bbff50a9946e1613f58f /qemu-bridge-helper.c
parent0889ee8339e51dfdf78c39a8f15b6e5fe5ab49d5 (diff)
downloadqemu-arm-static-d0ba767e7dc0fded6daae31c11d13b538ed8d53f.tar.gz
qemu-arm-static-d0ba767e7dc0fded6daae31c11d13b538ed8d53f.tar.bz2
qemu-arm-static-d0ba767e7dc0fded6daae31c11d13b538ed8d53f.zip
Change-Id: I79199e4f7e9f27b498cd41113833a6158d4f07a2 Signed-off-by: biao716.wang <biao716.wang@samsung.com>
Diffstat (limited to 'qemu-bridge-helper.c')
-rw-r--r--qemu-bridge-helper.c106
1 files changed, 74 insertions, 32 deletions
diff --git a/qemu-bridge-helper.c b/qemu-bridge-helper.c
index 6a0974eb4..f3483b0a3 100644
--- a/qemu-bridge-helper.c
+++ b/qemu-bridge-helper.c
@@ -10,22 +10,22 @@
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
- *
*/
-#include "config-host.h"
+/*
+ * Known shortcomings:
+ * - There is no manual page
+ * - The syntax of the ACL file is not documented anywhere
+ * - parse_acl_file() doesn't report fopen() failure properly, fails
+ * to check ferror() after fgets() failure, arbitrarily truncates
+ * long lines, handles whitespace inconsistently, error messages
+ * don't point to the offending file and line, errors in included
+ * files are reported, but otherwise ignored, ...
+ */
+
+#include "qemu/osdep.h"
-#include <stdio.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <ctype.h>
-#include <glib.h>
-#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -40,10 +40,11 @@
#endif
#include "qemu/queue.h"
+#include "qemu/cutils.h"
#include "net/tap-linux.h"
-#ifdef CONFIG_LIBCAP
+#ifdef CONFIG_LIBCAP_NG
#include <cap-ng.h>
#endif
@@ -85,7 +86,7 @@ static int parse_acl_file(const char *filename, ACLList *acl_list)
char *ptr = line;
char *cmd, *arg, *argend;
- while (isspace(*ptr)) {
+ while (g_ascii_isspace(*ptr)) {
ptr++;
}
@@ -102,25 +103,33 @@ static int parse_acl_file(const char *filename, ACLList *acl_list)
if (arg == NULL) {
fprintf(stderr, "Invalid config line:\n %s\n", line);
- fclose(f);
- errno = EINVAL;
- return -1;
+ goto err;
}
*arg = 0;
arg++;
- while (isspace(*arg)) {
+ while (g_ascii_isspace(*arg)) {
arg++;
}
argend = arg + strlen(arg);
- while (arg != argend && isspace(*(argend - 1))) {
+ while (arg != argend && g_ascii_isspace(*(argend - 1))) {
argend--;
}
*argend = 0;
+ if (!g_str_equal(cmd, "include") && strlen(arg) >= IFNAMSIZ) {
+ fprintf(stderr, "name `%s' too long: %zu\n", arg, strlen(arg));
+ goto err;
+ }
+
if (strcmp(cmd, "deny") == 0) {
- acl_rule = g_malloc(sizeof(*acl_rule));
+ acl_rule = calloc(1, sizeof(*acl_rule));
+ if (!acl_rule) {
+ fclose(f);
+ errno = ENOMEM;
+ return -1;
+ }
if (strcmp(arg, "all") == 0) {
acl_rule->type = ACL_DENY_ALL;
} else {
@@ -129,7 +138,12 @@ static int parse_acl_file(const char *filename, ACLList *acl_list)
}
QSIMPLEQ_INSERT_TAIL(acl_list, acl_rule, entry);
} else if (strcmp(cmd, "allow") == 0) {
- acl_rule = g_malloc(sizeof(*acl_rule));
+ acl_rule = calloc(1, sizeof(*acl_rule));
+ if (!acl_rule) {
+ fclose(f);
+ errno = ENOMEM;
+ return -1;
+ }
if (strcmp(arg, "all") == 0) {
acl_rule->type = ACL_ALLOW_ALL;
} else {
@@ -142,15 +156,18 @@ static int parse_acl_file(const char *filename, ACLList *acl_list)
parse_acl_file(arg, acl_list);
} else {
fprintf(stderr, "Unknown command `%s'\n", cmd);
- fclose(f);
- errno = EINVAL;
- return -1;
+ goto err;
}
}
fclose(f);
-
return 0;
+
+err:
+ fclose(f);
+ errno = EINVAL;
+ return -1;
+
}
static bool has_vnet_hdr(int fd)
@@ -201,7 +218,7 @@ static int send_fd(int c, int fd)
return sendmsg(c, &msg, 0);
}
-#ifdef CONFIG_LIBCAP
+#ifdef CONFIG_LIBCAP_NG
static int drop_privileges(void)
{
/* clear all capabilities */
@@ -229,7 +246,7 @@ int main(int argc, char **argv)
unsigned long ifargs[4];
#endif
int ifindex;
- int fd, ctlfd, unixfd = -1;
+ int fd = -1, ctlfd = -1, unixfd = -1;
int use_vnet = 0;
int mtu;
const char *bridge = NULL;
@@ -239,8 +256,9 @@ int main(int argc, char **argv)
ACLList acl_list;
int access_allowed, access_denied;
int ret = EXIT_SUCCESS;
+ g_autofree char *acl_file = NULL;
-#ifdef CONFIG_LIBCAP
+#ifdef CONFIG_LIBCAP_NG
/* if we're run from an suid binary, immediately drop privileges preserving
* cap_net_admin */
if (geteuid() == 0 && getuid() != geteuid()) {
@@ -251,6 +269,8 @@ int main(int argc, char **argv)
}
#endif
+ qemu_init_exec_dir(argv[0]);
+
/* parse arguments */
for (index = 1; index < argc; index++) {
if (strcmp(argv[index], "--use-vnet") == 0) {
@@ -269,12 +289,17 @@ int main(int argc, char **argv)
usage();
return EXIT_FAILURE;
}
+ if (strlen(bridge) >= IFNAMSIZ) {
+ fprintf(stderr, "name `%s' too long: %zu\n", bridge, strlen(bridge));
+ return EXIT_FAILURE;
+ }
/* parse default acl file */
QSIMPLEQ_INIT(&acl_list);
- if (parse_acl_file(DEFAULT_ACL_FILE, &acl_list) == -1) {
+ acl_file = get_relocated_path(DEFAULT_ACL_FILE);
+ if (parse_acl_file(acl_file, &acl_list) == -1) {
fprintf(stderr, "failed to parse default acl file `%s'\n",
- DEFAULT_ACL_FILE);
+ acl_file);
ret = EXIT_FAILURE;
goto cleanup;
}
@@ -423,6 +448,18 @@ int main(int argc, char **argv)
goto cleanup;
}
+#ifndef CONFIG_LIBCAP
+ /*
+ * avoid sending the fd as root user if running suid to not fool
+ * peer credentials to daemons that dont expect that
+ */
+ if (setuid(getuid()) < 0) {
+ fprintf(stderr, "Failed to drop privileges.\n");
+ ret = EXIT_FAILURE;
+ goto cleanup;
+ }
+#endif
+
/* write fd to the domain socket */
if (send_fd(unixfd, fd) == -1) {
fprintf(stderr, "failed to write fd to unix socket: %s\n",
@@ -436,10 +473,15 @@ int main(int argc, char **argv)
/* profit! */
cleanup:
-
+ if (fd >= 0) {
+ close(fd);
+ }
+ if (ctlfd >= 0) {
+ close(ctlfd);
+ }
while ((acl_rule = QSIMPLEQ_FIRST(&acl_list)) != NULL) {
QSIMPLEQ_REMOVE_HEAD(&acl_list, entry);
- g_free(acl_rule);
+ free(acl_rule);
}
return ret;