PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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 "miscadmin.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_ERROR_DISCOVERY , OAUTH_STATE_FINISHED }
 

Functions

static void oauth_get_mechanisms (Port *port, StringInfo buf)
 
static voidoauth_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 bool check_validator_hba_options (Port *port, const char **logdetail)
 
static charsanitize_char (char c)
 
static charparse_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, const char **logdetail)
 
static void validate_kvpair (const char *key, const char *val)
 
static const charvalidate_token_format (const char *header)
 
bool check_oauth_validator (HbaLine *hbaline, int elevel, char **err_msg)
 
void RegisterOAuthHBAOptions (ValidatorModuleState *state, int num, const char *opts[])
 
bool valid_oauth_hba_option_name (const char *name)
 
const charGetOAuthHBAOption (const ValidatorModuleState *state, const char *optname)
 

Variables

charoauth_validator_libraries_string = NULL
 
static ValidatorModuleStatevalidator_module_state
 
static const OAuthValidatorCallbacksValidatorCallbacks
 
static MemoryContext ValidatorMemoryContext
 
static ListValidatorOptions
 
static bool ValidatorOptionsChecked
 
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 87 of file auth-oauth.c.

◆ BEARER_SCHEME

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

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

◆ KVSEP

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

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

Enumeration Type Documentation

◆ oauth_state

Enumerator
OAUTH_STATE_INIT 
OAUTH_STATE_ERROR 
OAUTH_STATE_ERROR_DISCOVERY 
OAUTH_STATE_FINISHED 

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

64{
69};
@ OAUTH_STATE_INIT
Definition auth-oauth.c:65
@ OAUTH_STATE_ERROR_DISCOVERY
Definition auth-oauth.c:67
@ OAUTH_STATE_FINISHED
Definition auth-oauth.c:68
@ OAUTH_STATE_ERROR
Definition auth-oauth.c:66

Function Documentation

◆ check_oauth_validator()

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

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

858{
859 int line_num = hbaline->linenumber;
860 const char *file_name = hbaline->sourcefile;
861 char *rawstring;
862 List *elemlist = NIL;
863
864 *err_msg = NULL;
865
867 {
868 ereport(elevel,
870 errmsg("oauth_validator_libraries must be set for authentication method %s",
871 "oauth"),
872 errcontext("line %d of configuration file \"%s\"",
873 line_num, file_name));
874 *err_msg = psprintf("oauth_validator_libraries must be set for authentication method %s",
875 "oauth");
876 return false;
877 }
878
879 /* SplitDirectoriesString needs a modifiable copy */
881
883 {
884 /* syntax error in list */
885 ereport(elevel,
887 errmsg("invalid list syntax in parameter \"%s\"",
888 "oauth_validator_libraries"));
889 *err_msg = psprintf("invalid list syntax in parameter \"%s\"",
890 "oauth_validator_libraries");
891 goto done;
892 }
893
894 if (!hbaline->oauth_validator)
895 {
896 if (elemlist->length == 1)
897 {
898 hbaline->oauth_validator = pstrdup(linitial(elemlist));
899 goto done;
900 }
901
902 ereport(elevel,
904 errmsg("authentication method \"oauth\" requires argument \"validator\" to be set when oauth_validator_libraries contains multiple options"),
905 errcontext("line %d of configuration file \"%s\"",
906 line_num, file_name));
907 *err_msg = "authentication method \"oauth\" requires argument \"validator\" to be set when oauth_validator_libraries contains multiple options";
908 goto done;
909 }
910
911 foreach_ptr(char, allowed, elemlist)
912 {
913 if (strcmp(allowed, hbaline->oauth_validator) == 0)
914 goto done;
915 }
916
917 ereport(elevel,
919 errmsg("validator \"%s\" is not permitted by %s",
920 hbaline->oauth_validator, "oauth_validator_libraries"),
921 errcontext("line %d of configuration file \"%s\"",
922 line_num, file_name));
923 *err_msg = psprintf("validator \"%s\" is not permitted by %s",
924 hbaline->oauth_validator, "oauth_validator_libraries");
925
926done:
929
930 return (*err_msg == NULL);
931}
char * oauth_validator_libraries_string
Definition auth-oauth.c:35
int errcode(int sqlerrcode)
Definition elog.c:874
#define errcontext
Definition elog.h:200
#define ereport(elevel,...)
Definition elog.h:152
void list_free_deep(List *list)
Definition list.c:1560
char * pstrdup(const char *in)
Definition mcxt.c:1781
void pfree(void *pointer)
Definition mcxt.c:1616
static char * errmsg
#define NIL
Definition pg_list.h:68
#define foreach_ptr(type, var, lst)
Definition pg_list.h:501
#define linitial(l)
Definition pg_list.h:178
static int fb(int x)
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
Definition pg_list.h:54
bool SplitDirectoriesString(char *rawstring, char separator, List **namelist)
Definition varlena.c:2950

References ereport, errcode(), errcontext, errmsg, fb(), foreach_ptr, linitial, list_free_deep(), NIL, oauth_validator_libraries_string, pfree(), psprintf(), pstrdup(), and SplitDirectoriesString().

Referenced by parse_hba_line().

◆ check_validator_hba_options()

static bool check_validator_hba_options ( Port port,
const char **  logdetail 
)
static

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

1031{
1032 HbaLine *hba = port->hba;
1033
1034 foreach_ptr(char, key, hba->oauth_opt_keys)
1035 {
1036 bool found = false;
1037
1038 /* O(n^2) shouldn't be a problem here in practice. */
1039 foreach_ptr(char, optname, ValidatorOptions)
1040 {
1041 if (strcmp(key, optname) == 0)
1042 {
1043 found = true;
1044 break;
1045 }
1046 }
1047
1048 if (!found)
1049 {
1050 /*
1051 * Unknown option name. Mirror the error messages in hba.c here,
1052 * keeping in mind that the original "validator." prefix was
1053 * stripped from the key during parsing.
1054 *
1055 * Since this is affecting live connections, which is unusual for
1056 * HBA, be noisy with a WARNING. (Warnings aren't sent to clients
1057 * prior to successful authentication, so this won't disclose the
1058 * server config.) It'll duplicate some of the information in the
1059 * logdetail, but that should make it hard to miss the connection
1060 * between the two.
1061 */
1062 char *name = psprintf("validator.%s", key);
1063
1064 *logdetail = psprintf(_("unrecognized authentication option name: \"%s\""),
1065 name);
1068 errmsg("unrecognized authentication option name: \"%s\"",
1069 name),
1070 /* translator: the first %s is the name of the module */
1071 errdetail("The installed validator module (\"%s\") did not define an option named \"%s\".",
1072 hba->oauth_validator, key),
1073 errhint("All OAuth connections matching this line will fail. Correct the option and reload the server configuration."),
1074 errcontext("line %d of configuration file \"%s\"",
1075 hba->linenumber, hba->sourcefile));
1076
1077 return false;
1078 }
1079 }
1080
1081 ValidatorOptionsChecked = true; /* unfetter GetOAuthHBAOption() */
1082 return true;
1083}
static List * ValidatorOptions
Definition auth-oauth.c:50
static bool ValidatorOptionsChecked
Definition auth-oauth.c:51
#define _(x)
Definition elog.c:95
int errhint(const char *fmt,...) pg_attribute_printf(1
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define WARNING
Definition elog.h:37
static int port
Definition pg_regress.c:117
Definition hba.h:95
char * sourcefile
Definition hba.h:96
int linenumber
Definition hba.h:97
char * oauth_validator
Definition hba.h:132
List * oauth_opt_keys
Definition hba.h:134
const char * name

References _, ereport, errcode(), errcontext, errdetail(), errhint(), errmsg, fb(), foreach_ptr, HbaLine::linenumber, name, HbaLine::oauth_opt_keys, HbaLine::oauth_validator, port, psprintf(), HbaLine::sourcefile, ValidatorOptions, ValidatorOptionsChecked, and WARNING.

Referenced by oauth_exchange().

◆ generate_error_response()

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

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

531{
533 StringInfoData issuer;
534
535 /*
536 * The admin needs to set an issuer and scope for OAuth to work. There's
537 * not really a way to hide this from the user, either, because we can't
538 * choose a "default" issuer, so be honest in the failure message. (In
539 * practice such configurations are rejected during HBA parsing.)
540 */
541 if (!ctx->issuer || !ctx->scope)
544 errmsg("OAuth is not properly configured for this user"),
545 errdetail_log("The issuer and scope parameters must be set in pg_hba.conf."));
546
547 /*
548 * Build a default .well-known URI based on our issuer, unless the HBA has
549 * already provided one.
550 */
551 initStringInfo(&issuer);
552 appendStringInfoString(&issuer, ctx->issuer);
553 if (strstr(ctx->issuer, "/.well-known/") == NULL)
554 appendStringInfoString(&issuer, "/.well-known/openid-configuration");
555
557
558 /*
559 * Escaping the string here is belt-and-suspenders defensive programming
560 * since escapable characters aren't valid in either the issuer URI or the
561 * scope list, but the HBA doesn't enforce that yet.
562 */
563 appendStringInfoString(&buf, "{ \"status\": \"invalid_token\", ");
564
565 appendStringInfoString(&buf, "\"openid-configuration\": ");
566 escape_json(&buf, issuer.data);
567 pfree(issuer.data);
568
569 appendStringInfoString(&buf, ", \"scope\": ");
570 escape_json(&buf, ctx->scope);
571
573
574 *output = buf.data;
575 *outputlen = buf.len;
576}
#define FATAL
Definition elog.h:42
int int int errdetail_log(const char *fmt,...) pg_attribute_printf(1
FILE * output
void escape_json(StringInfo buf, const char *str)
Definition json.c:1572
static char buf[DEFAULT_XLOG_SEG_SIZE]
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:77
const char * issuer
Definition auth-oauth.c:76

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

Referenced by oauth_exchange().

◆ GetOAuthHBAOption()

const char * GetOAuthHBAOption ( const ValidatorModuleState state,
const char optname 
)

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

1091{
1092 HbaLine *hba = MyProcPort->hba;
1093 ListCell *lc_k;
1094 ListCell *lc_v;
1095 const char *ret = NULL;
1096
1098 {
1099 /*
1100 * Prevent the startup_cb from retrieving HBA options that it has just
1101 * registered. This probably seems strange -- why refuse to hand out
1102 * information we already know? -- but this lets us reserve the
1103 * ability to perform the startup_cb call earlier, before we know
1104 * which HBA line is matched by a connection, without breaking this
1105 * API.
1106 */
1107 return NULL;
1108 }
1109
1110 if (!state || !hba)
1111 {
1112 Assert(false);
1113 return NULL;
1114 }
1115
1117
1119 {
1120 const char *key = lfirst(lc_k);
1121 const char *val = lfirst(lc_v);
1122
1123 if (strcmp(key, optname) == 0)
1124 {
1125 /*
1126 * Don't return yet -- when regular HBA options are specified more
1127 * than once, the last one wins. Do the same for these options.
1128 */
1129 ret = val;
1130 }
1131 }
1132
1133 return ret;
1134}
#define Assert(condition)
Definition c.h:943
struct Port * MyProcPort
Definition globals.c:53
long val
Definition informix.c:689
#define lfirst(lc)
Definition pg_list.h:172
static int list_length(const List *l)
Definition pg_list.h:152
#define forboth(cell1, list1, cell2, list2)
Definition pg_list.h:550
List * oauth_opt_vals
Definition hba.h:135
HbaLine * hba
Definition libpq-be.h:165

References Assert, fb(), forboth, Port::hba, lfirst, list_length(), MyProcPort, HbaLine::oauth_opt_keys, HbaLine::oauth_opt_vals, val, and ValidatorOptionsChecked.

Referenced by validate_token(), and validator_startup().

◆ load_validator_library()

static void load_validator_library ( const char libname)
static

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

773{
776
777 /*
778 * The presence, and validity, of libname has already been established by
779 * check_oauth_validator so we don't need to perform more than Assert
780 * level checking here.
781 */
783
785 load_external_function(libname, "_PG_oauth_validator_module_init",
786 false, NULL);
787
788 /*
789 * The validator init function is required since it will set the callbacks
790 * for the validator library.
791 */
792 if (validator_init == NULL)
794 errmsg("%s module \"%s\" must define the symbol %s",
795 "OAuth validator", libname, "_PG_oauth_validator_module_init"));
796
797 ValidatorCallbacks = (*validator_init) ();
799
800 /*
801 * Check the magic number, to protect against break-glass scenarios where
802 * the ABI must change within a major version. load_external_function()
803 * already checks for compatibility across major versions.
804 */
807 errmsg("%s module \"%s\": magic number mismatch",
808 "OAuth validator", libname),
809 errdetail("Server has magic number 0x%08X, module has 0x%08X.",
811
812 /*
813 * Make sure all required callbacks are present in the ValidatorCallbacks
814 * structure. Right now only the validation callback is required.
815 */
818 errmsg("%s module \"%s\" must provide a %s callback",
819 "OAuth validator", libname, "validate_cb"));
820
821 /* Allocate memory for validator library private state data */
824
827
828 /* Shut down the library before cleaning up its state. */
831
833}
static void shutdown_validator_library(void *arg)
Definition auth-oauth.c:840
static const OAuthValidatorCallbacks * ValidatorCallbacks
Definition auth-oauth.c:47
static ValidatorModuleState * validator_module_state
Definition auth-oauth.c:46
void * load_external_function(const char *filename, const char *funcname, bool signalNotFound, void **filehandle)
Definition dfmgr.c:95
#define ERROR
Definition elog.h:40
#define palloc0_object(type)
Definition fe_memutils.h:75
void MemoryContextRegisterResetCallback(MemoryContext context, MemoryContextCallback *cb)
Definition mcxt.c:582
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
#define PG_OAUTH_VALIDATOR_MAGIC
Definition oauth.h:88
const OAuthValidatorCallbacks *(* OAuthValidatorModuleInit)(void)
Definition oauth.h:115
MemoryContextCallbackFunction func
Definition palloc.h:49
ValidatorValidateCB validate_cb
Definition oauth.h:96
ValidatorStartupCB startup_cb
Definition oauth.h:94

References Assert, CurrentMemoryContext, ereport, errdetail(), errmsg, ERROR, fb(), MemoryContextCallback::func, load_external_function(), OAuthValidatorCallbacks::magic, MemoryContextRegisterResetCallback(), palloc0_object, 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 144 of file auth-oauth.c.

146{
147 char *input_copy;
148 char *p;
149 char cbind_flag;
150 char *auth;
151 int status;
152
153 struct oauth_ctx *ctx = opaq;
154
155 *output = NULL;
156 *outputlen = -1;
157
158 /*
159 * If the client didn't include an "Initial Client Response" in the
160 * SASLInitialResponse message, send an empty challenge, to which the
161 * client will respond with the same data that usually comes in the
162 * Initial Client Response.
163 */
164 if (input == NULL)
165 {
167
168 *output = pstrdup("");
169 *outputlen = 0;
171 }
172
173 /*
174 * Check that the input length agrees with the string length of the input.
175 */
176 if (inputlen == 0)
179 errmsg("malformed OAUTHBEARER message"),
180 errdetail("The message is empty."));
181 if (inputlen != strlen(input))
184 errmsg("malformed OAUTHBEARER message"),
185 errdetail("Message length does not match input length."));
186
187 switch (ctx->state)
188 {
189 case OAUTH_STATE_INIT:
190 /* Handle this case below. */
191 break;
192
195
196 /*
197 * Only one response is valid for the client during authentication
198 * failure: a single kvsep.
199 */
200 if (inputlen != 1 || *input != KVSEP)
203 errmsg("malformed OAUTHBEARER message"),
204 errdetail("Client did not send a kvsep response."));
205
206 /*
207 * The (failed) handshake is now complete. Don't report discovery
208 * requests in the server log unless the log level is high enough.
209 */
211 {
212 ereport(DEBUG1, errmsg("OAuth issuer discovery requested"));
213
216 }
217
218 /* We're not in discovery, so this is just a normal auth failure. */
221
222 default:
223 elog(ERROR, "invalid OAUTHBEARER exchange state");
225 }
226
227 /* Handle the client's initial message. */
228 p = input_copy = pstrdup(input);
229
230 /*
231 * OAUTHBEARER does not currently define a channel binding (so there is no
232 * OAUTHBEARER-PLUS, and we do not accept a 'p' specifier). We accept a
233 * 'y' specifier purely for the remote chance that a future specification
234 * could define one; then future clients can still interoperate with this
235 * server implementation. 'n' is the expected case.
236 */
237 cbind_flag = *p;
238 switch (cbind_flag)
239 {
240 case 'p':
243 errmsg("malformed OAUTHBEARER message"),
244 errdetail("The server does not support channel binding for OAuth, but the client message includes channel binding data."));
245 break;
246
247 case 'y': /* fall through */
248 case 'n':
249 p++;
250 if (*p != ',')
253 errmsg("malformed OAUTHBEARER message"),
254 errdetail("Comma expected, but found character \"%s\".",
255 sanitize_char(*p)));
256 p++;
257 break;
258
259 default:
262 errmsg("malformed OAUTHBEARER message"),
263 errdetail("Unexpected channel-binding flag \"%s\".",
264 sanitize_char(cbind_flag)));
265 }
266
267 /*
268 * Forbid optional authzid (authorization identity). We don't support it.
269 */
270 if (*p == 'a')
273 errmsg("client uses authorization identity, but it is not supported"));
274 if (*p != ',')
277 errmsg("malformed OAUTHBEARER message"),
278 errdetail("Unexpected attribute \"%s\" in client-first-message.",
279 sanitize_char(*p)));
280 p++;
281
282 /* All remaining fields are separated by the RFC's kvsep (\x01). */
283 if (*p != KVSEP)
286 errmsg("malformed OAUTHBEARER message"),
287 errdetail("Key-value separator expected, but found character \"%s\".",
288 sanitize_char(*p)));
289 p++;
290
292 if (!auth)
295 errmsg("malformed OAUTHBEARER message"),
296 errdetail("Message does not contain an auth value."));
297
298 /* We should be at the end of our message. */
299 if (*p)
302 errmsg("malformed OAUTHBEARER message"),
303 errdetail("Message contains additional data after the final terminator."));
304
305 /*
306 * Make sure all custom HBA options are understood by the validator before
307 * continuing, since we couldn't check them during server start/reload.
308 */
309 if (!check_validator_hba_options(ctx->port, logdetail))
310 {
313 }
314
315 if (auth[0] == '\0')
316 {
317 /*
318 * An empty auth value represents a discovery request; the client
319 * expects it to fail. Skip validation entirely and move directly to
320 * the error response.
321 */
323
326 }
327 else if (!validate(ctx->port, auth, logdetail))
328 {
330
333 }
334 else
335 {
338 }
339
340 /* Don't let extra copies of the bearer token hang around. */
342
343 return status;
344}
static bool check_validator_hba_options(Port *port, const char **logdetail)
static void generate_error_response(struct oauth_ctx *ctx, char **output, int *outputlen)
Definition auth-oauth.c:530
#define KVSEP
Definition auth-oauth.c:86
static char * sanitize_char(char c)
Definition auth-oauth.c:355
static bool validate(Port *port, const char *auth, const char **logdetail)
Definition auth-oauth.c:672
static char * parse_kvpairs_for_auth(char **input)
Definition auth-oauth.c:431
#define DEBUG1
Definition elog.h:31
#define elog(elevel,...)
Definition elog.h:228
#define ERRCODE_PROTOCOL_VIOLATION
Definition fe-connect.c:96
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_ABANDONED
Definition sasl.h:28
#define PG_SASL_EXCHANGE_SUCCESS
Definition sasl.h:26
Port * port
Definition auth-oauth.c:75
enum oauth_state state
Definition auth-oauth.c:74

References Assert, check_validator_hba_options(), DEBUG1, elog, ereport, errcode(), ERRCODE_PROTOCOL_VIOLATION, errdetail(), errmsg, ERROR, explicit_bzero(), fb(), generate_error_response(), input, KVSEP, OAUTH_STATE_ERROR, OAUTH_STATE_ERROR_DISCOVERY, OAUTH_STATE_FINISHED, OAUTH_STATE_INIT, output, parse_kvpairs_for_auth(), PG_SASL_EXCHANGE_ABANDONED, 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 96 of file auth-oauth.c.

97{
98 /* Only OAUTHBEARER is supported. */
101}
#define OAUTHBEARER_NAME
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 109 of file auth-oauth.c.

110{
111 struct oauth_ctx *ctx;
112
116 errmsg("client selected an invalid SASL authentication mechanism"));
117
118 /* Save our memory context for later use by client API calls. */
120
121 ctx = palloc0_object(struct oauth_ctx);
122
123 ctx->state = OAUTH_STATE_INIT;
124 ctx->port = port;
125
126 Assert(port->hba);
127 ctx->issuer = port->hba->oauth_issuer;
128 ctx->scope = port->hba->oauth_scope;
129
130 load_validator_library(port->hba->oauth_validator);
131
132 return ctx;
133}
static MemoryContext ValidatorMemoryContext
Definition auth-oauth.c:49
static void load_validator_library(const char *libname)
Definition auth-oauth.c:772

References Assert, CurrentMemoryContext, ereport, errcode(), ERRCODE_PROTOCOL_VIOLATION, errmsg, ERROR, fb(), oauth_ctx::issuer, load_validator_library(), OAUTH_STATE_INIT, OAUTHBEARER_NAME, palloc0_object, oauth_ctx::port, port, oauth_ctx::scope, oauth_ctx::state, and ValidatorMemoryContext.

◆ parse_kvpairs_for_auth()

static char * parse_kvpairs_for_auth ( char **  input)
static

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

432{
433 char *pos = *input;
434 char *auth = NULL;
435
436 /*----
437 * The relevant ABNF, from Sec. 3.1:
438 *
439 * kvsep = %x01
440 * key = 1*(ALPHA)
441 * value = *(VCHAR / SP / HTAB / CR / LF )
442 * kvpair = key "=" value kvsep
443 * ;;gs2-header = See RFC 5801
444 * client-resp = (gs2-header kvsep *kvpair kvsep) / kvsep
445 *
446 * By the time we reach this code, the gs2-header and initial kvsep have
447 * already been validated. We start at the beginning of the first kvpair.
448 */
449
450 while (*pos)
451 {
452 char *end;
453 char *sep;
454 char *key;
455 char *value;
456
457 /*
458 * Find the end of this kvpair. Note that input is null-terminated by
459 * the SASL code, so the strchr() is bounded.
460 */
461 end = strchr(pos, KVSEP);
462 if (!end)
465 errmsg("malformed OAUTHBEARER message"),
466 errdetail("Message contains an unterminated key/value pair."));
467 *end = '\0';
468
469 if (pos == end)
470 {
471 /* Empty kvpair, signifying the end of the list. */
472 *input = pos + 1;
473 return auth;
474 }
475
476 /*
477 * Find the end of the key name.
478 */
479 sep = strchr(pos, '=');
480 if (!sep)
483 errmsg("malformed OAUTHBEARER message"),
484 errdetail("Message contains a key without a value."));
485 *sep = '\0';
486
487 /* Both key and value are now safely terminated. */
488 key = pos;
489 value = sep + 1;
491
492 if (strcmp(key, AUTH_KEY) == 0)
493 {
494 if (auth)
497 errmsg("malformed OAUTHBEARER message"),
498 errdetail("Message contains multiple auth values."));
499
500 auth = value;
501 }
502 else
503 {
504 /*
505 * The RFC also defines the host and port keys, but they are not
506 * required for OAUTHBEARER and we do not use them. Also, per Sec.
507 * 3.1, any key/value pairs we don't recognize must be ignored.
508 */
509 }
510
511 /* Move to the next pair. */
512 pos = end + 1;
513 }
514
517 errmsg("malformed OAUTHBEARER message"),
518 errdetail("Message did not contain a final terminator."));
519
521 return NULL;
522}
static void validate_kvpair(const char *key, const char *val)
Definition auth-oauth.c:372
#define AUTH_KEY
Definition auth-oauth.c:87
#define pg_unreachable()
Definition c.h:367
static struct @177 value

References AUTH_KEY, ereport, errcode(), ERRCODE_PROTOCOL_VIOLATION, errdetail(), errmsg, ERROR, fb(), input, KVSEP, pg_unreachable, validate_kvpair(), and value.

Referenced by oauth_exchange().

◆ RegisterOAuthHBAOptions()

void RegisterOAuthHBAOptions ( ValidatorModuleState state,
int  num,
const char opts[] 
)

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

951{
952 MemoryContext oldcontext;
953
954 if (!state)
955 {
956 Assert(false);
957 return;
958 }
959
961
962 for (int i = 0; i < num; i++)
963 {
965 {
966 /*
967 * The user can't set this option in the HBA, so GetOAuthHBAOption
968 * would always return NULL.
969 */
971 errmsg("HBA option name \"%s\" is invalid and will be ignored",
972 opts[i]),
973 /* translator: the second %s is a function name */
974 errcontext("validator module \"%s\", in call to %s",
976 "RegisterOAuthHBAOptions"));
977 continue;
978 }
979
981 }
982
983 MemoryContextSwitchTo(oldcontext);
984
985 /*
986 * Wait to validate the HBA against the registered options until later
987 * (see check_validator_hba_options()).
988 *
989 * Delaying allows the validator to make multiple registration calls, to
990 * append to the list; it lets us make the check in a place where we can
991 * report the error without leaking details to the client; and it avoids
992 * exporting the order of operations between HBA matching and the
993 * startup_cb call as an API guarantee. (The last issue may become
994 * relevant with a threaded model.)
995 */
996}
bool valid_oauth_hba_option_name(const char *name)
int i
Definition isn.c:77
List * lappend(List *list, void *datum)
Definition list.c:339
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
static AmcheckOptions opts
Definition pg_amcheck.c:112

References Assert, ereport, errcontext, errmsg, Port::hba, i, lappend(), MemoryContextSwitchTo(), MyProcPort, HbaLine::oauth_validator, opts, pstrdup(), valid_oauth_hba_option_name(), ValidatorMemoryContext, ValidatorOptions, and WARNING.

Referenced by validator_startup().

◆ sanitize_char()

static char * sanitize_char ( char  c)
static

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

356{
357 static char buf[5];
358
359 if (c >= 0x21 && c <= 0x7E)
360 snprintf(buf, sizeof(buf), "'%c'", c);
361 else
362 snprintf(buf, sizeof(buf), "0x%02x", (unsigned char) c);
363 return buf;
364}
#define snprintf
Definition port.h:260
char * c

References buf, and snprintf.

Referenced by oauth_exchange().

◆ shutdown_validator_library()

static void shutdown_validator_library ( void arg)
static

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

841{
844
845 /* The backing memory for this is about to disappear. */
847}
ValidatorShutdownCB shutdown_cb
Definition oauth.h:95

References fb(), NIL, OAuthValidatorCallbacks::shutdown_cb, validator_module_state, ValidatorCallbacks, and ValidatorOptions.

Referenced by load_validator_library().

◆ valid_oauth_hba_option_name()

bool valid_oauth_hba_option_name ( const char name)

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

1004{
1005 /*
1006 * This list is not incredibly principled, since the goal is just to bound
1007 * compatibility guarantees for our HBA parser. Alphanumerics seem
1008 * obviously fine, and it's difficult to argue against the punctuation
1009 * that's already included in some HBA option names and identifiers.
1010 */
1011 static const char *name_allowed_set =
1012 "abcdefghijklmnopqrstuvwxyz"
1013 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1014 "0123456789_-";
1015
1016 size_t span;
1017
1018 if (!name[0])
1019 return false;
1020
1022 return name[span] == '\0';
1023}

References fb(), and name.

Referenced by parse_hba_auth_opt(), and RegisterOAuthHBAOptions().

◆ validate()

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

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

673{
674 int map_status;
676 const char *token;
677 bool status;
678
679 /* Ensure that we have a correct token to validate */
681 return false;
682
683 /*
684 * Ensure that we have a validation library loaded, this should always be
685 * the case and an error here is indicative of a bug.
686 */
690 errmsg("validation of OAuth token requested without a validator loaded"));
691
692 /* Call the validation function from the validator module */
695 port->user_name, ret))
696 {
699 errmsg("internal error in OAuth validator module"),
700 ret->error_detail ? errdetail_log("%s", ret->error_detail) : 0);
701
702 *logdetail = ret->error_detail;
703 return false;
704 }
705
706 /*
707 * Log any authentication results even if the token isn't authorized; it
708 * might be useful for auditing or troubleshooting.
709 */
710 if (ret->authn_id)
712
713 if (!ret->authorized)
714 {
715 if (ret->error_detail)
716 *logdetail = ret->error_detail;
717 else
718 *logdetail = _("Validator failed to authorize the provided token.");
719
720 status = false;
721 goto cleanup;
722 }
723
724 if (port->hba->oauth_skip_usermap)
725 {
726 /*
727 * If the validator is our authorization authority, we're done.
728 * Authentication may or may not have been performed depending on the
729 * validator implementation; all that matters is that the validator
730 * says the user can log in with the target role.
731 */
732 status = true;
733 goto cleanup;
734 }
735
736 /* Make sure the validator authenticated the user. */
737 if (ret->authn_id == NULL || ret->authn_id[0] == '\0')
738 {
739 *logdetail = _("Validator provided no identity.");
740
741 status = false;
742 goto cleanup;
743 }
744
745 /* Finally, check the user map. */
746 map_status = check_usermap(port->hba->usermap, port->user_name,
748 status = (map_status == STATUS_OK);
749
750cleanup:
751
752 /*
753 * Clear and free the validation result from the validator module once
754 * we're done with it.
755 */
756 if (ret->authn_id != NULL)
757 pfree(ret->authn_id);
758 pfree(ret);
759
760 return status;
761}
static const char * validate_token_format(const char *header)
Definition auth-oauth.c:601
void set_authn_id(Port *port, const char *id)
Definition auth.c:336
static void cleanup(void)
Definition bootstrap.c:886
#define STATUS_OK
Definition c.h:1258
int check_usermap(const char *usermap_name, const char *pg_user, const char *system_user, bool case_insensitive)
Definition hba.c:2791
#define token
ClientConnectionInfo MyClientConnectionInfo
Definition miscinit.c:1020
const char * authn_id
Definition libpq-be.h:99
char * error_detail
Definition oauth.h:65

References _, ClientConnectionInfo::authn_id, ValidatorModuleResult::authn_id, ValidatorModuleResult::authorized, check_usermap(), cleanup(), ereport, errcode(), errdetail_log(), errmsg, ValidatorModuleResult::error_detail, FATAL, fb(), MyClientConnectionInfo, palloc0_object, 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 372 of file auth-oauth.c.

373{
374 /*-----
375 * From Sec 3.1:
376 * key = 1*(ALPHA)
377 */
378 static const char *key_allowed_set =
379 "abcdefghijklmnopqrstuvwxyz"
380 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
381
382 size_t span;
383
384 if (!key[0])
387 errmsg("malformed OAUTHBEARER message"),
388 errdetail("Message contains an empty key name."));
389
391 if (key[span] != '\0')
394 errmsg("malformed OAUTHBEARER message"),
395 errdetail("Message contains an invalid key name."));
396
397 /*-----
398 * From Sec 3.1:
399 * value = *(VCHAR / SP / HTAB / CR / LF )
400 *
401 * The VCHAR (visible character) class is large; a loop is more
402 * straightforward than strspn().
403 */
404 for (; *val; ++val)
405 {
406 if (0x21 <= *val && *val <= 0x7E)
407 continue; /* VCHAR */
408
409 switch (*val)
410 {
411 case ' ':
412 case '\t':
413 case '\r':
414 case '\n':
415 continue; /* SP, HTAB, CR, LF */
416
417 default:
420 errmsg("malformed OAUTHBEARER message"),
421 errdetail("Message contains an invalid value."));
422 }
423 }
424}

References ereport, errcode(), ERRCODE_PROTOCOL_VIOLATION, errdetail(), errmsg, ERROR, fb(), and val.

Referenced by parse_kvpairs_for_auth().

◆ validate_token_format()

static const char * validate_token_format ( const char header)
static

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

602{
603 size_t span;
604 const char *token;
605 static const char *const b64token_allowed_set =
606 "abcdefghijklmnopqrstuvwxyz"
607 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
608 "0123456789-._~+/";
609
610 /* Missing auth headers should be handled by the caller. */
611 Assert(header);
612 /* Empty auth (discovery) should be handled before calling validate(). */
613 Assert(header[0] != '\0');
614
616 {
619 errmsg("malformed OAuth bearer token"),
620 errdetail_log("Client response indicated a non-Bearer authentication scheme."));
621 return NULL;
622 }
623
624 /* Pull the bearer token out of the auth value. */
625 token = header + strlen(BEARER_SCHEME);
626
627 /* Swallow any additional spaces. */
628 while (*token == ' ')
629 token++;
630
631 /* Tokens must not be empty. */
632 if (!*token)
633 {
636 errmsg("malformed OAuth bearer token"),
637 errdetail_log("Bearer token is empty."));
638 return NULL;
639 }
640
641 /*
642 * Make sure the token contains only allowed characters. Tokens may end
643 * with any number of '=' characters.
644 */
646 while (token[span] == '=')
647 span++;
648
649 if (token[span] != '\0')
650 {
651 /*
652 * This error message could be more helpful by printing the
653 * problematic character(s), but that'd be a bit like printing a piece
654 * of someone's password into the logs.
655 */
658 errmsg("malformed OAuth bearer token"),
659 errdetail_log("Bearer token is not in the correct format."));
660 return NULL;
661 }
662
663 return token;
664}
#define BEARER_SCHEME
Definition auth-oauth.c:88
#define COMMERROR
Definition elog.h:34
int pg_strncasecmp(const char *s1, const char *s2, size_t n)

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

Referenced by validate().

Variable Documentation

◆ oauth_validator_libraries_string

char* oauth_validator_libraries_string = NULL

Definition at line 35 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:109
static void oauth_get_mechanisms(Port *port, StringInfo buf)
Definition auth-oauth.c:96
static int oauth_exchange(void *opaq, const char *input, int inputlen, char **output, int *outputlen, const char **logdetail)
Definition auth-oauth.c:144
#define PG_MAX_AUTH_TOKEN_LENGTH
Definition auth.h:33

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

54 {
55 .get_mechanisms = oauth_get_mechanisms,
56 .init = oauth_init,
57 .exchange = oauth_exchange,
58
59 .max_message_length = PG_MAX_AUTH_TOKEN_LENGTH,
60};

Referenced by ClientAuthentication().

◆ validator_module_state

ValidatorModuleState* validator_module_state
static

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

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

◆ ValidatorCallbacks

const OAuthValidatorCallbacks* ValidatorCallbacks
static

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

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

◆ ValidatorMemoryContext

MemoryContext ValidatorMemoryContext
static

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

Referenced by oauth_init(), and RegisterOAuthHBAOptions().

◆ ValidatorOptions

List* ValidatorOptions
static

◆ ValidatorOptionsChecked

bool ValidatorOptionsChecked
static

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

Referenced by check_validator_hba_options(), and GetOAuthHBAOption().