On 2023-03-11 14:02, Alejandro Colomar wrote:
we should use "%s" (if we go the way of snprintf(3)).

Yes, thanks for catching that. However, I came up with a better way that avoids snprintf (and strlcpy) entirely both here and the other place I used snprintf.

Attached is a revised set of patches that addresses the comments you made and embodies my followups.
From 324bb0e914b5470650df02bd7b64e963665d44c1 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Sat, 11 Mar 2023 00:01:02 -0800
Subject: [PATCH 1/8] Simplify change_field by using strcpy

* lib/fields.c (change_field): Since we know the string fits,
use strcpy rather than strlcpy.

Signed-off-by: Paul Eggert <egg...@cs.ucla.edu>
---
 lib/fields.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/lib/fields.c b/lib/fields.c
index 0b5f91b2..8801bce6 100644
--- a/lib/fields.c
+++ b/lib/fields.c
@@ -100,7 +100,6 @@ void change_field (char *buf, size_t maxsize, const char *prompt)
 			cp++;
 		}
 
-		strlcpy (buf, cp, maxsize);
+		strcpy (buf, cp);
 	}
 }
-
-- 
2.37.2

From b13ffb86dcd10e8160eee10bd286fc73937c3e8b Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Sat, 11 Mar 2023 13:41:54 -0800
Subject: [PATCH 2/8] Omit unneeded change_field test

* fields.c (change_field): Omit unnecessary test.

Signed-off-by: Paul Eggert <egg...@cs.ucla.edu>
---
 lib/fields.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/fields.c b/lib/fields.c
index 8801bce6..fa5fd156 100644
--- a/lib/fields.c
+++ b/lib/fields.c
@@ -96,7 +96,7 @@ void change_field (char *buf, size_t maxsize, const char *prompt)
 		*cp = '\0';
 
 		cp = newf;
-		while (('\0' != *cp) && isspace (*cp)) {
+		while (isspace (*cp)) {
 			cp++;
 		}
 
-- 
2.37.2

From 090722a20765cf9a248050524143fce5b68cfe8c Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Sat, 11 Mar 2023 13:43:36 -0800
Subject: [PATCH 3/8] Fix change_field buffer underrun

* lib/fields.c (change_field): Don't point
before array start; that has undefined behavior.

Signed-off-by: Paul Eggert <egg...@cs.ucla.edu>
---
 lib/fields.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/lib/fields.c b/lib/fields.c
index fa5fd156..640be931 100644
--- a/lib/fields.c
+++ b/lib/fields.c
@@ -91,8 +91,9 @@ void change_field (char *buf, size_t maxsize, const char *prompt)
 		 * entering a space.  --marekm
 		 */
 
-		while (--cp >= newf && isspace (*cp));
-		cp++;
+		while (newf < cp && isspace (cp[-1])) {
+			cp--;
+		}
 		*cp = '\0';
 
 		cp = newf;
-- 
2.37.2

From 4982d5f2fe2f2c339568996ebb17a99200d2f106 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Sat, 11 Mar 2023 00:02:45 -0800
Subject: [PATCH 4/8] Prefer strcpy to strlcpy when either works

* lib/gshadow.c (sgetsgent): Use strcpy not strlcpy,
since the string is known to fit.

Signed-off-by: Paul Eggert <egg...@cs.ucla.edu>
---
 lib/gshadow.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/gshadow.c b/lib/gshadow.c
index c17af67f..ca14449a 100644
--- a/lib/gshadow.c
+++ b/lib/gshadow.c
@@ -128,7 +128,7 @@ void endsgent (void)
 		sgrbuflen = len;
 	}
 
-	strlcpy (sgrbuf, string, len);
+	strcpy (sgrbuf, string);
 
 	cp = strrchr (sgrbuf, '\n');
 	if (NULL != cp) {
-- 
2.37.2

From 54fac7560f87a134c4d3045ce7048f4819c4e492 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Sat, 11 Mar 2023 00:38:24 -0800
Subject: [PATCH 5/8] Avoid silent truncation of console file data

* libmisc/console.c (is_listed): Rework so that there is no
fixed-size buffer, and no need to use fgets or strlcpy or strtok.
Instead, the code works with arbitrary-sized input,
without silently truncating data or mishandling NUL
bytes in the console file.

Signed-off-by: Paul Eggert <egg...@cs.ucla.edu>
---
 libmisc/console.c | 41 ++++++++++++++++++++---------------------
 1 file changed, 20 insertions(+), 21 deletions(-)

diff --git a/libmisc/console.c b/libmisc/console.c
index 7e2132dd..8264e1a3 100644
--- a/libmisc/console.c
+++ b/libmisc/console.c
@@ -24,7 +24,6 @@
 static bool is_listed (const char *cfgin, const char *tty, bool def)
 {
 	FILE *fp;
-	char buf[1024], *s;
 	const char *cons;
 
 	/*
@@ -43,17 +42,17 @@ static bool is_listed (const char *cfgin, const char *tty, bool def)
 	 */
 
 	if (*cons != '/') {
-		char *pbuf;
-		strlcpy (buf, cons, sizeof (buf));
-		pbuf = &buf[0];
-		while ((s = strtok (pbuf, ":")) != NULL) {
-			if (strcmp (s, tty) == 0) {
+		size_t ttylen = strlen (tty);
+		for (;;) {
+			if (strncmp (cons, tty, ttylen) == 0
+			    && (cons[ttylen] == ':' || !cons[ttylen])) {
 				return true;
 			}
-
-			pbuf = NULL;
+			cons = strchr (cons, ':');
+			if (!cons)
+				return false;
+			cons++;
 		}
-		return false;
 	}
 
 	/*
@@ -70,21 +69,22 @@ static bool is_listed (const char *cfgin, const char *tty, bool def)
 	 * See if this tty is listed in the console file.
 	 */
 
-	while (fgets (buf, sizeof (buf), fp) != NULL) {
-		/* Remove optional trailing '\n'. */
-		buf[strcspn (buf, "\n")] = '\0';
-		if (strcmp (buf, tty) == 0) {
-			(void) fclose (fp);
-			return true;
+	const char *tp = tty;
+	bool listed = false;
+	for (int c; 0 <= (c = getc (fp)); ) {
+		if (c == '\n') {
+			if (tp && !*tp) {
+				listed = true;
+				break;
+			}
+			tp = tty;
+		} else if (tp) {
+			tp = *tp == c && c ? tp + 1 : NULL;
 		}
 	}
 
-	/*
-	 * This tty isn't a console.
-	 */
-
 	(void) fclose (fp);
-	return false;
+	return listed;
 }
 
 /*
@@ -105,4 +105,3 @@ bool console (const char *tty)
 
 	return is_listed ("CONSOLE", tty, true);
 }
-
-- 
2.37.2

From f3514f26297e884a00d4fb29191bd9978eb03e7b Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Sat, 11 Mar 2023 00:42:29 -0800
Subject: [PATCH 6/8] Fix crash with large timestamps

* libmisc/date_to_str.c (date_to_str): Do not crash if gmtime
returns NULL because the timestamp is far in the future.
Instead, use a dummy struct tm * to pacify any pedantic runtime.
Simplify by always calling strftime, instead of sometimes strftime
and sometimes strlcpy.

Signed-off-by: Paul Eggert <egg...@cs.ucla.edu>
---
 libmisc/date_to_str.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/libmisc/date_to_str.c b/libmisc/date_to_str.c
index f3b9dc76..0840c38c 100644
--- a/libmisc/date_to_str.c
+++ b/libmisc/date_to_str.c
@@ -35,13 +35,16 @@
 
 void date_to_str (size_t size, char buf[size], long date)
 {
-	time_t t;
+	time_t t = date;
+	struct tm *tm = gmtime (&t);
 
-	t = date;
-	if (date < 0) {
-		(void) strlcpy (buf, "never", size);
-	} else {
-		(void) strftime (buf, size, "%Y-%m-%d", gmtime (&t));
-		buf[size - 1] = '\0';
-	}
+	/* A dummy whose address can be passed to strftime to avoid
+	   undefined behavior.  It's OK for it to be uninitialized
+	   since no conversion specs are used.  */
+	struct tm dummy;
+
+	(void) strftime (buf, size,
+			 date < 0 ? "never" : tm ? "%Y-%m-%d" : "future",
+			 tm ? tm : &dummy);
+	buf[size - 1] = '\0';
 }
-- 
2.37.2

From fab3bcdcb3f38c7f6f5c326f4ceafb3ea54bba73 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Sat, 11 Mar 2023 10:07:32 -0800
Subject: [PATCH 7/8] Fix is_my_tty overruns and truncations

* libmisc/utmp.c (is_my_tty): Declare the parameter
as a char array not char *, as it is not necessarily null-terminated.
Avoid a read overrun when reading ut_utname.  Do not silently truncate
the string returned by ttyname; instead, do not cache an overlong
ttyname, as the behavior is correct in this case (albeit slower).
Use strnlen + strcpy instead of strlcpy to avoid polluting the
cache with truncated data.

Signed-off-by: Paul Eggert <egg...@cs.ucla.edu>
---
 libmisc/utmp.c | 42 ++++++++++++++++++++++--------------------
 1 file changed, 22 insertions(+), 20 deletions(-)

diff --git a/libmisc/utmp.c b/libmisc/utmp.c
index ff6acee0..bf7e5675 100644
--- a/libmisc/utmp.c
+++ b/libmisc/utmp.c
@@ -24,36 +24,38 @@
 
 #ident "$Id$"
 
+enum { UT_LINE_LEN = sizeof (getutent ()->ut_line) };
 
 /*
  * is_my_tty -- determine if "tty" is the same TTY stdin is using
  */
-static bool is_my_tty (const char *tty)
+static bool is_my_tty (const char tty[UT_LINE_LEN])
 {
-	/* full_tty shall be at least sizeof utmp.ut_line + 5 */
-	char full_tty[200];
-	/* tmptty shall be bigger than full_tty */
-	static char tmptty[sizeof (full_tty)+1];
-
-	if ('/' != *tty) {
-		(void) snprintf (full_tty, sizeof full_tty, "/dev/%s", tty);
-		tty = &full_tty[0];
-	}
+	/* A null-terminated copy of tty, prefixed with "/dev/" if tty
+	   is not absolute.  There is no need for snprintf, as sprintf
+	   cannot overrun.  */
+	char full_tty[sizeof "/dev/" + UT_LINE_LEN];
+	(void) sprintf (full_tty, "%s%.*s", '/' == *tty ? "" : "/dev/",
+			UT_LINE_LEN, tty);
 
-	if ('\0' == tmptty[0]) {
-		const char *tname = ttyname (STDIN_FILENO);
-		if (NULL != tname)
-			(void) strlcpy (tmptty, tname, sizeof tmptty);
-	}
+	/* Cache of ttyname, valid if tmptty[0].  */
+	static char tmptty[UT_LINE_LEN + 1];
 
+	const char *tname;
 	if ('\0' == tmptty[0]) {
-		(void) puts (_("Unable to determine your tty name."));
-		exit (EXIT_FAILURE);
-	} else if (strncmp (tty, tmptty, sizeof (tmptty)) != 0) {
-		return false;
+		tname = ttyname (STDIN_FILENO);
+		if (! tname) {
+			(void) puts (_("Unable to determine your tty name."));
+			exit (EXIT_FAILURE);
+		}
+		if (strnlen (tname, sizeof tmptty) < sizeof tmptty) {
+			strcpy (tmptty, tname);
+		}
 	} else {
-		return true;
+		tname = tmptty;
 	}
+
+	return strcmp (full_tty, tname) == 0;
 }
 
 /*
-- 
2.37.2

From 9ebf228fb33f66d248b230d23b633800267e5a16 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Sat, 11 Mar 2023 10:34:21 -0800
Subject: [PATCH 8/8] Fix su silent truncation

* src/su.c (check_perms): Do not silently truncate user name.

Signed-off-by: Paul Eggert <egg...@cs.ucla.edu>
---
 src/su.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/su.c b/src/su.c
index 9c134a9b..112be456 100644
--- a/src/su.c
+++ b/src/su.c
@@ -658,7 +658,14 @@ static /*@only@*/struct passwd * check_perms (void)
 		SYSLOG ((LOG_INFO,
 		         "Change user from '%s' to '%s' as requested by PAM",
 		         name, tmp_name));
-		strlcpy (name, tmp_name, sizeof(name));
+		if (sizeof name <= strnlen (tmp_name, sizeof name)) {
+			fprintf (stderr, _("Overlong user name '%s'\n"),
+				 tmp_name);
+			SYSLOG ((LOG_NOTICE, "Overlong user name '%s'",
+				 tmp_name));
+			su_failure (caller_tty, true);
+		}
+		strcpy (name, tmp_name);
 		pw = xgetpwnam (name);
 		if (NULL == pw) {
 			(void) fprintf (stderr,
@@ -1213,4 +1220,3 @@ int main (int argc, char **argv)
 
 	return (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
 }
-
-- 
2.37.2

Reply via email to