auth-request-handler.c revision 1e0bdb2d0fa7bbd0a0a254754680f6c6d0195333
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (C) 2005 Timo Sirainen */
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainenauth_request_handler_create(struct auth *auth, int prepend_connect_uid,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen auth_request_callback_t *callback, void *context,
9b62bbaf33d4516b5dffb36c3ea32ce217e7fbb1Timo Sirainen pool = pool_alloconly_create("auth request handler", 4096);
ad49932dae8ba31e07544b66bbc4f4de707a751cTimo Sirainen handler = p_new(pool, struct auth_request_handler, 1);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen handler->requests = hash_create(default_pool, pool, 0, NULL, NULL);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen handler->prepend_connect_uid = prepend_connect_uid;
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainenvoid auth_request_handler_unref(struct auth_request_handler *handler)
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* notify parent that we're done with all requests */
b1e6ea3323b4d753909f79226a8a98d854f819faTimo Sirainenvoid auth_request_handler_set(struct auth_request_handler *handler,
648d24583c1574441c4fa0331a90bd4d6e7996c5Timo Sirainenstatic void auth_request_handler_remove(struct auth_request_handler *handler,
70905e51a5148bd5613cb04720807177474a2496Timo Sirainen hash_remove(handler->requests, POINTER_CAST(request->id));
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainenvoid auth_request_handler_check_timeouts(struct auth_request_handler *handler)
657afb33796f8216c568ad813627da89970760beTimo Sirainen if (request->created + AUTH_REQUEST_TIMEOUT < ioloop_time)
657afb33796f8216c568ad813627da89970760beTimo Sirainen auth_request_handler_remove(handler, request);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainenstatic const char *get_client_extra_fields(struct auth_request *request)
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen const char **fields;
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* we only wish to remove all fields prefixed with "userdb_" */
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen if (strstr(str_c(request->extra_fields), "userdb_") == NULL)
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* we're proxying - send back the password that was
bbf81c8fc6f21382707673dc6bd7b87ffc27981bTimo Sirainen sent by user (not the password in passdb). */
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen str_printfa(str, "pass=%s", request->mech_password);
1e0bdb2d0fa7bbd0a0a254754680f6c6d0195333Timo Sirainen fields = t_strsplit(str_c(request->extra_fields), "\t");
bbf81c8fc6f21382707673dc6bd7b87ffc27981bTimo Sirainen for (src = dest = 0; fields[src] != NULL; src++) {
1e0bdb2d0fa7bbd0a0a254754680f6c6d0195333Timo Sirainen if (strncmp(fields[src], "userdb_", 7) != 0) {
1e0bdb2d0fa7bbd0a0a254754680f6c6d0195333Timo Sirainenstatic void auth_callback(struct auth_request *request,
1e0bdb2d0fa7bbd0a0a254754680f6c6d0195333Timo Sirainen struct auth_request_handler *handler = request->context;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen str = t_str_new(128 + MAX_BASE64_ENCODED_SIZE(reply_size));
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen str_printfa(str, "%u\t", request->connect_uid);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen handler->callback(str_c(str), handler->context);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen str_printfa(str, "OK\t%u\tuser=%s", request->id, request->user);
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen if (request->no_login || handler->master_callback == NULL) {
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen /* this request doesn't have to wait for master
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen process to pick it up. delete it */
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen auth_request_handler_remove(handler, request);
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen handler->callback(str_c(str), handler->context);
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen /* we came here from flush_failures() */
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen handler->callback(str_c(str), handler->context);
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen /* remove the request from requests-list */
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen auth_request_handler_remove(handler, request);
2872c818f9c6704609f4d67d984b033a63e3a108Timo Sirainen /* passdb specifically requested not to delay the
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen handler->callback(str_c(str), handler->context);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* failure. don't announce it immediately to avoid
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen a) timing attacks, b) flooding */
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen /* NOTE: request may be destroyed now */
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainenstatic void auth_request_handler_auth_fail(struct auth_request_handler *handler,
f42581dd841281a4434e5c52488e0eda9716c891Timo Sirainen auth_request_log_info(request, request->mech->mech_name, "%s", reason);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen str_printfa(reply, "%u\t", request->connect_uid);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen str_printfa(reply, "FAIL\t%u\treason=%s", request->id, reason);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen handler->callback(str_c(reply), handler->context);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen auth_request_handler_remove(handler, request);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainenint auth_request_handler_auth_begin(struct auth_request_handler *handler,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen const char *const *list, *name, *arg, *initial_resp;
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen unsigned int id;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen /* <id> <mechanism> [...] */
708ea1c397d89586af66c97d74c907f3f2b95134Timo Sirainen "sent broken AUTH request", handler->client_pid);
708ea1c397d89586af66c97d74c907f3f2b95134Timo Sirainen id = (unsigned int)strtoul(list[0], NULL, 10);
e5dec382163b476bed16dbf7eb470913a9bbdbe1Timo Sirainen /* unsupported mechanism */
e5dec382163b476bed16dbf7eb470913a9bbdbe1Timo Sirainen i_error("BUG: Authentication client %u requested unsupported "
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen "authentication mechanism %s", handler->client_pid,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen request = auth_request_new(handler->auth, mech, auth_callback, handler);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* parse optional parameters */
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen else if (strcmp(name, "valid-client-cert") == 0)
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen "didn't specify service in request",
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen hash_insert(handler->requests, POINTER_CAST(id), request);
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen if (request->auth->ssl_require_client_cert && !valid_client_cert) {
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen /* we fail without valid certificate */
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen auth_request_handler_auth_fail(handler, request,
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen "Client didn't present valid SSL certificate");
7242e1ce7803b83bc82e239ef111b47c1c72dd4bAndrey Panin buf = buffer_create_dynamic(pool_datastack_create(),
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen if (base64_decode(initial_resp, len, NULL, buf) < 0) {
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen auth_request_handler_auth_fail(handler, request,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen "Invalid base64 data in initial response");
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* handler is referenced until auth_callback is called. */
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen auth_request_initial(request, initial_resp_data, initial_resp_len);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainenint auth_request_handler_auth_continue(struct auth_request_handler *handler,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen unsigned int id;
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen i_error("BUG: Authentication client sent broken CONT request");
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen request = hash_lookup(handler->requests, POINTER_CAST(id));
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen str_printfa(reply, "%u\t", handler->connect_uid);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen str_printfa(reply, "FAIL\t%u\treason=Timeouted", id);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen handler->callback(str_c(reply), handler->context);
382c7aec3e3449ed8271c2a202b67cefaa31dc8eTimo Sirainen /* accept input only once after mechanism has sent a CONT reply */
382c7aec3e3449ed8271c2a202b67cefaa31dc8eTimo Sirainen auth_request_handler_auth_fail(handler, request,
382c7aec3e3449ed8271c2a202b67cefaa31dc8eTimo Sirainen "Unexpected continuation");
382c7aec3e3449ed8271c2a202b67cefaa31dc8eTimo Sirainen buf = buffer_create_dynamic(pool_datastack_create(),
382c7aec3e3449ed8271c2a202b67cefaa31dc8eTimo Sirainen if (base64_decode(data, data_len, NULL, buf) < 0) {
382c7aec3e3449ed8271c2a202b67cefaa31dc8eTimo Sirainen auth_request_handler_auth_fail(handler, request,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen "Invalid base64 data in continued response");
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* handler is referenced until auth_callback is called. */
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen auth_request_continue(request, buf->data, buf->used);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainenstatic void userdb_callback(const char *result, struct auth_request *request)
258ff7d4f03dd9d29eca3664e4acacdf7f528234Timo Sirainen struct auth_request_handler *handler = request->context;
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen i_assert(request->state == AUTH_REQUEST_STATE_USERDB);
4f2248a8a70985c7295afc3bf91c848e81d740d9Timo Sirainen str_printfa(reply, "%u\t", request->connect_uid);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen str_printfa(reply, "NOTFOUND\t%u", request->id);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen str_printfa(reply, "USER\t%u\t", request->id);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen handler->master_callback(str_c(reply), request->master);
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainenvoid auth_request_handler_master_request(struct auth_request_handler *handler,
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen unsigned int id,
f42581dd841281a4434e5c52488e0eda9716c891Timo Sirainen str_printfa(reply, "%u\t", handler->connect_uid);
657afb33796f8216c568ad813627da89970760beTimo Sirainen request = hash_lookup(handler->requests, POINTER_CAST(client_id));
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen handler->master_callback(str_c(reply), master);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen auth_request_handler_remove(handler, request);
ccb77e2f63626ec46e5745ef4f38baa8e8e504fcTimo Sirainen if (request->state != AUTH_REQUEST_STATE_FINISHED ||
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen i_error("Master requested unfinished authentication request "
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen handler->master_callback(str_c(reply), master);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* the request isn't being referenced anywhere anymore,
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen so we can do a bit of kludging.. replace the request's
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen old client_id with master's id. */
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen /* handler is referenced until userdb_callback is called. */
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen auth_request_lookup_user(request, userdb_callback);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen auth_request = buffer_get_modifyable_data(auth_failures_buf, &size);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen for (i = 0; i < size; i++) {
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen i_assert(auth_request[i]->state == AUTH_REQUEST_STATE_FINISHED);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainenstatic void auth_failure_timeout(void *context __attr_unused__)
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen auth_failures_buf = buffer_create_dynamic(default_pool, 1024);