master-auth.c revision fd05c47210cad1f6d8effbda5cba7f7d938ca9a0
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce/* Copyright (c) 2005-2014 Dovecot authors, see the included COPYING file */
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce#include "lib.h"
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce#include "ioloop.h"
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce#include "fdpass.h"
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce#include "buffer.h"
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce#include "hash.h"
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce#include "master-service-private.h"
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce#include "master-auth.h"
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce#include <stdlib.h>
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce#include <unistd.h>
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce#include <sys/stat.h>
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce#define SOCKET_CONNECT_RETRY_MSECS 500
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce#define MASTER_AUTH_REQUEST_TIMEOUT_MSECS (MASTER_LOGIN_TIMEOUT_SECS/2*1000)
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorcestruct master_auth_connection {
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce struct master_auth *auth;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce unsigned int tag;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce int fd;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce struct io *io;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce struct timeout *to;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce char buf[sizeof(struct master_auth_reply)];
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce unsigned int buf_pos;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce master_auth_callback_t *callback;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce void *context;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce};
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorcestruct master_auth {
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce struct master_service *service;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce pool_t pool;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce const char *path;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce unsigned int tag_counter;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce HASH_TABLE(void *, struct master_auth_connection *) connections;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce};
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorcestruct master_auth *
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorcemaster_auth_init(struct master_service *service, const char *path)
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce{
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce struct master_auth *auth;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce pool_t pool;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce pool = pool_alloconly_create("master auth", 1024);
6acf7c92ab38ad388295b2d57cc97c4598aa95ccSimo Sorce auth = p_new(pool, struct master_auth, 1);
6acf7c92ab38ad388295b2d57cc97c4598aa95ccSimo Sorce auth->pool = pool;
6acf7c92ab38ad388295b2d57cc97c4598aa95ccSimo Sorce auth->service = service;
6acf7c92ab38ad388295b2d57cc97c4598aa95ccSimo Sorce auth->path = p_strdup(pool, path);
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce hash_table_create_direct(&auth->connections, pool, 0);
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce return auth;
581de96fc30b7fe44070f17a8a73f3374d38d6ffLukas Slebodnik}
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
581de96fc30b7fe44070f17a8a73f3374d38d6ffLukas Slebodnikstatic void
581de96fc30b7fe44070f17a8a73f3374d38d6ffLukas Slebodnikmaster_auth_connection_deinit(struct master_auth_connection **_conn)
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce{
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce struct master_auth_connection *conn = *_conn;
581de96fc30b7fe44070f17a8a73f3374d38d6ffLukas Slebodnik
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce *_conn = NULL;
581de96fc30b7fe44070f17a8a73f3374d38d6ffLukas Slebodnik
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce if (conn->tag != 0)
4869633dc87dadb2b9a114444d375c39703ac863Pavel Březina hash_table_remove(conn->auth->connections,
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce POINTER_CAST(conn->tag));
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce if (conn->callback != NULL)
13df7b9e400211c717284fb841c849ba034ed348Michal Zidek conn->callback(NULL, conn->context);
13df7b9e400211c717284fb841c849ba034ed348Michal Zidek
13df7b9e400211c717284fb841c849ba034ed348Michal Zidek if (conn->to != NULL)
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce timeout_remove(&conn->to);
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce if (conn->io != NULL)
31c47cacc7f9453153e57319474909d23122883fPavel Březina io_remove(&conn->io);
31c47cacc7f9453153e57319474909d23122883fPavel Březina if (conn->fd != -1) {
31c47cacc7f9453153e57319474909d23122883fPavel Březina if (close(conn->fd) < 0)
31c47cacc7f9453153e57319474909d23122883fPavel Březina i_fatal("close(%s) failed: %m", conn->auth->path);
31c47cacc7f9453153e57319474909d23122883fPavel Březina conn->fd = -1;
31c47cacc7f9453153e57319474909d23122883fPavel Březina }
581de96fc30b7fe44070f17a8a73f3374d38d6ffLukas Slebodnik i_free(conn);
225dc6914cdc8920b02a129b98ece1ed97b99c03Lukas Slebodnik}
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
9028706a00da1bc48547e74aa872c825ac15adb2Michal Zidekvoid master_auth_deinit(struct master_auth **_auth)
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce{
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce struct master_auth *auth = *_auth;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce struct hash_iterate_context *iter;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce void *key;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce struct master_auth_connection *conn;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce *_auth = NULL;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce iter = hash_table_iterate_init(auth->connections);
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce while (hash_table_iterate(iter, auth->connections, &key, &conn)) {
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce conn->tag = 0;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce master_auth_connection_deinit(&conn);
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce }
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce hash_table_iterate_deinit(&iter);
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce hash_table_destroy(&auth->connections);
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce pool_unref(&auth->pool);
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce}
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorcestatic void master_auth_connection_input(struct master_auth_connection *conn)
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce{
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce const struct master_auth_reply *reply;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce int ret;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce ret = read(conn->fd, conn->buf + conn->buf_pos,
581de96fc30b7fe44070f17a8a73f3374d38d6ffLukas Slebodnik sizeof(conn->buf) - conn->buf_pos);
581de96fc30b7fe44070f17a8a73f3374d38d6ffLukas Slebodnik if (ret <= 0) {
581de96fc30b7fe44070f17a8a73f3374d38d6ffLukas Slebodnik if (ret == 0 || errno == ECONNRESET) {
581de96fc30b7fe44070f17a8a73f3374d38d6ffLukas Slebodnik i_error("read(%s) failed: Remote closed connection "
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce "(service's process_limit reached?)",
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce conn->auth->path);
581de96fc30b7fe44070f17a8a73f3374d38d6ffLukas Slebodnik } else {
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce if (errno == EAGAIN)
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce return;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce i_error("read(%s) failed: %m", conn->auth->path);
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce }
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce master_auth_connection_deinit(&conn);
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce return;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce }
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce conn->buf_pos += ret;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce if (conn->buf_pos < sizeof(conn->buf))
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce return;
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce /* reply is now read */
c3ef027218fe9a7d16a70ca9d2f53e3d995e369fSimo Sorce reply = (const void *)conn->buf;
c3ef027218fe9a7d16a70ca9d2f53e3d995e369fSimo Sorce conn->buf_pos = 0;
c3ef027218fe9a7d16a70ca9d2f53e3d995e369fSimo Sorce
c3ef027218fe9a7d16a70ca9d2f53e3d995e369fSimo Sorce if (conn->tag != reply->tag)
c3ef027218fe9a7d16a70ca9d2f53e3d995e369fSimo Sorce i_error("master(%s): Received reply with unknown tag %u",
c3ef027218fe9a7d16a70ca9d2f53e3d995e369fSimo Sorce conn->auth->path, reply->tag);
c3ef027218fe9a7d16a70ca9d2f53e3d995e369fSimo Sorce else if (conn->callback == NULL) {
c3ef027218fe9a7d16a70ca9d2f53e3d995e369fSimo Sorce /* request aborted */
c3ef027218fe9a7d16a70ca9d2f53e3d995e369fSimo Sorce } else {
c3ef027218fe9a7d16a70ca9d2f53e3d995e369fSimo Sorce conn->callback(reply, conn->context);
ebf6735dd4f71bf3dc9105e5d04d11e744c64a59Lukas Slebodnik conn->callback = NULL;
ebf6735dd4f71bf3dc9105e5d04d11e744c64a59Lukas Slebodnik }
ebf6735dd4f71bf3dc9105e5d04d11e744c64a59Lukas Slebodnik master_auth_connection_deinit(&conn);
225dc6914cdc8920b02a129b98ece1ed97b99c03Lukas Slebodnik}
225dc6914cdc8920b02a129b98ece1ed97b99c03Lukas Slebodnik
225dc6914cdc8920b02a129b98ece1ed97b99c03Lukas Slebodnikstatic void master_auth_connection_timeout(struct master_auth_connection *conn)
225dc6914cdc8920b02a129b98ece1ed97b99c03Lukas Slebodnik{
39b31427e2d11ca318df11fd48db33a7cc610aa7Lukas Slebodnik i_error("master(%s): Auth request timed out (received %u/%u bytes)",
ebf6735dd4f71bf3dc9105e5d04d11e744c64a59Lukas Slebodnik conn->auth->path, conn->buf_pos,
ebf6735dd4f71bf3dc9105e5d04d11e744c64a59Lukas Slebodnik (unsigned int)sizeof(conn->buf));
ebf6735dd4f71bf3dc9105e5d04d11e744c64a59Lukas Slebodnik master_auth_connection_deinit(&conn);
ebf6735dd4f71bf3dc9105e5d04d11e744c64a59Lukas Slebodnik}
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorcevoid master_auth_request(struct master_auth *auth, int fd,
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce const struct master_auth_request *request,
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce const unsigned char *data,
master_auth_callback_t *callback,
void *context, unsigned int *tag_r)
{
struct master_auth_connection *conn;
struct master_auth_request req;
buffer_t *buf;
struct stat st;
ssize_t ret;
i_assert(request->client_pid != 0);
i_assert(request->auth_pid != 0);
conn = i_new(struct master_auth_connection, 1);
conn->auth = auth;
conn->callback = callback;
conn->context = context;
req = *request;
req.tag = ++auth->tag_counter;
if (req.tag == 0)
req.tag = ++auth->tag_counter;
if (fstat(fd, &st) < 0)
i_fatal("fstat(auth dest fd) failed: %m");
req.ino = st.st_ino;
buf = buffer_create_dynamic(pool_datastack_create(),
sizeof(req) + req.data_size);
buffer_append(buf, &req, sizeof(req));
buffer_append(buf, data, req.data_size);
conn->fd = net_connect_unix_with_retries(auth->path,
SOCKET_CONNECT_RETRY_MSECS);
if (conn->fd == -1) {
i_error("net_connect_unix(%s) failed: %m%s",
auth->path, errno != EAGAIN ? "" :
" - http://wiki2.dovecot.org/SocketUnavailable");
master_auth_connection_deinit(&conn);
return;
}
ret = fd_send(conn->fd, fd, buf->data, buf->used);
if (ret < 0)
i_error("fd_send(%s, %d) failed: %m", auth->path, fd);
else if ((size_t)ret != buf->used) {
i_error("fd_send(%s) sent only %d of %d bytes",
auth->path, (int)ret, (int)buf->used);
ret = -1;
}
if (ret < 0) {
master_auth_connection_deinit(&conn);
return;
}
conn->tag = req.tag;
conn->to = timeout_add(MASTER_AUTH_REQUEST_TIMEOUT_MSECS,
master_auth_connection_timeout, conn);
conn->io = io_add(conn->fd, IO_READ,
master_auth_connection_input, conn);
i_assert(hash_table_lookup(auth->connections, POINTER_CAST(req.tag)) == NULL);
hash_table_insert(auth->connections, POINTER_CAST(req.tag), conn);
*tag_r = req.tag;
}
void master_auth_request_abort(struct master_auth *auth, unsigned int tag)
{
struct master_auth_connection *conn;
conn = hash_table_lookup(auth->connections, POINTER_CAST(tag));
if (conn == NULL)
i_panic("master_auth_request_abort(): tag %u not found", tag);
conn->callback = NULL;
}