PostgreSQL Source Code: src/interfaces/libpq/fe-auth-oauth.c Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
17
18#ifdef USE_DYNAMIC_OAUTH
20#endif
21
29#include "pg_config_paths.h"
30
31
33 const char *sasl_mechanism);
35 char *input, int inputlen,
36 char **output, int *outputlen);
39
45};
46
47
48
49
50
51
52static void *
54 const char *sasl_mechanism)
55{
57
58
59
60
61
62 Assert(sasl_mechanism != NULL);
64
67 return NULL;
68
71
73}
74
75
76
77
78
79
80
81
82
83static void
85{
87
88
90
92}
93
94#define kvsep "\x01"
95
96
97
98
99
100
101
102
103
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;
114
115 if (discover)
116 {
117
118 authn_scheme = token = "";
119 }
120 else
121 {
122
123
124
125
126 authn_scheme = "Bearer ";
127
128
130 {
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)
147
148 return response;
149}
150
151
152
153
154
155
156#define ERROR_STATUS_FIELD "status"
157#define ERROR_SCOPE_FIELD "scope"
158#define ERROR_OPENID_CONFIGURATION_FIELD "openid-configuration"
159
160
161
162
163
164
165
166#define MAX_SASL_NESTING_LEVEL 8
167
169{
173
176
177
181};
182
183#define oauth_json_has_error(ctx) \
184 (PQExpBufferDataBroken((ctx)->errbuf) || (ctx)->errmsg)
185
186#define oauth_json_set_error(ctx, ...) \
187 do { \
188 appendPQExpBuffer(&(ctx)->errbuf, __VA_ARGS__); \
189 (ctx)->errmsg = (ctx)->errbuf.data; \
190 } while (0)
191
194{
196
198 {
200
204 }
205
209
211}
212
215{
217
220}
221
224{
226
227
228 if (ctx->nested == 1)
229 {
231 {
234 }
236 {
239 }
241 {
244 }
245 }
246
248}
249
252{
254
256 {
258 }
260 {
262
266 }
267
271
273}
274
277{
279
282}
283
286{
288
290 {
293 }
294
296 {
297 if (ctx->nested != 1)
298 {
299
300
301
302
305 "internal error: target scalar found at nesting level %d during OAUTHBEARER parsing",
308 }
309
310
311
312
313
315 {
320 }
321
322
324 {
329 }
330
334
337 }
338 else
339 {
340
341 }
342
344}
345
346#define HTTPS_SCHEME "https://"
347#define HTTP_SCHEME "http://"
348
349
350#define WK_PREFIX "/.well-known/"
351#define OPENID_WK_SUFFIX "openid-configuration"
352#define OAUTH_WK_SUFFIX "oauth-authorization-server"
353
354
355
356
357
358static char *
360{
361 const char *authority_start = NULL;
362 const char *wk_start;
363 const char *wk_end;
364 char *issuer;
365 ptrdiff_t start_offset,
366 end_offset;
367 size_t end_len;
368
369
370
371
372
373
374
376 authority_start = wkuri + strlen(HTTPS_SCHEME);
377
378 if (!authority_start
381 {
382
383 authority_start = wkuri + strlen(HTTP_SCHEME);
384 }
385
386 if (!authority_start)
387 {
389 "OAuth discovery URI \"%s\" must use HTTPS",
390 wkuri);
391 return NULL;
392 }
393
394
395
396
397
398
399
400
401
402
403 if (strpbrk(authority_start, "?#") != NULL)
404 {
406 "OAuth discovery URI \"%s\" must not contain query or fragment components",
407 wkuri);
408 return NULL;
409 }
410
411
412
413
414
415
416
417 wk_start = strstr(authority_start, WK_PREFIX);
418 if (!wk_start)
419 {
421 "OAuth discovery URI \"%s\" is not a .well-known URI",
422 wkuri);
423 return NULL;
424 }
425
426
427
428
429
430 wk_end = wk_start + strlen(WK_PREFIX);
431
436 else
437 wk_end = NULL;
438
439
440
441
442
443
444 if (!wk_end || (*wk_end != '/' && *wk_end != '\0'))
445 {
447 "OAuth discovery URI \"%s\" uses an unsupported .well-known suffix",
448 wkuri);
449 return NULL;
450 }
451
452
453
454
455
456
457
458 if (*wk_end != '\0')
459 {
460
461
462
463
464 const char *path_start;
465
466 path_start = strchr(authority_start, '/');
467 Assert(path_start);
468
469 if (wk_start != path_start)
470 {
472 "OAuth discovery URI \"%s\" uses an invalid format",
473 wkuri);
474 return NULL;
475 }
476 }
477
478
479 issuer = strdup(wkuri);
480 if (!issuer)
481 {
483 return NULL;
484 }
485
486
487
488
489
490
491 start_offset = wk_start - wkuri;
492 end_offset = wk_end - wkuri;
493 end_len = strlen(wk_end) + 1;
494
495 memmove(issuer + start_offset, issuer + end_offset, end_len);
496
497 return issuer;
498}
499
500
501
502
503
504
505static bool
507{
514
516
517
518 if (strlen(msg) != msglen)
519 {
521 "server's error message contained an embedded NULL, and was discarded");
522 return false;
523 }
524
525
526
527
528
530 {
532 "server's error response is not valid UTF-8");
533 return false;
534 }
535
538
541
548
550
552 {
557 else
558 {
559
560
561
562
564 errmsg = "";
565 }
566 }
569
572 "failed to parse server's error response: %s",
574
575
578
581
583 {
584 char *discovery_issuer;
585
586
587
588
589
590
591
592
593
594
595
597 if (!discovery_issuer)
598 goto cleanup;
599
601 {
603 "server's discovery document at %s (issuer \"%s\") is incompatible with oauth_issuer (%s)",
606
607 free(discovery_issuer);
609 }
610
611 free(discovery_issuer);
612
614 {
617 }
618 else
619 {
620
622 {
624 "server's discovery document has moved to %s (previous location was %s)",
628 }
629 }
630 }
631
633 {
634
636 {
638 ctx.scope = NULL;
639 }
640 }
641
643 {
645 "server sent error response without a status");
647 }
648
649 if (strcmp(ctx.status, "invalid_token") != 0)
650 {
651
652
653
654
656 "server rejected OAuth bearer token: %s",
659 }
660
662
667
669}
670
671
672
673
674
675
676
677
678
681{
685
686 if (!request->async)
687 {
689 "user-defined OAuth flow provided neither a token nor an async callback");
691 }
692
695 {
698 }
700 {
701
702
703
704
705
706 if (!request->token)
707 {
709 "user-defined OAuth flow did not provide a token");
711 }
712
715 {
718 }
719
721 }
722
723
725 {
727 "user-defined OAuth flow did not provide a socket for polling");
729 }
730
732}
733
734
735
736
737
738static void
740{
743
745
749
750 free(request);
751 state->async_ctx = NULL;
752}
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767#if !defined(USE_LIBCURL)
768
769
770
771
772
773bool
775{
776 return false;
777}
778
779#elif defined(USE_DYNAMIC_OAUTH)
780
781
782
783
784
785typedef char *(*libpq_gettext_func) (const char *msgid);
786
787
788
789
790
791
792
793#define DEFINE_GETTER(TYPE, MEMBER) \
794 typedef TYPE (*conn_ ## MEMBER ## _func) (PGconn *conn); \
795 static TYPE conn_ ## MEMBER(PGconn *conn) { return conn->MEMBER; }
796
797
798#define DEFINE_GETTER_P(TYPE, MEMBER) \
799 typedef TYPE (*conn_ ## MEMBER ## _func) (PGconn *conn); \
800 static TYPE conn_ ## MEMBER(PGconn *conn) { return &conn->MEMBER; }
801
802#define DEFINE_SETTER(TYPE, MEMBER) \
803 typedef void (*set_conn_ ## MEMBER ## _func) (PGconn *conn, TYPE val); \
804 static void set_conn_ ## MEMBER(PGconn *conn, TYPE val) { conn->MEMBER = val; }
805
806DEFINE_GETTER_P(PQExpBuffer, errorMessage);
807DEFINE_GETTER(char *, oauth_client_id);
808DEFINE_GETTER(char *, oauth_client_secret);
809DEFINE_GETTER(char *, oauth_discovery_uri);
810DEFINE_GETTER(char *, oauth_issuer_id);
811DEFINE_GETTER(char *, oauth_scope);
813
814DEFINE_SETTER(pgsocket, altsock);
815DEFINE_SETTER(char *, oauth_token);
816
817
818
819
820
821
822
823
824
825
826bool
828{
831 int lockerr;
832
835 conn_errorMessage_func errmsg_impl,
836 conn_oauth_client_id_func clientid_impl,
837 conn_oauth_client_secret_func clientsecret_impl,
838 conn_oauth_discovery_uri_func discoveryuri_impl,
839 conn_oauth_issuer_id_func issuerid_impl,
840 conn_oauth_scope_func scope_impl,
841 conn_sasl_state_func saslstate_impl,
842 set_conn_altsock_func setaltsock_impl,
843 set_conn_oauth_token_func settoken_impl);
846
847
848
849
850
851
852
853
854
855
856 const char *const module_name =
857#if defined(__darwin__)
858 LIBDIR "/libpq-oauth-" PG_MAJORVERSION DLSUFFIX;
859#else
860 "libpq-oauth-" PG_MAJORVERSION DLSUFFIX;
861#endif
862
864 if (->builtin_flow)
865 {
866
867
868
869
870
871
872
874 fprintf(stderr, "failed dlopen for libpq-oauth: %s\n", dlerror());
875
876 return false;
877 }
878
879 if ((init = dlsym(state->builtin_flow, "libpq_oauth_init")) == NULL
880 || (flow = dlsym(state->builtin_flow, "pg_fe_run_oauth_flow")) == NULL
881 || (cleanup = dlsym(state->builtin_flow, "pg_fe_cleanup_oauth_flow")) == NULL)
882 {
883
884
885
886
888 fprintf(stderr, "failed dlsym for libpq-oauth: %s\n", dlerror());
889
891 return false;
892 }
893
894
895
896
897
898
899
900
901
902
903
904
906 {
907
909
911 return false;
912 }
913
915 {
917#ifdef ENABLE_NLS
919#else
920 NULL,
921#endif
931
933 }
934
936
937
940
941 return true;
942}
943
944#else
945
946
947
948
949
952
953bool
955{
956
959
960 return true;
961}
962
963#endif
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982static bool
984{
985 int res;
989 };
990
992
993
995 if (res > 0)
996 {
998
999 if (request.token)
1000 {
1001
1002
1003
1004
1005
1008 {
1010 goto fail;
1011 }
1012
1013
1016 return true;
1017 }
1018
1019 request_copy = malloc(sizeof(*request_copy));
1020 if (!request_copy)
1021 {
1023 goto fail;
1024 }
1025
1026 *request_copy = request;
1027
1030 state->async_ctx = request_copy;
1031 }
1032 else if (res < 0)
1033 {
1035 goto fail;
1036 }
1038 {
1040 goto fail;
1041 }
1042
1043 return true;
1044
1045fail:
1048 return false;
1049}
1050
1051
1052
1053
1054
1055
1056static bool
1058{
1059
1060
1061
1062
1063
1064
1065
1067 return true;
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1086 {
1088 "server requires OAuth authentication, but oauth_issuer and oauth_client_id are not both set");
1089 return false;
1090 }
1091
1092
1093
1094
1095
1097 {
1098
1099
1100
1101
1105 return false;
1106
1109 {
1111 return false;
1112 }
1113 }
1114 else
1115 {
1116
1117
1118
1119
1122 {
1124 return false;
1125 }
1126 }
1127
1128 return true;
1129}
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1143 char *input, int inputlen,
1144 char **output, int *outputlen)
1145{
1148 bool discover = false;
1149
1151 *outputlen = 0;
1152
1153 switch (state->step)
1154 {
1156
1157 Assert(inputlen == -1);
1158
1161
1163 {
1164
1165
1166
1167
1168 }
1170 {
1171
1172
1173
1174
1175
1178
1180 {
1181
1182
1183
1184
1185
1186 }
1187 else
1188 {
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200 discover = true;
1201 }
1202 }
1203 else
1204 {
1205
1206
1207
1208
1209
1210 discover = true;
1211 }
1212
1213
1214
1215
1216
1217
1221
1222 *outputlen = strlen(*output);
1224
1226 {
1227
1228
1229
1230
1231
1232
1233
1235 }
1236
1238
1240 if (final)
1241 {
1242
1243
1244
1245
1246
1248 "server sent unexpected additional OAuth data");
1250 }
1251
1252
1253
1254
1255
1258 {
1261 }
1262 *outputlen = strlen(*output);
1263
1264
1267
1269 {
1270
1271
1272
1273
1274
1277 }
1278
1280 {
1281
1282
1283
1284
1286 {
1288 "server requires OAuth authentication, but no discovery metadata was provided");
1290 }
1291
1292
1295
1297 {
1298
1299
1300
1301
1302 goto reconnect;
1303 }
1304 }
1305
1306
1307
1308
1309
1310
1311
1315
1317
1318
1319
1320
1321
1323 {
1324 Assert(false);
1326 "internal error: OAuth flow did not set a token");
1328 }
1329
1330 goto reconnect;
1331
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1344 "server sent additional OAuth data after error");
1346
1347 default:
1349 break;
1350 }
1351
1352 Assert(false);
1354
1355reconnect:
1356
1357
1358
1359
1360
1364}
1365
1366static bool
1368{
1369
1370 return false;
1371}
1372
1373
1374
1375
1376
1377void
1379{
1381 return;
1382
1386}
1387
1388
1389
1390
1391bool
1393{
1394 const char *env = getenv("PGOAUTHDEBUG");
1395
1396 return (env && strcmp(env, "UNSAFE") == 0);
1397}
static void cleanup(void)
#define fprintf(file, fmt, msg)
int errmsg(const char *fmt,...)
void err(int eval, const char *fmt,...)
#define ERROR_SCOPE_FIELD
static bool setup_token_request(PGconn *conn, fe_oauth_state *state)
static JsonParseErrorType oauth_json_array_end(void *state)
static char * issuer_from_well_known_uri(PGconn *conn, const char *wkuri)
static bool handle_oauth_sasl_error(PGconn *conn, const char *msg, int msglen)
static void cleanup_user_oauth_flow(PGconn *conn)
static bool setup_oauth_parameters(PGconn *conn)
#define oauth_json_set_error(ctx,...)
static JsonParseErrorType oauth_json_object_field_start(void *state, char *name, bool isnull)
static JsonParseErrorType oauth_json_scalar(void *state, char *token, JsonTokenType type)
const pg_fe_sasl_mech pg_oauth_mech
static SASLStatus oauth_exchange(void *opaq, bool final, char *input, int inputlen, char **output, int *outputlen)
static bool oauth_channel_bound(void *opaq)
#define oauth_json_has_error(ctx)
static JsonParseErrorType oauth_json_array_start(void *state)
static JsonParseErrorType oauth_json_object_end(void *state)
static void oauth_free(void *opaq)
#define ERROR_OPENID_CONFIGURATION_FIELD
void pqClearOAuthToken(PGconn *conn)
static void * oauth_init(PGconn *conn, const char *password, const char *sasl_mechanism)
static char * client_initial_response(PGconn *conn, bool discover)
bool use_builtin_flow(PGconn *conn, fe_oauth_state *state)
#define ERROR_STATUS_FIELD
#define MAX_SASL_NESTING_LEVEL
static JsonParseErrorType oauth_json_object_start(void *state)
static PostgresPollingStatusType run_user_oauth_flow(PGconn *conn)
bool oauth_unsafe_debugging_enabled(void)
@ FE_OAUTH_REQUESTING_TOKEN
PQauthDataHook_type PQauthDataHook
Assert(PointerIsAligned(start, uint64))
JsonParseErrorType pg_parse_json(JsonLexContext *lex, const JsonSemAction *sem)
JsonLexContext * makeJsonLexContextCstringLen(JsonLexContext *lex, const char *json, size_t len, int encoding, bool need_escapes)
void setJsonLexContextOwnsTokens(JsonLexContext *lex, bool owned_by_context)
char * json_errdetail(JsonParseErrorType error, JsonLexContext *lex)
void freeJsonLexContext(JsonLexContext *lex)
void(* pgthreadlock_t)(int acquire)
PostgresPollingStatusType
@ PQAUTHDATA_OAUTH_BEARER_TOKEN
PostgresPollingStatusType pg_fe_run_oauth_flow(PGconn *conn)
void pg_fe_cleanup_oauth_flow(PGconn *conn)
set_conn_oauth_token_func set_conn_oauth_token
conn_oauth_client_secret_func conn_oauth_client_secret
conn_sasl_state_func conn_sasl_state
conn_oauth_client_id_func conn_oauth_client_id
conn_oauth_scope_func conn_oauth_scope
pgthreadlock_t pg_g_threadlock
conn_oauth_issuer_id_func conn_oauth_issuer_id
set_conn_altsock_func set_conn_altsock
conn_oauth_discovery_uri_func conn_oauth_discovery_uri
void libpq_append_conn_error(PGconn *conn, const char *fmt,...)
conn_errorMessage_func conn_errorMessage
char *(* libpq_gettext_func)(const char *msgid)
void explicit_bzero(void *buf, size_t len)
int pg_strncasecmp(const char *s1, const char *s2, size_t n)
void initPQExpBuffer(PQExpBuffer str)
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void termPQExpBuffer(PQExpBuffer str)
#define PQExpBufferDataBroken(buf)
int pthread_mutex_unlock(pthread_mutex_t *mp)
int pthread_mutex_lock(pthread_mutex_t *mp)
#define PTHREAD_MUTEX_INITIALIZER
json_struct_action array_end
json_struct_action object_start
json_ofield_action object_field_start
json_scalar_action scalar
json_struct_action array_start
json_struct_action object_end
void(* cleanup)(PGconn *conn, struct PGoauthBearerRequest *request)
const char * openid_configuration
PostgresPollingStatusType(* async)(PGconn *conn, struct PGoauthBearerRequest *request, SOCKTYPE *altsock)
const char * target_field_name
char * oauth_discovery_uri
void(* cleanup_async_auth)(PGconn *conn)
bool client_finished_auth
PostgresPollingStatusType(* async_auth)(PGconn *conn)
int pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)
void * dlopen(const char *file, int mode)
void * dlsym(void *handle, const char *symbol)
int dlclose(void *handle)