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

98

99

101{

106};

107

108static void

110{

115}

116

117

118

119

120

121

123{

130

131

134};

135

136static void

138{

145}

146

147

148

149

150

151

152

153

154

156{

159};

160

161static void

163{

165 free(err->error_description);

166}

167

168

169

170

171

172

173

174

175

176

178{

179

182

183

185};

186

187static void

189{

193}

194

195

196

197

198

199

201{

207};

208

209

210

211

212

213

215{

217

218 int timerfd;

219 pgsocket mux;

220

221

222 CURLM *curlm;

223

224 CURL *curl;

225

226

227 struct curl_slist *headers;

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252 const char *errctx;

255

256

257

258

259

262

263 int running;

264 bool user_prompted;

266 bool debugging;

267};

268

269

270

271

272static void

274{

275

276

277

278

279

280

281

282

283

284

285

287 {

288 CURLMcode err = curl_multi_remove_handle(actx->curlm, actx->curl);

289

292 "libcurl easy handle removal failed: %s",

293 curl_multi_strerror(err));

294 }

295

296 if (actx->curl)

297 {

298

299

300

301

302

303 curl_easy_cleanup(actx->curl);

304 }

305

307 {

308 CURLMcode err = curl_multi_cleanup(actx->curlm);

309

312 "libcurl multi handle cleanup failed: %s",

313 curl_multi_strerror(err));

314 }

315

318

319 curl_slist_free_all(actx->headers);

322

327

329}

330

331

332

333

334

335

336

337

338

339void

341{

343

344 if (state->async_ctx)

345 {

347 state->async_ctx = NULL;

348 }

349

351}

352

353

354

355

356

357

358

359#define actx_error(ACTX, FMT, ...) \

360 appendPQExpBuffer(&(ACTX)->errbuf, libpq_gettext(FMT), ##__VA_ARGS__)

361

362#define actx_error_str(ACTX, S) \

363 appendPQExpBufferStr(&(ACTX)->errbuf, S)

364

365

366

367

368

369

370#define CHECK_MSETOPT(ACTX, OPT, VAL, FAILACTION) \

371 do { \

372 struct async_ctx *_actx = (ACTX); \

373 CURLMcode _setopterr = curl_multi_setopt(_actx->curlm, OPT, VAL); \

374 if (_setopterr) { \

375 actx_error(_actx, "failed to set %s on OAuth connection: %s",\

376 #OPT, curl_multi_strerror(_setopterr)); \

377 FAILACTION; \

378 } \

379 } while (0)

380

381#define CHECK_SETOPT(ACTX, OPT, VAL, FAILACTION) \

382 do { \

383 struct async_ctx *_actx = (ACTX); \

384 CURLcode _setopterr = curl_easy_setopt(_actx->curl, OPT, VAL); \

385 if (_setopterr) { \

386 actx_error(_actx, "failed to set %s on OAuth connection: %s",\

387 #OPT, curl_easy_strerror(_setopterr)); \

388 FAILACTION; \

389 } \

390 } while (0)

391

392#define CHECK_GETINFO(ACTX, INFO, OUT, FAILACTION) \

393 do { \

394 struct async_ctx *_actx = (ACTX); \

395 CURLcode _getinfoerr = curl_easy_getinfo(_actx->curl, INFO, OUT); \

396 if (_getinfoerr) { \

397 actx_error(_actx, "failed to get %s from OAuth response: %s",\

398 #INFO, curl_easy_strerror(_getinfoerr)); \

399 FAILACTION; \

400 } \

401 } while (0)

402

403

404

405

406

407

408

409

410

411

412

413

414

415

417{

418 const char *name;

419

421

422

423 union

424 {

425 char **scalar;

426 struct curl_slist **array;

428

429 bool required;

430};

431

432

433#define PG_OAUTH_REQUIRED true

434#define PG_OAUTH_OPTIONAL false

435

436

438{

440 int nested;

441

444};

445

446#define oauth_parse_set_error(ctx, fmt, ...) \

447 appendPQExpBuffer((ctx)->errbuf, libpq_gettext(fmt), ##__VA_ARGS__)

448

449static void

451{

452 char *msgfmt;

453

455

456

457

458

459

461 {

463 msgfmt = "field \"%s\" must be a string";

464 break;

465

467 msgfmt = "field \"%s\" must be a number";

468 break;

469

471 msgfmt = "field \"%s\" must be an array of strings";

472 break;

473

474 default:

476 msgfmt = "field \"%s\" has unexpected type";

477 }

478

480}

481

484{

486

488 {

489

490

491

492

495 }

496

499}

500

503{

505

506

507 if (ctx->nested == 1)

508 {

510

511

512

513

514

516 {

519 "internal error: started field '%s' before field '%s' was finished",

522 }

523

524 while (field->name)

525 {

526 if (strcmp(name, field->name) == 0)

527 {

529 break;

530 }

531

532 ++field;

533 }

534

535

536

537

538

540 {

542

545 {

547 field->name);

549 }

550 }

551 }

552

554}

555

558{

560

562

563

564

565

566

568 {

571 "internal error: field '%s' still active at end of object",

574 }

575

577}

578

581{

583

585 {

588 }

589

591 {

593

595 {

598 }

599 }

600

603}

604

607{

609

611 {

612

613

614

615

616

618 {

621 "internal error: found unexpected array end while parsing field '%s'",

624 }

625

627 }

628

631}

632

635{

637

639 {

642 }

643

645 {

648

649

651 {

652

654 {

657 }

658

659

661 }

662

663 if (type != expected)

664 {

667 }

668

670 {

671

672 if (ctx->nested != 1)

673 {

676 "internal error: scalar target found at nesting level %d",

679 }

680

681

683 {

686 "internal error: scalar field '%s' would be assigned twice",

689 }

690

694

696

698 }

699 else

700 {

701 struct curl_slist *temp;

702

703

704 if (ctx->nested != 2)

705 {

708 "internal error: array member found at nesting level %d",

711 }

712

713

715 if (!temp)

717

719 }

720 }

721 else

722 {

723

724 }

725

727}

728

729

730

731

732

733static bool

735{

736 const size_t type_len = strlen(type);

737 char *content_type;

738

739 CHECK_GETINFO(actx, CURLINFO_CONTENT_TYPE, &content_type, return false);

740

741 if (!content_type)

742 {

743 actx_error(actx, "no content type was provided");

744 return false;

745 }

746

747

748

749

750

752 goto fail;

753

754

755 Assert(strlen(content_type) >= type_len);

756 if (content_type[type_len] == '\0')

757 return true;

758

759

760

761

762

763

764 for (size_t i = type_len; content_type[i]; ++i)

765 {

766 switch (content_type[i])

767 {

768 case ';':

769 return true;

770

771 case ' ':

772 case '\t':

773

774 break;

775

776 default:

777 goto fail;

778 }

779 }

780

781fail:

782 actx_error(actx, "unexpected content type: \"%s\"", content_type);

783 return false;

784}

785

786

787

788

789

790

791

792static bool

794{

801

803 return false;

804

805 if (strlen(resp->data) != resp->len)

806 {

807 actx_error(actx, "response contains embedded NULLs");

808 return false;

809 }

810

811

812

813

814

816 {

817 actx_error(actx, "response is not valid UTF-8");

818 return false;

819 }

820

823

827

834

836

838 {

839

840

841

842

843

846

848 }

849

850

852 {

856 {

859 }

860

862 }

863

865

869}

870

871

872

873

874

875

876

877

878

879static bool

881{

885

886

887

888

889

890

891

892

893

894

895

898

899 {0},

900 };

901

903}

904

905

906

907

908

909

910static double

912{

913 double parsed;

914 int cnt;

915

916

917

918

919

920 cnt = sscanf(s, "%lf", &parsed);

921

922 if (cnt != 1)

923 {

924

925

926

927

929 return 0;

930 }

931

932 return parsed;

933}

934

935

936

937

938

939

940

941

942

943

944

945static int

947{

948 double parsed;

949

951 parsed = ceil(parsed);

952

953 if (parsed < 1)

955

956 else if (parsed >= INT_MAX)

957 return INT_MAX;

958

959 return parsed;

960}

961

962

963

964

965

966

967

968

969

970

971static int

973{

974 double parsed;

975

977 parsed = floor(parsed);

978

979 if (parsed >= INT_MAX)

980 return INT_MAX;

981 else if (parsed <= INT_MIN)

982 return INT_MIN;

983

984 return parsed;

985}

986

987

988

989

990static bool

992{

998

999

1000

1001

1002

1004

1005

1006

1007

1008

1009

1012

1013 {0},

1014 };

1015

1017 return false;

1018

1019

1020

1021

1022

1025 else

1026 {

1027

1028

1029

1030

1032 }

1033

1036

1037 return true;

1038}

1039

1040

1041

1042

1043

1044static bool

1046{

1047 bool result;

1050

1052

1053 {0},

1054 };

1055

1057

1058

1059

1060

1061

1062 if (!result)

1063 actx->errctx = "failed to parse token error response";

1064

1065 return result;

1066}

1067

1068

1069

1070

1071

1072static void

1074{

1075 if (err->error_description)

1077 else

1078 {

1079

1080

1081

1082

1083

1084 long response_code;

1085

1086 CHECK_GETINFO(actx, CURLINFO_RESPONSE_CODE, &response_code, response_code = 0);

1087

1088 if (response_code == 401)

1089 {

1091 ? "provider rejected the oauth_client_secret"

1092 : "provider requires client authentication, and no oauth_client_secret is set");

1094 }

1095 }

1096

1098}

1099

1100

1101

1102

1103

1104static bool

1106{

1110

1111

1112

1113

1114

1115

1116

1117

1118

1119

1120

1121

1122

1123

1124

1125

1126

1127 {0},

1128 };

1129

1131}

1132

1133

1134

1135

1136

1137

1138

1139

1140

1141

1142

1143

1144

1145

1146static bool

1148{

1149#if defined(HAVE_SYS_EPOLL_H)

1150 struct epoll_event ev = {.events = EPOLLIN};

1151

1152 actx->mux = epoll_create1(EPOLL_CLOEXEC);

1153 if (actx->mux < 0)

1154 {

1155 actx_error(actx, "failed to create epoll set: %m");

1156 return false;

1157 }

1158

1159 actx->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);

1161 {

1162 actx_error(actx, "failed to create timerfd: %m");

1163 return false;

1164 }

1165

1166 if (epoll_ctl(actx->mux, EPOLL_CTL_ADD, actx->timerfd, &ev) < 0)

1167 {

1168 actx_error(actx, "failed to add timerfd to epoll set: %m");

1169 return false;

1170 }

1171

1172 return true;

1173#elif defined(HAVE_SYS_EVENT_H)

1174 actx->mux = kqueue();

1175 if (actx->mux < 0)

1176 {

1177

1178 actx_error(actx, "failed to create kqueue: %m");

1179 return false;

1180 }

1181

1182

1183

1184

1185

1186

1187

1188 actx->timerfd = kqueue();

1190 {

1191 actx_error(actx, "failed to create timer kqueue: %m");

1192 return false;

1193 }

1194

1195 return true;

1196#else

1197#error setup_multiplexer is not implemented on this platform

1198#endif

1199}

1200

1201

1202

1203

1204

1205static int

1207 void *socketp)

1208{

1210

1211#if defined(HAVE_SYS_EPOLL_H)

1212 struct epoll_event ev = {0};

1213 int res;

1214 int op = EPOLL_CTL_ADD;

1215

1216 switch (what)

1217 {

1218 case CURL_POLL_IN:

1219 ev.events = EPOLLIN;

1220 break;

1221

1222 case CURL_POLL_OUT:

1223 ev.events = EPOLLOUT;

1224 break;

1225

1226 case CURL_POLL_INOUT:

1227 ev.events = EPOLLIN | EPOLLOUT;

1228 break;

1229

1230 case CURL_POLL_REMOVE:

1231 op = EPOLL_CTL_DEL;

1232 break;

1233

1234 default:

1235 actx_error(actx, "unknown libcurl socket operation: %d", what);

1236 return -1;

1237 }

1238

1239 res = epoll_ctl(actx->mux, op, socket, &ev);

1240 if (res < 0 && errno == EEXIST)

1241 {

1242

1243 op = EPOLL_CTL_MOD;

1244 res = epoll_ctl(actx->mux, op, socket, &ev);

1245 }

1246

1247 if (res < 0)

1248 {

1249 switch (op)

1250 {

1251 case EPOLL_CTL_ADD:

1252 actx_error(actx, "could not add to epoll set: %m");

1253 break;

1254

1255 case EPOLL_CTL_DEL:

1256 actx_error(actx, "could not delete from epoll set: %m");

1257 break;

1258

1259 default:

1260 actx_error(actx, "could not update epoll set: %m");

1261 }

1262

1263 return -1;

1264 }

1265

1266 return 0;

1267#elif defined(HAVE_SYS_EVENT_H)

1268 struct kevent ev[2] = {0};

1269 struct kevent ev_out[2];

1270 struct timespec timeout = {0};

1271 int nev = 0;

1272 int res;

1273

1274 switch (what)

1275 {

1276 case CURL_POLL_IN:

1277 EV_SET(&ev[nev], socket, EVFILT_READ, EV_ADD | EV_RECEIPT, 0, 0, 0);

1278 nev++;

1279 break;

1280

1281 case CURL_POLL_OUT:

1282 EV_SET(&ev[nev], socket, EVFILT_WRITE, EV_ADD | EV_RECEIPT, 0, 0, 0);

1283 nev++;

1284 break;

1285

1286 case CURL_POLL_INOUT:

1287 EV_SET(&ev[nev], socket, EVFILT_READ, EV_ADD | EV_RECEIPT, 0, 0, 0);

1288 nev++;

1289 EV_SET(&ev[nev], socket, EVFILT_WRITE, EV_ADD | EV_RECEIPT, 0, 0, 0);

1290 nev++;

1291 break;

1292

1293 case CURL_POLL_REMOVE:

1294

1295

1296

1297

1298

1299

1300 EV_SET(&ev[nev], socket, EVFILT_READ, EV_DELETE | EV_RECEIPT, 0, 0, 0);

1301 nev++;

1302 EV_SET(&ev[nev], socket, EVFILT_WRITE, EV_DELETE | EV_RECEIPT, 0, 0, 0);

1303 nev++;

1304 break;

1305

1306 default:

1307 actx_error(actx, "unknown libcurl socket operation: %d", what);

1308 return -1;

1309 }

1310

1311 res = kevent(actx->mux, ev, nev, ev_out, lengthof(ev_out), &timeout);

1312 if (res < 0)

1313 {

1314 actx_error(actx, "could not modify kqueue: %m");

1315 return -1;

1316 }

1317

1318

1319

1320

1321

1322

1323 for (int i = 0; i < res; ++i)

1324 {

1325

1326

1327

1328

1329

1330 Assert(ev_out[i].flags & EV_ERROR);

1331

1332 errno = ev_out[i].data;

1333 if (errno && errno != ENOENT)

1334 {

1335 switch (what)

1336 {

1337 case CURL_POLL_REMOVE:

1338 actx_error(actx, "could not delete from kqueue: %m");

1339 break;

1340 default:

1341 actx_error(actx, "could not add to kqueue: %m");

1342 }

1343 return -1;

1344 }

1345 }

1346

1347 return 0;

1348#else

1349#error register_socket is not implemented on this platform

1350#endif

1351}

1352

1353

1354

1355

1356

1357

1358

1359

1360

1361

1362

1363

1364

1365

1366static bool

1368{

1369#if defined(HAVE_SYS_EPOLL_H)

1370 struct itimerspec spec = {0};

1371

1372 if (timeout < 0)

1373 {

1374

1375 }

1376 else if (timeout == 0)

1377 {

1378

1379

1380

1381

1382

1383 spec.it_value.tv_nsec = 1;

1384 }

1385 else

1386 {

1387 spec.it_value.tv_sec = timeout / 1000;

1388 spec.it_value.tv_nsec = (timeout % 1000) * 1000000;

1389 }

1390

1391 if (timerfd_settime(actx->timerfd, 0 , &spec, NULL) < 0)

1392 {

1393 actx_error(actx, "setting timerfd to %ld: %m", timeout);

1394 return false;

1395 }

1396

1397 return true;

1398#elif defined(HAVE_SYS_EVENT_H)

1399 struct kevent ev;

1400

1401#ifdef __NetBSD__

1402

1403

1404

1405

1406

1407 if (timeout == 0)

1408 timeout = 1;

1409#endif

1410

1411

1412

1413

1414

1415

1416

1417

1418

1419

1420 EV_SET(&ev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, 0);

1421 if (kevent(actx->timerfd, &ev, 1, NULL, 0, NULL) < 0 && errno != ENOENT)

1422 {

1423 actx_error(actx, "deleting kqueue timer: %m");

1424 return false;

1425 }

1426

1427 EV_SET(&ev, actx->timerfd, EVFILT_READ, EV_DELETE, 0, 0, 0);

1428 if (kevent(actx->mux, &ev, 1, NULL, 0, NULL) < 0 && errno != ENOENT)

1429 {

1430 actx_error(actx, "removing kqueue timer from multiplexer: %m");

1431 return false;

1432 }

1433

1434

1435 if (timeout < 0)

1436 return true;

1437

1438 EV_SET(&ev, 1, EVFILT_TIMER, (EV_ADD | EV_ONESHOT), 0, timeout, 0);

1439 if (kevent(actx->timerfd, &ev, 1, NULL, 0, NULL) < 0)

1440 {

1441 actx_error(actx, "setting kqueue timer to %ld: %m", timeout);

1442 return false;

1443 }

1444

1445 EV_SET(&ev, actx->timerfd, EVFILT_READ, EV_ADD, 0, 0, 0);

1446 if (kevent(actx->mux, &ev, 1, NULL, 0, NULL) < 0)

1447 {

1448 actx_error(actx, "adding kqueue timer to multiplexer: %m");

1449 return false;

1450 }

1451

1452 return true;

1453#else

1454#error set_timer is not implemented on this platform

1455#endif

1456}

1457

1458

1459

1460

1461

1462

1463static int

1465{

1466#if defined(HAVE_SYS_EPOLL_H)

1467 struct itimerspec spec = {0};

1468

1469 if (timerfd_gettime(actx->timerfd, &spec) < 0)

1470 {

1471 actx_error(actx, "getting timerfd value: %m");

1472 return -1;

1473 }

1474

1475

1476

1477

1478

1479

1480 Assert(spec.it_interval.tv_sec == 0

1481 && spec.it_interval.tv_nsec == 0);

1482

1483

1484 return (spec.it_value.tv_sec == 0

1485 && spec.it_value.tv_nsec == 0);

1486#elif defined(HAVE_SYS_EVENT_H)

1487 int res;

1488

1489

1491 if (res < 0)

1492 {

1493 actx_error(actx, "checking kqueue for timeout: %m");

1494 return -1;

1495 }

1496

1497 return (res > 0);

1498#else

1499#error timer_expired is not implemented on this platform

1500#endif

1501}

1502

1503

1504

1505

1506

1507static int

1509{

1511

1512

1513

1514

1515

1516

1517

1518

1520 return -1;

1521

1522 return 0;

1523}

1524

1525

1526

1527

1528

1529

1530

1531static int

1533 void *clientp)

1534{

1535 const char *prefix;

1536 bool printed_prefix = false;

1538

1539

1540 switch (type)

1541 {

1542 case CURLINFO_TEXT:

1543 prefix = "*";

1544 break;

1545

1546 case CURLINFO_HEADER_IN:

1547 case CURLINFO_DATA_IN:

1548 prefix = "<";

1549 break;

1550

1551 case CURLINFO_HEADER_OUT:

1552 case CURLINFO_DATA_OUT:

1553 prefix = ">";

1554 break;

1555

1556 default:

1557 return 0;

1558 }

1559

1561

1562

1563

1564

1565

1566

1567 for (int i = 0; i < size; i++)

1568 {

1570

1571 if (!printed_prefix)

1572 {

1574 printed_prefix = true;

1575 }

1576

1577 if (c >= 0x20 && c <= 0x7E)

1579 else if ((type == CURLINFO_HEADER_IN

1580 || type == CURLINFO_HEADER_OUT

1581 || type == CURLINFO_TEXT)

1582 && (c == '\r' || c == '\n'))

1583 {

1584

1585

1586

1587

1588 }

1589 else

1591

1592 if (c == '\n')

1593 {

1595 printed_prefix = false;

1596 }

1597 }

1598

1599 if (printed_prefix)

1601

1604 return 0;

1605}

1606

1607

1608

1609

1610

1611

1612

1613

1614static bool

1616{

1617

1618

1619

1620

1621 actx->curlm = curl_multi_init();

1622 if (!actx->curlm)

1623 {

1624

1625 actx_error(actx, "failed to create libcurl multi handle");

1626 return false;

1627 }

1628

1629

1630

1631

1632

1634 CHECK_MSETOPT(actx, CURLMOPT_SOCKETDATA, actx, return false);

1636 CHECK_MSETOPT(actx, CURLMOPT_TIMERDATA, actx, return false);

1637

1638

1639

1640

1641

1642 actx->curl = curl_easy_init();

1643 if (!actx->curl)

1644 {

1645 actx_error(actx, "failed to create libcurl handle");

1646 return false;

1647 }

1648

1649

1650

1651

1652

1653

1654

1655

1656

1657

1658

1659

1660

1661

1662

1663

1664 CHECK_SETOPT(actx, CURLOPT_NOSIGNAL, 1L, return false);

1665

1667 {

1668

1669

1670

1671

1672

1674 CHECK_SETOPT(actx, CURLOPT_VERBOSE, 1L, return false);

1675 }

1676

1678

1679

1680

1681

1682

1683

1684

1685

1686

1687 {

1688#if CURL_AT_LEAST_VERSION(7, 85, 0)

1689 const CURLoption popt = CURLOPT_PROTOCOLS_STR;

1690 const char *protos = "https";

1691 const char *const unsafe = "https,http";

1692#else

1693 const CURLoption popt = CURLOPT_PROTOCOLS;

1694 long protos = CURLPROTO_HTTPS;

1695 const long unsafe = CURLPROTO_HTTPS | CURLPROTO_HTTP;

1696#endif

1697

1699 protos = unsafe;

1700

1701 CHECK_SETOPT(actx, popt, protos, return false);

1702 }

1703

1704

1705

1706

1707

1708

1709

1710

1711

1713 {

1714 const char *env;

1715

1716 if ((env = getenv("PGOAUTHCAFILE")) != NULL)

1717 CHECK_SETOPT(actx, CURLOPT_CAINFO, env, return false);

1718 }

1719

1720

1721

1722

1723

1724

1725

1726 actx->headers = curl_slist_append(actx->headers, "Accept:");

1727 if (actx->headers == NULL)

1728 {

1730 return false;

1731 }

1733

1734 return true;

1735}

1736

1737

1738

1739

1740

1741

1742

1743

1744

1745

1746

1747static size_t

1749{

1750 struct async_ctx *actx = userdata;

1752 size_t len = size * nmemb;

1753

1754

1756 {

1757 actx_error(actx, "response is too large");

1758 return 0;

1759 }

1760

1761

1763

1764

1765

1766

1767

1769 {

1771 return 0;

1772 }

1773

1774 return len;

1775}

1776

1777

1778

1779

1780

1781

1782

1783

1784

1785

1786

1787static bool

1789{

1790 CURLMcode err;

1791

1794 CHECK_SETOPT(actx, CURLOPT_WRITEDATA, actx, return false);

1795

1796 err = curl_multi_add_handle(actx->curlm, actx->curl);

1797 if (err)

1798 {

1799 actx_error(actx, "failed to queue HTTP request: %s",

1800 curl_multi_strerror(err));

1801 return false;

1802 }

1803

1804

1805

1806

1807

1808

1809

1810

1811

1812

1813 err = curl_multi_socket_action(actx->curlm, CURL_SOCKET_TIMEOUT, 0, &actx->running);

1814 if (err)

1815 {

1816 actx_error(actx, "asynchronous HTTP request failed: %s",

1817 curl_multi_strerror(err));

1818 return false;

1819 }

1820

1821 return true;

1822}

1823

1824

1825

1826

1827

1828#ifndef CURL_IGNORE_DEPRECATION

1829#define CURL_IGNORE_DEPRECATION(x) x

1830#endif

1831

1832

1833

1834

1835

1838{

1839 CURLMcode err;

1840 CURLMsg *msg;

1841 int msgs_left;

1842 bool done;

1843

1845 {

1846

1847

1848

1849

1850

1851

1852

1853

1854

1855

1856

1857

1858

1859

1860

1861

1862

1864 err = curl_multi_socket_all(actx->curlm, &actx->running);

1865 )

1866

1867 if (err)

1868 {

1869 actx_error(actx, "asynchronous HTTP request failed: %s",

1870 curl_multi_strerror(err));

1872 }

1873

1875 {

1876

1878 }

1879 }

1880

1881 done = false;

1882 while ((msg = curl_multi_info_read(actx->curlm, &msgs_left)) != NULL)

1883 {

1884 if (msg->msg != CURLMSG_DONE)

1885 {

1886

1887

1888

1889

1890 continue;

1891 }

1892

1893

1894 if (msg->data.result != CURLE_OK)

1895 {

1896

1897

1898

1899

1901 actx_error_str(actx, curl_easy_strerror(msg->data.result));

1902

1904 }

1905

1906

1907 err = curl_multi_remove_handle(actx->curlm, msg->easy_handle);

1908 if (err)

1909 {

1910 actx_error(actx, "libcurl easy handle removal failed: %s",

1911 curl_multi_strerror(err));

1913 }

1914

1915 done = true;

1916 }

1917

1918

1919 if (!done)

1920 {

1921 actx_error(actx, "no result was retrieved for the finished handle");

1923 }

1924

1926}

1927

1928

1929

1930

1931

1932

1933

1934

1935

1936static void

1938{

1939 char *escaped;

1940 char *haystack;

1941 char *match;

1942

1943

1944 escaped = curl_easy_escape(NULL, s, 0);

1945 if (!escaped)

1946 {

1948 return;

1949 }

1950

1951

1952

1953

1954

1955

1956

1957 haystack = escaped;

1958

1959 while ((match = strstr(haystack, "%20")) != NULL)

1960 {

1961

1964

1965

1966 haystack = match + 3 ;

1967 }

1968

1969

1971

1972 curl_free(escaped);

1973}

1974

1975

1976

1977

1978

1979static char *

1981{

1983

1986

1988}

1989

1990

1991

1992

1993

1994static void

1996{

1997 if (buf->len)

1999

2003}

2004

2005

2006

2007

2008

2009

2010

2011

2012

2013

2014

2015

2016

2017

2018

2019

2020

2021

2022

2023static bool

2025{

2026 CHECK_SETOPT(actx, CURLOPT_HTTPGET, 1L, return false);

2027 CHECK_SETOPT(actx, CURLOPT_URL, discovery_uri, return false);

2028

2030}

2031

2032static bool

2034{

2035 long response_code;

2036

2037

2038

2039

2040

2041

2042

2043

2044

2045

2046

2047

2048

2049

2050 CHECK_GETINFO(actx, CURLINFO_RESPONSE_CODE, &response_code, return false);

2051

2052 if (response_code != 200)

2053 {

2054 actx_error(actx, "unexpected response code %ld", response_code);

2055 return false;

2056 }

2057

2058

2059

2060

2061 actx->errctx = "failed to parse OpenID discovery document";

2063 return false;

2064

2065

2066

2067

2069 {

2070

2071

2072

2074

2075 temp = curl_slist_append(temp, "authorization_code");

2076 if (temp)

2077 {

2078 temp = curl_slist_append(temp, "implicit");

2079 }

2080

2081 if (!temp)

2082 {

2084 return false;

2085 }

2086

2088 }

2089

2090 return true;

2091}

2092

2093

2094

2095

2096

2097static bool

2099{

2102

2103 Assert(oauth_issuer_id);

2105

2106

2107

2108

2109

2110

2111

2112

2113

2114

2115

2116

2117

2118

2119

2120

2121

2122

2124 {

2126 "the issuer identifier (%s) does not match oauth_issuer (%s)",

2128 return false;

2129 }

2130

2131 return true;

2132}

2133

2134#define HTTPS_SCHEME "https://"

2135#define OAUTH_GRANT_TYPE_DEVICE_CODE "urn:ietf:params:oauth:grant-type:device_code"

2136

2137

2138

2139

2140

2141

2142static bool

2144{

2146

2149

2151 {

2153 "issuer \"%s\" does not provide a device authorization endpoint",

2155 return false;

2156 }

2157

2158

2159

2160

2161

2162

2163

2164

2165

2166

2167

2168

2169

2170

2171

2172

2174 {

2177 {

2179 "device authorization endpoint \"%s\" must use HTTPS",

2181 return false;

2182 }

2183

2186 {

2188 "token endpoint \"%s\" must use HTTPS",

2190 return false;

2191 }

2192 }

2193

2194 return true;

2195}

2196

2197

2198

2199

2200

2201static bool

2203{

2206

2210

2211 if (oauth_client_secret)

2212 {

2213

2214

2215

2216

2217

2218

2219

2220

2221

2222

2223

2224

2225

2226

2227

2228

2229

2230

2231

2232

2233

2234

2235

2236

2237

2238

2241

2243 {

2246 }

2247

2251

2253 }

2254 else

2255 {

2256

2257

2258

2259

2261

2264 }

2265

2267

2271

2273}

2274

2275

2276

2277

2278

2279

2280

2281

2282

2283

2284

2285static bool

2287{

2291

2293 Assert(device_authz_uri);

2294

2295

2297 if (oauth_scope && oauth_scope[0])

2299

2301 return false;

2302

2304 {

2306 return false;

2307 }

2308

2309

2310 CHECK_SETOPT(actx, CURLOPT_URL, device_authz_uri, return false);

2311 CHECK_SETOPT(actx, CURLOPT_COPYPOSTFIELDS, work_buffer->data, return false);

2312

2314}

2315

2316static bool

2318{

2319 long response_code;

2320

2321 CHECK_GETINFO(actx, CURLINFO_RESPONSE_CODE, &response_code, return false);

2322

2323

2324

2325

2326

2327 if (response_code == 200)

2328 {

2329 actx->errctx = "failed to parse device authorization";

2331 return false;

2332

2333 return true;

2334 }

2335

2336

2337

2338

2339

2340

2341

2342 if (response_code == 400 || response_code == 401)

2343 {

2345

2347 {

2349 return false;

2350 }

2351

2352

2354

2356 return false;

2357 }

2358

2359

2360 actx_error(actx, "unexpected response code %ld", response_code);

2361 return false;

2362}

2363

2364

2365

2366

2367

2368

2369

2370

2371

2372

2373static bool

2375{

2379

2381 Assert(token_uri);

2382 Assert(device_code);

2383

2384

2388

2390 return false;

2391

2393 {

2395 return false;

2396 }

2397

2398

2399 CHECK_SETOPT(actx, CURLOPT_URL, token_uri, return false);

2400 CHECK_SETOPT(actx, CURLOPT_COPYPOSTFIELDS, work_buffer->data, return false);

2401

2403}

2404

2405static bool

2407{

2408 long response_code;

2409

2410 CHECK_GETINFO(actx, CURLINFO_RESPONSE_CODE, &response_code, return false);

2411

2412

2413

2414

2415 if (response_code == 200)

2416 {

2417 actx->errctx = "failed to parse access token response";

2419 return false;

2420

2421 return true;

2422 }

2423

2424

2425

2426

2427

2428

2429

2430 if (response_code == 400 || response_code == 401)

2431 {

2433 return false;

2434

2435 return true;

2436 }

2437

2438

2439 actx_error(actx, "unexpected response code %ld", response_code);

2440 return false;

2441}

2442

2443

2444

2445

2446

2447

2448

2449

2450

2451

2452static bool

2454{

2456 struct token tok = {0};

2458

2460 goto token_cleanup;

2461

2462

2464

2466 {

2469

2471 goto token_cleanup;

2472 }

2473

2474

2475

2476

2477

2479 if (strcmp(err->error, "authorization_pending") != 0 &&

2480 strcmp(err->error, "slow_down") != 0)

2481 {

2483 goto token_cleanup;

2484 }

2485

2486

2487

2488

2489

2490 if (strcmp(err->error, "slow_down") == 0)

2491 {

2493

2496 {

2497 actx_error(actx, "slow_down interval overflow");

2498 goto token_cleanup;

2499 }

2500 }

2501

2503

2504token_cleanup:

2507}

2508

2509

2510

2511

2512

2513static bool

2515{

2516 int res;

2522 };

2524

2526

2527 if (!res)

2528 {

2529

2530

2531

2532

2535 }

2536 else if (res < 0)

2537 {

2538 actx_error(actx, "device prompt failed");

2539 return false;

2540 }

2541

2542 return true;

2543}

2544

2545

2546

2547

2548

2549

2550

2551

2552

2553

2554

2555

2556

2557

2558static bool

2560{

2561

2562

2563

2564

2565

2566

2568#if HAVE_THREADSAFE_CURL_GLOBAL_INIT

2569 curl_version_info_data *info;

2570#endif

2571

2572#if !HAVE_THREADSAFE_CURL_GLOBAL_INIT

2573

2574

2575

2576

2577

2578

2579

2580

2582#endif

2583

2584

2585

2586

2587

2588

2590 goto done;

2591 else if (init_successful == PG_BOOL_NO)

2592 {

2594 "curl_global_init previously failed during OAuth setup");

2595 goto done;

2596 }

2597

2598

2599

2600

2601

2602

2603

2604

2605

2606

2607

2608

2609

2610 if (curl_global_init(CURL_GLOBAL_ALL & ~CURL_GLOBAL_WIN32) != CURLE_OK)

2611 {

2613 "curl_global_init failed during OAuth setup");

2615 goto done;

2616 }

2617

2618#if HAVE_THREADSAFE_CURL_GLOBAL_INIT

2619

2620

2621

2622

2623

2624

2625

2626

2627 info = curl_version_info(CURLVERSION_NOW);

2628 if (!(info->features & CURL_VERSION_THREADSAFE))

2629 {

2630

2631

2632

2633

2635 "\tCurl initialization was reported thread-safe when libpq\n"

2636 "\twas compiled, but the currently installed version of\n"

2637 "\tlibcurl reports that it is not. Recompile libpq against\n"

2638 "\tthe installed version of libcurl.");

2640 goto done;

2641 }

2642#endif

2643

2645

2646done:

2647#if !HAVE_THREADSAFE_CURL_GLOBAL_INIT

2649#endif

2650 return (init_successful == PG_BOOL_YES);

2651}

2652

2653

2654

2655

2656

2657

2658

2659

2660

2661

2662

2663

2664

2665

2666

2669{

2672 char *oauth_token = NULL;

2674

2677

2678 if (state->async_ctx)

2679 {

2680

2681

2682

2683

2684

2685 actx = calloc(1, sizeof(*actx));

2686 if (!actx)

2687 {

2690 }

2691

2694

2695

2697

2698 state->async_ctx = actx;

2699

2702

2704 goto error_return;

2705

2707 goto error_return;

2708 }

2709

2710 actx = state->async_ctx;

2711

2712 do

2713 {

2714

2716

2717 switch (actx->step)

2718 {

2720 break;

2721

2725 {

2727

2729

2731 goto error_return;

2733 {

2734

2735 return status;

2736 }

2737

2738 break;

2739 }

2740

2742

2743

2744

2745

2746

2747

2748

2750 {

2753 }

2754

2755

2757 goto error_return;

2758

2759 break;

2760 }

2761

2762

2763

2764

2765

2766

2767 switch (actx->step)

2768 {

2770 actx->errctx = "failed to fetch OpenID discovery document";

2772 goto error_return;

2773

2775 break;

2776

2779 goto error_return;

2780

2782 goto error_return;

2783

2784 actx->errctx = "cannot run OAuth device authorization";

2786 goto error_return;

2787

2788 actx->errctx = "failed to obtain device authorization";

2790 goto error_return;

2791

2793 break;

2794

2797 goto error_return;

2798

2799 actx->errctx = "failed to obtain access token";

2801 goto error_return;

2802

2804 break;

2805

2808 goto error_return;

2809

2810

2811

2812

2813

2815

2817 {

2818

2819

2820

2821

2823 goto error_return;

2824

2826 }

2827

2828 if (oauth_token)

2829 break;

2830

2831

2832

2833

2834

2836 goto error_return;

2837

2838

2839

2840

2841

2842

2844

2847 break;

2848

2850 actx->errctx = "failed to obtain access token";

2852 goto error_return;

2853

2855 break;

2856 }

2857

2858

2859

2860

2861

2862

2863 } while (!oauth_token && !actx->running);

2864

2865

2867

2868error_return:

2870

2871

2872

2873

2874

2877

2880 else

2882

2884 {

2886

2887

2889 {

2893 }

2894 }

2895

2897

2899}

2900

2901

2902

2903

2904

2907{

2909#ifndef WIN32

2910 sigset_t osigset;

2911 bool sigpipe_pending;

2912 bool masked;

2913

2914

2915

2916

2917

2918

2919

2920

2921

2922

2923

2924

2925

2926

2927

2928

2929

2930

2931

2932 masked = (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0);

2933#endif

2934

2936

2937#ifndef WIN32

2938 if (masked)

2939 {

2940

2941

2942

2943

2944 pq_reset_sigpipe(&osigset, sigpipe_pending, true );

2945 }

2946#endif

2947

2948 return result;

2949}

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