effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* tokeninfo endpoint, format https://endpoint/somewhere?token= */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* introspection endpoint, format https://endpoint/somewhere */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* expected scope, optional */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* mode of introspection, one of get, get-auth, post
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher - get: append token to url
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher - get-auth: send token with header Authorization: Bearer token
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher - post: send token=<token> as POST request
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* normalization var-expand template for username, defaults to %Lu */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* name of username attribute to lookup, mandatory */
8a05fd320a44636d120a18eb7e9956c7b35b3138Jakub Hrozek /* name of account is active attribute, optional */
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley /* expected active value for active attribute, optional */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* template to expand into passdb */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* TLS options */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* HTTP rawlog directory */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* HTTP client options */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* Should introspection be done even if not necessary */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* Should we send service and local/remote endpoints as X-Dovecot-Auth headers */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic struct db_oauth2 *db_oauth2_head = NULL;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher#define DEF_STR(name) DEF_STRUCT_STR(name, passdb_oauth2_settings)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, passdb_oauth2_settings)
07b7b76d7cd494cbd26263503ba2732c21819941Jan Zeleny#define DEF_INT(name) DEF_STRUCT_INT(name, passdb_oauth2_settings)
e82832a64fd456d1541ce0ea3902bcfb05e69642Stephen Gallagherstatic struct setting_def setting_defs[] = {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic struct passdb_oauth2_settings default_oauth2_settings = {
1b171c456ff901ab622e44bcfd213f7de86fd787Ariel Barriastatic const char *parse_setting(const char *key, const char *value,
e82832a64fd456d1541ce0ea3902bcfb05e69642Stephen Gallagher return parse_setting_from_defs(db->pool, setting_defs,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstruct db_oauth2 *db_oauth2_init(const char *config_path)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for(db = db_oauth2_head; db != NULL; db = db->next) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (strcmp(db->config_path, config_path) == 0) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher pool_t pool = pool_alloconly_create("db_oauth2", 128);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->config_path = p_strdup(pool, config_path);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (!settings_read_nosection(config_path, parse_setting, db, &error))
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher i_fatal("oauth2 %s: %s", config_path, error);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->tmpl = passdb_template_build(pool, db->set.pass_attrs);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ssl_set.cipher_list = db->set.tls_cipher_suite;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ssl_set.ca_file = db->set.tls_ca_cert_file;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (db->set.tls_cert_file != NULL && *db->set.tls_cert_file != '\0') {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ssl_set.cert.cert = db->set.tls_cert_file;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ssl_set.allow_invalid_cert = db->set.tls_allow_invalid_cert;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ssl_set.verbose_invalid_cert = db->set.debug;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher http_set.dns_client_socket_path = "dns-client";
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher http_set.user_agent = "dovecot-oauth2-passdb/" DOVECOT_VERSION;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (*db->set.tokeninfo_url == '\0' && *db->set.introspection_url == '\0')
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher i_fatal("oauth2: Tokeninfo or introspection URL must be given");
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher http_set.max_idle_time_msecs = db->set.max_idle_time_msecs;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher http_set.max_parallel_connections = db->set.max_parallel_connections;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher http_set.max_pipelined_requests = db->set.max_pipelined_requests;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->oauth2_set.tokeninfo_url = db->set.tokeninfo_url,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->oauth2_set.introspection_url = db->set.introspection_url;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->oauth2_set.timeout_msecs = db->set.timeout_msecs;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->oauth2_set.send_auth_headers = db->set.send_auth_headers;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (*db->set.introspection_mode == '\0' ||
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher strcmp(db->set.introspection_mode, "auth") == 0) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->oauth2_set.introspection_mode = INTROSPECTION_MODE_GET_AUTH;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher } else if (strcmp(db->set.introspection_mode, "get") == 0) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->oauth2_set.introspection_mode = INTROSPECTION_MODE_GET;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher } else if (strcmp(db->set.introspection_mode, "post") == 0) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->oauth2_set.introspection_mode = INTROSPECTION_MODE_POST;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher i_fatal("Invalid value '%s' for introspection mode, must be on auth, get or post",
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallaghervoid db_oauth2_unref(struct db_oauth2 **_db)
74e95cfd9d3939dfe9417d79d2f6fc79b361405fJakub Hrozek for(ptr = db_oauth2_head; ptr != NULL; ptr = db->next) {
return FALSE;
ptr++;
return FALSE;
return FALSE;
if (*req->db->set.active_attribute != '\0' && !auth_fields_exists(req->fields, req->db->set.active_attribute))
return FALSE;
return TRUE;
if (p == NULL)
const char **value_r,
return value;
static const struct var_expand_table *
const char *oauth2_value)
&count);
return table;
return TRUE;
const struct var_expand_table *
return FALSE;
return TRUE;
const char *error)
return FALSE;
if (auth_request_var_expand(username_req, req->db->set.username_format, req->auth_request, escape_none, &error) < 0 ||
return FALSE;
return FALSE;
return TRUE;
return FALSE;
return TRUE;
value);
if (!found) {
return FALSE;
return TRUE;
const char **error_r)