less: escape newlines in file names
authortb <tb@openbsd.org>
Sun, 14 Apr 2024 08:34:00 +0000 (08:34 +0000)
committertb <tb@openbsd.org>
Sun, 14 Apr 2024 08:34:00 +0000 (08:34 +0000)
Newlines in a filename can lead to arbitrary code execution
https://marc.info/?l=oss-security&m=171292433330233&w=2
via LESSOPEN.  The diff is a straightforward adaptation of
https://github.com/gwsw/less/commit/007521ac3c95bc76

The better fix is deleting the misfeatures that are LESSOPEN
and LESSCLOSE which will happen in a separate commit.

diff looks good to guenther

usr.bin/less/filename.c

index 8b2dd9a..dfe998f 100644 (file)
@@ -106,6 +106,15 @@ metachar(char c)
        return (strchr(metachars(), c) != NULL);
 }
 
+/*
+ * Must use quotes rather than escape characters for this meta character.
+ */
+static int
+must_quote(char c)
+{
+       return (c == '\n');
+}
+
 /*
  * Insert a backslash before each metacharacter in a string.
  */
@@ -136,6 +145,9 @@ shell_quote(const char *s)
                                 * doesn't support escape chars.  Use quotes.
                                 */
                                use_quotes = 1;
+                       } else if (must_quote(*p)) {
+                               /* Opening quote + character + closing quote. */
+                               len += 3;
                        } else {
                                /*
                                 * Allow space for the escape char.
@@ -155,14 +167,19 @@ shell_quote(const char *s)
        } else {
                newstr = r = ecalloc(len, sizeof (char));
                while (*s != '\0') {
-                       if (metachar(*s)) {
-                               /*
-                                * Add the escape char.
-                                */
+                       if (!metachar(*s)) {
+                               *r++ = *s++;
+                       } else if (must_quote(*s)) {
+                               /* Surround the character with quotes. */
+                               *r++ = openquote;
+                               *r++ = *s++;
+                               *r++ = closequote;
+                       } else {
+                               /* Escape the character. */
                                (void) strlcpy(r, esc, newstr + len - p);
                                r += esclen;
+                               *r++ = *s++;
                        }
-                       *r++ = *s++;
                }
                *r = '\0';
        }