]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
doveadm: Implement user and auth cache flush to server
authorAki Tuomi <aki.tuomi@dovecot.fi>
Tue, 31 May 2016 19:22:37 +0000 (22:22 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Wed, 29 Jun 2016 15:37:40 +0000 (18:37 +0300)
src/doveadm/Makefile.am
src/doveadm/doveadm-auth-server.c [new file with mode: 0644]
src/doveadm/doveadm-cmd.c
src/doveadm/doveadm-cmd.h
src/doveadm/doveadm.c
src/doveadm/main.c

index 785dc4010a6b620bc93f45b9790b8ef31f253a5c..eaf2726bbde9e232ddc75c2b0b3d91beab869559 100644 (file)
@@ -136,6 +136,7 @@ doveadm_SOURCES = \
 
 doveadm_server_SOURCES = \
        $(common) \
+       doveadm-auth-server.c \
        client-connection.c \
        client-connection-http.c \
        doveadm-print-server.c \
diff --git a/src/doveadm/doveadm-auth-server.c b/src/doveadm/doveadm-auth-server.c
new file mode 100644 (file)
index 0000000..bb06220
--- /dev/null
@@ -0,0 +1,502 @@
+/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "array.h"
+#include "str.h"
+#include "var-expand.h"
+#include "wildcard-match.h"
+#include "settings-parser.h"
+#include "master-service.h"
+#include "master-service-settings.h"
+#include "auth-client.h"
+#include "auth-master.h"
+#include "auth-server-connection.h"
+#include "master-auth.h"
+#include "master-login-auth.h"
+#include "mail-storage-service.h"
+#include "mail-user.h"
+#include "ostream.h"
+#include "json-parser.h"
+#include "doveadm.h"
+#include "doveadm-print.h"
+
+#include <stdio.h>
+#include <unistd.h>
+
+struct authtest_input {
+       pool_t pool;
+       const char *username;
+       const char *master_user;
+       const char *password;
+       struct auth_user_info info;
+       bool success;
+
+       struct auth_client_request *request;
+       struct master_auth_request master_auth_req;
+
+       unsigned int auth_id;
+       unsigned int auth_pid;
+       const char *auth_cookie;
+
+};
+
+static struct auth_master_connection *
+doveadm_get_auth_master_conn(const char *auth_socket_path)
+{
+       enum auth_master_flags flags = 0;
+
+       if (doveadm_debug)
+               flags |= AUTH_MASTER_FLAG_DEBUG;
+       return auth_master_init(auth_socket_path, flags);
+}
+
+static int
+cmd_user_input(struct auth_master_connection *conn,
+              const struct authtest_input *input,
+              const char *show_field, bool userdb)
+{
+       const char *lookup_name = userdb ? "userdb lookup" : "passdb lookup";
+       pool_t pool;
+       const char *updated_username = NULL, *const *fields, *p;
+       int ret;
+
+       pool = pool_alloconly_create("auth master lookup", 1024);
+
+       if (userdb) {
+               ret = auth_master_user_lookup(conn, input->username, &input->info,
+                                             pool, &updated_username, &fields);
+       } else {
+               ret = auth_master_pass_lookup(conn, input->username, &input->info,
+                                             pool, &fields);
+       }
+       if (ret < 0) {
+               const char *msg;
+               if (fields[0] == NULL) {
+                       msg = t_strdup_printf("\"error\":\"%s failed\"",
+                                             lookup_name);
+               } else {
+                       msg = t_strdup_printf("\"error\":\"%s failed: %s\"",
+                                             lookup_name,
+                                             fields[0]);
+               }
+               o_stream_nsend_str(doveadm_print_ostream, msg);
+               ret = -1;
+       } else if (ret == 0) {
+               o_stream_nsend_str(doveadm_print_ostream,
+                       t_strdup_printf("\"error\":\"%s: user doesn't exist\"",
+                               lookup_name));
+       } else if (show_field != NULL) {
+               unsigned int show_field_len = strlen(show_field);
+               string_t *json_field = t_str_new(show_field_len+1);
+               json_append_escaped(json_field, show_field);
+               o_stream_nsend_str(doveadm_print_ostream, t_strdup_printf("\"%s\":", str_c(json_field)));
+               for (; *fields; fields++) {
+                       if (strncmp(*fields, show_field, show_field_len) == 0 &&
+                           (*fields)[show_field_len] == '=') {
+                               string_t *jsonval = t_str_new(32);
+                               json_append_escaped(jsonval, *fields + show_field_len + 1);
+                               o_stream_nsend_str(doveadm_print_ostream, "\"");
+                               o_stream_nsend_str(doveadm_print_ostream, str_c(jsonval));
+                               o_stream_nsend_str(doveadm_print_ostream, "\"");
+                       }
+               }
+       } else {
+               string_t *jsonval = t_str_new(64);
+               o_stream_nsend_str(doveadm_print_ostream, "\"source\":\"");
+               o_stream_nsend_str(doveadm_print_ostream, userdb ? "userdb\"" : "passdb\"");
+
+               if (updated_username != NULL) {
+                       o_stream_nsend_str(doveadm_print_ostream, ",\"updated_username\":\"");
+                       str_truncate(jsonval, 0);
+                       json_append_escaped(jsonval, updated_username);
+                       o_stream_nsend_str(doveadm_print_ostream, str_c(jsonval));
+                       o_stream_nsend_str(doveadm_print_ostream, "\"");
+               }
+               for (; *fields; fields++) {
+                       const char *field = *fields;
+                       if (*field == '\0') continue;
+                       p = strchr(*fields, '=');
+                       str_truncate(jsonval, 0);
+                       if (p != NULL) {
+                               field = t_strcut(*fields, '=');
+                       }
+                       str_truncate(jsonval, 0);
+                       json_append_escaped(jsonval, field);
+                       o_stream_nsend_str(doveadm_print_ostream, ",\"");
+                       o_stream_nsend_str(doveadm_print_ostream, str_c(jsonval));
+                       o_stream_nsend_str(doveadm_print_ostream, "\":");
+                       if (p != NULL) {
+                               str_truncate(jsonval, 0);
+                               json_append_escaped(jsonval, p+1);
+                               o_stream_nsend_str(doveadm_print_ostream, "\"");
+                               o_stream_nsend_str(doveadm_print_ostream, str_c(jsonval));
+                               o_stream_nsend_str(doveadm_print_ostream, "\"");
+                       } else {
+                               o_stream_nsend_str(doveadm_print_ostream, "true");
+                       }
+               }
+       }
+       return ret;
+}
+
+static void auth_user_info_parse(struct auth_user_info *info, const char *arg)
+{
+       if (strncmp(arg, "service=", 8) == 0)
+               info->service = arg + 8;
+       else if (strncmp(arg, "lip=", 4) == 0) {
+               if (net_addr2ip(arg + 4, &info->local_ip) < 0)
+                       i_fatal("lip: Invalid ip");
+       } else if (strncmp(arg, "rip=", 4) == 0) {
+               if (net_addr2ip(arg + 4, &info->remote_ip) < 0)
+                       i_fatal("rip: Invalid ip");
+       } else if (strncmp(arg, "lport=", 6) == 0) {
+               if (net_str2port(arg + 6, &info->local_port) < 0)
+                       i_fatal("lport: Invalid port number");
+       } else if (strncmp(arg, "rport=", 6) == 0) {
+               if (net_str2port(arg + 6, &info->remote_port) < 0)
+                       i_fatal("rport: Invalid port number");
+       } else {
+               i_fatal("Unknown -x argument: %s", arg);
+       }
+}
+
+static void
+cmd_user_list(struct auth_master_connection *conn,
+             const struct authtest_input *input,
+             char *const *users)
+{
+       struct auth_master_user_list_ctx *ctx;
+       const char *username, *user_mask = "*";
+       unsigned int i;
+
+       if (users[0] != NULL && users[1] == NULL)
+               user_mask = users[0];
+
+       ctx = auth_master_user_list_init(conn, user_mask, &input->info);
+       while ((username = auth_master_user_list_next(ctx)) != NULL) {
+               for (i = 0; users[i] != NULL; i++) {
+                       if (wildcard_match_icase(username, users[i]))
+                               break;
+               }
+               if (users[i] != NULL)
+                       printf("%s\n", username);
+       }
+       if (auth_master_user_list_deinit(&ctx) < 0)
+               i_fatal("user listing failed");
+}
+
+static void cmd_auth_cache_flush(int argc, char *argv[])
+{
+       const char *master_socket_path = NULL;
+       struct auth_master_connection *conn;
+       unsigned int count;
+       int c;
+
+       while ((c = getopt(argc, argv, "a:")) > 0) {
+               switch (c) {
+               case 'a':
+                       master_socket_path = optarg;
+                       break;
+               default:
+                       doveadm_exit_code = EX_USAGE;
+                       return;
+               }
+       }
+       argv += optind;
+
+       if (master_socket_path == NULL) {
+               master_socket_path = t_strconcat(doveadm_settings->base_dir,
+                                                "/auth-master", NULL);
+       }
+
+       conn = doveadm_get_auth_master_conn(master_socket_path);
+       if (auth_master_cache_flush(conn, (void *)argv, &count) < 0) {
+               i_error("Cache flush failed");
+               doveadm_exit_code = EX_TEMPFAIL;
+       } else {
+               doveadm_print_init("formatted");
+               doveadm_print_formatted_set_format("%{entries} cache entries flushed\n");
+               doveadm_print_header_simple("entries");
+               doveadm_print_num(count);
+       }
+       auth_master_deinit(&conn);
+}
+
+static void cmd_user_mail_input_field(const char *key, const char *value,
+                                     const char *show_field)
+{
+       string_t *jvalue = t_str_new(128);
+       if (show_field != NULL && strcmp(show_field, key) != 0) return;
+       json_append_escaped(jvalue, key);
+       o_stream_nsend_str(doveadm_print_ostream, "\"");
+       o_stream_nsend_str(doveadm_print_ostream, str_c(jvalue));
+       o_stream_nsend_str(doveadm_print_ostream, "\":\"");
+       str_truncate(jvalue, 0);
+       json_append_escaped(jvalue, value);
+       o_stream_nsend_str(doveadm_print_ostream, str_c(jvalue));
+       o_stream_nsend_str(doveadm_print_ostream, "\"");
+}
+
+static void
+cmd_user_mail_print_fields(const struct authtest_input *input,
+                          struct mail_user *user,
+                          const char *const *userdb_fields,
+                          const char *show_field)
+{
+       const struct mail_storage_settings *mail_set;
+       const char *key, *value;
+       unsigned int i;
+
+       if (strcmp(input->username, user->username) != 0) {
+               cmd_user_mail_input_field("user", user->username, show_field);
+               o_stream_nsend_str(doveadm_print_ostream, ",");
+       }
+       cmd_user_mail_input_field("uid", user->set->mail_uid, show_field);
+       o_stream_nsend_str(doveadm_print_ostream, ",");
+       cmd_user_mail_input_field("gid", user->set->mail_gid, show_field);
+       o_stream_nsend_str(doveadm_print_ostream, ",");
+       cmd_user_mail_input_field("home", user->set->mail_home, show_field);
+
+       mail_set = mail_user_set_get_storage_set(user);
+       o_stream_nsend_str(doveadm_print_ostream, ",");
+       cmd_user_mail_input_field("mail", mail_set->mail_location, show_field);
+
+       if (userdb_fields != NULL) {
+               for (i = 0; userdb_fields[i] != NULL; i++) {
+                       value = strchr(userdb_fields[i], '=');
+                       if (value != NULL)
+                               key = t_strdup_until(userdb_fields[i], value++);
+                       else {
+                               key = userdb_fields[i];
+                               value = "";
+                       }
+                       if (strcmp(key, "uid") != 0 &&
+                           strcmp(key, "gid") != 0 &&
+                           strcmp(key, "home") != 0 &&
+                           strcmp(key, "mail") != 0 &&
+                           *key != '\0') {
+                               o_stream_nsend_str(doveadm_print_ostream, ",");
+                               cmd_user_mail_input_field(key, value, show_field);
+                       }
+               }
+       }
+}
+
+static int
+cmd_user_mail_input(struct mail_storage_service_ctx *storage_service,
+                   const struct authtest_input *input,
+                   const char *show_field, const char *expand_field)
+{
+       struct mail_storage_service_input service_input;
+       struct mail_storage_service_user *service_user;
+       struct mail_user *user;
+       const char *error, *const *userdb_fields;
+       pool_t pool;
+       int ret;
+
+       memset(&service_input, 0, sizeof(service_input));
+       service_input.module = "mail";
+       service_input.service = input->info.service;
+       service_input.username = input->username;
+       service_input.local_ip = input->info.local_ip;
+       service_input.local_port = input->info.local_port;
+       service_input.remote_ip = input->info.remote_ip;
+       service_input.remote_port = input->info.remote_port;
+       service_input.debug = input->info.debug;
+
+       pool = pool_alloconly_create("userdb fields", 1024);
+       mail_storage_service_save_userdb_fields(storage_service, pool,
+                                               &userdb_fields);
+
+       if ((ret = mail_storage_service_lookup_next(storage_service, &service_input,
+                                                   &service_user, &user,
+                                                   &error)) <= 0) {
+               pool_unref(&pool);
+               if (ret < 0)
+                       return -1;
+               string_t *username = t_str_new(32);
+               json_append_escaped(username, input->username);
+               o_stream_nsend_str(doveadm_print_ostream,
+                       t_strdup_printf("\"error\":\"userdb lookup: user %s doesn't exist\"", str_c(username))
+               );
+               return 0;
+       }
+
+       if (expand_field == NULL)
+               cmd_user_mail_print_fields(input, user, userdb_fields, show_field);
+       else {
+               string_t *str = t_str_new(128);
+               var_expand_with_funcs(str, expand_field,
+                                     mail_user_var_expand_table(user),
+                                     mail_user_var_expand_func_table, user);
+               string_t *value = t_str_new(128);
+               json_append_escaped(value, expand_field);
+               o_stream_nsend_str(doveadm_print_ostream, "\"");
+               o_stream_nsend_str(doveadm_print_ostream, str_c(value));
+               o_stream_nsend_str(doveadm_print_ostream, "\":\"");
+               str_truncate(value, 0);
+               json_append_escaped(value, str_c(str));
+               o_stream_nsend_str(doveadm_print_ostream, str_c(value));
+               o_stream_nsend_str(doveadm_print_ostream, "\"");
+
+       }
+
+       mail_user_unref(&user);
+       mail_storage_service_user_free(&service_user);
+       pool_unref(&pool);
+       return 1;
+}
+
+static void cmd_user_ver2(struct doveadm_cmd_context *cctx)
+{
+       const char * const *optval;
+
+       const char *auth_socket_path = NULL;
+       struct auth_master_connection *conn;
+       struct authtest_input input;
+       const char *show_field = NULL, *expand_field = NULL;
+       struct mail_storage_service_ctx *storage_service = NULL;
+       bool have_wildcards, userdb_only = FALSE, first = TRUE;
+       int ret;
+
+       if (!doveadm_cmd_param_str(cctx, "socket-path", &auth_socket_path))
+               auth_socket_path = doveadm_settings->auth_socket_path;
+
+       (void)doveadm_cmd_param_str(cctx, "expand-field", &expand_field);
+       (void)doveadm_cmd_param_str(cctx, "field", &show_field);
+       (void)doveadm_cmd_param_bool(cctx, "userdb-only", &userdb_only);
+
+       if (doveadm_cmd_param_array(cctx, "auth-info", &optval))
+               for(;*optval != NULL; optval++)
+                       auth_user_info_parse(&input.info, *optval);
+
+       if (!doveadm_cmd_param_array(cctx, "user-mask", &optval)) {
+               doveadm_exit_code = EX_USAGE;
+               i_error("No user(s) specified");
+               return;
+       }
+
+       if (expand_field != NULL && userdb_only) {
+               i_error("-e can't be used with -u");
+               doveadm_exit_code = EX_USAGE;
+               return;
+       }
+       if (expand_field != NULL && show_field != NULL) {
+               i_error("-e can't be used with -f");
+               doveadm_exit_code = EX_USAGE;
+               return;
+       }
+
+       conn = doveadm_get_auth_master_conn(auth_socket_path);
+
+       have_wildcards = FALSE;
+
+       for(const char *const *val = optval; *val != NULL; val++) {
+               if (strchr(*val, '*') != NULL ||
+                   strchr(*val, '?') != NULL) {
+                       have_wildcards = TRUE;
+                       break;
+               }
+       }
+
+       if (have_wildcards) {
+               cmd_user_list(conn, &input, (char*const*)optval);
+               auth_master_deinit(&conn);
+               return;
+       }
+
+       if (!userdb_only) {
+               storage_service = mail_storage_service_init(master_service, NULL,
+                       MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP |
+                       MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR |
+                       MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT |
+                       MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS |
+                       MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES |
+                       MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS);
+               mail_storage_service_set_auth_conn(storage_service, conn);
+               conn = NULL;
+       }
+
+       string_t *json = t_str_new(64);
+       o_stream_nsend_str(doveadm_print_ostream, "{");
+
+       input.info.local_ip = cctx->local_ip;
+       input.info.local_port = cctx->local_port;
+       input.info.remote_ip = cctx->remote_ip;
+       input.info.remote_port = cctx->remote_port;
+
+       for(const char *const *val = optval; *val != NULL; val++) {
+               str_truncate(json, 0);
+               json_append_escaped(json, *val);
+
+               input.username = *val;
+               if (first)
+                       first = FALSE;
+               else
+                       o_stream_nsend_str(doveadm_print_ostream, ",");
+
+               o_stream_nsend_str(doveadm_print_ostream, "\"");
+               o_stream_nsend_str(doveadm_print_ostream, str_c(json));
+               o_stream_nsend_str(doveadm_print_ostream, "\"");
+               o_stream_nsend_str(doveadm_print_ostream, ":{");
+
+               ret = !userdb_only ?
+                       cmd_user_mail_input(storage_service, &input, show_field, expand_field) :
+                       cmd_user_input(conn, &input, show_field, TRUE);
+
+               o_stream_nsend_str(doveadm_print_ostream, "}");
+
+               switch (ret) {
+               case -1:
+                       doveadm_exit_code = EX_TEMPFAIL;
+                       break;
+               case 0:
+                       doveadm_exit_code = EX_NOUSER;
+                       break;
+               }
+       }
+
+       o_stream_nsend_str(doveadm_print_ostream,"}");
+
+       if (storage_service != NULL)
+               mail_storage_service_deinit(&storage_service);
+       if (conn != NULL)
+               auth_master_deinit(&conn);
+}
+
+static
+struct doveadm_cmd_ver2 doveadm_cmd_auth_server[] = {
+{
+       .name = "auth cache flush",
+       .old_cmd = cmd_auth_cache_flush,
+       .usage = "[-a <master socket path>] [<user> [...]]",
+DOVEADM_CMD_PARAMS_START
+DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0)
+DOVEADM_CMD_PARAM('\0', "user", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAMS_END
+},
+{
+       .name = "user",
+       .cmd = cmd_user_ver2,
+       .usage = "[-a <userdb socket path>] [-x <auth info>] [-f field] [-e <value>] [-u] <user mask> [...]",
+       .flags = CMD_FLAG_NO_PRINT,
+DOVEADM_CMD_PARAMS_START
+DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0)
+DOVEADM_CMD_PARAM('x', "auth-info", CMD_PARAM_ARRAY, 0)
+DOVEADM_CMD_PARAM('f', "field", CMD_PARAM_STR, 0)
+DOVEADM_CMD_PARAM('e', "expand-field", CMD_PARAM_STR, 0)
+DOVEADM_CMD_PARAM('u', "userdb-only", CMD_PARAM_BOOL, 0)
+DOVEADM_CMD_PARAM('\0', "user-mask", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAMS_END
+}
+};
+
+void doveadm_register_auth_server_commands(void)
+{
+       unsigned int i;
+
+       for (i = 0; i < N_ELEMENTS(doveadm_cmd_auth_server); i++) {
+               doveadm_cmd_register_ver2(&doveadm_cmd_auth_server[i]);
+       }
+}
index 0bb8dd408b9bec3cc87c1a19bc9b62a09adf3cb6..46bb92bd6bf988f4189336ea17961a03b9a0df5a 100644 (file)
@@ -173,7 +173,6 @@ void doveadm_cmds_init(void)
        for (i = 0; i < N_ELEMENTS(doveadm_commands_ver2); i++)
                doveadm_cmd_register_ver2(doveadm_commands_ver2[i]);
 
-       doveadm_register_auth_commands();
        doveadm_register_director_commands();
        doveadm_register_instance_commands();
        doveadm_register_mount_commands();
index db044979524742fde3b5c9df7a9a79b80f33b941..3c677daaa7b65b841ae5665f416ca8c90930e2e5 100644 (file)
@@ -101,6 +101,7 @@ doveadm_cmd_find_with_args(const char *cmd_name, int *argc,
                           const char *const *argv[]);
 
 void doveadm_register_auth_commands(void);
+void doveadm_register_auth_server_commands(void);
 void doveadm_register_director_commands(void);
 void doveadm_register_proxy_commands(void);
 void doveadm_register_log_commands(void);
index 677047e008329cd303023a6fac9e259c79626436..db15a3cc1edd158a4d897a86a335043bb4743522 100644 (file)
@@ -333,7 +333,7 @@ int main(int argc, char *argv[])
        doveadm_cmds_init();
        for (i = 0; i < N_ELEMENTS(doveadm_cmdline_commands); i++)
                doveadm_register_cmd(doveadm_cmdline_commands[i]);
-
+       doveadm_register_auth_commands();
        doveadm_cmd_register_ver2(&doveadm_cmd_stats_top_ver2);
 
        if (cmd_name != NULL && (quick_init ||
index dca58467425462f2cade483272ed33c34281e29c..744d58c4ceedf8d2ec39c68199324fbc563733dd 100644 (file)
@@ -73,6 +73,7 @@ static void main_init(void)
 
        doveadm_http_server_init();
        doveadm_cmds_init();
+       doveadm_register_auth_server_commands();
        doveadm_dump_init();
        doveadm_mail_init();
        dict_drivers_register_builtin();