@@ -184,6 +184,7 @@ static void postgres_fdw_get_connections_internal(FunctionCallInfo fcinfo,
184
184
enum pgfdwVersion api_version );
185
185
static int pgfdw_conn_check (PGconn * conn );
186
186
static bool pgfdw_conn_checkable (void );
187
+ static bool pgfdw_has_required_scram_options (const char * * keywords , const char * * values );
187
188
188
189
/*
189
190
* Get a PGconn which can be used to execute queries on the remote PostgreSQL
@@ -455,6 +456,15 @@ pgfdw_security_check(const char **keywords, const char **values, UserMapping *us
455
456
}
456
457
}
457
458
459
+ /*
460
+ * Ok if SCRAM pass-through is being used and all required SCRAM options
461
+ * are set correctly. If pgfdw_has_required_scram_options returns true we
462
+ * assume that UseScramPassthrough is also true since SCRAM options are
463
+ * only set when UseScramPassthrough is enabled.
464
+ */
465
+ if (MyProcPort -> has_scram_keys && pgfdw_has_required_scram_options (keywords , values ))
466
+ return ;
467
+
458
468
ereport (ERROR ,
459
469
(errcode (ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED ),
460
470
errmsg ("password or GSSAPI delegated credentials required" ),
@@ -485,9 +495,10 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
485
495
* and UserMapping. (Some of them might not be libpq options, in
486
496
* which case we'll just waste a few array slots.) Add 4 extra slots
487
497
* for application_name, fallback_application_name, client_encoding,
488
- * end marker.
498
+ * end marker, and 3 extra slots for scram keys and required scram
499
+ * pass-through options.
489
500
*/
490
- n = list_length (server -> options ) + list_length (user -> options ) + 4 + 2 ;
501
+ n = list_length (server -> options ) + list_length (user -> options ) + 4 + 3 ;
491
502
keywords = (const char * * ) palloc (n * sizeof (char * ));
492
503
values = (const char * * ) palloc (n * sizeof (char * ));
493
504
@@ -556,6 +567,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
556
567
values [n ] = GetDatabaseEncodingName ();
557
568
n ++ ;
558
569
570
+ /* Add required SCRAM pass-through connection options if it's enabled. */
559
571
if (MyProcPort -> has_scram_keys && UseScramPassthrough (server , user ))
560
572
{
561
573
int len ;
@@ -582,16 +594,20 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
582
594
if (encoded_len < 0 )
583
595
elog (ERROR , "could not encode SCRAM server key" );
584
596
n ++ ;
597
+
598
+ /*
599
+ * Require scram-sha-256 to ensure that no other auth method is
600
+ * used when connecting with foreign server.
601
+ */
602
+ keywords [n ] = "require_auth" ;
603
+ values [n ] = "scram-sha-256" ;
604
+ n ++ ;
585
605
}
586
606
587
607
keywords [n ] = values [n ] = NULL ;
588
608
589
- /*
590
- * Verify the set of connection parameters only if scram pass-through
591
- * is not being used because the password is not necessary.
592
- */
593
- if (!(MyProcPort -> has_scram_keys && UseScramPassthrough (server , user )))
594
- check_conn_params (keywords , values , user );
609
+ /* Verify the set of connection parameters. */
610
+ check_conn_params (keywords , values , user );
595
611
596
612
/* first time, allocate or get the custom wait event */
597
613
if (pgfdw_we_connect == 0 )
@@ -609,12 +625,8 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
609
625
server -> servername ),
610
626
errdetail_internal ("%s" , pchomp (PQerrorMessage (conn )))));
611
627
612
- /*
613
- * Perform post-connection security checks only if scram pass-through
614
- * is not being used because the password is not necessary.
615
- */
616
- if (!(MyProcPort -> has_scram_keys && UseScramPassthrough (server , user )))
617
- pgfdw_security_check (keywords , values , user , conn );
628
+ /* Perform post-connection security checks. */
629
+ pgfdw_security_check (keywords , values , user , conn );
618
630
619
631
/* Prepare new session for use */
620
632
configure_remote_session (conn );
@@ -725,6 +737,15 @@ check_conn_params(const char **keywords, const char **values, UserMapping *user)
725
737
if (!UserMappingPasswordRequired (user ))
726
738
return ;
727
739
740
+ /*
741
+ * Ok if SCRAM pass-through is being used and all required scram options
742
+ * are set correctly. If pgfdw_has_required_scram_options returns true we
743
+ * assume that UseScramPassthrough is also true since SCRAM options are
744
+ * only set when UseScramPassthrough is enabled.
745
+ */
746
+ if (MyProcPort -> has_scram_keys && pgfdw_has_required_scram_options (keywords , values ))
747
+ return ;
748
+
728
749
ereport (ERROR ,
729
750
(errcode (ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED ),
730
751
errmsg ("password or GSSAPI delegated credentials required" ),
@@ -2487,3 +2508,56 @@ pgfdw_conn_checkable(void)
2487
2508
return false;
2488
2509
#endif
2489
2510
}
2511
+
2512
+ /*
2513
+ * Ensure that require_auth and SCRAM keys are correctly set on values. SCRAM
2514
+ * keys used to pass-through are coming from the initial connection from the
2515
+ * client with the server.
2516
+ *
2517
+ * All required SCRAM options are set by postgres_fdw, so we just need to
2518
+ * ensure that these options are not overwritten by the user.
2519
+ */
2520
+ static bool
2521
+ pgfdw_has_required_scram_options (const char * * keywords , const char * * values )
2522
+ {
2523
+ bool has_scram_server_key = false;
2524
+ bool has_scram_client_key = false;
2525
+ bool has_require_auth = false;
2526
+ bool has_scram_keys = false;
2527
+
2528
+ /*
2529
+ * Continue iterating even if we found the keys that we need to validate
2530
+ * to make sure that there is no other declaration of these keys that can
2531
+ * overwrite the first.
2532
+ */
2533
+ for (int i = 0 ; keywords [i ] != NULL ; i ++ )
2534
+ {
2535
+ if (strcmp (keywords [i ], "scram_client_key" ) == 0 )
2536
+ {
2537
+ if (values [i ] != NULL && values [i ][0 ] != '\0' )
2538
+ has_scram_client_key = true;
2539
+ else
2540
+ has_scram_client_key = false;
2541
+ }
2542
+
2543
+ if (strcmp (keywords [i ], "scram_server_key" ) == 0 )
2544
+ {
2545
+ if (values [i ] != NULL && values [i ][0 ] != '\0' )
2546
+ has_scram_server_key = true;
2547
+ else
2548
+ has_scram_server_key = false;
2549
+ }
2550
+
2551
+ if (strcmp (keywords [i ], "require_auth" ) == 0 )
2552
+ {
2553
+ if (values [i ] != NULL && strcmp (values [i ], "scram-sha-256" ) == 0 )
2554
+ has_require_auth = true;
2555
+ else
2556
+ has_require_auth = false;
2557
+ }
2558
+ }
2559
+
2560
+ has_scram_keys = has_scram_client_key && has_scram_server_key && MyProcPort -> has_scram_keys ;
2561
+
2562
+ return (has_scram_keys && has_require_auth );
2563
+ }
0 commit comments