]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
auth: Handle proxy_maybe=yes with host=hostname properly.
authorTimo Sirainen <tss@iki.fi>
Sat, 25 Feb 2012 03:04:15 +0000 (05:04 +0200)
committerTimo Sirainen <tss@iki.fi>
Sat, 25 Feb 2012 03:04:15 +0000 (05:04 +0200)
src/auth/Makefile.am
src/auth/auth-master-connection.c
src/auth/auth-request-handler.c
src/auth/auth-request.c
src/auth/auth-request.h

index 8e399e841decee73078b002a028f2d50cea6e59a..c0fb9a72723163c6d06965dba8e71c6c840922cb 100644 (file)
@@ -24,6 +24,7 @@ pkglibexec_PROGRAMS = auth checkpassword-reply
 AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib \
        -I$(top_srcdir)/src/lib-auth \
+       -I$(top_srcdir)/src/lib-dns \
        -I$(top_srcdir)/src/lib-sql \
        -I$(top_srcdir)/src/lib-settings \
        -I$(top_srcdir)/src/lib-ntlm \
index 1c3598644f55ee48d0345e7be7d5ced704fcb6bd..33aba45f4f76ea0df700878fa3c59d42b494aa44 100644 (file)
@@ -287,18 +287,13 @@ master_input_user(struct auth_master_connection *conn, const char *args)
        return TRUE;
 }
 
-static void
-pass_callback(enum passdb_result result,
-             const unsigned char *credentials ATTR_UNUSED,
-             size_t size ATTR_UNUSED,
-             struct auth_request *auth_request)
+static void pass_callback_finish(struct auth_request *auth_request,
+                                enum passdb_result result)
 {
        struct auth_master_connection *conn = auth_request->master;
        struct auth_stream_reply *reply = auth_request->extra_fields;
        string_t *str;
 
-       auth_request_proxy_finish(auth_request, result == PASSDB_RESULT_OK);
-
        str = t_str_new(128);
        switch (result) {
        case PASSDB_RESULT_OK:
@@ -331,6 +326,34 @@ pass_callback(enum passdb_result result,
        auth_master_connection_unref(&conn);
 }
 
+static void
+auth_master_pass_proxy_finish(bool success, struct auth_request *auth_request)
+{
+       pass_callback_finish(auth_request, success ? PASSDB_RESULT_OK :
+                            PASSDB_RESULT_INTERNAL_FAILURE);
+}
+
+static void
+pass_callback(enum passdb_result result,
+             const unsigned char *credentials ATTR_UNUSED,
+             size_t size ATTR_UNUSED,
+             struct auth_request *auth_request)
+{
+       int ret;
+
+       if (result != PASSDB_RESULT_OK)
+               auth_request_proxy_finish_failure(auth_request);
+       else {
+               ret = auth_request_proxy_finish(auth_request,
+                                               auth_master_pass_proxy_finish);
+               if (ret == 0)
+                       return;
+               if (ret < 0)
+                       result = PASSDB_RESULT_INTERNAL_FAILURE;
+       }
+       pass_callback_finish(auth_request, result);
+}
+
 static const char *auth_restricted_reason(struct auth_master_connection *conn)
 {
        struct passwd pw;
index 7723fc93b51ad8631067e048ff617adeeb4c90b3..75caa4b3fff66724fc60ac03d33613bf6ab81e8d 100644 (file)
@@ -237,6 +237,72 @@ auth_request_handle_failure(struct auth_request *request,
        }
 }
 
+static void
+auth_request_handler_reply_success_finish(struct auth_request *request)
+{
+        struct auth_request_handler *handler = request->handler;
+       struct auth_stream_reply *reply;
+
+       reply = auth_stream_reply_init(pool_datastack_create());
+
+       if (request->last_penalty != 0 && auth_penalty != NULL) {
+               /* reset penalty */
+               auth_penalty_update(auth_penalty, request, 0);
+       }
+
+       auth_stream_reply_add(reply, "OK", NULL);
+       auth_stream_reply_add(reply, NULL, dec2str(request->id));
+       auth_stream_reply_add(reply, "user", request->user);
+       get_client_extra_fields(request, reply);
+       if (request->no_login || handler->master_callback == NULL) {
+               /* this request doesn't have to wait for master
+                  process to pick it up. delete it */
+               auth_request_handler_remove(handler, request);
+       }
+       handler->callback(reply, handler->context);
+}
+
+static void
+auth_request_handler_reply_failure_finish(struct auth_request *request)
+{
+       struct auth_stream_reply *reply;
+
+       reply = auth_stream_reply_init(pool_datastack_create());
+       auth_stream_reply_add(reply, "FAIL", NULL);
+       auth_stream_reply_add(reply, NULL, dec2str(request->id));
+       if (request->user != NULL)
+               auth_stream_reply_add(reply, "user", request->user);
+       else if (request->original_username != NULL) {
+               auth_stream_reply_add(reply, "user",
+                                     request->original_username);
+       }
+
+       if (request->internal_failure)
+               auth_stream_reply_add(reply, "temp", NULL);
+       else if (request->master_user != NULL) {
+               /* authentication succeeded, but we can't log in
+                  as the wanted user */
+               auth_stream_reply_add(reply, "authz", NULL);
+       }
+       if (request->no_failure_delay)
+               auth_stream_reply_add(reply, "nodelay", NULL);
+       get_client_extra_fields(request, reply);
+
+       auth_request_handle_failure(request, reply);
+}
+
+static void
+auth_request_handler_proxy_callback(bool success, struct auth_request *request)
+{
+        struct auth_request_handler *handler = request->handler;
+
+       if (success)
+               auth_request_handler_reply_success_finish(request);
+       else
+               auth_request_handler_reply_failure_finish(request);
+        auth_request_handler_unref(&handler);
+}
+
 void auth_request_handler_reply(struct auth_request *request,
                                enum auth_client_result result,
                                const void *auth_reply, size_t reply_size)
@@ -244,6 +310,7 @@ void auth_request_handler_reply(struct auth_request *request,
         struct auth_request_handler *handler = request->handler;
        struct auth_stream_reply *reply;
        string_t *str;
+       int ret;
 
        if (handler->destroyed) {
                /* the client connection was already closed. we can't do
@@ -255,9 +322,9 @@ void auth_request_handler_reply(struct auth_request *request,
                auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
        }
 
-       reply = auth_stream_reply_init(pool_datastack_create());
        switch (result) {
        case AUTH_CLIENT_RESULT_CONTINUE:
+               reply = auth_stream_reply_init(pool_datastack_create());
                auth_stream_reply_add(reply, "CONT", NULL);
                auth_stream_reply_add(reply, NULL, dec2str(request->id));
 
@@ -269,53 +336,23 @@ void auth_request_handler_reply(struct auth_request *request,
                handler->callback(reply, handler->context);
                break;
        case AUTH_CLIENT_RESULT_SUCCESS:
-               auth_request_proxy_finish(request, TRUE);
-
-               if (request->last_penalty != 0 && auth_penalty != NULL) {
-                       /* reset penalty */
-                       auth_penalty_update(auth_penalty, request, 0);
-               }
-
-               auth_stream_reply_add(reply, "OK", NULL);
-               auth_stream_reply_add(reply, NULL, dec2str(request->id));
-               auth_stream_reply_add(reply, "user", request->user);
                if (reply_size > 0) {
                        str = t_str_new(MAX_BASE64_ENCODED_SIZE(reply_size));
                        base64_encode(auth_reply, reply_size, str);
-                       auth_stream_reply_add(reply, "resp", str_c(str));
-               }
-               get_client_extra_fields(request, reply);
-               if (request->no_login || handler->master_callback == NULL) {
-                       /* this request doesn't have to wait for master
-                          process to pick it up. delete it */
-                       auth_request_handler_remove(handler, request);
+                       auth_stream_reply_add(request->extra_fields, "resp", str_c(str));
                }
-               handler->callback(reply, handler->context);
+               ret = auth_request_proxy_finish(request,
+                               auth_request_handler_proxy_callback);
+               if (ret < 0)
+                       auth_request_handler_reply_failure_finish(request);
+               else if (ret > 0)
+                       auth_request_handler_reply_success_finish(request);
+               else
+                       return;
                break;
        case AUTH_CLIENT_RESULT_FAILURE:
-               auth_request_proxy_finish(request, FALSE);
-
-               auth_stream_reply_add(reply, "FAIL", NULL);
-               auth_stream_reply_add(reply, NULL, dec2str(request->id));
-               if (request->user != NULL)
-                       auth_stream_reply_add(reply, "user", request->user);
-               else if (request->original_username != NULL) {
-                       auth_stream_reply_add(reply, "user",
-                                             request->original_username);
-               }
-
-               if (request->internal_failure)
-                       auth_stream_reply_add(reply, "temp", NULL);
-               else if (request->master_user != NULL) {
-                       /* authentication succeeded, but we can't log in
-                          as the wanted user */
-                       auth_stream_reply_add(reply, "authz", NULL);
-               }
-               if (request->no_failure_delay)
-                       auth_stream_reply_add(reply, "nodelay", NULL);
-               get_client_extra_fields(request, reply);
-
-               auth_request_handle_failure(request, reply);
+               auth_request_proxy_finish_failure(request);
+               auth_request_handler_reply_failure_finish(request);
                break;
        }
        /* NOTE: request may be destroyed now */
index dbfba604c2bf2ac0bedb572c2a85172366cd4203..6d71a2b841caed3684db0917331388d0ead42191 100644 (file)
@@ -11,6 +11,7 @@
 #include "str-sanitize.h"
 #include "strescape.h"
 #include "var-expand.h"
+#include "dns-lookup.h"
 #include "auth-cache.h"
 #include "auth-request.h"
 #include "auth-request-handler.h"
@@ -27,6 +28,8 @@
 #include <stdlib.h>
 #include <sys/stat.h>
 
+#define AUTH_DNS_SOCKET_PATH "dns-client"
+#define AUTH_DNS_TIMEOUT_MSECS (1000*10)
 #define CACHED_PASSWORD_SCHEME "SHA1"
 
 unsigned int auth_request_state_count[AUTH_REQUEST_STATE_MAX];
@@ -158,6 +161,11 @@ void auth_request_unref(struct auth_request **_request)
        auth_request_state_count[request->state]--;
        auth_refresh_proctitle();
 
+       if (request->mech_password != NULL) {
+               safe_memset(request->mech_password, 0,
+                           strlen(request->mech_password));
+       }
+
        if (request->to_abort != NULL)
                timeout_remove(&request->to_abort);
        if (request->to_penalty != NULL)
@@ -543,8 +551,6 @@ auth_request_verify_plain_callback_finish(enum passdb_result result,
        } else {
                auth_request_ref(request);
                request->private_callback.verify_plain(result, request);
-               safe_memset(request->mech_password, 0,
-                           strlen(request->mech_password));
                auth_request_unref(&request);
        }
 }
@@ -1410,53 +1416,144 @@ void auth_request_set_userdb_field_values(struct auth_request *request,
 
 static bool auth_request_proxy_is_self(struct auth_request *request)
 {
-       const char *const *tmp, *host = NULL, *port = NULL, *destuser = NULL;
-       struct ip_addr ip;
+       const char *const *tmp, *port = NULL, *destuser = NULL;
+
+       if (!request->proxy_host_is_self)
+               return FALSE;
 
        tmp = auth_stream_split(request->extra_fields);
        for (; *tmp != NULL; tmp++) {
-               if (strncmp(*tmp, "host=", 5) == 0)
-                       host = *tmp + 5;
-               else if (strncmp(*tmp, "port=", 5) == 0)
+               if (strncmp(*tmp, "port=", 5) == 0)
                        port = *tmp + 5;
-               if (strncmp(*tmp, "destuser=", 9) == 0)
+               else if (strncmp(*tmp, "destuser=", 9) == 0)
                        destuser = *tmp + 9;
        }
 
-       if (host == NULL || net_addr2ip(host, &ip) < 0) {
-               /* broken setup */
-               return FALSE;
-       }
-       if (!net_ip_compare(&ip, &request->local_ip))
-               return FALSE;
-
        if (port != NULL && !str_uint_equals(port, request->local_port))
                return FALSE;
        return destuser == NULL ||
                strcmp(destuser, request->original_username) == 0;
 }
 
-void auth_request_proxy_finish(struct auth_request *request, bool success)
+static bool
+auth_request_proxy_ip_is_self(struct auth_request *request,
+                             const struct ip_addr *ip)
 {
-       if (!request->proxy || request->no_login)
-               return;
+       return net_ip_compare(ip, &request->local_ip);
+}
 
-       if (!success) {
-               /* drop all proxy fields */
-       } else if (!request->proxy_maybe) {
+static void auth_request_proxy_finish_ip(struct auth_request *request)
+{
+       if (!request->proxy_maybe) {
                /* proxying */
                request->no_login = TRUE;
-               return;
        } else if (!auth_request_proxy_is_self(request)) {
                /* proxy destination isn't ourself - proxy */
                auth_stream_reply_remove(request->extra_fields, "proxy_maybe");
                auth_stream_reply_add(request->extra_fields, "proxy", NULL);
                request->no_login = TRUE;
-               return;
        } else {
                /* proxying to ourself - log in without proxying by dropping
                   all the proxying fields. */
+               auth_request_proxy_finish_failure(request);
        }
+}
+
+struct auth_request_proxy_dns_lookup_ctx {
+       struct auth_request *request;
+       auth_request_proxy_cb_t *callback;
+};
+
+static void
+auth_request_proxy_dns_callback(const struct dns_lookup_result *result,
+                               void *context)
+{
+       struct auth_request_proxy_dns_lookup_ctx *ctx = context;
+       struct auth_request *request = ctx->request;
+       const char *host;
+       unsigned int i;
+
+       if (result->ret != 0) {
+               host = auth_stream_reply_find(request->extra_fields, "host");
+               i_assert(host != NULL);
+               auth_request_log_error(request, "dns",
+                       "dns_lookup(%s) failed: %s", host, result->error);
+               request->internal_failure = TRUE;
+               auth_request_proxy_finish_failure(request);
+       } else {
+               auth_stream_reply_remove(request->extra_fields, "host");
+               auth_stream_reply_add(request->extra_fields, "host",
+                                     net_ip2addr(&result->ips[0]));
+               for (i = 0; i < result->ips_count; i++) {
+                       if (auth_request_proxy_ip_is_self(request,
+                                                         &result->ips[i])) {
+                               request->proxy_host_is_self = TRUE;
+                               break;
+                       }
+               }
+               auth_request_proxy_finish_ip(request);
+       }
+       if (ctx->callback != NULL)
+               ctx->callback(result->ret == 0, request);
+       i_free(ctx);
+}
+
+static int auth_request_proxy_host_lookup(struct auth_request *request,
+                                         auth_request_proxy_cb_t *callback)
+{
+       struct auth_request_proxy_dns_lookup_ctx *ctx;
+       struct dns_lookup_settings dns_set;
+       const char *host;
+       struct ip_addr ip;
+
+       host = auth_stream_reply_find(request->extra_fields, "host");
+       if (host == NULL)
+               return 1;
+       if (net_addr2ip(host, &ip) == 0) {
+               if (auth_request_proxy_ip_is_self(request, &ip))
+                       request->proxy_host_is_self = TRUE;
+               return 1;
+       }
+
+       /* need to do dns lookup for the host */
+       memset(&dns_set, 0, sizeof(dns_set));
+       dns_set.dns_client_socket_path = AUTH_DNS_SOCKET_PATH;
+       dns_set.timeout_msecs = AUTH_DNS_TIMEOUT_MSECS;
+
+       ctx = i_new(struct auth_request_proxy_dns_lookup_ctx, 1);
+       ctx->request = request;
+
+       if (dns_lookup(host, &dns_set, auth_request_proxy_dns_callback, ctx) < 0) {
+               /* failed early */
+               request->internal_failure = TRUE;
+               auth_request_proxy_finish_failure(request);
+               return -1;
+       }
+       ctx->callback = callback;
+       return 0;
+}
+
+int auth_request_proxy_finish(struct auth_request *request,
+                             auth_request_proxy_cb_t *callback)
+{
+       int ret;
+
+       if (!request->proxy)
+               return 1;
+
+       if ((ret = auth_request_proxy_host_lookup(request, callback)) <= 0)
+               return ret;
+
+       auth_request_proxy_finish_ip(request);
+       return 1;
+}
+
+void auth_request_proxy_finish_failure(struct auth_request *request)
+{
+       if (!request->proxy)
+               return;
+
+       /* drop all proxying fields */
        auth_stream_reply_remove(request->extra_fields, "proxy");
        auth_stream_reply_remove(request->extra_fields, "proxy_maybe");
        auth_stream_reply_remove(request->extra_fields, "host");
index 69592dbf32830c17c3a52860a8338e0017f42c0c..87a7ea1f7651218d2173c48e17bd16d33285bbca 100644 (file)
@@ -105,6 +105,7 @@ struct auth_request {
        unsigned int prefer_plain_credentials:1;
        unsigned int proxy:1;
        unsigned int proxy_maybe:1;
+       unsigned int proxy_host_is_self:1;
        unsigned int valid_client_cert:1;
        unsigned int no_penalty:1;
        unsigned int cert_username:1;
@@ -117,6 +118,8 @@ struct auth_request {
        /* ... mechanism specific data ... */
 };
 
+typedef void auth_request_proxy_cb_t(bool success, struct auth_request *);
+
 extern unsigned int auth_request_state_count[AUTH_REQUEST_STATE_MAX];
 
 struct auth_request *
@@ -177,7 +180,10 @@ void auth_request_set_userdb_field(struct auth_request *request,
 void auth_request_set_userdb_field_values(struct auth_request *request,
                                          const char *name,
                                          const char *const *values);
-void auth_request_proxy_finish(struct auth_request *request, bool success);
+/* returns -1 = failed, 0 = callback is called later, 1 = finished */
+int auth_request_proxy_finish(struct auth_request *request,
+                             auth_request_proxy_cb_t *callback);
+void auth_request_proxy_finish_failure(struct auth_request *request);
 
 void auth_request_log_password_mismatch(struct auth_request *request,
                                        const char *subsystem);