bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen HASH_TABLE(void *, struct auth_request *) requests;
6aafdd81aa1e12c127941c1ebd87e8ee4697ec3eTimo Sirainen auth_master_request_callback_t *master_callback;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainenstatic ARRAY(struct auth_request *) auth_failures_arr;
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenstatic void auth_failure_timeout(void *context) ATTR_NULL(1);
6aafdd81aa1e12c127941c1ebd87e8ee4697ec3eTimo Sirainenauth_request_handler_create(bool token_auth, auth_client_request_callback_t *callback,
6aafdd81aa1e12c127941c1ebd87e8ee4697ec3eTimo Sirainen auth_master_request_callback_t *master_callback)
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen pool = pool_alloconly_create("auth request handler", 4096);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen handler = p_new(pool, struct auth_request_handler, 1);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_table_create_direct(&handler->requests, pool, 0);
a7e2c98560cf54dc656711a237cb07da8a5a9ee4Timo Sirainenauth_request_handler_get_request_count(struct auth_request_handler *handler)
9d75363d3fbabc2fbc2d80f06672e3ed8965804aTimo Sirainenvoid auth_request_handler_abort_requests(struct auth_request_handler *handler)
f19cf95ae8fc233567b1c7751595eb66876d684aTimo Sirainen iter = hash_table_iterate_init(handler->requests);
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen while (hash_table_iterate(iter, handler->requests, &key, &auth_request)) {
cc4d0d30fbba883d5d1b600646491fb77bdb989cTimo Sirainen /* can't abort a pending passdb/userdb lookup */
9d75363d3fbabc2fbc2d80f06672e3ed8965804aTimo Sirainenvoid auth_request_handler_unref(struct auth_request_handler **_handler)
fbee9bffb56d882b98146dd0de76a5bcccc2bdc3Timo Sirainen struct auth_request_handler *handler = *_handler;
9d75363d3fbabc2fbc2d80f06672e3ed8965804aTimo Sirainen i_assert(hash_table_count(handler->requests) == 0);
9d75363d3fbabc2fbc2d80f06672e3ed8965804aTimo Sirainen /* notify parent that we're done with all requests */
5363f51ad46344f4e5952f2fef211a7cf8f95ddcTimo Sirainenvoid auth_request_handler_destroy(struct auth_request_handler **_handler)
5363f51ad46344f4e5952f2fef211a7cf8f95ddcTimo Sirainen struct auth_request_handler *handler = *_handler;
70905e51a5148bd5613cb04720807177474a2496Timo Sirainenvoid auth_request_handler_set(struct auth_request_handler *handler,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainenstatic void auth_request_handler_remove(struct auth_request_handler *handler,
ea95a057fa5f02d50027122cacd3147fce7679faTimo Sirainen /* already removed it */
e69e7b734b625de1f8921b7e0d92afa1df6b900dTimo Sirainen /* if db lookup is stuck, this call doesn't actually free the auth
e69e7b734b625de1f8921b7e0d92afa1df6b900dTimo Sirainen request, so make sure we don't get back here. */
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen hash_table_remove(handler->requests, POINTER_CAST(request->id));
eb7b8855cc45292334056f425645215e348ec493Timo Sirainenauth_str_add_keyvalue(string_t *dest, const char *key, const char *value)
eb7b8855cc45292334056f425645215e348ec493Timo Sirainenauth_str_append_extra_fields(struct auth_request *request, string_t *dest)
0693f78ec64fd8ffedbf84408e6b4fc9c4c2b89fTimo Sirainen if (!auth_fields_is_empty(request->extra_fields)) {
0693f78ec64fd8ffedbf84408e6b4fc9c4c2b89fTimo Sirainen auth_fields_append(request->extra_fields, dest,
e5d6ec3335b472e9bd9895d75b7723706c69e9fbTimo Sirainen null_strcmp(request->original_username, request->user) != 0 &&
e5d6ec3335b472e9bd9895d75b7723706c69e9fbTimo Sirainen !auth_fields_exists(request->extra_fields, "original_user")) {
e5d6ec3335b472e9bd9895d75b7723706c69e9fbTimo Sirainen !auth_fields_exists(request->extra_fields, "auth_user"))
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen auth_str_add_keyvalue(dest, "auth_user", request->master_user);
31633d676642b83305b8d46da495d9bb4e2d1ff8Timo Sirainen auth_fields_exists(request->extra_fields, "proxy")) {
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen /* we're proxying */
ee6df9526e9716b3f1734d85b566e00fc41208bcTimo Sirainen if (!auth_fields_exists(request->extra_fields, "pass") &&
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen /* send back the password that was sent by user
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen (not the password in passdb). */
ee6df9526e9716b3f1734d85b566e00fc41208bcTimo Sirainen !auth_fields_exists(request->extra_fields, "master")) {
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen /* the master username needs to be forwarded */
eb7b8855cc45292334056f425645215e348ec493Timo Sirainenauth_request_handle_failure(struct auth_request *request, const char *reply)
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen struct auth_request_handler *handler = request->handler;
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen /* we came here from flush_failures() */
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen /* remove the request from requests-list */
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen auth_request_handler_remove(handler, request);
31633d676642b83305b8d46da495d9bb4e2d1ff8Timo Sirainen if (auth_fields_exists(request->extra_fields, "nodelay")) {
9d75363d3fbabc2fbc2d80f06672e3ed8965804aTimo Sirainen /* passdb specifically requested not to delay the reply. */
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen /* failure. don't announce it immediately to avoid
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen a) timing attacks, b) flooding */
4b335788eb41dec2de5f78459d96387fcc710010Timo Sirainen timeout_add_short(AUTH_FAILURE_DELAY_CHECK_MSECS,
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainenauth_request_handler_reply_success_finish(struct auth_request *request)
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen struct auth_request_handler *handler = request->handler;
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen if (request->last_penalty != 0 && auth_penalty != NULL) {
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen /* reset penalty */
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen auth_penalty_update(auth_penalty, request, 0);
068ac108503f9b3c81bbdac3b95db545d479188aTimo Sirainen /* sanitize these fields, since the login code currently assumes they
068ac108503f9b3c81bbdac3b95db545d479188aTimo Sirainen are exactly in this format. */
068ac108503f9b3c81bbdac3b95db545d479188aTimo Sirainen auth_fields_booleanize(request->extra_fields, "nologin");
068ac108503f9b3c81bbdac3b95db545d479188aTimo Sirainen auth_fields_booleanize(request->extra_fields, "proxy");
eb7b8855cc45292334056f425645215e348ec493Timo Sirainen str_printfa(str, "OK\t%u\tuser=", request->id);
31633d676642b83305b8d46da495d9bb4e2d1ff8Timo Sirainen auth_fields_exists(request->extra_fields, "nologin") ||
31633d676642b83305b8d46da495d9bb4e2d1ff8Timo Sirainen auth_fields_exists(request->extra_fields, "proxy")) {
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen /* this request doesn't have to wait for master
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen process to pick it up. delete it */
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen auth_request_handler_remove(handler, request);
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainenauth_request_handler_reply_failure_finish(struct auth_request *request)
3d14f452f62077f849a1fce9f302843c2635f108Timo Sirainen auth_fields_remove(request->extra_fields, "nologin");
eb7b8855cc45292334056f425645215e348ec493Timo Sirainen auth_str_add_keyvalue(str, "user", request->user);
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen else if (request->original_username != NULL) {
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen /* authentication succeeded, but we can't log in
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen as the wanted user */
31633d676642b83305b8d46da495d9bb4e2d1ff8Timo Sirainen if (auth_fields_exists(request->extra_fields, "nodelay")) {
31633d676642b83305b8d46da495d9bb4e2d1ff8Timo Sirainen /* this is normally a hidden field, need to add it explicitly */
eb7b8855cc45292334056f425645215e348ec493Timo Sirainen auth_request_handle_failure(request, str_c(str));
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainenauth_request_handler_proxy_callback(bool success, struct auth_request *request)
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen struct auth_request_handler *handler = request->handler;
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen auth_request_handler_reply_success_finish(request);
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen auth_request_handler_reply_failure_finish(request);
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainenvoid auth_request_handler_reply(struct auth_request *request,
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen struct auth_request_handler *handler = request->handler;
5363f51ad46344f4e5952f2fef211a7cf8f95ddcTimo Sirainen /* the client connection was already closed. we can't do
5363f51ad46344f4e5952f2fef211a7cf8f95ddcTimo Sirainen anything but abort this request */
e8a96ad5c10e9d5c0c4e2e88dd09a38fdb3e34b4Timo Sirainen /* make sure this request is set to finished state
e8a96ad5c10e9d5c0c4e2e88dd09a38fdb3e34b4Timo Sirainen (it's not with result=continue) */
e8a96ad5c10e9d5c0c4e2e88dd09a38fdb3e34b4Timo Sirainen auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
eb7b8855cc45292334056f425645215e348ec493Timo Sirainen str = t_str_new(16 + MAX_BASE64_ENCODED_SIZE(reply_size));
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen str = t_str_new(MAX_BASE64_ENCODED_SIZE(reply_size));
ee6df9526e9716b3f1734d85b566e00fc41208bcTimo Sirainen auth_fields_add(request->extra_fields, "resp",
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen auth_request_handler_reply_failure_finish(request);
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen else if (ret > 0)
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen auth_request_handler_reply_success_finish(request);
91233a89f0060f95542ed661683e5d99a50f1778Timo Sirainen auth_request_handler_reply_failure_finish(request);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* NOTE: request may be destroyed now */
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainenvoid auth_request_handler_reply_continue(struct auth_request *request,
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen auth_request_handler_reply(request, AUTH_CLIENT_RESULT_CONTINUE,
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Boschauth_request_handler_auth_fail_code(struct auth_request_handler *handler,
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_info(request, AUTH_SUBSYS_MECH, "%s", reason);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen auth_request_handler_remove(handler, request);
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch(struct auth_request_handler *handler, struct auth_request *request,
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch auth_request_handler_auth_fail_code(handler, request, "", reason);
48ac75465ae154b1d705f18de6d95045ab714b65Timo Sirainenstatic void auth_request_timeout(struct auth_request *request)
ec7a751d45871fb36b19121fd4d7bd33145bf222Timo Sirainen unsigned int secs = (unsigned int)(time(NULL) - request->last_access);
a72dde3805d0e9148de4caf44d6f4dc167431380Timo Sirainen if (request->state != AUTH_REQUEST_STATE_MECH_CONTINUE) {
a72dde3805d0e9148de4caf44d6f4dc167431380Timo Sirainen /* client's fault */
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_error(request, AUTH_SUBSYS_MECH,
ec7a751d45871fb36b19121fd4d7bd33145bf222Timo Sirainen "Request %u.%u timed out after %u secs, state=%d",
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_info(request, AUTH_SUBSYS_MECH,
ec7a751d45871fb36b19121fd4d7bd33145bf222Timo Sirainen "Request timed out waiting for client to continue authentication "
48ac75465ae154b1d705f18de6d95045ab714b65Timo Sirainen auth_request_handler_remove(request->handler, request);
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainenstatic void auth_request_penalty_finish(struct auth_request *request)
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainenauth_penalty_callback(unsigned int penalty, struct auth_request *request)
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen request->to_penalty = timeout_add(secs * 1000,
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainenbool auth_request_handler_auth_begin(struct auth_request_handler *handler,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen const char *const *list, *name, *arg, *initial_resp;
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* <id> <mechanism> [...] */
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen "sent broken AUTH request", handler->client_pid);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch /* unsupported mechanism */
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("BUG: Authentication client %u requested invalid "
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch "authentication mechanism %s (DOVECOT-TOKEN required)",
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch handler->client_pid, str_sanitize(list[1], MAX_MECH_NAME_LEN));
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch struct auth *auth_default = auth_default_service();
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch mech = mech_register_find(auth_default->reg, list[1]);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch /* unsupported mechanism */
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("BUG: Authentication client %u requested unsupported "
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch "authentication mechanism %s", handler->client_pid,
e31bf6003e580bcb28af333119da9dd1e16fc811Timo Sirainen request->auth_only = handler->master_callback == NULL;
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* parse optional parameters */
ab122a3bbae3b5fd2aad66e2f2840149d98cee52Timo Sirainen if (auth_request_import_auth(request, name, arg))
382c7aec3e3449ed8271c2a202b67cefaa31dc8eTimo Sirainen /* this must be the last parameter */
382c7aec3e3449ed8271c2a202b67cefaa31dc8eTimo Sirainen "sent AUTH parameters after 'resp'",
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen "didn't specify service in request",
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen if (hash_table_lookup(handler->requests, POINTER_CAST(id)) != NULL) {
b5322c8270616a28f2e65cb3a09580c410bb0941Timo Sirainen "sent a duplicate ID %u", handler->client_pid, id);
d99107ddf4d9bccb710994482daf65276a9d6321Timo Sirainen request->to_abort = timeout_add(MASTER_AUTH_SERVER_TIMEOUT_SECS * 1000,
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen hash_table_insert(handler->requests, POINTER_CAST(id), request);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* we fail without valid certificate */
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen auth_request_handler_auth_fail(handler, request,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen "Client didn't present valid SSL certificate");
e4b72bd73bfffda7906faa248eab31f936cfc6faStephan Bosch /* Handle initial respose */
e4b72bd73bfffda7906faa248eab31f936cfc6faStephan Bosch /* No initial response */
8de6351e4f658b1100f0c19ea1af84ef2fe38753Stephan Bosch } else if (handler->conn->version_minor < 2 && *initial_resp == '\0') {
8de6351e4f658b1100f0c19ea1af84ef2fe38753Stephan Bosch /* Some authentication clients like Exim send and empty initial
8de6351e4f658b1100f0c19ea1af84ef2fe38753Stephan Bosch response field when it is in fact absent in the
8de6351e4f658b1100f0c19ea1af84ef2fe38753Stephan Bosch authentication command. This was allowed for older versions
8de6351e4f658b1100f0c19ea1af84ef2fe38753Stephan Bosch of the Dovecot authentication protocol. */
e4b72bd73bfffda7906faa248eab31f936cfc6faStephan Bosch } else if (*initial_resp == '\0' || strcmp(initial_resp, "=") == 0 ) {
e4b72bd73bfffda7906faa248eab31f936cfc6faStephan Bosch /* Empty initial response - Protocols that use SASL often
e4b72bd73bfffda7906faa248eab31f936cfc6faStephan Bosch use '=' to indicate an empty initial response; i.e., to
e4b72bd73bfffda7906faa248eab31f936cfc6faStephan Bosch distinguish it from an absent initial response. However, that
e4b72bd73bfffda7906faa248eab31f936cfc6faStephan Bosch should not be conveyed to the SASL layer (it is not even
e4b72bd73bfffda7906faa248eab31f936cfc6faStephan Bosch valid Base64); only the empty string should be passed on.
e4b72bd73bfffda7906faa248eab31f936cfc6faStephan Bosch Still, we recognize it here anyway, because we used to make
e4b72bd73bfffda7906faa248eab31f936cfc6faStephan Bosch the same mistake. */
e4b72bd73bfffda7906faa248eab31f936cfc6faStephan Bosch /* Initial response encoded in Bas64 */
c147bff818798a979d93537f72f5c1f68f5d5ba8Aki Tuomi buf = t_buffer_create(MAX_BASE64_DECODED_SIZE(len));
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen if (base64_decode(initial_resp, len, NULL, buf) < 0) {
8ac6623677005256bf99ab33a2ed98c69c1d656cStephan Bosch auth_request_handler_auth_fail_code(handler, request,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen "Invalid base64 data in initial response");
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen memcpy(initial_resp_data, buf->data, buf->used);
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen request->initial_response = initial_resp_data;
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen /* handler is referenced until auth_request_handler_reply()
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen is called. */
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen /* before we start authenticating, see if we need to wait first */
2c8ff32886e56a5e037169c9ebef4219f85a5629Timo Sirainen auth_penalty_lookup(auth_penalty, request, auth_penalty_callback);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenbool auth_request_handler_auth_continue(struct auth_request_handler *handler,
c53a3c54d388c0031aaa642e6b14d46eb86d9485Timo Sirainen if (data == NULL || str_to_uint(t_strdup_until(args, data), &id) < 0) {
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen i_error("BUG: Authentication client sent broken CONT request");
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen request = hash_table_lookup(handler->requests, POINTER_CAST(id));
eb7b8855cc45292334056f425645215e348ec493Timo Sirainen "FAIL\t%u\treason=Authentication request timed out", id);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* accept input only once after mechanism has sent a CONT reply */
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen auth_request_handler_auth_fail(handler, request,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen "Unexpected continuation");
c147bff818798a979d93537f72f5c1f68f5d5ba8Aki Tuomi buf = t_buffer_create(MAX_BASE64_DECODED_SIZE(data_len));
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen if (base64_decode(data, data_len, NULL, buf) < 0) {
8ac6623677005256bf99ab33a2ed98c69c1d656cStephan Bosch auth_request_handler_auth_fail_code(handler, request,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen "Invalid base64 data in continued response");
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen /* handler is referenced until auth_request_handler_reply()
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen is called. */
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen auth_request_continue(request, buf->data, buf->used);
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainenstatic void auth_str_append_userdb_extra_fields(struct auth_request *request,
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen auth_fields_append(request->userdb_reply, dest,
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen !auth_fields_exists(request->userdb_reply, "master_user")) {
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen if (*request->set->anonymous_username != '\0' &&
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen strcmp(request->user, request->set->anonymous_username) == 0) {
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen /* this is an anonymous login, either via ANONYMOUS
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen SASL mechanism or simply logging in as the anonymous
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen user via another mechanism */
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen /* generate auth_token when master service provided session_pid */
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen auth_str_add_keyvalue(dest, "auth_token", auth_token);
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen auth_str_add_keyvalue(dest, "auth_user", request->master_user);
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen } else if (request->original_username != NULL &&
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen strcmp(request->original_username, request->user) != 0) {
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainenstatic void userdb_callback(enum userdb_result result,
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen struct auth_request_handler *handler = request->handler;
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainen i_assert(request->state == AUTH_REQUEST_STATE_USERDB);
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
ee6df9526e9716b3f1734d85b566e00fc41208bcTimo Sirainen value = auth_fields_find(request->userdb_reply, "reason");
eb7b8855cc45292334056f425645215e348ec493Timo Sirainen str_printfa(str, "NOTFOUND\t%u", request->id);
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen auth_str_append_userdb_extra_fields(request, str);
eb7b8855cc45292334056f425645215e348ec493Timo Sirainen handler->master_callback(str_c(str), request->master);
ecb1b2d6236942bf82f822e8d0167f0e160b206dTimo Sirainen auth_master_connection_unref(&request->master);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Boschauth_master_request_failed(struct auth_request_handler *handler,
eb7b8855cc45292334056f425645215e348ec493Timo Sirainen unsigned int id)
eb7b8855cc45292334056f425645215e348ec493Timo Sirainen handler->master_callback(t_strdup_printf("FAIL\t%u", id), master);
daa7e7459749ae8f82cd3eed9c44522d81c609a3Timo Sirainenbool auth_request_handler_master_request(struct auth_request_handler *handler,
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch const char *const *params)
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen request = hash_table_lookup(handler->requests, POINTER_CAST(client_id));
eb7b8855cc45292334056f425645215e348ec493Timo Sirainen return auth_master_request_failed(handler, master, id);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen auth_request_handler_remove(handler, request);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch const char *name, *param = strchr(*params, '=');
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch (void)auth_request_import_master(request, name, param);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch /* verify session pid if specified and possible */
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch cred.pid != (pid_t)-1 && request->session_pid != cred.pid) {
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("Session pid %ld provided by master for request %u.%u "
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch "did not match peer credentials (pid=%ld, uid=%ld)",
eb7b8855cc45292334056f425645215e348ec493Timo Sirainen return auth_master_request_failed(handler, master, id);
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainen if (request->state != AUTH_REQUEST_STATE_FINISHED ||
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen i_error("Master requested unfinished authentication request "
eb7b8855cc45292334056f425645215e348ec493Timo Sirainen handler->master_callback(t_strdup_printf("FAIL\t%u", id),
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* the request isn't being referenced anywhere anymore,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen so we can do a bit of kludging.. replace the request's
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen old client_id with master's id. */
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen auth_request_set_state(request, AUTH_REQUEST_STATE_USERDB);
ecb1b2d6236942bf82f822e8d0167f0e160b206dTimo Sirainen /* master and handler are referenced until userdb_callback i
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen auth_request_lookup_user(request, userdb_callback);
3b8d05391336c0e4d24c8ddcc962f350409ffbd3Timo Sirainenvoid auth_request_handler_cancel_request(struct auth_request_handler *handler,
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen request = hash_table_lookup(handler->requests, POINTER_CAST(client_id));
3b8d05391336c0e4d24c8ddcc962f350409ffbd3Timo Sirainen auth_request_handler_remove(handler, request);
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainenvoid auth_request_handler_flush_failures(bool flush_all)
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen struct auth_request **auth_requests, *auth_request;
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen auth_requests = array_idx_modifiable(&auth_failures_arr, 0);
e18b4e41d9718a199a1980688787c2743c870002Timo Sirainen /* count the number of requests that we need to flush */
a550b0fbcf7e876eeb88f4528209ed28cc416752Timo Sirainen for (i = 0; i < count; i++) {
e18b4e41d9718a199a1980688787c2743c870002Timo Sirainen auth_request = auth_requests[aqueue_idx(auth_failures, i)];
19557f192d37cd54a1a090a8a26d9d47265e4413Aki Tuomi /* FIXME: assumes that failure_delay is always the same. */
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen diff = ioloop_time - auth_request->last_access;
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen if (diff < (time_t)auth_request->set->failure_delay &&
e18b4e41d9718a199a1980688787c2743c870002Timo Sirainen /* shuffle these requests to try to prevent any kind of timing attacks
e18b4e41d9718a199a1980688787c2743c870002Timo Sirainen where attacker performs multiple requests in parallel and attempts
e18b4e41d9718a199a1980688787c2743c870002Timo Sirainen to figure out results based on the order of replies. */
e18b4e41d9718a199a1980688787c2743c870002Timo Sirainen for (i = 0; i < count; i++) {
e18b4e41d9718a199a1980688787c2743c870002Timo Sirainen auth_request = auth_requests[aqueue_idx(auth_failures, i)];
e18b4e41d9718a199a1980688787c2743c870002Timo Sirainen /* swap i & j */
e18b4e41d9718a199a1980688787c2743c870002Timo Sirainen auth_requests[aqueue_idx(auth_failures, j)] = auth_request;
e18b4e41d9718a199a1980688787c2743c870002Timo Sirainen /* flush the requests */
e18b4e41d9718a199a1980688787c2743c870002Timo Sirainen for (i = 0; i < count; i++) {
b20202007c06fa77da44d00fbac7f029cb6d8f86Aki Tuomi auth_request = auth_requests[aqueue_idx(auth_failures, 0)];
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen i_assert(auth_request->state == AUTH_REQUEST_STATE_FINISHED);
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainenstatic void auth_failure_timeout(void *context ATTR_UNUSED)