public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: David Gibson <david@gibson.dropbear.id.au>
To: passt-dev@passt.top
Subject: [PATCH 07/28] Clean up parsing in conf_runas()
Date: Wed, 28 Sep 2022 14:33:18 +1000	[thread overview]
Message-ID: <20220928043339.613538-8-david@gibson.dropbear.id.au> (raw)
In-Reply-To: <20220928043339.613538-1-david@gibson.dropbear.id.au>

[-- Attachment #1: Type: text/plain, Size: 6002 bytes --]

conf_runas() handles several of the different possible cases for the
--runas argument in a slightly odd order.  Although it can parse both
numeric UIDs/GIDs and user/group names, it can't parse a numeric UID
combined with a group name or vice versa.  That's not obviously useful, but
it's slightly surprising gap to have.

Rework the parsing to be more systematic: first split the option into
user and (optional) group parts, then separately parse each part as either
numeric or a name.  As a bonus this removes some clang-tidy warnings.

While we're there also add cppcheck suppressions for getpwnam() and
getgrnam().  It complains about those because they're not reentrant.
passt is single threaded though, and is always likely to be during
this initialization code, even if we multithread later.

There were some existing suppressions for these in the cppcheck
invocation but they're no longer up to date.  Replace them with inline
suppressions which, being next to the code, are more likely to stay
correct.

Signed-off-by: David Gibson <david(a)gibson.dropbear.id.au>
---
 Makefile |  3 +-
 conf.c   | 95 +++++++++++++++++++++++++++++---------------------------
 2 files changed, 51 insertions(+), 47 deletions(-)

diff --git a/Makefile b/Makefile
index 59967eb..e0933f3 100644
--- a/Makefile
+++ b/Makefile
@@ -282,12 +282,12 @@ cppcheck: $(SRCS) $(HEADERS)
 	$(SYSTEM_INCLUDES:%=--config-exclude=%)				\
 	$(SYSTEM_INCLUDES:%=--suppress=*:%/*)				\
 	$(SYSTEM_INCLUDES:%=--suppress=unmatchedSuppression:%/*)	\
+	--inline-suppr							\
 	--suppress=objectIndex:tcp.c --suppress=objectIndex:udp.c	\
 	--suppress=va_list_usedBeforeStarted:util.c			\
 	--suppress=unusedFunction					\
 	--suppress=knownConditionTrueFalse:conf.c			\
 	--suppress=strtokCalled:conf.c --suppress=strtokCalled:qrap.c	\
-	--suppress=getpwnamCalled:passt.c				\
 	--suppress=localtimeCalled:pcap.c				\
 	--suppress=unusedStructMember:pcap.c				\
 	--suppress=funcArgNamesDifferent:util.h				\
@@ -295,7 +295,6 @@ cppcheck: $(SRCS) $(HEADERS)
 									\
 	--suppress=unmatchedSuppression:conf.c				\
 	--suppress=unmatchedSuppression:dhcp.c				\
-	--suppress=unmatchedSuppression:passt.c				\
 	--suppress=unmatchedSuppression:pcap.c				\
 	--suppress=unmatchedSuppression:qrap.c				\
 	--suppress=unmatchedSuppression:tcp.c				\
diff --git a/conf.c b/conf.c
index dbc8864..c668eea 100644
--- a/conf.c
+++ b/conf.c
@@ -859,46 +859,50 @@ dns6:
  *
  * Return: 0 on success, negative error code on failure
  */
-static int conf_runas(const char *opt, unsigned int *uid, unsigned int *gid)
+static int conf_runas(char *opt, unsigned int *uid, unsigned int *gid)
 {
-	char ubuf[LOGIN_NAME_MAX], gbuf[LOGIN_NAME_MAX], *endptr;
-	struct passwd *pw;
-	struct group *gr;
+	const char *uopt, *gopt = NULL;
+	char *sep = strchr(opt, ':');
+	char *endptr;
 
-	/* NOLINTNEXTLINE(cert-err34-c): 2 if conversion succeeds */
-	if (sscanf(opt, "%u:%u", uid, gid) == 2 && *uid && *gid)
-		return 0;
-
-	*uid = strtol(opt, &endptr, 0);
-	if (!*endptr && (*gid = *uid))
-		return 0;
-
-#ifdef GLIBC_NO_STATIC_NSS
-	(void)ubuf;
-	(void)gbuf;
-	(void)pw;
-	(void)gr;
-
-	return -EINVAL;
-#else
-	/* NOLINTNEXTLINE(cert-err34-c): 2 if conversion succeeds */
-	if (sscanf(opt, "%" STR(LOGIN_NAME_MAX) "[^:]:"
-			"%" STR(LOGIN_NAME_MAX) "s", ubuf, gbuf) == 2) {
-		if (!(pw = getpwnam(ubuf)) || !(*uid = pw->pw_uid))
-			return -ENOENT;
+	if (sep) {
+		*sep = '\0';
+		gopt = sep + 1;
+	}
+	uopt = opt;
 
-		if (!(gr = getgrnam(gbuf)) || !(*gid = gr->gr_gid))
+	*gid = *uid = strtol(uopt, &endptr, 0);
+	if (*endptr) {
+#ifndef GLIBC_NO_STATIC_NSS
+		/* Not numeric, look up as a username */
+		struct passwd *pw;
+		/* cppcheck-suppress getpwnamCalled */
+		if (!(pw = getpwnam(uopt)) || !(*uid = pw->pw_uid))
 			return -ENOENT;
+		*gid = pw->pw_gid;
+#else
+		return -EINVAL;
+#endif
+	}
 
+	if (!gopt)
 		return 0;
-	}
 
-	pw = getpwnam(ubuf);
-	if (!pw || !(*uid = pw->pw_uid) || !(*gid = pw->pw_gid))
-		return -ENOENT;
+	*gid = strtol(gopt, &endptr, 0);
+	if (*endptr) {
+#ifndef GLIBC_NO_STATIC_NSS
+		/* Not numeric, look up as a group name */
+		struct group *gr;
+		/* cppcheck-suppress getgrnamCalled */
+		if (!(gr = getgrnam(gopt)))
+			return -ENOENT;
+		*gid = gr->gr_gid;
+#else
+		return -EINVAL;
+#endif
+	}
 
 	return 0;
-#endif /* !GLIBC_NO_STATIC_NSS */
 }
 
 /**
@@ -909,10 +913,9 @@ static int conf_runas(const char *opt, unsigned int *uid, unsigned int *gid)
  *
  * Return: 0 on success, negative error code on failure
  */
-static int conf_ugid(const char *runas, uid_t *uid, gid_t *gid)
+static int conf_ugid(char *runas, uid_t *uid, gid_t *gid)
 {
 	const char root_uid_map[] = "         0          0 4294967295";
-	struct passwd *pw;
 	char buf[BUFSIZ];
 	int ret;
 	int fd;
@@ -951,21 +954,23 @@ static int conf_ugid(const char *runas, uid_t *uid, gid_t *gid)
 
 	/* ...otherwise use nobody:nobody */
 	warn("Don't run as root. Changing to nobody...");
+	{
 #ifndef GLIBC_NO_STATIC_NSS
-	pw = getpwnam("nobody");
-	if (!pw) {
-		perror("getpwnam");
-		exit(EXIT_FAILURE);
-	}
+		struct passwd *pw;
+		/* cppcheck-suppress getpwnamCalled */
+		pw = getpwnam("nobody");
+		if (!pw) {
+			perror("getpwnam");
+			exit(EXIT_FAILURE);
+		}
 
-	*uid = pw->pw_uid;
-	*gid = pw->pw_gid;
+		*uid = pw->pw_uid;
+		*gid = pw->pw_gid;
 #else
-	(void)pw;
-
-	/* Common value for 'nobody', not really specified */
-	*uid = *gid = 65534;
+		/* Common value for 'nobody', not really specified */
+		*uid = *gid = 65534;
 #endif
+	}
 	return 0;
 }
 
@@ -1032,8 +1037,8 @@ void conf(struct ctx *c, int argc, char **argv)
 	struct fqdn *dnss = c->dns_search;
 	uint32_t *dns4 = c->ip4.dns;
 	int name, ret, mask, b, i;
-	const char *runas = NULL;
 	unsigned int ifi = 0;
+	char *runas = NULL;
 	uid_t uid;
 	gid_t gid;
 
-- 
@@ -859,46 +859,50 @@ dns6:
  *
  * Return: 0 on success, negative error code on failure
  */
-static int conf_runas(const char *opt, unsigned int *uid, unsigned int *gid)
+static int conf_runas(char *opt, unsigned int *uid, unsigned int *gid)
 {
-	char ubuf[LOGIN_NAME_MAX], gbuf[LOGIN_NAME_MAX], *endptr;
-	struct passwd *pw;
-	struct group *gr;
+	const char *uopt, *gopt = NULL;
+	char *sep = strchr(opt, ':');
+	char *endptr;
 
-	/* NOLINTNEXTLINE(cert-err34-c): 2 if conversion succeeds */
-	if (sscanf(opt, "%u:%u", uid, gid) == 2 && *uid && *gid)
-		return 0;
-
-	*uid = strtol(opt, &endptr, 0);
-	if (!*endptr && (*gid = *uid))
-		return 0;
-
-#ifdef GLIBC_NO_STATIC_NSS
-	(void)ubuf;
-	(void)gbuf;
-	(void)pw;
-	(void)gr;
-
-	return -EINVAL;
-#else
-	/* NOLINTNEXTLINE(cert-err34-c): 2 if conversion succeeds */
-	if (sscanf(opt, "%" STR(LOGIN_NAME_MAX) "[^:]:"
-			"%" STR(LOGIN_NAME_MAX) "s", ubuf, gbuf) == 2) {
-		if (!(pw = getpwnam(ubuf)) || !(*uid = pw->pw_uid))
-			return -ENOENT;
+	if (sep) {
+		*sep = '\0';
+		gopt = sep + 1;
+	}
+	uopt = opt;
 
-		if (!(gr = getgrnam(gbuf)) || !(*gid = gr->gr_gid))
+	*gid = *uid = strtol(uopt, &endptr, 0);
+	if (*endptr) {
+#ifndef GLIBC_NO_STATIC_NSS
+		/* Not numeric, look up as a username */
+		struct passwd *pw;
+		/* cppcheck-suppress getpwnamCalled */
+		if (!(pw = getpwnam(uopt)) || !(*uid = pw->pw_uid))
 			return -ENOENT;
+		*gid = pw->pw_gid;
+#else
+		return -EINVAL;
+#endif
+	}
 
+	if (!gopt)
 		return 0;
-	}
 
-	pw = getpwnam(ubuf);
-	if (!pw || !(*uid = pw->pw_uid) || !(*gid = pw->pw_gid))
-		return -ENOENT;
+	*gid = strtol(gopt, &endptr, 0);
+	if (*endptr) {
+#ifndef GLIBC_NO_STATIC_NSS
+		/* Not numeric, look up as a group name */
+		struct group *gr;
+		/* cppcheck-suppress getgrnamCalled */
+		if (!(gr = getgrnam(gopt)))
+			return -ENOENT;
+		*gid = gr->gr_gid;
+#else
+		return -EINVAL;
+#endif
+	}
 
 	return 0;
-#endif /* !GLIBC_NO_STATIC_NSS */
 }
 
 /**
@@ -909,10 +913,9 @@ static int conf_runas(const char *opt, unsigned int *uid, unsigned int *gid)
  *
  * Return: 0 on success, negative error code on failure
  */
-static int conf_ugid(const char *runas, uid_t *uid, gid_t *gid)
+static int conf_ugid(char *runas, uid_t *uid, gid_t *gid)
 {
 	const char root_uid_map[] = "         0          0 4294967295";
-	struct passwd *pw;
 	char buf[BUFSIZ];
 	int ret;
 	int fd;
@@ -951,21 +954,23 @@ static int conf_ugid(const char *runas, uid_t *uid, gid_t *gid)
 
 	/* ...otherwise use nobody:nobody */
 	warn("Don't run as root. Changing to nobody...");
+	{
 #ifndef GLIBC_NO_STATIC_NSS
-	pw = getpwnam("nobody");
-	if (!pw) {
-		perror("getpwnam");
-		exit(EXIT_FAILURE);
-	}
+		struct passwd *pw;
+		/* cppcheck-suppress getpwnamCalled */
+		pw = getpwnam("nobody");
+		if (!pw) {
+			perror("getpwnam");
+			exit(EXIT_FAILURE);
+		}
 
-	*uid = pw->pw_uid;
-	*gid = pw->pw_gid;
+		*uid = pw->pw_uid;
+		*gid = pw->pw_gid;
 #else
-	(void)pw;
-
-	/* Common value for 'nobody', not really specified */
-	*uid = *gid = 65534;
+		/* Common value for 'nobody', not really specified */
+		*uid = *gid = 65534;
 #endif
+	}
 	return 0;
 }
 
@@ -1032,8 +1037,8 @@ void conf(struct ctx *c, int argc, char **argv)
 	struct fqdn *dnss = c->dns_search;
 	uint32_t *dns4 = c->ip4.dns;
 	int name, ret, mask, b, i;
-	const char *runas = NULL;
 	unsigned int ifi = 0;
+	char *runas = NULL;
 	uid_t uid;
 	gid_t gid;
 
-- 
2.37.3


  parent reply	other threads:[~2022-09-28  4:33 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-09-28  4:33 [PATCH 00/28] Fixes for static checkers David Gibson
2022-09-28  4:33 ` [PATCH 01/28] Clean up parsing of port ranges David Gibson
2022-09-28 20:57   ` Stefano Brivio
2022-09-29  1:04     ` David Gibson
2022-09-28  4:33 ` [PATCH 02/28] clang-tidy: Suppress warning about unchecked error in logfn macro David Gibson
2022-09-28  4:33 ` [PATCH 03/28] clang-tidy: Fix spurious null pointer warning in pasta_start_ns() David Gibson
2022-09-28  4:33 ` [PATCH 04/28] clang-tidy: Remove duplicate #include from icmp.c David Gibson
2022-09-28  4:33 ` [PATCH 05/28] Catch failures when installing signal handlers David Gibson
2022-09-28  4:33 ` [PATCH 06/28] Pack DHCPv6 "on wire" structures David Gibson
2022-09-28  4:33 ` David Gibson [this message]
2022-09-28 20:57   ` [PATCH 07/28] Clean up parsing in conf_runas() Stefano Brivio
2022-09-29  1:44     ` David Gibson
2022-09-28  4:33 ` [PATCH 08/28] cppcheck: Reduce scope of some variables David Gibson
2022-09-28  4:33 ` [PATCH 09/28] Don't shadow 'i' in conf_ports() David Gibson
2022-09-28  4:33 ` [PATCH 10/28] Don't shadow global function names David Gibson
2022-09-28  4:33 ` [PATCH 11/28] Stricter checking for nsholder.c David Gibson
2022-09-28  4:33 ` [PATCH 12/28] cppcheck: Work around false positive NULL pointer dereference error David Gibson
2022-09-28  4:33 ` [PATCH 13/28] cppcheck: Use inline suppression for ffsl() David Gibson
2022-09-28  4:33 ` [PATCH 14/28] cppcheck: Use inline suppressions for qrap.c David Gibson
2022-09-28  4:33 ` [PATCH 15/28] cppcheck: Use inline suppression for strtok() in conf.c David Gibson
2022-09-28  4:33 ` [PATCH 16/28] Avoid ugly 'end' members in netlink structures David Gibson
2022-09-28  4:33 ` [PATCH 17/28] cppcheck: Broaden suppression for unused struct members David Gibson
2022-09-28  4:33 ` [PATCH 18/28] cppcheck: Remove localtime suppression for pcap.c David Gibson
2022-09-28  4:33 ` [PATCH 19/28] qrap: Handle case of PATH environment variable being unset David Gibson
2022-09-28  4:33 ` [PATCH 20/28] cppcheck: Suppress same-value-in-ternary branches warning David Gibson
2022-09-28 20:58   ` Stefano Brivio
2022-09-29  1:00     ` David Gibson
2022-09-28  4:33 ` [PATCH 21/28] cppcheck: Suppress NULL pointer warning in tcp_sock_consume() David Gibson
2022-09-28 20:58   ` Stefano Brivio
2022-09-29  1:07     ` David Gibson
2022-09-28  4:33 ` [PATCH 22/28] Regenerate seccomp.h if seccomp.sh changes David Gibson
2022-09-28  4:33 ` [PATCH 23/28] cppcheck: Avoid errors due to zeroes in bitwise ORs David Gibson
2022-09-28  4:33 ` [PATCH 24/28] cppcheck: Remove unused knownConditionTrueFalse suppression David Gibson
2022-09-28 20:58   ` Stefano Brivio
2022-09-29  1:24     ` David Gibson
2022-09-28  4:33 ` [PATCH 25/28] cppcheck: Remove unused objectIndex suppressions David Gibson
2022-09-28 20:58   ` Stefano Brivio
2022-09-29  1:12     ` David Gibson
2022-09-28  4:33 ` [PATCH 26/28] cppcheck: Remove unused va_list_usedBeforeStarted suppression David Gibson
2022-09-28  4:33 ` [PATCH 27/28] Mark unused functions for cppcheck David Gibson
2022-09-28  4:33 ` [PATCH 28/28] cppcheck: Remove unused unmatchedSuppression suppressions David Gibson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220928043339.613538-8-david@gibson.dropbear.id.au \
    --to=david@gibson.dropbear.id.au \
    --cc=passt-dev@passt.top \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).