]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: Handle %{uid} and %{gid} expansion without relying on process's euid...
authorTimo Sirainen <tss@iki.fi>
Mon, 12 Dec 2011 04:36:31 +0000 (06:36 +0200)
committerTimo Sirainen <tss@iki.fi>
Mon, 12 Dec 2011 04:36:31 +0000 (06:36 +0200)
src/lib-storage/mail-storage-service.c
src/lib-storage/mail-user.c
src/lib-storage/mail-user.h

index bb894378385b000532564130c2964f74dfef2815..059c41691895ffdf696d573460b896560bf9b2da 100644 (file)
 #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;
@@ -348,57 +357,142 @@ static bool parse_gid(const char *str, gid_t *gid_r, const char **error_r)
        }
 }
 
+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)";
@@ -419,7 +513,7 @@ service_drop_privileges(struct mail_storage_service_user *user,
 
        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();
@@ -433,7 +527,7 @@ service_drop_privileges(struct mail_storage_service_user *user,
                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 */
@@ -460,7 +554,7 @@ service_drop_privileges(struct mail_storage_service_user *user,
                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);
@@ -475,17 +569,21 @@ service_drop_privileges(struct mail_storage_service_user *user,
 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);
 
@@ -528,54 +626,6 @@ mail_storage_service_init_post(struct mail_storage_service_ctx *ctx,
        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;
@@ -592,7 +642,8 @@ static void mail_storage_service_io_deactivate(void *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 {
@@ -600,7 +651,7 @@ mail_storage_service_init_log(struct mail_storage_service_ctx *ctx,
 
                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;
 
@@ -950,8 +1001,8 @@ int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
                              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;
@@ -959,17 +1010,15 @@ int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
                (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;
        }
 
@@ -980,36 +1029,36 @@ int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
        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",
@@ -1025,7 +1074,7 @@ int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
           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);
@@ -1037,18 +1086,14 @@ int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
 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,
index 4ea4778aa2f1ab7fe965a4f447f4900d514cf20d..5fb740f99141ffcbacd01475488a092d597f5515 100644 (file)
@@ -159,12 +159,10 @@ struct mail_user *mail_user_find(struct mail_user *user, const char *name)
        return NULL;
 }
 
-void mail_user_set_vars(struct mail_user *user, uid_t uid, gid_t gid,
-                       const char *service, const struct ip_addr *local_ip,
+void mail_user_set_vars(struct mail_user *user, const char *service,
+                       const struct ip_addr *local_ip,
                        const struct ip_addr *remote_ip)
 {
-       user->uid = uid;
-       user->gid = gid;
        user->service = p_strdup(user->pool, service);
        if (local_ip != NULL && local_ip->family != 0) {
                user->local_ip = p_new(user->pool, struct ip_addr, 1);
index c626c8b71bb29ceda521a704b60f2d75c8425025..b4c4a0796c4554f4e1ccacb3e1906fdbe3c30735 100644 (file)
@@ -78,8 +78,8 @@ void mail_user_unref(struct mail_user **user);
 struct mail_user *mail_user_find(struct mail_user *user, const char *name);
 
 /* Specify mail location %variable expansion data. */
-void mail_user_set_vars(struct mail_user *user, uid_t uid, gid_t gid,
-                       const char *service, const struct ip_addr *local_ip,
+void mail_user_set_vars(struct mail_user *user, const char *service,
+                       const struct ip_addr *local_ip,
                        const struct ip_addr *remote_ip);
 /* Return %variable expansion table for the user. */
 const struct var_expand_table *