my-ship-it opened a new issue, #1992:
URL: https://github.com/apache/cloudberry/issues/1992
### Apache Cloudberry version
Affected: 2.1.0-incubating and earlier, and current `main` (verified on
`c781604c5ba`).
### What happened
The privilege check that enforces "only a superuser may create a `file://`
external table"
is skipped entirely when the session runs in utility mode.
In `gpcontrib/gp_exttable_fdw/option.c`, `gp_exttable_permission_check()`
gates the whole
check block on the dispatch role:
```c
if (!is_superuser && Gp_role == GP_ROLE_DISPATCH)
{
/*
* - Never allow 'file' exttables if not superuser.
* - Allow http, gpfdist or gpfdists tables if pg_auth has the right
* permissions for this role and for this type of table
*/
is_valid_locationuris(location_list, is_writable);
...
}
```
Under `Gp_role == GP_ROLE_UTILITY` the condition is false, so neither the
`file://`
superuser restriction nor the `gpfdist`/`gpfdists` `pg_authid` privilege
checks
(`rolcreaterexthttp`, `rolcreaterextgpfd`, `rolcreatewextgpfd`) ever run.
The role is reachable by an unprivileged user: `gp_role` is a `PGC_BACKEND`
GUC, and
its validator `check_gp_role()` (`src/backend/cdb/cdbvars.c`) has no
superuser gate —
it only forbids *upgrading* an already-assigned role. A client can therefore
request
utility mode in the startup packet (`PGOPTIONS='-c gp_role=utility'`) as an
ordinary
login role.
The resulting table is a normal catalog entry. Once created in a
utility-mode session,
it can be read from an ordinary dispatch session, so the attacker gets
arbitrary
server-side file reads with the privileges of the OS account running the
database
(`/etc/passwd`, `pg_hba.conf`, `postgresql.conf`, key material, WAL and data
files, …).
### What you think should happen instead
The `file://` protocol restriction — and the `pg_authid` protocol privileges
for
`gpfdist`/`gpfdists` — are security boundaries and must hold in every
connection mode
a user can reach. `gp_role` is a transport/topology setting, not an
authorization
level, so it must not be able to turn a privilege check off.
### How to reproduce
```bash
# 1. As a superuser, create an unprivileged login role.
psql -p 7000 -c "CREATE ROLE lowpriv LOGIN;"
# 2. Connect as that role in utility mode and create a file:// external
table.
PGOPTIONS='-c gp_role=utility' psql -p 7000 -U lowpriv -d postgres <<'SQL'
SELECT current_setting('gp_role'); -- utility
SELECT current_setting('is_superuser'); -- off
CREATE READABLE EXTERNAL TABLE etc_passwd (data text)
LOCATION ('file://localhost/etc/passwd') FORMAT 'TEXT';
SQL
# Expected: ERROR: must be superuser to create an external table with a file
protocol
# Actual: CREATE EXTERNAL TABLE
# 3. Read it back from an ordinary (dispatch) session as the same
unprivileged role.
psql -p 7000 -U lowpriv -d postgres -c "SELECT * FROM etc_passwd;"
```
The same bypass applies to `gpfdist://` / `gpfdists://` locations for a role
that lacks
`rolcreaterextgpfd`, and to the `http://` protocol for a role that lacks
`rolcreaterexthttp`.
### Operating System
Rocky Linux 9.6 (Blue Onyx). Not OS-specific.
### Anything else
**Impact.** CVSS v3.1 **6.5 Medium** —
`AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N`. Reproducible every time; no race, no
special
cluster configuration. This is not by design: the superuser-only restriction
on
`file://` is documented and is simply not reached in utility mode.
**Proposed fix.** Run the check in utility mode as well:
```diff
- if(!is_superuser && Gp_role == GP_ROLE_DISPATCH)
+ if(!is_superuser &&
+ (Gp_role == GP_ROLE_DISPATCH || Gp_role == GP_ROLE_UTILITY))
```
`GP_ROLE_EXECUTE` is deliberately left out. That role is only ever set by
the internal
dispatch handshake and cannot be forged through `PGOPTIONS`, so excluding it
closes the
hole without re-validating DDL on the segments that the coordinator has
already checked.
**Test coverage.** A regression case belongs in
`src/test/isolation2/input/external_table.source`: from a `-1U` (utility)
session,
`SET SESSION AUTHORIZATION` to a non-superuser role and assert that both a
`file://`
and an unprivileged `gpfdist://` `CREATE READABLE EXTERNAL TABLE` are
rejected, then
assert nothing was created in `pg_class`.
**Credit.** Reported to `[email protected]` by Geo (`[email protected]`);
triaged and
accepted by the Apache Cloudberry PMC. Back-ports to affected release
branches to follow.
- [x] Yes, I am willing to submit a PR!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]