mail-process.c revision 90f993b4ba4c13d1e73ee7f312bd188b85604410
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (C) 2002 Timo Sirainen */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "common.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "fd-close-on-exec.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "env-util.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "str.h"
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen#include "network.h"
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen#include "restrict-access.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "restrict-process-size.h"
86bea1f8bffc2d98196f8655eecea9174c4f458aTimo Sirainen#include "var-expand.h"
86bea1f8bffc2d98196f8655eecea9174c4f458aTimo Sirainen#include "mail-process.h"
86bea1f8bffc2d98196f8655eecea9174c4f458aTimo Sirainen
86bea1f8bffc2d98196f8655eecea9174c4f458aTimo Sirainen#include <stdlib.h>
86bea1f8bffc2d98196f8655eecea9174c4f458aTimo Sirainen#include <unistd.h>
86bea1f8bffc2d98196f8655eecea9174c4f458aTimo Sirainen#include <grp.h>
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include <syslog.h>
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include <sys/stat.h>
b82474d60c15409eda71c55971710fd3b12b8a0fTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenstatic unsigned int mail_process_count = 0;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainenstatic int validate_uid_gid(uid_t uid, gid_t gid)
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen{
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainen if (uid == 0) {
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen i_error("mail process isn't allowed for root");
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen return FALSE;
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen }
484e12acec34f16e5a8adc001e23ae48f1dda8c7Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen if (uid < (uid_t)set->first_valid_uid ||
22535a9e685e29214082878e37a267157044618eTimo Sirainen (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) {
b321df9603081896b70ec44635af96d674a9839aTimo Sirainen i_error("mail process isn't allowed to use UID %s "
b321df9603081896b70ec44635af96d674a9839aTimo Sirainen "(modify first_valid_uid in config file)",
b321df9603081896b70ec44635af96d674a9839aTimo Sirainen dec2str(uid));
849969f639a00eab26791db3cb1b66430420c0cdTimo Sirainen return FALSE;
f3d506e525a720f214020ca0f989a1966b30edaeTimo Sirainen }
b321df9603081896b70ec44635af96d674a9839aTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (gid < (gid_t)set->first_valid_gid ||
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen (set->last_valid_gid != 0 && gid > (gid_t)set->last_valid_gid)) {
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen i_error("mail process isn't allowed to use primary group ID %s "
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen "with UID %s (see first_valid_gid in config file).",
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen dec2str(gid), dec2str(uid));
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen return FALSE;
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen }
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen return TRUE;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen}
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainenstatic int validate_chroot(const char *dir)
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen{
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen const char *const *chroot_dirs;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (*dir == '\0')
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen return FALSE;
f3d506e525a720f214020ca0f989a1966b30edaeTimo Sirainen
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen if (set->valid_chroot_dirs == NULL)
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen return FALSE;
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen chroot_dirs = t_strsplit(set->valid_chroot_dirs, ":");
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen while (*chroot_dirs != NULL) {
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen if (**chroot_dirs != '\0' &&
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen return TRUE;
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen chroot_dirs++;
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen }
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen return FALSE;
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen}
e48d89622047bd8bbd0475b881ca9377d592f535Timo Sirainen
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainenstatic const char *expand_mail_env(const char *env, const char *user,
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen const char *home)
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen{
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen string_t *str;
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen const char *p;
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen str = t_str_new(256);
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen /* it's either type:data or just data */
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen p = strchr(env, ':');
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen if (p != NULL) {
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen while (env != p) {
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen str_append_c(str, *env);
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen env++;
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen }
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen str_append_c(str, *env++);
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen }
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (env[0] == '~' && env[1] == '/') {
e48d89622047bd8bbd0475b881ca9377d592f535Timo Sirainen /* expand home */
e48d89622047bd8bbd0475b881ca9377d592f535Timo Sirainen str_append(str, home);
f3d506e525a720f214020ca0f989a1966b30edaeTimo Sirainen env++;
849969f639a00eab26791db3cb1b66430420c0cdTimo Sirainen }
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
43a66a0b16299bd4f7615acd85e98bd3832c54d5Timo Sirainen /* expand %vars */
43a66a0b16299bd4f7615acd85e98bd3832c54d5Timo Sirainen var_expand(str, env, user, home);
43a66a0b16299bd4f7615acd85e98bd3832c54d5Timo Sirainen return str_c(str);
43a66a0b16299bd4f7615acd85e98bd3832c54d5Timo Sirainen}
43a66a0b16299bd4f7615acd85e98bd3832c54d5Timo Sirainen
43a66a0b16299bd4f7615acd85e98bd3832c54d5Timo Sirainenint create_mail_process(int socket, struct ip_addr *ip,
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen const char *executable, const char *module_dir,
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen unsigned int process_size, int process_type,
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen struct auth_master_reply *reply, const char *data)
{
static const char *argv[] = { NULL, NULL, NULL };
const char *addr, *mail, *chroot_dir, *home_dir, *full_home_dir;
char title[1024];
pid_t pid;
int i, err;
if (mail_process_count == set->max_mail_processes) {
i_error("Maximum number of mail processes exceeded");
return FALSE;
}
if (!validate_uid_gid(reply->uid, reply->gid))
return FALSE;
home_dir = data + reply->home_idx;
chroot_dir = data + reply->chroot_idx;
if (*chroot_dir != '\0' && !validate_chroot(chroot_dir)) {
i_error("Invalid chroot directory: %s", chroot_dir);
return FALSE;
}
pid = fork();
if (pid < 0) {
i_error("fork() failed: %m");
return FALSE;
}
if (pid != 0) {
/* master */
mail_process_count++;
PID_ADD_PROCESS_TYPE(pid, process_type);
return TRUE;
}
child_process_init_env();
/* move the client socket into stdin and stdout fds */
fd_close_on_exec(socket, FALSE);
if (dup2(socket, 0) < 0)
i_fatal("mail: dup2(stdin) failed: %m");
if (dup2(socket, 1) < 0)
i_fatal("mail: dup2(stdout) failed: %m");
if (close(socket) < 0)
i_error("mail: close(mail client) failed: %m");
/* setup environment - set the most important environment first
(paranoia about filling up environment without noticing) */
restrict_access_set_env(data + reply->system_user_idx,
reply->uid, reply->gid, chroot_dir,
set->first_valid_gid, set->last_valid_gid);
restrict_process_size(process_size, (unsigned int)-1);
if (*home_dir != '\0') {
full_home_dir = *chroot_dir == '\0' ? home_dir :
t_strconcat(chroot_dir, "/", home_dir, NULL);
if (chdir(full_home_dir) < 0)
i_fatal("chdir(%s) failed: %m", full_home_dir);
}
env_put("LOGGED_IN=1");
env_put(t_strconcat("HOME=", home_dir, NULL));
env_put(t_strconcat("MAIL_CACHE_FIELDS=",
set->mail_cache_fields, NULL));
env_put(t_strconcat("MAIL_NEVER_CACHE_FIELDS=",
set->mail_never_cache_fields, NULL));
env_put(t_strdup_printf("MAILBOX_CHECK_INTERVAL=%u",
set->mailbox_check_interval));
env_put(t_strdup_printf("MAILBOX_IDLE_CHECK_INTERVAL=%u",
set->mailbox_idle_check_interval));
env_put(t_strconcat("CLIENT_WORKAROUNDS=",
set->client_workarounds, NULL));
env_put(t_strdup_printf("MAIL_MAX_FLAG_LENGTH=%u",
set->mail_max_flag_length));
if (set->mail_save_crlf)
env_put("MAIL_SAVE_CRLF=1");
if (set->mail_read_mmaped)
env_put("MAIL_READ_MMAPED=1");
if (set->maildir_copy_with_hardlinks)
env_put("MAILDIR_COPY_WITH_HARDLINKS=1");
if (set->maildir_check_content_changes)
env_put("MAILDIR_CHECK_CONTENT_CHANGES=1");
if (set->mail_full_filesystem_access)
env_put("FULL_FILESYSTEM_ACCESS=1");
(void)umask(set->umask);
env_put(t_strconcat("MBOX_LOCKS=", set->mbox_locks, NULL));
env_put(t_strdup_printf("MBOX_LOCK_TIMEOUT=%u",
set->mbox_lock_timeout));
env_put(t_strdup_printf("MBOX_DOTLOCK_CHANGE_TIMEOUT=%u",
set->mbox_dotlock_change_timeout));
if (set->mbox_read_dotlock)
env_put("MBOX_READ_DOTLOCK=1");
if (module_dir != NULL && *module_dir != '\0')
env_put(t_strconcat("MODULE_DIR=", module_dir, NULL));
/* user given environment - may be malicious. virtual_user comes from
auth process, but don't trust that too much either. Some auth
mechanism might allow leaving extra data there. */
mail = data + reply->mail_idx;
if (*mail == '\0' && set->default_mail_env != NULL) {
mail = expand_mail_env(set->default_mail_env,
data + reply->virtual_user_idx,
home_dir);
}
env_put(t_strconcat("MAIL=", mail, NULL));
env_put(t_strconcat("USER=", data + reply->virtual_user_idx, NULL));
addr = net_ip2addr(ip);
env_put(t_strconcat("IP=", addr, NULL));
if (set->verbose_proctitle) {
if (addr == NULL)
addr = "??";
i_snprintf(title, sizeof(title), "[%s %s]",
data + reply->virtual_user_idx, addr);
argv[1] = title;
}
/* make sure we don't leak syslog fd, but do it last so that
any errors above will be logged */
closelog();
if (set->mail_drop_priv_before_exec)
restrict_access_by_env(TRUE);
/* hide the path, it's ugly */
argv[0] = strrchr(executable, '/');
if (argv[0] == NULL) argv[0] = executable; else argv[0]++;
execv(executable, (char **) argv);
err = errno;
for (i = 0; i < 3; i++)
(void)close(i);
i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
/* not reached */
return FALSE;
}
void mail_process_destroyed(pid_t pid __attr_unused__)
{
mail_process_count--;
}