]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added ssl_require_client_cert auth-specific setting. Hide
authorTimo Sirainen <tss@iki.fi>
Mon, 17 May 2004 01:32:16 +0000 (04:32 +0300)
committerTimo Sirainen <tss@iki.fi>
Mon, 17 May 2004 01:32:16 +0000 (04:32 +0300)
ssl_verify_client_cert from default config file as it's automatically set if
needed and there's not much point in forcing it.

--HG--
branch : HEAD

17 files changed:
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

index 715912f0fa0e39096d95c50c89bb87c2fd2acfae..cbfb3daff5c65a2429ce55e4b18286fbcfb93ffe 100644 (file)
@@ -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.
 #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 @@ auth default {
 
   # 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
index 1309a759cec3f24206607c9bdc85871b18a29302..5f9fbb3842537406bb039983caab46986bcc9ef4 100644 (file)
@@ -22,6 +22,10 @@ enum auth_protocol {
        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 @@ struct auth_client_request_new {
 
        enum auth_mech mech;
        enum auth_protocol protocol;
+        enum auth_client_request_new_flags flags;
 };
 
 /* Continue authentication request */
index 621b5169ed3968608b537cc0073ee638545690a6..44aad31c5b605a10c53ecf1d5f77aa43fe99e906 100644 (file)
@@ -22,6 +22,7 @@ const char *anonymous_username;
 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 @@ void mech_request_new(struct auth_client_connection *conn,
                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 @@ void mech_init(void)
        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)
index cb50688cba08d2dbcd3bade57eb72818183faea0..fb8e101f23c209bc7c9e2f0e42797b9176c1ea08 100644 (file)
@@ -43,6 +43,7 @@ extern const char *const *auth_realms;
 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);
index b9033f0dcd95f14325dff033ab9fdfe290593531..0bb91ca667aa4ff0ce1fbf61589c1e01c388353f 100644 (file)
@@ -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 void login_callback(struct auth_request *request,
        }
 }
 
+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 @@ int cmd_login(struct imap_client *client, struct imap_arg *args)
        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 @@ int cmd_authenticate(struct imap_client *client, struct imap_arg *args)
        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) {
index bc1f11e5058f6b001cf7bd1ebfa912e3ba471830..6ef5cb1927b913e9a83810b19819b3527d57f3c8 100644 (file)
@@ -124,7 +124,8 @@ static int cmd_starttls(struct imap_client *client)
                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 @@ void client_destroy(struct imap_client *client, const char *reason)
                client->common.fd = -1;
        }
 
+       if (client->common.proxy != NULL)
+               ssl_proxy_free(client->common.proxy);
        client_unref(client);
 }
 
index 1883206801a429729dfd320bdb4eddc53b15cdc9..830b6654b038a27b58b58f13b237a6617e70c19b 100644 (file)
@@ -31,6 +31,7 @@ void auth_client_connect_missing_servers(struct auth_client *client);
 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);
 
index bda435efd405b2d81175f46a5eba7b58c5aba12a..dd9251f6fa3692bfed92cd9d902e5e7a6d3d4c41 100644 (file)
@@ -11,7 +11,8 @@ struct auth_request {
         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 @@ static int auth_server_send_new_request(struct auth_server_connection *conn,
        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 @@ void auth_server_requests_remove_all(struct auth_server_connection *conn)
 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 @@ auth_client_request_new(struct auth_client *client,
        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 */
index 1d52e7e57fa4dcb040c032f55a2c6827e39e31dc..97b738c0fa765112ae8dce33938008cb4926d30b 100644 (file)
@@ -6,6 +6,7 @@
 
 struct client {
        struct ip_addr ip;
+       struct ssl_proxy *proxy;
 
        int fd;
        struct io *io;
index 271d9126c5cdf92913f27d63c8d841843902a82f..7375d5a34d28013e2931ef92e6eddea95d2a0355 100644 (file)
@@ -95,6 +95,8 @@ static void login_accept(void *context __attr_unused__)
 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 @@ static void login_accept_ssl(void *context __attr_unused__)
        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 @@ int main(int argc __attr_unused__, char *argv[], char *envp[])
 {
        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 @@ int main(int argc __attr_unused__, char *argv[], char *envp[])
                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 @@ int main(int argc __attr_unused__, char *argv[], char *envp[])
                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();
index db229ba74ae1903781aac4fb7878bb4c7b7612bc..e416d33c6a094f65b8b32884ab326392fdee2045 100644 (file)
@@ -41,8 +41,11 @@ struct ssl_proxy {
 
        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 @@ static void ssl_step(void *context)
        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,26 +345,32 @@ int ssl_proxy_new(int fd, struct ip_addr *ip)
        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];
 }
 
+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)
 {
        if (--proxy->refcount > 0)
@@ -401,6 +412,22 @@ static RSA *ssl_gen_rsa_key(SSL *ssl __attr_unused__,
        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 @@ void ssl_proxy_init(void)
        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 @@ void ssl_proxy_init(void)
 
        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
index 80ca7a5ed550fef30593be00ae80f88b678ad0ba..c5dabdff1641f695f2c2292e8c66ef03ae51bef3 100644 (file)
@@ -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);
index 30fc950f7918ba215619658529b5b7348c4537f1..3dceaf79c7caba8d06dba8592e256a3bb3270ed4 100644 (file)
@@ -335,6 +335,8 @@ static pid_t create_auth_process(struct auth_process_group *group)
                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);
 
index 804cadedb1bea01d70cc97265e9f5f051adbd2b6..601ca3b2f42f5c9278350fe1311bdc45af8d362e 100644 (file)
@@ -132,6 +132,7 @@ static struct setting_def auth_setting_defs[] = {
 
        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 @@ struct auth_settings default_auth_settings = {
 
        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 @@ static int auth_settings_verify(struct auth_settings *auth)
                        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;
 }
 
index a608aaa2358ce0b893041957c328c0434520f6d4..4cc06ae0bba93c660ee44bfbee45e11d1fcf51ff 100644 (file)
@@ -109,6 +109,7 @@ struct auth_settings {
        const char *anonymous_username;
 
        int use_cyrus_sasl, verbose;
+       int ssl_require_client_cert;
 
        unsigned int count;
        unsigned int process_size;
index 7001a56e27455e71ecf565cd3042e4b3e2c3925b..31b5cb5760a73b477f78e2b2be9486880d5f5106 100644 (file)
@@ -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 @@ static void client_send_auth_data(struct pop3_client *client,
        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 @@ int cmd_pass(struct pop3_client *client, const char *args)
        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 @@ int cmd_auth(struct pop3_client *client, const char *args)
        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 */
index e0b998457822b28afdb4554583050a03e89c9350..52f4e5d685951292cd839ae1ad44d8926e764233 100644 (file)
@@ -80,7 +80,8 @@ static int cmd_stls(struct pop3_client *client)
                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 @@ void client_destroy(struct pop3_client *client, const char *reason)
        net_disconnect(client->common.fd);
        client->common.fd = -1;
 
+       if (client->common.proxy != NULL)
+               ssl_proxy_free(client->common.proxy);
        client_unref(client);
 }