Re: [exim] mailman in a jail(8) (for people without FreeBSD …

Góra strony
Delete this message
Reply to this message
Autor: Phil Pennock
Data:  
Dla: Moritz Wilhelmy
CC: exim-users
Temat: Re: [exim] mailman in a jail(8) (for people without FreeBSD experience: think chroot(8))
On 2012-02-19 at 15:06 +0100, Moritz Wilhelmy wrote:
> I've installed mailman in a FreeBSD-jail(8) (much like chroot(8)-jails,
> but can't be left that easily via fchdir etc.), and now I have the
> problem that I can't reach mailman (version 2, version 3 would have
> LMTP, which would eliminate the entire problem). Exim, trying to be
> secure software is refusing to run the jail(8) command as root, which
> means I am having a hard time to pass things into the jail via a pipe
> transport.
>
> I see a few more or less ugly kludges around the problem:
>
> - make a setuid wrapper that runs mailman in the jail.
> - run another exim instance in the jail as some kind of LMTP or SMTP
> wrapper around mailman v2
>
> Does someone know a better way (e.g. a designated LMTP wrapper
> implementation)?


If you're using Jails for security, you might want to consider whether
or not the MTA should run inside a jail too.

As long as you're not doing so, try the patch (that's both below and
attached). I don't currently have any Jails setup, so can't actually
*test* it, but it does compile. *cough* On FreeBSD 7, and there may
have been incompatible changes in later releases.

This is relative to git head, but should apply back for a release or two
without issues, I think.

Please let me know how you get on, if you try it, so we can consider it
for inclusion in the next release (and you don't get stuck maintaining a
patch locally).

Regards,
-Phil

From aac11fa66a9f4a2b7417d328bd9b0d59666bd7c5 Mon Sep 17 00:00:00 2001
From: Phil Pennock <pdp@???>
Date: Mon, 20 Feb 2012 03:08:05 -0500
Subject: [PATCH] jail_identifier support for pipe transport

---
 src/OS/os.c-FreeBSD       |   82 +++++++++++++++++++++++++++++++++++++++++++++
 src/OS/os.h-FreeBSD       |    9 +++++
 src/src/transports/pipe.c |   53 +++++++++++++++++++++++++++--
 src/src/transports/pipe.h |    1 +
 4 files changed, 142 insertions(+), 3 deletions(-)
 create mode 100644 src/OS/os.c-FreeBSD


diff --git a/src/OS/os.c-FreeBSD b/src/OS/os.c-FreeBSD
new file mode 100644
index 0000000..3b9fa14
--- /dev/null
+++ b/src/OS/os.c-FreeBSD
@@ -0,0 +1,82 @@
+#ifndef COMPILE_UTILITY /* Utilities don't need special code */
+
+#ifdef HAVE_FREEBSD_JAIL
+
+#include <sys/sysctl.h>
+
+/* Given a string, search system jail list to find the jid for that string.
+
+Returns:
+ -1    unable to extract jail identifier
+ >= 0  jail identifier
+*/
+
+int
+parse_jail_identifier(uschar *identifier)
+{
+/* Logic borrowed from FreeBSD 7's src/usr.sbin/jls/jls.c */
+/* Later releases might permit matching on IPv6? */
+struct xprison *xp;
+struct in_addr in;
+size_t i, len;
+int jid;
+uschar *store_reset_point;
+
+if (!identifier)
+  return -1;
+
+if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1)
+  {
+  DEBUG(D_any)
+    debug_printf("PJI: sysctlbyname(): security.jail.list (1:len): %s\n", strerror(errno));
+  return -1;
+  }
+if (len < sizeof(*xp))
+  {
+  DEBUG(D_any)
+    debug_printf("PJI: length too short, aborting for safety [%ld]\n", (long) len);
+  return -1;
+  }
+
+store_reset_point = store_get(0);
+xp = (struct xprison *) store_get(len);
+if (sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1)
+  {
+  DEBUG(D_any)
+    debug_printf("PJI: sysctlbyname(): security.jail.list (2:fetch): %s\n", strerror(errno));
+  return -1;
+  }
+if (len < sizeof(*xp) || len % sizeof(*xp) ||
+    xp->pr_version != XPRISON_VERSION)
+  {
+    int i=0;
+    if (len >= sizeof(*xp))
+      i = xp->pr_version;
+    DEBUG(D_any)
+      debug_printf("PJI: kernel ABI changed from Exim, not mutually compatible.\n"
+        "  sizeof(struct xprison)=%ld  len=%ld  pr_version=%d  XPRISON_VERSION=%d\n",
+        (long) sizeof(*xp), (long) len, i, XPRISON_VERSION);
+    return -1;
+  }
+
+jid = -1;
+for (i = 0; i < len / sizeof(*xp); i++)
+  {
+  in.s_addr = ntohl(xp->pr_ip);
+  if (streqic(identifier, US xp->pr_host) ||
+      streqic(identifier, US xp->pr_path) ||
+      streqic(identifier, US inet_ntoa(in)))
+    {
+    jid = xp->pr_id;
+    break;
+    }
+  xp++;
+  }
+
+store_reset(store_reset_point);
+return jid;
+}
+#endif /* HAVE_FREEBSD_JAIL */
+
+/* vim: set ft=c : */
+#endif /* COMPILE_UTILITY */
diff --git a/src/OS/os.h-FreeBSD b/src/OS/os.h-FreeBSD
index c5ed042..9ed2080 100644
--- a/src/OS/os.h-FreeBSD
+++ b/src/OS/os.h-FreeBSD
@@ -8,6 +8,15 @@
 #define HAVE_SRANDOMDEV
 #define HAVE_ARC4RANDOM


+#ifndef COMPILE_UTILITY
+# if __FreeBSD__ >= 7
+# define HAVE_FREEBSD_JAIL
+# include <sys/param.h>
+# include <sys/jail.h>
+int parse_jail_identifier(unsigned char *);
+# endif
+#endif
+
typedef struct flock flock_t;

 /* End */
diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c
index 15714f3..e1000c9 100644
--- a/src/src/transports/pipe.c
+++ b/src/src/transports/pipe.c
@@ -43,6 +43,10 @@ optionlist pipe_transport_options[] = {
       (void *)offsetof(pipe_transport_options_block, freeze_signal) },
   { "ignore_status",     opt_bool,
       (void *)offsetof(pipe_transport_options_block, ignore_status) },
+  #ifdef HAVE_FREEBSD_JAIL
+  { "jail_identifier",   opt_stringptr,
+      (void *)offsetof(pipe_transport_options_block, jail_identifier) },
+  #endif
   { "log_defer_output",  opt_bool | opt_public,
       (void *)offsetof(transport_instance, log_defer_output) },
   { "log_fail_output",   opt_bool | opt_public,
@@ -106,6 +110,7 @@ pipe_transport_options_block pipe_transport_option_defaults = {
      mac_expanded_string(EX_CANTCREAT),
   NULL,           /* check_string */
   NULL,           /* escape_string */
+  NULL,           /* jail_identifier */
   022,            /* umask */
   20480,          /* max_output */
   60*60,          /* timeout */
@@ -123,7 +128,6 @@ pipe_transport_options_block pipe_transport_option_defaults = {
 };



-
 /*************************************************
 *              Setup entry point                 *
 *************************************************/
@@ -131,7 +135,8 @@ pipe_transport_options_block pipe_transport_option_defaults = {
 /* Called for each delivery in the privileged state, just before the uid/gid
 are changed and the main entry point is called. In a system that supports the
 login_cap facilities, this function is used to set the class resource limits
-for the user.  It may also re-enable coredumps.
+for the user.  It may also re-enable coredumps.  On FreeBSD, it might switch
+into a jail.


 Arguments:
   tblock     points to the transport instance
@@ -158,6 +163,49 @@ gid = gid;
 errmsg = errmsg;
 ob = ob;


+#ifdef HAVE_FREEBSD_JAIL
+if (ob->jail_identifier)
+  {
+  int jrc, jid = -1;
+  long jtmp;
+  uschar *jail_str, *jend;
+
+  jail_str = expand_string(ob->jail_identifier);
+  if (jail_str)
+    {
+    jtmp = Ustrtol(jail_str, &jend, 10);
+    if (jend == jail_str)
+      {
+      /* os.c provides this */
+      jid = parse_jail_identifier(jail_str);
+      DEBUG(D_transport)
+        debug_printf("jail setup: jail identifier \"%s\" -> %d%s\n",
+          jail_str, jid,
+          jid < 0 ? " (IGNORED)" : "");
+      }
+    else if (jtmp > INT_MAX || jtmp < 0)
+      {
+      log_write(0, LOG_MAIN,
+          "jail_identifier \"%s\" not integer >= 0", jail_str);
+      return DEFER;
+      }
+    else
+      jid = (int) jtmp;
+    }
+
+  if (jid > -1)
+    {
+    jrc = jail_attach(jid);
+    if (jrc < 0)
+      {
+      log_write(0, LOG_MAIN,
+          "delivery jail_attach(%d) failed: %s", jid, strerror(errno));
+      return DEFER;
+      }
+    }
+  }
+#endif
+
 #ifdef HAVE_SETCLASSRESOURCES
 if (ob->use_classresources)
   {
@@ -196,7 +244,6 @@ return OK;
 }



-
 /*************************************************
 *          Initialization entry point            *
 *************************************************/
diff --git a/src/src/transports/pipe.h b/src/src/transports/pipe.h
index 343628e..ebee5e8 100644
--- a/src/src/transports/pipe.h
+++ b/src/src/transports/pipe.h
@@ -17,6 +17,7 @@ typedef struct {
   uschar *temp_errors;
   uschar *check_string;
   uschar *escape_string;
+  uschar *jail_identifier;
   int   umask;
   int   max_output;
   int   timeout;
-- 
1.7.9


From aac11fa66a9f4a2b7417d328bd9b0d59666bd7c5 Mon Sep 17 00:00:00 2001
From: Phil Pennock <pdp@???>
Date: Mon, 20 Feb 2012 03:08:05 -0500
Subject: [PATCH] jail_identifier support for pipe transport

---
 src/OS/os.c-FreeBSD       |   82 +++++++++++++++++++++++++++++++++++++++++++++
 src/OS/os.h-FreeBSD       |    9 +++++
 src/src/transports/pipe.c |   53 +++++++++++++++++++++++++++--
 src/src/transports/pipe.h |    1 +
 4 files changed, 142 insertions(+), 3 deletions(-)
 create mode 100644 src/OS/os.c-FreeBSD


diff --git a/src/OS/os.c-FreeBSD b/src/OS/os.c-FreeBSD
new file mode 100644
index 0000000..3b9fa14
--- /dev/null
+++ b/src/OS/os.c-FreeBSD
@@ -0,0 +1,82 @@
+#ifndef COMPILE_UTILITY /* Utilities don't need special code */
+
+#ifdef HAVE_FREEBSD_JAIL
+
+#include <sys/sysctl.h>
+
+/* Given a string, search system jail list to find the jid for that string.
+
+Returns:
+ -1    unable to extract jail identifier
+ >= 0  jail identifier
+*/
+
+int
+parse_jail_identifier(uschar *identifier)
+{
+/* Logic borrowed from FreeBSD 7's src/usr.sbin/jls/jls.c */
+/* Later releases might permit matching on IPv6? */
+struct xprison *xp;
+struct in_addr in;
+size_t i, len;
+int jid;
+uschar *store_reset_point;
+
+if (!identifier)
+  return -1;
+
+if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1)
+  {
+  DEBUG(D_any)
+    debug_printf("PJI: sysctlbyname(): security.jail.list (1:len): %s\n", strerror(errno));
+  return -1;
+  }
+if (len < sizeof(*xp))
+  {
+  DEBUG(D_any)
+    debug_printf("PJI: length too short, aborting for safety [%ld]\n", (long) len);
+  return -1;
+  }
+
+store_reset_point = store_get(0);
+xp = (struct xprison *) store_get(len);
+if (sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1)
+  {
+  DEBUG(D_any)
+    debug_printf("PJI: sysctlbyname(): security.jail.list (2:fetch): %s\n", strerror(errno));
+  return -1;
+  }
+if (len < sizeof(*xp) || len % sizeof(*xp) ||
+    xp->pr_version != XPRISON_VERSION)
+  {
+    int i=0;
+    if (len >= sizeof(*xp))
+      i = xp->pr_version;
+    DEBUG(D_any)
+      debug_printf("PJI: kernel ABI changed from Exim, not mutually compatible.\n"
+        "  sizeof(struct xprison)=%ld  len=%ld  pr_version=%d  XPRISON_VERSION=%d\n",
+        (long) sizeof(*xp), (long) len, i, XPRISON_VERSION);
+    return -1;
+  }
+
+jid = -1;
+for (i = 0; i < len / sizeof(*xp); i++)
+  {
+  in.s_addr = ntohl(xp->pr_ip);
+  if (streqic(identifier, US xp->pr_host) ||
+      streqic(identifier, US xp->pr_path) ||
+      streqic(identifier, US inet_ntoa(in)))
+    {
+    jid = xp->pr_id;
+    break;
+    }
+  xp++;
+  }
+
+store_reset(store_reset_point);
+return jid;
+}
+#endif /* HAVE_FREEBSD_JAIL */
+
+/* vim: set ft=c : */
+#endif /* COMPILE_UTILITY */
diff --git a/src/OS/os.h-FreeBSD b/src/OS/os.h-FreeBSD
index c5ed042..9ed2080 100644
--- a/src/OS/os.h-FreeBSD
+++ b/src/OS/os.h-FreeBSD
@@ -8,6 +8,15 @@
 #define HAVE_SRANDOMDEV
 #define HAVE_ARC4RANDOM


+#ifndef COMPILE_UTILITY
+# if __FreeBSD__ >= 7
+# define HAVE_FREEBSD_JAIL
+# include <sys/param.h>
+# include <sys/jail.h>
+int parse_jail_identifier(unsigned char *);
+# endif
+#endif
+
typedef struct flock flock_t;

 /* End */
diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c
index 15714f3..e1000c9 100644
--- a/src/src/transports/pipe.c
+++ b/src/src/transports/pipe.c
@@ -43,6 +43,10 @@ optionlist pipe_transport_options[] = {
       (void *)offsetof(pipe_transport_options_block, freeze_signal) },
   { "ignore_status",     opt_bool,
       (void *)offsetof(pipe_transport_options_block, ignore_status) },
+  #ifdef HAVE_FREEBSD_JAIL
+  { "jail_identifier",   opt_stringptr,
+      (void *)offsetof(pipe_transport_options_block, jail_identifier) },
+  #endif
   { "log_defer_output",  opt_bool | opt_public,
       (void *)offsetof(transport_instance, log_defer_output) },
   { "log_fail_output",   opt_bool | opt_public,
@@ -106,6 +110,7 @@ pipe_transport_options_block pipe_transport_option_defaults = {
      mac_expanded_string(EX_CANTCREAT),
   NULL,           /* check_string */
   NULL,           /* escape_string */
+  NULL,           /* jail_identifier */
   022,            /* umask */
   20480,          /* max_output */
   60*60,          /* timeout */
@@ -123,7 +128,6 @@ pipe_transport_options_block pipe_transport_option_defaults = {
 };



-
 /*************************************************
 *              Setup entry point                 *
 *************************************************/
@@ -131,7 +135,8 @@ pipe_transport_options_block pipe_transport_option_defaults = {
 /* Called for each delivery in the privileged state, just before the uid/gid
 are changed and the main entry point is called. In a system that supports the
 login_cap facilities, this function is used to set the class resource limits
-for the user.  It may also re-enable coredumps.
+for the user.  It may also re-enable coredumps.  On FreeBSD, it might switch
+into a jail.


 Arguments:
   tblock     points to the transport instance
@@ -158,6 +163,49 @@ gid = gid;
 errmsg = errmsg;
 ob = ob;


+#ifdef HAVE_FREEBSD_JAIL
+if (ob->jail_identifier)
+  {
+  int jrc, jid = -1;
+  long jtmp;
+  uschar *jail_str, *jend;
+
+  jail_str = expand_string(ob->jail_identifier);
+  if (jail_str)
+    {
+    jtmp = Ustrtol(jail_str, &jend, 10);
+    if (jend == jail_str)
+      {
+      /* os.c provides this */
+      jid = parse_jail_identifier(jail_str);
+      DEBUG(D_transport)
+        debug_printf("jail setup: jail identifier \"%s\" -> %d%s\n",
+          jail_str, jid,
+          jid < 0 ? " (IGNORED)" : "");
+      }
+    else if (jtmp > INT_MAX || jtmp < 0)
+      {
+      log_write(0, LOG_MAIN,
+          "jail_identifier \"%s\" not integer >= 0", jail_str);
+      return DEFER;
+      }
+    else
+      jid = (int) jtmp;
+    }
+
+  if (jid > -1)
+    {
+    jrc = jail_attach(jid);
+    if (jrc < 0)
+      {
+      log_write(0, LOG_MAIN,
+          "delivery jail_attach(%d) failed: %s", jid, strerror(errno));
+      return DEFER;
+      }
+    }
+  }
+#endif
+
 #ifdef HAVE_SETCLASSRESOURCES
 if (ob->use_classresources)
   {
@@ -196,7 +244,6 @@ return OK;
 }



-
 /*************************************************
 *          Initialization entry point            *
 *************************************************/
diff --git a/src/src/transports/pipe.h b/src/src/transports/pipe.h
index 343628e..ebee5e8 100644
--- a/src/src/transports/pipe.h
+++ b/src/src/transports/pipe.h
@@ -17,6 +17,7 @@ typedef struct {
   uschar *temp_errors;
   uschar *check_string;
   uschar *escape_string;
+  uschar *jail_identifier;
   int   umask;
   int   max_output;
   int   timeout;
-- 
1.7.9