#define ERRSTR_INVALID_USER_SETTINGS \
"Invalid user settings. Refer to server log for more information."
+struct mail_storage_service_privileges {
+ uid_t uid;
+ gid_t gid;
+ const char *uid_source, *gid_source;
+
+ const char *home;
+ const char *chroot;
+};
+
struct mail_storage_service_ctx {
pool_t pool;
struct master_service *service;
}
}
+static const struct var_expand_table *
+get_var_expand_table(struct master_service *service,
+ struct mail_storage_service_input *input,
+ struct mail_storage_service_privileges *priv)
+{
+ static struct var_expand_table static_tab[] = {
+ { 'u', NULL, "user" },
+ { 'n', NULL, "username" },
+ { 'd', NULL, "domain" },
+ { 's', NULL, "service" },
+ { 'l', NULL, "lip" },
+ { 'r', NULL, "rip" },
+ { 'p', NULL, "pid" },
+ { 'i', NULL, "uid" },
+ { '\0', NULL, "gid" },
+ { '\0', NULL, NULL }
+ };
+ struct var_expand_table *tab;
+
+ tab = t_malloc(sizeof(static_tab));
+ memcpy(tab, static_tab, sizeof(static_tab));
+
+ tab[0].value = input->username;
+ tab[1].value = t_strcut(input->username, '@');
+ tab[2].value = strchr(input->username, '@');
+ if (tab[2].value != NULL) tab[2].value++;
+ tab[3].value = service->name;
+ tab[4].value = net_ip2addr(&input->local_ip);
+ tab[5].value = net_ip2addr(&input->remote_ip);
+ tab[6].value = my_pid;
+ tab[7].value = dec2str(priv->uid == (uid_t)-1 ? geteuid() : priv->uid);
+ tab[8].value = dec2str(priv->gid == (gid_t)-1 ? getegid() : priv->gid);
+ return tab;
+}
+
+static const char *
+user_expand_varstr(struct master_service *service,
+ struct mail_storage_service_input *input,
+ struct mail_storage_service_privileges *priv,
+ const char *str)
+{
+ string_t *ret;
+
+ if (*str == SETTING_STRVAR_EXPANDED[0])
+ return str + 1;
+
+ i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]);
+
+ ret = t_str_new(256);
+ var_expand(ret, str + 1, get_var_expand_table(service, input, priv));
+ return str_c(ret);
+}
+
static int
-service_drop_privileges(struct mail_storage_service_user *user,
- const struct mail_user_settings *set,
- const char *home, const char *chroot,
- bool disallow_root, bool keep_setuid_root,
- bool setenv_only, const char **error_r)
+service_parse_privileges(struct mail_storage_service_ctx *ctx,
+ struct mail_storage_service_user *user,
+ struct mail_storage_service_privileges *priv_r,
+ const char **error_r)
{
- struct restrict_access_settings rset;
- uid_t current_euid, setuid_uid = 0;
- const char *cur_chroot, *error;
+ const struct mail_user_settings *set = user->user_set;
+ uid_t uid = (uid_t)-1;
+ gid_t gid = (gid_t)-1;
- current_euid = geteuid();
- restrict_access_init(&rset);
- restrict_access_get_env(&rset);
+ memset(priv_r, 0, sizeof(*priv_r));
if (*set->mail_uid != '\0') {
- if (!parse_uid(set->mail_uid, &rset.uid, &error)) {
- *error_r = t_strdup_printf("%s (from %s)", error,
+ if (!parse_uid(set->mail_uid, &uid, error_r)) {
+ *error_r = t_strdup_printf("%s (from %s)", *error_r,
user->uid_source);
return -1;
}
- if (rset.uid < (uid_t)set->first_valid_uid ||
+ if (uid < (uid_t)set->first_valid_uid ||
(set->last_valid_uid != 0 &&
- rset.uid > (uid_t)set->last_valid_uid)) {
+ uid > (uid_t)set->last_valid_uid)) {
*error_r = t_strdup_printf(
"Mail access for users with UID %s not permitted "
"(see first_valid_uid in config file, uid from %s).",
- dec2str(rset.uid), user->uid_source);
+ dec2str(uid), user->uid_source);
return -1;
}
- rset.uid_source = user->uid_source;
- } else if (rset.uid == (uid_t)-1 &&
- disallow_root && current_euid == 0) {
- *error_r = "User is missing UID (see mail_uid setting)";
- return -1;
}
+ priv_r->uid = uid;
+ priv_r->uid_source = user->uid_source;
+
if (*set->mail_gid != '\0') {
- if (!parse_gid(set->mail_gid, &rset.gid, &error)) {
- *error_r = t_strdup_printf("%s (from %s)", error,
+ if (!parse_gid(set->mail_gid, &gid, error_r)) {
+ *error_r = t_strdup_printf("%s (from %s)", *error_r,
user->gid_source);
return -1;
}
- if (rset.gid < (gid_t)set->first_valid_gid ||
+ if (gid < (gid_t)set->first_valid_gid ||
(set->last_valid_gid != 0 &&
- rset.gid > (gid_t)set->last_valid_gid)) {
+ gid > (gid_t)set->last_valid_gid)) {
*error_r = t_strdup_printf(
"Mail access for users with GID %s not permitted "
"(see first_valid_gid in config file, gid from %s).",
- dec2str(rset.gid), user->gid_source);
+ dec2str(gid), user->gid_source);
return -1;
}
- rset.gid_source = user->gid_source;
+ }
+ priv_r->gid = gid;
+ priv_r->gid_source = user->gid_source;
+
+ /* variable strings are expanded in mail_user_init(),
+ but we need the home and chroot sooner so do them separately here. */
+ priv_r->home = user_expand_varstr(ctx->service, &user->input, priv_r,
+ user->user_set->mail_home);
+ priv_r->chroot = user_expand_varstr(ctx->service, &user->input, priv_r,
+ user->user_set->mail_chroot);
+ return 0;
+}
+
+static int
+service_drop_privileges(struct mail_storage_service_user *user,
+ struct mail_storage_service_privileges *priv,
+ bool disallow_root, bool keep_setuid_root,
+ bool setenv_only, const char **error_r)
+{
+ const struct mail_user_settings *set = user->user_set;
+ struct restrict_access_settings rset;
+ uid_t current_euid, setuid_uid = 0;
+ const char *cur_chroot, *error;
+
+ current_euid = geteuid();
+ restrict_access_init(&rset);
+ restrict_access_get_env(&rset);
+ if (priv->uid != (uid_t)-1) {
+ rset.uid = priv->uid;
+ rset.uid_source = priv->uid_source;
+ } else if (rset.uid == (uid_t)-1 &&
+ disallow_root && current_euid == 0) {
+ *error_r = "User is missing UID (see mail_uid setting)";
+ return -1;
+ }
+ if (priv->gid != (gid_t)-1) {
+ rset.gid = priv->gid;
+ rset.gid_source = priv->gid_source;
} else if (rset.gid == (gid_t)-1 && disallow_root &&
set->first_valid_gid > 0 && getegid() == 0) {
*error_r = "User is missing GID (see mail_gid setting)";
rset.first_valid_gid = set->first_valid_gid;
rset.last_valid_gid = set->last_valid_gid;
- rset.chroot_dir = *chroot == '\0' ? NULL : chroot;
+ rset.chroot_dir = *priv->chroot == '\0' ? NULL : priv->chroot;
rset.system_groups_user = user->system_groups_user;
cur_chroot = restrict_access_get_current_chroot();
if (strcmp(rset.chroot_dir, cur_chroot) != 0) {
*error_r = t_strdup_printf(
"Process is already chrooted to %s, "
- "can't chroot to %s", cur_chroot, chroot);
+ "can't chroot to %s", cur_chroot, priv->chroot);
return -1;
}
/* chrooting to same directory where we're already chrooted */
disallow_root = FALSE;
}
if (!setenv_only) {
- restrict_access(&rset, *home == '\0' ? NULL : home,
+ restrict_access(&rset, *priv->home == '\0' ? NULL : priv->home,
disallow_root);
} else {
restrict_access_set_env(&rset);
static int
mail_storage_service_init_post(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
- const char *home, struct mail_user **mail_user_r,
+ struct mail_storage_service_privileges *priv,
+ struct mail_user **mail_user_r,
const char **error_r)
{
const struct mail_storage_settings *mail_set;
+ const char *home = priv->home;
struct mail_user *mail_user;
mail_user = mail_user_alloc(user->input.username, user->user_info,
user->user_set);
mail_user_set_home(mail_user, *home == '\0' ? NULL : home);
- mail_user_set_vars(mail_user, geteuid(), getegid(), ctx->service->name,
+ mail_user_set_vars(mail_user, ctx->service->name,
&user->input.local_ip, &user->input.remote_ip);
+ mail_user->uid = priv->uid == (uid_t)-1 ? geteuid() : priv->uid;
+ mail_user->gid = priv->gid == (gid_t)-1 ? getegid() : priv->gid;
mail_set = mail_user_set_get_storage_set(mail_user);
return 0;
}
-static const struct var_expand_table *
-get_var_expand_table(struct master_service *service,
- struct mail_storage_service_input *input)
-{
- static struct var_expand_table static_tab[] = {
- { 'u', NULL, "user" },
- { 'n', NULL, "username" },
- { 'd', NULL, "domain" },
- { 's', NULL, "service" },
- { 'l', NULL, "lip" },
- { 'r', NULL, "rip" },
- { 'p', NULL, "pid" },
- { 'i', NULL, "uid" },
- { '\0', NULL, NULL }
- };
- struct var_expand_table *tab;
-
- tab = t_malloc(sizeof(static_tab));
- memcpy(tab, static_tab, sizeof(static_tab));
-
- tab[0].value = input->username;
- tab[1].value = t_strcut(input->username, '@');
- tab[2].value = strchr(input->username, '@');
- if (tab[2].value != NULL) tab[2].value++;
- tab[3].value = service->name;
- tab[4].value = net_ip2addr(&input->local_ip);
- tab[5].value = net_ip2addr(&input->remote_ip);
- tab[6].value = my_pid;
- tab[7].value = dec2str(geteuid());
- return tab;
-}
-
-static const char *
-user_expand_varstr(struct master_service *service,
- struct mail_storage_service_input *input, const char *str)
-{
- string_t *ret;
-
- if (*str == SETTING_STRVAR_EXPANDED[0])
- return str + 1;
-
- i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]);
-
- ret = t_str_new(256);
- var_expand(ret, str + 1, get_var_expand_table(service, input));
- return str_c(ret);
-}
-
static void mail_storage_service_io_activate(void *context)
{
struct mail_storage_service_user *user = context;
static void
mail_storage_service_init_log(struct mail_storage_service_ctx *ctx,
- struct mail_storage_service_user *user)
+ struct mail_storage_service_user *user,
+ struct mail_storage_service_privileges *priv)
{
ctx->log_initialized = TRUE;
T_BEGIN {
str = t_str_new(256);
var_expand(str, user->user_set->mail_log_prefix,
- get_var_expand_table(ctx->service, &user->input));
+ get_var_expand_table(ctx->service, &user->input, priv));
user->log_prefix = p_strdup(user->pool, str_c(str));
} T_END;
struct mail_storage_service_user *user,
struct mail_user **mail_user_r)
{
- const struct mail_user_settings *user_set = user->user_set;
- const char *home, *chroot, *error;
+ struct mail_storage_service_privileges priv;
+ const char *error;
unsigned int len;
bool disallow_root =
(user->flags & MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT) != 0;
(user->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0;
bool use_chroot;
- /* variable strings are expanded in mail_user_init(),
- but we need the home and chroot sooner so do them separately here. */
- home = user_expand_varstr(ctx->service, &user->input,
- user_set->mail_home);
- chroot = user_expand_varstr(ctx->service, &user->input,
- user_set->mail_chroot);
+ if (service_parse_privileges(ctx, user, &priv, &error) < 0) {
+ i_error("user %s: %s", user->input.username, error);
+ return -2;
+ }
- if (*home != '/' && *home != '\0') {
+ if (*priv.home != '/' && *priv.home != '\0') {
i_error("user %s: "
"Relative home directory paths not supported: %s",
- user->input.username, home);
+ user->input.username, priv.home);
return -2;
}
use_chroot = !temp_priv_drop ||
restrict_access_get_current_chroot() != NULL;
- len = strlen(chroot);
- if (len > 2 && strcmp(chroot + len - 2, "/.") == 0 &&
- strncmp(home, chroot, len - 2) == 0) {
+ len = strlen(priv.chroot);
+ if (len > 2 && strcmp(priv.chroot + len - 2, "/.") == 0 &&
+ strncmp(priv.home, priv.chroot, len - 2) == 0) {
/* mail_chroot = /chroot/. means that the home dir already
contains the chroot dir. remove it from home. */
if (use_chroot) {
- home += len - 2;
- if (*home == '\0')
- home = "/";
- chroot = t_strndup(chroot, len - 2);
+ priv.home += len - 2;
+ if (*priv.home == '\0')
+ priv.home = "/";
+ priv.chroot = t_strndup(priv.chroot, len - 2);
- set_keyval(ctx, user, "mail_home", home);
- set_keyval(ctx, user, "mail_chroot", chroot);
+ set_keyval(ctx, user, "mail_home", priv.home);
+ set_keyval(ctx, user, "mail_chroot", priv.chroot);
}
} else if (len > 0 && !use_chroot) {
/* we're not going to chroot. fix home directory so we can
access it. */
- if (*home == '\0' || strcmp(home, "/") == 0)
- home = chroot;
+ if (*priv.home == '\0' || strcmp(priv.home, "/") == 0)
+ priv.home = priv.chroot;
else
- home = t_strconcat(chroot, home, NULL);
- chroot = "";
- set_keyval(ctx, user, "mail_home", home);
+ priv.home = t_strconcat(priv.chroot, priv.home, NULL);
+ priv.chroot = "";
+ set_keyval(ctx, user, "mail_home", priv.home);
}
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0)
- mail_storage_service_init_log(ctx, user);
+ mail_storage_service_init_log(ctx, user, &priv);
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) == 0) {
- if (service_drop_privileges(user, user_set, home, chroot,
+ if (service_drop_privileges(user, &priv,
disallow_root, temp_priv_drop,
FALSE, &error) < 0) {
i_error("user %s: Couldn't drop privileges: %s",
initialized yet. */
module_dir_init(mail_storage_service_modules);
- if (mail_storage_service_init_post(ctx, user, home,
+ if (mail_storage_service_init_post(ctx, user, &priv,
mail_user_r, &error) < 0) {
i_error("user %s: Initialization failed: %s",
user->input.username, error);
void mail_storage_service_restrict_setenv(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user)
{
- const struct mail_user_settings *user_set = user->user_set;
- const char *home, *chroot, *error;
-
- home = user_expand_varstr(ctx->service, &user->input,
- user_set->mail_home);
- chroot = user_expand_varstr(ctx->service, &user->input,
- user_set->mail_chroot);
+ struct mail_storage_service_privileges priv;
+ const char *error;
- if (service_drop_privileges(user, user_set, home, chroot,
- FALSE, FALSE, TRUE,
- &error) < 0)
- i_fatal("%s", error);
+ if (service_parse_privileges(ctx, user, &priv, &error) < 0)
+ i_fatal("user %s: %s", user->input.username, error);
+ if (service_drop_privileges(user, &priv,
+ FALSE, FALSE, TRUE, &error) < 0)
+ i_fatal("user %s: %s", user->input.username, error);
}
int mail_storage_service_lookup_next(struct mail_storage_service_ctx *ctx,