PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
auth-oauth.c File Reference
#include "postgres.h"
#include <unistd.h>
#include <fcntl.h>
#include "common/oauth-common.h"
#include "fmgr.h"
#include "lib/stringinfo.h"
#include "libpq/auth.h"
#include "libpq/hba.h"
#include "libpq/oauth.h"
#include "libpq/sasl.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "utils/json.h"
#include "utils/varlena.h"
Include dependency graph for auth-oauth.c:

Go to the source code of this file.

Data Structures

struct  oauth_ctx
 

Macros

#define KVSEP   0x01 /* separator byte for key/value pairs */
 
#define AUTH_KEY   "auth" /* key containing the Authorization header */
 
#define BEARER_SCHEME   "Bearer " /* required header scheme (case-insensitive!) */
 

Enumerations

enum  oauth_state { OAUTH_STATE_INIT = 0 , OAUTH_STATE_ERROR , OAUTH_STATE_FINISHED }
 

Functions

static void oauth_get_mechanisms (Port *port, StringInfo buf)
 
static void * oauth_init (Port *port, const char *selected_mech, const char *shadow_pass)
 
static int oauth_exchange (void *opaq, const char *input, int inputlen, char **output, int *outputlen, const char **logdetail)
 
static void load_validator_library (const char *libname)
 
static void shutdown_validator_library (void *arg)
 
static char * sanitize_char (char c)
 
static char * parse_kvpairs_for_auth (char **input)
 
static void generate_error_response (struct oauth_ctx *ctx, char **output, int *outputlen)
 
static bool validate (Port *port, const char *auth)
 
static void validate_kvpair (const char *key, const char *val)
 
static const char * validate_token_format (const char *header)
 
bool check_oauth_validator (HbaLine *hbaline, int elevel, char **err_msg)
 

Variables

char * oauth_validator_libraries_string = NULL
 
static ValidatorModuleStatevalidator_module_state
 
static const OAuthValidatorCallbacksValidatorCallbacks
 
const pg_be_sasl_mech pg_be_oauth_mech
 

Macro Definition Documentation

◆ AUTH_KEY

#define AUTH_KEY   "auth" /* key containing the Authorization header */

Definition at line 80 of file auth-oauth.c.

◆ BEARER_SCHEME

#define BEARER_SCHEME   "Bearer " /* required header scheme (case-insensitive!) */

Definition at line 81 of file auth-oauth.c.

◆ KVSEP

#define KVSEP   0x01 /* separator byte for key/value pairs */

Definition at line 79 of file auth-oauth.c.

Enumeration Type Documentation

◆ oauth_state

Enumerator
OAUTH_STATE_INIT 
OAUTH_STATE_ERROR 
OAUTH_STATE_FINISHED 

Definition at line 57 of file auth-oauth.c.

58{
62};
@ OAUTH_STATE_INIT
Definition: auth-oauth.c:59
@ OAUTH_STATE_FINISHED
Definition: auth-oauth.c:61
@ OAUTH_STATE_ERROR
Definition: auth-oauth.c:60

Function Documentation

◆ check_oauth_validator()

bool check_oauth_validator ( HbaLine hbaline,
int  elevel,
char **  err_msg 
)

Definition at line 820 of file auth-oauth.c.

821{
822 int line_num = hbaline->linenumber;
823 const char *file_name = hbaline->sourcefile;
824 char *rawstring;
825 List *elemlist = NIL;
826
827 *err_msg = NULL;
828
830 {
831 ereport(elevel,
832 errcode(ERRCODE_CONFIG_FILE_ERROR),
833 errmsg("oauth_validator_libraries must be set for authentication method %s",
834 "oauth"),
835 errcontext("line %d of configuration file \"%s\"",
836 line_num, file_name));
837 *err_msg = psprintf("oauth_validator_libraries must be set for authentication method %s",
838 "oauth");
839 return false;
840 }
841
842 /* SplitDirectoriesString needs a modifiable copy */
844
845 if (!SplitDirectoriesString(rawstring, ',', &elemlist))
846 {
847 /* syntax error in list */
848 ereport(elevel,
849 errcode(ERRCODE_CONFIG_FILE_ERROR),
850 errmsg("invalid list syntax in parameter \"%s\"",
851 "oauth_validator_libraries"));
852 *err_msg = psprintf("invalid list syntax in parameter \"%s\"",
853 "oauth_validator_libraries");
854 goto done;
855 }
856
857 if (!hbaline->oauth_validator)
858 {
859 if (elemlist->length == 1)
860 {
861 hbaline->oauth_validator = pstrdup(linitial(elemlist));
862 goto done;
863 }
864
865 ereport(elevel,
866 errcode(ERRCODE_CONFIG_FILE_ERROR),
867 errmsg("authentication method \"oauth\" requires argument \"validator\" to be set when oauth_validator_libraries contains multiple options"),
868 errcontext("line %d of configuration file \"%s\"",
869 line_num, file_name));
870 *err_msg = "authentication method \"oauth\" requires argument \"validator\" to be set when oauth_validator_libraries contains multiple options";
871 goto done;
872 }
873
874 foreach_ptr(char, allowed, elemlist)
875 {
876 if (strcmp(allowed, hbaline->oauth_validator) == 0)
877 goto done;
878 }
879
880 ereport(elevel,
881 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
882 errmsg("validator \"%s\" is not permitted by %s",
883 hbaline->oauth_validator, "oauth_validator_libraries"),
884 errcontext("line %d of configuration file \"%s\"",
885 line_num, file_name));
886 *err_msg = psprintf("validator \"%s\" is not permitted by %s",
887 hbaline->oauth_validator, "oauth_validator_libraries");
888
889done:
890 list_free_deep(elemlist);
891 pfree(rawstring);
892
893 return (*err_msg == NULL);
894}
char * oauth_validator_libraries_string
Definition: auth-oauth.c:34
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define errcontext
Definition: elog.h:196
#define ereport(elevel,...)
Definition: elog.h:149
void list_free_deep(List *list)
Definition: list.c:1560
char * pstrdup(const char *in)
Definition: mcxt.c:1699
void pfree(void *pointer)
Definition: mcxt.c:1524
#define NIL
Definition: pg_list.h:68
#define foreach_ptr(type, var, lst)
Definition: pg_list.h:469
#define linitial(l)
Definition: pg_list.h:178
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
char * sourcefile
Definition: hba.h:97
int linenumber
Definition: hba.h:98
char * oauth_validator
Definition: hba.h:141
Definition: pg_list.h:54
int length
Definition: pg_list.h:56
bool SplitDirectoriesString(char *rawstring, char separator, List **namelist)
Definition: varlena.c:3652

References ereport, errcode(), errcontext, errmsg(), foreach_ptr, List::length, HbaLine::linenumber, linitial, list_free_deep(), NIL, HbaLine::oauth_validator, oauth_validator_libraries_string, pfree(), psprintf(), pstrdup(), HbaLine::sourcefile, and SplitDirectoriesString().

Referenced by parse_hba_line().

◆ generate_error_response()

static void generate_error_response ( struct oauth_ctx ctx,
char **  output,
int *  outputlen 
)
static

Definition at line 485 of file auth-oauth.c.

486{
488 StringInfoData issuer;
489
490 /*
491 * The admin needs to set an issuer and scope for OAuth to work. There's
492 * not really a way to hide this from the user, either, because we can't
493 * choose a "default" issuer, so be honest in the failure message. (In
494 * practice such configurations are rejected during HBA parsing.)
495 */
496 if (!ctx->issuer || !ctx->scope)
498 errcode(ERRCODE_INTERNAL_ERROR),
499 errmsg("OAuth is not properly configured for this user"),
500 errdetail_log("The issuer and scope parameters must be set in pg_hba.conf."));
501
502 /*
503 * Build a default .well-known URI based on our issuer, unless the HBA has
504 * already provided one.
505 */
506 initStringInfo(&issuer);
507 appendStringInfoString(&issuer, ctx->issuer);
508 if (strstr(ctx->issuer, "/.well-known/") == NULL)
509 appendStringInfoString(&issuer, "/.well-known/openid-configuration");
510
512
513 /*
514 * Escaping the string here is belt-and-suspenders defensive programming
515 * since escapable characters aren't valid in either the issuer URI or the
516 * scope list, but the HBA doesn't enforce that yet.
517 */
518 appendStringInfoString(&buf, "{ \"status\": \"invalid_token\", ");
519
520 appendStringInfoString(&buf, "\"openid-configuration\": ");
521 escape_json(&buf, issuer.data);
522 pfree(issuer.data);
523
524 appendStringInfoString(&buf, ", \"scope\": ");
525 escape_json(&buf, ctx->scope);
526
528
529 *output = buf.data;
530 *outputlen = buf.len;
531}
int errdetail_log(const char *fmt,...)
Definition: elog.c:1251
#define FATAL
Definition: elog.h:41
FILE * output
void escape_json(StringInfo buf, const char *str)
Definition: json.c:1602
static char * buf
Definition: pg_test_fsync.c:72
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:230
void initStringInfo(StringInfo str)
Definition: stringinfo.c:97
const char * scope
Definition: auth-oauth.c:70
const char * issuer
Definition: auth-oauth.c:69

References appendStringInfoString(), buf, ereport, errcode(), errdetail_log(), errmsg(), escape_json(), FATAL, initStringInfo(), oauth_ctx::issuer, output, pfree(), and oauth_ctx::scope.

Referenced by oauth_exchange().

◆ load_validator_library()

static void load_validator_library ( const char *  libname)
static

Definition at line 738 of file auth-oauth.c.

739{
740 OAuthValidatorModuleInit validator_init;
742
743 /*
744 * The presence, and validity, of libname has already been established by
745 * check_oauth_validator so we don't need to perform more than Assert
746 * level checking here.
747 */
748 Assert(libname && *libname);
749
750 validator_init = (OAuthValidatorModuleInit)
751 load_external_function(libname, "_PG_oauth_validator_module_init",
752 false, NULL);
753
754 /*
755 * The validator init function is required since it will set the callbacks
756 * for the validator library.
757 */
758 if (validator_init == NULL)
760 errmsg("%s module \"%s\" must define the symbol %s",
761 "OAuth validator", libname, "_PG_oauth_validator_module_init"));
762
763 ValidatorCallbacks = (*validator_init) ();
765
766 /*
767 * Check the magic number, to protect against break-glass scenarios where
768 * the ABI must change within a major version. load_external_function()
769 * already checks for compatibility across major versions.
770 */
773 errmsg("%s module \"%s\": magic number mismatch",
774 "OAuth validator", libname),
775 errdetail("Server has magic number 0x%08X, module has 0x%08X.",
777
778 /*
779 * Make sure all required callbacks are present in the ValidatorCallbacks
780 * structure. Right now only the validation callback is required.
781 */
782 if (ValidatorCallbacks->validate_cb == NULL)
784 errmsg("%s module \"%s\" must provide a %s callback",
785 "OAuth validator", libname, "validate_cb"));
786
787 /* Allocate memory for validator library private state data */
789 validator_module_state->sversion = PG_VERSION_NUM;
790
791 if (ValidatorCallbacks->startup_cb != NULL)
793
794 /* Shut down the library before cleaning up its state. */
795 mcb = palloc0(sizeof(*mcb));
797
799}
static void shutdown_validator_library(void *arg)
Definition: auth-oauth.c:806
static const OAuthValidatorCallbacks * ValidatorCallbacks
Definition: auth-oauth.c:45
static ValidatorModuleState * validator_module_state
Definition: auth-oauth.c:44
void * load_external_function(const char *filename, const char *funcname, bool signalNotFound, void **filehandle)
Definition: dfmgr.c:95
int errdetail(const char *fmt,...)
Definition: elog.c:1203
#define ERROR
Definition: elog.h:39
Assert(PointerIsAligned(start, uint64))
void MemoryContextRegisterResetCallback(MemoryContext context, MemoryContextCallback *cb)
Definition: mcxt.c:568
void * palloc0(Size size)
Definition: mcxt.c:1347
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
#define PG_OAUTH_VALIDATOR_MAGIC
Definition: oauth.h:74
const OAuthValidatorCallbacks *(* OAuthValidatorModuleInit)(void)
Definition: oauth.h:90
MemoryContextCallbackFunction func
Definition: palloc.h:49
ValidatorValidateCB validate_cb
Definition: oauth.h:82
ValidatorStartupCB startup_cb
Definition: oauth.h:80

References Assert(), CurrentMemoryContext, ereport, errdetail(), errmsg(), ERROR, MemoryContextCallback::func, load_external_function(), OAuthValidatorCallbacks::magic, MemoryContextRegisterResetCallback(), palloc0(), PG_OAUTH_VALIDATOR_MAGIC, shutdown_validator_library(), OAuthValidatorCallbacks::startup_cb, ValidatorModuleState::sversion, OAuthValidatorCallbacks::validate_cb, validator_module_state, and ValidatorCallbacks.

Referenced by oauth_init().

◆ oauth_exchange()

static int oauth_exchange ( void *  opaq,
const char *  input,
int  inputlen,
char **  output,
int *  outputlen,
const char **  logdetail 
)
static

Definition at line 134 of file auth-oauth.c.

136{
137 char *input_copy;
138 char *p;
139 char cbind_flag;
140 char *auth;
141 int status;
142
143 struct oauth_ctx *ctx = opaq;
144
145 *output = NULL;
146 *outputlen = -1;
147
148 /*
149 * If the client didn't include an "Initial Client Response" in the
150 * SASLInitialResponse message, send an empty challenge, to which the
151 * client will respond with the same data that usually comes in the
152 * Initial Client Response.
153 */
154 if (input == NULL)
155 {
157
158 *output = pstrdup("");
159 *outputlen = 0;
161 }
162
163 /*
164 * Check that the input length agrees with the string length of the input.
165 */
166 if (inputlen == 0)
168 errcode(ERRCODE_PROTOCOL_VIOLATION),
169 errmsg("malformed OAUTHBEARER message"),
170 errdetail("The message is empty."));
171 if (inputlen != strlen(input))
173 errcode(ERRCODE_PROTOCOL_VIOLATION),
174 errmsg("malformed OAUTHBEARER message"),
175 errdetail("Message length does not match input length."));
176
177 switch (ctx->state)
178 {
179 case OAUTH_STATE_INIT:
180 /* Handle this case below. */
181 break;
182
184
185 /*
186 * Only one response is valid for the client during authentication
187 * failure: a single kvsep.
188 */
189 if (inputlen != 1 || *input != KVSEP)
191 errcode(ERRCODE_PROTOCOL_VIOLATION),
192 errmsg("malformed OAUTHBEARER message"),
193 errdetail("Client did not send a kvsep response."));
194
195 /* The (failed) handshake is now complete. */
198
199 default:
200 elog(ERROR, "invalid OAUTHBEARER exchange state");
202 }
203
204 /* Handle the client's initial message. */
205 p = input_copy = pstrdup(input);
206
207 /*
208 * OAUTHBEARER does not currently define a channel binding (so there is no
209 * OAUTHBEARER-PLUS, and we do not accept a 'p' specifier). We accept a
210 * 'y' specifier purely for the remote chance that a future specification
211 * could define one; then future clients can still interoperate with this
212 * server implementation. 'n' is the expected case.
213 */
214 cbind_flag = *p;
215 switch (cbind_flag)
216 {
217 case 'p':
219 errcode(ERRCODE_PROTOCOL_VIOLATION),
220 errmsg("malformed OAUTHBEARER message"),
221 errdetail("The server does not support channel binding for OAuth, but the client message includes channel binding data."));
222 break;
223
224 case 'y': /* fall through */
225 case 'n':
226 p++;
227 if (*p != ',')
229 errcode(ERRCODE_PROTOCOL_VIOLATION),
230 errmsg("malformed OAUTHBEARER message"),
231 errdetail("Comma expected, but found character \"%s\".",
232 sanitize_char(*p)));
233 p++;
234 break;
235
236 default:
238 errcode(ERRCODE_PROTOCOL_VIOLATION),
239 errmsg("malformed OAUTHBEARER message"),
240 errdetail("Unexpected channel-binding flag \"%s\".",
241 sanitize_char(cbind_flag)));
242 }
243
244 /*
245 * Forbid optional authzid (authorization identity). We don't support it.
246 */
247 if (*p == 'a')
249 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
250 errmsg("client uses authorization identity, but it is not supported"));
251 if (*p != ',')
253 errcode(ERRCODE_PROTOCOL_VIOLATION),
254 errmsg("malformed OAUTHBEARER message"),
255 errdetail("Unexpected attribute \"%s\" in client-first-message.",
256 sanitize_char(*p)));
257 p++;
258
259 /* All remaining fields are separated by the RFC's kvsep (\x01). */
260 if (*p != KVSEP)
262 errcode(ERRCODE_PROTOCOL_VIOLATION),
263 errmsg("malformed OAUTHBEARER message"),
264 errdetail("Key-value separator expected, but found character \"%s\".",
265 sanitize_char(*p)));
266 p++;
267
268 auth = parse_kvpairs_for_auth(&p);
269 if (!auth)
271 errcode(ERRCODE_PROTOCOL_VIOLATION),
272 errmsg("malformed OAUTHBEARER message"),
273 errdetail("Message does not contain an auth value."));
274
275 /* We should be at the end of our message. */
276 if (*p)
278 errcode(ERRCODE_PROTOCOL_VIOLATION),
279 errmsg("malformed OAUTHBEARER message"),
280 errdetail("Message contains additional data after the final terminator."));
281
282 if (!validate(ctx->port, auth))
283 {
284 generate_error_response(ctx, output, outputlen);
285
288 }
289 else
290 {
293 }
294
295 /* Don't let extra copies of the bearer token hang around. */
296 explicit_bzero(input_copy, inputlen);
297
298 return status;
299}
static void generate_error_response(struct oauth_ctx *ctx, char **output, int *outputlen)
Definition: auth-oauth.c:485
static bool validate(Port *port, const char *auth)
Definition: auth-oauth.c:638
#define KVSEP
Definition: auth-oauth.c:79
static char * sanitize_char(char c)
Definition: auth-oauth.c:310
static char * parse_kvpairs_for_auth(char **input)
Definition: auth-oauth.c:386
#define elog(elevel,...)
Definition: elog.h:225
FILE * input
void explicit_bzero(void *buf, size_t len)
#define PG_SASL_EXCHANGE_FAILURE
Definition: sasl.h:27
#define PG_SASL_EXCHANGE_CONTINUE
Definition: sasl.h:25
#define PG_SASL_EXCHANGE_SUCCESS
Definition: sasl.h:26
Port * port
Definition: auth-oauth.c:68
enum oauth_state state
Definition: auth-oauth.c:67

References Assert(), elog, ereport, errcode(), errdetail(), errmsg(), ERROR, explicit_bzero(), generate_error_response(), input, KVSEP, OAUTH_STATE_ERROR, OAUTH_STATE_FINISHED, OAUTH_STATE_INIT, output, parse_kvpairs_for_auth(), PG_SASL_EXCHANGE_CONTINUE, PG_SASL_EXCHANGE_FAILURE, PG_SASL_EXCHANGE_SUCCESS, oauth_ctx::port, pstrdup(), sanitize_char(), oauth_ctx::state, and validate().

◆ oauth_get_mechanisms()

static void oauth_get_mechanisms ( Port port,
StringInfo  buf 
)
static

Definition at line 89 of file auth-oauth.c.

90{
91 /* Only OAUTHBEARER is supported. */
94}
#define OAUTHBEARER_NAME
Definition: oauth-common.h:17
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:242

References appendStringInfoChar(), appendStringInfoString(), buf, and OAUTHBEARER_NAME.

◆ oauth_init()

static void * oauth_init ( Port port,
const char *  selected_mech,
const char *  shadow_pass 
)
static

Definition at line 102 of file auth-oauth.c.

103{
104 struct oauth_ctx *ctx;
105
106 if (strcmp(selected_mech, OAUTHBEARER_NAME) != 0)
108 errcode(ERRCODE_PROTOCOL_VIOLATION),
109 errmsg("client selected an invalid SASL authentication mechanism"));
110
111 ctx = palloc0(sizeof(*ctx));
112
113 ctx->state = OAUTH_STATE_INIT;
114 ctx->port = port;
115
116 Assert(port->hba);
117 ctx->issuer = port->hba->oauth_issuer;
118 ctx->scope = port->hba->oauth_scope;
119
120 load_validator_library(port->hba->oauth_validator);
121
122 return ctx;
123}
static void load_validator_library(const char *libname)
Definition: auth-oauth.c:738
static int port
Definition: pg_regress.c:115

References Assert(), ereport, errcode(), errmsg(), ERROR, oauth_ctx::issuer, load_validator_library(), OAUTH_STATE_INIT, OAUTHBEARER_NAME, palloc0(), oauth_ctx::port, port, oauth_ctx::scope, and oauth_ctx::state.

◆ parse_kvpairs_for_auth()

static char * parse_kvpairs_for_auth ( char **  input)
static

Definition at line 386 of file auth-oauth.c.

387{
388 char *pos = *input;
389 char *auth = NULL;
390
391 /*----
392 * The relevant ABNF, from Sec. 3.1:
393 *
394 * kvsep = %x01
395 * key = 1*(ALPHA)
396 * value = *(VCHAR / SP / HTAB / CR / LF )
397 * kvpair = key "=" value kvsep
398 * ;;gs2-header = See RFC 5801
399 * client-resp = (gs2-header kvsep *kvpair kvsep) / kvsep
400 *
401 * By the time we reach this code, the gs2-header and initial kvsep have
402 * already been validated. We start at the beginning of the first kvpair.
403 */
404
405 while (*pos)
406 {
407 char *end;
408 char *sep;
409 char *key;
410 char *value;
411
412 /*
413 * Find the end of this kvpair. Note that input is null-terminated by
414 * the SASL code, so the strchr() is bounded.
415 */
416 end = strchr(pos, KVSEP);
417 if (!end)
419 errcode(ERRCODE_PROTOCOL_VIOLATION),
420 errmsg("malformed OAUTHBEARER message"),
421 errdetail("Message contains an unterminated key/value pair."));
422 *end = '\0';
423
424 if (pos == end)
425 {
426 /* Empty kvpair, signifying the end of the list. */
427 *input = pos + 1;
428 return auth;
429 }
430
431 /*
432 * Find the end of the key name.
433 */
434 sep = strchr(pos, '=');
435 if (!sep)
437 errcode(ERRCODE_PROTOCOL_VIOLATION),
438 errmsg("malformed OAUTHBEARER message"),
439 errdetail("Message contains a key without a value."));
440 *sep = '\0';
441
442 /* Both key and value are now safely terminated. */
443 key = pos;
444 value = sep + 1;
446
447 if (strcmp(key, AUTH_KEY) == 0)
448 {
449 if (auth)
451 errcode(ERRCODE_PROTOCOL_VIOLATION),
452 errmsg("malformed OAUTHBEARER message"),
453 errdetail("Message contains multiple auth values."));
454
455 auth = value;
456 }
457 else
458 {
459 /*
460 * The RFC also defines the host and port keys, but they are not
461 * required for OAUTHBEARER and we do not use them. Also, per Sec.
462 * 3.1, any key/value pairs we don't recognize must be ignored.
463 */
464 }
465
466 /* Move to the next pair. */
467 pos = end + 1;
468 }
469
471 errcode(ERRCODE_PROTOCOL_VIOLATION),
472 errmsg("malformed OAUTHBEARER message"),
473 errdetail("Message did not contain a final terminator."));
474
476 return NULL;
477}
static void validate_kvpair(const char *key, const char *val)
Definition: auth-oauth.c:327
#define AUTH_KEY
Definition: auth-oauth.c:80
#define pg_unreachable()
Definition: c.h:332
static struct @162 value

References AUTH_KEY, ereport, errcode(), errdetail(), errmsg(), ERROR, input, sort-test::key, KVSEP, pg_unreachable, validate_kvpair(), and value.

Referenced by oauth_exchange().

◆ sanitize_char()

static char * sanitize_char ( char  c)
static

Definition at line 310 of file auth-oauth.c.

311{
312 static char buf[5];
313
314 if (c >= 0x21 && c <= 0x7E)
315 snprintf(buf, sizeof(buf), "'%c'", c);
316 else
317 snprintf(buf, sizeof(buf), "0x%02x", (unsigned char) c);
318 return buf;
319}
#define snprintf
Definition: port.h:239
char * c

References buf, and snprintf.

Referenced by oauth_exchange().

◆ shutdown_validator_library()

static void shutdown_validator_library ( void *  arg)
static

◆ validate()

static bool validate ( Port port,
const char *  auth 
)
static

Definition at line 638 of file auth-oauth.c.

639{
640 int map_status;
642 const char *token;
643 bool status;
644
645 /* Ensure that we have a correct token to validate */
646 if (!(token = validate_token_format(auth)))
647 return false;
648
649 /*
650 * Ensure that we have a validation library loaded, this should always be
651 * the case and an error here is indicative of a bug.
652 */
655 errcode(ERRCODE_INTERNAL_ERROR),
656 errmsg("validation of OAuth token requested without a validator loaded"));
657
658 /* Call the validation function from the validator module */
659 ret = palloc0(sizeof(ValidatorModuleResult));
661 port->user_name, ret))
662 {
664 errcode(ERRCODE_INTERNAL_ERROR),
665 errmsg("internal error in OAuth validator module"));
666 return false;
667 }
668
669 /*
670 * Log any authentication results even if the token isn't authorized; it
671 * might be useful for auditing or troubleshooting.
672 */
673 if (ret->authn_id)
675
676 if (!ret->authorized)
677 {
678 ereport(LOG,
679 errmsg("OAuth bearer authentication failed for user \"%s\"",
680 port->user_name),
681 errdetail_log("Validator failed to authorize the provided token."));
682
683 status = false;
684 goto cleanup;
685 }
686
687 if (port->hba->oauth_skip_usermap)
688 {
689 /*
690 * If the validator is our authorization authority, we're done.
691 * Authentication may or may not have been performed depending on the
692 * validator implementation; all that matters is that the validator
693 * says the user can log in with the target role.
694 */
695 status = true;
696 goto cleanup;
697 }
698
699 /* Make sure the validator authenticated the user. */
700 if (ret->authn_id == NULL || ret->authn_id[0] == '\0')
701 {
702 ereport(LOG,
703 errmsg("OAuth bearer authentication failed for user \"%s\"",
704 port->user_name),
705 errdetail_log("Validator provided no identity."));
706
707 status = false;
708 goto cleanup;
709 }
710
711 /* Finally, check the user map. */
712 map_status = check_usermap(port->hba->usermap, port->user_name,
714 status = (map_status == STATUS_OK);
715
716cleanup:
717
718 /*
719 * Clear and free the validation result from the validator module once
720 * we're done with it.
721 */
722 if (ret->authn_id != NULL)
723 pfree(ret->authn_id);
724 pfree(ret);
725
726 return status;
727}
static const char * validate_token_format(const char *header)
Definition: auth-oauth.c:556
void set_authn_id(Port *port, const char *id)
Definition: auth.c:333
static void cleanup(void)
Definition: bootstrap.c:713
#define STATUS_OK
Definition: c.h:1140
#define LOG
Definition: elog.h:31
#define WARNING
Definition: elog.h:36
int check_usermap(const char *usermap_name, const char *pg_user, const char *system_user, bool case_insensitive)
Definition: hba.c:2966
#define token
Definition: indent_globs.h:126
ClientConnectionInfo MyClientConnectionInfo
Definition: miscinit.c:1066
const char * authn_id
Definition: libpq-be.h:105

References ClientConnectionInfo::authn_id, ValidatorModuleResult::authn_id, ValidatorModuleResult::authorized, check_usermap(), cleanup(), ereport, errcode(), errdetail_log(), errmsg(), FATAL, LOG, MyClientConnectionInfo, palloc0(), pfree(), port, set_authn_id(), STATUS_OK, token, OAuthValidatorCallbacks::validate_cb, validate_token_format(), validator_module_state, ValidatorCallbacks, and WARNING.

Referenced by attribute_reloptions(), bloptions(), brinoptions(), btoptions(), build_local_reloptions(), build_reloptions(), default_reloptions(), dioptions(), fillRelOptions(), ginoptions(), gistoptions(), hashoptions(), heap_reloptions(), index_opclass_options(), index_reloptions(), oauth_exchange(), parse_one_reloption(), parseLocalRelOptions(), parseRelOptions(), parseRelOptionsInternal(), partitioned_table_reloptions(), spgoptions(), tablespace_reloptions(), and view_reloptions().

◆ validate_kvpair()

static void validate_kvpair ( const char *  key,
const char *  val 
)
static

Definition at line 327 of file auth-oauth.c.

328{
329 /*-----
330 * From Sec 3.1:
331 * key = 1*(ALPHA)
332 */
333 static const char *key_allowed_set =
334 "abcdefghijklmnopqrstuvwxyz"
335 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
336
337 size_t span;
338
339 if (!key[0])
341 errcode(ERRCODE_PROTOCOL_VIOLATION),
342 errmsg("malformed OAUTHBEARER message"),
343 errdetail("Message contains an empty key name."));
344
345 span = strspn(key, key_allowed_set);
346 if (key[span] != '\0')
348 errcode(ERRCODE_PROTOCOL_VIOLATION),
349 errmsg("malformed OAUTHBEARER message"),
350 errdetail("Message contains an invalid key name."));
351
352 /*-----
353 * From Sec 3.1:
354 * value = *(VCHAR / SP / HTAB / CR / LF )
355 *
356 * The VCHAR (visible character) class is large; a loop is more
357 * straightforward than strspn().
358 */
359 for (; *val; ++val)
360 {
361 if (0x21 <= *val && *val <= 0x7E)
362 continue; /* VCHAR */
363
364 switch (*val)
365 {
366 case ' ':
367 case '\t':
368 case '\r':
369 case '\n':
370 continue; /* SP, HTAB, CR, LF */
371
372 default:
374 errcode(ERRCODE_PROTOCOL_VIOLATION),
375 errmsg("malformed OAUTHBEARER message"),
376 errdetail("Message contains an invalid value."));
377 }
378 }
379}
long val
Definition: informix.c:689

References ereport, errcode(), errdetail(), errmsg(), ERROR, sort-test::key, and val.

Referenced by parse_kvpairs_for_auth().

◆ validate_token_format()

static const char * validate_token_format ( const char *  header)
static

Definition at line 556 of file auth-oauth.c.

557{
558 size_t span;
559 const char *token;
560 static const char *const b64token_allowed_set =
561 "abcdefghijklmnopqrstuvwxyz"
562 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
563 "0123456789-._~+/";
564
565 /* Missing auth headers should be handled by the caller. */
566 Assert(header);
567
568 if (header[0] == '\0')
569 {
570 /*
571 * A completely empty auth header represents a query for
572 * authentication parameters. The client expects it to fail; there's
573 * no need to make any extra noise in the logs.
574 *
575 * TODO: should we find a way to return STATUS_EOF at the top level,
576 * to suppress the authentication error entirely?
577 */
578 return NULL;
579 }
580
581 if (pg_strncasecmp(header, BEARER_SCHEME, strlen(BEARER_SCHEME)))
582 {
584 errcode(ERRCODE_PROTOCOL_VIOLATION),
585 errmsg("malformed OAuth bearer token"),
586 errdetail_log("Client response indicated a non-Bearer authentication scheme."));
587 return NULL;
588 }
589
590 /* Pull the bearer token out of the auth value. */
591 token = header + strlen(BEARER_SCHEME);
592
593 /* Swallow any additional spaces. */
594 while (*token == ' ')
595 token++;
596
597 /* Tokens must not be empty. */
598 if (!*token)
599 {
601 errcode(ERRCODE_PROTOCOL_VIOLATION),
602 errmsg("malformed OAuth bearer token"),
603 errdetail_log("Bearer token is empty."));
604 return NULL;
605 }
606
607 /*
608 * Make sure the token contains only allowed characters. Tokens may end
609 * with any number of '=' characters.
610 */
611 span = strspn(token, b64token_allowed_set);
612 while (token[span] == '=')
613 span++;
614
615 if (token[span] != '\0')
616 {
617 /*
618 * This error message could be more helpful by printing the
619 * problematic character(s), but that'd be a bit like printing a piece
620 * of someone's password into the logs.
621 */
623 errcode(ERRCODE_PROTOCOL_VIOLATION),
624 errmsg("malformed OAuth bearer token"),
625 errdetail_log("Bearer token is not in the correct format."));
626 return NULL;
627 }
628
629 return token;
630}
#define BEARER_SCHEME
Definition: auth-oauth.c:81
#define COMMERROR
Definition: elog.h:33
int pg_strncasecmp(const char *s1, const char *s2, size_t n)
Definition: pgstrcasecmp.c:69

References Assert(), BEARER_SCHEME, COMMERROR, ereport, errcode(), errdetail_log(), errmsg(), pg_strncasecmp(), and token.

Referenced by validate().

Variable Documentation

◆ oauth_validator_libraries_string

char* oauth_validator_libraries_string = NULL

Definition at line 34 of file auth-oauth.c.

Referenced by check_oauth_validator().

◆ pg_be_oauth_mech

const pg_be_sasl_mech pg_be_oauth_mech
Initial value:
= {
.get_mechanisms = oauth_get_mechanisms,
.init = oauth_init,
.exchange = oauth_exchange,
.max_message_length = PG_MAX_AUTH_TOKEN_LENGTH,
}
static void * oauth_init(Port *port, const char *selected_mech, const char *shadow_pass)
Definition: auth-oauth.c:102
static void oauth_get_mechanisms(Port *port, StringInfo buf)
Definition: auth-oauth.c:89
static int oauth_exchange(void *opaq, const char *input, int inputlen, char **output, int *outputlen, const char **logdetail)
Definition: auth-oauth.c:134
#define PG_MAX_AUTH_TOKEN_LENGTH
Definition: auth.h:33

Definition at line 48 of file auth-oauth.c.

Referenced by ClientAuthentication().

◆ validator_module_state

ValidatorModuleState* validator_module_state
static

Definition at line 44 of file auth-oauth.c.

Referenced by load_validator_library(), shutdown_validator_library(), and validate().

◆ ValidatorCallbacks

const OAuthValidatorCallbacks* ValidatorCallbacks
static

Definition at line 45 of file auth-oauth.c.

Referenced by load_validator_library(), shutdown_validator_library(), and validate().