auth-process.c revision 798cfe56c9871262770384da1239162b3800cce1
/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
#include "common.h"
#include "hash.h"
#include "ioloop.h"
#include "env-util.h"
#include "fd-close-on-exec.h"
#include "unix-socket-create.h"
#include "network.h"
#include "istream.h"
#include "ostream.h"
#include "str.h"
#include "restrict-access.h"
#include "restrict-process-size.h"
#include "auth-process.h"
#include "child-process.h"
#include "../auth/auth-master-interface.h"
#include "log.h"
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pwd.h>
#include <syslog.h>
#define MAX_INBUF_SIZE 8192
#define MAX_OUTBUF_SIZE 65536
struct auth_process_group {
struct auth_process_group *next;
int listen_fd;
struct auth_settings *set;
unsigned int process_count;
struct auth_process *processes;
};
struct auth_process {
struct auth_process *next;
struct auth_process_group *group;
int fd;
int worker_listen_fd;
struct hash_table *requests;
unsigned int external:1;
unsigned int version_received:1;
unsigned int initialized:1;
unsigned int in_auth_reply:1;
};
bool have_initialized_auth_processes = FALSE;
static struct child_process auth_child_process =
{ PROCESS_TYPE_AUTH, 0 };
static struct child_process auth_worker_child_process =
{ PROCESS_TYPE_AUTH_WORKER, 0 };
static unsigned int auth_tag;
static struct auth_process_group *process_groups;
static bool auth_stalled = FALSE;
static void auth_process_destroy(struct auth_process *p);
static void auth_processes_start_missing(void *context);
unsigned int login_id,
struct login_auth_request *request)
{
if (ret >= 0) {
/* FIXME: well .. I'm not sure if it'd be better to
just block here. I don't think this condition should
happen often, so this could mean that the auth
process is stuck. Or that the computer is just
too heavily loaded. Possibility to block infinitely
is annoying though, so for now don't do it. */
i_warning("Auth process %s transmit buffer full, "
}
} else {
}
}
static bool
{
struct login_auth_request *request;
const char *const *list;
unsigned int id;
/* <id> <userid> [..] */
i_error("BUG: Auth process %s sent corrupted USER line",
return FALSE;
}
i_error("BUG: Auth process %s sent unrequested reply with ID "
return FALSE;
}
if (!auth_success_written) {
int fd;
if (fd == -1)
else
}
return TRUE;
}
static bool
{
struct login_auth_request *request;
unsigned int id;
i_error("BUG: Auth process %s sent unrequested reply with ID "
return FALSE;
}
return TRUE;
}
static bool
{
unsigned int pid;
if (process->initialized) {
i_error("BUG: Authentication server re-handshaking");
return FALSE;
}
if (pid == 0) {
i_error("BUG: Authentication server said it's PID 0");
return FALSE;
}
i_error("BUG: Authentication server sent invalid SPID "
return FALSE;
}
return TRUE;
}
static bool
{
struct login_auth_request *request;
const char *error;
unsigned int id;
error++;
i_error("BUG: Auth process %s sent unrequested reply with ID "
return FALSE;
}
return TRUE;
}
static bool
{
else
return TRUE;
}
{
const char *line;
bool ret;
case 0:
return;
case -1:
/* disconnected */
return;
case -2:
/* buffer full */
i_error("BUG: Auth process %s sent us more than %d "
(int)MAX_INBUF_SIZE);
return;
}
if (!process->version_received) {
return;
/* make sure the major version matches */
i_error("Auth process %s not compatible with master "
"process (mixed old and new binaries?)",
return;
}
}
T_BEGIN {
} T_END;
if (!ret) {
break;
}
}
}
static void auth_worker_input(struct auth_process *p)
{
int fd;
if (fd < 0) {
if (fd == -2)
i_error("accept(worker) failed: %m");
return;
}
create_auth_worker(p, fd);
}
static struct auth_process *
{
struct auth_process *p;
if (pid != 0)
group->process_count++;
p->worker_listen_fd =
if (p->worker_listen_fd == -1)
i_fatal("Couldn't create auth worker listener");
auth_worker_input, p);
return p;
}
static void auth_process_destroy(struct auth_process *p)
{
struct hash_iterate_context *iter;
struct auth_process **pos;
const char *path;
/* log the process exit and kill ourself */
log_deinit();
i_fatal("Auth process died too early - shutting down");
}
if (*pos == p) {
break;
}
}
p->group->process_count--;
if (close(p->worker_listen_fd) < 0)
i_error("close(worker_listen) failed: %m");
hash_destroy(&p->requests);
i_stream_destroy(&p->input);
o_stream_destroy(&p->output);
i_error("close(auth) failed: %m");
i_free(p);
}
static void
{
return;
}
const char *path)
{
struct auth_process *auth;
int fd;
if (fd == -1) {
return -1;
}
return 0;
}
{
struct auth_socket_settings *as;
struct auth_passdb_settings *ap;
struct auth_userdb_settings *au;
const char *str;
int i;
/* setup access environment */
/* set other environment */
env_put("DOVECOT_MASTER=1");
}
}
}
}
continue;
}
env_put("VERBOSE=1");
env_put("VERBOSE_DEBUG=1");
if (set->debug_passwords)
env_put("VERBOSE_DEBUG_PASSWORDS=1");
if (set->ssl_require_client_cert)
env_put("SSL_REQUIRE_CLIENT_CERT=1");
if (set->ssl_username_from_cert)
env_put("SSL_USERNAME_FROM_CERT=1");
if (set->use_winbind)
env_put("USE_WINBIND=1");
/* Environment may be used by Kerberos 5 library directly,
although we also try to use it directly as well */
}
}
}
{
struct auth_socket_settings *as;
const char *prefix, *executable;
/* see if this is a connect socket */
/* create communication to process with a socket pair */
i_error("socketpair() failed: %m");
return -1;
}
if (log_fd < 0)
pid = -1;
else {
if (pid < 0)
i_error("fork() failed: %m");
}
if (pid < 0) {
return -1;
}
if (pid != 0) {
/* master */
return 0;
}
/* move master communication handle to 0 */
i_fatal("dup2(stdin) failed: %m");
i_fatal("dup2(stdout) failed: %m");
i_fatal("dup2(stderr) failed: %m");
i_fatal("dup2() failed: %m");
}
for (i = 0; i <= 2; i++)
fd_close_on_exec(i, FALSE);
/* make sure we don't leak syslog fd, but do it last so that
any errors above will be logged */
closelog();
return -1;
}
{
const char *prefix, *executable;
int log_fd, i;
if (log_fd < 0)
pid = -1;
else {
if (pid < 0)
i_error("fork() failed: %m");
}
if (pid < 0) {
return -1;
}
if (pid != 0) {
/* master */
return 0;
}
gets ignored. */
i_fatal("dup2(stdin) failed: %m");
i_fatal("dup2(stdout) failed: %m");
i_fatal("dup2(stderr) failed: %m");
i_fatal("dup2(4) failed: %m");
for (i = 0; i <= 2; i++)
fd_close_on_exec(i, FALSE);
/* make sure we don't leak syslog fd, but do it last so that
any errors above will be logged */
closelog();
return -1;
}
{
struct auth_process_group *group;
struct auth_process *p;
return p;
}
}
return NULL;
}
{
struct auth_process_group *group;
const char *path;
return;
i_fatal("Couldn't create auth process listener");
}
{
struct auth_process *next;
const char *path;
}
}
void auth_processes_destroy_all(void)
{
struct auth_process_group *next;
while (process_groups != NULL) {
}
}
{
struct auth_settings *auth_set;
}
}
static void auth_processes_stall(void)
{
if (auth_stalled)
return;
i_error("Temporary failure in creating authentication processes, "
"slowing down for now");
auth_stalled = TRUE;
timeout_remove(&to);
}
static void
{
struct auth_process_group *group;
unsigned int count;
if (process_groups == NULL) {
/* first time here, create the groups */
}
if (create_auth_process(group) < 0) {
return;
}
}
}
if (auth_stalled) {
/* processes were created successfully */
i_info("Created authentication processes successfully, "
"unstalling");
timeout_remove(&to);
}
}
void auth_processes_init(void)
{
}
void auth_processes_deinit(void)
{
timeout_remove(&to);
}