/* Should we send service and local/remote endpoints as X-Dovecot-Auth headers */
bool send_auth_headers;
bool use_grant_password;
+ bool blocking;
};
struct db_oauth2 {
DEF_STR(openid_configuration_url),
DEF_BOOL(send_auth_headers),
DEF_BOOL(use_grant_password),
-
+ DEF_BOOL(blocking),
DEF_BOOL(debug),
{ 0, NULL, 0 }
.local_validation_key_dict = "",
.send_auth_headers = FALSE,
.use_grant_password = FALSE,
+ .blocking = TRUE,
.debug = FALSE,
};
+static void db_oauth2_callback(struct db_oauth2_request *req,
+ enum passdb_result result,
+ const char *error_prefix, const char *error);
+
static const char *parse_setting(const char *key, const char *value,
struct db_oauth2 *db)
{
i_assert(ptr != NULL && ptr == db);
/* make sure all requests are aborted */
- while (db->head != NULL)
- oauth2_request_abort(&db->head->req);
-
+ while (db->head != NULL) {
+ if (db->head->req != NULL)
+ oauth2_request_abort(&db->head->req);
+ else {
+ struct db_oauth2_request *req = db->head;
+ DLLIST_REMOVE(&db->head, req);
+ db_oauth2_callback(req, PASSDB_RESULT_INTERNAL_FAILURE,
+ "", "aborted");
+ }
+ }
http_client_deinit(&db->client);
if (db->oauth2_set.key_dict != NULL)
dict_deinit(&db->oauth2_set.key_dict);
return db->set.use_grant_password;
}
+bool db_oauth2_is_blocking(const struct db_oauth2 *db)
+{
+ return db->set.blocking;
+}
+
void db_oauth2_deinit(void)
{
while (db_oauth2_head != NULL) {
/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */
#include "auth-common.h"
+#include "auth-fields.h"
+#include "auth-worker-connection.h"
#include "str.h"
+#include "strescape.h"
#include "json-generator.h"
#include "mech.h"
#include "passdb.h"
}
static void
-mech_oauth2_verify_token_continue(struct db_oauth2_request *db_request,
- enum passdb_result result,
- const char *error,
- struct oauth2_auth_request *oauth2_req)
+mech_oauth2_verify_token_continue(struct oauth2_auth_request *oauth2_req,
+ const char *const *args)
{
- struct auth_request *auth_request = &oauth2_req->auth;
- if (result != PASSDB_RESULT_OK) {
- e_error(auth_request->mech_event, "%s", error);
- oauth2_verify_callback(result, NULL, 0, auth_request);
+ struct auth_request *request = &oauth2_req->auth;
+ int parsed;
+ enum passdb_result result;
+
+ /* OK result user fields */
+ if (args[0] == NULL || args[1] == NULL) {
+ result = PASSDB_RESULT_INTERNAL_FAILURE;
+ e_error(request->mech_event, "BUG: Invalid auth worker response: empty");
+ } else if (str_to_int(args[1], &parsed) < 0) {
+ result = PASSDB_RESULT_INTERNAL_FAILURE;
+ e_error(request->mech_event, "BUG: Invalid auth worker response: cannot parse '%s'", args[1]);
+ } else if (args[2] == NULL) {
+ result = PASSDB_RESULT_INTERNAL_FAILURE;
+ e_error(request->mech_event, "BUG: Invalid auth worker response: cannot parse '%s'", args[1]);
+ } else {
+ result = parsed;
+ }
+
+ if (result == PASSDB_RESULT_OK) {
+ request->passdb_success = TRUE;
+ auth_request_set_password_verified(request);
+ auth_request_set_fields(request, args + 3, NULL);
+ auth_request_lookup_credentials(request, "", oauth2_verify_callback);
+ auth_request_unref(&request);
return;
}
- db_request->auth_request->passdb_success = TRUE;
- auth_request_set_field(auth_request, "token", db_request->token, NULL);
- auth_request_lookup_credentials(auth_request, "", oauth2_verify_callback);
+ oauth2_verify_callback(result, uchar_empty_ptr, 0, request);
+ auth_request_unref(&request);
+}
+
+static bool
+mech_oauth2_verify_token_input_args(struct auth_worker_connection *conn ATTR_UNUSED,
+ const char *const *args, void *context)
+{
+ struct oauth2_auth_request *oauth2_req = context;
+ mech_oauth2_verify_token_continue(oauth2_req, args);
+ return TRUE;
+}
+
+static void
+mech_oauth2_verify_token_local_continue(struct db_oauth2_request *db_req,
+ enum passdb_result result,
+ const char *error,
+ struct oauth2_auth_request *oauth2_req)
+{
+ struct auth_request *request = &oauth2_req->auth;
+ if (result == PASSDB_RESULT_OK) {
+ auth_request_set_password_verified(request);
+ auth_request_set_field(request, "token", db_req->token, NULL);
+ auth_request_lookup_credentials(request, "", oauth2_verify_callback);
+ auth_request_unref(&request);
+ pool_unref(&db_req->pool);
+ return;
+ } else {
+ e_error(request->mech_event, "Token verification failed: %s", error);
+ }
+ oauth2_verify_callback(result, uchar_empty_ptr, 0, request);
+ auth_request_unref(&request);
+ pool_unref(&db_req->pool);
}
static void
mech_oauth2_verify_token(struct oauth2_auth_request *oauth2_req, const char *token)
{
- /* decode token */
- db_oauth2_lookup(oauth2_req->db, &oauth2_req->db_req, token, &oauth2_req->auth,
- mech_oauth2_verify_token_continue, oauth2_req);
+ struct auth_request *auth_request = &oauth2_req->auth;
+ auth_request_ref(auth_request);
+
+ if (!db_oauth2_is_blocking(oauth2_req->db)) {
+ pool_t pool = pool_alloconly_create(MEMPOOL_GROWING"oauth2 request", 256);
+ struct db_oauth2_request *db_req =
+ p_new(pool, struct db_oauth2_request, 1);
+ db_req->pool = pool;
+ db_req->auth_request = auth_request;
+ db_oauth2_lookup(oauth2_req->db, db_req, token, db_req->auth_request,
+ mech_oauth2_verify_token_local_continue, oauth2_req);
+ } else {
+ string_t *str = t_str_new(128);
+ str_append(str, "TOKEN\tOAUTH2\t");
+ str_append_tabescaped(str, token);
+ str_append_c(str, '\t');
+ auth_request_export(auth_request, str);
+ auth_worker_call(oauth2_req->db_req.pool, auth_request->fields.user,
+ str_c(str), mech_oauth2_verify_token_input_args, oauth2_req);
+ }
}
static void oauth2_init_db(struct oauth2_auth_request *oauth2_req)
/* Copyright (c) 2024 Dovecot authors, see the included COPYING file */
#include "test-auth.h"
+#include "ostream.h"
#include "auth-common.h"
#include "settings-parser.h"
#include "auth-settings.h"
struct auth_settings test_auth_set;
static struct mechanisms_register *mech_reg;
+#define TEST_OAUTH2_CONFIG_FILE "test-oauth2-config"
+
void test_auth_init(void)
{
const char *const protocols[] = {NULL};
process_start_time = time(NULL);
+ /* create oauth2 config file */
+ struct ostream *os =
+ o_stream_create_file(TEST_OAUTH2_CONFIG_FILE, 0, 0600, 0);
+ o_stream_nsend_str(os, "tokeninfo_url = http://localhost\nclient_id=foo\nblocking=no\n");
+ test_assert(o_stream_finish(os) == 1);
+ o_stream_unref(&os);
+
/* Copy default settings */
test_auth_set = *(const struct auth_settings *)auth_setting_parser_info.defaults;
test_auth_set.pool = pool_alloconly_create("test settings", 128);
test_auth_set.realms_arr = t_strsplit_spaces("example.com ", " ");
/* For tests of mech-anonymous. */
test_auth_set.anonymous_username = "anonuser";
+ /* For oauth2 tests */
+ test_auth_set.oauth2_config_file = TEST_OAUTH2_CONFIG_FILE;
mech_init(global_auth_settings);
mech_reg = mech_register_init(global_auth_settings);
{
auth_penalty_deinit(&auth_penalty);
mech_otp_deinit();
+ db_oauth2_deinit();
auths_deinit();
auth_token_deinit();
password_schemes_deinit();
auths_free();
pool_unref(&test_auth_set.pool);
i_unlink_if_exists("auth-token-secret.dat");
+ i_unlink_if_exists(TEST_OAUTH2_CONFIG_FILE);
}