18#ifdef USE_DYNAMIC_OAUTH
29#include "pg_config_paths.h"
33 const char *sasl_mechanism);
35 char *
input,
int inputlen,
36 char **
output,
int *outputlen);
54 const char *sasl_mechanism)
62 Assert(sasl_mechanism != NULL);
108 static const char *
const resp_format =
"n,," kvsep "auth=%s%s" kvsep kvsep;
111 const char *authn_scheme;
112 char *response = NULL;
118 authn_scheme =
token =
"";
126 authn_scheme =
"Bearer ";
133 "internal error: no OAuth token was set for the connection");
142 response = strdup(
buf.data);
156#define ERROR_STATUS_FIELD "status"
157#define ERROR_SCOPE_FIELD "scope"
158#define ERROR_OPENID_CONFIGURATION_FIELD "openid-configuration"
175#define oauth_json_has_error(ctx) \
176 (PQExpBufferDataBroken((ctx)->errbuf) || (ctx)->errmsg)
178#define oauth_json_set_error(ctx, ...) \
180 appendPQExpBuffer(&(ctx)->errbuf, __VA_ARGS__); \
181 (ctx)->errmsg = (ctx)->errbuf.data; \
281 "internal error: target scalar found at nesting level %d during OAUTHBEARER parsing",
322#define HTTPS_SCHEME "https://"
323#define HTTP_SCHEME "http://"
326#define WK_PREFIX "/.well-known/"
327#define OPENID_WK_SUFFIX "openid-configuration"
328#define OAUTH_WK_SUFFIX "oauth-authorization-server"
337 const char *authority_start = NULL;
338 const char *wk_start;
341 ptrdiff_t start_offset,
362 if (!authority_start)
365 "OAuth discovery URI \"%s\" must use HTTPS",
379 if (strpbrk(authority_start,
"?#") != NULL)
382 "OAuth discovery URI \"%s\" must not contain query or fragment components",
393 wk_start = strstr(authority_start,
WK_PREFIX);
397 "OAuth discovery URI \"%s\" is not a .well-known URI",
420 if (!wk_end || (*wk_end !=
'/' && *wk_end !=
'\0'))
423 "OAuth discovery URI \"%s\" uses an unsupported .well-known suffix",
440 const char *path_start;
442 path_start = strchr(authority_start,
'/');
445 if (wk_start != path_start)
448 "OAuth discovery URI \"%s\" uses an invalid format",
455 issuer = strdup(wkuri);
467 start_offset = wk_start - wkuri;
468 end_offset = wk_end - wkuri;
469 end_len = strlen(wk_end) + 1;
471 memmove(issuer + start_offset, issuer + end_offset, end_len);
494 if (strlen(msg) != msglen)
497 "server's error message contained an embedded NULL, and was discarded");
508 "server's error response is not valid UTF-8");
539 errmsg =
"<unexpected empty error>";
547 "failed to parse server's error response: %s",
559 char *discovery_issuer;
572 if (!discovery_issuer)
578 "server's discovery document at %s (issuer \"%s\") is incompatible with oauth_issuer (%s)",
582 free(discovery_issuer);
586 free(discovery_issuer);
599 "server's discovery document has moved to %s (previous location was %s)",
620 "server sent error response without a status");
624 if (strcmp(ctx.
status,
"invalid_token") != 0)
631 "server rejected OAuth bearer token: %s",
664 "user-defined OAuth flow provided neither a token nor an async callback");
684 "user-defined OAuth flow did not provide a token");
702 "user-defined OAuth flow did not provide a socket for polling");
726 state->async_ctx = NULL;
742#if !defined(USE_LIBCURL)
754#elif defined(USE_DYNAMIC_OAUTH)
760typedef char *(*libpq_gettext_func) (
const char *msgid);
768#define DEFINE_GETTER(TYPE, MEMBER) \
769 typedef TYPE (*conn_ ## MEMBER ## _func) (PGconn *conn); \
770 static TYPE conn_ ## MEMBER(PGconn *conn) { return conn->MEMBER; }
773#define DEFINE_GETTER_P(TYPE, MEMBER) \
774 typedef TYPE (*conn_ ## MEMBER ## _func) (PGconn *conn); \
775 static TYPE conn_ ## MEMBER(PGconn *conn) { return &conn->MEMBER; }
777#define DEFINE_SETTER(TYPE, MEMBER) \
778 typedef void (*set_conn_ ## MEMBER ## _func) (PGconn *conn, TYPE val); \
779 static void set_conn_ ## MEMBER(PGconn *conn, TYPE val) { conn->MEMBER = val; }
782DEFINE_GETTER(
char *, oauth_client_id);
783DEFINE_GETTER(
char *, oauth_client_secret);
784DEFINE_GETTER(
char *, oauth_discovery_uri);
785DEFINE_GETTER(
char *, oauth_issuer_id);
786DEFINE_GETTER(
char *, oauth_scope);
790DEFINE_SETTER(
char *, oauth_token);
810 conn_errorMessage_func errmsg_impl,
811 conn_oauth_client_id_func clientid_impl,
812 conn_oauth_client_secret_func clientsecret_impl,
813 conn_oauth_discovery_uri_func discoveryuri_impl,
814 conn_oauth_issuer_id_func issuerid_impl,
815 conn_oauth_scope_func scope_impl,
816 conn_sasl_state_func saslstate_impl,
817 set_conn_altsock_func setaltsock_impl,
818 set_conn_oauth_token_func settoken_impl);
831 const char *
const module_name =
832#if defined(__darwin__)
833 LIBDIR
"/libpq-oauth-" PG_MAJORVERSION DLSUFFIX;
835 "libpq-oauth-" PG_MAJORVERSION DLSUFFIX;
839 if (!
state->builtin_flow)
849 fprintf(stderr,
"failed dlopen for libpq-oauth: %s\n",
dlerror());
854 if ((
init =
dlsym(
state->builtin_flow,
"libpq_oauth_init")) == NULL
855 || (flow =
dlsym(
state->builtin_flow,
"pg_fe_run_oauth_flow")) == NULL
994 request_copy =
malloc(
sizeof(*request_copy));
1001 *request_copy = request;
1005 state->async_ctx = request_copy;
1063 "server requires OAuth authentication, but oauth_issuer and oauth_client_id are not both set");
1118 char *
input,
int inputlen,
1119 char **
output,
int *outputlen)
1123 bool discover =
false;
1128 switch (
state->step)
1197 *outputlen = strlen(*
output);
1223 "server sent unexpected additional OAuth data");
1237 *outputlen = strlen(*
output);
1263 "server requires OAuth authentication, but no discovery metadata was provided");
1301 "internal error: OAuth flow did not set a token");
1319 "server sent additional OAuth data after error");
1369 const char *env = getenv(
"PGOAUTHDEBUG");
1371 return (env && strcmp(env,
"UNSAFE") == 0);
static void cleanup(void)
#define fprintf(file, fmt, msg)
int errmsg(const char *fmt,...)
void err(int eval, const char *fmt,...)
#define ERROR_SCOPE_FIELD
static bool setup_token_request(PGconn *conn, fe_oauth_state *state)
static char * issuer_from_well_known_uri(PGconn *conn, const char *wkuri)
static bool handle_oauth_sasl_error(PGconn *conn, const char *msg, int msglen)
static void cleanup_user_oauth_flow(PGconn *conn)
static bool setup_oauth_parameters(PGconn *conn)
#define oauth_json_set_error(ctx,...)
static JsonParseErrorType oauth_json_object_field_start(void *state, char *name, bool isnull)
static JsonParseErrorType oauth_json_scalar(void *state, char *token, JsonTokenType type)
const pg_fe_sasl_mech pg_oauth_mech
static SASLStatus oauth_exchange(void *opaq, bool final, char *input, int inputlen, char **output, int *outputlen)
static bool oauth_channel_bound(void *opaq)
#define oauth_json_has_error(ctx)
static JsonParseErrorType oauth_json_array_start(void *state)
static JsonParseErrorType oauth_json_object_end(void *state)
static void oauth_free(void *opaq)
#define ERROR_OPENID_CONFIGURATION_FIELD
void pqClearOAuthToken(PGconn *conn)
static void * oauth_init(PGconn *conn, const char *password, const char *sasl_mechanism)
static char * client_initial_response(PGconn *conn, bool discover)
bool use_builtin_flow(PGconn *conn, fe_oauth_state *state)
#define ERROR_STATUS_FIELD
static JsonParseErrorType oauth_json_object_start(void *state)
static PostgresPollingStatusType run_user_oauth_flow(PGconn *conn)
bool oauth_unsafe_debugging_enabled(void)
@ FE_OAUTH_REQUESTING_TOKEN
PQauthDataHook_type PQauthDataHook
Assert(PointerIsAligned(start, uint64))
JsonParseErrorType pg_parse_json(JsonLexContext *lex, const JsonSemAction *sem)
JsonLexContext * makeJsonLexContextCstringLen(JsonLexContext *lex, const char *json, size_t len, int encoding, bool need_escapes)
void setJsonLexContextOwnsTokens(JsonLexContext *lex, bool owned_by_context)
char * json_errdetail(JsonParseErrorType error, JsonLexContext *lex)
void freeJsonLexContext(JsonLexContext *lex)
void(* pgthreadlock_t)(int acquire)
PostgresPollingStatusType
@ PQAUTHDATA_OAUTH_BEARER_TOKEN
PostgresPollingStatusType pg_fe_run_oauth_flow(PGconn *conn)
void pg_fe_cleanup_oauth_flow(PGconn *conn)
set_conn_oauth_token_func set_conn_oauth_token
conn_oauth_client_secret_func conn_oauth_client_secret
conn_sasl_state_func conn_sasl_state
conn_oauth_client_id_func conn_oauth_client_id
conn_oauth_scope_func conn_oauth_scope
pgthreadlock_t pg_g_threadlock
conn_oauth_issuer_id_func conn_oauth_issuer_id
set_conn_altsock_func set_conn_altsock
conn_oauth_discovery_uri_func conn_oauth_discovery_uri
void libpq_append_conn_error(PGconn *conn, const char *fmt,...)
conn_errorMessage_func conn_errorMessage
char *(* libpq_gettext_func)(const char *msgid)
void explicit_bzero(void *buf, size_t len)
int pg_strncasecmp(const char *s1, const char *s2, size_t n)
void initPQExpBuffer(PQExpBuffer str)
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void termPQExpBuffer(PQExpBuffer str)
#define PQExpBufferDataBroken(buf)
int pthread_mutex_unlock(pthread_mutex_t *mp)
int pthread_mutex_lock(pthread_mutex_t *mp)
#define PTHREAD_MUTEX_INITIALIZER
json_struct_action object_start
json_ofield_action object_field_start
json_scalar_action scalar
json_struct_action array_start
json_struct_action object_end
void(* cleanup)(PGconn *conn, struct PGoauthBearerRequest *request)
const char * openid_configuration
PostgresPollingStatusType(* async)(PGconn *conn, struct PGoauthBearerRequest *request, SOCKTYPE *altsock)
const char * target_field_name
char * oauth_discovery_uri
void(* cleanup_async_auth)(PGconn *conn)
bool client_finished_auth
PostgresPollingStatusType(* async_auth)(PGconn *conn)
int pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)
void * dlopen(const char *file, int mode)
void * dlsym(void *handle, const char *symbol)
int dlclose(void *handle)