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

232 int timerfd;

233 pgsocket mux;

234

235

236 CURLM *curlm;

237

238 CURL *curl;

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

266 const char *errctx;

269

270

271

272

273

276

277 int running;

278 bool user_prompted;

280 bool debugging;

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{

432 const char *name;

433

435

436

437 union

438 {

439 char **scalar;

440 struct curl_slist **array;

442

443 bool required;

444};

445

446

447#define PG_OAUTH_REQUIRED true

448#define PG_OAUTH_OPTIONAL false

449

450

452{

454 int nested;

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 (state->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)