Hi tech@ This is a feture that came up in a chat I had with Kurt Mosiejczuk. I have been recently reading source daily as a learning experience and decided that implementing the feature we discussed would be a nice exercise.
The attached diff extends the configuration syntax with a new option 'confirm' which when present on a rule triggers doas to print what command is about to run, by whom and as what user with a question asking if the execution should continue. Rationale for adding this are scripts that shell out doas commands showing just a prompt with no way for the user to tell what command he is authenticating. with the following rule permit confirm persist mulander as root running a script that calls doas results in the following behavior: $ sh test.sh mulander wants to run 'whoami' as root. Continue? [yN] doas: aborted by user allowing the user to bail out or proceed. This re-uses variables already there for syslog logging (except we don't use pwd as that would require moving getcwd calls early and we had to introduce `first` for getchar checking). brynet@ pointed out -n (non interactive mode), when -n is present we bail out with the following message: $ doas.new -n whoami doas: Confirmation required This email is a request for comment, roughly I want to know if others see this feature as valuable. The diff currently lacks manpage changes, I will work on those if the general decision is to include this feature. I won't cry if we decide to drop this. I would have implemented it anyways for fun :) Regards, Adam
Index: doas.c =================================================================== RCS file: /cvs/src/usr.bin/doas/doas.c,v retrieving revision 1.72 diff -u -p -r1.72 doas.c --- doas.c 27 May 2017 09:51:07 -0000 1.72 +++ doas.c 8 Jun 2017 17:37:20 -0000 @@ -256,7 +256,7 @@ main(int argc, char **argv) uid_t target = 0; gid_t groups[NGROUPS_MAX + 1]; int ngroups; - int i, ch; + int i, ch, first; int sflag = 0; int nflag = 0; char cwdpath[PATH_MAX]; @@ -355,6 +355,20 @@ main(int argc, char **argv) syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed command for %s: %s", myname, cmdline); errc(1, EPERM, NULL); + } + + if (rule->options & CONFIRM) { + if (nflag) + errx(1, "Confirmation required"); + + printf("%s wants to run '%s' as %s. Continue? [yN] ", + myname, cmdline, pw->pw_name); + fflush(stdout); + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (first != 'y' && first != 'Y') + errx(1, "aborted by user"); } if (!(rule->options & NOPASS)) { Index: doas.h =================================================================== RCS file: /cvs/src/usr.bin/doas/doas.h,v retrieving revision 1.13 diff -u -p -r1.13 doas.h --- doas.h 6 Apr 2017 21:12:06 -0000 1.13 +++ doas.h 8 Jun 2017 17:37:20 -0000 @@ -37,3 +37,5 @@ char **prepenv(const struct rule *); #define NOPASS 0x1 #define KEEPENV 0x2 #define PERSIST 0x4 +#define CONFIRM 0x8 + Index: parse.y =================================================================== RCS file: /cvs/src/usr.bin/doas/parse.y,v retrieving revision 1.26 diff -u -p -r1.26 parse.y --- parse.y 2 Jan 2017 01:40:20 -0000 1.26 +++ parse.y 8 Jun 2017 17:37:20 -0000 @@ -69,7 +69,7 @@ arraylen(const char **arr) %} -%token TPERMIT TDENY TAS TCMD TARGS +%token TPERMIT TDENY TAS TCMD TCONFIRM TARGS %token TNOPASS TPERSIST TKEEPENV TSETENV %token TSTRING @@ -136,6 +136,9 @@ options: /* none */ { option: TNOPASS { $$.options = NOPASS; $$.envlist = NULL; + } | TCONFIRM { + $$.options = CONFIRM; + $$.envlist = NULL; } | TPERSIST { $$.options = PERSIST; $$.envlist = NULL; @@ -209,6 +212,7 @@ static struct keyword { { "cmd", TCMD }, { "args", TARGS }, { "nopass", TNOPASS }, + { "confirm", TCONFIRM }, { "persist", TPERSIST }, { "keepenv", TKEEPENV }, { "setenv", TSETENV },