This prevents non-interactive invocations of ftp(1) from spawning
external commands in case they are compromised.  This is a significant
security benefit for sysupgrade(8).

In the future, output files (specified with -o) can be opened before
pledge(2) is called, which will improve security further.

Sincerely,

Demi M. Obenour

Index: usr.bin/ftp/main.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/main.c,v
retrieving revision 1.131
diff -u -p -u -p -r1.131 main.c
--- usr.bin/ftp/main.c  11 Feb 2020 18:41:39 -0000      1.131
+++ usr.bin/ftp/main.c  3 May 2020 18:18:31 -0000
@@ -483,27 +483,33 @@ main(volatile int argc, char *argv[])
        (void)signal(SIGWINCH, setttywidth);
 
        if (argc > 0) {
+               char **arg;
+               int needs_exec = 0;
                if (isurl(argv[0])) {
-                       if (pipeout) {
 #ifndef SMALL
-                               if (pledge("stdio rpath dns tty inet proc exec 
fattr",
-                                   NULL) == -1)
-                                       err(1, "pledge");
-#else
-                               if (pledge("stdio rpath dns tty inet fattr",
-                                   NULL) == -1)
-                                       err(1, "pledge");
+                       for (arg = argv; *arg; ++arg)
+                               needs_exec |= is_interactive_url(*arg);
 #endif
+                       if (pipeout) {
+                               if (needs_exec) {
+                                       if (pledge("stdio rpath dns tty inet 
proc exec fattr",
+                                           NULL) == -1)
+                                               err(1, "pledge");
+                               } else {
+                                       if (pledge("stdio rpath dns tty inet 
fattr",
+                                           NULL) == -1)
+                                               err(1, "pledge");
+                               }
                        } else {
-#ifndef SMALL
-                               if (pledge("stdio rpath wpath cpath dns tty 
inet proc exec fattr",
-                                   NULL) == -1)
-                                       err(1, "pledge");
-#else
-                               if (pledge("stdio rpath wpath cpath dns tty 
inet fattr",
-                                   NULL) == -1)
-                                       err(1, "pledge");
-#endif
+                               if (needs_exec) {
+                                       if (pledge("stdio rpath wpath cpath dns 
tty inet proc exec fattr",
+                                           NULL) == -1)
+                                               err(1, "pledge");
+                               } else {
+                                       if (pledge("stdio rpath wpath cpath dns 
tty inet fattr",
+                                           NULL) == -1)
+                                               err(1, "pledge");
+                               }
                        }
 
                        rval = auto_fetch(argc, argv, outfile);
Index: usr.bin/ftp/fetch.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/fetch.c,v
retrieving revision 1.194
diff -u -p -u -p -r1.194 fetch.c
--- usr.bin/ftp/fetch.c 22 Feb 2020 01:00:07 -0000      1.194
+++ usr.bin/ftp/fetch.c 3 May 2020 18:18:32 -0000
@@ -1601,6 +1601,32 @@ hextochar(const char *str)
        return ret;
 }
 
+#ifndef SMALL
+int
+is_interactive_url(const char *p)
+{
+       size_t urllen;
+       const char *path;
+       if (strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
+#ifndef NOSSL
+           strncasecmp(p, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 ||
+#endif /* !NOSSL */
+           strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0)
+               return (0);
+       if ((urllen = strlen(p)) == 0)
+               errx(1, "empty URL not allowed");
+       if (p[urllen - 1] == '/')
+               return (1);
+       if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) != 0)
+               return (0);
+       p += sizeof(FTP_URL) - 1;
+       urllen -= sizeof(FTP_URL) - 1;
+       if (urllen == 0)
+               errx(1, "empty URL not allowed");
+       return strchr(p, '/') == NULL;
+}
+#endif
+
 int
 isurl(const char *p)
 {
Index: usr.bin/ftp/extern.h
===================================================================
RCS file: /cvs/src/usr.bin/ftp/extern.h,v
retrieving revision 1.51
diff -u -p -u -p -r1.51 extern.h
--- usr.bin/ftp/extern.h        16 May 2019 12:44:17 -0000      1.51
+++ usr.bin/ftp/extern.h        3 May 2020 18:18:32 -0000
@@ -88,6 +88,7 @@ char   *hookup(char *, char *);
 int    initconn(void);
 void   intr(void);
 int    isurl(const char *);
+int    is_interactive_url(const char *);
 int    ftp_login(const char *, char *, char *);
 void   lostpeer(void);
 void   makeargv(void);

Reply via email to