22#if defined(HAVE_SYS_EPOLL_H)
24#include <sys/timerfd.h>
25#elif defined(HAVE_SYS_EVENT_H)
28#error libpq-oauth is not supported on this platform
35#ifdef USE_DYNAMIC_OAUTH
59#if defined(USE_DYNAMIC_OAUTH) && defined(LIBPQ_INT_H)
60#error do not rely on libpq-int.h in dynamic builds of libpq-oauth
75#define MAX_OAUTH_RESPONSE_SIZE (256 * 1024)
89#define MAX_OAUTH_NESTING_LEVEL 16
308 libpq_gettext(
"WARNING: libcurl easy handle removal failed: %s\n"),
328 libpq_gettext(
"WARNING: libcurl multi handle cleanup failed: %s\n"),
341 if (
actx->timerfd >= 0)
397 if (
actx->curl_err[0])
419#define actx_error(ACTX, FMT, ...) \
420 appendPQExpBuffer(&(ACTX)->errbuf, libpq_gettext(FMT), ##__VA_ARGS__)
422#define actx_error_internal(ACTX, FMT, ...) \
423 appendPQExpBuffer(&(ACTX)->errbuf, FMT, ##__VA_ARGS__)
425#define actx_error_str(ACTX, S) \
426 appendPQExpBufferStr(&(ACTX)->errbuf, S)
433#define CHECK_MSETOPT(ACTX, OPT, VAL, FAILACTION) \
435 struct async_ctx *_actx = (ACTX); \
436 CURLMcode _setopterr = curl_multi_setopt(_actx->curlm, OPT, VAL); \
438 actx_error(_actx, "failed to set %s on OAuth connection: %s",\
439 #OPT, curl_multi_strerror(_setopterr)); \
444#define CHECK_SETOPT(ACTX, OPT, VAL, FAILACTION) \
446 struct async_ctx *_actx = (ACTX); \
447 CURLcode _setopterr = curl_easy_setopt(_actx->curl, OPT, VAL); \
449 actx_error(_actx, "failed to set %s on OAuth connection: %s",\
450 #OPT, curl_easy_strerror(_setopterr)); \
455#define CHECK_GETINFO(ACTX, INFO, OUT, FAILACTION) \
457 struct async_ctx *_actx = (ACTX); \
458 CURLcode _getinfoerr = curl_easy_getinfo(_actx->curl, INFO, OUT); \
460 actx_error(_actx, "failed to get %s from OAuth response: %s",\
461 #INFO, curl_easy_strerror(_getinfoerr)); \
496#define PG_OAUTH_REQUIRED true
497#define PG_OAUTH_OPTIONAL false
509#define oauth_parse_set_error(ctx, fmt, ...) \
510 appendPQExpBuffer((ctx)->errbuf, libpq_gettext(fmt), ##__VA_ARGS__)
512#define oauth_parse_set_error_internal(ctx, fmt, ...) \
513 appendPQExpBuffer((ctx)->errbuf, fmt, ##__VA_ARGS__)
591 "internal error: started field \"%s\" before field \"%s\" was finished",
643 "internal error: field \"%s\" still active at end of object",
699 "internal error: found unexpected array end while parsing field \"%s\"",
754 "internal error: scalar target found at nesting level %d",
764 "internal error: scalar field \"%s\" would be assigned twice",
786 "internal error: array member found at nesting level %d",
844 switch (content_type[
i])
860 actx_error(
actx,
"unexpected content type: \"%s\"", content_type);
1153 if (
err->error_description)
1169 ?
gettext_noop(
"provider rejected the oauth_client_secret")
1170 :
gettext_noop(
"provider requires client authentication, and no oauth_client_secret is set"));
1227#if defined(HAVE_SYS_EPOLL_H)
1238 if (
actx->timerfd < 0)
1251#elif defined(HAVE_SYS_EVENT_H)
1266 if (
actx->timerfd < 0)
1274#error setup_multiplexer is not implemented on this platform
1288#if defined(HAVE_SYS_EPOLL_H)
1344#elif defined(HAVE_SYS_EVENT_H)
1406 for (
int i = 0;
i < res; ++
i)
1432#error register_socket is not implemented on this platform
1451#if defined(HAVE_SYS_EPOLL_H)
1454#elif defined(HAVE_SYS_EVENT_H)
1479#error comb_multiplexer is not implemented on this platform
1499#if defined(HAVE_SYS_EPOLL_H)
1513 spec.it_value.tv_nsec = 1;
1518 spec.it_value.tv_nsec = (
timeout % 1000) * 1000000;
1528#elif defined(HAVE_SYS_EVENT_H)
1584#error set_timer is not implemented on this platform
1596#if defined(HAVE_SYS_EPOLL_H) || defined(HAVE_SYS_EVENT_H)
1609#error timer_expired is not implemented on this platform
1707 for (
int i = 0;
i < size;
i++)
1717 if (
c >= 0x20 &&
c <= 0x7E)
1722 && (
c ==
'\r' ||
c ==
'\n'))
1828#if CURL_AT_LEAST_VERSION(7, 85, 0)
1830 const char *
protos =
"https";
1831 const char *
const unsafe =
"https,http";
1956#ifndef CURL_IGNORE_DEPRECATION
1957#define CURL_IGNORE_DEPRECATION(x) x
1964#define PG_CURL_IGNORE_DEPRECATION(x) CURL_IGNORE_DEPRECATION(x;)
2033 if (
actx->errbuf.len == 0)
2054 actx_error(
actx,
"no result was retrieved for the finished handle");
2201 if (!
actx->provider.grant_types_supported)
2220 actx->provider.grant_types_supported =
temp;
2234 const char *oauth_issuer_id =
actx->issuer_id;
2259 "the issuer identifier (%s) does not match oauth_issuer (%s)",
2267#define HTTPS_SCHEME "https://"
2268#define OAUTH_GRANT_TYPE_DEVICE_CODE "urn:ietf:params:oauth:grant-type:device_code"
2286 "issuer \"%s\" does not provide a device authorization endpoint",
2312 "device authorization endpoint \"%s\" must use HTTPS",
2321 "token endpoint \"%s\" must use HTTPS",
2337 const char *oauth_client_id =
actx->client_id;
2338 const char *oauth_client_secret =
actx->client_secret;
2344 if (oauth_client_secret)
2385 actx->used_basic_auth =
true;
2396 actx->used_basic_auth =
false;
2421 const char *oauth_scope =
actx->scope;
2429 if (oauth_scope && oauth_scope[0])
2509 const char *device_code =
actx->authz.device_code;
2596 if (
tok.access_token)
2610 if (
strcmp(
err->error,
"authorization_pending") != 0 &&
2621 if (
strcmp(
err->error,
"slow_down") == 0)
2625 actx->authz.interval += 5;
2650 .user_code =
actx->authz.user_code,
2651 .verification_uri_complete =
actx->authz.verification_uri_complete,
2652 .expires_in =
actx->authz.expires_in,
2699#if HAVE_THREADSAFE_CURL_GLOBAL_INIT
2703#if !HAVE_THREADSAFE_CURL_GLOBAL_INIT
2724 req->error =
libpq_gettext(
"curl_global_init previously failed during OAuth setup");
2747#if HAVE_THREADSAFE_CURL_GLOBAL_INIT
2764 "\tCurl initialization was reported thread-safe when libpq\n"
2765 "\twas compiled, but the currently installed version of\n"
2766 "\tlibcurl reports that it is not. Recompile libpq against\n"
2767 "\tthe installed version of libcurl.");
2776#if !HAVE_THREADSAFE_CURL_GLOBAL_INIT
2801 char *oauth_token =
NULL;
2871 *altsock =
actx->timerfd;
2931 request->v1.token = oauth_token;
2933 if (!
actx->user_prompted)
2942 actx->user_prompted =
true;
2960 *altsock =
actx->timerfd;
2980 }
while (!oauth_token && !
actx->running);
3003 bool sigpipe_pending;
3038 actx->dbg_num_calls++;
3041 actx->dbg_num_calls);
3112 if (
strcmp(opt->keyword,
"oauth_client_id") == 0)
3115 if (!
actx->client_id)
3118 else if (
strcmp(opt->keyword,
"oauth_client_secret") == 0)
3121 if (!
actx->client_secret)
3124 else if (
strcmp(opt->keyword,
"oauth_ca_file") == 0)
3135 actx->discovery_uri =
request->v1.openid_configuration;
static void cleanup(void)
#define Assert(condition)
#define fprintf(file, fmt, msg)
void err(int eval, const char *fmt,...)
PQauthDataHook_type PQgetAuthDataHook(void)
PQconninfoOption * PQconninfo(PGconn *conn)
void PQconninfoFree(PQconninfoOption *connOptions)
int PQsocketPoll(int sock, int forRead, int forWrite, pg_usec_time_t end_time)
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)
int(* PQauthDataHook_type)(PGauthData type, PGconn *conn, void *data)
PostgresPollingStatusType
@ PQAUTHDATA_PROMPT_OAUTH_DEVICE
static bool drain_timer_events(struct async_ctx *actx, bool *was_expired)
static char * urlencode(const char *s)
static bool setup_multiplexer(struct async_ctx *actx)
static bool finish_token_request(struct async_ctx *actx, struct token *tok)
static JsonParseErrorType oauth_json_array_end(void *state)
static void append_urlencoded(PQExpBuffer buf, const char *s)
static bool start_token_request(struct async_ctx *actx, PGconn *conn)
#define MAX_OAUTH_RESPONSE_SIZE
static bool parse_token_error(struct async_ctx *actx, struct token_error *err)
int pg_start_oauthbearer(PGconn *conn, PGoauthBearerRequestV2 *request)
static bool add_client_identification(struct async_ctx *actx, PQExpBuffer reqbody, PGconn *conn)
static int parse_interval(struct async_ctx *actx, const char *interval_str)
static void free_provider(struct provider *provider)
static void build_urlencoded(PQExpBuffer buf, const char *key, const char *value)
#define PG_CURL_IGNORE_DEPRECATION(x)
#define oauth_parse_set_error_internal(ctx, fmt,...)
static void record_token_error(struct async_ctx *actx, const struct token_error *err)
static bool parse_device_authz(struct async_ctx *actx, struct device_authz *authz)
static void report_type_mismatch(struct oauth_parse *ctx)
static int register_socket(CURL *curl, curl_socket_t socket, int what, void *ctx, void *socketp)
#define PG_OAUTH_OPTIONAL
static bool set_timer(struct async_ctx *actx, long timeout)
#define actx_error_internal(ACTX, FMT,...)
static bool parse_access_token(struct async_ctx *actx, struct token *tok)
static int timer_expired(struct async_ctx *actx)
static PostgresPollingStatusType drive_request(struct async_ctx *actx)
static bool start_device_authz(struct async_ctx *actx, PGconn *conn)
static bool prompt_user(struct async_ctx *actx, PGconn *conn)
#define CHECK_MSETOPT(ACTX, OPT, VAL, FAILACTION)
static bool finish_discovery(struct async_ctx *actx)
static void append_actx_error(PGoauthBearerRequestV2 *req, struct async_ctx *actx)
static double parse_json_number(const char *s)
static bool start_discovery(struct async_ctx *actx, const char *discovery_uri)
static JsonParseErrorType oauth_json_object_field_start(void *state, char *name, bool isnull)
static JsonParseErrorType oauth_json_scalar(void *state, char *token, JsonTokenType type)
static void free_token_error(struct token_error *err)
#define actx_error_str(ACTX, S)
static bool finish_device_authz(struct async_ctx *actx)
static size_t append_data(char *buf, size_t size, size_t nmemb, void *userdata)
#define CHECK_SETOPT(ACTX, OPT, VAL, FAILACTION)
static bool parse_oauth_json(struct async_ctx *actx, const struct json_field *fields)
#define MAX_OAUTH_NESTING_LEVEL
#define OAUTH_GRANT_TYPE_DEVICE_CODE
static PostgresPollingStatusType pg_fe_run_oauth_flow(PGconn *conn, struct PGoauthBearerRequest *request, int *altsock)
static JsonParseErrorType oauth_json_array_start(void *state)
static JsonParseErrorType oauth_json_object_end(void *state)
static void pg_fe_cleanup_oauth_flow(PGconn *conn, PGoauthBearerRequest *request)
static bool initialize_curl(PGoauthBearerRequestV2 *req)
static int debug_callback(CURL *handle, curl_infotype type, char *data, size_t size, void *clientp)
static PostgresPollingStatusType pg_fe_run_oauth_flow_impl(PGconn *conn, PGoauthBearerRequestV2 *request, int *altsock)
static void free_token(struct token *tok)
#define oauth_parse_set_error(ctx, fmt,...)
static bool comb_multiplexer(struct async_ctx *actx)
@ OAUTH_STEP_DEVICE_AUTHORIZATION
@ OAUTH_STEP_WAIT_INTERVAL
@ OAUTH_STEP_TOKEN_REQUEST
static int register_timer(CURLM *curlm, long timeout, void *ctx)
static void free_async_ctx(struct async_ctx *actx)
#define CHECK_GETINFO(ACTX, INFO, OUT, FAILACTION)
static bool check_content_type(struct async_ctx *actx, const char *type)
static bool check_issuer(struct async_ctx *actx, PGconn *conn)
#define actx_error(ACTX, FMT,...)
static bool parse_provider(struct async_ctx *actx, struct provider *provider)
static bool start_request(struct async_ctx *actx)
static int parse_expires_in(struct async_ctx *actx, const char *expires_in_str)
static void free_device_authz(struct device_authz *authz)
static bool handle_token_response(struct async_ctx *actx, char **token)
static JsonParseErrorType oauth_json_object_start(void *state)
#define PG_OAUTH_REQUIRED
static bool check_for_device_flow(struct async_ctx *actx)
static bool setup_curl_handles(struct async_ctx *actx)
#define OAUTHDEBUG_UNSAFE_HTTP
static uint32 oauth_parse_debug_flags(void)
#define OAUTHDEBUG_CALL_COUNT
#define OAUTHDEBUG_UNSAFE_TRACE
#define OAUTHDEBUG_UNSAFE_DOS_ENDPOINT
void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
#define pgunlock_thread()
static char buf[DEFAULT_XLOG_SEG_SIZE]
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 resetPQExpBuffer(PQExpBuffer str)
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
void appendPQExpBufferChar(PQExpBuffer str, char ch)
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
void termPQExpBuffer(PQExpBuffer str)
#define PQExpBufferBroken(str)
#define PQExpBufferDataBroken(buf)
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
const char * verification_uri
struct device_authz authz
PQExpBufferData work_data
const char * discovery_uri
char curl_err[CURL_ERROR_SIZE]
struct curl_slist * headers
char * verification_uri_complete
struct curl_slist ** array
const struct json_field * active
const struct json_field * fields
char * device_authorization_endpoint
struct curl_slist * grant_types_supported
int pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)
#define socket(af, type, protocol)