PostgreSQL Source Code git master
Loading...
Searching...
No Matches
fe-auth-oauth.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * fe-auth-oauth.c
4 * The front-end (client) implementation of OAuth/OIDC authentication
5 * using the SASL OAUTHBEARER mechanism.
6 *
7 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
9 *
10 * IDENTIFICATION
11 * src/interfaces/libpq/fe-auth-oauth.c
12 *
13 *-------------------------------------------------------------------------
14 */
15
16#include "postgres_fe.h"
17
18#ifdef USE_DYNAMIC_OAUTH
19#include <dlfcn.h>
20#endif
21
22#include "common/base64.h"
23#include "common/hmac.h"
24#include "common/jsonapi.h"
25#include "common/oauth-common.h"
26#include "fe-auth.h"
27#include "fe-auth-oauth.h"
28#include "mb/pg_wchar.h"
29#include "pg_config_paths.h"
30
31/* The exported OAuth callback mechanism. */
32static void *oauth_init(PGconn *conn, const char *password,
33 const char *sasl_mechanism);
34static SASLStatus oauth_exchange(void *opaq, bool final,
35 char *input, int inputlen,
36 char **output, int *outputlen);
37static bool oauth_channel_bound(void *opaq);
38static void oauth_free(void *opaq);
39
46
47/*
48 * Initializes mechanism state for OAUTHBEARER.
49 *
50 * For a full description of the API, see libpq/fe-auth-sasl.h.
51 */
52static void *
54 const char *sasl_mechanism)
55{
57
58 /*
59 * We only support one SASL mechanism here; anything else is programmer
60 * error.
61 */
62 Assert(sasl_mechanism != NULL);
63 Assert(strcmp(sasl_mechanism, OAUTHBEARER_NAME) == 0);
64
65 state = calloc(1, sizeof(*state));
66 if (!state)
67 return NULL;
68
69 state->step = FE_OAUTH_INIT;
70 state->conn = conn;
71
72 return state;
73}
74
75/*
76 * Frees the state allocated by oauth_init().
77 *
78 * This handles only mechanism state tied to the connection lifetime; state
79 * stored in state->async_ctx is freed up either immediately after the
80 * authentication handshake succeeds, or before the mechanism is cleaned up on
81 * failure. See pg_fe_cleanup_oauth_flow() and cleanup_oauth_flow().
82 */
83static void
85{
87
88 /* Any async authentication state should have been cleaned up already. */
89 Assert(!state->async_ctx);
90
91 free(state);
92}
93
94#define kvsep "\x01"
95
96/*
97 * Constructs an OAUTHBEARER client initial response (RFC 7628, Sec. 3.1).
98 *
99 * If discover is true, the initial response will contain a request for the
100 * server's required OAuth parameters (Sec. 4.3). Otherwise, conn->token must
101 * be set; it will be sent as the connection's bearer token.
102 *
103 * Returns the response as a null-terminated string, or NULL on error.
104 */
105static char *
107{
108 static const char *const resp_format = "n,," kvsep "auth=%s%s" kvsep kvsep;
109
111 const char *authn_scheme;
112 char *response = NULL;
113 const char *token = conn->oauth_token;
114
115 if (discover)
116 {
117 /* Parameter discovery uses a completely empty auth value. */
118 authn_scheme = token = "";
119 }
120 else
121 {
122 /*
123 * Use a Bearer authentication scheme (RFC 6750, Sec. 2.1). A trailing
124 * space is used as a separator.
125 */
126 authn_scheme = "Bearer ";
127
128 /* conn->token must have been set in this case. */
129 if (!token)
130 {
131 Assert(false);
133 "internal error: no OAuth token was set for the connection");
134 return NULL;
135 }
136 }
137
140
142 response = strdup(buf.data);
144
145 if (!response)
146 libpq_append_conn_error(conn, "out of memory");
147
148 return response;
149}
150
151/*
152 * JSON Parser (for the OAUTHBEARER error result)
153 */
154
155/* Relevant JSON fields in the error result object. */
156#define ERROR_STATUS_FIELD "status"
157#define ERROR_SCOPE_FIELD "scope"
158#define ERROR_OPENID_CONFIGURATION_FIELD "openid-configuration"
159
160/*
161 * Limit the maximum number of nested objects/arrays. Because OAUTHBEARER
162 * doesn't have any defined extensions for its JSON yet, we can be much more
163 * conservative here than with libpq-oauth's MAX_OAUTH_NESTING_LEVEL; we expect
164 * a nesting level of 1 in practice.
165 */
166#define MAX_SASL_NESTING_LEVEL 8
167
169{
170 char *errmsg; /* any non-NULL value stops all processing */
171 PQExpBufferData errbuf; /* backing memory for errmsg */
172 int nested; /* nesting level (zero is the top) */
173
174 const char *target_field_name; /* points to a static allocation */
175 char **target_field; /* see below */
176
177 /* target_field, if set, points to one of the following: */
178 char *status;
179 char *scope;
181};
182
183#define oauth_json_has_error(ctx) \
184 (PQExpBufferDataBroken((ctx)->errbuf) || (ctx)->errmsg)
185
186#define oauth_json_set_error(ctx, fmt, ...) \
187 do { \
188 appendPQExpBuffer(&(ctx)->errbuf, libpq_gettext(fmt), ##__VA_ARGS__); \
189 (ctx)->errmsg = (ctx)->errbuf.data; \
190 } while (0)
191
192/* An untranslated version of oauth_json_set_error(). */
193#define oauth_json_set_error_internal(ctx, ...) \
194 do { \
195 appendPQExpBuffer(&(ctx)->errbuf, __VA_ARGS__); \
196 (ctx)->errmsg = (ctx)->errbuf.data; \
197 } while (0)
198
201{
202 struct json_ctx *ctx = state;
203
204 if (ctx->target_field)
205 {
206 Assert(ctx->nested == 1);
207
209 "field \"%s\" must be a string",
210 ctx->target_field_name);
211 }
212
213 ++ctx->nested;
215 oauth_json_set_error(ctx, "JSON is too deeply nested");
216
218}
219
222{
223 struct json_ctx *ctx = state;
224
225 --ctx->nested;
226 return JSON_SUCCESS;
227}
228
230oauth_json_object_field_start(void *state, char *name, bool isnull)
231{
232 struct json_ctx *ctx = state;
233
234 /* Only top-level keys are considered. */
235 if (ctx->nested == 1)
236 {
237 if (strcmp(name, ERROR_STATUS_FIELD) == 0)
238 {
240 ctx->target_field = &ctx->status;
241 }
242 else if (strcmp(name, ERROR_SCOPE_FIELD) == 0)
243 {
245 ctx->target_field = &ctx->scope;
246 }
248 {
250 ctx->target_field = &ctx->discovery_uri;
251 }
252 }
253
254 return JSON_SUCCESS;
255}
256
259{
260 struct json_ctx *ctx = state;
261
262 if (!ctx->nested)
263 {
264 oauth_json_set_error(ctx, "top-level element must be an object");
265 }
266 else if (ctx->target_field)
267 {
268 Assert(ctx->nested == 1);
269
271 "field \"%s\" must be a string",
272 ctx->target_field_name);
273 }
274
275 ++ctx->nested;
277 oauth_json_set_error(ctx, "JSON is too deeply nested");
278
280}
281
284{
285 struct json_ctx *ctx = state;
286
287 --ctx->nested;
288 return JSON_SUCCESS;
289}
290
293{
294 struct json_ctx *ctx = state;
295
296 if (!ctx->nested)
297 {
298 oauth_json_set_error(ctx, "top-level element must be an object");
300 }
301
302 if (ctx->target_field)
303 {
304 if (ctx->nested != 1)
305 {
306 /*
307 * ctx->target_field should not have been set for nested keys.
308 * Assert and don't continue any further for production builds.
309 */
310 Assert(false);
312 "internal error: target scalar found at nesting level %d during OAUTHBEARER parsing",
313 ctx->nested);
315 }
316
317 /*
318 * We don't allow duplicate field names; error out if the target has
319 * already been set.
320 */
321 if (*ctx->target_field)
322 {
324 "field \"%s\" is duplicated",
325 ctx->target_field_name);
327 }
328
329 /* The only fields we support are strings. */
330 if (type != JSON_TOKEN_STRING)
331 {
333 "field \"%s\" must be a string",
334 ctx->target_field_name);
336 }
337
338 *ctx->target_field = strdup(token);
339 if (!*ctx->target_field)
340 return JSON_OUT_OF_MEMORY;
341
342 ctx->target_field = NULL;
343 ctx->target_field_name = NULL;
344 }
345 else
346 {
347 /* otherwise we just ignore it */
348 }
349
350 return JSON_SUCCESS;
351}
352
353#define HTTPS_SCHEME "https://"
354#define HTTP_SCHEME "http://"
355
356/* We support both well-known suffixes defined by RFC 8414. */
357#define WK_PREFIX "/.well-known/"
358#define OPENID_WK_SUFFIX "openid-configuration"
359#define OAUTH_WK_SUFFIX "oauth-authorization-server"
360
361/*
362 * Derives an issuer identifier from one of our recognized .well-known URIs,
363 * using the rules in RFC 8414.
364 */
365static char *
367{
368 const char *authority_start = NULL;
369 const char *wk_start;
370 const char *wk_end;
371 char *issuer;
374 size_t end_len;
375
376 /*
377 * https:// is required for issuer identifiers (RFC 8414, Sec. 2; OIDC
378 * Discovery 1.0, Sec. 3). This is a case-insensitive comparison at this
379 * level (but issuer identifier comparison at the level above this is
380 * case-sensitive, so in practice it's probably moot).
381 */
384
385 if (!authority_start
388 {
389 /* Allow http:// for testing only. */
391 }
392
393 if (!authority_start)
394 {
396 "OAuth discovery URI \"%s\" must use HTTPS",
397 wkuri);
398 return NULL;
399 }
400
401 /*
402 * Well-known URIs in general may support queries and fragments, but the
403 * two types we support here do not. (They must be constructed from the
404 * components of issuer identifiers, which themselves may not contain any
405 * queries or fragments.)
406 *
407 * It's important to check this first, to avoid getting tricked later by a
408 * prefix buried inside a query or fragment.
409 */
410 if (strpbrk(authority_start, "?#") != NULL)
411 {
413 "OAuth discovery URI \"%s\" must not contain query or fragment components",
414 wkuri);
415 return NULL;
416 }
417
418 /*
419 * Find the start of the .well-known prefix. IETF rules (RFC 8615) state
420 * this must be at the beginning of the path component, but OIDC defined
421 * it at the end instead (OIDC Discovery 1.0, Sec. 4), so we have to
422 * search for it anywhere.
423 */
425 if (!wk_start)
426 {
428 "OAuth discovery URI \"%s\" is not a .well-known URI",
429 wkuri);
430 return NULL;
431 }
432
433 /*
434 * Now find the suffix type. We only support the two defined in OIDC
435 * Discovery 1.0 and RFC 8414.
436 */
438
443 else
444 wk_end = NULL;
445
446 /*
447 * Even if there's a match, we still need to check to make sure the suffix
448 * takes up the entire path segment, to weed out constructions like
449 * "/.well-known/openid-configuration-bad".
450 */
451 if (!wk_end || (*wk_end != '/' && *wk_end != '\0'))
452 {
454 "OAuth discovery URI \"%s\" uses an unsupported .well-known suffix",
455 wkuri);
456 return NULL;
457 }
458
459 /*
460 * Finally, make sure the .well-known components are provided either as a
461 * prefix (IETF style) or as a postfix (OIDC style). In other words,
462 * "https://localhost/a/.well-known/openid-configuration/b" is not allowed
463 * to claim association with "https://localhost/a/b".
464 */
465 if (*wk_end != '\0')
466 {
467 /*
468 * It's not at the end, so it's required to be at the beginning at the
469 * path. Find the starting slash.
470 */
471 const char *path_start;
472
474 Assert(path_start); /* otherwise we wouldn't have found WK_PREFIX */
475
476 if (wk_start != path_start)
477 {
479 "OAuth discovery URI \"%s\" uses an invalid format",
480 wkuri);
481 return NULL;
482 }
483 }
484
485 /* Checks passed! Now build the issuer. */
486 issuer = strdup(wkuri);
487 if (!issuer)
488 {
489 libpq_append_conn_error(conn, "out of memory");
490 return NULL;
491 }
492
493 /*
494 * The .well-known components are from [wk_start, wk_end). Remove those to
495 * form the issuer ID, by shifting the path suffix (which may be empty)
496 * leftwards.
497 */
500 end_len = strlen(wk_end) + 1; /* move the NULL terminator too */
501
502 memmove(issuer + start_offset, issuer + end_offset, end_len);
503
504 return issuer;
505}
506
507/*
508 * Parses the server error result (RFC 7628, Sec. 3.2.2) contained in msg and
509 * stores any discovered openid_configuration and scope settings for the
510 * connection.
511 */
512static bool
514{
515 JsonLexContext *lex;
516 JsonSemAction sem = {0};
518 struct json_ctx ctx = {0};
519 char *errmsg = NULL;
520 bool success = false;
521
522 Assert(conn->oauth_issuer_id); /* ensured by setup_oauth_parameters() */
523
524 /* Sanity check. */
525 if (strlen(msg) != msglen)
526 {
528 "server's error message contained an embedded NULL, and was discarded");
529 return false;
530 }
531
532 /*
533 * pg_parse_json doesn't validate the incoming UTF-8, so we have to check
534 * that up front.
535 */
537 {
539 "server's error response is not valid UTF-8");
540 return false;
541 }
542
544 setJsonLexContextOwnsTokens(lex, true); /* must not leak on error */
545
547 sem.semstate = &ctx;
548
555
556 err = pg_parse_json(lex, &sem);
557
559 {
561 errmsg = libpq_gettext("out of memory");
562 else if (ctx.errmsg)
563 errmsg = ctx.errmsg;
564 else
565 {
566 /*
567 * Developer error: one of the action callbacks didn't call
568 * oauth_json_set_error() before erroring out.
569 */
571 errmsg = "<unexpected empty error>";
572 }
573 }
574 else if (err != JSON_SUCCESS)
575 errmsg = json_errdetail(err, lex);
576
577 if (errmsg)
579 "failed to parse server's error response: %s",
580 errmsg);
581
582 /* Don't need the error buffer or the JSON lexer anymore. */
585
586 if (errmsg)
587 goto cleanup;
588
589 if (ctx.discovery_uri)
590 {
591 char *discovery_issuer;
592
593 /*
594 * The URI MUST correspond to our existing issuer, to avoid mix-ups.
595 *
596 * Issuer comparison is done byte-wise, rather than performing any URL
597 * normalization; this follows the suggestions for issuer comparison
598 * in RFC 9207 Sec. 2.4 (which requires simple string comparison) and
599 * vastly simplifies things. Since this is the key protection against
600 * a rogue server sending the client to an untrustworthy location,
601 * simpler is better.
602 */
604 if (!discovery_issuer)
605 goto cleanup; /* error message already set */
606
608 {
610 "server's discovery document at %s (issuer \"%s\") is incompatible with oauth_issuer (%s)",
613
615 goto cleanup;
616 }
617
619
621 {
623 ctx.discovery_uri = NULL;
624 }
625 else
626 {
627 /* This must match the URI we'd previously determined. */
629 {
631 "server's discovery document has moved to %s (previous location was %s)",
632 ctx.discovery_uri,
634 goto cleanup;
635 }
636 }
637 }
638
639 if (ctx.scope)
640 {
641 /* Servers may not override a previously set oauth_scope. */
642 if (!conn->oauth_scope)
643 {
644 conn->oauth_scope = ctx.scope;
645 ctx.scope = NULL;
646 }
647 }
648
649 if (!ctx.status)
650 {
652 "server sent error response without a status");
653 goto cleanup;
654 }
655
656 if (strcmp(ctx.status, "invalid_token") != 0)
657 {
658 /*
659 * invalid_token is the only error code we'll automatically retry for;
660 * otherwise, just bail out now.
661 */
663 "server rejected OAuth bearer token: %s",
664 ctx.status);
665 goto cleanup;
666 }
667
668 success = true;
669
670cleanup:
671 free(ctx.status);
672 free(ctx.scope);
673 free(ctx.discovery_uri);
674
675 return success;
676}
677
678/*
679 * Helper for handling flow failures. If anything was put into request->error,
680 * it's added to conn->errorMessage here.
681 */
682static void
684{
686 const char *errmsg = request->error;
687
688 /*
689 * User-defined flows are called out explicitly so that the user knows who
690 * to blame. Builtin flows don't need that extra message length; we expect
691 * them to always fill in request->error on failure anyway.
692 */
693 if (state->builtin)
694 {
695 if (!errmsg)
696 {
697 /*
698 * Don't turn a bug here into a crash in production, but don't
699 * bother translating either.
700 */
701 Assert(false);
702 errmsg = "builtin flow failed but did not provide an error message";
703 }
704
706 }
707 else
708 {
710 libpq_gettext("user-defined OAuth flow failed"));
711 if (errmsg)
712 {
715 }
716 }
717
719}
720
721/*
722 * Callback implementation of conn->async_auth() for OAuth flows. Delegates the
723 * retrieval of the token to the PGoauthBearerRequestV2.async() callback.
724 *
725 * This will be called multiple times as needed; the callback is responsible for
726 * setting an altsock to signal and returning the correct PGRES_POLLING_*
727 * statuses for use by PQconnectPoll().
728 */
731{
735
736 if (!request->v1.async)
737 {
738 Assert(!state->builtin); /* be very noisy if our code does this */
740 "user-defined OAuth flow provided neither a token nor an async callback");
742 }
743
744 status = request->v1.async(conn,
746 &conn->altsock);
747
749 {
751 return status;
752 }
753 else if (status == PGRES_POLLING_OK)
754 {
755 /*
756 * We already have a token, so copy it into the conn. (We can't hold
757 * onto the original string, since it may not be safe for us to free()
758 * it.)
759 */
760 if (!request->v1.token)
761 {
762 Assert(!state->builtin);
764 "user-defined OAuth flow did not provide a token");
766 }
767
768 conn->oauth_token = strdup(request->v1.token);
769 if (!conn->oauth_token)
770 {
771 libpq_append_conn_error(conn, "out of memory");
773 }
774
775 return PGRES_POLLING_OK;
776 }
777
778 /* The hook wants the client to poll the altsock. Make sure it set one. */
780 {
781 Assert(!state->builtin);
783 "user-defined OAuth flow did not provide a socket for polling");
785 }
786
787 return status;
788}
789
790/*
791 * Cleanup callback for the async flow. Delegates most of its job to
792 * PGoauthBearerRequest.cleanup(), then disconnects the altsock and frees the
793 * request itself.
794 *
795 * This is called either at the end of a successful authentication, or during
796 * pqDropConnection(), so we won't leak resources even if PQconnectPoll() never
797 * calls us back.
798 */
799static void
801{
804
806
807 if (request->v1.cleanup)
808 request->v1.cleanup(conn, (PGoauthBearerRequest *) request);
810
811 free(request);
812 state->async_ctx = NULL;
813}
814
815/*-------------
816 * Builtin Flow
817 *
818 * There are three potential implementations of use_builtin_flow:
819 *
820 * 1) If the OAuth client is disabled at configuration time, return zero.
821 * Dependent clients must provide their own flow.
822 * 2) If the OAuth client is enabled and USE_DYNAMIC_OAUTH is defined, dlopen()
823 * the libpq-oauth plugin and use its implementation.
824 * 3) Otherwise, use flow callbacks that are statically linked into the
825 * executable.
826 *
827 * For caller convenience, the return value follows the convention of
828 * PQauthDataHook: zero means no implementation is provided, negative indicates
829 * failure, and positive indicates success.
830 */
831
832#if !defined(USE_LIBCURL)
833
834/*
835 * This configuration doesn't support the builtin flow.
836 */
837
838static int
843
844#elif defined(USE_DYNAMIC_OAUTH)
845
846/*
847 * Use the builtin flow in the libpq-oauth plugin, which is loaded at runtime.
848 */
849
850typedef char *(*libpq_gettext_func) (const char *msgid);
851
852/*
853 * Loads the libpq-oauth plugin via dlopen(), initializes it, and plugs its
854 * callbacks into the connection's async auth handlers.
855 *
856 * Failure to load here results in a relatively quiet connection error, to
857 * handle the use case where the build supports loading a flow but a user does
858 * not want to install it. Troubleshooting of linker/loader failures can be done
859 * via PGOAUTHDEBUG.
860 *
861 * The lifetime of *request ends shortly after this call, so it must be copied
862 * to longer-lived storage.
863 */
864static int
866{
867 static bool initialized = false;
869 int lockerr;
870
873
874 /*
875 * On macOS only, load the module using its absolute install path; the
876 * standard search behavior is not very helpful for this use case. Unlike
877 * on other platforms, DYLD_LIBRARY_PATH is used as a fallback even with
878 * absolute paths (modulo SIP effects), so tests can continue to work.
879 *
880 * On the other platforms, load the module using only the basename, to
881 * rely on the runtime linker's standard search behavior.
882 */
883 const char *const module_name =
884#if defined(__darwin__)
885 LIBDIR "/libpq-oauth" DLSUFFIX;
886#else
887 "libpq-oauth" DLSUFFIX;
888#endif
889
890 state->builtin_flow = dlopen(module_name, RTLD_NOW | RTLD_LOCAL);
891 if (!state->builtin_flow)
892 {
893 /*
894 * For end users, this probably isn't an error condition, it just
895 * means the flow isn't installed. Developers and package maintainers
896 * may want to debug this via the PGOAUTHDEBUG envvar, though.
897 *
898 * Note that POSIX dlerror() isn't guaranteed to be threadsafe.
899 */
901 fprintf(stderr, "failed dlopen for libpq-oauth: %s\n", dlerror());
902
903 return 0;
904 }
905
906 if ((init = dlsym(state->builtin_flow, "libpq_oauth_init")) == NULL
907 || (start_flow = dlsym(state->builtin_flow, "pg_start_oauthbearer")) == NULL)
908 {
909 /*
910 * This is more of an error condition than the one above, but the
911 * cause is still locked behind PGOAUTHDEBUG due to the dlerror()
912 * threadsafety issue.
913 */
915 fprintf(stderr, "failed dlsym for libpq-oauth: %s\n", dlerror());
916
917 dlclose(state->builtin_flow);
918 state->builtin_flow = NULL;
919
920 request->error = libpq_gettext("could not find entry point for libpq-oauth");
921 return -1;
922 }
923
924 /*
925 * Past this point, we do not unload the module. It stays in the process
926 * permanently.
927 */
928
929 /*
930 * We need to inject necessary function pointers into the module. This
931 * only needs to be done once -- even if the pointers are constant,
932 * assigning them while another thread is executing the flows feels like
933 * tempting fate.
934 */
936 {
937 /* Should not happen... but don't continue if it does. */
938 Assert(false);
939
941 "use_builtin_flow: failed to lock mutex (%d)\n",
942 lockerr);
943
944 request->error = ""; /* satisfy report_flow_error() */
945 return -1;
946 }
947
948 if (!initialized)
949 {
950 init(
953#else
954 NULL
955#endif
956 );
957
958 initialized = true;
959 }
960
962
963 return (start_flow(conn, request) == 0) ? 1 : -1;
964}
965
966#else
967
968/*
969 * For static builds, we can just call pg_start_oauthbearer() directly. It's
970 * provided by libpq-oauth.a.
971 */
972
974
975static int
977{
978 return (pg_start_oauthbearer(conn, request) == 0) ? 1 : -1;
979}
980
981#endif /* USE_LIBCURL */
982
983
984/*
985 * Chooses an OAuth client flow for the connection, which will retrieve a Bearer
986 * token for presentation to the server.
987 *
988 * If the application has registered a custom flow handler using
989 * PQAUTHDATA_OAUTH_BEARER_TOKEN[_V2], it may either return a token immediately
990 * (e.g. if it has one cached for immediate use), or set up for a series of
991 * asynchronous callbacks which will be managed by run_oauth_flow().
992 *
993 * If the default handler is used instead, a Device Authorization flow is used
994 * for the connection if support has been compiled in. (See oauth-curl.c for
995 * implementation details.)
996 *
997 * If neither a custom handler nor the builtin flow is available, the connection
998 * fails here.
999 */
1000static bool
1002{
1003 int res;
1005 .v1 = {
1007 .scope = conn->oauth_scope,
1008 },
1009 .issuer = conn->oauth_issuer_id,
1010 };
1011
1012 Assert(request.v1.openid_configuration);
1013 Assert(request.issuer);
1014
1015 /*
1016 * The client may have overridden the OAuth flow. Try the v2 hook first,
1017 * then fall back to the v1 implementation. If neither is available, try
1018 * the builtin flow.
1019 */
1021 if (res == 0)
1023 if (res == 0)
1024 {
1025 state->builtin = true;
1027 }
1028
1029 if (res > 0)
1030 {
1032
1033 if (request.v1.token)
1034 {
1035 /*
1036 * We already have a token, so copy it into the conn. (We can't
1037 * hold onto the original string, since it may not be safe for us
1038 * to free() it.)
1039 */
1040 conn->oauth_token = strdup(request.v1.token);
1041 if (!conn->oauth_token)
1042 {
1043 libpq_append_conn_error(conn, "out of memory");
1044 goto fail;
1045 }
1046
1047 /* short-circuit */
1048 if (request.v1.cleanup)
1049 request.v1.cleanup(conn, (PGoauthBearerRequest *) &request);
1050 return true;
1051 }
1052
1053 request_copy = malloc(sizeof(*request_copy));
1054 if (!request_copy)
1055 {
1056 libpq_append_conn_error(conn, "out of memory");
1057 goto fail;
1058 }
1059
1061
1064 state->async_ctx = request_copy;
1065
1066 return true;
1067 }
1068
1069 /*
1070 * Failure cases: either we tried to set up a flow and failed, or there
1071 * was no flow to try.
1072 */
1073 if (res < 0)
1075 else
1076 libpq_append_conn_error(conn, "no OAuth flows are available (try installing the libpq-oauth package)");
1077
1078fail:
1079 if (request.v1.cleanup)
1080 request.v1.cleanup(conn, (PGoauthBearerRequest *) &request);
1081 return false;
1082}
1083
1084/*
1085 * Fill in our issuer identifier (and discovery URI, if possible) using the
1086 * connection parameters. If conn->oauth_discovery_uri can't be populated in
1087 * this function, it will be requested from the server.
1088 */
1089static bool
1091{
1092 /*
1093 * This is the only function that sets conn->oauth_issuer_id. If a
1094 * previous connection attempt has already computed it, don't overwrite it
1095 * or the discovery URI. (There's no reason for them to change once
1096 * they're set, and handle_oauth_sasl_error() will fail the connection if
1097 * the server attempts to switch them on us later.)
1098 */
1099 if (conn->oauth_issuer_id)
1100 return true;
1101
1102 /*---
1103 * To talk to a server, we require the user to provide issuer and client
1104 * identifiers.
1105 *
1106 * While it's possible for an OAuth client to support multiple issuers, it
1107 * requires additional effort to make sure the flows in use are safe -- to
1108 * quote RFC 9207,
1109 *
1110 * OAuth clients that interact with only one authorization server are
1111 * not vulnerable to mix-up attacks. However, when such clients decide
1112 * to add support for a second authorization server in the future, they
1113 * become vulnerable and need to apply countermeasures to mix-up
1114 * attacks.
1115 *
1116 * For now, we allow only one.
1117 */
1119 {
1121 "server requires OAuth authentication, but oauth_issuer and oauth_client_id are not both set");
1122 return false;
1123 }
1124
1125 /*
1126 * oauth_issuer is interpreted differently if it's a well-known discovery
1127 * URI rather than just an issuer identifier.
1128 */
1130 {
1131 /*
1132 * Convert the URI back to an issuer identifier. (This also performs
1133 * validation of the URI format.)
1134 */
1137 if (!conn->oauth_issuer_id)
1138 return false; /* error message already set */
1139
1142 {
1143 libpq_append_conn_error(conn, "out of memory");
1144 return false;
1145 }
1146 }
1147 else
1148 {
1149 /*
1150 * Treat oauth_issuer as an issuer identifier. We'll ask the server
1151 * for the discovery URI.
1152 */
1154 if (!conn->oauth_issuer_id)
1155 {
1156 libpq_append_conn_error(conn, "out of memory");
1157 return false;
1158 }
1159 }
1160
1161 return true;
1162}
1163
1164/*
1165 * Implements the OAUTHBEARER SASL exchange (RFC 7628, Sec. 3.2).
1166 *
1167 * If the necessary OAuth parameters are set up on the connection, this will run
1168 * the client flow asynchronously and present the resulting token to the server.
1169 * Otherwise, an empty discovery response will be sent and any parameters sent
1170 * back by the server will be stored for a second attempt.
1171 *
1172 * For a full description of the API, see libpq/sasl.h.
1173 */
1174static SASLStatus
1175oauth_exchange(void *opaq, bool final,
1176 char *input, int inputlen,
1177 char **output, int *outputlen)
1178{
1180 PGconn *conn = state->conn;
1181 bool discover = false;
1182
1183 *output = NULL;
1184 *outputlen = 0;
1185
1186 switch (state->step)
1187 {
1188 case FE_OAUTH_INIT:
1189 /* We begin in the initial response phase. */
1190 Assert(inputlen == -1);
1191
1193 return SASL_FAILED;
1194
1195 if (conn->oauth_token)
1196 {
1197 /*
1198 * A previous connection already fetched the token; we'll use
1199 * it below.
1200 */
1201 }
1202 else if (conn->oauth_discovery_uri)
1203 {
1204 /*
1205 * We don't have a token, but we have a discovery URI already
1206 * stored. Decide whether we're using a user-provided OAuth
1207 * flow or the one we have built in.
1208 */
1210 return SASL_FAILED;
1211
1212 if (conn->oauth_token)
1213 {
1214 /*
1215 * A really smart user implementation may have already
1216 * given us the token (e.g. if there was an unexpired copy
1217 * already cached), and we can use it immediately.
1218 */
1219 }
1220 else
1221 {
1222 /*
1223 * Otherwise, we'll have to hand the connection over to
1224 * our OAuth implementation.
1225 *
1226 * This could take a while, since it generally involves a
1227 * user in the loop. To avoid consuming the server's
1228 * authentication timeout, we'll continue this handshake
1229 * to the end, so that the server can close its side of
1230 * the connection. We'll open a second connection later
1231 * once we've retrieved a token.
1232 */
1233 discover = true;
1234 }
1235 }
1236 else
1237 {
1238 /*
1239 * If we don't have a token, and we don't have a discovery URI
1240 * to be able to request a token, we ask the server for one
1241 * explicitly.
1242 */
1243 discover = true;
1244 }
1245
1246 /*
1247 * Generate an initial response. This either contains a token, if
1248 * we have one, or an empty discovery response which is doomed to
1249 * fail.
1250 */
1252 if (!*output)
1253 return SASL_FAILED;
1254
1255 *outputlen = strlen(*output);
1257
1258 if (conn->oauth_token)
1259 {
1260 /*
1261 * For the purposes of require_auth, our side of
1262 * authentication is done at this point; the server will
1263 * either accept the connection or send an error. Unlike
1264 * SCRAM, there is no additional server data to check upon
1265 * success.
1266 */
1267 conn->client_finished_auth = true;
1268 }
1269
1270 return SASL_CONTINUE;
1271
1273 if (final)
1274 {
1275 /*
1276 * OAUTHBEARER does not make use of additional data with a
1277 * successful SASL exchange, so we shouldn't get an
1278 * AuthenticationSASLFinal message.
1279 */
1281 "server sent unexpected additional OAuth data");
1282 return SASL_FAILED;
1283 }
1284
1285 /*
1286 * An error message was sent by the server. Respond with the
1287 * required dummy message (RFC 7628, sec. 3.2.3).
1288 */
1289 *output = strdup(kvsep);
1290 if (unlikely(!*output))
1291 {
1292 libpq_append_conn_error(conn, "out of memory");
1293 return SASL_FAILED;
1294 }
1295 *outputlen = strlen(*output); /* == 1 */
1296
1297 /* Grab the settings from discovery. */
1299 return SASL_FAILED;
1300
1301 if (conn->oauth_token)
1302 {
1303 /*
1304 * The server rejected our token. Continue onwards towards the
1305 * expected FATAL message, but mark our state to catch any
1306 * unexpected "success" from the server.
1307 */
1309 return SASL_CONTINUE;
1310 }
1311
1312 if (!conn->async_auth)
1313 {
1314 /*
1315 * No OAuth flow is set up yet. Did we get enough information
1316 * from the server to create one?
1317 */
1319 {
1321 "server requires OAuth authentication, but no discovery metadata was provided");
1322 return SASL_FAILED;
1323 }
1324
1325 /* Yes. Set up the flow now. */
1327 return SASL_FAILED;
1328
1329 if (conn->oauth_token)
1330 {
1331 /*
1332 * A token was available in a custom flow's cache. Skip
1333 * the asynchronous processing.
1334 */
1335 goto reconnect;
1336 }
1337 }
1338
1339 /*
1340 * Time to retrieve a token. This involves a number of HTTP
1341 * connections and timed waits, so we escape the synchronous auth
1342 * processing and tell PQconnectPoll to transfer control to our
1343 * async implementation.
1344 */
1345 Assert(conn->async_auth); /* should have been set already */
1347 return SASL_ASYNC;
1348
1350
1351 /*
1352 * We've returned successfully from token retrieval. Double-check
1353 * that we have what we need for the next connection.
1354 */
1355 if (!conn->oauth_token)
1356 {
1357 Assert(false); /* should have failed before this point! */
1359 "internal error: OAuth flow did not set a token");
1360 return SASL_FAILED;
1361 }
1362
1363 goto reconnect;
1364
1366
1367 /*
1368 * After an error, the server should send an error response to
1369 * fail the SASL handshake, which is handled in higher layers.
1370 *
1371 * If we get here, the server either sent *another* challenge
1372 * which isn't defined in the RFC, or completed the handshake
1373 * successfully after telling us it was going to fail. Neither is
1374 * acceptable.
1375 */
1377 "server sent additional OAuth data after error");
1378 return SASL_FAILED;
1379
1380 default:
1381 libpq_append_conn_error(conn, "invalid OAuth exchange state");
1382 break;
1383 }
1384
1385 Assert(false); /* should never get here */
1386 return SASL_FAILED;
1387
1388reconnect:
1389
1390 /*
1391 * Despite being a failure from the point of view of SASL, we have enough
1392 * information to restart with a new connection.
1393 */
1394 libpq_append_conn_error(conn, "retrying connection with new bearer token");
1395 conn->oauth_want_retry = true;
1396 return SASL_FAILED;
1397}
1398
1399static bool
1401{
1402 /* This mechanism does not support channel binding. */
1403 return false;
1404}
1405
1406/*
1407 * Fully clears out any stored OAuth token. This is done proactively upon
1408 * successful connection as well as during pqClosePGconn().
1409 */
1410void
1420
1421/*
1422 * Returns true if the PGOAUTHDEBUG=UNSAFE flag is set in the environment.
1423 */
1424bool
1426{
1427 const char *env = getenv("PGOAUTHDEBUG");
1428
1429 return (env && strcmp(env, "UNSAFE") == 0);
1430}
static void cleanup(void)
Definition bootstrap.c:879
#define Assert(condition)
Definition c.h:945
#define unlikely(x)
Definition c.h:432
#define fprintf(file, fmt, msg)
Definition cubescan.l:21
void err(int eval, const char *fmt,...)
Definition err.c:43
#define HTTP_SCHEME
#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)
#define HTTPS_SCHEME
static bool handle_oauth_sasl_error(PGconn *conn, const char *msg, int msglen)
#define oauth_json_set_error(ctx, fmt,...)
static bool setup_oauth_parameters(PGconn *conn)
#define WK_PREFIX
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
#define OPENID_WK_SUFFIX
static SASLStatus oauth_exchange(void *opaq, bool final, char *input, int inputlen, char **output, int *outputlen)
#define OAUTH_WK_SUFFIX
static bool oauth_channel_bound(void *opaq)
static PostgresPollingStatusType run_oauth_flow(PGconn *conn)
static int use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *request)
static void report_flow_error(PGconn *conn, const PGoauthBearerRequestV2 *request)
#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
static void cleanup_oauth_flow(PGconn *conn)
#define oauth_json_set_error_internal(ctx,...)
void pqClearOAuthToken(PGconn *conn)
static void * oauth_init(PGconn *conn, const char *password, const char *sasl_mechanism)
#define kvsep
static char * client_initial_response(PGconn *conn, bool discover)
#define ERROR_STATUS_FIELD
#define MAX_SASL_NESTING_LEVEL
static JsonParseErrorType oauth_json_object_start(void *state)
bool oauth_unsafe_debugging_enabled(void)
@ FE_OAUTH_REQUESTING_TOKEN
@ FE_OAUTH_SERVER_ERROR
@ FE_OAUTH_INIT
@ FE_OAUTH_BEARER_SENT
SASLStatus
@ SASL_ASYNC
@ SASL_CONTINUE
@ SASL_FAILED
PQauthDataHook_type PQauthDataHook
Definition fe-auth.c:1586
void libpq_append_conn_error(PGconn *conn, const char *fmt,...)
Definition fe-misc.c:1404
FILE * input
FILE * output
static bool success
Definition initdb.c:188
JsonParseErrorType pg_parse_json(JsonLexContext *lex, const JsonSemAction *sem)
Definition jsonapi.c:744
JsonLexContext * makeJsonLexContextCstringLen(JsonLexContext *lex, const char *json, size_t len, int encoding, bool need_escapes)
Definition jsonapi.c:392
void setJsonLexContextOwnsTokens(JsonLexContext *lex, bool owned_by_context)
Definition jsonapi.c:542
char * json_errdetail(JsonParseErrorType error, JsonLexContext *lex)
Definition jsonapi.c:2404
void freeJsonLexContext(JsonLexContext *lex)
Definition jsonapi.c:687
JsonParseErrorType
Definition jsonapi.h:35
@ JSON_OUT_OF_MEMORY
Definition jsonapi.h:52
@ JSON_SEM_ACTION_FAILED
Definition jsonapi.h:59
@ JSON_SUCCESS
Definition jsonapi.h:36
JsonTokenType
Definition jsonapi.h:18
@ JSON_TOKEN_STRING
Definition jsonapi.h:20
PostgresPollingStatusType
Definition libpq-fe.h:120
@ PGRES_POLLING_OK
Definition libpq-fe.h:124
@ PGRES_POLLING_FAILED
Definition libpq-fe.h:121
@ PQAUTHDATA_OAUTH_BEARER_TOKEN
Definition libpq-fe.h:202
@ PQAUTHDATA_OAUTH_BEARER_TOKEN_V2
Definition libpq-fe.h:204
#define PG_UTF8
Definition mbprint.c:43
#define OAUTHBEARER_NAME
int pg_start_oauthbearer(PGconn *conn, PGoauthBearerRequestV2 *request)
#define libpq_gettext(x)
Definition oauth-utils.h:45
char *(* libpq_gettext_func)(const char *msgid)
Definition oauth-utils.h:21
static char * errmsg
static char buf[DEFAULT_XLOG_SEG_SIZE]
void explicit_bzero(void *buf, size_t len)
#define PGINVALID_SOCKET
Definition port.h:31
int pg_strncasecmp(const char *s1, const char *s2, size_t n)
void initPQExpBuffer(PQExpBuffer str)
Definition pqexpbuffer.c:90
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void appendPQExpBufferChar(PQExpBuffer str, char ch)
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
void termPQExpBuffer(PQExpBuffer str)
#define PQExpBufferDataBroken(buf)
Definition pqexpbuffer.h:67
static int fb(int x)
int pthread_mutex_unlock(pthread_mutex_t *mp)
int pthread_mutex_lock(pthread_mutex_t *mp)
#define PTHREAD_MUTEX_INITIALIZER
#define calloc(a, b)
#define free(a)
#define malloc(a)
#define init()
static char * password
Definition streamutil.c:51
PGconn * conn
Definition streamutil.c:52
json_struct_action array_end
Definition jsonapi.h:157
json_struct_action object_start
Definition jsonapi.h:154
json_ofield_action object_field_start
Definition jsonapi.h:158
json_scalar_action scalar
Definition jsonapi.h:162
void * semstate
Definition jsonapi.h:153
json_struct_action array_start
Definition jsonapi.h:156
json_struct_action object_end
Definition jsonapi.h:155
PGoauthBearerRequest v1
Definition libpq-fe.h:826
const char * openid_configuration
Definition libpq-fe.h:767
char * discovery_uri
const char * target_field_name
char * status
char * scope
PQExpBufferData errbuf
char ** target_field
char * errmsg
char * oauth_discovery_uri
Definition libpq-int.h:441
char * oauth_scope
Definition libpq-int.h:445
void(* cleanup_async_auth)(PGconn *conn)
Definition libpq-int.h:529
bool client_finished_auth
Definition libpq-int.h:521
char * oauth_client_id
Definition libpq-int.h:443
char * oauth_issuer
Definition libpq-int.h:439
bool oauth_want_retry
Definition libpq-int.h:447
char * oauth_token
Definition libpq-int.h:446
char * oauth_issuer_id
Definition libpq-int.h:440
PQExpBufferData errorMessage
Definition libpq-int.h:683
pgsocket altsock
Definition libpq-int.h:530
PostgresPollingStatusType(* async_auth)(PGconn *conn)
Definition libpq-int.h:528
void * sasl_state
Definition libpq-int.h:612
static JsonSemAction sem
const char * type
const char * name
int pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)
Definition wchar.c:2224
void * dlopen(const char *file, int mode)
Definition win32dlopen.c:76
char * dlerror(void)
Definition win32dlopen.c:40
void * dlsym(void *handle, const char *symbol)
Definition win32dlopen.c:61
#define RTLD_NOW
Definition win32_port.h:530
int dlclose(void *handle)
Definition win32dlopen.c:49
static bool initialized
Definition win32ntdll.c:36