auth-worker-client.c revision 8eb94c5190ba09bb6f6f068eec7bf96750f08d1d
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (C) 2005 Timo Sirainen */
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#include "common.h"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#include "ioloop.h"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#include "network.h"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#include "istream.h"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#include "ostream.h"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#include "str.h"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#include "auth-request.h"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#include "auth-worker-client.h"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#include <stdlib.h>
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen/* If no requests have come within this time, kill ourself */
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen#define AUTH_WORKER_MAX_IDLE (60*10)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#define OUTBUF_THROTTLE_SIZE (1024*10)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
6ded8819b9002150a95a7615e4f64f091c250464Timo Sirainenstruct auth_worker_client {
6ded8819b9002150a95a7615e4f64f091c250464Timo Sirainen int refcount;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen struct auth *auth;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen int fd;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen struct io *io;
2f8da04d700cc23fcd6630226a4866e828b761bdTimo Sirainen struct istream *input;
2f8da04d700cc23fcd6630226a4866e828b761bdTimo Sirainen struct ostream *output;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen time_t last_request;
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen struct timeout *to;
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen};
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainenstatic void
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainenauth_worker_client_check_throttle(struct auth_worker_client *client)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen{
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (o_stream_get_buffer_used_size(client->output) >=
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen OUTBUF_THROTTLE_SIZE) {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen /* stop reading new requests until client has read the pending
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen replies. */
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (client->io != NULL)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen io_remove(&client->io);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen }
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen}
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainenstatic struct auth_request *
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainenworker_auth_request_new(struct auth_worker_client *client, unsigned int id,
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen const char *args)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen{
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen struct auth_request *auth_request;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen const char *key, *value, *const *tmp;
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen auth_request = auth_request_new_dummy(client->auth);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen client->refcount++;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen auth_request->context = client;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen auth_request->id = id;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen t_push();
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen for (tmp = t_strsplit(args, "\t"); *tmp != NULL; tmp++) {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen value = strchr(*tmp, '=');
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (value == NULL)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen continue;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen key = t_strdup_until(*tmp, value);
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen value++;
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen (void)auth_request_import(auth_request, key, value);
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen }
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen t_pop();
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen return auth_request;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen}
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainenstatic void verify_plain_callback(enum passdb_result result,
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen struct auth_request *request)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen{
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen struct auth_worker_client *client = request->context;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen string_t *str;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str = t_str_new(64);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str_printfa(str, "%u\t", request->id);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (result != PASSDB_RESULT_OK)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str_printfa(str, "FAIL\t%d", result);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen else {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str_append(str, "OK\t");
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str_append(str, request->user);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str_append_c(str, '\t');
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (request->passdb_password != NULL)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str_append(str, request->passdb_password);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str_append_c(str, '\t');
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (request->no_password)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str_append(str, "nopassword\t");
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (request->extra_fields != NULL) {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen const char *field =
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen auth_stream_reply_export(request->extra_fields);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str_append(str, field);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen }
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen }
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str_append_c(str, '\n');
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen o_stream_send(client->output, str_data(str), str_len(str));
082e82792b8ac33ad42beac510441b37a3c50737Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen auth_request_unref(&request);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen auth_worker_client_check_throttle(client);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen auth_worker_client_unref(&client);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen}
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainenstatic void
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainenauth_worker_handle_passv(struct auth_worker_client *client,
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen unsigned int id, const char *args)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen{
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen /* verify plaintext password */
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen struct auth_request *auth_request;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen const char *password;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen unsigned int passdb_id;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen passdb_id = atoi(t_strcut(args, '\t'));
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen args = strchr(args, '\t');
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (args == NULL) {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen i_error("BUG: Auth worker server sent us invalid PASSV");
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen return;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen }
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen args++;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen password = t_strcut(args, '\t');
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen args = strchr(args, '\t');
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (args != NULL) args++;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen auth_request = worker_auth_request_new(client, id, args);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen auth_request->mech_password =
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen p_strdup(auth_request->pool, password);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (auth_request->user == NULL || auth_request->service == NULL) {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen i_error("BUG: PASSV had missing parameters");
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen auth_request_unref(&auth_request);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen return;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen }
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen while (auth_request->passdb->id != passdb_id) {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen auth_request->passdb = auth_request->passdb->next;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (auth_request->passdb == NULL) {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen i_error("BUG: PASSV had invalid passdb ID");
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen auth_request_unref(&auth_request);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen return;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen }
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen }
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen auth_request->passdb->passdb->iface.
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen verify_plain(auth_request, password, verify_plain_callback);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen}
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainenstatic void
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainenlookup_credentials_callback(enum passdb_result result, const char *credentials,
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen struct auth_request *request)
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen{
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen struct auth_worker_client *client = request->context;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen string_t *str;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str = t_str_new(64);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str_printfa(str, "%u\t", request->id);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
8fcf84e6b7a55049a6d407b17af50d6ae8c6386fPhil Carmody if (result != PASSDB_RESULT_OK)
8fcf84e6b7a55049a6d407b17af50d6ae8c6386fPhil Carmody str_printfa(str, "FAIL\t%d", result);
8fcf84e6b7a55049a6d407b17af50d6ae8c6386fPhil Carmody else {
8fcf84e6b7a55049a6d407b17af50d6ae8c6386fPhil Carmody str_printfa(str, "OK\t%s\t{%s}%s\t", request->user,
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen passdb_credentials_to_str(request->credentials),
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen credentials);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (request->extra_fields != NULL) {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen const char *field =
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen auth_stream_reply_export(request->extra_fields);
baf3e87e186453fda13bd21f7cbcb2efc8492e8bTimo Sirainen str_append(str, field);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen }
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen }
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen str_append_c(str, '\n');
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen o_stream_send(client->output, str_data(str), str_len(str));
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
auth_request_unref(&request);
auth_worker_client_check_throttle(client);
auth_worker_client_unref(&client);
}
static void
auth_worker_handle_passl(struct auth_worker_client *client,
unsigned int id, const char *args)
{
/* lookup credentials */
struct auth_request *auth_request;
const char *credentials_str;
enum passdb_credentials credentials;
unsigned int num;
num = atoi(t_strcut(args, '\t'));
args = strchr(args, '\t');
if (args == NULL) {
i_error("BUG: Auth worker server sent us invalid PASSL");
return;
}
args++;
credentials_str = t_strcut(args, '\t');
args = strchr(args, '\t');
if (args != NULL) args++;
credentials = atoi(credentials_str);
auth_request = worker_auth_request_new(client, id, args);
auth_request->credentials = credentials;
if (auth_request->user == NULL || auth_request->service == NULL) {
i_error("BUG: PASSL had missing parameters");
auth_request_unref(&auth_request);
return;
}
for (; num > 0; num--) {
auth_request->passdb = auth_request->passdb->next;
if (auth_request->passdb == NULL) {
i_error("BUG: PASSL had invalid passdb num");
auth_request_unref(&auth_request);
return;
}
}
auth_request->passdb->passdb->iface.
lookup_credentials(auth_request, lookup_credentials_callback);
}
static void
lookup_user_callback(struct auth_stream_reply *reply,
struct auth_request *auth_request)
{
struct auth_worker_client *client = auth_request->context;
string_t *str;
str = t_str_new(64);
str_printfa(str, "%u\t", auth_request->id);
if (reply != NULL)
str_append(str, auth_stream_reply_export(reply));
str_append_c(str, '\n');
o_stream_send(client->output, str_data(str), str_len(str));
auth_request_unref(&auth_request);
auth_worker_client_check_throttle(client);
auth_worker_client_unref(&client);
}
static void
auth_worker_handle_user(struct auth_worker_client *client,
unsigned int id, const char *args)
{
/* lookup user */
struct auth_request *auth_request;
unsigned int num;
num = atoi(t_strcut(args, '\t'));
args = strchr(args, '\t');
if (args != NULL) args++;
auth_request = worker_auth_request_new(client, id, args);
if (auth_request->user == NULL || auth_request->service == NULL) {
i_error("BUG: USER had missing parameters");
auth_request_unref(&auth_request);
return;
}
for (; num > 0; num--) {
auth_request->userdb = auth_request->userdb->next;
if (auth_request->userdb == NULL) {
i_error("BUG: USER had invalid userdb num");
auth_request_unref(&auth_request);
return;
}
}
auth_request->userdb->userdb->iface->
lookup(auth_request, lookup_user_callback);
}
static bool
auth_worker_handle_line(struct auth_worker_client *client, const char *line)
{
const char *p;
unsigned int id;
p = strchr(line, '\t');
if (p == NULL)
return FALSE;
id = (unsigned int)strtoul(t_strdup_until(line, p), NULL, 10);
line = p + 1;
if (strncmp(line, "PASSV\t", 6) == 0)
auth_worker_handle_passv(client, id, line + 6);
else if (strncmp(line, "PASSL\t", 6) == 0)
auth_worker_handle_passl(client, id, line + 6);
else if (strncmp(line, "USER\t", 5) == 0)
auth_worker_handle_user(client, id, line + 5);
return TRUE;
}
static void auth_worker_input(void *context)
{
struct auth_worker_client *client = context;
char *line;
bool ret;
switch (i_stream_read(client->input)) {
case 0:
return;
case -1:
/* disconnected */
auth_worker_client_destroy(&client);
return;
case -2:
/* buffer full */
i_error("BUG: Auth worker server sent us more than %d bytes",
(int)AUTH_WORKER_MAX_LINE_LENGTH);
auth_worker_client_destroy(&client);
return;
}
client->last_request = ioloop_time;
client->refcount++;
while ((line = i_stream_next_line(client->input)) != NULL) {
t_push();
ret = auth_worker_handle_line(client, line);
t_pop();
if (!ret) {
auth_worker_client_destroy(&client);
break;
}
}
auth_worker_client_unref(&client);
}
static int auth_worker_output(void *context)
{
struct auth_worker_client *client = context;
if (o_stream_flush(client->output) < 0) {
auth_worker_client_destroy(&client);
return 1;
}
if (o_stream_get_buffer_used_size(client->output) <=
OUTBUF_THROTTLE_SIZE/3 && client->io == NULL) {
/* allow input again */
client->io = io_add(client->fd, IO_READ,
auth_worker_input, client);
}
return 1;
}
static void auth_worker_client_timeout(void *context)
{
struct auth_worker_client *client = context;
if (client->last_request + AUTH_WORKER_MAX_IDLE <= ioloop_time)
auth_worker_client_destroy(&client);
}
struct auth_worker_client *
auth_worker_client_create(struct auth *auth, int fd)
{
struct auth_worker_client *client;
client = i_new(struct auth_worker_client, 1);
client->refcount = 1;
client->auth = auth;
client->fd = fd;
client->input =
i_stream_create_file(fd, default_pool,
AUTH_WORKER_MAX_LINE_LENGTH, FALSE);
client->output =
o_stream_create_file(fd, default_pool, (size_t)-1, FALSE);
o_stream_set_flush_callback(client->output, auth_worker_output, client);
client->io = io_add(fd, IO_READ, auth_worker_input, client);
client->last_request = ioloop_time;
client->to = timeout_add(1000*60, auth_worker_client_timeout, client);
return client;
}
void auth_worker_client_destroy(struct auth_worker_client **_client)
{
struct auth_worker_client *client = *_client;
*_client = NULL;
if (client->fd == -1)
return;
timeout_remove(&client->to);
i_stream_close(client->input);
o_stream_close(client->output);
if (client->io != NULL)
io_remove(&client->io);
net_disconnect(client->fd);
client->fd = -1;
io_loop_stop(ioloop);
}
void auth_worker_client_unref(struct auth_worker_client **_client)
{
struct auth_worker_client *client = *_client;
if (--client->refcount > 0) {
*_client = NULL;
return;
}
if (client->fd != -1)
auth_worker_client_destroy(_client);
i_stream_unref(&client->input);
o_stream_unref(&client->output);
i_free(client);
}