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

35 char *input, int inputlen,

36 char **output, int *outputlen);

39

45};

46

47

48

49

50

51

52static void *

54 const char *sasl_mechanism)

55{

57

58

59

60

61

62 Assert(sasl_mechanism != NULL);

64

67 return NULL;

68

71

73}

74

75

76

77

78

79

80

81

82

83static void

85{

87

88

90

92}

93

94#define kvsep "\x01"

95

96

97

98

99

100

101

102

103

104

105static char *

107{

108 static const char *const resp_format = "n,," kvsep "auth=%s%s" kvsep kvsep;

109

111 const char *authn_scheme;

112 char *response = NULL;

114

115 if (discover)

116 {

117

118 authn_scheme = token = "";

119 }

120 else

121 {

122

123

124

125

126 authn_scheme = "Bearer ";

127

128

130 {

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

134 return NULL;

135 }

136 }

137

140

142 response = strdup(buf.data);

144

145 if (!response)

147

148 return response;

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

169{

170 char *errmsg;

172 int nested;

173

176

177

181};

182

183#define oauth_json_has_error(ctx) \

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

185

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

187 do { \

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

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

190 } while (0)

191

194{

196

198 {

200

204 }

205

209

211}

212

215{

217

220}

221

224{

226

227

228 if (ctx->nested == 1)

229 {

231 {

234 }

236 {

239 }

241 {

244 }

245 }

246

248}

249

252{

254

256 {

258 }

260 {

262

266 }

267

271

273}

274

277{

279

282}

283

286{

288

290 {

293 }

294

296 {

297 if (ctx->nested != 1)

298 {

299

300

301

302

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

308 }

309

310

311

312

313

315 {

320 }

321

322

324 {

329 }

330

334

337 }

338 else

339 {

340

341 }

342

344}

345

346#define HTTPS_SCHEME "https://"

347#define HTTP_SCHEME "http://"

348

349

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

351#define OPENID_WK_SUFFIX "openid-configuration"

352#define OAUTH_WK_SUFFIX "oauth-authorization-server"

353

354

355

356

357

358static char *

360{

361 const char *authority_start = NULL;

362 const char *wk_start;

363 const char *wk_end;

364 char *issuer;

365 ptrdiff_t start_offset,

366 end_offset;

367 size_t end_len;

368

369

370

371

372

373

374

376 authority_start = wkuri + strlen(HTTPS_SCHEME);

377

378 if (!authority_start

381 {

382

383 authority_start = wkuri + strlen(HTTP_SCHEME);

384 }

385

386 if (!authority_start)

387 {

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

390 wkuri);

391 return NULL;

392 }

393

394

395

396

397

398

399

400

401

402

403 if (strpbrk(authority_start, "?#") != NULL)

404 {

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

407 wkuri);

408 return NULL;

409 }

410

411

412

413

414

415

416

417 wk_start = strstr(authority_start, WK_PREFIX);

418 if (!wk_start)

419 {

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

422 wkuri);

423 return NULL;

424 }

425

426

427

428

429

430 wk_end = wk_start + strlen(WK_PREFIX);

431

436 else

437 wk_end = NULL;

438

439

440

441

442

443

444 if (!wk_end || (*wk_end != '/' && *wk_end != '\0'))

445 {

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

448 wkuri);

449 return NULL;

450 }

451

452

453

454

455

456

457

458 if (*wk_end != '\0')

459 {

460

461

462

463

464 const char *path_start;

465

466 path_start = strchr(authority_start, '/');

467 Assert(path_start);

468

469 if (wk_start != path_start)

470 {

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

473 wkuri);

474 return NULL;

475 }

476 }

477

478

479 issuer = strdup(wkuri);

480 if (!issuer)

481 {

483 return NULL;

484 }

485

486

487

488

489

490

491 start_offset = wk_start - wkuri;

492 end_offset = wk_end - wkuri;

493 end_len = strlen(wk_end) + 1;

494

495 memmove(issuer + start_offset, issuer + end_offset, end_len);

496

497 return issuer;

498}

499

500

501

502

503

504

505static bool

507{

514

516

517

518 if (strlen(msg) != msglen)

519 {

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

522 return false;

523 }

524

525

526

527

528

530 {

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

533 return false;

534 }

535

538

541

548

550

552 {

557 else

558 {

559

560

561

562

564 errmsg = "";

565 }

566 }

569

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

574

575

578

581

583 {

584 char *discovery_issuer;

585

586

587

588

589

590

591

592

593

594

595

597 if (!discovery_issuer)

598 goto cleanup;

599

601 {

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

606

607 free(discovery_issuer);

609 }

610

611 free(discovery_issuer);

612

614 {

617 }

618 else

619 {

620

622 {

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

628 }

629 }

630 }

631

633 {

634

636 {

638 ctx.scope = NULL;

639 }

640 }

641

643 {

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

647 }

648

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

650 {

651

652

653

654

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

659 }

660

662

667

669}

670

671

672

673

674

675

676

677

678

681{

685

686 if (!request->async)

687 {

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

691 }

692

695 {

698 }

700 {

701

702

703

704

705

706 if (!request->token)

707 {

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

711 }

712

715 {

718 }

719

721 }

722

723

725 {

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

729 }

730

732}

733

734

735

736

737

738static void

740{

743

745

749

750 free(request);

751 state->async_ctx = NULL;

752}

753

754

755

756

757

758

759

760

761

762

763

764

765

766

767#if !defined(USE_LIBCURL)

768

769

770

771

772

773bool

775{

776 return false;

777}

778

779#elif defined(USE_DYNAMIC_OAUTH)

780

781

782

783

784

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

786

787

788

789

790

791

792

793#define DEFINE_GETTER(TYPE, MEMBER) \

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

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

796

797

798#define DEFINE_GETTER_P(TYPE, MEMBER) \

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

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

801

802#define DEFINE_SETTER(TYPE, MEMBER) \

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

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

805

806DEFINE_GETTER_P(PQExpBuffer, errorMessage);

807DEFINE_GETTER(char *, oauth_client_id);

808DEFINE_GETTER(char *, oauth_client_secret);

809DEFINE_GETTER(char *, oauth_discovery_uri);

810DEFINE_GETTER(char *, oauth_issuer_id);

811DEFINE_GETTER(char *, oauth_scope);

813

814DEFINE_SETTER(pgsocket, altsock);

815DEFINE_SETTER(char *, oauth_token);

816

817

818

819

820

821

822

823

824

825

826bool

828{

831 int lockerr;

832

835 conn_errorMessage_func errmsg_impl,

836 conn_oauth_client_id_func clientid_impl,

837 conn_oauth_client_secret_func clientsecret_impl,

838 conn_oauth_discovery_uri_func discoveryuri_impl,

839 conn_oauth_issuer_id_func issuerid_impl,

840 conn_oauth_scope_func scope_impl,

841 conn_sasl_state_func saslstate_impl,

842 set_conn_altsock_func setaltsock_impl,

843 set_conn_oauth_token_func settoken_impl);

846

847

848

849

850

851

852

853

854

855

856 const char *const module_name =

857#if defined(__darwin__)

858 LIBDIR "/libpq-oauth-" PG_MAJORVERSION DLSUFFIX;

859#else

860 "libpq-oauth-" PG_MAJORVERSION DLSUFFIX;

861#endif

862

864 if (state->builtin_flow)

865 {

866

867

868

869

870

871

872

874 fprintf(stderr, "failed dlopen for libpq-oauth: %s\n", dlerror());

875

876 return false;

877 }

878

879 if ((init = dlsym(state->builtin_flow, "libpq_oauth_init")) == NULL

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

881 || (cleanup = dlsym(state->builtin_flow, "pg_fe_cleanup_oauth_flow")) == NULL)

882 {

883

884

885

886

888 fprintf(stderr, "failed dlsym for libpq-oauth: %s\n", dlerror());

889

891 return false;

892 }

893

894

895

896

897

898

899

900

901

902

903

904

906 {

907

909

911 return false;

912 }

913

915 {

917#ifdef ENABLE_NLS

919#else

920 NULL,

921#endif

931

933 }

934

936

937

940

941 return true;

942}

943

944#else

945

946

947

948

949

952

953bool

955{

956

959

960 return true;

961}

962

963#endif

964

965

966

967

968

969

970

971

972

973

974

975

976

977

978

979

980

981

982static bool

984{

985 int res;

989 };

990

992

993

995 if (res > 0)

996 {

998

999 if (request.token)

1000 {

1001

1002

1003

1004

1005

1008 {

1010 goto fail;

1011 }

1012

1013

1016 return true;

1017 }

1018

1019 request_copy = malloc(sizeof(*request_copy));

1020 if (!request_copy)

1021 {

1023 goto fail;

1024 }

1025

1026 *request_copy = request;

1027

1030 state->async_ctx = request_copy;

1031 }

1032 else if (res < 0)

1033 {

1035 goto fail;

1036 }

1038 {

1040 goto fail;

1041 }

1042

1043 return true;

1044

1045fail:

1048 return false;

1049}

1050

1051

1052

1053

1054

1055

1056static bool

1058{

1059

1060

1061

1062

1063

1064

1065

1067 return true;

1068

1069

1070

1071

1072

1073

1074

1075

1076

1077

1078

1079

1080

1081

1082

1083

1084

1086 {

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

1089 return false;

1090 }

1091

1092

1093

1094

1095

1097 {

1098

1099

1100

1101

1105 return false;

1106

1109 {

1111 return false;

1112 }

1113 }

1114 else

1115 {

1116

1117

1118

1119

1122 {

1124 return false;

1125 }

1126 }

1127

1128 return true;

1129}

1130

1131

1132

1133

1134

1135

1136

1137

1138

1139

1140

1143 char *input, int inputlen,

1144 char **output, int *outputlen)

1145{

1148 bool discover = false;

1149

1151 *outputlen = 0;

1152

1153 switch (state->step)

1154 {

1156

1157 Assert(inputlen == -1);

1158

1161

1163 {

1164

1165

1166

1167

1168 }

1170 {

1171

1172

1173

1174

1175

1178

1180 {

1181

1182

1183

1184

1185

1186 }

1187 else

1188 {

1189

1190

1191

1192

1193

1194

1195

1196

1197

1198

1199

1200 discover = true;

1201 }

1202 }

1203 else

1204 {

1205

1206

1207

1208

1209

1210 discover = true;

1211 }

1212

1213

1214

1215

1216

1217

1221

1222 *outputlen = strlen(*output);

1224

1226 {

1227

1228

1229

1230

1231

1232

1233

1235 }

1236

1238

1240 if (final)

1241 {

1242

1243

1244

1245

1246

1248 "server sent unexpected additional OAuth data");

1250 }

1251

1252

1253

1254

1255

1258 {

1261 }

1262 *outputlen = strlen(*output);

1263

1264

1267

1269 {

1270

1271

1272

1273

1274

1277 }

1278

1280 {

1281

1282

1283

1284

1286 {

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

1290 }

1291

1292

1295

1297 {

1298

1299

1300

1301

1302 goto reconnect;

1303 }

1304 }

1305

1306

1307

1308

1309

1310

1311

1315

1317

1318

1319

1320

1321

1323 {

1324 Assert(false);

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

1328 }

1329

1330 goto reconnect;

1331

1333

1334

1335

1336

1337

1338

1339

1340

1341

1342

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

1346

1347 default:

1349 break;

1350 }

1351

1352 Assert(false);

1354

1355reconnect:

1356

1357

1358

1359

1360

1364}

1365

1366static bool

1368{

1369

1370 return false;

1371}

1372

1373

1374

1375

1376

1377void

1379{

1381 return;

1382

1386}

1387

1388

1389

1390

1391bool

1393{

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

1395

1396 return (env && strcmp(env, "UNSAFE") == 0);

1397}

static void cleanup(void)

#define fprintf(file, fmt, msg)

int errmsg(const char *fmt,...)

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 bool setup_oauth_parameters(PGconn *conn)

#define oauth_json_set_error(ctx,...)

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

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

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)

void(* pgthreadlock_t)(int acquire)

PostgresPollingStatusType

@ PQAUTHDATA_OAUTH_BEARER_TOKEN

PostgresPollingStatusType pg_fe_run_oauth_flow(PGconn *conn)

void pg_fe_cleanup_oauth_flow(PGconn *conn)

set_conn_oauth_token_func set_conn_oauth_token

conn_oauth_client_secret_func conn_oauth_client_secret

conn_sasl_state_func conn_sasl_state

conn_oauth_client_id_func conn_oauth_client_id

conn_oauth_scope_func conn_oauth_scope

pgthreadlock_t pg_g_threadlock

conn_oauth_issuer_id_func conn_oauth_issuer_id

set_conn_altsock_func set_conn_altsock

conn_oauth_discovery_uri_func conn_oauth_discovery_uri

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

conn_errorMessage_func conn_errorMessage

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

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

void(* cleanup)(PGconn *conn, struct PGoauthBearerRequest *request)

const char * openid_configuration

PostgresPollingStatusType(* async)(PGconn *conn, struct PGoauthBearerRequest *request, SOCKTYPE *altsock)

const char * target_field_name

char * oauth_discovery_uri

void(* cleanup_async_auth)(PGconn *conn)

bool client_finished_auth

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)