|
48 | 48 | #include "funcapi.h"
|
49 | 49 | #include "lib/stringinfo.h"
|
50 | 50 | #include "libpq-fe.h"
|
| 51 | +#include "libpq/libpq-be.h" |
51 | 52 | #include "libpq/libpq-be-fe-helpers.h"
|
52 | 53 | #include "mb/pg_wchar.h"
|
53 | 54 | #include "miscadmin.h"
|
@@ -111,7 +112,8 @@ static HeapTuple get_tuple_of_interest(Relation rel, int *pkattnums, int pknumat
|
111 | 112 | static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode);
|
112 | 113 | static char *generate_relation_name(Relation rel);
|
113 | 114 | static void dblink_connstr_check(const char *connstr);
|
114 |
| -static void dblink_security_check(PGconn *conn, remoteConn *rconn); |
| 115 | +static bool dblink_connstr_has_pw(const char *connstr); |
| 116 | +static void dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr); |
115 | 117 | static void dblink_res_error(PGconn *conn, const char *conname, PGresult *res,
|
116 | 118 | bool fail, const char *fmt,...) pg_attribute_printf(5, 6);
|
117 | 119 | static char *get_connect_string(const char *servername);
|
@@ -213,7 +215,7 @@ dblink_get_conn(char *conname_or_str,
|
213 | 215 | errmsg("could not establish connection"),
|
214 | 216 | errdetail_internal("%s", msg)));
|
215 | 217 | }
|
216 |
| - dblink_security_check(conn, rconn); |
| 218 | + dblink_security_check(conn, rconn, connstr); |
217 | 219 | if (PQclientEncoding(conn) != GetDatabaseEncoding())
|
218 | 220 | PQsetClientEncoding(conn, GetDatabaseEncodingName());
|
219 | 221 | freeconn = true;
|
@@ -307,7 +309,7 @@ dblink_connect(PG_FUNCTION_ARGS)
|
307 | 309 | }
|
308 | 310 |
|
309 | 311 | /* check password actually used if not superuser */
|
310 |
| - dblink_security_check(conn, rconn); |
| 312 | + dblink_security_check(conn, rconn, connstr); |
311 | 313 |
|
312 | 314 | /* attempt to set client encoding to match server encoding, if needed */
|
313 | 315 | if (PQclientEncoding(conn) != GetDatabaseEncoding())
|
@@ -2584,64 +2586,99 @@ deleteConnection(const char *name)
|
2584 | 2586 | errmsg("undefined connection name")));
|
2585 | 2587 | }
|
2586 | 2588 |
|
| 2589 | +/* |
| 2590 | + * We need to make sure that the connection made used credentials |
| 2591 | + * which were provided by the user, so check what credentials were |
| 2592 | + * used to connect and then make sure that they came from the user. |
| 2593 | + */ |
2587 | 2594 | static void
|
2588 |
| -dblink_security_check(PGconn *conn, remoteConn *rconn) |
| 2595 | +dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr) |
2589 | 2596 | {
|
2590 |
| - if (!superuser()) |
2591 |
| - { |
2592 |
| - if (!PQconnectionUsedPassword(conn)) |
2593 |
| - { |
2594 |
| - libpqsrv_disconnect(conn); |
2595 |
| - if (rconn) |
2596 |
| - pfree(rconn); |
| 2597 | + /* Superuser bypasses security check */ |
| 2598 | + if (superuser()) |
| 2599 | + return; |
2597 | 2600 |
|
2598 |
| - ereport(ERROR, |
2599 |
| - (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), |
2600 |
| - errmsg("password is required"), |
2601 |
| - errdetail("Non-superuser cannot connect if the server does not request a password."), |
2602 |
| - errhint("Target server's authentication method must be changed."))); |
2603 |
| - } |
2604 |
| - } |
| 2601 | + /* If password was used to connect, make sure it was one provided */ |
| 2602 | + if (PQconnectionUsedPassword(conn) && dblink_connstr_has_pw(connstr)) |
| 2603 | + return; |
| 2604 | + |
| 2605 | +#ifdef ENABLE_GSS |
| 2606 | + /* If GSSAPI creds used to connect, make sure it was one delegated */ |
| 2607 | + if (PQconnectionUsedGSSAPI(conn) && be_gssapi_get_deleg(MyProcPort)) |
| 2608 | + return; |
| 2609 | +#endif |
| 2610 | + |
| 2611 | + /* Otherwise, fail out */ |
| 2612 | + libpqsrv_disconnect(conn); |
| 2613 | + if (rconn) |
| 2614 | + pfree(rconn); |
| 2615 | + |
| 2616 | + ereport(ERROR, |
| 2617 | + (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), |
| 2618 | + errmsg("password or GSSAPI delegated credentials required"), |
| 2619 | + errdetail("Non-superusers may only connect using credentials they provide, eg: password in connection string or delegated GSSAPI credentials"), |
| 2620 | + errhint("Ensure provided credentials match target server's authentication method."))); |
2605 | 2621 | }
|
2606 | 2622 |
|
2607 | 2623 | /*
|
2608 |
| - * For non-superusers, insist that the connstr specify a password. This |
2609 |
| - * prevents a password from being picked up from .pgpass, a service file, |
2610 |
| - * the environment, etc. We don't want the postgres user's passwords |
2611 |
| - * to be accessible to non-superusers. |
| 2624 | + * Function to check if the connection string includes an explicit |
| 2625 | + * password, needed to ensure that non-superuser password-based auth |
| 2626 | + * is using a provided password and not one picked up from the |
| 2627 | + * environment. |
2612 | 2628 | */
|
2613 |
| -static void |
2614 |
| -dblink_connstr_check(const char *connstr) |
| 2629 | +static bool |
| 2630 | +dblink_connstr_has_pw(const char *connstr) |
2615 | 2631 | {
|
2616 |
| - if (!superuser()) |
2617 |
| - { |
2618 |
| - PQconninfoOption *options; |
2619 |
| - PQconninfoOption *option; |
2620 |
| - bool connstr_gives_password = false; |
| 2632 | + PQconninfoOption *options; |
| 2633 | + PQconninfoOption *option; |
| 2634 | + bool connstr_gives_password = false; |
2621 | 2635 |
|
2622 |
| - options = PQconninfoParse(connstr, NULL); |
2623 |
| - if (options) |
| 2636 | + options = PQconninfoParse(connstr, NULL); |
| 2637 | + if (options) |
| 2638 | + { |
| 2639 | + for (option = options; option->keyword != NULL; option++) |
2624 | 2640 | {
|
2625 |
| - for (option = options; option->keyword != NULL; option++) |
| 2641 | + if (strcmp(option->keyword, "password") == 0) |
2626 | 2642 | {
|
2627 |
| - if (strcmp(option->keyword, "password") == 0) |
| 2643 | + if (option->val != NULL && option->val[0] != '\0') |
2628 | 2644 | {
|
2629 |
| - if (option->val != NULL && option->val[0] != '\0') |
2630 |
| - { |
2631 |
| - connstr_gives_password = true; |
2632 |
| - break; |
2633 |
| - } |
| 2645 | + connstr_gives_password = true; |
| 2646 | + break; |
2634 | 2647 | }
|
2635 | 2648 | }
|
2636 |
| - PQconninfoFree(options); |
2637 | 2649 | }
|
2638 |
| - |
2639 |
| - if (!connstr_gives_password) |
2640 |
| - ereport(ERROR, |
2641 |
| - (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), |
2642 |
| - errmsg("password is required"), |
2643 |
| - errdetail("Non-superusers must provide a password in the connection string."))); |
| 2650 | + PQconninfoFree(options); |
2644 | 2651 | }
|
| 2652 | + |
| 2653 | + return connstr_gives_password; |
| 2654 | +} |
| 2655 | + |
| 2656 | +/* |
| 2657 | + * For non-superusers, insist that the connstr specify a password, except |
| 2658 | + * if GSSAPI credentials have been delegated (and we check that they are used |
| 2659 | + * for the connection in dblink_security_check later). This prevents a |
| 2660 | + * password or GSSAPI credentials from being picked up from .pgpass, a |
| 2661 | + * service file, the environment, etc. We don't want the postgres user's |
| 2662 | + * passwords or Kerberos credentials to be accessible to non-superusers. |
| 2663 | + */ |
| 2664 | +static void |
| 2665 | +dblink_connstr_check(const char *connstr) |
| 2666 | +{ |
| 2667 | + if (superuser()) |
| 2668 | + return; |
| 2669 | + |
| 2670 | + if (dblink_connstr_has_pw(connstr)) |
| 2671 | + return; |
| 2672 | + |
| 2673 | +#ifdef ENABLE_GSS |
| 2674 | + if (be_gssapi_get_deleg(MyProcPort)) |
| 2675 | + return; |
| 2676 | +#endif |
| 2677 | + |
| 2678 | + ereport(ERROR, |
| 2679 | + (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), |
| 2680 | + errmsg("password or GSSAPI delegated credentials required"), |
| 2681 | + errdetail("Non-superusers must provide a password in the connection string or send delegated GSSAPI credentials."))); |
2645 | 2682 | }
|
2646 | 2683 |
|
2647 | 2684 | /*
|
|
0 commit comments