Skip to content

Commit b5b1240

Browse files
committed
Match pg_user_mappings limits to information_schema.user_mapping_options.
Both views replace the umoptions field with NULL when the user does not meet qualifications to see it. They used different qualifications, and pg_user_mappings documented qualifications did not match its implemented qualifications. Make its documentation and implementation match those of user_mapping_options. One might argue for stronger qualifications, but these have long, documented tenure. pg_user_mappings has always exhibited this problem, so back-patch to 9.2 (all supported versions). Michael Paquier and Feike Steenbergen. Reviewed by Jeff Janes. Reported by Andrew Wheelwright. Security: CVE-2017-7486
1 parent 3eab811 commit b5b1240

File tree

5 files changed

+82
-8
lines changed

5 files changed

+82
-8
lines changed

doc/src/sgml/catalogs.sgml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9212,8 +9212,11 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
92129212
<entry></entry>
92139213
<entry>
92149214
User mapping specific options, as <quote>keyword=value</>
9215-
strings, if the current user is the owner of the foreign
9216-
server, else null
9215+
strings. This column will show as null unless the current user
9216+
is the user being mapped, or the mapping is for
9217+
<literal>PUBLIC</literal> and the current user is the server
9218+
owner, or the current user is a superuser. The intent is
9219+
to protect password information stored as user mapping option.
92179220
</entry>
92189221
</row>
92199222
</tbody>

src/backend/catalog/system_views.sql

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -696,11 +696,11 @@ CREATE VIEW pg_user_mappings AS
696696
ELSE
697697
A.rolname
698698
END AS usename,
699-
CASE WHEN pg_has_role(S.srvowner, 'USAGE') OR has_server_privilege(S.oid, 'USAGE') THEN
700-
U.umoptions
701-
ELSE
702-
NULL
703-
END AS umoptions
699+
CASE WHEN (U.umuser <> 0 AND A.rolname = current_user)
700+
OR (U.umuser = 0 AND pg_has_role(S.srvowner, 'USAGE'))
701+
OR (SELECT rolsuper FROM pg_authid WHERE rolname = current_user)
702+
THEN U.umoptions
703+
ELSE NULL END AS umoptions
704704
FROM pg_user_mapping U
705705
LEFT JOIN pg_authid A ON (A.oid = U.umuser) JOIN
706706
pg_foreign_server S ON (U.umserver = S.oid);

src/test/regress/expected/foreign_data.out

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,7 +1157,61 @@ WARNING: no privileges were granted for "s9"
11571157
CREATE USER MAPPING FOR current_user SERVER s9;
11581158
DROP SERVER s9 CASCADE; -- ERROR
11591159
ERROR: must be owner of foreign server s9
1160+
-- Check visibility of user mapping data
1161+
SET ROLE regress_test_role;
1162+
CREATE SERVER s10 FOREIGN DATA WRAPPER foo;
1163+
CREATE USER MAPPING FOR public SERVER s10 OPTIONS (user 'secret');
1164+
GRANT USAGE ON FOREIGN SERVER s10 TO unprivileged_role;
1165+
-- owner of server can see option fields
1166+
\deu+
1167+
List of user mappings
1168+
Server | User name | FDW Options
1169+
--------+-------------------+-------------------
1170+
s10 | public | ("user" 'secret')
1171+
s4 | foreign_data_user |
1172+
s5 | regress_test_role | (modified '1')
1173+
s6 | regress_test_role |
1174+
s8 | foreign_data_user |
1175+
s8 | public |
1176+
s9 | unprivileged_role |
1177+
t1 | public | (modified '1')
1178+
(8 rows)
1179+
1180+
RESET ROLE;
1181+
-- superuser can see option fields
1182+
\deu+
1183+
List of user mappings
1184+
Server | User name | FDW Options
1185+
--------+-------------------+---------------------
1186+
s10 | public | ("user" 'secret')
1187+
s4 | foreign_data_user |
1188+
s5 | regress_test_role | (modified '1')
1189+
s6 | regress_test_role |
1190+
s8 | foreign_data_user | (password 'public')
1191+
s8 | public |
1192+
s9 | unprivileged_role |
1193+
t1 | public | (modified '1')
1194+
(8 rows)
1195+
1196+
-- unprivileged user cannot see option fields
1197+
SET ROLE unprivileged_role;
1198+
\deu+
1199+
List of user mappings
1200+
Server | User name | FDW Options
1201+
--------+-------------------+-------------
1202+
s10 | public |
1203+
s4 | foreign_data_user |
1204+
s5 | regress_test_role |
1205+
s6 | regress_test_role |
1206+
s8 | foreign_data_user |
1207+
s8 | public |
1208+
s9 | unprivileged_role |
1209+
t1 | public |
1210+
(8 rows)
1211+
11601212
RESET ROLE;
1213+
DROP SERVER s10 CASCADE;
1214+
NOTICE: drop cascades to user mapping for public on server s10
11611215
-- DROP FOREIGN TABLE
11621216
DROP FOREIGN TABLE no_table; -- ERROR
11631217
ERROR: foreign table "no_table" does not exist

src/test/regress/expected/rules.out

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2026,7 +2026,9 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
20262026
| ELSE a.rolname +
20272027
| END AS usename, +
20282028
| CASE +
2029-
| WHEN (pg_has_role(s.srvowner, 'USAGE'::text) OR has_server_privilege(s.oid, 'USAGE'::text)) THEN u.umoptions +
2029+
| WHEN ((((u.umuser <> (0)::oid) AND (a.rolname = "current_user"())) OR ((u.umuser = (0)::oid) AND pg_has_role(s.srvowner, 'USAGE'::text))) OR ( SELECT pg_authid.rolsuper +
2030+
| FROM pg_authid +
2031+
| WHERE (pg_authid.rolname = "current_user"()))) THEN u.umoptions +
20302032
| ELSE NULL::text[] +
20312033
| END AS umoptions +
20322034
| FROM ((pg_user_mapping u +

src/test/regress/sql/foreign_data.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,22 @@ ALTER SERVER s9 VERSION '1.2'; -- ERROR
468468
GRANT USAGE ON FOREIGN SERVER s9 TO regress_test_role; -- WARNING
469469
CREATE USER MAPPING FOR current_user SERVER s9;
470470
DROP SERVER s9 CASCADE; -- ERROR
471+
472+
-- Check visibility of user mapping data
473+
SET ROLE regress_test_role;
474+
CREATE SERVER s10 FOREIGN DATA WRAPPER foo;
475+
CREATE USER MAPPING FOR public SERVER s10 OPTIONS (user 'secret');
476+
GRANT USAGE ON FOREIGN SERVER s10 TO unprivileged_role;
477+
-- owner of server can see option fields
478+
\deu+
479+
RESET ROLE;
480+
-- superuser can see option fields
481+
\deu+
482+
-- unprivileged user cannot see option fields
483+
SET ROLE unprivileged_role;
484+
\deu+
471485
RESET ROLE;
486+
DROP SERVER s10 CASCADE;
472487

473488
-- DROP FOREIGN TABLE
474489
DROP FOREIGN TABLE no_table; -- ERROR

0 commit comments

Comments
 (0)