mail-process.c revision dc4127f13f2d5cf44577471123ae70b7772b2e8c
7cb128dc4cae2a03a742f63ba7afee23c78e3af0Phil Carmody/* Copyright (C) 2002 Timo Sirainen */
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen#include "common.h"
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen#include "fd-close-on-exec.h"
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen#include "env-util.h"
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen#include "str.h"
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen#include "network.h"
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen#include "restrict-access.h"
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen#include "restrict-process-size.h"
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen#include "var-expand.h"
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen#include "mail-process.h"
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen
202a34580f6204672d9b0d6a0756f35a3c4cdef6Timo Sirainen#include <stdlib.h>
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen#include <unistd.h>
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen#include <grp.h>
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen#include <syslog.h>
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen#include <sys/stat.h>
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainenstatic unsigned int mail_process_count = 0;
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainenstatic int validate_uid_gid(uid_t uid, gid_t gid)
202a34580f6204672d9b0d6a0756f35a3c4cdef6Timo Sirainen{
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen if (uid == 0) {
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen i_error("mail process isn't allowed for root");
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen return FALSE;
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen }
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen if (uid != 0 && gid == 0) {
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen i_error("mail process isn't allowed to be in group 0");
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen return FALSE;
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen }
202a34580f6204672d9b0d6a0756f35a3c4cdef6Timo Sirainen
202a34580f6204672d9b0d6a0756f35a3c4cdef6Timo Sirainen if (uid < (uid_t)set->first_valid_uid ||
202a34580f6204672d9b0d6a0756f35a3c4cdef6Timo Sirainen (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) {
202a34580f6204672d9b0d6a0756f35a3c4cdef6Timo Sirainen i_error("mail process isn't allowed to use UID %s",
202a34580f6204672d9b0d6a0756f35a3c4cdef6Timo Sirainen dec2str(uid));
202a34580f6204672d9b0d6a0756f35a3c4cdef6Timo Sirainen return FALSE;
202a34580f6204672d9b0d6a0756f35a3c4cdef6Timo Sirainen }
202a34580f6204672d9b0d6a0756f35a3c4cdef6Timo Sirainen
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen if (gid < (gid_t)set->first_valid_gid ||
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen (set->last_valid_gid != 0 && gid > (gid_t)set->last_valid_gid)) {
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen i_error("mail process isn't allowed to use "
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen "GID %s (UID is %s)", dec2str(gid), dec2str(uid));
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen return FALSE;
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen }
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen return TRUE;
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen}
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainenstatic int validate_chroot(const char *dir)
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen{
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen const char *const *chroot_dirs;
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen if (*dir == '\0')
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen return FALSE;
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen if (set->valid_chroot_dirs == NULL)
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen return FALSE;
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen chroot_dirs = t_strsplit(set->valid_chroot_dirs, ":");
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen while (*chroot_dirs != NULL) {
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen if (**chroot_dirs != '\0' &&
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen return TRUE;
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen chroot_dirs++;
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen }
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen return FALSE;
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen}
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainenstatic const char *expand_mail_env(const char *env, const char *user,
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen const char *home)
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen{
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen string_t *str;
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen const char *p;
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen
25fb397382c6f7d39bfeee85774e7675f02bfb3cTimo Sirainen str = t_str_new(256);
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen
cbb79ea1b1a49255c6edc46409a544666b22788fTimo Sirainen /* it's either type:data or just data */
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen p = strchr(env, ':');
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen if (p != NULL) {
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen while (env != p) {
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen str_append_c(str, *env);
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen env++;
3157f61431f19e01173e2e0d270c28af86dc97aaTimo Sirainen }
str_append_c(str, *env++);
}
if (env[0] == '~' && env[1] == '/') {
/* expand home */
str_append(str, home);
env++;
}
/* expand %vars */
var_expand(str, env, user, home);
return str_c(str);
}
int create_mail_process(int socket, struct ip_addr *ip,
const char *executable, unsigned int process_size,
struct auth_master_reply *reply, const char *data)
{
static const char *argv[] = { NULL, NULL, NULL };
const char *host, *mail;
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;
if (reply->chroot && !validate_chroot(data + reply->home_idx))
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_MAIL);
return TRUE;
}
clean_child_process();
/* 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 (dup2(null_fd, 2) < 0)
i_fatal("mail: dup2(stderr) 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,
reply->chroot ? data + reply->home_idx : NULL);
restrict_process_size(process_size, (unsigned int)-1);
env_put("LOGGED_IN=1");
env_put(t_strconcat("HOME=", data + reply->home_idx, 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));
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->overwrite_incompatible_index)
env_put("OVERWRITE_INCOMPATIBLE_INDEX=1");
if (umask(set->umask) != set->umask)
i_fatal("Invalid umask: %o", 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");
/* 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,
data + reply->home_idx);
}
env_put(t_strconcat("MAIL=", mail, NULL));
env_put(t_strconcat("USER=", data + reply->virtual_user_idx, NULL));
if (set->verbose_proctitle) {
host = net_ip2host(ip);
if (host == NULL)
host = "??";
i_snprintf(title, sizeof(title), "[%s %s]",
data + reply->virtual_user_idx, host);
argv[1] = title;
}
/* make sure we don't leak syslog fd, but do it last so that
any errors above will be logged */
closelog();
/* 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--;
}