login-process.c revision a7c58de2babd44256c9bb63f12876433646f9e7e
/* Copyright (C) 2002 Timo Sirainen */
#include "common.h"
#include "ioloop.h"
#include "network.h"
#include "ostream.h"
#include "fdpass.h"
#include "fd-close-on-exec.h"
#include "env-util.h"
#include "restrict-access.h"
#include "restrict-process-size.h"
#include "login-process.h"
#include "auth-process.h"
#include "mail-process.h"
#include "master-login-interface.h"
#include <unistd.h>
#include <syslog.h>
struct login_group {
struct login_group *next;
struct login_settings *set;
unsigned int processes;
unsigned int listening_processes;
unsigned int wanted_processes_count;
struct login_process *oldest_nonlisten_process;
struct login_process *newest_nonlisten_process;
const char *executable;
unsigned int process_size;
int *listen_fd, *ssl_listen_fd;
};
struct login_process {
struct login_group *group;
int refcount;
int fd;
unsigned int initialized:1;
unsigned int listening:1;
unsigned int destroyed:1;
};
struct login_auth_request {
struct login_process *process;
unsigned int tag;
unsigned int login_tag;
int fd;
};
static unsigned int auth_id_counter, login_pid_counter;
static struct hash_table *processes;
static struct login_group *login_groups;
static void login_process_destroy(struct login_process *p);
static void login_process_unref(struct login_process *p);
{
struct login_group *group;
/* not enabled */
return;
}
} else
}
{
}
{
struct master_login_reply master_reply;
else {
}
/* reply to login */
sizeof(master_reply)) < 0)
i_error("close(mail client) failed: %m");
}
static void login_process_mark_nonlistening(struct login_process *p)
{
if (!p->listening) {
i_error("login: received another \"not listening\" "
"notification");
return;
}
p->group->listening_processes--;
p->group->newest_nonlisten_process = p;
p->group->oldest_nonlisten_process = p;
}
static void login_process_input(void *context)
{
struct login_process *p = context;
struct auth_process *auth_process;
struct login_auth_request *authreq;
struct master_login_request req;
if (ret == 0) {
/* disconnected, ie. the login process died */
} else if (ret > 0) {
/* req wasn't fully read */
i_error("login: fd_read() couldn't read all req");
} else {
i_error("login: fd_read() failed: %m");
}
if (client_fd != -1) {
i_error("close(mail client) failed: %m");
}
return;
}
if (client_fd == -1) {
/* just a notification that the login process */
if (!p->initialized) {
/* initialization notify */
p->initialized = TRUE;;
} else {
/* not listening for new connections anymore */
}
return;
}
/* ask the cookie from the auth process */
p->refcount++;
if (auth_process == NULL) {
i_error("login: Authentication process %u doesn't exist",
} else {
}
}
static struct login_process *
{
struct login_process *p;
p->refcount = 1;
sizeof(struct master_login_reply)*10,
p->group->listening_processes++;
return p;
}
static void login_process_remove_from_lists(struct login_process *p)
{
if (p == p->group->oldest_nonlisten_process)
else
if (p == p->group->newest_nonlisten_process)
else
}
static void login_process_destroy(struct login_process *p)
{
if (p->destroyed)
return;
i_error("Login process died too early - shutting down");
}
if (p->listening)
p->group->listening_processes--;
o_stream_close(p->output);
i_error("close(login) failed: %m");
if (!p->listening)
if (p->pid != 0)
}
static void login_process_unref(struct login_process *p)
{
if (--p->refcount > 0)
return;
o_stream_unref(p->output);
i_free(p);
}
{
/* setup access environment - needs to be done after
clean_child_process() since it clears environment */
env_put("DOVECOT_MASTER=1");
if (!set->ssl_disable) {
}
if (set->disable_plaintext_auth)
env_put("DISABLE_PLAINTEXT_AUTH=1");
if (set->verbose_proctitle)
env_put("VERBOSE_PROCTITLE=1");
if (set->verbose_ssl)
env_put("VERBOSE_SSL=1");
env_put("PROCESS_PER_CONNECTION=1");
env_put("MAX_LOGGING_USERS=1");
} else {
}
}
{
int fd[2];
}
i_fatal("Login process must not run as root");
/* create communication to process with a socket pair */
i_error("socketpair() failed: %m");
return -1;
}
if (pid < 0) {
i_error("fork() failed: %m");
return -1;
}
if (pid != 0) {
/* master */
return pid;
}
/* move the listen handle */
i_fatal("login: dup2(listen_fd) failed: %m");
/* move the SSL listen handle */
i_fatal("login: dup2(ssl_listen_fd) failed: %m");
/* move communication handle */
i_fatal("login: dup2(master) failed: %m");
if (!set->login_chroot) {
/* no chrooting, but still change to the directory */
}
/* 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 */
return -1;
}
{
struct login_process *p;
/* don't start raising the process count if they're dying all
the time */
if (p != NULL)
p->group->wanted_processes_count = 0;
}
void *context __attr_unused__)
{
}
void login_processes_destroy_all(void)
{
while (login_groups != NULL) {
}
}
{
/* create max. one process every second, that way if it keeps
dying all the time we don't eat all cpu with fork()ing. */
(void)create_login_process(group);
return;
}
/* we want to respond fast when multiple clients are connecting
at once, but we also want to prevent fork-bombing. use the
same method as apache: check once a second if we need new
processes. if yes and we've used all the existing processes,
double their amount (unless we've hit the high limit).
Then for each second that didn't use all existing processes,
drop the max. process count by one. */
else if (group->listening_processes == 0)
(void)create_login_process(group);
}
static void
{
struct login_group *group;
struct login_settings *login;
if (login_groups == NULL) {
}
}
static int login_process_send_env(struct login_process *p)
{
extern char **environ;
char **env;
int ret = 0;
/* this will clear our environment. luckily we don't need it. */
ret = -1;
break;
}
}
ret = -1;
env_clean();
return ret;
}
{
struct login_process *p;
int fd;
if (fd < 0) {
if (fd < -1)
i_fatal("accept(inetd_login_fd) failed: %m");
} else {
p->initialized = TRUE;;
if (login_process_send_env(p) < 0) {
i_warning("Couldn't send environment to login process");
}
}
}
void login_processes_init(void)
{
auth_id_counter = 0;
login_pid_counter = 0;
login_groups = NULL;
if (!IS_INETD()) {
} else {
/* use the first login group for everyone */
}
}
void login_processes_deinit(void)
{
}