Index: usr.bin/iconv/iconv.c =================================================================== --- usr.bin/iconv/iconv.c +++ usr.bin/iconv/iconv.c @@ -28,6 +28,7 @@ */ #include +#include #include #include @@ -35,13 +36,15 @@ #include #include #include +#include #include #include #include #include +#include #include -static int do_conv(FILE *, const char *, const char *, bool, bool); +static int do_conv(FILE *, iconv_t, bool, bool); static int do_list(unsigned int, const char * const *, void *); static void usage(void) __dead2; @@ -67,23 +70,16 @@ #define INBUFSIZE 1024 #define OUTBUFSIZE (INBUFSIZE * 2) static int -do_conv(FILE *fp, const char *from, const char *to, bool silent, - bool hide_invalid) +do_conv(FILE *fp, iconv_t cd, bool silent, bool hide_invalid) { - iconv_t cd; char inbuf[INBUFSIZE], outbuf[OUTBUFSIZE], *in, *out; unsigned long long invalids; size_t inbytes, outbytes, ret; - if ((cd = iconv_open(to, from)) == (iconv_t)-1) - err(EXIT_FAILURE, "iconv_open(%s, %s)", to, from); - - if (hide_invalid) { - int arg = 1; + int arg = (int)hide_invalid; + if (iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, (void *)&arg) == -1) + err(EXIT_FAILURE, "iconvctl(DISCARD_ILSEQ, %d)", arg); - if (iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, (void *)&arg) == -1) - err(EXIT_FAILURE, NULL); - } invalids = 0; while ((inbytes = fread(inbuf, 1, INBUFSIZE, fp)) > 0) { in = inbuf; @@ -133,7 +129,6 @@ if (invalids > 0 && !silent) warnx("warning: invalid characters: %llu", invalids); - iconv_close(cd); return (invalids > 0); } @@ -155,6 +150,9 @@ int main(int argc, char **argv) { + cap_rights_t rights; + unsigned long cmd; + iconv_t cd; FILE *fp; const char *opt_f, *opt_t; int ch, i, res; @@ -201,9 +199,40 @@ argv += optind; if ((strcmp(opt_f, "") == 0) && (strcmp(opt_t, "") == 0)) usage(); - if (argc == 0) - res = do_conv(stdin, opt_f, opt_t, opt_s, opt_c); - else { + + cap_rights_init(&rights, CAP_FSTAT, CAP_IOCTL, CAP_WRITE); + if (cap_rights_limit(STDOUT_FILENO, &rights) < 0 && errno != ENOSYS) + err(EXIT_FAILURE, "unable to limit rights for stdout"); + + cap_rights_init(&rights, CAP_FSTAT, CAP_IOCTL, CAP_READ); + if (cap_rights_limit(STDIN_FILENO, &rights) < 0 && errno != ENOSYS) + err(EXIT_FAILURE, "unable to limit rights for stdin"); + + /* Required for printf(3)/std input, via isatty(3). */ + cmd = TIOCGETA; + if (cap_ioctls_limit(STDOUT_FILENO, &cmd, 1) < 0 && errno != ENOSYS) + err(EXIT_FAILURE, "unable to limit ioctls for stdout"); + if (cap_ioctls_limit(STDIN_FILENO, &cmd, 1) < 0 && errno != ENOSYS) + err(EXIT_FAILURE, "unable to limit ioctls for stdin"); + + /* + * Cache NLS data, for strerror, for err(3), before entering capability + * mode. + */ + (void)catopen("libc", NL_CAT_LOCALE); + + /* + * Cache iconv conversion handle before entering sandbox. + */ + cd = iconv_open(opt_t, opt_f); + if (cd == (iconv_t)-1) + err(EXIT_FAILURE, "iconv_open(%s, %s)", opt_t, opt_f); + + if (argc == 0) { + if (cap_enter() < 0 && errno != ENOSYS) + err(EXIT_FAILURE, "unable to enter capability mode"); + res = do_conv(stdin, cd, opt_s, opt_c); + } else { res = 0; for (i = 0; i < argc; i++) { fp = (strcmp(argv[i], "-") != 0) ? @@ -211,9 +240,17 @@ if (fp == NULL) err(EXIT_FAILURE, "Cannot open `%s'", argv[i]); - res |= do_conv(fp, opt_f, opt_t, opt_s, opt_c); + /* Enter Capsicum sandbox for final input file. */ + if (i + 1 == argc && cap_enter() < 0 && errno != ENOSYS) + err(EXIT_FAILURE, + "unable to enter capability mode"); + res |= do_conv(fp, cd, opt_s, opt_c); (void)fclose(fp); + + /* Reset iconv descriptor state. */ + (void)iconv(cd, NULL, NULL, NULL, NULL); } } + iconv_close(cd); return (res == 0 ? EXIT_SUCCESS : EXIT_FAILURE); }