Skip to content

Commit

Permalink
apparmor: Add apparmor plugin
Browse files Browse the repository at this point in the history
It lets dovecot temporarily switch to a new apparmor
context for a user.
  • Loading branch information
cmouse authored and sirainen committed Aug 21, 2017
1 parent b67fe20 commit 3a67e54
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 0 deletions.
3 changes: 3 additions & 0 deletions configure.ac
Expand Up @@ -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
Expand Down Expand Up @@ -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])

Expand Down
24 changes: 24 additions & 0 deletions m4/want_apparmor.m4
@@ -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")
])
5 changes: 5 additions & 0 deletions src/plugins/Makefile.am
Expand Up @@ -14,6 +14,10 @@ if HAVE_LDAP
DICT_LDAP = dict-ldap
endif

if HAVE_APPARMOR
APPARMOR = apparmor
endif

SUBDIRS = \
acl \
imap-acl \
Expand Down Expand Up @@ -45,5 +49,6 @@ SUBDIRS = \
$(FTS_LUCENE) \
$(FTS_SOLR) \
$(DICT_LDAP) \
$(APPARMOR) \
fs-compress \
var-expand-crypt
14 changes: 14 additions & 0 deletions src/plugins/apparmor/Makefile.am
@@ -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
115 changes: 115 additions & 0 deletions src/plugins/apparmor/apparmor-plugin.c
@@ -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);
}

0 comments on commit 3a67e54

Please sign in to comment.