1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-07 00:36:50 +03:00

Add USER SET parameter values for pg_db_role_setting

The USER SET flag specifies that the variable should be set on behalf of an
ordinary role.  That lets ordinary roles set placeholder variables, which
permission requirements are not known yet.  Such a value wouldn't be used if
the variable finally appear to require superuser privileges.

The new flags are stored in the pg_db_role_setting.setuser array.  Catversion
is bumped.

This commit is inspired by the previous work by Steve Chavez.

Discussion: https://postgr.es/m/CAPpHfdsLd6E--epnGqXENqLP6dLwuNZrPMcNYb3wJ87WR7UBOQ%40mail.gmail.com
Author: Alexander Korotkov, Steve Chavez
Reviewed-by: Pavel Borisov, Steve Chavez
This commit is contained in:
Alexander Korotkov
2022-12-09 13:12:20 +03:00
parent 5defdef8aa
commit 096dd80f3c
34 changed files with 680 additions and 51 deletions

View File

@ -225,7 +225,6 @@ static bool reporting_enabled; /* true to enable GUC_REPORT */
static int GUCNestLevel = 0; /* 1 when in main transaction */
static int guc_var_compare(const void *a, const void *b);
static uint32 guc_name_hash(const void *key, Size keysize);
static int guc_name_match(const void *key1, const void *key2, Size keysize);
@ -245,7 +244,7 @@ static void reapply_stacked_values(struct config_generic *variable,
GucContext curscontext, GucSource cursource,
Oid cursrole);
static bool validate_option_array_item(const char *name, const char *value,
bool skipIfNoPermissions);
bool user_set, bool skipIfNoPermissions);
static void write_auto_conf_file(int fd, const char *filename, ConfigVariable *head);
static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
const char *name, const char *value);
@ -6182,7 +6181,6 @@ ParseLongOption(const char *string, char **name, char **value)
{
*name = palloc(equal_pos + 1);
strlcpy(*name, string, equal_pos + 1);
*value = pstrdup(&string[equal_pos + 1]);
}
else
@ -6205,7 +6203,7 @@ ParseLongOption(const char *string, char **name, char **value)
* The array parameter must be an array of TEXT (it must not be NULL).
*/
void
ProcessGUCArray(ArrayType *array,
ProcessGUCArray(ArrayType *array, ArrayType *usersetArray,
GucContext context, GucSource source, GucAction action)
{
int i;
@ -6218,6 +6216,7 @@ ProcessGUCArray(ArrayType *array,
for (i = 1; i <= ARR_DIMS(array)[0]; i++)
{
Datum d;
Datum userSetDatum = BoolGetDatum(false);
bool isnull;
char *s;
char *name;
@ -6246,9 +6245,29 @@ ProcessGUCArray(ArrayType *array,
continue;
}
(void) set_config_option(name, value,
context, source,
action, true, 0, false);
if (usersetArray)
userSetDatum = array_ref(usersetArray, 1, &i,
-1 /* varlenarray */ ,
sizeof(bool) /* BOOL's typlen */ ,
true /* BOOL's typbyval */ ,
TYPALIGN_CHAR /* BOOL's typalign */ ,
&isnull);
if (isnull)
userSetDatum = BoolGetDatum(false);
/*
* USER SET values are appliciable only for PGC_USERSET parameters. We
* use InvalidOid as role in order to evade possible privileges of the
* current user.
*/
if (!DatumGetBool(userSetDatum))
(void) set_config_option(name, value,
context, source,
action, true, 0, false);
else
(void) set_config_option_ext(name, value,
PGC_USERSET, source, InvalidOid,
action, true, 0, false);
pfree(name);
pfree(value);
@ -6262,7 +6281,8 @@ ProcessGUCArray(ArrayType *array,
* to indicate the current table entry is NULL.
*/
ArrayType *
GUCArrayAdd(ArrayType *array, const char *name, const char *value)
GUCArrayAdd(ArrayType *array, ArrayType **usersetArray,
const char *name, const char *value, bool user_set)
{
struct config_generic *record;
Datum datum;
@ -6273,7 +6293,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
Assert(value);
/* test if the option is valid and we're allowed to set it */
(void) validate_option_array_item(name, value, false);
(void) validate_option_array_item(name, value, user_set, false);
/* normalize name (converts obsolete GUC names to modern spellings) */
record = find_option(name, false, true, WARNING);
@ -6314,6 +6334,27 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
/* check for match up through and including '=' */
if (strncmp(current, newval, strlen(name) + 1) == 0)
{
bool currentUserSet = false;
if (usersetArray)
{
currentUserSet = DatumGetBool(array_ref(*usersetArray, 1, &i,
-1 /* varlenarray */ ,
sizeof(bool) /* BOOL's typlen */ ,
true /* BOOL's typbyval */ ,
TYPALIGN_CHAR /* BOOL's typalign */ ,
&isnull));
if (isnull)
currentUserSet = false;
}
/*
* Recheck permissons if we found an option without USER SET
* flag while we're setting an optionn with USER SET flag.
*/
if (!currentUserSet && user_set)
(void) validate_option_array_item(name, value,
false, false);
index = i;
break;
}
@ -6326,9 +6367,25 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
-1 /* TEXT's typlen */ ,
false /* TEXT's typbyval */ ,
TYPALIGN_INT /* TEXT's typalign */ );
if (usersetArray)
*usersetArray = array_set(*usersetArray, 1, &index,
BoolGetDatum(user_set),
false,
-1 /* varlena array */ ,
sizeof(bool) /* BOOL's typlen */ ,
true /* BOOL's typbyval */ ,
TYPALIGN_CHAR /* BOOL's typalign */ );
}
else
{
a = construct_array_builtin(&datum, 1, TEXTOID);
if (usersetArray)
{
datum = BoolGetDatum(user_set);
*usersetArray = construct_array_builtin(&datum, 1, BOOLOID);
}
}
return a;
}
@ -6340,18 +6397,16 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
* is NULL then a null should be stored.
*/
ArrayType *
GUCArrayDelete(ArrayType *array, const char *name)
GUCArrayDelete(ArrayType *array, ArrayType **usersetArray, const char *name)
{
struct config_generic *record;
ArrayType *newarray;
ArrayType *newUsersetArray;
int i;
int index;
Assert(name);
/* test if the option is valid and we're allowed to set it */
(void) validate_option_array_item(name, NULL, false);
/* normalize name (converts obsolete GUC names to modern spellings) */
record = find_option(name, false, true, WARNING);
if (record)
@ -6362,11 +6417,13 @@ GUCArrayDelete(ArrayType *array, const char *name)
return NULL;
newarray = NULL;
newUsersetArray = NULL;
index = 1;
for (i = 1; i <= ARR_DIMS(array)[0]; i++)
{
Datum d;
Datum userSetDatum = BoolGetDatum(false);
char *val;
bool isnull;
@ -6380,13 +6437,29 @@ GUCArrayDelete(ArrayType *array, const char *name)
continue;
val = TextDatumGetCString(d);
if (usersetArray)
userSetDatum = array_ref(*usersetArray, 1, &i,
-1 /* varlenarray */ ,
sizeof(bool) /* BOOL's typlen */ ,
true /* BOOL's typbyval */ ,
TYPALIGN_CHAR /* BOOL's typalign */ ,
&isnull);
if (isnull)
userSetDatum = BoolGetDatum(false);
/* ignore entry if it's what we want to delete */
if (strncmp(val, name, strlen(name)) == 0
&& val[strlen(name)] == '=')
{
/* test if the option is valid and we're allowed to set it */
(void) validate_option_array_item(name, NULL,
DatumGetBool(userSetDatum), false);
continue;
}
/* else add it to the output array */
if (newarray)
{
newarray = array_set(newarray, 1, &index,
d,
false,
@ -6394,12 +6467,28 @@ GUCArrayDelete(ArrayType *array, const char *name)
-1 /* TEXT's typlen */ ,
false /* TEXT's typbyval */ ,
TYPALIGN_INT /* TEXT's typalign */ );
if (usersetArray)
newUsersetArray = array_set(newUsersetArray, 1, &index,
userSetDatum,
false,
-1 /* varlena array */ ,
sizeof(bool) /* BOOL's typlen */ ,
true /* BOOL's typbyval */ ,
TYPALIGN_CHAR /* BOOL's typalign */ );
}
else
{
newarray = construct_array_builtin(&d, 1, TEXTOID);
if (usersetArray)
newUsersetArray = construct_array_builtin(&d, 1, BOOLOID);
}
index++;
}
if (usersetArray)
*usersetArray = newUsersetArray;
return newarray;
}
@ -6410,9 +6499,10 @@ GUCArrayDelete(ArrayType *array, const char *name)
* those that are PGC_USERSET or we have permission to set
*/
ArrayType *
GUCArrayReset(ArrayType *array)
GUCArrayReset(ArrayType *array, ArrayType **usersetArray)
{
ArrayType *newarray;
ArrayType *newUsersetArray;
int i;
int index;
@ -6425,11 +6515,13 @@ GUCArrayReset(ArrayType *array)
return NULL;
newarray = NULL;
newUsersetArray = NULL;
index = 1;
for (i = 1; i <= ARR_DIMS(array)[0]; i++)
{
Datum d;
Datum userSetDatum = BoolGetDatum(false);
char *val;
char *eqsgn;
bool isnull;
@ -6444,15 +6536,27 @@ GUCArrayReset(ArrayType *array)
continue;
val = TextDatumGetCString(d);
if (usersetArray)
userSetDatum = array_ref(*usersetArray, 1, &i,
-1 /* varlenarray */ ,
sizeof(bool) /* BOOL's typlen */ ,
true /* BOOL's typbyval */ ,
TYPALIGN_CHAR /* BOOL's typalign */ ,
&isnull);
if (isnull)
userSetDatum = BoolGetDatum(false);
eqsgn = strchr(val, '=');
*eqsgn = '\0';
/* skip if we have permission to delete it */
if (validate_option_array_item(val, NULL, true))
if (validate_option_array_item(val, NULL,
DatumGetBool(userSetDatum), true))
continue;
/* else add it to the output array */
if (newarray)
{
newarray = array_set(newarray, 1, &index,
d,
false,
@ -6460,13 +6564,29 @@ GUCArrayReset(ArrayType *array)
-1 /* TEXT's typlen */ ,
false /* TEXT's typbyval */ ,
TYPALIGN_INT /* TEXT's typalign */ );
if (usersetArray)
newUsersetArray = array_set(newUsersetArray, 1, &index,
userSetDatum,
false,
-1 /* varlena array */ ,
sizeof(bool) /* BOOL's typlen */ ,
true /* BOOL's typbyval */ ,
TYPALIGN_CHAR /* BOOL's typalign */ );
}
else
{
newarray = construct_array_builtin(&d, 1, TEXTOID);
if (usersetArray)
newUsersetArray = construct_array_builtin(&userSetDatum, 1, BOOLOID);
}
index++;
pfree(val);
}
if (usersetArray)
*usersetArray = newUsersetArray;
return newarray;
}
@ -6474,15 +6594,16 @@ GUCArrayReset(ArrayType *array)
* Validate a proposed option setting for GUCArrayAdd/Delete/Reset.
*
* name is the option name. value is the proposed value for the Add case,
* or NULL for the Delete/Reset cases. If skipIfNoPermissions is true, it's
* not an error to have no permissions to set the option.
* or NULL for the Delete/Reset cases. user_set indicates this is the USER SET
* option. If skipIfNoPermissions is true, it's not an error to have no
* permissions to set the option.
*
* Returns true if OK, false if skipIfNoPermissions is true and user does not
* have permission to change this option (all other error cases result in an
* error being thrown).
*/
static bool
validate_option_array_item(const char *name, const char *value,
validate_option_array_item(const char *name, const char *value, bool user_set,
bool skipIfNoPermissions)
{
@ -6518,8 +6639,10 @@ validate_option_array_item(const char *name, const char *value,
{
/*
* We cannot do any meaningful check on the value, so only permissions
* are useful to check.
* are useful to check. USER SET options are always allowed.
*/
if (user_set)
return true;
if (superuser() ||
pg_parameter_aclcheck(name, GetUserId(), ACL_SET) == ACLCHECK_OK)
return true;