static int auth_server_connection_input_line(struct auth_server_connection *conn, const char *line) { const char *const *args; if (conn->client->debug) i_debug("auth input: %s", line); args = t_strsplit_tab(line); if (args[0] == NULL) { i_error("Auth server sent empty line"); return -1; } if (strcmp(args[0], "OK") == 0) return auth_server_input_ok(conn, args + 1); else if (strcmp(args[0], "CONT") == 0) return auth_server_input_cont(conn, args + 1); else if (strcmp(args[0], "FAIL") == 0) return auth_server_input_fail(conn, args + 1); else if (strcmp(args[0], "MECH") == 0) return auth_server_input_mech(conn, args + 1); else if (strcmp(args[0], "SPID") == 0) return auth_server_input_spid(conn, args + 1); else if (strcmp(args[0], "CUID") == 0) return auth_server_input_cuid(conn, args + 1); else if (strcmp(args[0], "COOKIE") == 0) return auth_server_input_cookie(conn, args + 1); else if (strcmp(args[0], "DONE") == 0) return auth_server_input_done(conn); else { i_error("Auth server sent unknown command: %s", args[0]); return -1; } }
static bool master_login_auth_input_fail(struct master_login_auth *auth, const char *args_line) { struct master_login_auth_request *request; const char *const *args, *error = NULL; unsigned int i, id; args = t_strsplit_tab(args_line); if (args[0] == NULL || str_to_uint(args[0], &id) < 0) { i_error("Auth server sent broken FAIL line"); return FALSE; } for (i = 1; args[i] != NULL; i++) { if (strncmp(args[i], "reason=", 7) == 0) error = args[i] + 7; } request = master_login_auth_lookup_request(auth, id); if (request != NULL) { if (error == NULL) { request_internal_failure(request, "Internal auth failure"); } else { i_error("Internal auth failure: %s " "(client-pid=%u client-id=%u)", error, request->client_pid, request->auth_id); request->callback(NULL, error, request->context); } i_free(request); } return TRUE; }
static void cmd_director_status_user(struct director_context *ctx, char *argv[]) { const char *user = argv[0], *tag = argv[1]; const char *line, *const *args; unsigned int expires; director_send(ctx, t_strdup_printf("USER-LOOKUP\t%s\t%s\n", user, tag != NULL ? tag : "")); line = i_stream_read_next_line(ctx->input); if (line == NULL) { i_error("Lookup failed"); doveadm_exit_code = EX_TEMPFAIL; return; } args = t_strsplit_tab(line); if (str_array_length(args) != 4 || str_to_uint(args[1], &expires) < 0) { i_error("Invalid reply from director"); doveadm_exit_code = EX_PROTOCOL; return; } if (args[0][0] != '\0') { printf("Current: %s (expires %s)\n", args[0], unixdate2str(expires)); } else { printf("Current: not assigned\n"); } printf("Hashed: %s\n", args[2]); printf("Initial config: %s\n", args[3]); director_disconnect(ctx); }
bool passdb_cache_lookup_credentials(struct auth_request *request, const char *key, const char **password_r, const char **scheme_r, enum passdb_result *result_r, bool use_expired) { const char *value, *const *list; struct auth_cache_node *node; bool neg_expired; if (passdb_cache == NULL) return FALSE; if (!passdb_cache_lookup(request, key, use_expired, &node, &value, &neg_expired)) return FALSE; if (*value == '\0') { /* negative cache entry */ *result_r = PASSDB_RESULT_USER_UNKNOWN; *password_r = NULL; *scheme_r = NULL; return TRUE; } list = t_strsplit_tab(value); auth_request_set_fields(request, list + 1, NULL); *result_r = PASSDB_RESULT_OK; *password_r = *list[0] == '\0' ? NULL : list[0]; *scheme_r = password_get_scheme(password_r); i_assert(*scheme_r != NULL || *password_r == NULL); return TRUE; }
static int master_input_cache_flush(struct auth_master_connection *conn, const char *args) { const char *const *list; unsigned int count; /* <id> [<user> [<user> [..]] */ list = t_strsplit_tab(args); if (list[0] == NULL) { i_error("BUG: doveadm sent broken CACHE-FLUSH"); return FALSE; } if (passdb_cache == NULL) { /* cache disabled */ count = 0; } else if (list[1] == NULL) { /* flush the whole cache */ count = auth_cache_clear(passdb_cache); } else { count = auth_cache_clear_users(passdb_cache, list+1); } (void)o_stream_send_str(conn->output, t_strdup_printf("OK\t%s\t%u\n", list[0], count)); return TRUE; }
bool passdb_cache_verify_plain(struct auth_request *request, const char *key, const char *password, enum passdb_result *result_r, bool use_expired) { const char *value, *cached_pw, *scheme, *const *list; struct auth_cache_node *node; int ret; bool neg_expired; if (passdb_cache == NULL || key == NULL) return FALSE; if (!passdb_cache_lookup(request, key, use_expired, &node, &value, &neg_expired)) return FALSE; if (*value == '\0') { /* negative cache entry */ auth_request_log_unknown_user(request, AUTH_SUBSYS_DB); *result_r = PASSDB_RESULT_USER_UNKNOWN; return TRUE; } list = t_strsplit_tab(value); cached_pw = list[0]; if (*cached_pw == '\0') { /* NULL password */ auth_request_log_info(request, AUTH_SUBSYS_DB, "Cached NULL password access"); ret = 1; } else { scheme = password_get_scheme(&cached_pw); i_assert(scheme != NULL); ret = auth_request_password_verify(request, password, cached_pw, scheme, AUTH_SUBSYS_DB); if (ret == 0 && (node->last_success || neg_expired)) { /* a) the last authentication was successful. assume that the password was changed and cache is expired. b) negative TTL reached, use it for password mismatches too. */ node->last_success = FALSE; return FALSE; } } node->last_success = ret > 0; /* save the extra_fields only after we know we're using the cached data */ auth_request_set_fields(request, list + 1, NULL); *result_r = ret > 0 ? PASSDB_RESULT_OK : PASSDB_RESULT_PASSWORD_MISMATCH; return TRUE; }
static int master_input_auth_request(struct auth_master_connection *conn, const char *args, const char *cmd, struct auth_request **request_r, const char **error_r) { struct auth_request *auth_request; const char *const *list, *name, *arg, *username; unsigned int id; /* <id> <userid> [<parameters>] */ list = t_strsplit_tab(args); if (list[0] == NULL || list[1] == NULL || str_to_uint(list[0], &id) < 0) { i_error("BUG: Master sent broken %s", cmd); return -1; } auth_request = auth_request_new_dummy(); auth_request->id = id; auth_request->master = conn; auth_master_connection_ref(conn); username = list[1]; for (list += 2; *list != NULL; list++) { arg = strchr(*list, '='); if (arg == NULL) { name = *list; arg = ""; } else { name = t_strdup_until(*list, arg); arg++; } (void)auth_request_import_info(auth_request, name, arg); } if (auth_request->service == NULL) { i_error("BUG: Master sent %s request without service", cmd); auth_request_unref(&auth_request); auth_master_connection_unref(&conn); return -1; } auth_request_init(auth_request); if (!auth_request_set_username(auth_request, username, error_r)) { *request_r = auth_request; return 0; } *request_r = auth_request; return 1; }
static int config_read_reply_header(struct istream *istream, const char *path, pool_t pool, const struct master_service_settings_input *input, struct master_service_settings_output *output_r, const char **error_r) { const char *line; ssize_t ret; while ((ret = i_stream_read(istream)) > 0) { line = i_stream_next_line(istream); if (line != NULL) break; } if (ret <= 0) { if (ret == 0) return 1; *error_r = istream->stream_errno != 0 ? t_strdup_printf("read(%s) failed: %s", path, i_stream_get_error(istream)) : t_strdup_printf("read(%s) failed: EOF", path); return -1; } T_BEGIN { const char *const *arg = t_strsplit_tab(line); ARRAY_TYPE(const_string) services; p_array_init(&services, pool, 8); for (; *arg != NULL; arg++) { if (strcmp(*arg, "service-uses-local") == 0) output_r->service_uses_local = TRUE; else if (strcmp(*arg, "service-uses-remote") == 0) output_r->service_uses_remote = TRUE; if (strcmp(*arg, "used-local") == 0) output_r->used_local = TRUE; else if (strcmp(*arg, "used-remote") == 0) output_r->used_remote = TRUE; else if (strncmp(*arg, "service=", 8) == 0) { const char *name = p_strdup(pool, *arg + 8); array_append(&services, &name, 1); } } if (input->service == NULL) { array_append_zero(&services); output_r->specific_services = array_idx(&services, 0); } } T_END; return 0; }
static void cmd_director_status(int argc, char *argv[]) { struct director_context *ctx; const char *line, *const *args; ctx = cmd_director_init(argc, argv, "a:t:", cmd_director_status); if (argv[optind] != NULL) { cmd_director_status_user(ctx, argv+optind); return; } doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header_simple("mail server ip"); doveadm_print_header_simple("tag"); doveadm_print_header_simple("vhosts"); doveadm_print_header_simple("state"); doveadm_print_header("state-changed", "state changed", 0); doveadm_print_header_simple("users"); director_send(ctx, "HOST-LIST\n"); while ((line = i_stream_read_next_line(ctx->input)) != NULL) { if (*line == '\0') break; T_BEGIN { unsigned int arg_count; time_t ts; args = t_strsplit_tab(line); arg_count = str_array_length(args); if (arg_count >= 6) { /* ip vhosts users tag updown updown-ts */ doveadm_print(args[0]); doveadm_print(args[3]); doveadm_print(args[1]); doveadm_print(args[4][0] == 'D' ? "down" : "up"); if (str_to_time(args[5], &ts) < 0 || ts <= 0) doveadm_print("-"); else doveadm_print(unixdate2str(ts)); doveadm_print(args[2]); } } T_END; } if (line == NULL) { i_error("Director disconnected unexpectedly"); doveadm_exit_code = EX_TEMPFAIL; } director_disconnect(ctx); }
static void penalty_parse_line(const char *line, struct penalty_line *line_r) { const char *const *args = t_strsplit_tab(line); const char *ident = args[0]; const char *penalty_str = args[1]; const char *last_penalty_str = args[2]; const char *last_update_str = args[3]; memset(line_r, 0, sizeof(*line_r)); (void)net_addr2ip(ident, &line_r->ip); line_r->penalty = strtoul(penalty_str, NULL, 10); line_r->last_penalty = strtoul(last_penalty_str, NULL, 10); line_r->last_update = strtoul(last_update_str, NULL, 10); }
static void penalty_parse_line(const char *line, struct penalty_line *line_r) { const char *const *args = t_strsplit_tab(line); const char *ident = args[0]; const char *penalty_str = args[1]; const char *last_penalty_str = args[2]; const char *last_update_str = args[3]; memset(line_r, 0, sizeof(*line_r)); (void)net_addr2ip(ident, &line_r->ip); if (str_to_uint(penalty_str, &line_r->penalty) < 0 || str_to_time(last_penalty_str, &line_r->last_penalty) < 0 || str_to_time(last_update_str, &line_r->last_update) < 0) i_fatal("Read invalid penalty line: %s", line); }
static bool master_input_request(struct auth_master_connection *conn, const char *args) { struct auth_client_connection *client_conn; const char *const *list, *const *params; unsigned int id, client_pid, client_id; uint8_t cookie[MASTER_AUTH_COOKIE_SIZE]; buffer_t buf; /* <id> <client-pid> <client-id> <cookie> [<parameters>] */ list = t_strsplit_tab(args); if (str_array_length(list) < 4 || str_to_uint(list[0], &id) < 0 || str_to_uint(list[1], &client_pid) < 0 || str_to_uint(list[2], &client_id) < 0) { i_error("BUG: Master sent broken REQUEST"); return FALSE; } buffer_create_from_data(&buf, cookie, sizeof(cookie)); if (hex_to_binary(list[3], &buf) < 0) { i_error("BUG: Master sent broken REQUEST cookie"); return FALSE; } params = list + 4; client_conn = auth_client_connection_lookup(client_pid); if (client_conn == NULL) { i_error("Master requested auth for nonexistent client %u", client_pid); o_stream_nsend_str(conn->output, t_strdup_printf("FAIL\t%u\n", id)); } else if (memcmp(client_conn->cookie, cookie, sizeof(cookie)) != 0) { i_error("Master requested auth for client %u with invalid cookie", client_pid); o_stream_nsend_str(conn->output, t_strdup_printf("FAIL\t%u\n", id)); } else if (!auth_request_handler_master_request( client_conn->request_handler, conn, id, client_id, params)) { i_error("Master requested auth for non-login client %u", client_pid); o_stream_nsend_str(conn->output, t_strdup_printf("FAIL\t%u\n", id)); } return TRUE; }
static enum passdb_result auth_worker_reply_parse(struct auth_request *request, const char *reply) { enum passdb_result ret; const char *const *args; args = t_strsplit_tab(reply); if (strcmp(*args, "OK") == 0 && args[1] != NULL && args[2] != NULL) { /* OK \t user \t password [\t extra] */ auth_request_set_field(request, "user", args[1], NULL); auth_worker_reply_parse_args(request, args + 2); return PASSDB_RESULT_OK; } if (strcmp(*args, "FAIL") == 0 && args[1] != NULL) { int result; /* FAIL \t result [\t user \t password [\t extra]] */ if (str_to_int(args[1], &result) < 0) { /* shouldn't happen */ } else { ret = (enum passdb_result)result; if (ret == PASSDB_RESULT_OK) { /* shouldn't happen */ } else if (args[2] == NULL) { /* internal failure most likely */ return ret; } else if (args[3] != NULL) { if (*args[2] != '\0') { auth_request_set_field(request, "user", args[2], NULL); } auth_worker_reply_parse_args(request, args + 3); return ret; } } } auth_request_log_error(request, AUTH_SUBSYS_DB, "Received invalid reply from worker: %s", reply); return PASSDB_RESULT_INTERNAL_FAILURE; }
bool passdb_cache_lookup_credentials(struct auth_request *request, const char *key, const char **password_r, const char **scheme_r, enum passdb_result *result_r, bool use_expired) { const char *value, *const *list; struct auth_cache_node *node; bool expired, neg_expired; if (passdb_cache == NULL) return FALSE; value = auth_cache_lookup(passdb_cache, request, key, &node, &expired, &neg_expired); if (value == NULL || (expired && !use_expired)) { auth_request_log_debug(request, AUTH_SUBSYS_DB, value == NULL ? "cache miss" : "cache expired"); return FALSE; } passdb_cache_log_hit(request, value); if (*value == '\0') { /* negative cache entry */ *result_r = PASSDB_RESULT_USER_UNKNOWN; *password_r = NULL; *scheme_r = NULL; return TRUE; } list = t_strsplit_tab(value); auth_request_set_fields(request, list + 1, NULL); *result_r = PASSDB_RESULT_OK; *password_r = *list[0] == '\0' ? NULL : list[0]; *scheme_r = password_get_scheme(password_r); i_assert(*scheme_r != NULL || *password_r == NULL); return TRUE; }
static bool master_login_auth_input_user(struct master_login_auth *auth, const char *args) { struct master_login_auth_request *request; const char *const *list; unsigned int id; /* <id> <userid> [..] */ list = t_strsplit_tab(args); if (list[0] == NULL || list[1] == NULL || str_to_uint(list[0], &id) < 0) { i_error("Auth server sent corrupted USER line"); return FALSE; } request = master_login_auth_lookup_request(auth, id); if (request != NULL) { request->callback(list + 1, NULL, request->context); i_free(request); } return TRUE; }
static void auth_input_line(const char *line, void *context) { struct login_connection *conn = context; struct login_host_request *request; const char *const *args, *line_params, *username = NULL; bool proxy = FALSE, host = FALSE; if (line == NULL) { /* auth connection died -> kill also this login connection */ login_connection_deinit(&conn); return; } if (!conn->userdb && strncmp(line, "OK\t", 3) == 0) line_params = line + 3; else if (conn->userdb && strncmp(line, "PASS\t", 5) == 0) line_params = line + 5; else { login_connection_send_line(conn, line); return; } /* OK <id> [<parameters>] */ args = t_strsplit_tab(line_params); if (*args != NULL) { /* we should always get here, but in case we don't just forward as-is and let login process handle the error. */ args++; } for (; *args != NULL; args++) { if (strncmp(*args, "proxy", 5) == 0 && ((*args)[5] == '=' || (*args)[5] == '\0')) proxy = TRUE; else if (strncmp(*args, "host=", 5) == 0) host = TRUE; else if (strncmp(*args, "destuser="******"user=", 5) == 0) { if (username == NULL) username = *args + 5; } } if (!proxy || host || username == NULL) { login_connection_send_line(conn, line); return; } if (*conn->dir->set->master_user_separator != '\0') { /* with master user logins we still want to use only the login username */ username = t_strcut(username, *conn->dir->set->master_user_separator); } /* we need to add the host. the lookup might be asynchronous */ request = i_new(struct login_host_request, 1); request->conn = conn; request->line = i_strdup(line); request->username = i_strdup(username); conn->refcount++; director_request(conn->dir, username, login_host_callback, request); }
static void auth_input_line(const char *line, void *context) { struct login_connection *conn = context; struct login_host_request *request, temp_request; const char *const *args, *line_params, *username = NULL, *tag = ""; bool proxy = FALSE, host = FALSE; if (line == NULL) { /* auth connection died -> kill also this login connection */ login_connection_deinit(&conn); return; } if (conn->type != LOGIN_CONNECTION_TYPE_USERDB && strncmp(line, "OK\t", 3) == 0) line_params = line + 3; else if (conn->type == LOGIN_CONNECTION_TYPE_USERDB && strncmp(line, "PASS\t", 5) == 0) line_params = line + 5; else { login_connection_send_line(conn, line); return; } /* OK <id> [<parameters>] */ args = t_strsplit_tab(line_params); if (*args != NULL) { /* we should always get here, but in case we don't just forward as-is and let login process handle the error. */ args++; } memset(&temp_request, 0, sizeof(temp_request)); for (; *args != NULL; args++) { if (strncmp(*args, "proxy", 5) == 0 && ((*args)[5] == '=' || (*args)[5] == '\0')) proxy = TRUE; else if (strncmp(*args, "host=", 5) == 0) host = TRUE; else if (strncmp(*args, "lip=", 4) == 0) { if (net_addr2ip((*args) + 4, &temp_request.local_ip) < 0) i_error("auth sent invalid lip field: %s", (*args) + 6); } else if (strncmp(*args, "lport=", 6) == 0) { if (str_to_uint((*args) + 6, &temp_request.local_port) < 0) i_error("auth sent invalid lport field: %s", (*args) + 6); } else if (strncmp(*args, "port=", 5) == 0) { if (str_to_uint((*args) + 5, &temp_request.dest_port) < 0) i_error("auth sent invalid port field: %s", (*args) + 6); } else if (strncmp(*args, "destuser="******"director_tag=", 13) == 0) tag = *args + 13; else if (strncmp(*args, "director_proxy_maybe", 20) == 0 && ((*args)[20] == '=' || (*args)[20] == '\0')) temp_request.director_proxy_maybe = TRUE; else if (strncmp(*args, "user=", 5) == 0) { if (username == NULL) username = *args + 5; } } if ((!proxy && !temp_request.director_proxy_maybe) || host || username == NULL) { login_connection_send_line(conn, line); return; } if (*conn->dir->set->master_user_separator != '\0') { /* with master user logins we still want to use only the login username */ username = t_strcut(username, *conn->dir->set->master_user_separator); } /* we need to add the host. the lookup might be asynchronous */ request = i_new(struct login_host_request, 1); *request = temp_request; request->conn = conn; request->line = i_strdup(line); request->username = i_strdup(username); conn->refcount++; director_request(conn->dir, username, tag, login_host_callback, request); }
static void client_connected(struct master_service_connection *conn) { enum mail_storage_service_flags flags = MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS; string_t *instr, *keys; const char **args, *key, *value, *error, *version_line, *data_line; struct mail_storage_service_ctx *service_ctx; struct mail_storage_service_input input; struct mail_storage_service_user *user; char buf[1024]; unsigned int i, socket_count; int fd = -1; ssize_t ret; alarm(SCRIPT_LOGIN_READ_TIMEOUT_SECS); net_set_nonblock(conn->fd, FALSE); instr = t_str_new(1024); ret = fd_read(conn->fd, buf, sizeof(buf), &fd); while (ret > 0) { str_append_n(instr, buf, ret); if (buf[ret-1] == '\n' && strchr(str_c(instr), '\n')[1] != '\0') { str_truncate(instr, str_len(instr)-1); break; } ret = read(conn->fd, buf, sizeof(buf)); } version_line = str_c(instr); data_line = strchr(version_line, '\n'); if (data_line != NULL) version_line = t_strdup_until(version_line, data_line++); else version_line = NULL; if (ret > 0 || version_line != NULL) { if (version_line == NULL || !version_string_verify(version_line, "script-login", SCRIPT_LOGIN_PROTOCOL_VERSION_MAJOR)) { i_fatal("Client not compatible with this binary " "(connecting to wrong socket?)"); } } if (ret <= 0) { if (ret < 0) i_fatal("read() failed: %m"); else i_fatal("read() failed: disconnected"); } if (fd == -1) i_fatal("client fd not received"); alarm(0); /* put everything to environment */ env_clean(); keys = t_str_new(256); args = t_strsplit_tab(data_line); if (str_array_length(args) < 3) i_fatal("Missing input fields"); i = 0; memset(&input, 0, sizeof(input)); input.module = "mail"; /* need to get mail_uid, mail_gid */ input.service = "script-login"; (void)net_addr2ip(args[i++], &input.local_ip); (void)net_addr2ip(args[i++], &input.remote_ip); input.username = args[i++]; input.userdb_fields = args + i; env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&input.local_ip), NULL)); env_put(t_strconcat("IP=", net_ip2addr(&input.remote_ip), NULL)); env_put(t_strconcat("USER="******"%s ", key); } } env_put(t_strconcat(ENV_USERDB_KEYS"=", str_c(keys), NULL)); master_service_init_log(master_service, t_strdup_printf("script-login(%s): ", input.username)); if (drop_to_userdb_privileges) { service_ctx = mail_storage_service_init(master_service, NULL, flags); if (mail_storage_service_lookup(service_ctx, &input, &user, &error) <= 0) i_fatal("%s", error); mail_storage_service_restrict_setenv(service_ctx, user); /* we can't exec anything in a chroot */ env_remove("RESTRICT_CHROOT"); restrict_access_by_env(getenv("HOME"), TRUE); } if (dup2(fd, STDIN_FILENO) < 0) i_fatal("dup2() failed: %m"); if (dup2(fd, STDOUT_FILENO) < 0) i_fatal("dup2() failed: %m"); if (close(fd) < 0) i_fatal("close() failed: %m"); if (conn->fd != SCRIPT_COMM_FD) { if (dup2(conn->fd, SCRIPT_COMM_FD) < 0) i_fatal("dup2() failed: %m"); if (close(conn->fd) < 0) i_fatal("close() failed: %m"); } /* close all listener sockets */ socket_count = master_service_get_socket_count(master_service); for (i = 0; i < socket_count; i++) { if (close(MASTER_LISTEN_FD_FIRST + i) < 0) i_error("close(listener) failed: %m"); } if (close(MASTER_STATUS_FD) < 0) i_error("close(status) failed: %m"); execvp_const(exec_args[0], exec_args); }
bool auth_request_handler_auth_begin(struct auth_request_handler *handler, const char *args) { const struct mech_module *mech; struct auth_request *request; const char *const *list, *name, *arg, *initial_resp; void *initial_resp_data; unsigned int id; buffer_t *buf; i_assert(!handler->destroyed); /* <id> <mechanism> [...] */ list = t_strsplit_tab(args); if (list[0] == NULL || list[1] == NULL || str_to_uint(list[0], &id) < 0) { i_error("BUG: Authentication client %u " "sent broken AUTH request", handler->client_pid); return FALSE; } if (handler->token_auth) { mech = &mech_dovecot_token; if (strcmp(list[1], mech->mech_name) != 0) { /* unsupported mechanism */ i_error("BUG: Authentication client %u requested invalid " "authentication mechanism %s (DOVECOT-TOKEN required)", handler->client_pid, str_sanitize(list[1], MAX_MECH_NAME_LEN)); return FALSE; } } else { mech = mech_module_find(list[1]); if (mech == NULL) { /* unsupported mechanism */ i_error("BUG: Authentication client %u requested unsupported " "authentication mechanism %s", handler->client_pid, str_sanitize(list[1], MAX_MECH_NAME_LEN)); return FALSE; } } request = auth_request_new(mech); request->handler = handler; request->connect_uid = handler->connect_uid; request->client_pid = handler->client_pid; request->id = id; request->auth_only = handler->master_callback == NULL; /* parse optional parameters */ initial_resp = NULL; for (list += 2; *list != NULL; list++) { arg = strchr(*list, '='); if (arg == NULL) { name = *list; arg = ""; } else { name = t_strdup_until(*list, arg); arg++; } if (auth_request_import_auth(request, name, arg)) ; else if (strcmp(name, "resp") == 0) { initial_resp = arg; /* this must be the last parameter */ list++; break; } } if (*list != NULL) { i_error("BUG: Authentication client %u " "sent AUTH parameters after 'resp'", handler->client_pid); auth_request_unref(&request); return FALSE; } if (request->service == NULL) { i_error("BUG: Authentication client %u " "didn't specify service in request", handler->client_pid); auth_request_unref(&request); return FALSE; } if (hash_table_lookup(handler->requests, POINTER_CAST(id)) != NULL) { i_error("BUG: Authentication client %u " "sent a duplicate ID %u", handler->client_pid, id); auth_request_unref(&request); return FALSE; } auth_request_init(request); request->to_abort = timeout_add(MASTER_AUTH_SERVER_TIMEOUT_SECS * 1000, auth_request_timeout, request); hash_table_insert(handler->requests, POINTER_CAST(id), request); if (request->set->ssl_require_client_cert && !request->valid_client_cert) { /* we fail without valid certificate */ auth_request_handler_auth_fail(handler, request, "Client didn't present valid SSL certificate"); return TRUE; } /* Empty initial response is a "=" base64 string. Completely empty string shouldn't really be sent, but at least Exim does it, so just allow it for backwards compatibility.. */ if (initial_resp != NULL && *initial_resp != '\0') { size_t len = strlen(initial_resp); buf = buffer_create_dynamic(pool_datastack_create(), MAX_BASE64_DECODED_SIZE(len)); if (base64_decode(initial_resp, len, NULL, buf) < 0) { auth_request_handler_auth_fail(handler, request, "Invalid base64 data in initial response"); return TRUE; } initial_resp_data = p_malloc(request->pool, I_MAX(buf->used, 1)); memcpy(initial_resp_data, buf->data, buf->used); request->initial_response = initial_resp_data; request->initial_response_len = buf->used; } /* handler is referenced until auth_request_handler_reply() is called. */ handler->refcount++; /* before we start authenticating, see if we need to wait first */ auth_penalty_lookup(auth_penalty, request, auth_penalty_callback); return TRUE; }