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"
166#define MAX_SASL_NESTING_LEVEL 8
183#define oauth_json_has_error(ctx) \
184 (PQExpBufferDataBroken((ctx)->errbuf) || (ctx)->errmsg)
186#define oauth_json_set_error(ctx, ...) \
188 appendPQExpBuffer(&(ctx)->errbuf, __VA_ARGS__); \
189 (ctx)->errmsg = (ctx)->errbuf.data; \
305 "internal error: target scalar found at nesting level %d during OAUTHBEARER parsing",
346#define HTTPS_SCHEME "https://"
347#define HTTP_SCHEME "http://"
350#define WK_PREFIX "/.well-known/"
351#define OPENID_WK_SUFFIX "openid-configuration"
352#define OAUTH_WK_SUFFIX "oauth-authorization-server"
361 const char *authority_start = NULL;
362 const char *wk_start;
365 ptrdiff_t start_offset,
386 if (!authority_start)
389 "OAuth discovery URI \"%s\" must use HTTPS",
403 if (strpbrk(authority_start,
"?#") != NULL)
406 "OAuth discovery URI \"%s\" must not contain query or fragment components",
417 wk_start = strstr(authority_start,
WK_PREFIX);
421 "OAuth discovery URI \"%s\" is not a .well-known URI",
444 if (!wk_end || (*wk_end !=
'/' && *wk_end !=
'\0'))
447 "OAuth discovery URI \"%s\" uses an unsupported .well-known suffix",
464 const char *path_start;
466 path_start = strchr(authority_start,
'/');
469 if (wk_start != path_start)
472 "OAuth discovery URI \"%s\" uses an invalid format",
479 issuer = strdup(wkuri);
491 start_offset = wk_start - wkuri;
492 end_offset = wk_end - wkuri;
493 end_len = strlen(wk_end) + 1;
495 memmove(issuer + start_offset, issuer + end_offset, end_len);
518 if (strlen(msg) != msglen)
521 "server's error message contained an embedded NULL, and was discarded");
532 "server's error response is not valid UTF-8");
564 errmsg =
"<unexpected empty error>";
572 "failed to parse server's error response: %s",
584 char *discovery_issuer;
597 if (!discovery_issuer)
603 "server's discovery document at %s (issuer \"%s\") is incompatible with oauth_issuer (%s)",
607 free(discovery_issuer);
611 free(discovery_issuer);
624 "server's discovery document has moved to %s (previous location was %s)",
645 "server sent error response without a status");
649 if (strcmp(ctx.
status,
"invalid_token") != 0)
656 "server rejected OAuth bearer token: %s",
689 "user-defined OAuth flow provided neither a token nor an async callback");
709 "user-defined OAuth flow did not provide a token");
727 "user-defined OAuth flow did not provide a socket for polling");
751 state->async_ctx = NULL;
767#if !defined(USE_LIBCURL)
779#elif defined(USE_DYNAMIC_OAUTH)
785typedef char *(*libpq_gettext_func) (
const char *msgid);
793#define DEFINE_GETTER(TYPE, MEMBER) \
794 typedef TYPE (*conn_ ## MEMBER ## _func) (PGconn *conn); \
795 static TYPE conn_ ## MEMBER(PGconn *conn) { return conn->MEMBER; }
798#define DEFINE_GETTER_P(TYPE, MEMBER) \
799 typedef TYPE (*conn_ ## MEMBER ## _func) (PGconn *conn); \
800 static TYPE conn_ ## MEMBER(PGconn *conn) { return &conn->MEMBER; }
802#define DEFINE_SETTER(TYPE, MEMBER) \
803 typedef void (*set_conn_ ## MEMBER ## _func) (PGconn *conn, TYPE val); \
804 static void set_conn_ ## MEMBER(PGconn *conn, TYPE val) { conn->MEMBER = val; }
807DEFINE_GETTER(
char *, oauth_client_id);
808DEFINE_GETTER(
char *, oauth_client_secret);
809DEFINE_GETTER(
char *, oauth_discovery_uri);
810DEFINE_GETTER(
char *, oauth_issuer_id);
811DEFINE_GETTER(
char *, oauth_scope);
815DEFINE_SETTER(
char *, oauth_token);
835 conn_errorMessage_func errmsg_impl,
836 conn_oauth_client_id_func clientid_impl,
837 conn_oauth_client_secret_func clientsecret_impl,
838 conn_oauth_discovery_uri_func discoveryuri_impl,
839 conn_oauth_issuer_id_func issuerid_impl,
840 conn_oauth_scope_func scope_impl,
841 conn_sasl_state_func saslstate_impl,
842 set_conn_altsock_func setaltsock_impl,
843 set_conn_oauth_token_func settoken_impl);
856 const char *
const module_name =
857#if defined(__darwin__)
858 LIBDIR
"/libpq-oauth-" PG_MAJORVERSION DLSUFFIX;
860 "libpq-oauth-" PG_MAJORVERSION DLSUFFIX;
864 if (!
state->builtin_flow)
874 fprintf(stderr,
"failed dlopen for libpq-oauth: %s\n",
dlerror());
879 if ((
init =
dlsym(
state->builtin_flow,
"libpq_oauth_init")) == NULL
880 || (flow =
dlsym(
state->builtin_flow,
"pg_fe_run_oauth_flow")) == NULL
1019 request_copy =
malloc(
sizeof(*request_copy));
1026 *request_copy = request;
1030 state->async_ctx = request_copy;
1088 "server requires OAuth authentication, but oauth_issuer and oauth_client_id are not both set");
1143 char *
input,
int inputlen,
1144 char **
output,
int *outputlen)
1148 bool discover =
false;
1153 switch (
state->step)
1222 *outputlen = strlen(*
output);
1248 "server sent unexpected additional OAuth data");
1262 *outputlen = strlen(*
output);
1288 "server requires OAuth authentication, but no discovery metadata was provided");
1326 "internal error: OAuth flow did not set a token");
1344 "server sent additional OAuth data after error");
1394 const char *env = getenv(
"PGOAUTHDEBUG");
1396 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 JsonParseErrorType oauth_json_array_end(void *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
#define MAX_SASL_NESTING_LEVEL
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 array_end
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)