Mercurial > dovecot > core-2.2
changeset 2027:dc5d0da1abe9 HEAD
Added ssl_require_client_cert auth-specific setting. Hide
ssl_verify_client_cert from default config file as it's automatically set if
needed and there's not much point in forcing it.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 17 May 2004 04:32:16 +0300 |
parents | 53585aa87f9c |
children | ec22548b6124 |
files | dovecot-example.conf src/auth/auth-client-interface.h src/auth/mech.c src/auth/mech.h src/imap-login/client-authenticate.c src/imap-login/client.c src/lib-auth/auth-client.h src/lib-auth/auth-server-request.c src/login-common/client-common.h src/login-common/main.c src/login-common/ssl-proxy-openssl.c src/login-common/ssl-proxy.h src/master/auth-process.c src/master/master-settings.c src/master/master-settings.h src/pop3-login/client-authenticate.c src/pop3-login/client.c |
diffstat | 17 files changed, 144 insertions(+), 29 deletions(-) [+] |
line wrap: on
line diff
--- a/dovecot-example.conf Mon May 17 02:29:27 2004 +0300 +++ b/dovecot-example.conf Mon May 17 04:32:16 2004 +0300 @@ -37,7 +37,7 @@ # File containing trusted SSL certificate authorities. Usually not needed. #ssl_ca_file = -# Require client to send a valid certificate, otherwise fail the SSL handshake. +# Request client to send a certificate. #ssl_verify_client_cert = no # SSL parameter file. Master process generates this file for login processes. @@ -312,10 +312,9 @@ #umask = 0077 # Drop all privileges before exec()ing the mail process. This is mostly -# meant for debugging, otherwise you don't get core dumps. Note that setting -# this to yes means that log file is opened as the logged in user, which -# might not work. It could also be a small security risk if you use single UID -# for multiple users, as the users could ptrace() each others processes then. +# meant for debugging, otherwise you don't get core dumps. It could be a small +# security risk if you use single UID for multiple users, as the users could +# ptrace() each others processes then. #mail_drop_priv_before_exec = no # Set max. process size in megabytes. Most of the memory goes to mmap()ing @@ -437,6 +436,9 @@ # Number of authentication processes to create #count = 1 + + # Require a valid SSL client certificate or the authentication fails. + #ssl_require_client_cert = no } # PAM doesn't provide a way to get uid, gid or home directory. If you don't
--- a/src/auth/auth-client-interface.h Mon May 17 02:29:27 2004 +0300 +++ b/src/auth/auth-client-interface.h Mon May 17 04:32:16 2004 +0300 @@ -22,6 +22,10 @@ AUTH_PROTOCOL_POP3 = 0x02 }; +enum auth_client_request_new_flags { + AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT = 0x01 +}; + enum auth_client_request_type { AUTH_CLIENT_REQUEST_NEW = 1, AUTH_CLIENT_REQUEST_CONTINUE @@ -51,6 +55,7 @@ enum auth_mech mech; enum auth_protocol protocol; + enum auth_client_request_new_flags flags; }; /* Continue authentication request */
--- a/src/auth/mech.c Mon May 17 02:29:27 2004 +0300 +++ b/src/auth/mech.c Mon May 17 04:32:16 2004 +0300 @@ -22,6 +22,7 @@ char username_chars[256]; static int set_use_cyrus_sasl; +static int ssl_require_client_cert; static struct mech_module_list *mech_modules; static struct auth_client_request_reply failure_reply; @@ -75,6 +76,16 @@ return; } + if (ssl_require_client_cert && + (request->flags & AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT) == 0) { + /* we fail without valid certificate */ + if (verbose) + i_info("Client didn't present valid SSL certificate"); + failure_reply.id = request->id; + callback(&failure_reply, NULL, conn); + return; + } + #ifdef USE_CYRUS_SASL2 if (set_use_cyrus_sasl) { auth_request = mech_cyrus_sasl_new(conn, request, callback); @@ -291,6 +302,7 @@ if (set_use_cyrus_sasl) mech_cyrus_sasl_init_lib(); #endif + ssl_require_client_cert = getenv("SSL_REQUIRE_CLIENT_CERT") != NULL; } void mech_deinit(void)
--- a/src/auth/mech.h Mon May 17 02:29:27 2004 +0300 +++ b/src/auth/mech.h Mon May 17 04:32:16 2004 +0300 @@ -43,6 +43,7 @@ extern const char *default_realm; extern const char *anonymous_username; extern char username_chars[256]; +extern int ssl_require_client_cert; void mech_register_module(struct mech_module *module); void mech_unregister_module(struct mech_module *module);
--- a/src/imap-login/client-authenticate.c Mon May 17 02:29:27 2004 +0300 +++ b/src/imap-login/client-authenticate.c Mon May 17 04:32:16 2004 +0300 @@ -11,6 +11,7 @@ #include "imap-parser.h" #include "auth-client.h" #include "../auth/auth-mech-desc.h" +#include "ssl-proxy.h" #include "client.h" #include "client-authenticate.h" #include "auth-common.h" @@ -160,6 +161,17 @@ } } +static enum auth_client_request_new_flags +client_get_auth_flags(struct imap_client *client) +{ + enum auth_client_request_new_flags auth_flags = 0; + + if (client->common.proxy != NULL && + ssl_proxy_has_valid_client_cert(client->common.proxy)) + auth_flags |= AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT; + return auth_flags; +} + int cmd_login(struct imap_client *client, struct imap_arg *args) { const char *user, *pass, *error; @@ -193,10 +205,12 @@ buffer_append(client->plain_login, pass, strlen(pass)); client_ref(client); + client->common.auth_request = auth_client_request_new(auth_client, AUTH_MECH_PLAIN, - AUTH_PROTOCOL_IMAP, login_callback, - client, &error); + AUTH_PROTOCOL_IMAP, + client_get_auth_flags(client), + login_callback, client, &error); if (client->common.auth_request == NULL) { client_send_tagline(client, t_strconcat( "NO Login failed: ", error, NULL)); @@ -324,6 +338,7 @@ client->common.auth_request = auth_client_request_new(auth_client, mech->mech, AUTH_PROTOCOL_IMAP, + client_get_auth_flags(client), authenticate_callback, client, &error); if (client->common.auth_request != NULL) {
--- a/src/imap-login/client.c Mon May 17 02:29:27 2004 +0300 +++ b/src/imap-login/client.c Mon May 17 04:32:16 2004 +0300 @@ -124,7 +124,8 @@ client->common.io = NULL; } - fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip); + fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip, + &client->common.proxy); if (fd_ssl != -1) { client->tls = TRUE; client->secured = TRUE; @@ -421,6 +422,8 @@ client->common.fd = -1; } + if (client->common.proxy != NULL) + ssl_proxy_free(client->common.proxy); client_unref(client); }
--- a/src/lib-auth/auth-client.h Mon May 17 02:29:27 2004 +0300 +++ b/src/lib-auth/auth-client.h Mon May 17 04:32:16 2004 +0300 @@ -31,6 +31,7 @@ struct auth_request * auth_client_request_new(struct auth_client *client, enum auth_mech mech, enum auth_protocol protocol, + enum auth_client_request_new_flags flags, auth_request_callback_t *callback, void *context, const char **error_r);
--- a/src/lib-auth/auth-server-request.c Mon May 17 02:29:27 2004 +0300 +++ b/src/lib-auth/auth-server-request.c Mon May 17 04:32:16 2004 +0300 @@ -11,7 +11,8 @@ struct auth_server_connection *conn; enum auth_mech mech; - enum auth_protocol protocol; + enum auth_protocol protocol; + enum auth_client_request_new_flags flags; unsigned int id; @@ -35,6 +36,7 @@ auth_request.id = request->id; auth_request.protocol = request->protocol; auth_request.mech = request->mech; + auth_request.flags = request->flags; if (o_stream_send(conn->output, &auth_request, sizeof(auth_request)) < 0) { @@ -177,6 +179,7 @@ struct auth_request * auth_client_request_new(struct auth_client *client, enum auth_mech mech, enum auth_protocol protocol, + enum auth_client_request_new_flags flags, auth_request_callback_t *callback, void *context, const char **error_r) { @@ -191,6 +194,7 @@ request->conn = conn; request->mech = mech; request->protocol = protocol; + request->flags = flags; request->id = ++client->request_id_counter; if (request->id == 0) { /* wrapped - ID 0 not allowed */
--- a/src/login-common/client-common.h Mon May 17 02:29:27 2004 +0300 +++ b/src/login-common/client-common.h Mon May 17 04:32:16 2004 +0300 @@ -6,6 +6,7 @@ struct client { struct ip_addr ip; + struct ssl_proxy *proxy; int fd; struct io *io;
--- a/src/login-common/main.c Mon May 17 02:29:27 2004 +0300 +++ b/src/login-common/main.c Mon May 17 04:32:16 2004 +0300 @@ -95,6 +95,8 @@ static void login_accept_ssl(void *context __attr_unused__) { struct ip_addr ip; + struct client *client; + struct ssl_proxy *proxy; int fd, fd_ssl; fd = net_accept(LOGIN_SSL_LISTEN_FD, &ip, NULL); @@ -107,11 +109,13 @@ if (process_per_connection) main_close_listen(); - fd_ssl = ssl_proxy_new(fd, &ip); + fd_ssl = ssl_proxy_new(fd, &ip, &proxy); if (fd_ssl == -1) net_disconnect(fd); - else - (void)client_create(fd_ssl, &ip, TRUE); + else { + client = client_create(fd_ssl, &ip, TRUE); + client->proxy = proxy; + } } static void auth_connect_notify(struct auth_client *client __attr_unused__, @@ -213,6 +217,8 @@ { const char *name, *group_name; struct ip_addr ip; + struct ssl_proxy *proxy = NULL; + struct client *client; int i, fd = -1, master_fd = -1; is_inetd = getenv("DOVECOT_MASTER") == NULL; @@ -258,7 +264,7 @@ fd = 1; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--ssl") == 0) { - fd = ssl_proxy_new(fd, &ip); + fd = ssl_proxy_new(fd, &ip, &proxy); if (fd == -1) i_fatal("SSL initialization failed"); } else if (strncmp(argv[i], "--group=", 8) != 0) @@ -269,8 +275,10 @@ closing_down = TRUE; } - if (fd != -1) - (void)client_create(fd, &ip, TRUE); + if (fd != -1) { + client = client_create(fd, &ip, TRUE); + client->proxy = proxy; + } io_loop_run(ioloop); main_deinit();
--- a/src/login-common/ssl-proxy-openssl.c Mon May 17 02:29:27 2004 +0300 +++ b/src/login-common/ssl-proxy-openssl.c Mon May 17 04:32:16 2004 +0300 @@ -41,8 +41,11 @@ unsigned int handshaked:1; unsigned int destroyed:1; + unsigned int cert_received:1; + unsigned int cert_broken:1; }; +static int extdata_index; static SSL_CTX *ssl_ctx; static struct hash_table *ssl_proxies; @@ -308,12 +311,14 @@ ssl_proxy_unref(proxy); } -int ssl_proxy_new(int fd, struct ip_addr *ip) +int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r) { struct ssl_proxy *proxy; SSL *ssl; int sfd[2]; + *proxy_r = NULL; + if (!ssl_initialized) return -1; @@ -340,24 +345,30 @@ net_set_nonblock(fd, TRUE); proxy = i_new(struct ssl_proxy, 1); - proxy->refcount = 1; + proxy->refcount = 2; proxy->ssl = ssl; proxy->fd_ssl = fd; proxy->fd_plain = sfd[0]; proxy->ip = *ip; + SSL_set_ex_data(ssl, extdata_index, proxy); hash_insert(ssl_proxies, proxy, proxy); - proxy->refcount++; ssl_handshake(proxy); - if (!ssl_proxy_unref(proxy)) { - /* handshake failed. return the disconnected socket anyway - so the caller doesn't try to use the old closed fd */ - return sfd[1]; - } + main_ref(); + + *proxy_r = proxy; + return sfd[1]; +} - main_ref(); - return sfd[1]; +int ssl_proxy_has_valid_client_cert(struct ssl_proxy *proxy) +{ + return proxy->cert_received && !proxy->cert_broken; +} + +void ssl_proxy_free(struct ssl_proxy *proxy) +{ + ssl_proxy_unref(proxy); } static int ssl_proxy_unref(struct ssl_proxy *proxy) @@ -401,6 +412,22 @@ return RSA_generate_key(keylength, RSA_F4, NULL, NULL); } +static int ssl_verify_client_cert(int preverify_ok, X509_STORE_CTX *ctx) +{ + SSL *ssl; + struct ssl_proxy *proxy; + + ssl = X509_STORE_CTX_get_ex_data(ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + proxy = SSL_get_ex_data(ssl, extdata_index); + + proxy->cert_received = TRUE; + if (!preverify_ok) + proxy->cert_broken = TRUE; + + return 1; +} + void ssl_proxy_init(void) { const char *cafile, *certfile, *keyfile, *paramfile, *cipher_list; @@ -419,6 +446,8 @@ SSL_library_init(); SSL_load_error_strings(); + extdata_index = SSL_get_ex_new_index(0, "dovecot", NULL, NULL, NULL); + if ((ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) i_fatal("SSL_CTX_new() failed"); @@ -455,8 +484,8 @@ if (getenv("SSL_VERIFY_CLIENT_CERT") != NULL) { SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT | - SSL_VERIFY_CLIENT_ONCE, NULL); + SSL_VERIFY_CLIENT_ONCE, + ssl_verify_client_cert); } /* PRNG initialization might want to use /dev/urandom, make sure it
--- a/src/login-common/ssl-proxy.h Mon May 17 02:29:27 2004 +0300 +++ b/src/login-common/ssl-proxy.h Mon May 17 04:32:16 2004 +0300 @@ -2,13 +2,16 @@ #define __SSL_PROXY_H struct ip_addr; +struct ssl_proxy; extern int ssl_initialized; /* establish SSL connection with the given fd, returns a new fd which you must use from now on, or -1 if error occured. Unless -1 is returned, the given fd must be simply forgotten. */ -int ssl_proxy_new(int fd, struct ip_addr *ip); +int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r); +int ssl_proxy_has_valid_client_cert(struct ssl_proxy *proxy); +void ssl_proxy_free(struct ssl_proxy *proxy); void ssl_proxy_init(void); void ssl_proxy_deinit(void);
--- a/src/master/auth-process.c Mon May 17 02:29:27 2004 +0300 +++ b/src/master/auth-process.c Mon May 17 04:32:16 2004 +0300 @@ -335,6 +335,8 @@ env_put("USE_CYRUS_SASL=1"); if (group->set->verbose) env_put("VERBOSE=1"); + if (group->set->ssl_require_client_cert) + env_put("SSL_REQUIRE_CLIENT_CERT=1"); restrict_process_size(group->set->process_size, (unsigned int)-1);
--- a/src/master/master-settings.c Mon May 17 02:29:27 2004 +0300 +++ b/src/master/master-settings.c Mon May 17 04:32:16 2004 +0300 @@ -132,6 +132,7 @@ DEF(SET_BOOL, use_cyrus_sasl), DEF(SET_BOOL, verbose), + DEF(SET_BOOL, ssl_require_client_cert), DEF(SET_INT, count), DEF(SET_INT, process_size), @@ -263,6 +264,7 @@ MEMBER(use_cyrus_sasl) FALSE, MEMBER(verbose) FALSE, + MEMBER(ssl_require_client_cert) FALSE, MEMBER(count) 1, MEMBER(process_size) 256, @@ -334,6 +336,15 @@ auth->chroot); return FALSE; } + + if (auth->ssl_require_client_cert) { + /* if we require valid cert, make sure we also ask for it */ + if (auth->parent->pop3 != NULL) + auth->parent->pop3->ssl_verify_client_cert = TRUE; + if (auth->parent->imap != NULL) + auth->parent->imap->ssl_verify_client_cert = TRUE; + } + return TRUE; }
--- a/src/master/master-settings.h Mon May 17 02:29:27 2004 +0300 +++ b/src/master/master-settings.h Mon May 17 04:32:16 2004 +0300 @@ -109,6 +109,7 @@ const char *anonymous_username; int use_cyrus_sasl, verbose; + int ssl_require_client_cert; unsigned int count; unsigned int process_size;
--- a/src/pop3-login/client-authenticate.c Mon May 17 02:29:27 2004 +0300 +++ b/src/pop3-login/client-authenticate.c Mon May 17 04:32:16 2004 +0300 @@ -11,6 +11,7 @@ #include "auth-client.h" #include "../auth/auth-mech-desc.h" #include "../pop3/capability.h" +#include "ssl-proxy.h" #include "master.h" #include "auth-common.h" #include "client.h" @@ -132,6 +133,17 @@ t_pop(); } +static enum auth_client_request_new_flags +client_get_auth_flags(struct pop3_client *client) +{ + enum auth_client_request_new_flags auth_flags = 0; + + if (client->common.proxy != NULL && + ssl_proxy_has_valid_client_cert(client->common.proxy)) + auth_flags |= AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT; + return auth_flags; +} + static void login_callback(struct auth_request *request, struct auth_client_request_reply *reply, const unsigned char *data, void *context) @@ -196,6 +208,7 @@ client->common.auth_request = auth_client_request_new(auth_client, AUTH_MECH_PLAIN, AUTH_PROTOCOL_POP3, + client_get_auth_flags(client), login_callback, client, &error); if (client->common.auth_request != NULL) { /* don't read any input from client until login is finished */ @@ -305,6 +318,7 @@ client->common.auth_request = auth_client_request_new(auth_client, mech->mech, AUTH_PROTOCOL_POP3, + client_get_auth_flags(client), authenticate_callback, client, &error); if (client->common.auth_request != NULL) { /* following input data will go to authentication */
--- a/src/pop3-login/client.c Mon May 17 02:29:27 2004 +0300 +++ b/src/pop3-login/client.c Mon May 17 04:32:16 2004 +0300 @@ -80,7 +80,8 @@ client->common.io = NULL; } - fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip); + fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip, + &client->common.proxy); if (fd_ssl != -1) { client->tls = TRUE; client->secured = TRUE; @@ -298,6 +299,8 @@ net_disconnect(client->common.fd); client->common.fd = -1; + if (client->common.proxy != NULL) + ssl_proxy_free(client->common.proxy); client_unref(client); }