summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorMagnus Hagander2010-12-29 10:05:03 +0000
committerMagnus Hagander2010-12-29 10:05:03 +0000
commit9b8aff8c192e2f313f90395d114c58a9ef84f97f (patch)
treea4a4f7a5c25d4bbdd85599471a206433de704f1d /src/backend
parentf2ba1e994c4d17dc3d4b8d48d3933c96d09127e1 (diff)
Add REPLICATION privilege for ROLEs
This privilege is required to do Streaming Replication, instead of superuser, making it possible to set up a SR slave that doesn't have write permissions on the master. Superuser privileges do NOT override this check, so in order to use the default superuser account for replication it must be explicitly granted the REPLICATION permissions. This is backwards incompatible change, in the interest of higher default security.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/access/transam/xlog.c8
-rw-r--r--src/backend/catalog/system_views.sql3
-rw-r--r--src/backend/commands/user.c46
-rw-r--r--src/backend/parser/gram.y20
-rw-r--r--src/backend/utils/init/miscinit.c19
-rw-r--r--src/backend/utils/init/postinit.c6
6 files changed, 91 insertions, 11 deletions
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index b49b933de3e..bf62138bf86 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -8301,10 +8301,10 @@ pg_start_backup(PG_FUNCTION_ARGS)
struct stat stat_buf;
FILE *fp;
- if (!superuser())
+ if (!superuser() && !is_authenticated_user_replication_role())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to run a backup")));
+ errmsg("must be superuser or replication role to run a backup")));
if (RecoveryInProgress())
ereport(ERROR,
@@ -8493,10 +8493,10 @@ pg_stop_backup(PG_FUNCTION_ARGS)
int waits = 0;
bool reported_waiting = false;
- if (!superuser())
+ if (!superuser() && !is_authenticated_user_replication_role())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to run a backup"))));
+ (errmsg("must be superuser or replication role to run a backup"))));
if (RecoveryInProgress())
ereport(ERROR,
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 346eaaf8924..e0e7efdf5d4 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -15,6 +15,7 @@ CREATE VIEW pg_roles AS
rolcreatedb,
rolcatupdate,
rolcanlogin,
+ rolreplication,
rolconnlimit,
'********'::text as rolpassword,
rolvaliduntil,
@@ -30,6 +31,7 @@ CREATE VIEW pg_shadow AS
rolcreatedb AS usecreatedb,
rolsuper AS usesuper,
rolcatupdate AS usecatupd,
+ rolreplication AS userepl,
rolpassword AS passwd,
rolvaliduntil::abstime AS valuntil,
setconfig AS useconfig
@@ -54,6 +56,7 @@ CREATE VIEW pg_user AS
usecreatedb,
usesuper,
usecatupd,
+ userepl,
'********'::text as passwd,
valuntil,
useconfig
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 2634ca10e64..df83cdea2cf 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -94,6 +94,7 @@ CreateRole(CreateRoleStmt *stmt)
bool createrole = false; /* Can this user create roles? */
bool createdb = false; /* Can the user create databases? */
bool canlogin = false; /* Can this user login? */
+ bool isreplication = false; /* Is this a replication role? */
int connlimit = -1; /* maximum connections allowed */
List *addroleto = NIL; /* roles to make this a member of */
List *rolemembers = NIL; /* roles to be members of this role */
@@ -107,6 +108,7 @@ CreateRole(CreateRoleStmt *stmt)
DefElem *dcreaterole = NULL;
DefElem *dcreatedb = NULL;
DefElem *dcanlogin = NULL;
+ DefElem *disreplication = NULL;
DefElem *dconnlimit = NULL;
DefElem *daddroleto = NULL;
DefElem *drolemembers = NULL;
@@ -190,6 +192,14 @@ CreateRole(CreateRoleStmt *stmt)
errmsg("conflicting or redundant options")));
dcanlogin = defel;
}
+ else if (strcmp(defel->defname, "isreplication") == 0)
+ {
+ if (disreplication)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ disreplication = defel;
+ }
else if (strcmp(defel->defname, "connectionlimit") == 0)
{
if (dconnlimit)
@@ -247,6 +257,8 @@ CreateRole(CreateRoleStmt *stmt)
createdb = intVal(dcreatedb->arg) != 0;
if (dcanlogin)
canlogin = intVal(dcanlogin->arg) != 0;
+ if (disreplication)
+ isreplication = intVal(disreplication->arg) != 0;
if (dconnlimit)
{
connlimit = intVal(dconnlimit->arg);
@@ -272,6 +284,13 @@ CreateRole(CreateRoleStmt *stmt)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create superusers")));
}
+ else if (isreplication)
+ {
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to create replication users")));
+ }
else
{
if (!have_createrole_privilege())
@@ -341,6 +360,7 @@ CreateRole(CreateRoleStmt *stmt)
/* superuser gets catupdate right by default */
new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
+ new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
if (password)
@@ -439,6 +459,7 @@ AlterRole(AlterRoleStmt *stmt)
int createrole = -1; /* Can this user create roles? */
int createdb = -1; /* Can the user create databases? */
int canlogin = -1; /* Can this user login? */
+ int isreplication = -1; /* Is this a replication role? */
int connlimit = -1; /* maximum connections allowed */
List *rolemembers = NIL; /* roles to be added/removed */
char *validUntil = NULL; /* time the login is valid until */
@@ -450,6 +471,7 @@ AlterRole(AlterRoleStmt *stmt)
DefElem *dcreaterole = NULL;
DefElem *dcreatedb = NULL;
DefElem *dcanlogin = NULL;
+ DefElem *disreplication = NULL;
DefElem *dconnlimit = NULL;
DefElem *drolemembers = NULL;
DefElem *dvalidUntil = NULL;
@@ -514,6 +536,14 @@ AlterRole(AlterRoleStmt *stmt)
errmsg("conflicting or redundant options")));
dcanlogin = defel;
}
+ else if (strcmp(defel->defname, "isreplication") == 0)
+ {
+ if (disreplication)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ disreplication = defel;
+ }
else if (strcmp(defel->defname, "connectionlimit") == 0)
{
if (dconnlimit)
@@ -556,6 +586,8 @@ AlterRole(AlterRoleStmt *stmt)
createdb = intVal(dcreatedb->arg);
if (dcanlogin)
canlogin = intVal(dcanlogin->arg);
+ if (disreplication)
+ isreplication = intVal(disreplication->arg);
if (dconnlimit)
{
connlimit = intVal(dconnlimit->arg);
@@ -594,12 +626,20 @@ AlterRole(AlterRoleStmt *stmt)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter superusers")));
}
+ else if (((Form_pg_authid) GETSTRUCT(tuple))->rolreplication || isreplication >= 0)
+ {
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to alter replication users")));
+ }
else if (!have_createrole_privilege())
{
if (!(inherit < 0 &&
createrole < 0 &&
createdb < 0 &&
canlogin < 0 &&
+ isreplication < 0 &&
!dconnlimit &&
!rolemembers &&
!validUntil &&
@@ -685,6 +725,12 @@ AlterRole(AlterRoleStmt *stmt)
new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
}
+ if (isreplication >= 0)
+ {
+ new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0);
+ new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
+ }
+
if (dconnlimit)
{
new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8fc79b63377..37840baa0f6 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -510,8 +510,9 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NOCREATEDB
- NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER
- NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC
+ NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOREPLICATION_P
+ NOSUPERUSER NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
+ NULLS_P NUMERIC
OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
@@ -523,8 +524,9 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
QUOTE
RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REINDEX
- RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
- RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
+ RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA REPLICATION_P
+ RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK
+ ROW ROWS RULE
SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE
@@ -864,6 +866,14 @@ AlterOptRoleElem:
{
$$ = makeDefElem("canlogin", (Node *)makeInteger(FALSE));
}
+ | REPLICATION_P
+ {
+ $$ = makeDefElem("isreplication", (Node *)makeInteger(TRUE));
+ }
+ | NOREPLICATION_P
+ {
+ $$ = makeDefElem("isreplication", (Node *)makeInteger(FALSE));
+ }
| CONNECTION LIMIT SignedIconst
{
$$ = makeDefElem("connectionlimit", (Node *)makeInteger($3));
@@ -11288,6 +11298,7 @@ unreserved_keyword:
| NOCREATEUSER
| NOINHERIT
| NOLOGIN_P
+ | NOREPLICATION_P
| NOSUPERUSER
| NOTHING
| NOTIFY
@@ -11330,6 +11341,7 @@ unreserved_keyword:
| REPEATABLE
| REPLACE
| REPLICA
+ | REPLICATION_P
| RESET
| RESTART
| RESTRICT
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index d74b5ccb30d..0d5ffb0a8e5 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -231,6 +231,7 @@ static int SecurityRestrictionContext = 0;
static bool SetRoleIsActive = false;
+
/*
* GetUserId - get the current effective user ID.
*
@@ -389,6 +390,24 @@ SetUserIdAndContext(Oid userid, bool sec_def_context)
/*
+ * Check if the authenticated user is a replication role
+ */
+bool
+is_authenticated_user_replication_role(void)
+{
+ bool result = false;
+ HeapTuple utup;
+
+ utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(AuthenticatedUserId));
+ if (HeapTupleIsValid(utup))
+ {
+ result = ((Form_pg_authid) GETSTRUCT(utup))->rolreplication;
+ ReleaseSysCache(utup);
+ }
+ return result;
+}
+
+/*
* Initialize user identity during normal backend startup
*/
void
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index db06cda46e8..eaaf27ffa51 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -658,11 +658,11 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
{
Assert(!bootstrap);
- /* must have authenticated as a superuser */
- if (!am_superuser)
+ /* must have authenticated as a replication role */
+ if (!is_authenticated_user_replication_role())
ereport(FATAL,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to start walsender")));
+ errmsg("must be replication role to start walsender")));
/* process any options passed in the startup packet */
if (MyProcPort != NULL)