auth-request-var-expand.c \
auth-sasl-mech-apop.c \
auth-sasl-mech-dovecot-token.c \
+ auth-sasl-mech-oauth2.c \
auth-sasl.c \
auth-settings.c \
auth-fields.c \
/* Copyright (c) 2023 Dovecot authors, see the included COPYING file */
#include "auth-common.h"
-#include "auth-sasl.h"
-#include "auth-sasl-oauth2.h"
+#include "str.h"
+#include "strescape.h"
+#include "sasl-server-oauth2.h"
+#include "auth-worker-connection.h"
#include "auth-request.h"
-
+#include "db-oauth2.h"
+#include "auth-sasl.h"
#include "auth-sasl-oauth2.h"
/*
* Token verification
*/
+static struct db_oauth2 *db_oauth2 = NULL;
+
+struct oauth2_token_lookup {
+ struct sasl_server_oauth2_request request;
+
+ struct db_oauth2 *db;
+ struct db_oauth2_request db_req;
+ lookup_credentials_callback_t *callback;
+};
+
static void
oauth2_verify_finish(enum passdb_result result,
struct auth_request *auth_request)
{
struct sasl_server_req_ctx *srctx = &auth_request->sasl.req;
- struct sasl_server_mech_request *request =
- sasl_server_request_get_mech_request(srctx);
- struct oauth2_auth_request *oauth2_req =
- container_of(request, struct oauth2_auth_request, request);
struct sasl_server_oauth2_failure failure;
i_zero(&failure);
i_unreached();
}
- if (oauth2_req->db != NULL) {
- failure.openid_configuration =
- db_oauth2_get_openid_configuration_url(oauth2_req->db);
- }
sasl_server_oauth2_request_fail(srctx, &failure);
}
}
static void
-mech_oauth2_verify_token_continue(struct oauth2_auth_request *oauth2_req,
+mech_oauth2_verify_token_continue(struct oauth2_token_lookup *lookup,
const char *const *args)
{
- struct auth_request *auth_request = oauth2_req->request.request;
+ struct sasl_server_req_ctx *srctx = lookup->request.rctx;
+ struct auth_request *auth_request =
+ container_of(srctx, struct auth_request, sasl.req);
int parsed;
enum passdb_result result;
struct auth_worker_connection *conn ATTR_UNUSED,
const char *const *args, void *context)
{
- struct oauth2_auth_request *oauth2_req = context;
+ struct oauth2_token_lookup *lookup = context;
- mech_oauth2_verify_token_continue(oauth2_req, args);
+ mech_oauth2_verify_token_continue(lookup, args);
return TRUE;
}
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 oauth2_token_lookup *lookup)
{
- struct auth_request *auth_request = oauth2_req->request.request;
+ struct sasl_server_req_ctx *srctx = lookup->request.rctx;
+ struct auth_request *auth_request =
+ container_of(srctx, struct auth_request, sasl.req);
if (result == PASSDB_RESULT_OK) {
auth_request_set_password_verified(auth_request);
pool_unref(&db_req->pool);
}
-static void
-auth_sasl_oauth2_verify_token(struct oauth2_auth_request *oauth2_req,
- const char *token)
+static int
+oauth2_auth_new(struct sasl_server_req_ctx *srctx, pool_t pool,
+ const char *token, struct sasl_server_oauth2_request **req_r)
{
- struct auth_request *auth_request = oauth2_req->request.request;
+ struct auth_request *auth_request =
+ container_of(srctx, struct auth_request, sasl.req);
+ struct oauth2_token_lookup *lookup;
+
+ if (db_oauth2 == NULL) {
+ e_error(auth_request->event, "BUG: oauth2 database missing");
+ return -1;
+ }
+
+ lookup = p_new(pool, struct oauth2_token_lookup, 1);
+ sasl_server_oauth2_request_init(&lookup->request, pool, srctx);
+ lookup->db_req.pool = pool;
+ lookup->db = db_oauth2;
auth_request_ref(auth_request);
- if (!db_oauth2_use_worker(oauth2_req->db)) {
+ if (!db_oauth2_use_worker(lookup->db)) {
pool_t pool = pool_alloconly_create(
MEMPOOL_GROWING"oauth2 request", 256);
struct db_oauth2_request *db_req =
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);
+ lookup->db, db_req, token, db_req->auth_request,
+ mech_oauth2_verify_token_local_continue, lookup);
} else {
string_t *str = t_str_new(128);
str_append(str, "TOKEN\tOAUTH2\t");
str_append_c(str, '\t');
auth_request_export(auth_request, str);
auth_worker_call(
- oauth2_req->db_req.pool,
+ lookup->db_req.pool,
auth_request->fields.user, str_c(str),
- mech_oauth2_verify_token_input_args, oauth2_req);
+ mech_oauth2_verify_token_input_args, lookup);
}
+
+ *req_r = &lookup->request;
+ return 0;
}
+static const struct sasl_server_oauth2_funcs mech_funcs = {
+ .auth_new = oauth2_auth_new,
+};
+
void auth_sasl_oauth2_initialize(void)
{
const char *mech, *error;
}
}
}
+
+/*
+ * Mechanisms
+ */
+
+static void
+mech_oauth_init_settings(struct sasl_server_oauth2_settings *oauth2_set)
+{
+ i_assert(db_oauth2 != NULL);
+
+ i_zero(oauth2_set);
+ oauth2_set->openid_configuration_url =
+ db_oauth2_get_openid_configuration_url(db_oauth2);
+}
+
+static bool
+mech_oauthbearer_register(struct sasl_server_instance *sasl_inst,
+ const struct auth_settings *set ATTR_UNUSED)
+{
+ struct sasl_server_oauth2_settings oauth2_set;
+
+ mech_oauth_init_settings(&oauth2_set);
+ sasl_server_mech_register_oauthbearer(sasl_inst, &mech_funcs,
+ &oauth2_set);
+ return TRUE;
+}
+
+static bool
+mech_xoauth2_register(struct sasl_server_instance *sasl_inst,
+ const struct auth_settings *set ATTR_UNUSED)
+{
+ struct sasl_server_oauth2_settings oauth2_set;
+
+ mech_oauth_init_settings(&oauth2_set);
+ sasl_server_mech_register_xoauth2(sasl_inst, &mech_funcs,
+ &oauth2_set);
+ return TRUE;
+}
+
+static const struct auth_sasl_mech_module mech_oauthbearer = {
+ .mech_name = SASL_MECH_NAME_OAUTHBEARER,
+
+ .mech_register = mech_oauthbearer_register,
+};
+
+static const struct auth_sasl_mech_module mech_xoauth2 = {
+ .mech_name = SASL_MECH_NAME_XOAUTH2,
+
+ .mech_register = mech_xoauth2_register,
+};
+
+void auth_sasl_mech_oauth2_register(void)
+{
+ auth_sasl_mech_register_module(&mech_oauthbearer);
+ auth_sasl_mech_register_module(&mech_xoauth2);
+}
void auth_sasl_oauth2_initialize(void);
+void auth_sasl_mech_oauth2_register(void);
+
#endif
MECH_SIMPLE_REGISTER__TEMPLATE(digest_md5)
MECH_SIMPLE_REGISTER__TEMPLATE(external)
MECH_SIMPLE_REGISTER__TEMPLATE(login)
-MECH_SIMPLE_REGISTER__TEMPLATE(oauthbearer)
-MECH_SIMPLE_REGISTER__TEMPLATE(xoauth2)
MECH_SIMPLE_REGISTER__TEMPLATE(otp)
MECH_SIMPLE_REGISTER__TEMPLATE(plain)
MECH_SIMPLE_REGISTER__TEMPLATE(scram_sha1)
.mech_register = mech_login_register,
};
-static const struct auth_sasl_mech_module mech_oauthbearer = {
- .mech_name = SASL_MECH_NAME_OAUTHBEARER,
-
- .mech_register = mech_oauthbearer_register,
-};
-
static const struct auth_sasl_mech_module mech_otp = {
.mech_name = SASL_MECH_NAME_OTP,
.mech_register = mech_winbind_gss_spnego_register,
};
-static const struct auth_sasl_mech_module mech_xoauth2 = {
- .mech_name = SASL_MECH_NAME_XOAUTH2,
-
- .mech_register = mech_xoauth2_register,
-};
-
static const struct auth_sasl_mech_module mech_apop = {
.mech_name = AUTH_SASL_MECH_NAME_APOP,
auth_sasl_mech_register_module(&mech_login);
if (set->use_winbind)
auth_sasl_mech_register_module(&mech_winbind_ntlm);
- auth_sasl_mech_register_module(&mech_oauthbearer);
+ auth_sasl_mech_oauth2_register();
auth_sasl_mech_register_module(&mech_otp);
auth_sasl_mech_register_module(&mech_plain);
auth_sasl_mech_register_module(&mech_scram_sha1);
auth_sasl_mech_register_module(&mech_scram_sha1_plus);
auth_sasl_mech_register_module(&mech_scram_sha256);
auth_sasl_mech_register_module(&mech_scram_sha256_plus);
- auth_sasl_mech_register_module(&mech_xoauth2);
auth_sasl_mech_register_module(&mech_apop);
}
#include "auth-common.h"
#include "auth-fields.h"
-#include "auth-worker-connection.h"
#include "ioloop.h"
#include "str.h"
-#include "strescape.h"
+#include "buffer.h"
#include "json-ostream.h"
#include "auth-gs2.h"
-#include "db-oauth2.h"
#include "oauth2.h"
#include "sasl-server-protected.h"
struct oauth2_auth_request {
struct sasl_server_mech_request request;
- struct db_oauth2 *db;
- struct db_oauth2_request db_req;
- lookup_credentials_callback_t *callback;
+ struct sasl_server_oauth2_request *backend;
bool failed:1;
bool verifying_token:1;
};
+struct oauth2_auth_mech {
+ struct sasl_server_mech mech;
+ const struct sasl_server_oauth2_funcs *funcs;
+
+ struct sasl_server_oauth2_settings set;
+};
+
static const struct sasl_server_mech_def mech_oauthbearer;
static const struct sasl_server_mech_def mech_xoauth2;
-static struct db_oauth2 *db_oauth2 = NULL;
-
static void
oauth2_fail(struct oauth2_auth_request *oauth2_req,
const struct sasl_server_oauth2_failure *failure)
{
struct sasl_server_mech_request *request = &oauth2_req->request;
+ const struct oauth2_auth_mech *oauth2_mech =
+ container_of(request->mech,
+ const struct oauth2_auth_mech, mech);
+
+ oauth2_req->verifying_token = FALSE;
if (failure == NULL) {
sasl_server_request_internal_failure(request);
json_ostream_nwrite_string(joutput, "scope", "mail");
else
json_ostream_nwrite_string(joutput, "scope", failure->scope);
- if (failure->openid_configuration != NULL &&
- *failure->openid_configuration != '\0') {
+ if (oauth2_mech->set.openid_configuration_url != NULL &&
+ *oauth2_mech->set.openid_configuration_url != '\0') {
json_ostream_nwrite_string(
joutput, "openid-configuration",
- failure->openid_configuration);
+ oauth2_mech->set.openid_configuration_url);
}
json_ostream_nascend_object(joutput);
json_ostream_nfinish_destroy(&joutput);
container_of(request, struct oauth2_auth_request, request);
i_assert(oauth2_req->verifying_token);
+ oauth2_req->verifying_token = FALSE;
sasl_server_request_success(request, "", 0);
+
+ sasl_server_mech_request_unref(&request);
}
void sasl_server_oauth2_request_fail(
container_of(request, struct oauth2_auth_request, request);
i_assert(oauth2_req->verifying_token);
+ oauth2_req->verifying_token = FALSE;
oauth2_fail(oauth2_req, failure);
+
+ sasl_server_mech_request_unref(&request);
}
-#include "auth-sasl-mech-oauth2.c"
+struct sasl_server_oauth2_request *
+sasl_server_oauth2_request_get(struct sasl_server_req_ctx *rctx)
+{
+ struct sasl_server_mech_request *request =
+ sasl_server_request_get_mech_request(rctx);
+
+ i_assert(request->mech->def == &mech_oauthbearer ||
+ request->mech->def == &mech_xoauth2);
+
+ struct oauth2_auth_request *oauth2_req =
+ container_of(request, struct oauth2_auth_request, request);
+
+ return oauth2_req->backend;
+}
static void
mech_oauth2_verify_token(struct oauth2_auth_request *oauth2_req,
const char *token)
{
+ struct sasl_server_mech_request *request = &oauth2_req->request;
+ struct sasl_server_req_ctx *rctx =
+ sasl_server_request_get_req_ctx(request);
+ const struct oauth2_auth_mech *oauth2_mech =
+ container_of(request->mech,
+ const struct oauth2_auth_mech, mech);
+
+ /* Add reference for the lookup; dropped upon success/failure */
+ sasl_server_mech_request_ref(request);
+
i_assert(token != NULL);
+ i_assert(oauth2_mech->funcs != NULL &&
+ oauth2_mech->funcs->auth_new != NULL);
oauth2_req->verifying_token = TRUE;
- auth_sasl_oauth2_verify_token(oauth2_req, token);
+
+ /* Call the backend auth_new() function under an additional reference in
+ case it fails the request immediately. */
+ sasl_server_mech_request_ref(request);
+ if (oauth2_mech->funcs->auth_new(rctx, request->pool, token,
+ &oauth2_req->backend) < 0) {
+ if (!oauth2_req->failed)
+ sasl_server_oauth2_request_fail(rctx, NULL);
+ i_assert(oauth2_req->backend == NULL);
+ } else {
+ i_assert(!oauth2_req->verifying_token ||
+ oauth2_req->backend != NULL);
+ i_assert(oauth2_req->backend == NULL ||
+ oauth2_req->backend->pool != NULL);
+ i_assert(oauth2_req->backend == NULL ||
+ oauth2_req->backend->rctx != NULL);
+ }
+ sasl_server_mech_request_unref(&request);
}
/* Input syntax for data:
struct oauth2_auth_request *oauth2_req =
container_of(request, struct oauth2_auth_request, request);
- if (oauth2_req->db == NULL) {
- e_error(request->mech_event, "BUG: oauth2 database missing");
- sasl_server_request_internal_failure(request);
- return;
- }
if (data_size == 0) {
oauth2_fail_invalid_request(oauth2_req);
return;
struct oauth2_auth_request *oauth2_req =
container_of(request, struct oauth2_auth_request, request);
- if (oauth2_req->db == NULL) {
- e_error(request->mech_event, "BUG: oauth2 database missing");
- sasl_server_request_internal_failure(request);
- return;
- }
if (data_size == 0) {
oauth2_fail_invalid_request(oauth2_req);
return;
struct oauth2_auth_request *request;
request = p_new(pool, struct oauth2_auth_request, 1);
- request->db_req.pool = pool;
- request->db = db_oauth2;
return &request->request;
}
+static void mech_oauth2_auth_free(struct sasl_server_mech_request *request)
+{
+ struct oauth2_auth_request *oauth2_req =
+ container_of(request, struct oauth2_auth_request, request);
+ const struct oauth2_auth_mech *oauth2_mech =
+ container_of(request->mech,
+ const struct oauth2_auth_mech, mech);
+
+ i_assert(oauth2_mech->funcs != NULL);
+ if (oauth2_req->backend != NULL &&
+ oauth2_mech->funcs->auth_free != NULL)
+ oauth2_mech->funcs->auth_free(oauth2_req->backend);
+}
+
+static struct sasl_server_mech *mech_oauth2_mech_new(pool_t pool)
+{
+ struct oauth2_auth_mech *oauth2_mech;
+
+ oauth2_mech = p_new(pool, struct oauth2_auth_mech, 1);
+
+ return &oauth2_mech->mech;
+}
+
static const struct sasl_server_mech_funcs mech_oauthbearer_funcs = {
.auth_new = mech_oauth2_auth_new,
.auth_initial = sasl_server_mech_generic_auth_initial,
.auth_continue = mech_oauthbearer_auth_continue,
+ .auth_free = mech_oauth2_auth_free,
+
+ .mech_new = mech_oauth2_mech_new,
};
static const struct sasl_server_mech_def mech_oauthbearer = {
.auth_new = mech_oauth2_auth_new,
.auth_initial = sasl_server_mech_generic_auth_initial,
.auth_continue = mech_xoauth2_auth_continue,
+ .auth_free = mech_oauth2_auth_free,
+
+ .mech_new = mech_oauth2_mech_new,
};
static const struct sasl_server_mech_def mech_xoauth2 = {
.funcs = &mech_xoauth2_funcs,
};
-void sasl_server_mech_register_oauthbearer(struct sasl_server_instance *sinst)
+static void
+mech_oauth2_register(struct sasl_server_instance *sinst,
+ const struct sasl_server_mech_def *mech_def,
+ const struct sasl_server_oauth2_funcs *funcs,
+ const struct sasl_server_oauth2_settings *set)
+{
+ struct sasl_server_mech *mech;
+
+ i_assert(funcs != NULL && funcs->auth_new != NULL);
+
+ mech = sasl_server_mech_register(sinst, mech_def, NULL);
+
+ struct oauth2_auth_mech *oauth2_mech =
+ container_of(mech, struct oauth2_auth_mech, mech);
+
+ oauth2_mech->funcs = funcs;
+
+ if (set != NULL) {
+ oauth2_mech->set.openid_configuration_url =
+ p_strdup(mech->pool, set->openid_configuration_url);
+ }
+}
+
+void sasl_server_mech_register_oauthbearer(
+ struct sasl_server_instance *sinst,
+ const struct sasl_server_oauth2_funcs *funcs,
+ const struct sasl_server_oauth2_settings *set)
{
- sasl_server_mech_register(sinst, &mech_oauthbearer, NULL);
+ mech_oauth2_register(sinst, &mech_oauthbearer, funcs, set);
}
-void sasl_server_mech_register_xoauth2(struct sasl_server_instance *sinst)
+void sasl_server_mech_register_xoauth2(
+ struct sasl_server_instance *sinst,
+ const struct sasl_server_oauth2_funcs *funcs,
+ const struct sasl_server_oauth2_settings *set)
{
- sasl_server_mech_register(sinst, &mech_xoauth2, NULL);
+ mech_oauth2_register(sinst, &mech_xoauth2, funcs, set);
}
struct sasl_server_oauth2_failure {
const char *status;
const char *scope;
- const char *openid_configuration;
};
+struct sasl_server_oauth2_request {
+ pool_t pool;
+ struct sasl_server_req_ctx *rctx;
+};
+
+struct sasl_server_oauth2_settings {
+ const char *openid_configuration_url;
+};
+
+struct sasl_server_oauth2_funcs {
+ int (*auth_new)(struct sasl_server_req_ctx *rctx, pool_t pool,
+ const char *token,
+ struct sasl_server_oauth2_request **req_r);
+ void (*auth_free)(struct sasl_server_oauth2_request *req);
+};
+
+static inline void
+sasl_server_oauth2_request_init(struct sasl_server_oauth2_request *request_r,
+ pool_t pool, struct sasl_server_req_ctx *srctx)
+{
+ i_zero(request_r);
+ request_r->pool = pool;
+ request_r->rctx = srctx;
+}
+
void sasl_server_oauth2_request_succeed(struct sasl_server_req_ctx *rctx);
void sasl_server_oauth2_request_fail(
struct sasl_server_req_ctx *rctx,
const struct sasl_server_oauth2_failure *failure);
+struct sasl_server_oauth2_request *
+sasl_server_oauth2_request_get(struct sasl_server_req_ctx *rctx);
+
+void sasl_server_mech_register_oauthbearer(
+ struct sasl_server_instance *sinst,
+ const struct sasl_server_oauth2_funcs *funcs,
+ const struct sasl_server_oauth2_settings *set);
+void sasl_server_mech_register_xoauth2(
+ struct sasl_server_instance *sinst,
+ const struct sasl_server_oauth2_funcs *funcs,
+ const struct sasl_server_oauth2_settings *set);
+
#endif
void sasl_server_mech_register_otp(struct sasl_server_instance *sinst);
-/* OAUTH2 */
-
-void sasl_server_mech_register_oauthbearer(struct sasl_server_instance *sinst);
-void sasl_server_mech_register_xoauth2(struct sasl_server_instance *sinst);
-
/* Winbind */
struct sasl_server_winbind_settings {