PostgreSQL Source Code: src/interfaces/libpq-oauth/oauth-curl.c Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
17
18#include <curl/curl.h>
19#include <math.h>
21
22#if defined(HAVE_SYS_EPOLL_H)
23#include <sys/epoll.h>
24#include <sys/timerfd.h>
25#elif defined(HAVE_SYS_EVENT_H)
26#include <sys/event.h>
27#else
28#error libpq-oauth is not supported on this platform
29#endif
30
35
36#ifdef USE_DYNAMIC_OAUTH
37
38
39
40
41
42
44
45#else
46
47
48
49
50
52
53#define conn_errorMessage(CONN) (&CONN->errorMessage)
54#define conn_oauth_client_id(CONN) (CONN->oauth_client_id)
55#define conn_oauth_client_secret(CONN) (CONN->oauth_client_secret)
56#define conn_oauth_discovery_uri(CONN) (CONN->oauth_discovery_uri)
57#define conn_oauth_issuer_id(CONN) (CONN->oauth_issuer_id)
58#define conn_oauth_scope(CONN) (CONN->oauth_scope)
59#define conn_sasl_state(CONN) (CONN->sasl_state)
60
61#define set_conn_altsock(CONN, VAL) do { CONN->altsock = VAL; } while (0)
62#define set_conn_oauth_token(CONN, VAL) do { CONN->oauth_token = VAL; } while (0)
63
64#endif
65
66
67#if defined(USE_DYNAMIC_OAUTH) && defined(LIBPQ_INT_H)
68#error do not rely on libpq-int.h in dynamic builds of libpq-oauth
69#endif
70
71
72
73
74
75
76
77
78
79
80
81
82
83#define MAX_OAUTH_RESPONSE_SIZE (256 * 1024)
84
85
86
87
88
89
90
91
92
93
94
95
96
97#define MAX_OAUTH_NESTING_LEVEL 16
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
115{
120};
121
122static void
124{
129}
130
131
132
133
134
135
137{
144
145
148};
149
150static void
152{
159}
160
161
162
163
164
165
166
167
168
170{
173};
174
175static void
177{
179 free(err->error_description);
180}
181
182
183
184
185
186
187
188
189
190
192{
193
196
197
199};
200
201static void
203{
207}
208
209
210
211
212
213
215{
221};
222
223
224
225
226
227
229{
231
234
235
237
239
240
241 struct curl_slist *headers;
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
269
270
271
272
273
276
278 bool user_prompted;
281};
282
283
284
285
286static void
288{
289
290
291
292
293
294
295
296
297
298
299
301 {
302 CURLMcode err = curl_multi_remove_handle(actx->curlm, actx->curl);
303
306 "libcurl easy handle removal failed: %s",
307 curl_multi_strerror(err));
308 }
309
310 if (actx->curl)
311 {
312
313
314
315
316
317 curl_easy_cleanup(actx->curl);
318 }
319
321 {
322 CURLMcode err = curl_multi_cleanup(actx->curlm);
323
326 "libcurl multi handle cleanup failed: %s",
327 curl_multi_strerror(err));
328 }
329
332
333 curl_slist_free_all(actx->headers);
336
341
343}
344
345
346
347
348
349
350
351
352
353void
355{
357
358 if (state->async_ctx)
359 {
361 state->async_ctx = NULL;
362 }
363
365}
366
367
368
369
370
371
372
373#define actx_error(ACTX, FMT, ...) \
374 appendPQExpBuffer(&(ACTX)->errbuf, libpq_gettext(FMT), ##__VA_ARGS__)
375
376#define actx_error_str(ACTX, S) \
377 appendPQExpBufferStr(&(ACTX)->errbuf, S)
378
379
380
381
382
383
384#define CHECK_MSETOPT(ACTX, OPT, VAL, FAILACTION) \
385 do { \
386 struct async_ctx *_actx = (ACTX); \
387 CURLMcode _setopterr = curl_multi_setopt(_actx->curlm, OPT, VAL); \
388 if (_setopterr) { \
389 actx_error(_actx, "failed to set %s on OAuth connection: %s",\
390 #OPT, curl_multi_strerror(_setopterr)); \
391 FAILACTION; \
392 } \
393 } while (0)
394
395#define CHECK_SETOPT(ACTX, OPT, VAL, FAILACTION) \
396 do { \
397 struct async_ctx *_actx = (ACTX); \
398 CURLcode _setopterr = curl_easy_setopt(_actx->curl, OPT, VAL); \
399 if (_setopterr) { \
400 actx_error(_actx, "failed to set %s on OAuth connection: %s",\
401 #OPT, curl_easy_strerror(_setopterr)); \
402 FAILACTION; \
403 } \
404 } while (0)
405
406#define CHECK_GETINFO(ACTX, INFO, OUT, FAILACTION) \
407 do { \
408 struct async_ctx *_actx = (ACTX); \
409 CURLcode _getinfoerr = curl_easy_getinfo(_actx->curl, INFO, OUT); \
410 if (_getinfoerr) { \
411 actx_error(_actx, "failed to get %s from OAuth response: %s",\
412 #INFO, curl_easy_strerror(_getinfoerr)); \
413 FAILACTION; \
414 } \
415 } while (0)
416
417
418
419
420
421
422
423
424
425
426
427
428
429
431{
433
435
436
437 union
438 {
440 struct curl_slist **array;
442
444};
445
446
447#define PG_OAUTH_REQUIRED true
448#define PG_OAUTH_OPTIONAL false
449
450
452{
455
458};
459
460#define oauth_parse_set_error(ctx, fmt, ...) \
461 appendPQExpBuffer((ctx)->errbuf, libpq_gettext(fmt), ##__VA_ARGS__)
462
463static void
465{
466 char *msgfmt;
467
469
470
471
472
473
475 {
477 msgfmt = "field \"%s\" must be a string";
478 break;
479
481 msgfmt = "field \"%s\" must be a number";
482 break;
483
485 msgfmt = "field \"%s\" must be an array of strings";
486 break;
487
488 default:
490 msgfmt = "field \"%s\" has unexpected type";
491 }
492
494}
495
498{
500
502 {
503
504
505
506
509 }
510
513 {
516 }
517
519}
520
523{
525
526
527 if (ctx->nested == 1)
528 {
530
531
532
533
534
536 {
539 "internal error: started field '%s' before field '%s' was finished",
542 }
543
544 while (field->name)
545 {
546 if (strcmp(name, field->name) == 0)
547 {
549 break;
550 }
551
552 ++field;
553 }
554
555
556
557
558
560 {
562
565 {
567 field->name);
569 }
570 }
571 }
572
574}
575
578{
580
582
583
584
585
586
588 {
591 "internal error: field '%s' still active at end of object",
594 }
595
597}
598
601{
603
605 {
608 }
609
611 {
613
615 {
618 }
619 }
620
623 {
626 }
627
629}
630
633{
635
637 {
638
639
640
641
642
644 {
647 "internal error: found unexpected array end while parsing field '%s'",
650 }
651
653 }
654
657}
658
661{
663
665 {
668 }
669
671 {
674
675
677 {
678
680 {
683 }
684
685
687 }
688
689 if (type != expected)
690 {
693 }
694
696 {
697
698 if (ctx->nested != 1)
699 {
702 "internal error: scalar target found at nesting level %d",
705 }
706
707
709 {
712 "internal error: scalar field '%s' would be assigned twice",
715 }
716
720
722
724 }
725 else
726 {
727 struct curl_slist *temp;
728
729
730 if (ctx->nested != 2)
731 {
734 "internal error: array member found at nesting level %d",
737 }
738
739
741 if (!temp)
743
745 }
746 }
747 else
748 {
749
750 }
751
753}
754
755
756
757
758
759static bool
761{
762 const size_t type_len = strlen(type);
763 char *content_type;
764
765 CHECK_GETINFO(actx, CURLINFO_CONTENT_TYPE, &content_type, return false);
766
767 if (!content_type)
768 {
769 actx_error(actx, "no content type was provided");
770 return false;
771 }
772
773
774
775
776
778 goto fail;
779
780
781 Assert(strlen(content_type) >= type_len);
782 if (content_type[type_len] == '\0')
783 return true;
784
785
786
787
788
789
790 for (size_t i = type_len; content_type[i]; ++i)
791 {
792 switch (content_type[i])
793 {
794 case ';':
795 return true;
796
797 case ' ':
798 case '\t':
799
800 break;
801
802 default:
803 goto fail;
804 }
805 }
806
807fail:
808 actx_error(actx, "unexpected content type: \"%s\"", content_type);
809 return false;
810}
811
812
813
814
815
816
817
818static bool
820{
827
829 return false;
830
831 if (strlen(resp->data) != resp->len)
832 {
833 actx_error(actx, "response contains embedded NULLs");
834 return false;
835 }
836
837
838
839
840
842 {
843 actx_error(actx, "response is not valid UTF-8");
844 return false;
845 }
846
849
853
860
862
864 {
865
866
867
868
869
872
874 }
875
876
878 {
882 {
885 }
886
888 }
889
891
895}
896
897
898
899
900
901
902
903
904
905static bool
907{
911
912
913
914
915
916
917
918
919
920
921
924
925 {0},
926 };
927
929}
930
931
932
933
934
935
936static double
938{
939 double parsed;
940 int cnt;
941
942
943
944
945
946 cnt = sscanf(s, "%lf", &parsed);
947
948 if (cnt != 1)
949 {
950
951
952
953
955 return 0;
956 }
957
958 return parsed;
959}
960
961
962
963
964
965
966
967
968
969
970
971static int
973{
974 double parsed;
975
977 parsed = ceil(parsed);
978
979 if (parsed < 1)
981
982 else if (parsed >= INT_MAX)
983 return INT_MAX;
984
985 return parsed;
986}
987
988
989
990
991
992
993
994
995
996
997static int
999{
1000 double parsed;
1001
1003 parsed = floor(parsed);
1004
1005 if (parsed >= INT_MAX)
1006 return INT_MAX;
1007 else if (parsed <= INT_MIN)
1008 return INT_MIN;
1009
1010 return parsed;
1011}
1012
1013
1014
1015
1016static bool
1018{
1024
1025
1026
1027
1028
1030
1031
1032
1033
1034
1035
1038
1039 {0},
1040 };
1041
1043 return false;
1044
1045
1046
1047
1048
1051 else
1052 {
1053
1054
1055
1056
1058 }
1059
1062
1063 return true;
1064}
1065
1066
1067
1068
1069
1070static bool
1072{
1073 bool result;
1076
1078
1079 {0},
1080 };
1081
1083
1084
1085
1086
1087
1088 if (!result)
1089 actx->errctx = "failed to parse token error response";
1090
1091 return result;
1092}
1093
1094
1095
1096
1097
1098static void
1100{
1101 if (err->error_description)
1103 else
1104 {
1105
1106
1107
1108
1109
1110 long response_code;
1111
1112 CHECK_GETINFO(actx, CURLINFO_RESPONSE_CODE, &response_code, response_code = 0);
1113
1114 if (response_code == 401)
1115 {
1117 ? "provider rejected the oauth_client_secret"
1118 : "provider requires client authentication, and no oauth_client_secret is set");
1120 }
1121 }
1122
1124}
1125
1126
1127
1128
1129
1130static bool
1132{
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153 {0},
1154 };
1155
1157}
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172static bool
1174{
1175#if defined(HAVE_SYS_EPOLL_H)
1176 struct epoll_event ev = {.events = EPOLLIN};
1177
1178 actx->mux = epoll_create1(EPOLL_CLOEXEC);
1179 if (actx->mux < 0)
1180 {
1181 actx_error(actx, "failed to create epoll set: %m");
1182 return false;
1183 }
1184
1185 actx->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
1187 {
1188 actx_error(actx, "failed to create timerfd: %m");
1189 return false;
1190 }
1191
1192 if (epoll_ctl(actx->mux, EPOLL_CTL_ADD, actx->timerfd, &ev) < 0)
1193 {
1194 actx_error(actx, "failed to add timerfd to epoll set: %m");
1195 return false;
1196 }
1197
1198 return true;
1199#elif defined(HAVE_SYS_EVENT_H)
1200 actx->mux = kqueue();
1201 if (actx->mux < 0)
1202 {
1203
1204 actx_error(actx, "failed to create kqueue: %m");
1205 return false;
1206 }
1207
1208
1209
1210
1211
1212
1213
1214 actx->timerfd = kqueue();
1216 {
1217 actx_error(actx, "failed to create timer kqueue: %m");
1218 return false;
1219 }
1220
1221 return true;
1222#else
1223#error setup_multiplexer is not implemented on this platform
1224#endif
1225}
1226
1227
1228
1229
1230
1231static int
1233 void *socketp)
1234{
1236
1237#if defined(HAVE_SYS_EPOLL_H)
1238 struct epoll_event ev = {0};
1239 int res;
1240 int op = EPOLL_CTL_ADD;
1241
1242 switch (what)
1243 {
1244 case CURL_POLL_IN:
1245 ev.events = EPOLLIN;
1246 break;
1247
1248 case CURL_POLL_OUT:
1249 ev.events = EPOLLOUT;
1250 break;
1251
1252 case CURL_POLL_INOUT:
1253 ev.events = EPOLLIN | EPOLLOUT;
1254 break;
1255
1256 case CURL_POLL_REMOVE:
1257 op = EPOLL_CTL_DEL;
1258 break;
1259
1260 default:
1261 actx_error(actx, "unknown libcurl socket operation: %d", what);
1262 return -1;
1263 }
1264
1265 res = epoll_ctl(actx->mux, op, socket, &ev);
1266 if (res < 0 && errno == EEXIST)
1267 {
1268
1269 op = EPOLL_CTL_MOD;
1270 res = epoll_ctl(actx->mux, op, socket, &ev);
1271 }
1272
1273 if (res < 0)
1274 {
1275 switch (op)
1276 {
1277 case EPOLL_CTL_ADD:
1278 actx_error(actx, "could not add to epoll set: %m");
1279 break;
1280
1281 case EPOLL_CTL_DEL:
1282 actx_error(actx, "could not delete from epoll set: %m");
1283 break;
1284
1285 default:
1286 actx_error(actx, "could not update epoll set: %m");
1287 }
1288
1289 return -1;
1290 }
1291
1292 return 0;
1293#elif defined(HAVE_SYS_EVENT_H)
1294 struct kevent ev[2] = {0};
1295 struct kevent ev_out[2];
1296 struct timespec timeout = {0};
1297 int nev = 0;
1298 int res;
1299
1300 switch (what)
1301 {
1302 case CURL_POLL_IN:
1303 EV_SET(&ev[nev], socket, EVFILT_READ, EV_ADD | EV_RECEIPT, 0, 0, 0);
1304 nev++;
1305 break;
1306
1307 case CURL_POLL_OUT:
1308 EV_SET(&ev[nev], socket, EVFILT_WRITE, EV_ADD | EV_RECEIPT, 0, 0, 0);
1309 nev++;
1310 break;
1311
1312 case CURL_POLL_INOUT:
1313 EV_SET(&ev[nev], socket, EVFILT_READ, EV_ADD | EV_RECEIPT, 0, 0, 0);
1314 nev++;
1315 EV_SET(&ev[nev], socket, EVFILT_WRITE, EV_ADD | EV_RECEIPT, 0, 0, 0);
1316 nev++;
1317 break;
1318
1319 case CURL_POLL_REMOVE:
1320
1321
1322
1323
1324
1325
1326 EV_SET(&ev[nev], socket, EVFILT_READ, EV_DELETE | EV_RECEIPT, 0, 0, 0);
1327 nev++;
1328 EV_SET(&ev[nev], socket, EVFILT_WRITE, EV_DELETE | EV_RECEIPT, 0, 0, 0);
1329 nev++;
1330 break;
1331
1332 default:
1333 actx_error(actx, "unknown libcurl socket operation: %d", what);
1334 return -1;
1335 }
1336
1337 res = kevent(actx->mux, ev, nev, ev_out, lengthof(ev_out), &timeout);
1338 if (res < 0)
1339 {
1340 actx_error(actx, "could not modify kqueue: %m");
1341 return -1;
1342 }
1343
1344
1345
1346
1347
1348
1349 for (int i = 0; i < res; ++i)
1350 {
1351
1352
1353
1354
1355
1356 Assert(ev_out[i].flags & EV_ERROR);
1357
1358 errno = ev_out[i].data;
1359 if (errno && errno != ENOENT)
1360 {
1361 switch (what)
1362 {
1363 case CURL_POLL_REMOVE:
1364 actx_error(actx, "could not delete from kqueue: %m");
1365 break;
1366 default:
1367 actx_error(actx, "could not add to kqueue: %m");
1368 }
1369 return -1;
1370 }
1371 }
1372
1373 return 0;
1374#else
1375#error register_socket is not implemented on this platform
1376#endif
1377}
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392static bool
1394{
1395#if defined(HAVE_SYS_EPOLL_H)
1396 struct itimerspec spec = {0};
1397
1398 if (timeout < 0)
1399 {
1400
1401 }
1402 else if (timeout == 0)
1403 {
1404
1405
1406
1407
1408
1409 spec.it_value.tv_nsec = 1;
1410 }
1411 else
1412 {
1413 spec.it_value.tv_sec = timeout / 1000;
1414 spec.it_value.tv_nsec = (timeout % 1000) * 1000000;
1415 }
1416
1417 if (timerfd_settime(actx->timerfd, 0 , &spec, NULL) < 0)
1418 {
1419 actx_error(actx, "setting timerfd to %ld: %m", timeout);
1420 return false;
1421 }
1422
1423 return true;
1424#elif defined(HAVE_SYS_EVENT_H)
1425 struct kevent ev;
1426
1427#ifdef __NetBSD__
1428
1429
1430
1431
1432
1433 if (timeout == 0)
1434 timeout = 1;
1435#endif
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446 EV_SET(&ev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, 0);
1447 if (kevent(actx->timerfd, &ev, 1, NULL, 0, NULL) < 0 && errno != ENOENT)
1448 {
1449 actx_error(actx, "deleting kqueue timer: %m");
1450 return false;
1451 }
1452
1453 EV_SET(&ev, actx->timerfd, EVFILT_READ, EV_DELETE, 0, 0, 0);
1454 if (kevent(actx->mux, &ev, 1, NULL, 0, NULL) < 0 && errno != ENOENT)
1455 {
1456 actx_error(actx, "removing kqueue timer from multiplexer: %m");
1457 return false;
1458 }
1459
1460
1461 if (timeout < 0)
1462 return true;
1463
1464 EV_SET(&ev, 1, EVFILT_TIMER, (EV_ADD | EV_ONESHOT), 0, timeout, 0);
1465 if (kevent(actx->timerfd, &ev, 1, NULL, 0, NULL) < 0)
1466 {
1467 actx_error(actx, "setting kqueue timer to %ld: %m", timeout);
1468 return false;
1469 }
1470
1471 EV_SET(&ev, actx->timerfd, EVFILT_READ, EV_ADD, 0, 0, 0);
1472 if (kevent(actx->mux, &ev, 1, NULL, 0, NULL) < 0)
1473 {
1474 actx_error(actx, "adding kqueue timer to multiplexer: %m");
1475 return false;
1476 }
1477
1478 return true;
1479#else
1480#error set_timer is not implemented on this platform
1481#endif
1482}
1483
1484
1485
1486
1487
1488
1489static int
1491{
1492#if defined(HAVE_SYS_EPOLL_H)
1493 struct itimerspec spec = {0};
1494
1495 if (timerfd_gettime(actx->timerfd, &spec) < 0)
1496 {
1497 actx_error(actx, "getting timerfd value: %m");
1498 return -1;
1499 }
1500
1501
1502
1503
1504
1505
1506 Assert(spec.it_interval.tv_sec == 0
1507 && spec.it_interval.tv_nsec == 0);
1508
1509
1510 return (spec.it_value.tv_sec == 0
1511 && spec.it_value.tv_nsec == 0);
1512#elif defined(HAVE_SYS_EVENT_H)
1513 int res;
1514
1515
1517 if (res < 0)
1518 {
1519 actx_error(actx, "checking kqueue for timeout: %m");
1520 return -1;
1521 }
1522
1523 return (res > 0);
1524#else
1525#error timer_expired is not implemented on this platform
1526#endif
1527}
1528
1529
1530
1531
1532
1533static int
1535{
1537
1538
1539
1540
1541
1542
1543
1544
1546 return -1;
1547
1548 return 0;
1549}
1550
1551
1552
1553
1554
1555
1556
1557static int
1559 void *clientp)
1560{
1561 const char *prefix;
1562 bool printed_prefix = false;
1564
1565
1566 switch (type)
1567 {
1568 case CURLINFO_TEXT:
1569 prefix = "*";
1570 break;
1571
1572 case CURLINFO_HEADER_IN:
1573 case CURLINFO_DATA_IN:
1574 prefix = "<";
1575 break;
1576
1577 case CURLINFO_HEADER_OUT:
1578 case CURLINFO_DATA_OUT:
1579 prefix = ">";
1580 break;
1581
1582 default:
1583 return 0;
1584 }
1585
1587
1588
1589
1590
1591
1592
1593 for (int i = 0; i < size; i++)
1594 {
1596
1597 if (!printed_prefix)
1598 {
1600 printed_prefix = true;
1601 }
1602
1603 if (c >= 0x20 && c <= 0x7E)
1605 else if ((type == CURLINFO_HEADER_IN
1606 || type == CURLINFO_HEADER_OUT
1607 || type == CURLINFO_TEXT)
1608 && (c == '\r' || c == '\n'))
1609 {
1610
1611
1612
1613
1614 }
1615 else
1617
1618 if (c == '\n')
1619 {
1621 printed_prefix = false;
1622 }
1623 }
1624
1625 if (printed_prefix)
1627
1630 return 0;
1631}
1632
1633
1634
1635
1636
1637
1638
1639
1640static bool
1642{
1643
1644
1645
1646
1647 actx->curlm = curl_multi_init();
1648 if (!actx->curlm)
1649 {
1650
1651 actx_error(actx, "failed to create libcurl multi handle");
1652 return false;
1653 }
1654
1655
1656
1657
1658
1660 CHECK_MSETOPT(actx, CURLMOPT_SOCKETDATA, actx, return false);
1662 CHECK_MSETOPT(actx, CURLMOPT_TIMERDATA, actx, return false);
1663
1664
1665
1666
1667
1668 actx->curl = curl_easy_init();
1669 if (!actx->curl)
1670 {
1671 actx_error(actx, "failed to create libcurl handle");
1672 return false;
1673 }
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690 CHECK_SETOPT(actx, CURLOPT_NOSIGNAL, 1L, return false);
1691
1693 {
1694
1695
1696
1697
1698
1700 CHECK_SETOPT(actx, CURLOPT_VERBOSE, 1L, return false);
1701 }
1702
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713 {
1714#if CURL_AT_LEAST_VERSION(7, 85, 0)
1715 const CURLoption popt = CURLOPT_PROTOCOLS_STR;
1716 const char *protos = "https";
1717 const char *const unsafe = "https,http";
1718#else
1719 const CURLoption popt = CURLOPT_PROTOCOLS;
1720 long protos = CURLPROTO_HTTPS;
1721 const long unsafe = CURLPROTO_HTTPS | CURLPROTO_HTTP;
1722#endif
1723
1725 protos = unsafe;
1726
1727 CHECK_SETOPT(actx, popt, protos, return false);
1728 }
1729
1730
1731
1732
1733
1734
1735
1736
1737
1739 {
1740 const char *env;
1741
1742 if ((env = getenv("PGOAUTHCAFILE")) != NULL)
1743 CHECK_SETOPT(actx, CURLOPT_CAINFO, env, return false);
1744 }
1745
1746
1747
1748
1749
1750
1751
1752 actx->headers = curl_slist_append(actx->headers, "Accept:");
1753 if (actx->headers == NULL)
1754 {
1756 return false;
1757 }
1759
1760 return true;
1761}
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773static size_t
1775{
1776 struct async_ctx *actx = userdata;
1778 size_t len = size * nmemb;
1779
1780
1782 {
1783 actx_error(actx, "response is too large");
1784 return 0;
1785 }
1786
1787
1789
1790
1791
1792
1793
1795 {
1797 return 0;
1798 }
1799
1800 return len;
1801}
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813static bool
1815{
1816 CURLMcode err;
1817
1820 CHECK_SETOPT(actx, CURLOPT_WRITEDATA, actx, return false);
1821
1822 err = curl_multi_add_handle(actx->curlm, actx->curl);
1823 if (err)
1824 {
1825 actx_error(actx, "failed to queue HTTP request: %s",
1826 curl_multi_strerror(err));
1827 return false;
1828 }
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839 err = curl_multi_socket_action(actx->curlm, CURL_SOCKET_TIMEOUT, 0, &actx->running);
1840 if (err)
1841 {
1842 actx_error(actx, "asynchronous HTTP request failed: %s",
1843 curl_multi_strerror(err));
1844 return false;
1845 }
1846
1847 return true;
1848}
1849
1850
1851
1852
1853
1854#ifndef CURL_IGNORE_DEPRECATION
1855#define CURL_IGNORE_DEPRECATION(x) x
1856#endif
1857
1858
1859
1860
1861
1864{
1865 CURLMcode err;
1866 CURLMsg *msg;
1867 int msgs_left;
1868 bool done;
1869
1871 {
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1890 err = curl_multi_socket_all(actx->curlm, &actx->running);
1891 )
1892
1893 if (err)
1894 {
1895 actx_error(actx, "asynchronous HTTP request failed: %s",
1896 curl_multi_strerror(err));
1898 }
1899
1901 {
1902
1904 }
1905 }
1906
1907 done = false;
1908 while ((msg = curl_multi_info_read(actx->curlm, &msgs_left)) != NULL)
1909 {
1910 if (msg->msg != CURLMSG_DONE)
1911 {
1912
1913
1914
1915
1916 continue;
1917 }
1918
1919
1920 if (msg->data.result != CURLE_OK)
1921 {
1922
1923
1924
1925
1927 actx_error_str(actx, curl_easy_strerror(msg->data.result));
1928
1930 }
1931
1932
1933 err = curl_multi_remove_handle(actx->curlm, msg->easy_handle);
1934 if (err)
1935 {
1936 actx_error(actx, "libcurl easy handle removal failed: %s",
1937 curl_multi_strerror(err));
1939 }
1940
1941 done = true;
1942 }
1943
1944
1945 if (!done)
1946 {
1947 actx_error(actx, "no result was retrieved for the finished handle");
1949 }
1950
1952}
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962static void
1964{
1965 char *escaped;
1966 char *haystack;
1967 char *match;
1968
1969
1970 escaped = curl_easy_escape(NULL, s, 0);
1971 if (!escaped)
1972 {
1974 return;
1975 }
1976
1977
1978
1979
1980
1981
1982
1983 haystack = escaped;
1984
1985 while ((match = strstr(haystack, "%20")) != NULL)
1986 {
1987
1990
1991
1992 haystack = match + 3 ;
1993 }
1994
1995
1997
1998 curl_free(escaped);
1999}
2000
2001
2002
2003
2004
2005static char *
2007{
2009
2012
2014}
2015
2016
2017
2018
2019
2020static void
2022{
2023 if (buf->len)
2025
2029}
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049static bool
2051{
2052 CHECK_SETOPT(actx, CURLOPT_HTTPGET, 1L, return false);
2053 CHECK_SETOPT(actx, CURLOPT_URL, discovery_uri, return false);
2054
2056}
2057
2058static bool
2060{
2061 long response_code;
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076 CHECK_GETINFO(actx, CURLINFO_RESPONSE_CODE, &response_code, return false);
2077
2078 if (response_code != 200)
2079 {
2080 actx_error(actx, "unexpected response code %ld", response_code);
2081 return false;
2082 }
2083
2084
2085
2086
2087 actx->errctx = "failed to parse OpenID discovery document";
2089 return false;
2090
2091
2092
2093
2095 {
2096
2097
2098
2100
2101 temp = curl_slist_append(temp, "authorization_code");
2102 if (temp)
2103 {
2104 temp = curl_slist_append(temp, "implicit");
2105 }
2106
2107 if (!temp)
2108 {
2110 return false;
2111 }
2112
2114 }
2115
2116 return true;
2117}
2118
2119
2120
2121
2122
2123static bool
2125{
2128
2129 Assert(oauth_issuer_id);
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2150 {
2152 "the issuer identifier (%s) does not match oauth_issuer (%s)",
2154 return false;
2155 }
2156
2157 return true;
2158}
2159
2160#define HTTPS_SCHEME "https://"
2161#define OAUTH_GRANT_TYPE_DEVICE_CODE "urn:ietf:params:oauth:grant-type:device_code"
2162
2163
2164
2165
2166
2167
2168static bool
2170{
2172
2175
2177 {
2179 "issuer \"%s\" does not provide a device authorization endpoint",
2181 return false;
2182 }
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2200 {
2203 {
2205 "device authorization endpoint \"%s\" must use HTTPS",
2207 return false;
2208 }
2209
2212 {
2214 "token endpoint \"%s\" must use HTTPS",
2216 return false;
2217 }
2218 }
2219
2220 return true;
2221}
2222
2223
2224
2225
2226
2227static bool
2229{
2232
2236
2237 if (oauth_client_secret)
2238 {
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2267
2269 {
2272 }
2273
2277
2279 }
2280 else
2281 {
2282
2283
2284
2285
2287
2290 }
2291
2293
2297
2299}
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311static bool
2313{
2317
2319 Assert(device_authz_uri);
2320
2321
2323 if (oauth_scope && oauth_scope[0])
2325
2327 return false;
2328
2330 {
2332 return false;
2333 }
2334
2335
2336 CHECK_SETOPT(actx, CURLOPT_URL, device_authz_uri, return false);
2337 CHECK_SETOPT(actx, CURLOPT_COPYPOSTFIELDS, work_buffer->data, return false);
2338
2340}
2341
2342static bool
2344{
2345 long response_code;
2346
2347 CHECK_GETINFO(actx, CURLINFO_RESPONSE_CODE, &response_code, return false);
2348
2349
2350
2351
2352
2353 if (response_code == 200)
2354 {
2355 actx->errctx = "failed to parse device authorization";
2357 return false;
2358
2359 return true;
2360 }
2361
2362
2363
2364
2365
2366
2367
2368 if (response_code == 400 || response_code == 401)
2369 {
2371
2373 {
2375 return false;
2376 }
2377
2378
2380
2382 return false;
2383 }
2384
2385
2386 actx_error(actx, "unexpected response code %ld", response_code);
2387 return false;
2388}
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399static bool
2401{
2405
2407 Assert(token_uri);
2408 Assert(device_code);
2409
2410
2414
2416 return false;
2417
2419 {
2421 return false;
2422 }
2423
2424
2425 CHECK_SETOPT(actx, CURLOPT_URL, token_uri, return false);
2426 CHECK_SETOPT(actx, CURLOPT_COPYPOSTFIELDS, work_buffer->data, return false);
2427
2429}
2430
2431static bool
2433{
2434 long response_code;
2435
2436 CHECK_GETINFO(actx, CURLINFO_RESPONSE_CODE, &response_code, return false);
2437
2438
2439
2440
2441 if (response_code == 200)
2442 {
2443 actx->errctx = "failed to parse access token response";
2445 return false;
2446
2447 return true;
2448 }
2449
2450
2451
2452
2453
2454
2455
2456 if (response_code == 400 || response_code == 401)
2457 {
2459 return false;
2460
2461 return true;
2462 }
2463
2464
2465 actx_error(actx, "unexpected response code %ld", response_code);
2466 return false;
2467}
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478static bool
2480{
2482 struct token tok = {0};
2484
2486 goto token_cleanup;
2487
2488
2490
2492 {
2495
2497 goto token_cleanup;
2498 }
2499
2500
2501
2502
2503
2505 if (strcmp(err->error, "authorization_pending") != 0 &&
2506 strcmp(err->error, "slow_down") != 0)
2507 {
2509 goto token_cleanup;
2510 }
2511
2512
2513
2514
2515
2516 if (strcmp(err->error, "slow_down") == 0)
2517 {
2519
2522 {
2523 actx_error(actx, "slow_down interval overflow");
2524 goto token_cleanup;
2525 }
2526 }
2527
2529
2530token_cleanup:
2533}
2534
2535
2536
2537
2538
2539static bool
2541{
2542 int res;
2548 };
2550
2552
2553 if (!res)
2554 {
2555
2556
2557
2558
2561 }
2562 else if (res < 0)
2563 {
2564 actx_error(actx, "device prompt failed");
2565 return false;
2566 }
2567
2568 return true;
2569}
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584static bool
2586{
2587
2588
2589
2590
2591
2592
2594#if HAVE_THREADSAFE_CURL_GLOBAL_INIT
2595 curl_version_info_data *info;
2596#endif
2597
2598#if !HAVE_THREADSAFE_CURL_GLOBAL_INIT
2599
2600
2601
2602
2603
2604
2605
2606
2608#endif
2609
2610
2611
2612
2613
2614
2616 goto done;
2617 else if (init_successful == PG_BOOL_NO)
2618 {
2620 "curl_global_init previously failed during OAuth setup");
2621 goto done;
2622 }
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636 if (curl_global_init(CURL_GLOBAL_ALL & ~CURL_GLOBAL_WIN32) != CURLE_OK)
2637 {
2639 "curl_global_init failed during OAuth setup");
2641 goto done;
2642 }
2643
2644#if HAVE_THREADSAFE_CURL_GLOBAL_INIT
2645
2646
2647
2648
2649
2650
2651
2652
2653 info = curl_version_info(CURLVERSION_NOW);
2654 if (!(info->features & CURL_VERSION_THREADSAFE))
2655 {
2656
2657
2658
2659
2661 "\tCurl initialization was reported thread-safe when libpq\n"
2662 "\twas compiled, but the currently installed version of\n"
2663 "\tlibcurl reports that it is not. Recompile libpq against\n"
2664 "\tthe installed version of libcurl.");
2666 goto done;
2667 }
2668#endif
2669
2671
2672done:
2673#if !HAVE_THREADSAFE_CURL_GLOBAL_INIT
2675#endif
2676 return (init_successful == PG_BOOL_YES);
2677}
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2695{
2698 char *oauth_token = NULL;
2700
2703
2704 if (->async_ctx)
2705 {
2706
2707
2708
2709
2710
2711 actx = calloc(1, sizeof(*actx));
2712 if (!actx)
2713 {
2716 }
2717
2720
2721
2723
2724 state->async_ctx = actx;
2725
2728
2730 goto error_return;
2731
2733 goto error_return;
2734 }
2735
2736 actx = state->async_ctx;
2737
2738 do
2739 {
2740
2742
2743 switch (actx->step)
2744 {
2746 break;
2747
2751 {
2753
2755
2757 goto error_return;
2759 {
2760
2761 return status;
2762 }
2763
2764 break;
2765 }
2766
2768
2769
2770
2771
2772
2773
2774
2776 {
2779 }
2780
2781
2783 goto error_return;
2784
2785 break;
2786 }
2787
2788
2789
2790
2791
2792
2793 switch (actx->step)
2794 {
2796 actx->errctx = "failed to fetch OpenID discovery document";
2798 goto error_return;
2799
2801 break;
2802
2805 goto error_return;
2806
2808 goto error_return;
2809
2810 actx->errctx = "cannot run OAuth device authorization";
2812 goto error_return;
2813
2814 actx->errctx = "failed to obtain device authorization";
2816 goto error_return;
2817
2819 break;
2820
2823 goto error_return;
2824
2825 actx->errctx = "failed to obtain access token";
2827 goto error_return;
2828
2830 break;
2831
2834 goto error_return;
2835
2836
2837
2838
2839
2841
2843 {
2844
2845
2846
2847
2849 goto error_return;
2850
2852 }
2853
2854 if (oauth_token)
2855 break;
2856
2857
2858
2859
2860
2862 goto error_return;
2863
2864
2865
2866
2867
2868
2870
2873 break;
2874
2876 actx->errctx = "failed to obtain access token";
2878 goto error_return;
2879
2881 break;
2882 }
2883
2884
2885
2886
2887
2888
2889 } while (!oauth_token && !actx->running);
2890
2891
2893
2894error_return:
2896
2897
2898
2899
2900
2903
2906 else
2908
2910 {
2912
2913
2915 {
2919 }
2920 }
2921
2923
2925}
2926
2927
2928
2929
2930
2933{
2935#ifndef WIN32
2936 sigset_t osigset;
2937 bool sigpipe_pending;
2938 bool masked;
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958 masked = (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0);
2959#endif
2960
2962
2963#ifndef WIN32
2964 if (masked)
2965 {
2966
2967
2968
2969
2970 pq_reset_sigpipe(&osigset, sigpipe_pending, true );
2971 }
2972#endif
2973
2974 return result;
2975}
static void cleanup(void)
#define fprintf(file, fmt, msg)
void err(int eval, const char *fmt,...)
PQauthDataHook_type PQgetAuthDataHook(void)
int PQsocketPoll(int sock, int forRead, int forWrite, pg_usec_time_t end_time)
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)
int(* PQauthDataHook_type)(PGauthData type, PGconn *conn, void *data)
PostgresPollingStatusType
@ PQAUTHDATA_PROMPT_OAUTH_DEVICE
PostgresPollingStatusType pg_fe_run_oauth_flow(PGconn *conn)
static char * urlencode(const char *s)
static bool setup_multiplexer(struct async_ctx *actx)
static bool finish_token_request(struct async_ctx *actx, struct token *tok)
static JsonParseErrorType oauth_json_array_end(void *state)
static void append_urlencoded(PQExpBuffer buf, const char *s)
static bool start_token_request(struct async_ctx *actx, PGconn *conn)
static bool initialize_curl(PGconn *conn)
#define MAX_OAUTH_RESPONSE_SIZE
static bool parse_token_error(struct async_ctx *actx, struct token_error *err)
void pg_fe_cleanup_oauth_flow(PGconn *conn)
static bool add_client_identification(struct async_ctx *actx, PQExpBuffer reqbody, PGconn *conn)
static int parse_interval(struct async_ctx *actx, const char *interval_str)
static void free_provider(struct provider *provider)
static void build_urlencoded(PQExpBuffer buf, const char *key, const char *value)
#define conn_oauth_issuer_id(CONN)
static void record_token_error(struct async_ctx *actx, const struct token_error *err)
static bool parse_device_authz(struct async_ctx *actx, struct device_authz *authz)
static void report_type_mismatch(struct oauth_parse *ctx)
static int register_socket(CURL *curl, curl_socket_t socket, int what, void *ctx, void *socketp)
#define PG_OAUTH_OPTIONAL
static bool set_timer(struct async_ctx *actx, long timeout)
static bool parse_access_token(struct async_ctx *actx, struct token *tok)
static int timer_expired(struct async_ctx *actx)
static PostgresPollingStatusType drive_request(struct async_ctx *actx)
static bool start_device_authz(struct async_ctx *actx, PGconn *conn)
static bool prompt_user(struct async_ctx *actx, PGconn *conn)
#define CHECK_MSETOPT(ACTX, OPT, VAL, FAILACTION)
static bool finish_discovery(struct async_ctx *actx)
static double parse_json_number(const char *s)
#define conn_oauth_discovery_uri(CONN)
static bool start_discovery(struct async_ctx *actx, const char *discovery_uri)
static JsonParseErrorType oauth_json_object_field_start(void *state, char *name, bool isnull)
static JsonParseErrorType oauth_json_scalar(void *state, char *token, JsonTokenType type)
static void free_token_error(struct token_error *err)
#define actx_error_str(ACTX, S)
static bool finish_device_authz(struct async_ctx *actx)
#define conn_oauth_scope(CONN)
static size_t append_data(char *buf, size_t size, size_t nmemb, void *userdata)
#define CHECK_SETOPT(ACTX, OPT, VAL, FAILACTION)
static PostgresPollingStatusType pg_fe_run_oauth_flow_impl(PGconn *conn)
static bool parse_oauth_json(struct async_ctx *actx, const struct json_field *fields)
#define MAX_OAUTH_NESTING_LEVEL
#define OAUTH_GRANT_TYPE_DEVICE_CODE
#define conn_oauth_client_id(CONN)
#define CURL_IGNORE_DEPRECATION(x)
static JsonParseErrorType oauth_json_array_start(void *state)
static JsonParseErrorType oauth_json_object_end(void *state)
#define set_conn_altsock(CONN, VAL)
static int debug_callback(CURL *handle, curl_infotype type, char *data, size_t size, void *clientp)
static void free_token(struct token *tok)
#define oauth_parse_set_error(ctx, fmt,...)
@ OAUTH_STEP_DEVICE_AUTHORIZATION
@ OAUTH_STEP_WAIT_INTERVAL
@ OAUTH_STEP_TOKEN_REQUEST
static int register_timer(CURLM *curlm, long timeout, void *ctx)
#define conn_oauth_client_secret(CONN)
#define set_conn_oauth_token(CONN, VAL)
#define CHECK_GETINFO(ACTX, INFO, OUT, FAILACTION)
static bool check_content_type(struct async_ctx *actx, const char *type)
static bool check_issuer(struct async_ctx *actx, PGconn *conn)
#define actx_error(ACTX, FMT,...)
static bool parse_provider(struct async_ctx *actx, struct provider *provider)
static bool start_request(struct async_ctx *actx)
static int parse_expires_in(struct async_ctx *actx, const char *expires_in_str)
static void free_async_ctx(PGconn *conn, struct async_ctx *actx)
static void free_device_authz(struct device_authz *authz)
#define conn_sasl_state(CONN)
#define conn_errorMessage(CONN)
static bool handle_token_response(struct async_ctx *actx, char **token)
static JsonParseErrorType oauth_json_object_start(void *state)
#define PG_OAUTH_REQUIRED
static bool check_for_device_flow(struct async_ctx *actx)
static bool setup_curl_handles(struct async_ctx *actx)
void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
void libpq_append_conn_error(PGconn *conn, const char *fmt,...)
bool oauth_unsafe_debugging_enabled(void)
#define pgunlock_thread()
int pg_strncasecmp(const char *s1, const char *s2, size_t n)
void initPQExpBuffer(PQExpBuffer str)
void resetPQExpBuffer(PQExpBuffer str)
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
void appendPQExpBufferChar(PQExpBuffer str, char ch)
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
void termPQExpBuffer(PQExpBuffer str)
#define PQExpBufferBroken(str)
#define PQExpBufferDataBroken(buf)
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 * verification_uri
struct device_authz authz
PQExpBufferData work_data
char curl_err[CURL_ERROR_SIZE]
struct curl_slist * headers
char * verification_uri_complete
struct curl_slist ** array
union json_field::@188 target
const struct json_field * active
const struct json_field * fields
char * device_authorization_endpoint
struct curl_slist * grant_types_supported
int pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)
#define socket(af, type, protocol)