aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils')
-rw-r--r--src/backend/utils/adt/acl.c114
-rw-r--r--src/backend/utils/cache/syscache.c23
-rw-r--r--src/backend/utils/misc/guc.c146
3 files changed, 262 insertions, 21 deletions
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 83cf7ac9ff9..8bdba1c42a5 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -23,6 +23,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_class.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "commands/proclang.h"
@@ -36,6 +37,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
+#include "utils/guc.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -109,6 +111,7 @@ static Oid convert_tablespace_name(text *tablespacename);
static AclMode convert_tablespace_priv_string(text *priv_type_text);
static Oid convert_type_name(text *typename);
static AclMode convert_type_priv_string(text *priv_type_text);
+static AclMode convert_parameter_priv_string(text *priv_text);
static AclMode convert_role_priv_string(text *priv_type_text);
static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
@@ -306,6 +309,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_SET_CHR:
+ read = ACL_SET;
+ break;
+ case ACL_ALTER_SYSTEM_CHR:
+ read = ACL_ALTER_SYSTEM;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -794,6 +803,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_PARAMETER_ACL:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_PARAMETER_ACL;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -876,6 +889,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'n':
objtype = OBJECT_SCHEMA;
break;
+ case 'p':
+ objtype = OBJECT_PARAMETER_ACL;
+ break;
case 't':
objtype = OBJECT_TABLESPACE;
break;
@@ -1602,6 +1618,10 @@ convert_priv_string(text *priv_type_text)
return ACL_CREATE_TEMP;
if (pg_strcasecmp(priv_type, "CONNECT") == 0)
return ACL_CONNECT;
+ if (pg_strcasecmp(priv_type, "SET") == 0)
+ return ACL_SET;
+ if (pg_strcasecmp(priv_type, "ALTER SYSTEM") == 0)
+ return ACL_ALTER_SYSTEM;
if (pg_strcasecmp(priv_type, "RULE") == 0)
return 0; /* ignore old RULE privileges */
@@ -1698,6 +1718,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET:
+ return "SET";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
@@ -4429,6 +4453,96 @@ convert_type_priv_string(text *priv_type_text)
return convert_any_priv_string(priv_type_text, type_priv_map);
}
+/*
+ * has_parameter_privilege variants
+ * These are all named "has_parameter_privilege" at the SQL level.
+ * They take various combinations of parameter name with
+ * user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has been granted
+ * the indicated privilege or false if not.
+ */
+
+/*
+ * has_param_priv_byname
+ *
+ * Helper function to check user privileges on a parameter given the
+ * role by Oid, parameter by text name, and privileges as AclMode.
+ */
+static bool
+has_param_priv_byname(Oid roleid, const text *parameter, AclMode priv)
+{
+ char *paramstr = text_to_cstring(parameter);
+
+ return pg_parameter_aclcheck(paramstr, roleid, priv) == ACLCHECK_OK;
+}
+
+/*
+ * has_parameter_privilege_name_name
+ * Check user privileges on a parameter given name username, text
+ * parameter, and text priv name.
+ */
+Datum
+has_parameter_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *parameter = PG_GETARG_TEXT_PP(1);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
+ Oid roleid = get_role_oid_or_public(NameStr(*username));
+
+ PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
+}
+
+/*
+ * has_parameter_privilege_name
+ * Check user privileges on a parameter given text parameter and text priv
+ * name. current_user is assumed
+ */
+Datum
+has_parameter_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *parameter = PG_GETARG_TEXT_PP(0);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(1));
+
+ PG_RETURN_BOOL(has_param_priv_byname(GetUserId(), parameter, priv));
+}
+
+/*
+ * has_parameter_privilege_id_name
+ * Check user privileges on a parameter given roleid, text parameter, and
+ * text priv name.
+ */
+Datum
+has_parameter_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *parameter = PG_GETARG_TEXT_PP(1);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
+
+ PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
+}
+
+/*
+ * Support routines for has_parameter_privilege family.
+ */
+
+/*
+ * convert_parameter_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_parameter_priv_string(text *priv_text)
+{
+ static const priv_map parameter_priv_map[] = {
+ {"SET", ACL_SET},
+ {"SET WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SET)},
+ {"ALTER SYSTEM", ACL_ALTER_SYSTEM},
+ {"ALTER SYSTEM WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_ALTER_SYSTEM)},
+ {NULL, 0}
+ };
+
+ return convert_any_priv_string(priv_text, parameter_priv_map);
+}
/*
* pg_has_role variants
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index a675877d195..8d265f2d23c 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -47,6 +47,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_partitioned_table.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication.h"
@@ -574,6 +575,28 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {ParameterAclRelationId, /* PARAMETERACLNAME */
+ ParameterAclParnameIndexId,
+ 1,
+ {
+ Anum_pg_parameter_acl_parname,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {ParameterAclRelationId, /* PARAMETERACLOID */
+ ParameterAclOidIndexId,
+ 1,
+ {
+ Anum_pg_parameter_acl_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{PartitionedRelationId, /* PARTRELID */
PartitionedRelidIndexId,
1,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9e8ab1420d9..5538465d7d6 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -45,6 +45,7 @@
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/storage.h"
#include "commands/async.h"
#include "commands/prepare.h"
@@ -5714,6 +5715,65 @@ guc_name_compare(const char *namea, const char *nameb)
/*
+ * Convert a GUC name to the form that should be used in pg_parameter_acl.
+ *
+ * We need to canonicalize entries since, for example, case should not be
+ * significant. In addition, we apply the map_old_guc_names[] mapping so that
+ * any obsolete names will be converted when stored in a new PG version.
+ * Note however that this function does not verify legality of the name.
+ *
+ * The result is a palloc'd string.
+ */
+char *
+convert_GUC_name_for_parameter_acl(const char *name)
+{
+ char *result;
+
+ /* Apply old-GUC-name mapping. */
+ for (int i = 0; map_old_guc_names[i] != NULL; i += 2)
+ {
+ if (guc_name_compare(name, map_old_guc_names[i]) == 0)
+ {
+ name = map_old_guc_names[i + 1];
+ break;
+ }
+ }
+
+ /* Apply case-folding that matches guc_name_compare(). */
+ result = pstrdup(name);
+ for (char *ptr = result; *ptr != '\0'; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch >= 'A' && ch <= 'Z')
+ {
+ ch += 'a' - 'A';
+ *ptr = ch;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Check whether we should allow creation of a pg_parameter_acl entry
+ * for the given name. (This can be applied either before or after
+ * canonicalizing it.)
+ */
+bool
+check_GUC_name_for_parameter_acl(const char *name)
+{
+ /* OK if the GUC exists. */
+ if (find_option(name, false, true, DEBUG1) != NULL)
+ return true;
+ /* Otherwise, it'd better be a valid custom GUC name. */
+ if (valid_custom_variable_name(name))
+ return true;
+ return false;
+}
+
+
+/*
* Initialize GUC options during program startup.
*
* Note that we cannot read the config file yet, since we have not yet
@@ -7518,14 +7578,24 @@ set_config_option(const char *name, const char *value,
*/
break;
case PGC_SU_BACKEND:
- /* Reject if we're connecting but user is not superuser */
if (context == PGC_BACKEND)
{
- ereport(elevel,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied to set parameter \"%s\"",
- name)));
- return 0;
+ /*
+ * Check whether the current user has been granted privilege
+ * to set this GUC.
+ */
+ AclResult aclresult;
+
+ aclresult = pg_parameter_aclcheck(name, GetUserId(), ACL_SET);
+ if (aclresult != ACLCHECK_OK)
+ {
+ /* No granted privilege */
+ ereport(elevel,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+ return 0;
+ }
}
/* fall through to process the same as PGC_BACKEND */
/* FALLTHROUGH */
@@ -7568,11 +7638,22 @@ set_config_option(const char *name, const char *value,
case PGC_SUSET:
if (context == PGC_USERSET || context == PGC_BACKEND)
{
- ereport(elevel,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied to set parameter \"%s\"",
- name)));
- return 0;
+ /*
+ * Check whether the current user has been granted privilege
+ * to set this GUC.
+ */
+ AclResult aclresult;
+
+ aclresult = pg_parameter_aclcheck(name, GetUserId(), ACL_SET);
+ if (aclresult != ACLCHECK_OK)
+ {
+ /* No granted privilege */
+ ereport(elevel,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+ return 0;
+ }
}
break;
case PGC_USERSET:
@@ -8617,11 +8698,6 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
char AutoConfFileName[MAXPGPATH];
char AutoConfTmpFileName[MAXPGPATH];
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to execute ALTER SYSTEM command")));
-
/*
* Extract statement arguments
*/
@@ -8650,6 +8726,29 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
}
/*
+ * Check permission to run ALTER SYSTEM on the target variable
+ */
+ if (!superuser())
+ {
+ if (resetall)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to perform ALTER SYSTEM RESET ALL")));
+ else
+ {
+ AclResult aclresult;
+
+ aclresult = pg_parameter_aclcheck(name, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+ }
+ }
+
+ /*
* Unless it's RESET_ALL, validate the target variable and value
*/
if (!resetall)
@@ -8760,13 +8859,18 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
}
/*
- * Invoke the post-alter hook for altering this GUC variable.
+ * Invoke the post-alter hook for setting this GUC variable. GUCs
+ * typically do not have corresponding entries in pg_parameter_acl, so we
+ * call the hook using the name rather than a potentially-non-existent
+ * OID. Nonetheless, we pass ParameterAclRelationId so that this call
+ * context can be distinguished from others. (Note that "name" will be
+ * NULL in the RESET ALL case.)
*
* We do this here rather than at the end, because ALTER SYSTEM is not
* transactional. If the hook aborts our transaction, it will be cleaner
* to do so before we touch any files.
*/
- InvokeObjectPostAlterHookArgStr(InvalidOid, name,
+ InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, name,
ACL_ALTER_SYSTEM,
altersysstmt->setstmt->kind,
false);
@@ -8943,9 +9047,9 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
break;
}
- /* Invoke the post-alter hook for setting this GUC variable. */
- InvokeObjectPostAlterHookArgStr(InvalidOid, stmt->name,
- ACL_SET_VALUE, stmt->kind, false);
+ /* Invoke the post-alter hook for setting this GUC variable, by name. */
+ InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, stmt->name,
+ ACL_SET, stmt->kind, false);
}
/*