Mercurial > dovecot > original-hg > dovecot-1.2
view src/login/client-authenticate.c @ 804:bf38c8f30a4c HEAD
Added safe_memset() which guarantees that compiler optimizations don't
optimize it away. Not that we really need to clear the passwords from
memory, but won't hurt much either :)
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 18 Dec 2002 12:40:43 +0200 |
parents | 553f050c8313 |
children | 5ac361acb316 |
line wrap: on
line source
/* 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 "temp-string.h" #include "auth-connection.h" #include "client.h" #include "client-authenticate.h" #include "master.h" typedef struct { int method; const char *name; int plaintext; } AuthMethodDesc; static AuthMethod auth_methods = 0; static char *auth_methods_capability = NULL; static AuthMethodDesc auth_method_desc[AUTH_METHODS_COUNT] = { { AUTH_METHOD_PLAIN, NULL, TRUE }, { AUTH_METHOD_DIGEST_MD5, "DIGEST-MD5", FALSE } }; const char *client_authenticate_get_capabilities(void) { TempString *str; int i; if (auth_methods == available_auth_methods) return auth_methods_capability; auth_methods = available_auth_methods; i_free(auth_methods_capability); str = t_string_new(128); t_string_append_c(str, ' '); for (i = 0; i < AUTH_METHODS_COUNT; i++) { if ((auth_methods & auth_method_desc[i].method) && auth_method_desc[i].name != NULL) { if (str->len > 0) t_string_append_c(str, ' '); t_string_append(str, "AUTH="); t_string_append(str, auth_method_desc[i].name); } } auth_methods_capability = str->len == 1 ? NULL : i_strdup_empty(str->str); return auth_methods_capability; } static AuthMethodDesc *auth_method_find(const char *name) { int i; for (i = 0; i < AUTH_METHODS_COUNT; i++) { if (auth_method_desc[i].name != NULL && strcasecmp(auth_method_desc[i].name, name) == 0) return &auth_method_desc[i]; } return NULL; } static void client_auth_abort(Client *client, const char *msg) { 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->fd == -1 ? NULL : io_add(client->fd, IO_READ, client_input, client); client_unref(client); } static void master_callback(MasterReplyResult result, void *context) { Client *client = context; switch (result) { case MASTER_RESULT_SUCCESS: client_destroy(client, "Logged in."); client_unref(client); break; case MASTER_RESULT_INTERNAL_FAILURE: client_auth_abort(client, "Internal failure"); break; default: client_auth_abort(client, NULL); break; } } static void client_send_auth_data(Client *client, const unsigned char *data, size_t size) { Buffer *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 int auth_callback(AuthRequest *request, int auth_process, AuthResult result, const unsigned char *reply_data, size_t reply_data_size, void *context) { Client *client = context; switch (result) { case AUTH_RESULT_CONTINUE: client->auth_request = request; return TRUE; case AUTH_RESULT_SUCCESS: client->auth_request = NULL; master_request_imap(client->fd, auth_process, client->tag, request->cookie, &client->ip, master_callback, client); /* disable IO until we're back from master */ if (client->io != NULL) { io_remove(client->io); client->io = NULL; } return FALSE; case AUTH_RESULT_FAILURE: /* see if we have error message */ if (reply_data_size > 0 && reply_data[reply_data_size-1] == '\0') { client_auth_abort(client, t_strconcat( "NO Authentication failed: ", (const char *) reply_data, NULL)); } else { /* default error message */ client_auth_abort(client, NULL); } return FALSE; default: client_auth_abort(client, t_strconcat( "NO Authentication failed: ", reply_data, NULL)); return FALSE; } } static void login_callback(AuthRequest *request, int auth_process, AuthResult result, const unsigned char *reply_data, size_t reply_data_size, void *context) { Client *client = context; const void *ptr; size_t size; if (auth_callback(request, auth_process, result, reply_data, reply_data_size, context)) { 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(Client *client, const char *user, const char *pass) { const char *error; if (!client->tls && disable_plaintext_auth) { client_send_tagline(client, "NO Plaintext authentication disabled."); return TRUE; } /* code it into user\0user\0password */ buffer_set_used_size(client->plain_login, 0); buffer_append(client->plain_login, user, strlen(user)); 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_METHOD_PLAIN, login_callback, client, &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(AuthRequest *request, int auth_process, AuthResult result, const unsigned char *reply_data, size_t reply_data_size, void *context) { Client *client = context; if (auth_callback(request, auth_process, result, reply_data, reply_data_size, context)) client_send_auth_data(client, reply_data, reply_data_size); } static void client_auth_input(void *context, int fd __attr_unused__, IO io __attr_unused__) { Client *client = context; Buffer *buf; char *line; size_t linelen, bufsize; if (!client_read(client)) return; line = i_stream_next_line(client->input); if (line == NULL) return; if (strcmp(line, "*") == 0) { client_auth_abort(client, "NO Authentication aborted"); return; } t_push(); linelen = strlen(line); buf = buffer_create_static_hard(data_stack_pool, linelen); if (base64_decode(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); t_pop(); } int cmd_authenticate(Client *client, const char *method_name) { AuthMethodDesc *method; const char *error; if (*method_name == '\0') return FALSE; method = auth_method_find(method_name); if (method == NULL) { client_send_tagline(client, "NO Unsupported authentication method."); return TRUE; } if (!client->tls && method->plaintext && disable_plaintext_auth) { client_send_tagline(client, "NO Plaintext authentication disabled."); return TRUE; } client_ref(client); if (auth_init_request(method->method, authenticate_callback, client, &error)) { /* following input data will go to authentication */ if (client->io != NULL) io_remove(client->io); client->io = io_add(client->fd, IO_READ, client_auth_input, client); } else { client_send_tagline(client, t_strconcat( "NO Authentication failed: ", error, NULL)); client_unref(client); } return TRUE; }