/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "str.h"
#include "var-expand.h"
#include "env-util.h"
#include "var-expand.h"
#include "settings.h"
#include "oauth2.h"
#include "http-client.h"
#include "iostream-ssl.h"
#include "auth-request.h"
#include "passdb.h"
#include "passdb-template.h"
#include "llist.h"
#include "db-oauth2.h"
#include <stddef.h>
struct passdb_oauth2_settings {
/* tokeninfo endpoint, format https://endpoint/somewhere?token= */
const char *tokeninfo_url;
/* introspection endpoint, format https://endpoint/somewhere */
const char *introspection_url;
/* expected scope, optional */
const char *scope;
/* mode of introspection, one of get, get-auth, post
- get: append token to url
- get-auth: send token with header Authorization: Bearer token
- post: send token=<token> as POST request
*/
const char *introspection_mode;
/* normalization var-expand template for username, defaults to %Lu */
const char *username_format;
/* name of username attribute to lookup, mandatory */
const char *username_attribute;
/* name of account is active attribute, optional */
const char *active_attribute;
/* expected active value for active attribute, optional */
const char *active_value;
/* template to expand into passdb */
const char *pass_attrs;
/* TLS options */
const char *tls_ca_cert_file;
const char *tls_ca_cert_dir;
const char *tls_cert_file;
const char *tls_key_file;
const char *tls_cipher_suite;
/* HTTP rawlog directory */
const char *rawlog_dir;
/* HTTP client options */
unsigned int timeout_msecs;
unsigned int max_idle_time_msecs;
unsigned int max_parallel_connections;
unsigned int max_pipelined_requests;
bool tls_allow_invalid_cert;
bool debug;
/* Should introspection be done even if not necessary */
bool force_introspection;
bool send_auth_headers;
};
struct db_oauth2 {
const char *config_path;
unsigned int refcount;
};
{ 0, NULL, 0 }
};
.tokeninfo_url = "",
.introspection_url = "",
.scope = "",
.introspection_mode = "",
.username_format = "%Lu",
.username_attribute = "email",
.active_attribute = "",
.active_value = "",
.pass_attrs = "",
.rawlog_dir = "",
.timeout_msecs = 0,
.max_idle_time_msecs = 60000,
.max_parallel_connections = 1,
.max_pipelined_requests = 1,
.tls_ca_cert_file = NULL,
.tls_ca_cert_dir = NULL,
.tls_cert_file = NULL,
.tls_key_file = NULL,
.tls_cipher_suite = "HIGH:!SSLv2",
};
{
}
{
const char *error;
return db;
}
}
}
i_fatal("oauth2: Tokeninfo or introspection URL must be given");
} else {
i_fatal("Invalid value '%s' for introspection mode, must be on auth, get or post",
}
return db;
}
{
}
{
break;
}
}
/* make sure all requests are aborted */
}
static bool
{
unsigned int n,i;
return FALSE;
for(i=1;i<n;i+=2) {
const char *field;
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;
}
{
const char *p;
if (p == NULL)
return "";
else {
/* default value given */
return p+1;
}
}
const char **value_r,
const char **error_r ATTR_UNUSED)
{
return 1;
}
{
return value;
}
static const struct var_expand_table *
const char *oauth2_value)
{
&count);
return table;
}
static bool
{
/* var=$ expands into var=${oauth2:var} */
{ "oauth2", db_oauth2_var_expand_func_oauth2 },
};
unsigned int i, count;
if (passdb_template_is_empty(tmpl))
return TRUE;
for (i = 0; i < count; i += 2) {
value = "";
else {
str_truncate(dest, 0);
const struct var_expand_table *
return FALSE;
}
}
}
return TRUE;
}
{
"oauth2: Processing field %s",
}
}
enum passdb_result result,
const char *error)
{
"oauth2: callback(%d, %s)",
}
}
static bool
{
const char *error;
};
const char *username_value =
if (username_value == NULL) {
*error_r = "No username returned";
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;
} else {
return TRUE;
}
}
static bool
{
if (active_value == NULL ||
*error_r = "User account is not active";
return FALSE;
}
}
return TRUE;
}
static bool
{
"oauth2: Token scope(s): %s",
value);
}
if (!found) {
return FALSE;
}
}
return TRUE;
}
enum passdb_result *result_r,
const char **error_r)
{
} else {
}
}
static void
struct db_oauth2_request *req)
{
const char *error;
"oauth2: Introspection result: %s",
/* fail here */
} else {
}
}
{
"oauth2: Making introspection request to %s",
}
static void
struct db_oauth2_request *req)
{
const char *error;
error = "Invalid token";
} else {
!db_oauth2_have_all_fields(req))) {
"oauth2: Introspection needed after token validation");
return;
}
}
}
{
"oauth2: Making introspection request to %s",
} else {
"oauth2: Making token validation lookup to %s",
}
}