OpenSMTPD: unprivileged mode - now with diff

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

OpenSMTPD: unprivileged mode - now with diff

Christopher Zimmermann-2
Hi,

I further developed my approach to allow running smtpd with fewer
privileges. This diff does two things:

- always run lmtp deliveries as SMTPD_USER. The change to mda_unpriv.c
   is needed, because otherwise all mails would be delivered to
   SMTPD_USER.
- add two internal flags NOPRIV and NEEDPRIV. NOPRIV can be configured
   by the simple directive "no-priv". NEEDPRIV gets set on all delivery
   methods / options requiring setuid() to run as the receipient user.
   A configuration error is produced on any conflict betweed NEEDPRIV and
   NOPRIV.
   In case of a NOPRIV run smtpd will drop root privileges.
   This will break .forward and alias filters.

The change to the lmtp delivery has benefits even without the second
change. With the second change my smtpd now runs without root
privileges.
The NEEDPRIV/NOPRIV options are meant to allow restricting of the
privileges of other delivery methods.

I am now looking for OKs on the first change to do unprivileged lmtp
deliveries and feedback on the general approach of the second change.


Christopher



Index: mda_unpriv.c
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/mda_unpriv.c,v
retrieving revision 1.6
diff -u -p -r1.6 mda_unpriv.c
--- mda_unpriv.c 2 Feb 2020 22:13:48 -0000 1.6
+++ mda_unpriv.c 26 Apr 2020 05:27:34 -0000
@@ -69,8 +69,8 @@ mda_unpriv(struct dispatcher *dsp, struc
  xasprintf(&mda_environ[idx++], "RECIPIENT=%s@%s", deliver->dest.user, deliver->dest.domain);
  xasprintf(&mda_environ[idx++], "SHELL=/bin/sh");
  xasprintf(&mda_environ[idx++], "LOCAL=%s", deliver->rcpt.user);
- xasprintf(&mda_environ[idx++], "LOGNAME=%s", pw_name);
- xasprintf(&mda_environ[idx++], "USER=%s", pw_name);
+ xasprintf(&mda_environ[idx++], "LOGNAME=%s", deliver->userinfo.username);
+ xasprintf(&mda_environ[idx++], "USER=%s", deliver->userinfo.username);
 
  if (deliver->sender.user[0])
  xasprintf(&mda_environ[idx++], "SENDER=%s@%s",
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/parse.y,v
retrieving revision 1.277
diff -u -p -r1.277 parse.y
--- parse.y 24 Feb 2020 23:54:27 -0000 1.277
+++ parse.y 26 Apr 2020 05:27:35 -0000
@@ -154,6 +154,7 @@ static int host_v4(struct listen_opts *)
  static int host_v6(struct listen_opts *);
  static int host_dns(struct listen_opts *);
  static int interface(struct listen_opts *);
+static void need_priv(const char *);
 
  int delaytonum(char *);
  int is_if_in_group(const char *, const char *);
@@ -186,7 +187,7 @@ typedef struct {
  %token KEY
  %token LIMIT LISTEN LMTP LOCAL
  %token MAIL_FROM MAILDIR MASK_SRC MASQUERADE MATCH MAX_MESSAGE_SIZE MAX_DEFERRED MBOX MDA MTA MX
-%token NO_DSN NO_VERIFY NOOP
+%token NO_DSN NO_PRIV NO_VERIFY NOOP
  %token ON
  %token PHASE PKI PORT PROC PROC_EXEC PROXY_V2
  %token QUEUE QUIT
@@ -212,6 +213,7 @@ grammar : /* empty */
  | grammar ca '\n'
  | grammar mda '\n'
  | grammar mta '\n'
+ | grammar privs '\n'
  | grammar pki '\n'
  | grammar proc '\n'
  | grammar queue '\n'
@@ -379,6 +381,20 @@ MTA MAX_DEFERRED NUMBER  {
  ;
 
 
+privs:
+NO_PRIV {
+ if (conf->sc_opts & SMTPD_OPT_NEEDPRIV) {
+ yyerror("Unprivileged operation is not possible.");
+ YYERROR;
+ }
+ else {
+ log_warnx("Unprivileged operation requested.");
+ conf->sc_opts |= SMTPD_OPT_NOPRIV;
+ }
+}
+;
+
+
  pki:
  PKI STRING {
  char buf[HOST_NAME_MAX+1];
@@ -566,6 +582,8 @@ SRS KEY STRING {
 
  dispatcher_local_option:
  USER STRING {
+ need_priv("with user");
+
  if (dispatcher->u.local.is_mbox) {
  yyerror("user may not be specified for this dispatcher");
  YYERROR;
@@ -662,16 +680,20 @@ dispatcher_local_option dispatcher_local
 
  dispatcher_local:
  MBOX {
+ need_priv("mbox");
  dispatcher->u.local.is_mbox = 1;
  asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.local -f %%{mbox.from} -- %%{user.username}");
  } dispatcher_local_options
  | MAILDIR {
+ need_priv("maildir");
  asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.maildir");
  } dispatcher_local_options
  | MAILDIR JUNK {
+ need_priv("maildir");
  asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.maildir -j");
  } dispatcher_local_options
  | MAILDIR STRING {
+ need_priv("maildir");
  if (strncmp($2, "~/", 2) == 0)
  asprintf(&dispatcher->u.local.command,
     "/usr/libexec/mail.maildir \"%%{user.directory}/%s\"", $2+2);
@@ -680,6 +702,7 @@ MBOX {
     "/usr/libexec/mail.maildir \"%s\"", $2);
  } dispatcher_local_options
  | MAILDIR STRING JUNK {
+ need_priv("maildir");
  if (strncmp($2, "~/", 2) == 0)
  asprintf(&dispatcher->u.local.command,
     "/usr/libexec/mail.maildir -j \"%%{user.directory}/%s\"", $2+2);
@@ -690,12 +713,15 @@ MBOX {
  | LMTP STRING {
  asprintf(&dispatcher->u.local.command,
     "/usr/libexec/mail.lmtp -d %s -u", $2);
+ dispatcher->u.local.user = SMTPD_USER;
  } dispatcher_local_options
  | LMTP STRING RCPT_TO {
  asprintf(&dispatcher->u.local.command,
     "/usr/libexec/mail.lmtp -d %s -r", $2);
+ dispatcher->u.local.user = SMTPD_USER;
  } dispatcher_local_options
  | MDA STRING {
+ need_priv("mda");
  asprintf(&dispatcher->u.local.command,
     "/usr/libexec/mail.mda \"%s\"", $2);
  } dispatcher_local_options
@@ -703,6 +729,7 @@ MBOX {
  dispatcher->u.local.forward_only = 1;
  } dispatcher_local_options
  | EXPAND_ONLY {
+ need_priv("expand");
  dispatcher->u.local.expand_only = 1;
  } dispatcher_local_options
 
@@ -2656,6 +2683,7 @@ lookup(char *s)
  { "mta", MTA },
  { "mx", MX },
  { "no-dsn", NO_DSN },
+ { "no-priv", NO_PRIV },
  { "no-verify", NO_VERIFY },
  { "noop", NOOP },
  { "on", ON },
@@ -3469,6 +3497,18 @@ interface(struct listen_opts *lo)
  freeifaddrs(ifap);
 
  return ret;
+}
+
+static void
+need_priv(const char *method)
+{
+ if (conf->sc_opts & SMTPD_OPT_NOPRIV)
+ errx(1, "Delivery method %s needs privileges.", method);
+ else {
+ log_warnx("Privileges needed for method %s.",
+    method);
+ conf->sc_opts |= SMTPD_OPT_NEEDPRIV;
+ }
  }
 
  int
Index: smtpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/smtpd.c,v
retrieving revision 1.332
diff -u -p -r1.332 smtpd.c
--- smtpd.c 24 Feb 2020 16:16:08 -0000 1.332
+++ smtpd.c 26 Apr 2020 05:27:35 -0000
@@ -1036,11 +1036,14 @@ imsg_wait(struct imsgbuf *ibuf, struct i
 
  int
  smtpd(void) {
+ struct passwd *pw;
  struct event ev_sigint;
  struct event ev_sigterm;
  struct event ev_sigchld;
  struct event ev_sighup;
  struct timeval tv;
+ uid_t uid;
+ gid_t gid;
 
  imsg_callback = parent_imsg;
 
@@ -1085,6 +1088,17 @@ smtpd(void) {
 
  purge_task();
 
+ if (env->sc_opts & SMTPD_OPT_NOPRIV) {
+ if ((pw = getpwnam(SMTPD_USER)) == NULL)
+ fatalx("unknown user " SMTPD_USER);
+ uid = pw->pw_uid;
+ gid = pw->pw_gid;
+ if (setgroups(1, &gid) ||
+    setresgid(gid, gid, gid) ||
+    setresuid(uid, uid, uid))
+ fatal("smtpd: cannot drop privileges");
+ }
+
  if (pledge("stdio rpath wpath cpath fattr tmppath "
     "getpw sendfd proc exec id inet chown unix", NULL) == -1)
  err(1, "pledge");
@@ -1519,10 +1533,12 @@ forkmda(struct mproc *p, uint64_t id, st
 
  if (chdir(pw_dir) == -1 && chdir("/") == -1)
  err(1, "chdir");
- if (setgroups(1, &pw_gid) ||
-    setresgid(pw_gid, pw_gid, pw_gid) ||
-    setresuid(pw_uid, pw_uid, pw_uid))
- err(1, "forkmda: cannot drop privileges");
+ if (! (env->sc_opts & SMTPD_OPT_NOPRIV)) {
+ if (setgroups(1, &pw_gid) ||
+    setresgid(pw_gid, pw_gid, pw_gid) ||
+    setresuid(pw_uid, pw_uid, pw_uid))
+ err(1, "forkmda: cannot drop privileges");
+ }
  if (dup2(pipefd[0], STDIN_FILENO) == -1 ||
     dup2(allout, STDOUT_FILENO) == -1 ||
     dup2(allout, STDERR_FILENO) == -1)
Index: smtpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/smtpd.h,v
retrieving revision 1.656
diff -u -p -r1.656 smtpd.h
--- smtpd.h 8 Apr 2020 07:30:44 -0000 1.656
+++ smtpd.h 26 Apr 2020 05:27:35 -0000
@@ -550,6 +550,8 @@ struct smtpd {
 
  #define SMTPD_OPT_VERBOSE 0x00000001
  #define SMTPD_OPT_NOACTION 0x00000002
+#define SMTPD_OPT_NOPRIV 0x00000004
+#define SMTPD_OPT_NEEDPRIV 0x00000008
  uint32_t sc_opts;
 
  #define SMTPD_EXITING 0x00000001 /* unused */


--
http://gmerlin.de
OpenPGP: http://gmerlin.de/christopher.pub
CB07 DA40 B0B6 571D 35E2  0DEF 87E2 92A7 13E5 DEE1

Reply | Threaded
Open this post in threaded view
|

Re: OpenSMTPD: unprivileged mode - now with diff

Gilles Chehade-7
April 26, 2020 10:34 AM, "Christopher Zimmermann" <[hidden email]> wrote:

> Hi,
>
> I further developed my approach to allow running smtpd with fewer privileges. This diff does two
> things:
>
> - always run lmtp deliveries as SMTPD_USER. The change to mda_unpriv.c is needed, because otherwise
> all mails would be delivered to SMTPD_USER.
>
> - add two internal flags NOPRIV and NEEDPRIV. NOPRIV can be configured by the simple directive
> "no-priv". NEEDPRIV gets set on all delivery methods / options requiring setuid() to run as the
> receipient user.
> A configuration error is produced on any conflict betweed NEEDPRIV and NOPRIV.
> In case of a NOPRIV run smtpd will drop root privileges.
> This will break .forward and alias filters.
>
> The change to the lmtp delivery has benefits even without the second change. With the second change
> my smtpd now runs without root privileges.
> The NEEDPRIV/NOPRIV options are meant to allow restricting of the privileges of other delivery
> methods.
>
> I am now looking for OKs on the first change to do unprivileged lmtp deliveries and feedback on the
> general approach of the second change.
>

The LMTP change seems interesting to me, it means that a broken LMTP delivery
will fail with _smtpd privileges instead of the (unprivileged) recipient user
so I think it's a good move.

I'm less convinced by the other change and it doesn't only break .forward and
alias, it also breaks authentication, tables reloading, and probably stuff my
mind is not yet awake enough to think of.



> Index: mda_unpriv.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/smtpd/mda_unpriv.c,v
> retrieving revision 1.6
> diff -u -p -r1.6 mda_unpriv.c
> --- mda_unpriv.c 2 Feb 2020 22:13:48 -0000 1.6
> +++ mda_unpriv.c 26 Apr 2020 05:27:34 -0000
> @@ -69,8 +69,8 @@ mda_unpriv(struct dispatcher *dsp, struc
> xasprintf(&mda_environ[idx++], "RECIPIENT=%s@%s", deliver->dest.user, deliver->dest.domain);
> xasprintf(&mda_environ[idx++], "SHELL=/bin/sh");
> xasprintf(&mda_environ[idx++], "LOCAL=%s", deliver->rcpt.user);
> - xasprintf(&mda_environ[idx++], "LOGNAME=%s", pw_name);
> - xasprintf(&mda_environ[idx++], "USER=%s", pw_name);
> + xasprintf(&mda_environ[idx++], "LOGNAME=%s", deliver->userinfo.username);
> + xasprintf(&mda_environ[idx++], "USER=%s", deliver->userinfo.username);
> if (deliver->sender.user[0])
> xasprintf(&mda_environ[idx++], "SENDER=%s@%s",
> Index: parse.y
> ===================================================================
> RCS file: /cvs/src/usr.sbin/smtpd/parse.y,v
> retrieving revision 1.277
> diff -u -p -r1.277 parse.y
> --- parse.y 24 Feb 2020 23:54:27 -0000 1.277
> +++ parse.y 26 Apr 2020 05:27:35 -0000
> @@ -154,6 +154,7 @@ static int host_v4(struct listen_opts *)
> static int host_v6(struct listen_opts *);
> static int host_dns(struct listen_opts *);
> static int interface(struct listen_opts *);
> +static void need_priv(const char *);
> int delaytonum(char *);
> int is_if_in_group(const char *, const char *);
> @@ -186,7 +187,7 @@ typedef struct {
> %token KEY
> %token LIMIT LISTEN LMTP LOCAL
> %token MAIL_FROM MAILDIR MASK_SRC MASQUERADE MATCH MAX_MESSAGE_SIZE MAX_DEFERRED MBOX MDA MTA MX
> -%token NO_DSN NO_VERIFY NOOP
> +%token NO_DSN NO_PRIV NO_VERIFY NOOP
> %token ON
> %token PHASE PKI PORT PROC PROC_EXEC PROXY_V2
> %token QUEUE QUIT
> @@ -212,6 +213,7 @@ grammar : /* empty */
> | grammar ca '\n'
> | grammar mda '\n'
> | grammar mta '\n'
> + | grammar privs '\n'
> | grammar pki '\n'
> | grammar proc '\n'
> | grammar queue '\n'
> @@ -379,6 +381,20 @@ MTA MAX_DEFERRED NUMBER {
> ;
> +privs:
> +NO_PRIV {
> + if (conf->sc_opts & SMTPD_OPT_NEEDPRIV) {
> + yyerror("Unprivileged operation is not possible.");
> + YYERROR;
> + }
> + else {
> + log_warnx("Unprivileged operation requested.");
> + conf->sc_opts |= SMTPD_OPT_NOPRIV;
> + }
> +}
> +;
> +
> +
> pki:
> PKI STRING {
> char buf[HOST_NAME_MAX+1];
> @@ -566,6 +582,8 @@ SRS KEY STRING {
> dispatcher_local_option:
> USER STRING {
> + need_priv("with user");
> +
> if (dispatcher->u.local.is_mbox) {
> yyerror("user may not be specified for this dispatcher");
> YYERROR;
> @@ -662,16 +680,20 @@ dispatcher_local_option dispatcher_local
> dispatcher_local:
> MBOX {
> + need_priv("mbox");
> dispatcher->u.local.is_mbox = 1;
> asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.local -f %%{mbox.from} --
> %%{user.username}");
> } dispatcher_local_options
> | MAILDIR {
> + need_priv("maildir");
> asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.maildir");
> } dispatcher_local_options
> | MAILDIR JUNK {
> + need_priv("maildir");
> asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.maildir -j");
> } dispatcher_local_options
> | MAILDIR STRING {
> + need_priv("maildir");
> if (strncmp($2, "~/", 2) == 0)
> asprintf(&dispatcher->u.local.command,
> "/usr/libexec/mail.maildir \"%%{user.directory}/%s\"", $2+2);
> @@ -680,6 +702,7 @@ MBOX {
> "/usr/libexec/mail.maildir \"%s\"", $2);
> } dispatcher_local_options
> | MAILDIR STRING JUNK {
> + need_priv("maildir");
> if (strncmp($2, "~/", 2) == 0)
> asprintf(&dispatcher->u.local.command,
> "/usr/libexec/mail.maildir -j \"%%{user.directory}/%s\"", $2+2);
> @@ -690,12 +713,15 @@ MBOX {
> | LMTP STRING {
> asprintf(&dispatcher->u.local.command,
> "/usr/libexec/mail.lmtp -d %s -u", $2);
> + dispatcher->u.local.user = SMTPD_USER;
> } dispatcher_local_options
> | LMTP STRING RCPT_TO {
> asprintf(&dispatcher->u.local.command,
> "/usr/libexec/mail.lmtp -d %s -r", $2);
> + dispatcher->u.local.user = SMTPD_USER;
> } dispatcher_local_options
> | MDA STRING {
> + need_priv("mda");
> asprintf(&dispatcher->u.local.command,
> "/usr/libexec/mail.mda \"%s\"", $2);
> } dispatcher_local_options
> @@ -703,6 +729,7 @@ MBOX {
> dispatcher->u.local.forward_only = 1;
> } dispatcher_local_options
> | EXPAND_ONLY {
> + need_priv("expand");
> dispatcher->u.local.expand_only = 1;
> } dispatcher_local_options
> @@ -2656,6 +2683,7 @@ lookup(char *s)
> { "mta", MTA },
> { "mx", MX },
> { "no-dsn", NO_DSN },
> + { "no-priv", NO_PRIV },
> { "no-verify", NO_VERIFY },
> { "noop", NOOP },
> { "on", ON },
> @@ -3469,6 +3497,18 @@ interface(struct listen_opts *lo)
> freeifaddrs(ifap);
> return ret;
> +}
> +
> +static void
> +need_priv(const char *method)
> +{
> + if (conf->sc_opts & SMTPD_OPT_NOPRIV)
> + errx(1, "Delivery method %s needs privileges.", method);
> + else {
> + log_warnx("Privileges needed for method %s.",
> + method);
> + conf->sc_opts |= SMTPD_OPT_NEEDPRIV;
> + }
> }
> int
> Index: smtpd.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/smtpd/smtpd.c,v
> retrieving revision 1.332
> diff -u -p -r1.332 smtpd.c
> --- smtpd.c 24 Feb 2020 16:16:08 -0000 1.332
> +++ smtpd.c 26 Apr 2020 05:27:35 -0000
> @@ -1036,11 +1036,14 @@ imsg_wait(struct imsgbuf *ibuf, struct i
> int
> smtpd(void) {
> + struct passwd *pw;
> struct event ev_sigint;
> struct event ev_sigterm;
> struct event ev_sigchld;
> struct event ev_sighup;
> struct timeval tv;
> + uid_t uid;
> + gid_t gid;
> imsg_callback = parent_imsg;
> @@ -1085,6 +1088,17 @@ smtpd(void) {
> purge_task();
> + if (env->sc_opts & SMTPD_OPT_NOPRIV) {
> + if ((pw = getpwnam(SMTPD_USER)) == NULL)
> + fatalx("unknown user " SMTPD_USER);
> + uid = pw->pw_uid;
> + gid = pw->pw_gid;
> + if (setgroups(1, &gid) ||
> + setresgid(gid, gid, gid) ||
> + setresuid(uid, uid, uid))
> + fatal("smtpd: cannot drop privileges");
> + }
> +
> if (pledge("stdio rpath wpath cpath fattr tmppath "
> "getpw sendfd proc exec id inet chown unix", NULL) == -1)
> err(1, "pledge");
> @@ -1519,10 +1533,12 @@ forkmda(struct mproc *p, uint64_t id, st
> if (chdir(pw_dir) == -1 && chdir("/") == -1)
> err(1, "chdir");
> - if (setgroups(1, &pw_gid) ||
> - setresgid(pw_gid, pw_gid, pw_gid) ||
> - setresuid(pw_uid, pw_uid, pw_uid))
> - err(1, "forkmda: cannot drop privileges");
> + if (! (env->sc_opts & SMTPD_OPT_NOPRIV)) {
> + if (setgroups(1, &pw_gid) ||
> + setresgid(pw_gid, pw_gid, pw_gid) ||
> + setresuid(pw_uid, pw_uid, pw_uid))
> + err(1, "forkmda: cannot drop privileges");
> + }
> if (dup2(pipefd[0], STDIN_FILENO) == -1 ||
> dup2(allout, STDOUT_FILENO) == -1 ||
> dup2(allout, STDERR_FILENO) == -1)
> Index: smtpd.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/smtpd/smtpd.h,v
> retrieving revision 1.656
> diff -u -p -r1.656 smtpd.h
> --- smtpd.h 8 Apr 2020 07:30:44 -0000 1.656
> +++ smtpd.h 26 Apr 2020 05:27:35 -0000
> @@ -550,6 +550,8 @@ struct smtpd {
> #define SMTPD_OPT_VERBOSE 0x00000001
> #define SMTPD_OPT_NOACTION 0x00000002
> +#define SMTPD_OPT_NOPRIV 0x00000004
> +#define SMTPD_OPT_NEEDPRIV 0x00000008
> uint32_t sc_opts;
> #define SMTPD_EXITING 0x00000001 /* unused */
>
> -- http://gmerlin.de
> OpenPGP: http://gmerlin.de/christopher.pub
> CB07 DA40 B0B6 571D 35E2 0DEF 87E2 92A7 13E5 DEE1

Reply | Threaded
Open this post in threaded view
|

Re: OpenSMTPD: unprivileged mode - now with diff

Christopher Zimmermann-2
On Sun, Apr 26, 2020 at 08:55:14AM +0000, [hidden email] wrote:

>April 26, 2020 10:34 AM, "Christopher Zimmermann" <[hidden email]> wrote:
>
>> - always run lmtp deliveries as SMTPD_USER. The change to
>>   mda_unpriv.c is needed, because otherwise
>> all mails would be delivered to SMTPD_USER.
>>
>> - add two internal flags NOPRIV and NEEDPRIV. NOPRIV can be configured by the simple directive
>> "no-priv". NEEDPRIV gets set on all delivery methods / options requiring setuid() to run as the
>> receipient user.
>> A configuration error is produced on any conflict betweed NEEDPRIV and NOPRIV.
>> In case of a NOPRIV run smtpd will drop root privileges.
>> This will break .forward and alias filters.
>>

Hi, thanks for your fast reply.

>The LMTP change seems interesting to me, it means that a broken LMTP delivery
>will fail with _smtpd privileges instead of the (unprivileged) recipient user
>so I think it's a good move.

That's great. Is there anything that needs to change or be clarified
before you can give an ok?

>I'm less convinced by the other change and it doesn't only break .forward and
>alias, it also breaks authentication, tables reloading, and probably stuff my
>mind is not yet awake enough to think of.

Thanks for giving it a thought. I'm not entirely convinced either. But
believe some thought should be given to it.
In your opinion would it be generaly a bad idea to try run smtpd without
root privileges?
What exectly will cease to work?

- .forward and alias _filtering_ will break for sure.
   Forwarding won't break.

- Authentication: Authentication of system users will break. So I would
   mark auth without auth-table as need_priv() in parse.y.

- tables reloading: this is a problem only for tables with restricted
   read permissions, isn't it?
   I could try to figure out whether it is a problem in parse.y and mark
   it as need_priv() if necessary, but one could also just document the
   no-priv option with its limitations.

- other stuff: Can it be dealt with need_priv()? Will it lead to
   unpleasant surprises for users?

The general idea is to allow running smtpd without root priviliges where
possible and maybe try to change it to support running without root for
more use-cases over time. Like try to pick the low-hanging fruits first.

One less low-hanging fruits would be to integrate mail.lmtp(8) into
smtpd and move other mail.* functionality into a privileged lmtpd
daemon.
This would remove those vulnerable mda-exec lines from the envelope
files. That's just one idea how one could proceed. The current proposal
is to start stripping privileges where possible.


Christopher


 
-- http://gmerlin.de
OpenPGP: http://gmerlin.de/christopher.pub
CB07 DA40 B0B6 571D 35E2  0DEF 87E2 92A7 13E5 DEE1

Reply | Threaded
Open this post in threaded view
|

Re: OpenSMTPD: unprivileged mode - now with diff

Gregory Edigarov-5
Hello, Christopher

On the right of a person who had successfully run rootless sendmail
installations for many years,
please find some comments below.

On 2020-04-26 12:30, Christopher Zimmermann wrote:
> Thanks for giving it a thought. I'm not entirely convinced either. But
> believe some thought should be given to it.
> In your opinion would it be generaly a bad idea to try run smtpd
> without root privileges?
> What exectly will cease to work?
>
> - .forward and alias _filtering_ will break for sure.

not necessarily. at least as long as users will have smtpd and their
.forward and in the same group and you've documented it. also, .forward
and authentication could be handled by a separate daemon bound to unix
socket only, which won't listen to outside world. or even better,
separate all the functions, that usually need root access to such
daemon(s).

the only thing was broken for me in sendmail's case, was mbox
deliveries. but AFAIR, that was solved by having patched version of
mail.local.

--

With best regards,

       Gregory Edigarov


Reply | Threaded
Open this post in threaded view
|

Re: OpenSMTPD: unprivileged mode - now with diff

Christopher Zimmermann-2
In reply to this post by Gilles Chehade-7
On Sun, Apr 26, 2020 at 08:55:14AM +0000, [hidden email] wrote:

>April 26, 2020 10:34 AM, "Christopher Zimmermann" <[hidden email]> wrote:
>
>> Hi,
>>
>> I further developed my approach to allow running smtpd with fewer privileges. This diff does two
>> things:
>>
>> - always run lmtp deliveries as SMTPD_USER. The change to mda_unpriv.c is needed, because otherwise
>> all mails would be delivered to SMTPD_USER.
>>
>> - add two internal flags NOPRIV and NEEDPRIV. NOPRIV can be configured by the simple directive
>> "no-priv". NEEDPRIV gets set on all delivery methods / options requiring setuid() to run as the
>> receipient user.
>> A configuration error is produced on any conflict betweed NEEDPRIV and NOPRIV.
>> In case of a NOPRIV run smtpd will drop root privileges.
>> This will break .forward and alias filters.
>>
>> The change to the lmtp delivery has benefits even without the second change. With the second change
>> my smtpd now runs without root privileges.
>> The NEEDPRIV/NOPRIV options are meant to allow restricting of the privileges of other delivery
>> methods.
>>
>> I am now looking for OKs on the first change to do unprivileged lmtp deliveries and feedback on the
>> general approach of the second change.
>>
>
>The LMTP change seems interesting to me, it means that a broken LMTP delivery
>will fail with _smtpd privileges instead of the (unprivileged) recipient user
>so I think it's a good move.


Ok to commit the below change?

Christopher


Index: mda_unpriv.c
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/mda_unpriv.c,v
retrieving revision 1.6
diff -u -p -r1.6 mda_unpriv.c
--- mda_unpriv.c        2 Feb 2020 22:13:48 -0000       1.6
+++ mda_unpriv.c        23 May 2020 13:43:39 -0000
@@ -69,8 +69,8 @@ mda_unpriv(struct dispatcher *dsp, struc
         xasprintf(&mda_environ[idx++], "RECIPIENT=%s@%s", deliver->dest.user, deliver->dest.domain);
         xasprintf(&mda_environ[idx++], "SHELL=/bin/sh");
         xasprintf(&mda_environ[idx++], "LOCAL=%s", deliver->rcpt.user);
-       xasprintf(&mda_environ[idx++], "LOGNAME=%s", pw_name);
-       xasprintf(&mda_environ[idx++], "USER=%s", pw_name);
+       xasprintf(&mda_environ[idx++], "LOGNAME=%s", deliver->userinfo.username);
+       xasprintf(&mda_environ[idx++], "USER=%s", deliver->userinfo.username);

         if (deliver->sender.user[0])
                 xasprintf(&mda_environ[idx++], "SENDER=%s@%s",
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/parse.y,v
retrieving revision 1.277
diff -u -p -r1.277 parse.y
--- parse.y     24 Feb 2020 23:54:27 -0000      1.277
+++ parse.y     23 May 2020 13:43:40 -0000
@@ -690,10 +690,12 @@ MBOX {
  | LMTP STRING {
         asprintf(&dispatcher->u.local.command,
             "/usr/libexec/mail.lmtp -d %s -u", $2);
+       dispatcher->u.local.user = SMTPD_USER;
  } dispatcher_local_options
  | LMTP STRING RCPT_TO {
         asprintf(&dispatcher->u.local.command,
             "/usr/libexec/mail.lmtp -d %s -r", $2);
+       dispatcher->u.local.user = SMTPD_USER;
  } dispatcher_local_options
  | MDA STRING {
         asprintf(&dispatcher->u.local.command,



--
http://gmerlin.de
OpenPGP: http://gmerlin.de/christopher.pub
CB07 DA40 B0B6 571D 35E2  0DEF 87E2 92A7 13E5 DEE1

Reply | Threaded
Open this post in threaded view
|

Re: OpenSMTPD: unprivileged mode - now with diff

Todd C. Miller-3
On Sat, 23 May 2020 15:53:05 +0200, Christopher Zimmermann wrote:

> Ok to commit the below change?

OK millert@

 - todd