auth-master.c revision 9d2a30e39c0662498db3368dbb010e36df54b7e8
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "lib.h"
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen#include "lib-signals.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "array.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "ioloop.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "eacces-error.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "network.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "istream.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "ostream.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "str.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "master-interface.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "auth-master.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen#include <stdlib.h>
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen#include <unistd.h>
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen#define AUTH_PROTOCOL_MAJOR 1
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen#define AUTH_PROTOCOL_MINOR 0
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#define AUTH_MASTER_IDLE_SECS 60
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#define MAX_INBUF_SIZE 8192
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#define MAX_OUTBUF_SIZE 1024
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen#define DEFAULT_USERDB_LOOKUP_PREFIX "userdb lookup"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstruct auth_master_connection {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde char *auth_socket_path;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde enum auth_master_flags flags;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen int fd;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde struct ioloop *ioloop;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen struct io *io;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde struct istream *input;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen struct ostream *output;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde struct timeout *to;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde const char *prefix;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen unsigned int request_counter;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde bool (*reply_callback)(const char *cmd, const char *const *args,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen void *context);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde void *reply_context;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde unsigned int sent_handshake:1;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde unsigned int handshaked:1;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen unsigned int aborted:1;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen};
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildestruct auth_master_lookup_ctx {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde struct auth_master_connection *conn;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde const char *user;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen const char *expected_reply;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen int return_value;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde pool_t pool;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde const char **fields;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde};
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildestruct auth_master_user_list_ctx {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde struct auth_master_connection *conn;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde pool_t pool;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen ARRAY_TYPE(const_string) users;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen const char *const *user_strings;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen unsigned int idx, user_count;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen bool failed;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen};
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic void auth_input(struct auth_master_connection *conn);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildestruct auth_master_connection *
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildeauth_master_init(const char *auth_socket_path, enum auth_master_flags flags)
1c6dd898551d7d4d61970b24a8372438f6b72f97Timo Sirainen{
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde struct auth_master_connection *conn;
1c6dd898551d7d4d61970b24a8372438f6b72f97Timo Sirainen
1c6dd898551d7d4d61970b24a8372438f6b72f97Timo Sirainen conn = i_new(struct auth_master_connection, 1);
1c6dd898551d7d4d61970b24a8372438f6b72f97Timo Sirainen conn->auth_socket_path = i_strdup(auth_socket_path);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde conn->fd = -1;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->flags = flags;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde return conn;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic void auth_connection_close(struct auth_master_connection *conn)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (conn->to != NULL)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen timeout_remove(&conn->to);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (conn->fd != -1) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (close(conn->fd) < 0)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_error("close(%s) failed: %m", conn->auth_socket_path);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde conn->fd = -1;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde }
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->sent_handshake = FALSE;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->handshaked = FALSE;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildevoid auth_master_deinit(struct auth_master_connection **_conn)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde struct auth_master_connection *conn = *_conn;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen *_conn = NULL;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen auth_connection_close(conn);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_free(conn->auth_socket_path);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_free(conn);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic void auth_request_lookup_abort(struct auth_master_connection *conn)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen io_loop_stop(conn->ioloop);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->aborted = TRUE;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildestatic int auth_input_handshake(struct auth_master_connection *conn)
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen const char *line, *const *tmp;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen while ((line = i_stream_next_line(conn->input)) != NULL) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen tmp = t_strsplit(line, "\t");
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (strcmp(tmp[0], "VERSION") == 0 &&
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen tmp[1] != NULL && tmp[2] != NULL) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (strcmp(tmp[1], dec2str(AUTH_PROTOCOL_MAJOR)) != 0) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_error("userdb lookup: "
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen "Auth protocol version mismatch "
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen "(%s vs %d)", tmp[1],
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen AUTH_PROTOCOL_MAJOR);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen auth_request_lookup_abort(conn);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return -1;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen } else if (strcmp(tmp[0], "SPID") == 0) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->handshaked = TRUE;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen break;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return 0;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic int parse_reply(const char *cmd, const char *const *args,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen const char *expected_reply, const char *user, bool debug)
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde{
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde if (strcmp(cmd, expected_reply) == 0)
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde return 1;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde if (strcmp(cmd, "NOTFOUND") == 0)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return 0;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde if (strcmp(cmd, "FAIL") == 0) {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde if (*args == NULL) {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde i_error("user %s: Auth %s lookup failed",
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde user, expected_reply);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde } else if (debug) {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde i_debug("user %s: Auth %s lookup returned temporary failure: %s",
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen user, expected_reply, *args);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return -1;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde }
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde i_error("Unknown reply: %s", cmd);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde return -1;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic bool auth_lookup_reply_callback(const char *cmd, const char *const *args,
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde void *context)
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde{
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde struct auth_master_lookup_ctx *ctx = context;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde unsigned int i, len;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen bool debug = (ctx->conn->flags & AUTH_MASTER_FLAG_DEBUG) != 0;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde io_loop_stop(ctx->conn->ioloop);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde ctx->return_value =
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen parse_reply(cmd, args, ctx->expected_reply, ctx->user, debug);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen len = str_array_length(args);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (ctx->return_value >= 0) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen ctx->fields = p_new(ctx->pool, const char *, len + 1);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen for (i = 0; i < len; i++)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen ctx->fields[i] = p_strdup(ctx->pool, args[i]);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen } else {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* put the reason string into first field */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen ctx->fields = p_new(ctx->pool, const char *, 2);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen for (i = 0; i < len; i++) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (strncmp(args[i], "reason=", 7) == 0) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen ctx->fields[0] =
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen p_strdup(ctx->pool, args[i] + 7);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen break;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (debug)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_debug("auth input: %s", t_strarray_join(args, " "));
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return TRUE;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildestatic bool
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenauth_handle_line(struct auth_master_connection *conn, const char *line)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen const char *cmd, *const *args, *id, *wanted_id;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen args = t_strsplit(line, "\t");
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen cmd = *args; args++;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (*args == NULL)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen id = "";
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen else {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen id = *args;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen args++;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen wanted_id = dec2str(conn->request_counter);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (strcmp(id, wanted_id) == 0)
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde return conn->reply_callback(cmd, args, conn->reply_context);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde if (strcmp(cmd, "CUID") == 0) {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde i_error("%s: %s is an auth client socket. "
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen "It should be a master socket.",
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->prefix, conn->auth_socket_path);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen } else {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_error("%s: BUG: Unexpected input: %s", conn->prefix, line);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen auth_request_lookup_abort(conn);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return FALSE;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic void auth_input(struct auth_master_connection *conn)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen const char *line;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen bool ret;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen switch (i_stream_read(conn->input)) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen case 0:
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen case -1:
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* disconnected */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_error("%s: Disconnected unexpectedly",
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->prefix);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen auth_request_lookup_abort(conn);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde case -2:
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* buffer full */
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde i_error("%s: BUG: Received more than %d bytes",
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde conn->prefix, MAX_INBUF_SIZE);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen auth_request_lookup_abort(conn);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde return;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (!conn->handshaked) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (auth_input_handshake(conn) < 0)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde while ((line = i_stream_next_line(conn->input)) != NULL) {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde T_BEGIN {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen ret = auth_handle_line(conn, line);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen } T_END;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (!ret)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic int auth_master_connect(struct auth_master_connection *conn)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen int fd;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_assert(conn->fd == -1);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* max. 1 second wait here. */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen fd = net_connect_unix_with_retries(conn->auth_socket_path, 1000);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (fd == -1) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (errno == EACCES) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_error("userdb lookup: %s",
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen eacces_error_get("connect",
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->auth_socket_path));
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen } else {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_error("userdb lookup: connect(%s) failed: %m",
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->auth_socket_path);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return -1;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->fd = fd;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return 0;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic void auth_request_timeout(struct auth_master_connection *conn)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (!conn->handshaked)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_error("%s: Connecting timed out", conn->prefix);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen else
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_error("%s: Request timed out", conn->prefix);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen auth_request_lookup_abort(conn);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic void auth_idle_timeout(struct auth_master_connection *conn)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen auth_connection_close(conn);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildestatic void auth_master_set_io(struct auth_master_connection *conn)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (conn->to != NULL)
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde timeout_remove(&conn->to);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->ioloop = io_loop_create();
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE, FALSE);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->output = o_stream_create_fd(conn->fd, MAX_OUTBUF_SIZE, FALSE);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->io = io_add(conn->fd, IO_READ, auth_input, conn);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->to = timeout_add(1000*MASTER_AUTH_LOOKUP_TIMEOUT_SECS,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen auth_request_timeout, conn);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen lib_signals_reset_ioloop();
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic void auth_master_unset_io(struct auth_master_connection *conn,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen struct ioloop *prev_ioloop)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (prev_ioloop != NULL) {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde io_loop_set_current(prev_ioloop);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde lib_signals_reset_ioloop();
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde io_loop_set_current(conn->ioloop);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen timeout_remove(&conn->to);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen io_remove(&conn->io);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_stream_unref(&conn->input);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen o_stream_unref(&conn->output);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen io_loop_destroy(&conn->ioloop);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if ((conn->flags & AUTH_MASTER_FLAG_NO_IDLE_TIMEOUT) == 0) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (prev_ioloop == NULL)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen auth_connection_close(conn);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen else {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->to = timeout_add(1000*AUTH_MASTER_IDLE_SECS,
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde auth_idle_timeout, conn);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildestatic bool is_valid_string(const char *str)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen const char *p;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* make sure we're not sending any characters that have a special
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen meaning. */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen for (p = str; *p != '\0'; p++) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (*p == '\t' || *p == '\n' || *p == '\r')
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return FALSE;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return TRUE;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic int auth_master_run_cmd(struct auth_master_connection *conn,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen const char *cmd)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde struct ioloop *prev_ioloop;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde const char *str;
if (conn->fd == -1) {
if (auth_master_connect(conn) < 0)
return -1;
}
prev_ioloop = current_ioloop;
auth_master_set_io(conn);
o_stream_cork(conn->output);
if (!conn->sent_handshake) {
str = t_strdup_printf("VERSION\t%d\t%d\n",
AUTH_PROTOCOL_MAJOR, AUTH_PROTOCOL_MINOR);
o_stream_send_str(conn->output, str);
conn->sent_handshake = TRUE;
}
o_stream_send_str(conn->output, cmd);
o_stream_uncork(conn->output);
if (conn->output->stream_errno != 0) {
errno = conn->output->stream_errno;
i_error("write(auth socket) failed: %m");
} else {
io_loop_run(conn->ioloop);
}
auth_master_unset_io(conn, prev_ioloop);
if (conn->aborted) {
conn->aborted = FALSE;
auth_connection_close(conn);
return -1;
}
return 0;
}
static unsigned int
auth_master_next_request_id(struct auth_master_connection *conn)
{
if (++conn->request_counter == 0) {
/* avoid zero */
conn->request_counter++;
}
return conn->request_counter;
}
static void
auth_user_info_export(string_t *str, const struct auth_user_info *info)
{
str_append(str, "service=");
str_append(str, info->service);
if (info->local_ip.family != 0)
str_printfa(str, "\tlip=%s", net_ip2addr(&info->local_ip));
if (info->local_port != 0)
str_printfa(str, "\tlport=%d", info->local_port);
if (info->remote_ip.family != 0)
str_printfa(str, "\trip=%s", net_ip2addr(&info->remote_ip));
if (info->remote_port != 0)
str_printfa(str, "\trport=%d", info->remote_port);
}
int auth_master_user_lookup(struct auth_master_connection *conn,
const char *user, const struct auth_user_info *info,
pool_t pool, const char **username_r,
const char *const **fields_r)
{
struct auth_master_lookup_ctx ctx;
string_t *str;
if (!is_valid_string(user) || !is_valid_string(info->service)) {
/* non-allowed characters, the user can't exist */
*username_r = NULL;
*fields_r = NULL;
return 0;
}
memset(&ctx, 0, sizeof(ctx));
ctx.conn = conn;
ctx.return_value = -1;
ctx.pool = pool;
ctx.expected_reply = "USER";
ctx.user = user;
conn->reply_callback = auth_lookup_reply_callback;
conn->reply_context = &ctx;
str = t_str_new(128);
str_printfa(str, "USER\t%u\t%s\t",
auth_master_next_request_id(conn), user);
auth_user_info_export(str, info);
str_append_c(str, '\n');
conn->prefix = t_strdup_printf("userdb lookup(%s)", user);
(void)auth_master_run_cmd(conn, str_c(str));
conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX;
if (ctx.return_value <= 0 || ctx.fields[0] == NULL) {
*username_r = NULL;
*fields_r = ctx.fields != NULL ? ctx.fields :
p_new(pool, const char *, 1);
if (ctx.return_value > 0) {
i_error("Userdb lookup didn't return username");
ctx.return_value = -1;
}
} else {
*username_r = ctx.fields[0];
*fields_r = ctx.fields + 1;
}
return ctx.return_value;
}
void auth_user_fields_parse(const char *const *fields, pool_t pool,
struct auth_user_reply *reply_r)
{
memset(reply_r, 0, sizeof(*reply_r));
reply_r->uid = (uid_t)-1;
reply_r->gid = (gid_t)-1;
p_array_init(&reply_r->extra_fields, pool, 64);
for (; *fields != NULL; fields++) {
if (strncmp(*fields, "uid=", 4) == 0) {
if (str_to_uid(*fields + 4, &reply_r->uid) < 0)
i_error("Invalid uid in reply");
} else if (strncmp(*fields, "gid=", 4) == 0) {
if (str_to_gid(*fields + 4, &reply_r->gid) < 0)
i_error("Invalid gid in reply");
} else if (strncmp(*fields, "home=", 5) == 0)
reply_r->home = p_strdup(pool, *fields + 5);
else if (strncmp(*fields, "chroot=", 7) == 0)
reply_r->chroot = p_strdup(pool, *fields + 7);
else {
const char *field = p_strdup(pool, *fields);
array_append(&reply_r->extra_fields, &field, 1);
}
}
}
int auth_master_pass_lookup(struct auth_master_connection *conn,
const char *user, const struct auth_user_info *info,
pool_t pool, const char *const **fields_r)
{
struct auth_master_lookup_ctx ctx;
string_t *str;
if (!is_valid_string(user) || !is_valid_string(info->service)) {
/* non-allowed characters, the user can't exist */
*fields_r = NULL;
return 0;
}
memset(&ctx, 0, sizeof(ctx));
ctx.conn = conn;
ctx.return_value = -1;
ctx.pool = pool;
ctx.expected_reply = "PASS";
ctx.user = user;
conn->reply_callback = auth_lookup_reply_callback;
conn->reply_context = &ctx;
str = t_str_new(128);
str_printfa(str, "PASS\t%u\t%s\t",
auth_master_next_request_id(conn), user);
auth_user_info_export(str, info);
str_append_c(str, '\n');
conn->prefix = t_strdup_printf("passdb lookup(%s)", user);
(void)auth_master_run_cmd(conn, str_c(str));
conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX;
*fields_r = ctx.fields != NULL ? ctx.fields :
p_new(pool, const char *, 1);
return ctx.return_value;
}
static bool
auth_user_list_reply_callback(const char *cmd, const char *const *args,
void *context)
{
struct auth_master_user_list_ctx *ctx = context;
const char *user;
timeout_reset(ctx->conn->to);
if (strcmp(cmd, "DONE") == 0) {
io_loop_stop(ctx->conn->ioloop);
if (args[0] != NULL && strcmp(args[0], "fail") == 0) {
i_error("User listing returned failure");
ctx->failed = TRUE;
}
return TRUE;
}
if (strcmp(cmd, "LIST") == 0 && args[0] != NULL) {
/* we'll just read all the users into memory. otherwise we'd
have to use a separate connection for listing and there's
a higher chance of a failure since the connection could be
open to dovecot-auth for a long time. */
user = p_strdup(ctx->pool, args[0]);
array_append(&ctx->users, &user, 1);
return TRUE;
}
return FALSE;
}
struct auth_master_user_list_ctx *
auth_master_user_list_init(struct auth_master_connection *conn)
{
struct auth_master_user_list_ctx *ctx;
const char *str;
pool_t pool;
pool = pool_alloconly_create("auth master user list", 10240);
ctx = p_new(pool, struct auth_master_user_list_ctx, 1);
ctx->pool = pool;
ctx->conn = conn;
i_array_init(&ctx->users, 128);
conn->reply_callback = auth_user_list_reply_callback;
conn->reply_context = ctx;
str = t_strdup_printf("LIST\t%u\n", auth_master_next_request_id(conn));
conn->prefix = "userdb list";
if (auth_master_run_cmd(conn, str) < 0)
ctx->failed = TRUE;
ctx->user_strings = array_get(&ctx->users, &ctx->user_count);
conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX;
return ctx;
}
const char *auth_master_user_list_next(struct auth_master_user_list_ctx *ctx)
{
if (ctx->idx == ctx->user_count)
return NULL;
return ctx->user_strings[ctx->idx++];
}
unsigned int auth_master_user_list_count(struct auth_master_user_list_ctx *ctx)
{
return ctx->user_count;
}
int auth_master_user_list_deinit(struct auth_master_user_list_ctx **_ctx)
{
struct auth_master_user_list_ctx *ctx = *_ctx;
int ret = ctx->failed ? -1 : 0;
*_ctx = NULL;
array_free(&ctx->users);
pool_unref(&ctx->pool);
return ret;
}