From: Timo Sirainen Date: Sat, 25 Feb 2012 03:04:15 +0000 (+0200) Subject: auth: Handle proxy_maybe=yes with host=hostname properly. X-Git-Tag: 2.2.alpha1~556 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8be0af2dd41283fdd250b9a0d8fad28bfe80ca85;p=thirdparty%2Fdovecot%2Fcore.git auth: Handle proxy_maybe=yes with host=hostname properly. --- diff --git a/src/auth/Makefile.am b/src/auth/Makefile.am index 8e399e841d..c0fb9a7272 100644 --- a/src/auth/Makefile.am +++ b/src/auth/Makefile.am @@ -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 \ diff --git a/src/auth/auth-master-connection.c b/src/auth/auth-master-connection.c index 1c3598644f..33aba45f4f 100644 --- a/src/auth/auth-master-connection.c +++ b/src/auth/auth-master-connection.c @@ -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; diff --git a/src/auth/auth-request-handler.c b/src/auth/auth-request-handler.c index 7723fc93b5..75caa4b3ff 100644 --- a/src/auth/auth-request-handler.c +++ b/src/auth/auth-request-handler.c @@ -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 */ diff --git a/src/auth/auth-request.c b/src/auth/auth-request.c index dbfba604c2..6d71a2b841 100644 --- a/src/auth/auth-request.c +++ b/src/auth/auth-request.c @@ -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 #include +#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"); diff --git a/src/auth/auth-request.h b/src/auth/auth-request.h index 69592dbf32..87a7ea1f76 100644 --- a/src/auth/auth-request.h +++ b/src/auth/auth-request.h @@ -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);