Mercurial > dovecot > original-hg > dovecot-1.2
diff src/imap-login/client-authenticate.c @ 1049:c41787e8c3f4 HEAD
Moved common login process code to login-common, created pop3-login.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 28 Jan 2003 23:35:25 +0200 |
parents | |
children | f6ec28683512 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/imap-login/client-authenticate.c Tue Jan 28 23:35:25 2003 +0200 @@ -0,0 +1,355 @@ +/* Copyright (C) 2002 Timo Sirainen */ + +#include "common.h" +#include "base64.h" +#include "buffer.h" +#include "ioloop.h" +#include "istream.h" +#include "ostream.h" +#include "safe-memset.h" +#include "str.h" +#include "imap-parser.h" +#include "auth-connection.h" +#include "../auth/auth-mech-desc.h" +#include "client.h" +#include "client-authenticate.h" +#include "master.h" + +static enum auth_mech auth_mechs = 0; +static char *auth_mechs_capability = NULL; + +const char *client_authenticate_get_capabilities(void) +{ + string_t *str; + int i; + + if (auth_mechs == available_auth_mechs) + return auth_mechs_capability; + + auth_mechs = available_auth_mechs; + i_free(auth_mechs_capability); + + str = t_str_new(128); + + for (i = 0; i < AUTH_MECH_COUNT; i++) { + if ((auth_mechs & auth_mech_desc[i].mech) && + auth_mech_desc[i].name != NULL) { + str_append_c(str, ' '); + str_append(str, "AUTH="); + str_append(str, auth_mech_desc[i].name); + } + } + + auth_mechs_capability = i_strdup_empty(str_c(str)); + return auth_mechs_capability; +} + +static struct auth_mech_desc *auth_mech_find(const char *name) +{ + int i; + + for (i = 0; i < AUTH_MECH_COUNT; i++) { + if (auth_mech_desc[i].name != NULL && + strcasecmp(auth_mech_desc[i].name, name) == 0) + return &auth_mech_desc[i]; + } + + return NULL; +} + +static void client_auth_abort(struct imap_client *client, const char *msg) +{ + if (client->auth_request != NULL) { + auth_abort_request(client->auth_request); + client->auth_request = NULL; + } + + client_send_tagline(client, msg != NULL ? msg : + "NO Authentication failed."); + o_stream_flush(client->output); + + /* get back to normal client input */ + if (client->io != NULL) + io_remove(client->io); + client->io = client->common.fd == -1 ? NULL : + io_add(client->common.fd, IO_READ, client_input, client); + + client_unref(client); +} + +static void master_callback(struct client *_client, int success) +{ + struct imap_client *client = (struct imap_client *) _client; + const char *reason = NULL; + + if (success) + reason = t_strconcat("Login: ", client->virtual_user, NULL); + else { + reason = t_strconcat("Internal login failure: ", + client->virtual_user, NULL); + client_send_line(client, "* BYE Internal login failure."); + } + + client_destroy(client, reason); + client_unref(client); +} + +static void client_send_auth_data(struct imap_client *client, + const unsigned char *data, size_t size) +{ + buffer_t *buf; + + t_push(); + + buf = buffer_create_dynamic(data_stack_pool, size*2, (size_t)-1); + buffer_append(buf, "+ ", 2); + base64_encode(data, size, buf); + buffer_append(buf, "\r\n", 2); + + o_stream_send(client->output, buffer_get_data(buf, NULL), + buffer_get_used_size(buf)); + o_stream_flush(client->output); + + t_pop(); +} + +static const char *auth_login_get_str(struct auth_login_reply *reply, + const unsigned char *data, size_t idx) +{ + size_t stop; + + if (idx >= reply->data_size || idx >= reply->reply_idx) + return NULL; + + stop = reply->reply_idx < reply->data_size ? + reply->reply_idx-1 : reply->data_size; + + return t_strndup(data, stop); +} + +static int auth_callback(struct auth_request *request, + struct auth_login_reply *reply, + const unsigned char *data, void *context) +{ + struct imap_client *client = context; + const char *user, *realm; + + if (reply == NULL) { + /* failed */ + client->auth_request = NULL; + client_auth_abort(client, "NO Authentication process died."); + return FALSE; + } + + switch (reply->result) { + case AUTH_LOGIN_RESULT_CONTINUE: + client->auth_request = request; + return TRUE; + + case AUTH_LOGIN_RESULT_SUCCESS: + client->auth_request = NULL; + + user = auth_login_get_str(reply, data, reply->username_idx); + realm = auth_login_get_str(reply, data, reply->realm_idx); + + i_free(client->virtual_user); + client->virtual_user = realm == NULL ? + i_strdup(user) : i_strconcat(user, "@", realm, NULL); + + /* we should be able to log in. if we fail, just + disconnect the client. */ + client_send_tagline(client, "OK Logged in."); + + master_request_imap(&client->common, master_callback, + request->conn->pid, request->id); + + /* disable IO until we're back from master */ + if (client->io != NULL) { + io_remove(client->io); + client->io = NULL; + } + return FALSE; + + case AUTH_LOGIN_RESULT_FAILURE: + /* see if we have error message */ + client->auth_request = NULL; + + if (reply->data_size > 0 && data[reply->data_size-1] == '\0') { + client_auth_abort(client, t_strconcat( + "NO Authentication failed: ", + (const char *) data, NULL)); + } else { + /* default error message */ + client_auth_abort(client, NULL); + } + return FALSE; + } + + i_unreached(); +} + +static void login_callback(struct auth_request *request, + struct auth_login_reply *reply, + const unsigned char *data, struct client *_client) +{ + struct imap_client *client = (struct imap_client *) _client; + const void *ptr; + size_t size; + + if (auth_callback(request, reply, data, client)) { + ptr = buffer_get_data(client->plain_login, &size); + auth_continue_request(request, ptr, size); + + buffer_set_used_size(client->plain_login, 0); + } +} + +int cmd_login(struct imap_client *client, struct imap_arg *args) +{ + const char *user, *pass, *error; + + /* two arguments: username and password */ + if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING) + return FALSE; + if (args[1].type != IMAP_ARG_ATOM && args[1].type != IMAP_ARG_STRING) + return FALSE; + if (args[2].type != IMAP_ARG_EOL) + return FALSE; + + user = IMAP_ARG_STR(&args[0]); + pass = IMAP_ARG_STR(&args[1]); + + if (!client->tls && disable_plaintext_auth) { + client_send_tagline(client, + "NO Plaintext authentication disabled."); + return TRUE; + } + + /* authorization ID \0 authentication ID \0 pass */ + buffer_set_used_size(client->plain_login, 0); + buffer_append_c(client->plain_login, '\0'); + buffer_append(client->plain_login, user, strlen(user)); + buffer_append_c(client->plain_login, '\0'); + buffer_append(client->plain_login, pass, strlen(pass)); + + client_ref(client); + if (auth_init_request(AUTH_MECH_PLAIN, login_callback, + &client->common, &error)) { + /* don't read any input from client until login is finished */ + if (client->io != NULL) { + io_remove(client->io); + client->io = NULL; + } + return TRUE; + } else { + client_send_tagline(client, t_strconcat( + "NO Login failed: ", error, NULL)); + client_unref(client); + return TRUE; + } +} + +static void authenticate_callback(struct auth_request *request, + struct auth_login_reply *reply, + const unsigned char *data, + struct client *_client) +{ + struct imap_client *client = (struct imap_client *) _client; + + if (auth_callback(request, reply, data, client)) + client_send_auth_data(client, data, reply->data_size); +} + +static void client_auth_input(void *context) +{ + struct imap_client *client = context; + buffer_t *buf; + char *line; + size_t linelen, bufsize; + + if (!client_read(client)) + return; + + if (client->skip_line) { + if (i_stream_next_line(client->input) == NULL) + return; + + client->skip_line = FALSE; + } + + /* @UNSAFE */ + line = i_stream_next_line(client->input); + if (line == NULL) + return; + + if (strcmp(line, "*") == 0) { + client_auth_abort(client, "NO Authentication aborted"); + return; + } + + linelen = strlen(line); + buf = buffer_create_static_hard(data_stack_pool, linelen); + + if (base64_decode((const unsigned char *) line, linelen, + NULL, buf) <= 0) { + /* failed */ + client_auth_abort(client, "NO Invalid base64 data"); + } else if (client->auth_request == NULL) { + client_auth_abort(client, "NO Don't send unrequested data"); + } else { + auth_continue_request(client->auth_request, + buffer_get_data(buf, NULL), + buffer_get_used_size(buf)); + } + + /* clear sensitive data */ + safe_memset(line, 0, linelen); + + bufsize = buffer_get_used_size(buf); + safe_memset(buffer_free_without_data(buf), 0, bufsize); +} + +int cmd_authenticate(struct imap_client *client, struct imap_arg *args) +{ + struct auth_mech_desc *mech; + const char *mech_name, *error; + + /* we want only one argument: authentication mechanism name */ + if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING) + return FALSE; + if (args[1].type != IMAP_ARG_EOL) + return FALSE; + + mech_name = IMAP_ARG_STR(&args[0]); + if (*mech_name == '\0') + return FALSE; + + mech = auth_mech_find(mech_name); + if (mech == NULL) { + client_send_tagline(client, + "NO Unsupported authentication mechanism."); + return TRUE; + } + + if (!client->tls && mech->plaintext && disable_plaintext_auth) { + client_send_tagline(client, + "NO Plaintext authentication disabled."); + return TRUE; + } + + client_ref(client); + if (auth_init_request(mech->mech, authenticate_callback, + &client->common, &error)) { + /* following input data will go to authentication */ + if (client->io != NULL) + io_remove(client->io); + client->io = io_add(client->common.fd, IO_READ, + client_auth_input, client); + } else { + client_send_tagline(client, t_strconcat( + "NO Authentication failed: ", error, NULL)); + client_unref(client); + } + + return TRUE; +}