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 \
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:
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;
}
}
+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)
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
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));
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 */
#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"
#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];
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)
} 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);
}
}
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");
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;
/* ... 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 *
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);