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);
39
46
47
48
49
50
51
52static void *
54 const char *sasl_mechanism)
55{
57
58
59
60
61
64
68
71
73}
74
75
76
77
78
79
80
81
82
83static void
93
94#define kvsep "\x01"
95
96
97
98
99
100
101
102
103
104
105static char *
107{
109
114
116 {
117
119 }
120 else
121 {
122
123
124
125
127
128
130 {
133 "internal error: no OAuth token was set for the connection");
135 }
136 }
137
140
144
147
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
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
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{
203
205 {
207
209 "field \"%s\" must be a string",
211 }
212
216
218}
219
228
231{
233
234
235 if (ctx->nested == 1)
236 {
238 {
241 }
243 {
246 }
248 {
251 }
252 }
253
255}
256
259{
261
263 {
265 }
267 {
269
271 "field \"%s\" must be a string",
273 }
274
278
280}
281
290
293{
295
297 {
300 }
301
303 {
304 if (ctx->nested != 1)
305 {
306
307
308
309
312 "internal error: target scalar found at nesting level %d during OAUTHBEARER parsing",
315 }
316
317
318
319
320
322 {
324 "field \"%s\" is duplicated",
327 }
328
329
331 {
333 "field \"%s\" must be a string",
336 }
337
341
344 }
345 else
346 {
347
348 }
349
351}
352
353#define HTTPS_SCHEME "https://"
354#define HTTP_SCHEME "http://"
355
356
357#define WK_PREFIX "/.well-known/"
358#define OPENID_WK_SUFFIX "openid-configuration"
359#define OAUTH_WK_SUFFIX "oauth-authorization-server"
360
361
362
363
364
365static char *
367{
371 char *issuer;
375
376
377
378
379
380
381
384
388 {
389
391 }
392
394 {
396 "OAuth discovery URI \"%s\" must use HTTPS",
399 }
400
401
402
403
404
405
406
407
408
409
411 {
413 "OAuth discovery URI \"%s\" must not contain query or fragment components",
416 }
417
418
419
420
421
422
423
426 {
428 "OAuth discovery URI \"%s\" is not a .well-known URI",
431 }
432
433
434
435
436
438
443 else
445
446
447
448
449
450
452 {
454 "OAuth discovery URI \"%s\" uses an unsupported .well-known suffix",
457 }
458
459
460
461
462
463
464
466 {
467
468
469
470
472
474 Assert(path_start);
475
477 {
479 "OAuth discovery URI \"%s\" uses an invalid format",
482 }
483 }
484
485
487 if (!issuer)
488 {
491 }
492
493
494
495
496
497
501
503
504 return issuer;
505}
506
507
508
509
510
511
512static bool
514{
521
523
524
526 {
528 "server's error message contained an embedded NULL, and was discarded");
529 return false;
530 }
531
532
533
534
535
537 {
539 "server's error response is not valid UTF-8");
540 return false;
541 }
542
545
548
555
557
559 {
564 else
565 {
566
567
568
569
571 errmsg = "";
572 }
573 }
576
579 "failed to parse server's error response: %s",
581
582
585
588
590 {
592
593
594
595
596
597
598
599
600
601
602
605 goto cleanup;
606
608 {
610 "server's discovery document at %s (issuer \"%s\") is incompatible with oauth_issuer (%s)",
613
616 }
617
619
621 {
624 }
625 else
626 {
627
629 {
631 "server's discovery document has moved to %s (previous location was %s)",
635 }
636 }
637 }
638
640 {
641
643 {
646 }
647 }
648
650 {
652 "server sent error response without a status");
654 }
655
656 if (strcmp(ctx.status, "invalid_token") != 0)
657 {
658
659
660
661
663 "server rejected OAuth bearer token: %s",
666 }
667
669
674
676}
677
678
679
680
681
682static void
684{
686 libpq_gettext("user-defined OAuth flow failed"));
687
689 {
692 }
693
695}
696
697
698
699
700
701
702
703
704
707{
711
713 {
715 "user-defined OAuth flow provided neither a token nor an async callback");
717 }
718
722
724 {
727 }
729 {
730
731
732
733
734
736 {
738 "user-defined OAuth flow did not provide a token");
740 }
741
744 {
747 }
748
750 }
751
752
754 {
756 "user-defined OAuth flow did not provide a socket for polling");
758 }
759
761}
762
763
764
765
766
767
768static void
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797#if !defined(USE_LIBCURL)
798
799
800
801
802
803bool
805{
806 return false;
807}
808
809#elif defined(USE_DYNAMIC_OAUTH)
810
811
812
813
814
815typedef char *(*libpq_gettext_func) (const char *msgid);
816
817
818
819
820
821
822
823#define DEFINE_GETTER(TYPE, MEMBER) \
824 typedef TYPE (*conn_ ## MEMBER ## _func) (PGconn *conn); \
825 static TYPE conn_ ## MEMBER(PGconn *conn) { return conn->MEMBER; }
826
827
828#define DEFINE_GETTER_P(TYPE, MEMBER) \
829 typedef TYPE (*conn_ ## MEMBER ## _func) (PGconn *conn); \
830 static TYPE conn_ ## MEMBER(PGconn *conn) { return &conn->MEMBER; }
831
832#define DEFINE_SETTER(TYPE, MEMBER) \
833 typedef void (*set_conn_ ## MEMBER ## _func) (PGconn *conn, TYPE val); \
834 static void set_conn_ ## MEMBER(PGconn *conn, TYPE val) { conn->MEMBER = val; }
835
843
846
847
848
849
850
851
852
853
854
855
856bool
858{
862
876
877
878
879
880
881
882
883
884
885
887#if defined(__darwin__)
889#else
891#endif
892
894 if (->builtin_flow)
895 {
896
897
898
899
900
901
902
905
906 return false;
907 }
908
910 || (flow = dlsym(state->builtin_flow, "pg_fe_run_oauth_flow")) == NULL
912 {
913
914
915
916
919
921 return false;
922 }
923
924
925
926
927
928
929
930
931
932
933
934
936 {
937
939
941 return false;
942 }
943
945 {
949#else
961
963 }
964
966
967
970
971 return true;
972}
973
974#else
975
976
977
978
979
982
983bool
985{
986
989
990 return true;
991}
992
993#endif
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012static bool
1014{
1015 int res;
1017 .v1 = {
1020 },
1022 };
1023
1026
1027
1028
1029
1030
1032 if (res == 0)
1034
1035 if (res > 0)
1036 {
1038
1040 {
1041
1042
1043
1044
1045
1048 {
1051 }
1052
1053
1056 return true;
1057 }
1058
1061 {
1064 }
1065
1067
1071 }
1072 else if (res < 0)
1073 {
1076 }
1078 {
1081 }
1082
1083 return true;
1084
1088 return false;
1089}
1090
1091
1092
1093
1094
1095
1096static bool
1098{
1099
1100
1101
1102
1103
1104
1105
1107 return true;
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1126 {
1128 "server requires OAuth authentication, but oauth_issuer and oauth_client_id are not both set");
1129 return false;
1130 }
1131
1132
1133
1134
1135
1137 {
1138
1139
1140
1141
1145 return false;
1146
1149 {
1151 return false;
1152 }
1153 }
1154 else
1155 {
1156
1157
1158
1159
1162 {
1164 return false;
1165 }
1166 }
1167
1168 return true;
1169}
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1185{
1189
1192
1193 switch (state->step)
1194 {
1196
1198
1201
1203 {
1204
1205
1206
1207
1208 }
1210 {
1211
1212
1213
1214
1215
1218
1220 {
1221
1222
1223
1224
1225
1226 }
1227 else
1228 {
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1241 }
1242 }
1243 else
1244 {
1245
1246
1247
1248
1249
1251 }
1252
1253
1254
1255
1256
1257
1261
1264
1266 {
1267
1268
1269
1270
1271
1272
1273
1275 }
1276
1278
1280 if (final)
1281 {
1282
1283
1284
1285
1286
1288 "server sent unexpected additional OAuth data");
1290 }
1291
1292
1293
1294
1295
1298 {
1301 }
1303
1304
1307
1309 {
1310
1311
1312
1313
1314
1317 }
1318
1320 {
1321
1322
1323
1324
1326 {
1328 "server requires OAuth authentication, but no discovery metadata was provided");
1330 }
1331
1332
1335
1337 {
1338
1339
1340
1341
1343 }
1344 }
1345
1346
1347
1348
1349
1350
1351
1355
1357
1358
1359
1360
1361
1363 {
1364 Assert(false);
1366 "internal error: OAuth flow did not set a token");
1368 }
1369
1371
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1384 "server sent additional OAuth data after error");
1386
1387 default:
1389 break;
1390 }
1391
1392 Assert(false);
1394
1396
1397
1398
1399
1400
1404}
1405
1406static bool
1408{
1409
1410 return false;
1411}
1412
1413
1414
1415
1416
1417void
1419{
1421 return;
1422
1426}
1427
1428
1429
1430
1431bool
1433{
1434 const char *env = getenv("PGOAUTHDEBUG");
1435
1437}
static void cleanup(void)
#define Assert(condition)
#define fprintf(file, fmt, msg)
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 void report_user_flow_error(PGconn *conn, const PGoauthBearerRequestV2 *request)
#define oauth_json_set_error(ctx, fmt,...)
static bool setup_oauth_parameters(PGconn *conn)
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
#define oauth_json_set_error_internal(ctx,...)
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
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
@ PQAUTHDATA_OAUTH_BEARER_TOKEN_V2
PostgresPollingStatusType pg_fe_run_oauth_flow(PGconn *conn)
void pg_fe_cleanup_oauth_flow(PGconn *conn)
#define conn_oauth_issuer_id(CONN)
#define conn_oauth_discovery_uri(CONN)
#define conn_oauth_scope(CONN)
#define conn_oauth_client_id(CONN)
#define set_conn_altsock(CONN, VAL)
#define conn_oauth_client_secret(CONN)
#define set_conn_oauth_token(CONN, VAL)
#define conn_sasl_state(CONN)
#define conn_errorMessage(CONN)
pgthreadlock_t pg_g_threadlock
void libpq_append_conn_error(PGconn *conn, const char *fmt,...)
char *(* libpq_gettext_func)(const char *msgid)
static char buf[DEFAULT_XLOG_SEG_SIZE]
void explicit_bzero(void *buf, size_t len)
int pg_strncasecmp(const char *s1, const char *s2, size_t n)
void initPQExpBuffer(PQExpBuffer str)
void 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)
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
const char * openid_configuration
const char * target_field_name
char * oauth_discovery_uri
void(* cleanup_async_auth)(PGconn *conn)
bool client_finished_auth
PQExpBufferData errorMessage
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)