]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
apparmor: Add apparmor plugin
authorAki Tuomi <aki.tuomi@dovecot.fi>
Thu, 13 Jul 2017 07:02:26 +0000 (10:02 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Mon, 21 Aug 2017 09:29:03 +0000 (12:29 +0300)
It lets dovecot temporarily switch to a new apparmor
context for a user.

configure.ac
m4/want_apparmor.m4 [new file with mode: 0644]
src/plugins/Makefile.am
src/plugins/apparmor/Makefile.am [new file with mode: 0644]
src/plugins/apparmor/apparmor-plugin.c [new file with mode: 0644]

index ff431d170f83484e32402ee283d9daf34c1f4808..9ca67d35a013a9cd59f0e1e080aa4f7589fcb182 100644 (file)
@@ -2923,6 +2923,8 @@ if test "$want_icu" != "no"; then
 fi
 AM_CONDITIONAL(BUILD_LIBICU, test "$have_icu" = "yes")
 
+DOVECOT_WANT_APPARMOR
+
 if test $have_lucene = no; then
   not_fts="$not_fts lucene"
 fi
@@ -3086,6 +3088,7 @@ src/plugins/zlib/Makefile
 src/plugins/imap-zlib/Makefile
 src/plugins/mail-crypt/Makefile
 src/plugins/var-expand-crypt/Makefile
+src/plugins/apparmor/Makefile
 stamp.h
 dovecot-config.in])
 
diff --git a/m4/want_apparmor.m4 b/m4/want_apparmor.m4
new file mode 100644 (file)
index 0000000..6934ea7
--- /dev/null
@@ -0,0 +1,24 @@
+AC_DEFUN([DOVECOT_WANT_APPARMOR], [
+  want_apparmor=auto
+  AC_ARG_WITH([apparmor],
+     [AS_HELP_STRING([--with-apparmor], [enable apparmor plugin (default=auto)])],
+     [want_apparmor=$withval])
+
+  have_apparmor=no
+  if test $want_apparmor != no; then
+    AC_CHECK_HEADER([sys/apparmor.h], [
+      AC_CHECK_LIB([apparmor], [aa_change_hat], [
+        have_apparmor=yes
+        AC_SUBST([APPARMOR_LIBS], [-lapparmor])
+      ])
+    ])
+  fi
+
+  if test $want_apparmor = yes; then
+    if test $have_apparmor = no; then
+      AC_MSG_FAILURE([apparmor was not found])
+    fi
+  fi
+
+  AM_CONDITIONAL(HAVE_APPARMOR, test "$have_apparmor" = "yes")
+])
index 4565163f714646b483412e05c91b52da22161c44..c341f6ac0cabd659a4e0d9c0f74d50e28bb0f85b 100644 (file)
@@ -14,6 +14,10 @@ if HAVE_LDAP
 DICT_LDAP = dict-ldap
 endif
 
+if HAVE_APPARMOR
+APPARMOR = apparmor
+endif
+
 SUBDIRS = \
        acl \
        imap-acl \
@@ -45,5 +49,6 @@ SUBDIRS = \
        $(FTS_LUCENE) \
        $(FTS_SOLR) \
        $(DICT_LDAP) \
+       $(APPARMOR) \
        fs-compress \
        var-expand-crypt
diff --git a/src/plugins/apparmor/Makefile.am b/src/plugins/apparmor/Makefile.am
new file mode 100644 (file)
index 0000000..510053b
--- /dev/null
@@ -0,0 +1,14 @@
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/lib \
+       -I$(top_srcdir)/src/lib-mail \
+       -I$(top_srcdir)/src/lib-index \
+       -I$(top_srcdir)/src/lib-storage
+
+NOPLUGIN_LDFLAGS =
+lib01_apparmor_plugin_la_LDFLAGS = -module -avoid-version
+lib01_apparmor_plugin_la_LIBADD = $(APPARMOR_LIBS)
+lib01_apparmor_plugin_la_SOURCES = \
+       apparmor-plugin.c
+
+module_LTLIBRARIES = \
+       lib01_apparmor_plugin.la
diff --git a/src/plugins/apparmor/apparmor-plugin.c b/src/plugins/apparmor/apparmor-plugin.c
new file mode 100644 (file)
index 0000000..f42a58b
--- /dev/null
@@ -0,0 +1,115 @@
+/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "module-dir.h"
+#include "randgen.h"
+#include "mail-user.h"
+#include "mail-storage-private.h"
+#include "mail-storage-hooks.h"
+#include <sys/apparmor.h>
+
+#define APPARMOR_PLUGIN_SETTING_HAT_PREFIX "apparmor_hat"
+
+const char *apparmor_plugin_version = DOVECOT_ABI_VERSION;
+
+/* hooks into user creation and deinit, will try to use
+   hats provided by apparmor_hat, apparmor_hat1... etc */
+
+#define APPARMOR_USER_CONTEXT(obj) \
+       (struct apparmor_mail_user*)MODULE_CONTEXT(obj, apparmor_mail_user_module)
+
+static MODULE_CONTEXT_DEFINE_INIT(apparmor_mail_user_module,
+                                 &mail_user_module_register);
+
+struct apparmor_mail_user {
+       union mail_user_module_context module_ctx;
+       unsigned long token;
+};
+
+void apparmor_plugin_init(struct module*);
+void apparmor_plugin_deinit(void);
+
+static void apparmor_log_current_context(struct mail_user *user)
+{
+       char *con, *mode;
+       if (!user->mail_debug)
+               return;
+
+       if (aa_getcon(&con, &mode) < 0) {
+               i_debug("aa_getcon() failed: %m");
+       } else {
+               i_debug("apparmor: Current context=%s, mode=%s",
+                       con, mode);
+               free(con);
+       }
+}
+
+static void apparmor_mail_user_deinit(struct mail_user *user)
+{
+       struct apparmor_mail_user *auser = APPARMOR_USER_CONTEXT(user);
+
+       if (user == NULL)
+               return;
+
+       if (aa_change_hat(NULL, auser->token)<0)
+               i_fatal("aa_change_hat(NULL) failed: %m");
+
+       apparmor_log_current_context(user);
+}
+
+static void apparmor_mail_user_created(struct mail_user *user)
+{
+       struct mail_user_vfuncs *v = user->vlast;
+       struct apparmor_mail_user *auser;
+       ARRAY_TYPE(const_string) hats;
+       /* see if we can find any hats */
+       const char *hat =
+               mail_user_plugin_getenv(user, APPARMOR_PLUGIN_SETTING_HAT_PREFIX);
+       if (hat == NULL)
+               return;
+
+       t_array_init(&hats, 8);
+       array_append(&hats, &hat, 1);
+       for(unsigned int i = 2;; i++) {
+               hat = mail_user_plugin_getenv(user, t_strdup_printf("%s%u",
+                               APPARMOR_PLUGIN_SETTING_HAT_PREFIX, i));
+               if (hat == NULL) break;
+               array_append(&hats, &hat, 1);
+       }
+       array_append_zero(&hats);
+
+       /* we got hat(s) to try */
+       auser = p_new(user->pool, struct apparmor_mail_user, 1);
+       auser->module_ctx.super = *v;
+       user->vlast = &auser->module_ctx.super;
+       v->deinit = apparmor_mail_user_deinit;
+       MODULE_CONTEXT_SET(user, apparmor_mail_user_module, auser);
+
+       /* generate a magic token */
+       random_fill(&auser->token, sizeof(auser->token));
+
+       /* try change hat */
+       if (aa_change_hatv(array_idx_modifiable(&hats, 0), auser->token) < 0) {
+               i_fatal("aa_change_hatv(%s) failed: %m",
+                       t_array_const_string_join(&hats, ","));
+       }
+
+       apparmor_log_current_context(user);
+}
+
+static const struct mail_storage_hooks apparmor_hooks = {
+       .mail_user_created = apparmor_mail_user_created
+};
+
+void apparmor_plugin_init(struct module *module)
+{
+       random_init();
+       mail_storage_hooks_add(module, &apparmor_hooks);
+}
+
+void apparmor_plugin_deinit(void)
+{
+       random_deinit();
+       mail_storage_hooks_remove(&apparmor_hooks);
+}