PostgreSQL Source Code: src/interfaces/libpq/fe-auth-oauth.c Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

17

18#ifdef USE_DYNAMIC_OAUTH

20#endif

21

29#include "pg_config_paths.h"

30

31

33 const char *sasl_mechanism);

39

46

47

48

49

50

51

52static void *

54 const char *sasl_mechanism)

55{

57

58

59

60

61

64

68

71

73}

74

75

76

77

78

79

80

81

82

83static void

93

94#define kvsep "\x01"

95

96

97

98

99

100

101

102

103

104

105static char *

107{

109

114

116 {

117

119 }

120 else

121 {

122

123

124

125

127

128

130 {

133 "internal error: no OAuth token was set for the connection");

135 }

136 }

137

140

144

147

149}

150

151

152

153

154

155

156#define ERROR_STATUS_FIELD "status"

157#define ERROR_SCOPE_FIELD "scope"

158#define ERROR_OPENID_CONFIGURATION_FIELD "openid-configuration"

159

160

161

162

163

164

165

166#define MAX_SASL_NESTING_LEVEL 8

167

182

183#define oauth_json_has_error(ctx) \

184 (PQExpBufferDataBroken((ctx)->errbuf) || (ctx)->errmsg)

185

186#define oauth_json_set_error(ctx, fmt, ...) \

187 do { \

188 appendPQExpBuffer(&(ctx)->errbuf, libpq_gettext(fmt), ##__VA_ARGS__); \

189 (ctx)->errmsg = (ctx)->errbuf.data; \

190 } while (0)

191

192

193#define oauth_json_set_error_internal(ctx, ...) \

194 do { \

195 appendPQExpBuffer(&(ctx)->errbuf, __VA_ARGS__); \

196 (ctx)->errmsg = (ctx)->errbuf.data; \

197 } while (0)

198

201{

203

205 {

207

209 "field \"%s\" must be a string",

211 }

212

216

218}

219

228

231{

233

234

235 if (ctx->nested == 1)

236 {

238 {

241 }

243 {

246 }

248 {

251 }

252 }

253

255}

256

259{

261

263 {

265 }

267 {

269

271 "field \"%s\" must be a string",

273 }

274

278

280}

281

290

293{

295

297 {

300 }

301

303 {

304 if (ctx->nested != 1)

305 {

306

307

308

309

312 "internal error: target scalar found at nesting level %d during OAUTHBEARER parsing",

315 }

316

317

318

319

320

322 {

324 "field \"%s\" is duplicated",

327 }

328

329

331 {

333 "field \"%s\" must be a string",

336 }

337

341

344 }

345 else

346 {

347

348 }

349

351}

352

353#define HTTPS_SCHEME "https://"

354#define HTTP_SCHEME "http://"

355

356

357#define WK_PREFIX "/.well-known/"

358#define OPENID_WK_SUFFIX "openid-configuration"

359#define OAUTH_WK_SUFFIX "oauth-authorization-server"

360

361

362

363

364

365static char *

367{

371 char *issuer;

375

376

377

378

379

380

381

384

388 {

389

391 }

392

394 {

396 "OAuth discovery URI \"%s\" must use HTTPS",

399 }

400

401

402

403

404

405

406

407

408

409

411 {

413 "OAuth discovery URI \"%s\" must not contain query or fragment components",

416 }

417

418

419

420

421

422

423

426 {

428 "OAuth discovery URI \"%s\" is not a .well-known URI",

431 }

432

433

434

435

436

438

443 else

445

446

447

448

449

450

452 {

454 "OAuth discovery URI \"%s\" uses an unsupported .well-known suffix",

457 }

458

459

460

461

462

463

464

466 {

467

468

469

470

472

474 Assert(path_start);

475

477 {

479 "OAuth discovery URI \"%s\" uses an invalid format",

482 }

483 }

484

485

487 if (!issuer)

488 {

491 }

492

493

494

495

496

497

501

503

504 return issuer;

505}

506

507

508

509

510

511

512static bool

514{

521

523

524

526 {

528 "server's error message contained an embedded NULL, and was discarded");

529 return false;

530 }

531

532

533

534

535

537 {

539 "server's error response is not valid UTF-8");

540 return false;

541 }

542

545

548

555

557

559 {

564 else

565 {

566

567

568

569

571 errmsg = "";

572 }

573 }

576

579 "failed to parse server's error response: %s",

581

582

585

588

590 {

592

593

594

595

596

597

598

599

600

601

602

605 goto cleanup;

606

608 {

610 "server's discovery document at %s (issuer \"%s\") is incompatible with oauth_issuer (%s)",

613

616 }

617

619

621 {

624 }

625 else

626 {

627

629 {

631 "server's discovery document has moved to %s (previous location was %s)",

635 }

636 }

637 }

638

640 {

641

643 {

646 }

647 }

648

650 {

652 "server sent error response without a status");

654 }

655

656 if (strcmp(ctx.status, "invalid_token") != 0)

657 {

658

659

660

661

663 "server rejected OAuth bearer token: %s",

666 }

667

669

674

676}

677

678

679

680

681

682static void

684{

686 libpq_gettext("user-defined OAuth flow failed"));

687

689 {

692 }

693

695}

696

697

698

699

700

701

702

703

704

707{

711

713 {

715 "user-defined OAuth flow provided neither a token nor an async callback");

717 }

718

722

724 {

727 }

729 {

730

731

732

733

734

736 {

738 "user-defined OAuth flow did not provide a token");

740 }

741

744 {

747 }

748

750 }

751

752

754 {

756 "user-defined OAuth flow did not provide a socket for polling");

758 }

759

761}

762

763

764

765

766

767

768static void

783

784

785

786

787

788

789

790

791

792

793

794

795

796

797#if !defined(USE_LIBCURL)

798

799

800

801

802

803bool

805{

806 return false;

807}

808

809#elif defined(USE_DYNAMIC_OAUTH)

810

811

812

813

814

815typedef char *(*libpq_gettext_func) (const char *msgid);

816

817

818

819

820

821

822

823#define DEFINE_GETTER(TYPE, MEMBER) \

824 typedef TYPE (*conn_ ## MEMBER ## _func) (PGconn *conn); \

825 static TYPE conn_ ## MEMBER(PGconn *conn) { return conn->MEMBER; }

826

827

828#define DEFINE_GETTER_P(TYPE, MEMBER) \

829 typedef TYPE (*conn_ ## MEMBER ## _func) (PGconn *conn); \

830 static TYPE conn_ ## MEMBER(PGconn *conn) { return &conn->MEMBER; }

831

832#define DEFINE_SETTER(TYPE, MEMBER) \

833 typedef void (*set_conn_ ## MEMBER ## _func) (PGconn *conn, TYPE val); \

834 static void set_conn_ ## MEMBER(PGconn *conn, TYPE val) { conn->MEMBER = val; }

835

843

846

847

848

849

850

851

852

853

854

855

856bool

858{

862

876

877

878

879

880

881

882

883

884

885

887#if defined(__darwin__)

889#else

891#endif

892

894 if (state->builtin_flow)

895 {

896

897

898

899

900

901

902

905

906 return false;

907 }

908

910 || (flow = dlsym(state->builtin_flow, "pg_fe_run_oauth_flow")) == NULL

912 {

913

914

915

916

919

921 return false;

922 }

923

924

925

926

927

928

929

930

931

932

933

934

936 {

937

939

941 return false;

942 }

943

945 {

949#else

961

963 }

964

966

967

970

971 return true;

972}

973

974#else

975

976

977

978

979

982

983bool

985{

986

989

990 return true;

991}

992

993#endif

994

995

996

997

998

999

1000

1001

1002

1003

1004

1005

1006

1007

1008

1009

1010

1011

1012static bool

1014{

1015 int res;

1017 .v1 = {

1020 },

1022 };

1023

1026

1027

1028

1029

1030

1032 if (res == 0)

1034

1035 if (res > 0)

1036 {

1038

1040 {

1041

1042

1043

1044

1045

1048 {

1051 }

1052

1053

1056 return true;

1057 }

1058

1061 {

1064 }

1065

1067

1071 }

1072 else if (res < 0)

1073 {

1076 }

1078 {

1081 }

1082

1083 return true;

1084

1088 return false;

1089}

1090

1091

1092

1093

1094

1095

1096static bool

1098{

1099

1100

1101

1102

1103

1104

1105

1107 return true;

1108

1109

1110

1111

1112

1113

1114

1115

1116

1117

1118

1119

1120

1121

1122

1123

1124

1126 {

1128 "server requires OAuth authentication, but oauth_issuer and oauth_client_id are not both set");

1129 return false;

1130 }

1131

1132

1133

1134

1135

1137 {

1138

1139

1140

1141

1145 return false;

1146

1149 {

1151 return false;

1152 }

1153 }

1154 else

1155 {

1156

1157

1158

1159

1162 {

1164 return false;

1165 }

1166 }

1167

1168 return true;

1169}

1170

1171

1172

1173

1174

1175

1176

1177

1178

1179

1180

1185{

1189

1192

1193 switch (state->step)

1194 {

1196

1198

1201

1203 {

1204

1205

1206

1207

1208 }

1210 {

1211

1212

1213

1214

1215

1218

1220 {

1221

1222

1223

1224

1225

1226 }

1227 else

1228 {

1229

1230

1231

1232

1233

1234

1235

1236

1237

1238

1239

1241 }

1242 }

1243 else

1244 {

1245

1246

1247

1248

1249

1251 }

1252

1253

1254

1255

1256

1257

1261

1264

1266 {

1267

1268

1269

1270

1271

1272

1273

1275 }

1276

1278

1280 if (final)

1281 {

1282

1283

1284

1285

1286

1288 "server sent unexpected additional OAuth data");

1290 }

1291

1292

1293

1294

1295

1298 {

1301 }

1303

1304

1307

1309 {

1310

1311

1312

1313

1314

1317 }

1318

1320 {

1321

1322

1323

1324

1326 {

1328 "server requires OAuth authentication, but no discovery metadata was provided");

1330 }

1331

1332

1335

1337 {

1338

1339

1340

1341

1343 }

1344 }

1345

1346

1347

1348

1349

1350

1351

1355

1357

1358

1359

1360

1361

1363 {

1364 Assert(false);

1366 "internal error: OAuth flow did not set a token");

1368 }

1369

1371

1373

1374

1375

1376

1377

1378

1379

1380

1381

1382

1384 "server sent additional OAuth data after error");

1386

1387 default:

1389 break;

1390 }

1391

1392 Assert(false);

1394

1396

1397

1398

1399

1400

1404}

1405

1406static bool

1408{

1409

1410 return false;

1411}

1412

1413

1414

1415

1416

1417void

1419{

1421 return;

1422

1426}

1427

1428

1429

1430

1431bool

1433{

1434 const char *env = getenv("PGOAUTHDEBUG");

1435

1437}

static void cleanup(void)

#define Assert(condition)

#define fprintf(file, fmt, msg)

void err(int eval, const char *fmt,...)

#define ERROR_SCOPE_FIELD

static bool setup_token_request(PGconn *conn, fe_oauth_state *state)

static JsonParseErrorType oauth_json_array_end(void *state)

static char * issuer_from_well_known_uri(PGconn *conn, const char *wkuri)

static bool handle_oauth_sasl_error(PGconn *conn, const char *msg, int msglen)

static void cleanup_user_oauth_flow(PGconn *conn)

static void report_user_flow_error(PGconn *conn, const PGoauthBearerRequestV2 *request)

#define oauth_json_set_error(ctx, fmt,...)

static bool setup_oauth_parameters(PGconn *conn)

static JsonParseErrorType oauth_json_object_field_start(void *state, char *name, bool isnull)

static JsonParseErrorType oauth_json_scalar(void *state, char *token, JsonTokenType type)

const pg_fe_sasl_mech pg_oauth_mech

static SASLStatus oauth_exchange(void *opaq, bool final, char *input, int inputlen, char **output, int *outputlen)

static bool oauth_channel_bound(void *opaq)

#define oauth_json_has_error(ctx)

static JsonParseErrorType oauth_json_array_start(void *state)

static JsonParseErrorType oauth_json_object_end(void *state)

static void oauth_free(void *opaq)

#define ERROR_OPENID_CONFIGURATION_FIELD

#define oauth_json_set_error_internal(ctx,...)

void pqClearOAuthToken(PGconn *conn)

static void * oauth_init(PGconn *conn, const char *password, const char *sasl_mechanism)

static char * client_initial_response(PGconn *conn, bool discover)

bool use_builtin_flow(PGconn *conn, fe_oauth_state *state)

#define ERROR_STATUS_FIELD

#define MAX_SASL_NESTING_LEVEL

static JsonParseErrorType oauth_json_object_start(void *state)

static PostgresPollingStatusType run_user_oauth_flow(PGconn *conn)

bool oauth_unsafe_debugging_enabled(void)

@ FE_OAUTH_REQUESTING_TOKEN

PQauthDataHook_type PQauthDataHook

JsonParseErrorType pg_parse_json(JsonLexContext *lex, const JsonSemAction *sem)

JsonLexContext * makeJsonLexContextCstringLen(JsonLexContext *lex, const char *json, size_t len, int encoding, bool need_escapes)

void setJsonLexContextOwnsTokens(JsonLexContext *lex, bool owned_by_context)

char * json_errdetail(JsonParseErrorType error, JsonLexContext *lex)

void freeJsonLexContext(JsonLexContext *lex)

void(* pgthreadlock_t)(int acquire)

PostgresPollingStatusType

@ PQAUTHDATA_OAUTH_BEARER_TOKEN

@ PQAUTHDATA_OAUTH_BEARER_TOKEN_V2

PostgresPollingStatusType pg_fe_run_oauth_flow(PGconn *conn)

void pg_fe_cleanup_oauth_flow(PGconn *conn)

#define conn_oauth_issuer_id(CONN)

#define conn_oauth_discovery_uri(CONN)

#define conn_oauth_scope(CONN)

#define conn_oauth_client_id(CONN)

#define set_conn_altsock(CONN, VAL)

#define conn_oauth_client_secret(CONN)

#define set_conn_oauth_token(CONN, VAL)

#define conn_sasl_state(CONN)

#define conn_errorMessage(CONN)

pgthreadlock_t pg_g_threadlock

void libpq_append_conn_error(PGconn *conn, const char *fmt,...)

char *(* libpq_gettext_func)(const char *msgid)

static char buf[DEFAULT_XLOG_SEG_SIZE]

void explicit_bzero(void *buf, size_t len)

int pg_strncasecmp(const char *s1, const char *s2, size_t n)

void initPQExpBuffer(PQExpBuffer str)

void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)

void appendPQExpBufferChar(PQExpBuffer str, char ch)

void appendPQExpBufferStr(PQExpBuffer str, const char *data)

void termPQExpBuffer(PQExpBuffer str)

#define PQExpBufferDataBroken(buf)

int pthread_mutex_unlock(pthread_mutex_t *mp)

int pthread_mutex_lock(pthread_mutex_t *mp)

#define PTHREAD_MUTEX_INITIALIZER

json_struct_action array_end

json_struct_action object_start

json_ofield_action object_field_start

json_scalar_action scalar

json_struct_action array_start

json_struct_action object_end

const char * openid_configuration

const char * target_field_name

char * oauth_discovery_uri

void(* cleanup_async_auth)(PGconn *conn)

bool client_finished_auth

PQExpBufferData errorMessage

PostgresPollingStatusType(* async_auth)(PGconn *conn)

int pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)

void * dlopen(const char *file, int mode)

void * dlsym(void *handle, const char *symbol)

int dlclose(void *handle)