Index: usr.bin/uudecode/uudecode.c =================================================================== --- usr.bin/uudecode/uudecode.c +++ usr.bin/uudecode/uudecode.c @@ -49,12 +49,14 @@ * create the specified file, decoding as you go. * used with uuencode. */ +#include #include #include #include #include +#include #include #include #include @@ -69,7 +71,7 @@ static const char *infile, *outfile; static FILE *infp, *outfp; -static int base64, cflag, iflag, oflag, pflag, rflag, sflag; +static int base64, cflag, iflag, oflag, pflag, rflag, sflag, cwdfd; static void usage(void); static int decode(void); @@ -80,8 +82,15 @@ int main(int argc, char *argv[]) { - int rval, ch; + int rval, ch, fnum, i; + int *infds; + char **infiles; + cap_rights_t rights_ro; + fnum = 0; + rval = 0; + cap_rights_init(&rights_ro, CAP_READ, CAP_FSTAT, CAP_FCNTL); + if (strcmp(basename(argv[0]), "b64decode") == 0) base64 = 1; @@ -128,22 +137,57 @@ argv += optind; if (*argv != NULL) { - rval = 0; + infds = calloc(argc, sizeof(int)); + infiles = calloc(argc, sizeof(char*)); + if (infds == NULL || infiles == NULL) + errc(1, ENOMEM, "malloc() fds"); do { - infp = fopen(infile = *argv, "r"); - if (infp == NULL) { + infds[fnum] = open(*argv, O_RDONLY); + if (infds[fnum] == -1) { warn("%s", *argv); rval = 1; continue; } - rval |= decode(); - fclose(infp); + if (cap_rights_limit(infds[fnum], &rights_ro) < 0 && errno != ENOSYS) + err(1, "unable to limit rights on: %s", *argv); + if (cap_fcntls_limit(infds[fnum], CAP_FCNTL_GETFL) < 0 && errno != ENOSYS) + err(1, "unable to limit fcntls on: %s", *argv); + infiles[fnum] = *argv; + fnum++; } while (*++argv); } else { - infile = "stdin"; - infp = stdin; - rval = decode(); + infds = malloc(sizeof(int)); + infiles = malloc(sizeof(char*)); + if (infds == NULL || infiles == NULL) + errc(1, ENOMEM, "malloc() fd"); + infds[fnum] = STDIN_FILENO; + infiles[fnum] = strdup("stdin"); + if (infiles[fnum] == NULL) + errc(1, ENOMEM, "malloc()"); + fnum++; } + + if ((cwdfd = open(".", O_DIRECTORY)) < 0) + err(1, "Failed to open the working directory"); + + caph_cache_catpages(); + if (caph_limit_stdio() == -1) + err(1, "unable to limit stdio"); + if (cap_enter() < 0 && errno != ENOSYS) + err(1, "failed to enter security sandbox"); + + for (i = 0; i < fnum; i++) { + infp = fdopen(infds[i], "r"); + if (infp == NULL) + err(1, "unable to open: %s", infiles[i]); + infile = infiles[i]; + rval |= decode(); + fclose(infp); + } + + free(infds); + free(infiles); + exit(rval); } @@ -259,7 +303,7 @@ outfp = stdout; else { flags = O_WRONLY | O_CREAT | O_EXCL; - if (lstat(outfile, &st) == 0) { + if (fstatat(cwdfd, outfile, &st, AT_SYMLINK_NOFOLLOW) == 0) { if (iflag) { warnc(EEXIST, "%s: %s", infile, outfile); return (0); @@ -268,7 +312,8 @@ case S_IFREG: case S_IFLNK: /* avoid symlink attacks */ - if (unlink(outfile) == 0 || errno == ENOENT) + if (unlinkat(cwdfd, outfile, 0) == 0 || + errno == ENOENT) break; warn("%s: unlink %s", infile, outfile); return (1); @@ -285,12 +330,12 @@ return (1); } } else if (errno != ENOENT) { - warn("%s: %s", infile, outfile); + warn("%s: stat %s", infile, outfile); return (1); } - if ((fd = open(outfile, flags, mode)) < 0 || + if ((fd = openat(cwdfd, outfile, flags, mode)) < 0 || (outfp = fdopen(fd, "w")) == NULL) { - warn("%s: %s", infile, outfile); + warn("%s: open %s", infile, outfile); return (1); } }