Mercurial > dovecot > original-hg > dovecot-1.2
view src/login/auth-connection.c @ 930:fdcc1cab13ff HEAD
Log username with logins. auth/login processes are getting a bit kludgy..
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 09 Jan 2003 14:19:07 +0200 |
parents | ee0b3d18edd4 |
children | 501f076f2e74 |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "common.h" #include "hash.h" #include "ioloop.h" #include "network.h" #include "istream.h" #include "ostream.h" #include "auth-connection.h" #include <unistd.h> #include <dirent.h> #include <sys/stat.h> #define MAX_INBUF_SIZE AUTH_MAX_REQUEST_DATA_SIZE #define MAX_OUTBUF_SIZE \ (sizeof(struct auth_continued_request_data) + \ AUTH_MAX_REQUEST_DATA_SIZE) struct auth_connection { struct auth_connection *next; char *path; int fd; struct io *io; struct istream *input; struct ostream *output; unsigned int auth_process; enum auth_mech available_auth_mechs; struct auth_reply_data in_reply; struct hash_table *requests; unsigned int init_received:1; unsigned int in_reply_received:1; }; enum auth_mech available_auth_mechs; static int auth_reconnect; static unsigned int request_id_counter; static struct auth_connection *auth_connections; static struct timeout *to; static void auth_connection_destroy(struct auth_connection *conn); static void auth_input(void *context, int fd, struct io *io); static void auth_connect_missing(void); static struct auth_connection *auth_connection_find(const char *path) { struct auth_connection *conn; for (conn = auth_connections; conn != NULL; conn = conn->next) { if (strcmp(conn->path, path) == 0) return conn; } return NULL; } static struct auth_connection *auth_connection_new(const char *path) { struct auth_connection *conn; struct client_auth_init_data init_data; int fd; fd = net_connect_unix(path); if (fd == -1) { i_error("Can't connect to imap-auth at %s: %m", path); auth_reconnect = TRUE; return NULL; } conn = i_new(struct auth_connection, 1); conn->path = i_strdup(path); conn->fd = fd; conn->io = io_add(fd, IO_READ, auth_input, conn); conn->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE, IO_PRIORITY_DEFAULT, FALSE); conn->requests = hash_create(default_pool, 100, NULL, NULL); conn->next = auth_connections; auth_connections = conn; /* send our handshake */ memset(&init_data, 0, sizeof(init_data)); init_data.pid = login_process_uid; if (o_stream_send(conn->output, &init_data, sizeof(init_data)) < 0) { auth_connection_destroy(conn); return NULL; } return conn; } static void request_destroy(struct auth_request *request) { hash_remove(request->conn->requests, POINTER_CAST(request->id)); i_free(request); } static void request_abort(struct auth_request *request) { request->callback(request, request->conn->auth_process, AUTH_RESULT_INTERNAL_FAILURE, (const unsigned char *) "Authentication process died", 0, NULL, request->context); request_destroy(request); } static void request_hash_destroy(void *key __attr_unused__, void *value, void *context __attr_unused__) { request_abort(value); } static void auth_connection_destroy(struct auth_connection *conn) { struct auth_connection **pos; for (pos = &auth_connections; *pos != NULL; pos = &(*pos)->next) { if (*pos == conn) { *pos = conn->next; break; } } hash_foreach(conn->requests, request_hash_destroy, NULL); hash_destroy(conn->requests); if (close(conn->fd) < 0) i_error("close(imap-auth) failed: %m"); io_remove(conn->io); i_stream_unref(conn->input); o_stream_unref(conn->output); i_free(conn->path); i_free(conn); } static struct auth_connection * auth_connection_get(enum auth_mech mech, size_t size, const char **error) { struct auth_connection *conn; int found; found = FALSE; for (conn = auth_connections; conn != NULL; conn = conn->next) { if ((conn->available_auth_mechs & mech)) { if (o_stream_have_space(conn->output, size) > 0) return conn; found = TRUE; } } if (!found) { if ((available_auth_mechs & mech) == 0) *error = "Unsupported authentication mechanism"; else { *error = "Authentication server isn't connected, " "try again later.."; auth_reconnect = TRUE; } } else { *error = "Authentication servers are busy, wait.."; i_warning("Authentication servers are busy"); } return NULL; } static void update_available_auth_mechs(void) { struct auth_connection *conn; available_auth_mechs = 0; for (conn = auth_connections; conn != NULL; conn = conn->next) available_auth_mechs |= conn->available_auth_mechs; } static void auth_handle_init(struct auth_connection *conn, struct auth_init_data *init_data) { conn->auth_process = init_data->auth_process; conn->available_auth_mechs = init_data->auth_mechanisms; conn->init_received = TRUE; update_available_auth_mechs(); } static void auth_handle_reply(struct auth_connection *conn, struct auth_reply_data *reply_data, const unsigned char *data) { struct auth_request *request; request = hash_lookup(conn->requests, POINTER_CAST(reply_data->id)); if (request == NULL) { i_error("BUG: imap-auth sent us reply with unknown ID %u", reply_data->id); return; } /* save the returned cookie */ memcpy(request->cookie, reply_data->cookie, AUTH_COOKIE_SIZE); t_push(); request->callback(request, request->conn->auth_process, reply_data->result, data, reply_data->data_size, reply_data->virtual_user, request->context); t_pop(); if (reply_data->result != AUTH_RESULT_CONTINUE) request_destroy(request); } static void auth_input(void *context, int fd __attr_unused__, struct io *io __attr_unused__) { struct auth_connection *conn = context; struct auth_init_data init_data; const unsigned char *data; size_t size; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ auth_reconnect = TRUE; auth_connection_destroy(conn); return; case -2: /* buffer full - can't happen unless imap-auth is buggy */ i_error("BUG: imap-auth sent us more than %d bytes of data", MAX_INBUF_SIZE); auth_connection_destroy(conn); return; } data = i_stream_get_data(conn->input, &size); if (!conn->init_received) { if (size == sizeof(struct auth_init_data)) { memcpy(&init_data, data, sizeof(struct auth_init_data)); i_stream_skip(conn->input, sizeof(struct auth_init_data)); auth_handle_init(conn, &init_data); } else if (size > sizeof(struct auth_init_data)) { i_error("BUG: imap-auth sent us too much " "initialization data (%"PRIuSIZE_T " vs %" PRIuSIZE_T")", size, sizeof(struct auth_init_data)); auth_connection_destroy(conn); } return; } if (!conn->in_reply_received) { data = i_stream_get_data(conn->input, &size); if (size < sizeof(struct auth_reply_data)) return; memcpy(&conn->in_reply, data, sizeof(struct auth_reply_data)); data += sizeof(struct auth_reply_data); size -= sizeof(struct auth_reply_data); i_stream_skip(conn->input, sizeof(struct auth_reply_data)); conn->in_reply_received = TRUE; } if (size < conn->in_reply.data_size) return; /* we've got a full reply */ conn->in_reply_received = FALSE; auth_handle_reply(conn, &conn->in_reply, data); i_stream_skip(conn->input, conn->in_reply.data_size); } int auth_init_request(enum auth_mech mech, AuthCallback callback, void *context, const char **error) { struct auth_connection *conn; struct auth_request *request; struct auth_init_request_data request_data; if (auth_reconnect) auth_connect_missing(); conn = auth_connection_get(mech, sizeof(request_data), error); if (conn == NULL) return FALSE; /* create internal request structure */ request = i_new(struct auth_request, 1); request->mech = mech; request->conn = conn; request->id = ++request_id_counter; request->callback = callback; request->context = context; hash_insert(conn->requests, POINTER_CAST(request->id), request); /* send request to auth */ request_data.type = AUTH_REQUEST_INIT; request_data.mech = request->mech; request_data.id = request->id; if (o_stream_send(request->conn->output, &request_data, sizeof(request_data)) < 0) auth_connection_destroy(request->conn); return TRUE; } void auth_continue_request(struct auth_request *request, const unsigned char *data, size_t data_size) { struct auth_continued_request_data request_data; /* send continued request to auth */ memcpy(request_data.cookie, request->cookie, AUTH_COOKIE_SIZE); request_data.type = AUTH_REQUEST_CONTINUE; request_data.id = request->id; request_data.data_size = data_size; if (o_stream_send(request->conn->output, &request_data, sizeof(request_data)) < 0) auth_connection_destroy(request->conn); else if (o_stream_send(request->conn->output, data, data_size) < 0) auth_connection_destroy(request->conn); } void auth_abort_request(struct auth_request *request) { request_destroy(request); } static void auth_connect_missing(void) { DIR *dirp; struct dirent *dp; struct stat st; auth_reconnect = TRUE; /* we're chrooted into */ dirp = opendir("."); if (dirp == NULL) { i_error("opendir(\".\") failed when trying to get list of " "authentication servers: %m"); return; } while ((dp = readdir(dirp)) != NULL) { if (dp->d_name[0] == '.') continue; if (auth_connection_find(dp->d_name) != NULL) { /* already connected */ continue; } if (stat(dp->d_name, &st) == 0 && S_ISSOCK(st.st_mode)) { if (auth_connection_new(dp->d_name) != NULL) auth_reconnect = FALSE; } } (void)closedir(dirp); } static void auth_connect_missing_timeout(void *context __attr_unused__, struct timeout *timeout __attr_unused__) { if (auth_reconnect) auth_connect_missing(); } void auth_connection_init(void) { auth_connections = NULL; request_id_counter = 0; auth_reconnect = FALSE; auth_connect_missing(); to = timeout_add(1000, auth_connect_missing_timeout, NULL); } void auth_connection_deinit(void) { struct auth_connection *next; while (auth_connections != NULL) { next = auth_connections->next; auth_connection_destroy(auth_connections); auth_connections = next; } timeout_remove(to); }