Mercurial > dovecot > original-hg > dovecot-1.2
view src/auth/auth-master-connection.c @ 3036:fcecff14e470 HEAD
Added authentication debugging logging.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 06 Jan 2005 17:41:53 +0200 |
parents | 155386b3149d |
children | 2d33734b16d5 |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "common.h" #include "buffer.h" #include "hash.h" #include "str.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "network.h" #include "mech.h" #include "userdb.h" #include "auth-master-interface.h" #include "auth-client-connection.h" #include "auth-master-connection.h" #include <unistd.h> #include <stdlib.h> #define MAX_INBUF_SIZE 1024 #define MAX_OUTBUF_SIZE (1024*50) struct auth_listener { struct auth_master_connection *master; int client_listener; int fd; char *path; struct io *io; }; struct master_userdb_request { struct auth_master_connection *conn; unsigned int id; struct auth_request *auth_request; }; static int master_output(void *context); static void auth_master_connection_close(struct auth_master_connection *conn); static int auth_master_connection_unref(struct auth_master_connection *conn); static void master_send(struct auth_master_connection *conn, const char *fmt, ...) __attr_format__(2, 3); static void master_send(struct auth_master_connection *conn, const char *fmt, ...) { va_list args; string_t *str; t_push(); va_start(args, fmt); str = t_str_new(256); str_vprintfa(str, fmt, args); str_append_c(str, '\n'); (void)o_stream_send(conn->output, str_data(str), str_len(str)); va_end(args); t_pop(); } static void append_user_reply(string_t *str, const struct user_data *user) { const char *p; str_printfa(str, "%s\tuid=%s\tgid=%s", user->virtual_user, dec2str(user->uid), dec2str(user->gid)); if (user->system_user != NULL) str_printfa(str, "\tsystem_user=%s", user->system_user); if (user->mail != NULL) str_printfa(str, "\tmail=%s", user->mail); p = user->home != NULL ? strstr(user->home, "/./") : NULL; if (p == NULL) { if (user->home != NULL) str_printfa(str, "\thome=%s", user->home); } else { /* wu-ftpd like <chroot>/./<home> */ str_printfa(str, "\thome=%s\tchroot=%s", p + 3, t_strdup_until(user->home, p)); } } static void userdb_callback(const struct user_data *user, void *context) { struct master_userdb_request *master_request = context; string_t *str; if (verbose_debug && user != NULL) { i_info("userdb(%s): uid=%s gid=%s home=%s mail=%s", get_log_prefix(master_request->auth_request), dec2str(user->uid), dec2str(user->gid), user->home != NULL ? user->home : "", user->mail != NULL ? user->mail : ""); } if (auth_master_connection_unref(master_request->conn)) { if (user == NULL) { master_send(master_request->conn, "NOTFOUND\t%u", master_request->id); } else { str = t_str_new(256); str_printfa(str, "USER\t%u\t", master_request->id); append_user_reply(str, user); master_send(master_request->conn, "%s", str_c(str)); } } auth_request_destroy(master_request->auth_request); i_free(master_request); } static int master_input_request(struct auth_master_connection *conn, const char *args) { struct auth_client_connection *client_conn; struct master_userdb_request *master_request; struct auth_request *request; const char *const *list; unsigned int id, client_pid, client_id; /* <id> <client-pid> <client-id> */ list = t_strsplit(args, "\t"); if (list[0] == NULL || list[1] == NULL || list[2] == NULL) { i_error("BUG: Master sent broken REQUEST"); return FALSE; } id = (unsigned int)strtoul(list[0], NULL, 10); client_pid = (unsigned int)strtoul(list[1], NULL, 10); client_id = (unsigned int)strtoul(list[2], NULL, 10); client_conn = auth_client_connection_lookup(conn, client_pid); request = client_conn == NULL ? NULL : hash_lookup(client_conn->auth_requests, POINTER_CAST(client_id)); if (request == NULL) { if (verbose) { i_info("Master request %u.%u not found", client_pid, client_id); } master_send(conn, "NOTFOUND\t%u", id); } else if (!request->successful) { i_error("Master requested unfinished authentication request " "%u.%u", client_pid, client_id); master_send(conn, "NOTFOUND\t%u", id); } else { master_request = i_new(struct master_userdb_request, 1); master_request->conn = conn; master_request->id = id; master_request->auth_request = request; conn->refcount++; userdb->lookup(request, userdb_callback, master_request); } return TRUE; } static int master_input_die(struct auth_master_connection *conn) { return TRUE; } static void master_input(void *context) { struct auth_master_connection *conn = context; char *line; int ret; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ auth_master_connection_close(conn); return; case -2: /* buffer full */ i_error("BUG: Master sent us more than %d bytes", (int)MAX_INBUF_SIZE); auth_master_connection_close(conn); return; } if (!conn->version_received) { line = i_stream_next_line(conn->input); if (line == NULL) return; /* make sure the major version matches */ if (strncmp(line, "VERSION\t", 8) != 0 || atoi(t_strcut(line + 8, '\t')) != AUTH_MASTER_PROTOCOL_MAJOR_VERSION) { i_error("Master not compatible with this server " "(mixed old and new binaries?)"); auth_master_connection_close(conn); return; } conn->version_received = TRUE; } while ((line = i_stream_next_line(conn->input)) != NULL) { t_push(); if (strncmp(line, "REQUEST\t", 8) == 0) ret = master_input_request(conn, line + 8); else if (strcmp(line, "DIE") == 0) ret = master_input_die(conn); else { /* ignore unknown command */ ret = TRUE; } t_pop(); if (!ret) { auth_master_connection_close(conn); return; } } } static int master_output(void *context) { struct auth_master_connection *conn = context; int ret; if ((ret = o_stream_flush(conn->output)) < 0) { /* transmit error, probably master died */ auth_master_connection_close(conn); return 1; } if (o_stream_get_buffer_used_size(conn->output) <= MAX_OUTBUF_SIZE/2) { /* allow input again */ conn->io = io_add(conn->fd, IO_READ, master_input, conn); } return 1; } static void auth_master_connection_set_fd(struct auth_master_connection *conn, int fd) { if (conn->input != NULL) i_stream_unref(conn->input); if (conn->output != NULL) o_stream_unref(conn->output); if (conn->io != NULL) io_remove(conn->io); conn->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_file(fd, default_pool, (size_t)-1, FALSE); o_stream_set_flush_callback(conn->output, master_output, conn); conn->io = io_add(fd, IO_READ, master_input, conn); conn->fd = fd; } struct auth_master_connection * auth_master_connection_create(int fd, unsigned int pid) { struct auth_master_connection *conn; conn = i_new(struct auth_master_connection, 1); conn->refcount = 1; conn->pid = pid; conn->fd = fd; conn->listeners_buf = buffer_create_dynamic(default_pool, 64); if (fd != -1) auth_master_connection_set_fd(conn, fd); return conn; } void auth_master_connection_send_handshake(struct auth_master_connection *conn) { if (conn->output != NULL) { master_send(conn, "VERSION\t%u\t%u\nSPID\t%u\n", AUTH_MASTER_PROTOCOL_MAJOR_VERSION, AUTH_MASTER_PROTOCOL_MINOR_VERSION, conn->pid); } } static void auth_master_connection_close(struct auth_master_connection *conn) { if (!standalone) io_loop_stop(ioloop); if (close(conn->fd) < 0) i_error("close(): %m"); conn->fd = -1; o_stream_close(conn->output); conn->output = NULL; if (conn->io != NULL) { io_remove(conn->io); conn->io = NULL; } } void auth_master_connection_destroy(struct auth_master_connection *conn) { struct auth_listener **l; size_t i, size; if (conn->destroyed) return; conn->destroyed = TRUE; auth_client_connections_deinit(conn); if (conn->fd != -1) auth_master_connection_close(conn); l = buffer_get_modifyable_data(conn->listeners_buf, &size); size /= sizeof(*l); for (i = 0; i < size; i++) { net_disconnect(l[i]->fd); io_remove(l[i]->io); if (l[i]->path != NULL) { (void)unlink(l[i]->path); i_free(l[i]->path); } i_free(l[i]); } buffer_free(conn->listeners_buf); conn->listeners_buf = NULL; auth_master_connection_unref(conn); } static int auth_master_connection_unref(struct auth_master_connection *conn) { if (--conn->refcount > 0) return TRUE; if (conn->output != NULL) o_stream_unref(conn->output); i_free(conn); return FALSE; } static void auth_accept(void *context) { struct auth_listener *l = context; int fd; fd = net_accept(l->fd, NULL, NULL); if (fd < 0) { if (fd < -1) i_fatal("accept() failed: %m"); } else { net_set_nonblock(fd, TRUE); if (l->client_listener) (void)auth_client_connection_create(l->master, fd); else { /* we'll just replace the previous master.. */ auth_master_connection_set_fd(l->master, fd); auth_master_connection_send_handshake(l->master); } } } void auth_master_connection_add_listener(struct auth_master_connection *conn, int fd, const char *path, int client) { struct auth_listener *l; l = i_new(struct auth_listener, 1); l->master = conn; l->client_listener = client; l->fd = fd; l->path = i_strdup(path); l->io = io_add(fd, IO_READ, auth_accept, l); buffer_append(conn->listeners_buf, &l, sizeof(l)); }