auth-master.c revision efe78d3ba24fc866af1c79b9223dc0809ba26cad
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2005-2016 Dovecot authors, see the included COPYING file */
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen#define DEFAULT_USERDB_LOOKUP_PREFIX "userdb lookup"
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen bool (*reply_callback)(const char *cmd, const char *const *args,
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen const char **fields;
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainenstatic void auth_input(struct auth_master_connection *conn);
c5a6a6565be93224fc26522eda855b0990f256e8Timo Sirainenauth_master_init(const char *auth_socket_path, enum auth_master_flags flags)
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen conn = i_new(struct auth_master_connection, 1);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->auth_socket_path = i_strdup(auth_socket_path);
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainenstatic void auth_connection_close(struct auth_master_connection *conn)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen i_error("close(%s) failed: %m", conn->auth_socket_path);
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainenvoid auth_master_deinit(struct auth_master_connection **_conn)
89fda66c5c4204f3bc27e160fbadb463e028b811Timo Sirainenconst char *auth_master_get_socket_path(struct auth_master_connection *conn)
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainenstatic void auth_request_lookup_abort(struct auth_master_connection *conn)
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainenstatic int auth_input_handshake(struct auth_master_connection *conn)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen while ((line = i_stream_next_line(conn->input)) != NULL) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (strcmp(tmp[1], dec2str(AUTH_PROTOCOL_MAJOR)) != 0) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen "Auth protocol version mismatch "
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainenstatic int parse_reply(const char *cmd, const char *const *args,
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen const char *expected_reply, const char *user, bool debug)
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen } else if (debug) {
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen i_debug("user %s: Auth %s lookup returned temporary failure: %s",
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainenstatic const char *const *args_hide_passwords(const char *const *args)
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen const char *p, *p2;
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen unsigned int i;
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen /* if there are any keys that contain "pass" string */
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen /* there are. replace their values with <hidden> */
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainenstatic bool auth_lookup_reply_callback(const char *cmd, const char *const *args,
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen unsigned int i, len;
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen bool debug = (ctx->conn->flags & AUTH_MASTER_FLAG_DEBUG) != 0;
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen parse_reply(cmd, args, ctx->expected_reply, ctx->user, debug);
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen ctx->fields = p_new(ctx->pool, const char *, len + 1);
2ba63f475f74b2aa87f9fd9e28a6c5738deb0878Timo Sirainen for (i = 0; i < len; i++)
ef0c36aa8114feee80aa696d9cb8106140371243Timo Sirainen ctx->fields[i] = p_strdup(ctx->pool, args[i]);
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen /* put the reason string into first field */
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen ctx->fields = p_new(ctx->pool, const char *, 2);
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen for (i = 0; i < len; i++) {
9199e32b86d2de2fb7b73b6ec03b5c205b995490Timo Sirainen i_debug("auth %s input: %s", ctx->expected_reply,
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainenauth_handle_line(struct auth_master_connection *conn, const char *line)
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen const char *cmd, *const *args, *id, *wanted_id;
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen return conn->reply_callback(cmd, args, conn->reply_context);
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen "It should be a master socket.",
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen i_error("%s: BUG: Unexpected input: %s", conn->prefix, line);
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainenstatic void auth_input(struct auth_master_connection *conn)
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde /* disconnected */
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde /* buffer full */
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen i_error("%s: BUG: Received more than %d bytes",
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen while ((line = i_stream_next_line(conn->input)) != NULL) {
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainenstatic int auth_master_connect(struct auth_master_connection *conn)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* max. 1 second wait here. */
21c1655dbc5fe861a152dc9a8a388d0d64f5ae20Timo Sirainen fd = net_connect_unix_with_retries(conn->auth_socket_path, 1000);
9bb91f1dbf7cf8cfbd2df7784101df98d59fb46dTimo Sirainen i_error("userdb lookup: connect(%s) failed: %m",
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainenstatic void auth_request_timeout(struct auth_master_connection *conn)
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen i_error("%s: Connecting timed out", conn->prefix);
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen i_error("%s: Request timed out", conn->prefix);
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainenstatic void auth_idle_timeout(struct auth_master_connection *conn)
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainenstatic void auth_master_set_io(struct auth_master_connection *conn)
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE);
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi conn->output = o_stream_create_fd(conn->fd, MAX_OUTBUF_SIZE);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen conn->io = io_add(conn->fd, IO_READ, auth_input, conn);
d99107ddf4d9bccb710994482daf65276a9d6321Timo Sirainen conn->to = timeout_add(1000*MASTER_AUTH_LOOKUP_TIMEOUT_SECS,
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainenstatic void auth_master_unset_io(struct auth_master_connection *conn)
c5a6a6565be93224fc26522eda855b0990f256e8Timo Sirainen if ((conn->flags & AUTH_MASTER_FLAG_NO_IDLE_TIMEOUT) == 0) {
9d2a30e39c0662498db3368dbb010e36df54b7e8Timo Sirainen conn->to = timeout_add(1000*AUTH_MASTER_IDLE_SECS,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen const char *p;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* make sure we're not sending any characters that have a special
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainenstatic int auth_master_run_cmd_pre(struct auth_master_connection *conn,
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainen const char *cmd)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen const char *str;
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainenstatic int auth_master_run_cmd_post(struct auth_master_connection *conn)
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainenstatic int auth_master_run_cmd(struct auth_master_connection *conn,
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainen const char *cmd)
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainenstatic unsigned int
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainenauth_master_next_request_id(struct auth_master_connection *conn)
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen /* avoid zero */
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainenauth_user_info_export(string_t *str, const struct auth_user_info *info)
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen str_printfa(str, "\tlip=%s", net_ip2addr(&info->local_ip));
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen str_printfa(str, "\tlport=%d", info->local_port);
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen str_printfa(str, "\trip=%s", net_ip2addr(&info->remote_ip));
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen str_printfa(str, "\trport=%d", info->remote_port);
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainenint auth_master_user_lookup(struct auth_master_connection *conn,
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen const char *user, const struct auth_user_info *info,
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen const char *const **fields_r)
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen if (!is_valid_string(user) || !is_valid_string(info->service)) {
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen /* non-allowed characters, the user can't exist */
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen conn->reply_callback = auth_lookup_reply_callback;
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen conn->prefix = t_strdup_printf("userdb lookup(%s)", user);
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen if (ctx.return_value <= 0 || ctx.fields[0] == NULL) {
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen i_error("Userdb lookup didn't return username");
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainenvoid auth_user_fields_parse(const char *const *fields, pool_t pool,
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen p_array_init(&reply_r->extra_fields, pool, 64);
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen if (str_to_uid(*fields + 4, &reply_r->uid) < 0)
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen } else if (strncmp(*fields, "gid=", 4) == 0) {
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen if (str_to_gid(*fields + 4, &reply_r->gid) < 0)
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen reply_r->chroot = p_strdup(pool, *fields + 7);
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen array_append(&reply_r->extra_fields, &field, 1);
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainenint auth_master_pass_lookup(struct auth_master_connection *conn,
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen const char *user, const struct auth_user_info *info,
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen if (!is_valid_string(user) || !is_valid_string(info->service)) {
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen /* non-allowed characters, the user can't exist */
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen conn->reply_callback = auth_lookup_reply_callback;
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen conn->prefix = t_strdup_printf("passdb lookup(%s)", user);
a3ed53b37b359597dde3817c0a1e8863c7805077Timo Sirainen unsigned int count;
a43145989f87ec68754e21234e7b6d892c4a4421Timo Sirainenauth_cache_flush_reply_callback(const char *cmd, const char *const *args,
a3ed53b37b359597dde3817c0a1e8863c7805077Timo Sirainen else if (args[0] == NULL || str_to_uint(args[0], &ctx->count) < 0)
a43145989f87ec68754e21234e7b6d892c4a4421Timo Sirainenint auth_master_cache_flush(struct auth_master_connection *conn,
a43145989f87ec68754e21234e7b6d892c4a4421Timo Sirainen const char *const *users, unsigned int *count_r)
a43145989f87ec68754e21234e7b6d892c4a4421Timo Sirainen conn->reply_callback = auth_cache_flush_reply_callback;
a43145989f87ec68754e21234e7b6d892c4a4421Timo Sirainen str_printfa(str, "CACHE-FLUSH\t%u", auth_master_next_request_id(conn));
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainenauth_user_list_reply_callback(const char *cmd, const char *const *args,
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen struct auth_master_user_list_ctx *ctx = context;
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen if (args[0] != NULL && strcmp(args[0], "fail") == 0) {
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainen } else if (strcmp(cmd, "LIST") == 0 && args[0] != NULL) {
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen /* we'll just read all the users into memory. otherwise we'd
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen have to use a separate connection for listing and there's
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen a higher chance of a failure since the connection could be
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen open to dovecot-auth for a long time. */
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainen i_error("User listing returned invalid input");
665e9d14c005b65d95eae0baaa471c51e5caca73Timo Sirainenauth_master_user_list_init(struct auth_master_connection *conn,
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainen ctx = i_new(struct auth_master_user_list_ctx, 1);
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen conn->reply_callback = auth_user_list_reply_callback;
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainen if (auth_master_run_cmd_pre(conn, str_c(str)) < 0)
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainenconst char *auth_master_user_list_next(struct auth_master_user_list_ctx *ctx)
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainen /* try to read already buffered input */
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainen /* wait for more data */
e262b92fc5c1a73e5edac5b9f787c36325e2b6a1Timo Sirainen if (ctx->finished || ctx->failed || ctx->conn->aborted)
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainenint auth_master_user_list_deinit(struct auth_master_user_list_ctx **_ctx)