auth-master.c revision 69d60dcff2614c4bfc8ad59e8fdc09e39c9dd0dc
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#include "lib.h"
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#include "lib-signals.h"
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#include "array.h"
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#include "ioloop.h"
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#include "network.h"
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#include "istream.h"
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#include "ostream.h"
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#include "auth-master.h"
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#include <stdlib.h>
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#include <unistd.h>
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#define AUTH_PROTOCOL_MAJOR 1
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#define AUTH_PROTOCOL_MINOR 0
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#define AUTH_REQUEST_TIMEOUT_SECS 30
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#define AUTH_MASTER_IDLE_SECS 60
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes#define MAX_INBUF_SIZE 8192
70953fb44a7140fe206c3a5f011e24209c8c5c6abnicholes#define MAX_OUTBUF_SIZE 1024
70953fb44a7140fe206c3a5f011e24209c8c5c6abnicholes
70953fb44a7140fe206c3a5f011e24209c8c5c6abnicholes#define DEFAULT_USERDB_LOOKUP_PREFIX "userdb lookup"
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesstruct auth_master_connection {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes char *auth_socket_path;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes int fd;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes struct ioloop *ioloop;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes struct io *io;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes struct istream *input;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes struct ostream *output;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes struct timeout *to;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes const char *prefix;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes unsigned int request_counter;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes bool (*reply_callback)(const char *cmd, const char *const *args,
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes void *context);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes void *reply_context;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes unsigned int debug:1;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes unsigned int sent_handshake:1;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes unsigned int handshaked:1;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes unsigned int aborted:1;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes};
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesstruct auth_master_user_lookup_ctx {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes struct auth_master_connection *conn;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes pool_t pool;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes const char *user;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes struct auth_user_reply *user_reply;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes int return_value;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes};
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesstruct auth_master_user_list_ctx {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes struct auth_master_connection *conn;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes pool_t pool;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes ARRAY_TYPE(const_string) users;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes const char *const *user_strings;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes unsigned int idx, user_count;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes bool failed;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes};
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesstatic void auth_input(struct auth_master_connection *conn);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesstruct auth_master_connection *
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesauth_master_init(const char *auth_socket_path, bool debug)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes{
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes struct auth_master_connection *conn;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes conn = i_new(struct auth_master_connection, 1);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes conn->auth_socket_path = i_strdup(auth_socket_path);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes conn->fd = -1;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes conn->debug = debug;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return conn;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes}
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesstatic void auth_connection_close(struct auth_master_connection *conn)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes{
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (conn->to != NULL)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes timeout_remove(&conn->to);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (conn->fd != -1) {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (close(conn->fd) < 0)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes i_error("close(%s) failed: %m", conn->auth_socket_path);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes conn->fd = -1;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes conn->sent_handshake = FALSE;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes conn->handshaked = FALSE;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes}
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesvoid auth_master_deinit(struct auth_master_connection **_conn)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes{
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes struct auth_master_connection *conn = *_conn;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes *_conn = NULL;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes auth_connection_close(conn);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes i_free(conn->auth_socket_path);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes i_free(conn);
0a39e7683f6611d66c55712f50bb240428d832a1bnicholes}
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesstatic void auth_request_lookup_abort(struct auth_master_connection *conn)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes{
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes io_loop_stop(conn->ioloop);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes conn->aborted = TRUE;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes}
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesstatic void auth_parse_input(struct auth_master_user_lookup_ctx *ctx,
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes const char *const *args)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes{
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes struct auth_user_reply *reply = ctx->user_reply;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes memset(reply, 0, sizeof(*reply));
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes reply->uid = (uid_t)-1;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes reply->gid = (gid_t)-1;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes p_array_init(&reply->extra_fields, ctx->pool, 64);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes reply->user = p_strdup(ctx->pool, *args);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes for (args++; *args != NULL; args++) {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (ctx->conn->debug)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes i_info("auth input: %s", *args);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (strncmp(*args, "uid=", 4) == 0)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes reply->uid = strtoul(*args + 4, NULL, 10);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes else if (strncmp(*args, "gid=", 4) == 0)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes reply->gid = strtoul(*args + 4, NULL, 10);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes else if (strncmp(*args, "home=", 5) == 0)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes reply->home = p_strdup(ctx->pool, *args + 5);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes else if (strncmp(*args, "chroot=", 7) == 0)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes reply->chroot = p_strdup(ctx->pool, *args + 7);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes else {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes const char *field = p_strdup(ctx->pool, *args);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes array_append(&reply->extra_fields, &field, 1);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes}
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesstatic int auth_input_handshake(struct auth_master_connection *conn)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes{
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes const char *line, *const *tmp;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes while ((line = i_stream_next_line(conn->input)) != NULL) {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes tmp = t_strsplit(line, "\t");
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (strcmp(tmp[0], "VERSION") == 0 &&
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes tmp[1] != NULL && tmp[2] != NULL) {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (strcmp(tmp[1], dec2str(AUTH_PROTOCOL_MAJOR)) != 0) {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes i_error("userdb lookup: "
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes "Auth protocol version mismatch "
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes "(%s vs %d)", tmp[1],
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes AUTH_PROTOCOL_MAJOR);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes auth_request_lookup_abort(conn);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return -1;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes } else if (strcmp(tmp[0], "SPID") == 0) {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes conn->handshaked = TRUE;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes break;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return 0;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes}
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesstatic bool auth_user_reply_callback(const char *cmd, const char *const *args,
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes void *context)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes{
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes struct auth_master_user_lookup_ctx *ctx = context;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes io_loop_stop(ctx->conn->ioloop);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (strcmp(cmd, "USER") == 0) {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes auth_parse_input(ctx, args);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes ctx->return_value = 1;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return TRUE;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (strcmp(cmd, "NOTFOUND") == 0) {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes ctx->return_value = 0;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return TRUE;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (strcmp(cmd, "FAIL") == 0) {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes i_error("userdb lookup(%s) failed: %s", ctx->user,
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes *args != NULL ? *args : "Internal failure");
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return TRUE;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return FALSE;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes}
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesstatic bool
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesauth_handle_line(struct auth_master_connection *conn, const char *line)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes{
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes const char *cmd, *const *args, *id, *wanted_id;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes args = t_strsplit(line, "\t");
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes cmd = *args; args++;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (*args == NULL)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes id = "";
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes else {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes id = *args;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes args++;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes wanted_id = dec2str(conn->request_counter);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (strcmp(id, wanted_id) == 0)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return conn->reply_callback(cmd, args, conn->reply_context);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (strcmp(cmd, "CUID") == 0) {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes i_error("%s: %s is an auth client socket. "
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes "It should be a master socket.",
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes conn->prefix, conn->auth_socket_path);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes } else {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes i_error("%s: BUG: Unexpected input: %s", conn->prefix, line);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes auth_request_lookup_abort(conn);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return FALSE;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes}
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholesstatic void auth_input(struct auth_master_connection *conn)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes{
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes const char *line;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes bool ret;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes switch (i_stream_read(conn->input)) {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes case 0:
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes case -1:
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes /* disconnected */
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes i_error("%s: Disconnected unexpectedly",
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes conn->prefix);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes auth_request_lookup_abort(conn);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes case -2:
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes /* buffer full */
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes i_error("%s: BUG: Received more than %d bytes",
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes conn->prefix, MAX_INBUF_SIZE);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes auth_request_lookup_abort(conn);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (!conn->handshaked) {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (auth_input_handshake(conn) < 0)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes while ((line = i_stream_next_line(conn->input)) != NULL) {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes T_BEGIN {
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes ret = auth_handle_line(conn, line);
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes } T_END;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes if (!ret)
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes return;
3c937b528ca923d5b51e63def9f888af4a77bb40bnicholes }
}
static int auth_master_connect(struct auth_master_connection *conn)
{
int fd, try;
i_assert(conn->fd == -1);
/* max. 1 second wait here. */
for (try = 0; try < 10; try++) {
fd = net_connect_unix(conn->auth_socket_path);
if (fd != -1 || (errno != EAGAIN && errno != ECONNREFUSED))
break;
/* busy. wait for a while. */
usleep(((rand() % 10) + 1) * 10000);
}
if (fd == -1) {
i_error("userdb lookup: connect(%s) failed: %m",
conn->auth_socket_path);
return -1;
}
conn->fd = fd;
return 0;
}
static void auth_request_timeout(struct auth_master_connection *conn)
{
if (!conn->handshaked)
i_error("%s: Connecting timed out", conn->prefix);
else
i_error("%s: Request timed out", conn->prefix);
auth_request_lookup_abort(conn);
}
static void auth_idle_timeout(struct auth_master_connection *conn)
{
auth_connection_close(conn);
}
static void auth_master_set_io(struct auth_master_connection *conn)
{
if (conn->to != NULL)
timeout_remove(&conn->to);
conn->ioloop = io_loop_create();
conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE, FALSE);
conn->output = o_stream_create_fd(conn->fd, MAX_OUTBUF_SIZE, FALSE);
conn->io = io_add(conn->fd, IO_READ, auth_input, conn);
conn->to = timeout_add(1000*AUTH_REQUEST_TIMEOUT_SECS,
auth_request_timeout, conn);
lib_signals_reset_ioloop();
}
static void auth_master_unset_io(struct auth_master_connection *conn,
struct ioloop *prev_ioloop)
{
io_loop_set_current(prev_ioloop);
lib_signals_reset_ioloop();
io_loop_set_current(conn->ioloop);
timeout_remove(&conn->to);
io_remove(&conn->io);
i_stream_unref(&conn->input);
o_stream_unref(&conn->output);
io_loop_destroy(&conn->ioloop);
conn->to = timeout_add(1000*AUTH_MASTER_IDLE_SECS,
auth_idle_timeout, conn);
}
static bool is_valid_string(const char *str)
{
const char *p;
/* make sure we're not sending any characters that have a special
meaning. */
for (p = str; *p != '\0'; p++) {
if (*p == '\t' || *p == '\n' || *p == '\r')
return FALSE;
}
return TRUE;
}
static int auth_master_run_cmd(struct auth_master_connection *conn,
const char *cmd)
{
struct ioloop *prev_ioloop;
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;
}
int auth_master_user_lookup(struct auth_master_connection *conn,
const char *user, const char *service,
pool_t pool, struct auth_user_reply *reply_r)
{
struct auth_master_user_lookup_ctx ctx;
const char *str;
if (!is_valid_string(user) || !is_valid_string(service)) {
/* non-allowed characters, the user can't exist */
return 0;
}
memset(&ctx, 0, sizeof(ctx));
ctx.conn = conn;
ctx.return_value = -1;
ctx.pool = pool;
ctx.user = user;
ctx.user_reply = reply_r;
if (++conn->request_counter == 0) {
/* avoid zero */
conn->request_counter++;
}
conn->reply_callback = auth_user_reply_callback;
conn->reply_context = &ctx;
conn->prefix = t_strdup_printf("userdb lookup(%s)", user);
str = t_strdup_printf("USER\t%u\t%s\tservice=%s\n",
conn->request_counter, user, service);
(void)auth_master_run_cmd(conn, str);
conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX;
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;
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);
if (++conn->request_counter == 0) {
/* avoid zero */
conn->request_counter++;
}
conn->reply_callback = auth_user_list_reply_callback;
conn->reply_context = ctx;
str = t_strdup_printf("LIST\t%u\n", conn->request_counter);
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;
}