-I$(top_srcdir)/src/lib-dns \
-I$(top_srcdir)/src/lib-sql \
-I$(top_srcdir)/src/lib-settings \
+ -I$(top_srcdir)/src/lib-stats \
-I$(top_srcdir)/src/lib-ntlm \
-I$(top_srcdir)/src/lib-otp \
-I$(top_srcdir)/src/lib-master \
auth-penalty.c \
auth-request.c \
auth-request-handler.c \
+ auth-request-stats.c \
auth-request-var-expand.c \
auth-settings.c \
+ auth-stats.c \
auth-fields.c \
auth-token.c \
auth-worker-client.c \
auth-request-handler.h \
auth-request-var-expand.h \
auth-settings.h \
+ auth-stats.h \
auth-fields.h \
auth-token.h \
auth-worker-client.h \
checkpassword_reply_sources = \
checkpassword-reply.c
+stats_moduledir = $(moduledir)/stats
+stats_module_LTLIBRARIES = libstats_auth.la
+
+libstats_auth_la_LDFLAGS = -module -avoid-version
+libstats_auth_la_LIBADD = auth-stats.lo $(LIBDOVECOT)
+libstats_auth_la_DEPENDENCIES = auth-stats.lo
+libstats_auth_la_SOURCES =
+
test_programs = \
test-auth-cache \
test-auth-request-var-expand \
--- /dev/null
+/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
+
+#include "auth-common.h"
+#include "str.h"
+#include "strescape.h"
+#include "buffer.h"
+#include "base64.h"
+#include "stats.h"
+#include "stats-connection.h"
+#include "auth-stats.h"
+#include "auth-request.h"
+#include "auth-request-stats.h"
+
+#define USER_STATS_SOCKET_NAME "stats-user"
+
+static struct stats_connection *auth_stats_conn = NULL;
+static struct stats_item *auth_stats_item;
+
+struct auth_stats *auth_request_stats_get(struct auth_request *request)
+{
+ if (request->stats == NULL)
+ request->stats = stats_alloc(request->pool);
+ return stats_fill_ptr(request->stats, auth_stats_item);
+}
+
+void auth_request_stats_add_tempfail(struct auth_request *request)
+{
+ struct auth_stats *stats = auth_request_stats_get(request);
+
+ stats->auth_db_tempfail_count++;
+}
+
+void auth_request_stats_send(struct auth_request *request)
+{
+ string_t *str;
+ buffer_t *buf;
+
+ /* we'll send stats only when the request is finished. this reduces
+ memory usage and is a bit simpler. auth requests are typically
+ pretty short lived anyway. */
+ i_assert(!request->stats_sent);
+ request->stats_sent = TRUE;
+
+ if (request->stats == NULL) {
+ /* nothing happened in this request - don't send it */
+ return;
+ }
+ if (!request->set->stats)
+ return;
+
+ buf = buffer_create_dynamic(pool_datastack_create(), 128);
+ stats_export(buf, request->stats);
+
+ str = t_str_new(256);
+ str_append(str, "ADD-USER\t");
+ if (request->user != NULL)
+ str_append_tabescaped(str, request->user);
+ str_append_c(str, '\t');
+ str_append_tabescaped(str, request->service);
+ str_append_c(str, '\t');
+ base64_encode(buf->data, buf->used, str);
+
+ str_append_c(str, '\n');
+ stats_connection_send(auth_stats_conn, str);
+}
+
+void auth_request_stats_init(void)
+{
+ auth_stats_conn = stats_connection_create(USER_STATS_SOCKET_NAME);
+ auth_stats_item = stats_register(&auth_stats_vfuncs);
+}
+
+void auth_request_stats_deinit(void)
+{
+ stats_connection_unref(&auth_stats_conn);
+ stats_unregister(&auth_stats_item);
+}
--- /dev/null
+#ifndef AUTH_REQUEST_STATS_H
+#define AUTH_REQUEST_STATS_H
+
+#include "auth-stats.h"
+
+struct auth_request;
+
+struct auth_stats *auth_request_stats_get(struct auth_request *request);
+void auth_request_stats_add_tempfail(struct auth_request *request);
+void auth_request_stats_send(struct auth_request *request);
+
+void auth_request_stats_init(void);
+void auth_request_stats_deinit(void);
+
+#endif
#include "auth-cache.h"
#include "auth-request.h"
#include "auth-request-handler.h"
+#include "auth-request-stats.h"
#include "auth-client-connection.h"
#include "auth-master-connection.h"
#include "passdb.h"
void auth_request_success(struct auth_request *request,
const void *data, size_t data_size)
{
+ struct auth_stats *stats;
+
i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE);
if (request->failed || !request->passdb_success) {
return;
}
+ stats = auth_request_stats_get(request);
+ stats->auth_success_count++;
+ if (request->master_user != NULL)
+ stats->auth_master_success_count++;
+
auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
auth_request_refresh_last_access(request);
auth_request_handler_reply(request, AUTH_CLIENT_RESULT_SUCCESS,
void auth_request_fail(struct auth_request *request)
{
+ struct auth_stats *stats;
+
i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE);
+ stats = auth_request_stats_get(request);
+ stats->auth_failure_count++;
+
auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
auth_request_refresh_last_access(request);
auth_request_handler_reply(request, AUTH_CLIENT_RESULT_FAILURE, "", 0);
if (--request->refcount > 0)
return;
+ auth_request_stats_send(request);
auth_request_state_count[request->state]--;
auth_refresh_proctitle();
expired record. */
const char *cache_key = passdb->cache_key;
+ auth_request_stats_add_tempfail(request);
if (passdb_cache_verify_plain(request, cache_key,
request->mech_password,
&result, TRUE)) {
expired record. */
const char *cache_key = passdb->cache_key;
+ auth_request_stats_add_tempfail(request);
if (passdb_cache_lookup_credentials(request, cache_key,
&cache_cred, &cache_scheme,
&result, TRUE)) {
enum userdb_result *result_r,
bool use_expired)
{
+ struct auth_stats *stats = auth_request_stats_get(request);
const char *value;
struct auth_cache_node *node;
bool expired, neg_expired;
value = auth_cache_lookup(passdb_cache, request, key, &node,
&expired, &neg_expired);
if (value == NULL || (expired && !use_expired)) {
+ stats->auth_cache_miss_count++;
auth_request_log_debug(request, AUTH_SUBSYS_DB,
value == NULL ? "userdb cache miss" :
"userdb cache expired");
return FALSE;
}
+ stats->auth_cache_hit_count++;
auth_request_log_debug(request, AUTH_SUBSYS_DB,
"userdb cache hit: %s", value);
result_rule = request->userdb->result_success;
break;
case USERDB_RESULT_INTERNAL_FAILURE:
+ auth_request_stats_add_tempfail(request);
result_rule = request->userdb->result_internalfail;
break;
case USERDB_RESULT_USER_UNKNOWN:
struct auth_passdb *passdb;
struct auth_userdb *userdb;
+ struct stats *stats;
+
/* passdb lookups have a handler, userdb lookups don't */
struct auth_request_handler *handler;
struct auth_master_connection *master;
/* userdb_* fields have been set by the passdb lookup, userdb prefetch
will work. */
unsigned int userdb_prefetch_set:1;
+ unsigned int stats_sent:1;
/* ... mechanism specific data ... */
};
DEF(SET_STR, proxy_self),
DEF(SET_TIME, failure_delay),
+ DEF(SET_BOOL, stats),
DEF(SET_BOOL, verbose),
DEF(SET_BOOL, debug),
DEF(SET_BOOL, debug_passwords),
.proxy_self = "",
.failure_delay = 2,
+ .stats = FALSE,
.verbose = FALSE,
.debug = FALSE,
.debug_passwords = FALSE,
const char *proxy_self;
unsigned int failure_delay;
+ bool stats;
bool verbose, debug, debug_passwords;
const char *verbose_passwords;
bool ssl_require_client_cert;
--- /dev/null
+/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "stats.h"
+#include "stats-parser.h"
+#include "auth-stats.h"
+
+static struct stats_parser_field auth_stats_fields[] = {
+#define E(parsename, name, type) { parsename, offsetof(struct auth_stats, name), sizeof(((struct auth_stats *)0)->name), type }
+#define EN(parsename, name) E(parsename, name, STATS_PARSER_TYPE_UINT)
+ EN("auth_successes", auth_success_count),
+ EN("auth_master_successes", auth_master_success_count),
+ EN("auth_failures", auth_failure_count),
+ EN("auth_db_tempfails", auth_db_tempfail_count),
+
+ EN("auth_cache_hits", auth_cache_hit_count),
+ EN("auth_cache_misses", auth_cache_miss_count)
+};
+
+static size_t auth_stats_alloc_size(void)
+{
+ return sizeof(struct auth_stats);
+}
+
+static unsigned int auth_stats_field_count(void)
+{
+ return N_ELEMENTS(auth_stats_fields);
+}
+
+static const char *auth_stats_field_name(unsigned int n)
+{
+ i_assert(n < N_ELEMENTS(auth_stats_fields));
+
+ return auth_stats_fields[n].name;
+}
+
+static void
+auth_stats_field_value(string_t *str, const struct stats *stats,
+ unsigned int n)
+{
+ i_assert(n < N_ELEMENTS(auth_stats_fields));
+
+ stats_parser_value(str, &auth_stats_fields[n], stats);
+}
+
+static bool
+auth_stats_diff(const struct stats *stats1, const struct stats *stats2,
+ struct stats *diff_stats_r, const char **error_r)
+{
+ return stats_parser_diff(auth_stats_fields, N_ELEMENTS(auth_stats_fields),
+ stats1, stats2, diff_stats_r, error_r);
+}
+
+static void auth_stats_add(struct stats *dest, const struct stats *src)
+{
+ stats_parser_add(auth_stats_fields, N_ELEMENTS(auth_stats_fields),
+ dest, src);
+}
+
+static bool
+auth_stats_have_changed(const struct stats *_prev, const struct stats *_cur)
+{
+ return memcmp(_prev, _cur, sizeof(struct auth_stats)) != 0;
+}
+
+static void auth_stats_export(buffer_t *buf, const struct stats *_stats)
+{
+ const struct auth_stats *stats = (const struct auth_stats *)_stats;
+
+ buffer_append(buf, stats, sizeof(*stats));
+}
+
+static bool
+auth_stats_import(const unsigned char *data, size_t size, size_t *pos_r,
+ struct stats *_stats, const char **error_r)
+{
+ struct auth_stats *stats = (struct auth_stats *)_stats;
+
+ if (size < sizeof(*stats)) {
+ *error_r = "auth_stats too small";
+ return FALSE;
+ }
+ memcpy(stats, data, sizeof(*stats));
+ *pos_r = sizeof(*stats);
+ return TRUE;
+}
+
+const struct stats_vfuncs auth_stats_vfuncs = {
+ "auth",
+ auth_stats_alloc_size,
+ auth_stats_field_count,
+ auth_stats_field_name,
+ auth_stats_field_value,
+ auth_stats_diff,
+ auth_stats_add,
+ auth_stats_have_changed,
+ auth_stats_export,
+ auth_stats_import
+};
+
+/* for the stats_auth plugin: */
+void stats_auth_init(void);
+void stats_auth_deinit(void);
+
+static struct stats_item *auth_stats_item;
+
+void stats_auth_init(void)
+{
+ auth_stats_item = stats_register(&auth_stats_vfuncs);
+}
+
+void stats_auth_deinit(void)
+{
+ stats_unregister(&auth_stats_item);
+}
--- /dev/null
+#ifndef AUTH_STATS_H
+#define AUTH_STATS_H
+
+struct auth_stats {
+ uint32_t auth_success_count;
+ uint32_t auth_master_success_count;
+ uint32_t auth_failure_count;
+ uint32_t auth_db_tempfail_count;
+
+ uint32_t auth_cache_hit_count;
+ uint32_t auth_cache_miss_count;
+};
+
+extern const struct stats_vfuncs auth_stats_vfuncs;
+
+#endif
#include "auth-penalty.h"
#include "auth-token.h"
#include "auth-request-handler.h"
+#include "auth-request-stats.h"
#include "auth-worker-server.h"
#include "auth-worker-client.h"
#include "auth-master-connection.h"
if (!worker)
auth_penalty = auth_penalty_init(AUTH_PENALTY_ANVIL_PATH);
+ auth_request_stats_init();
mech_init(global_auth_settings);
mech_reg = mech_register_init(global_auth_settings);
dict_drivers_register_builtin();
passdbs_deinit();
passdb_cache_deinit();
password_schemes_deinit();
+ auth_request_stats_deinit();
+
sql_drivers_deinit();
random_deinit();
child_wait_deinit();
#include "auth-common.h"
#include "restrict-process-size.h"
+#include "auth-request-stats.h"
#include "password-scheme.h"
#include "passdb.h"
#include "passdb-cache.h"
-
struct auth_cache *passdb_cache = NULL;
static void
bool use_expired, struct auth_cache_node **node_r,
const char **value_r, bool *neg_expired_r)
{
+ struct auth_stats *stats = auth_request_stats_get(request);
const char *value;
bool expired;
value = auth_cache_lookup(passdb_cache, request, key, node_r,
&expired, neg_expired_r);
if (value == NULL || (expired && !use_expired)) {
+ stats->auth_cache_miss_count++;
auth_request_log_debug(request, AUTH_SUBSYS_DB,
value == NULL ? "cache miss" :
"cache expired");
return FALSE;
}
+ stats->auth_cache_hit_count++;
passdb_cache_log_hit(request, value);
*value_r = value;