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);