diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 767d9b9619..eb4878701a 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -907,6 +907,9 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control, * We use the equivalent of a function SET option to allow the setting to * persist for exactly the duration of the script execution. guc.c also * takes care of undoing the setting on error. + * + * log_min_messages can't be set by ordinary users, so for that one we + * pretend to be superuser. */ save_nestlevel = NewGUCNestLevel(); @@ -915,9 +918,10 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control, PGC_USERSET, PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false); if (log_min_messages < WARNING) - (void) set_config_option("log_min_messages", "warning", - PGC_SUSET, PGC_S_SESSION, - GUC_ACTION_SAVE, true, 0, false); + (void) set_config_option_ext("log_min_messages", "warning", + PGC_SUSET, PGC_S_SESSION, + BOOTSTRAP_SUPERUSERID, + GUC_ACTION_SAVE, true, 0, false); /* * Similarly disable check_function_bodies, to ensure that SQL functions diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index a7cc49898b..a3457ee6c8 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -5172,7 +5172,8 @@ static void reapply_stacked_values(struct config_generic *variable, struct config_string *pHolder, GucStack *stack, const char *curvalue, - GucContext curscontext, GucSource cursource); + GucContext curscontext, GucSource cursource, + Oid cursrole); static void ShowGUCConfigOption(const char *name, DestReceiver *dest); static void ShowAllGUCConfig(DestReceiver *dest); static char *_ShowOption(struct config_generic *record, bool use_units); @@ -5898,10 +5899,10 @@ InitializeWalConsistencyChecking(void) check_wal_consistency_checking_deferred = false; - set_config_option("wal_consistency_checking", - wal_consistency_checking_string, - PGC_POSTMASTER, guc->source, - GUC_ACTION_SET, true, ERROR, false); + set_config_option_ext("wal_consistency_checking", + wal_consistency_checking_string, + guc->scontext, guc->source, guc->srole, + GUC_ACTION_SET, true, ERROR, false); /* checking should not be deferred again */ Assert(!check_wal_consistency_checking_deferred); @@ -5980,6 +5981,8 @@ InitializeOneGUCOption(struct config_generic *gconf) gconf->reset_source = PGC_S_DEFAULT; gconf->scontext = PGC_INTERNAL; gconf->reset_scontext = PGC_INTERNAL; + gconf->srole = BOOTSTRAP_SUPERUSERID; + gconf->reset_srole = BOOTSTRAP_SUPERUSERID; gconf->stack = NULL; gconf->extra = NULL; gconf->last_reported = NULL; @@ -6357,6 +6360,7 @@ ResetAllOptions(void) gconf->source = gconf->reset_source; gconf->scontext = gconf->reset_scontext; + gconf->srole = gconf->reset_srole; if (gconf->flags & GUC_REPORT) { @@ -6402,6 +6406,7 @@ push_old_value(struct config_generic *gconf, GucAction action) { /* SET followed by SET LOCAL, remember SET's value */ stack->masked_scontext = gconf->scontext; + stack->masked_srole = gconf->srole; set_stack_value(gconf, &stack->masked); stack->state = GUC_SET_LOCAL; } @@ -6440,6 +6445,7 @@ push_old_value(struct config_generic *gconf, GucAction action) } stack->source = gconf->source; stack->scontext = gconf->scontext; + stack->srole = gconf->srole; set_stack_value(gconf, &stack->prior); gconf->stack = stack; @@ -6584,6 +6590,7 @@ AtEOXact_GUC(bool isCommit, int nestLevel) { /* LOCAL migrates down */ prev->masked_scontext = stack->scontext; + prev->masked_srole = stack->srole; prev->masked = stack->prior; prev->state = GUC_SET_LOCAL; } @@ -6599,6 +6606,7 @@ AtEOXact_GUC(bool isCommit, int nestLevel) discard_stack_value(gconf, &stack->prior); /* copy down the masked state */ prev->masked_scontext = stack->masked_scontext; + prev->masked_srole = stack->masked_srole; if (prev->state == GUC_SET_LOCAL) discard_stack_value(gconf, &prev->masked); prev->masked = stack->masked; @@ -6615,18 +6623,21 @@ AtEOXact_GUC(bool isCommit, int nestLevel) config_var_value newvalue; GucSource newsource; GucContext newscontext; + Oid newsrole; if (restoreMasked) { newvalue = stack->masked; newsource = PGC_S_SESSION; newscontext = stack->masked_scontext; + newsrole = stack->masked_srole; } else { newvalue = stack->prior; newsource = stack->source; newscontext = stack->scontext; + newsrole = stack->srole; } switch (gconf->vartype) @@ -6741,6 +6752,7 @@ AtEOXact_GUC(bool isCommit, int nestLevel) /* And restore source information */ gconf->source = newsource; gconf->scontext = newscontext; + gconf->srole = newsrole; } /* Finish popping the state stack */ @@ -7522,7 +7534,7 @@ parse_and_validate_value(struct config_generic *record, /* - * Sets option `name' to given value. + * set_config_option: sets option `name' to given value. * * The value should be a string, which will be parsed and converted to * the appropriate data type. The context and source parameters indicate @@ -7565,6 +7577,46 @@ set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload) +{ + Oid srole; + + /* + * Non-interactive sources should be treated as having all privileges, + * except for PGC_S_CLIENT. Note in particular that this is true for + * pg_db_role_setting sources (PGC_S_GLOBAL etc): we assume a suitable + * privilege check was done when the pg_db_role_setting entry was made. + */ + if (source >= PGC_S_INTERACTIVE || source == PGC_S_CLIENT) + srole = GetUserId(); + else + srole = BOOTSTRAP_SUPERUSERID; + + return set_config_option_ext(name, value, + context, source, srole, + action, changeVal, elevel, + is_reload); +} + +/* + * set_config_option_ext: sets option `name' to given value. + * + * This API adds the ability to explicitly specify which role OID + * is considered to be setting the value. Most external callers can use + * set_config_option() and let it determine that based on the GucSource, + * but there are a few that are supplying a value that was determined + * in some special way and need to override the decision. Also, when + * restoring a previously-assigned value, it's important to supply the + * same role OID that set the value originally; so all guc.c callers + * that are doing that type of thing need to call this directly. + * + * Generally, srole should be GetUserId() when the source is a SQL operation, + * or BOOTSTRAP_SUPERUSERID if the source is a config file or similar. + */ +int +set_config_option_ext(const char *name, const char *value, + GucContext context, GucSource source, Oid srole, + GucAction action, bool changeVal, int elevel, + bool is_reload) { struct config_generic *record; union config_var_val newval_union; @@ -7669,12 +7721,12 @@ set_config_option(const char *name, const char *value, if (context == PGC_BACKEND) { /* - * Check whether the current user has been granted privilege - * to set this GUC. + * Check whether the requesting user has been granted + * privilege to set this GUC. */ AclResult aclresult; - aclresult = pg_parameter_aclcheck(name, GetUserId(), ACL_SET); + aclresult = pg_parameter_aclcheck(name, srole, ACL_SET); if (aclresult != ACLCHECK_OK) { /* No granted privilege */ @@ -7727,12 +7779,12 @@ set_config_option(const char *name, const char *value, if (context == PGC_USERSET || context == PGC_BACKEND) { /* - * Check whether the current user has been granted privilege - * to set this GUC. + * Check whether the requesting user has been granted + * privilege to set this GUC. */ AclResult aclresult; - aclresult = pg_parameter_aclcheck(name, GetUserId(), ACL_SET); + aclresult = pg_parameter_aclcheck(name, srole, ACL_SET); if (aclresult != ACLCHECK_OK) { /* No granted privilege */ @@ -7849,6 +7901,7 @@ set_config_option(const char *name, const char *value, newextra = conf->reset_extra; source = conf->gen.reset_source; context = conf->gen.reset_scontext; + srole = conf->gen.reset_srole; } if (prohibitValueChange) @@ -7883,6 +7936,7 @@ set_config_option(const char *name, const char *value, newextra); conf->gen.source = source; conf->gen.scontext = context; + conf->gen.srole = srole; } if (makeDefault) { @@ -7895,6 +7949,7 @@ set_config_option(const char *name, const char *value, newextra); conf->gen.reset_source = source; conf->gen.reset_scontext = context; + conf->gen.reset_srole = srole; } for (stack = conf->gen.stack; stack; stack = stack->prev) { @@ -7905,6 +7960,7 @@ set_config_option(const char *name, const char *value, newextra); stack->source = source; stack->scontext = context; + stack->srole = srole; } } } @@ -7943,6 +7999,7 @@ set_config_option(const char *name, const char *value, newextra = conf->reset_extra; source = conf->gen.reset_source; context = conf->gen.reset_scontext; + srole = conf->gen.reset_srole; } if (prohibitValueChange) @@ -7977,6 +8034,7 @@ set_config_option(const char *name, const char *value, newextra); conf->gen.source = source; conf->gen.scontext = context; + conf->gen.srole = srole; } if (makeDefault) { @@ -7989,6 +8047,7 @@ set_config_option(const char *name, const char *value, newextra); conf->gen.reset_source = source; conf->gen.reset_scontext = context; + conf->gen.reset_srole = srole; } for (stack = conf->gen.stack; stack; stack = stack->prev) { @@ -7999,6 +8058,7 @@ set_config_option(const char *name, const char *value, newextra); stack->source = source; stack->scontext = context; + stack->srole = srole; } } } @@ -8037,6 +8097,7 @@ set_config_option(const char *name, const char *value, newextra = conf->reset_extra; source = conf->gen.reset_source; context = conf->gen.reset_scontext; + srole = conf->gen.reset_srole; } if (prohibitValueChange) @@ -8071,6 +8132,7 @@ set_config_option(const char *name, const char *value, newextra); conf->gen.source = source; conf->gen.scontext = context; + conf->gen.srole = srole; } if (makeDefault) { @@ -8083,6 +8145,7 @@ set_config_option(const char *name, const char *value, newextra); conf->gen.reset_source = source; conf->gen.reset_scontext = context; + conf->gen.reset_srole = srole; } for (stack = conf->gen.stack; stack; stack = stack->prev) { @@ -8093,6 +8156,7 @@ set_config_option(const char *name, const char *value, newextra); stack->source = source; stack->scontext = context; + stack->srole = srole; } } } @@ -8147,6 +8211,7 @@ set_config_option(const char *name, const char *value, newextra = conf->reset_extra; source = conf->gen.reset_source; context = conf->gen.reset_scontext; + srole = conf->gen.reset_srole; } if (prohibitValueChange) @@ -8191,6 +8256,7 @@ set_config_option(const char *name, const char *value, newextra); conf->gen.source = source; conf->gen.scontext = context; + conf->gen.srole = srole; } if (makeDefault) @@ -8204,6 +8270,7 @@ set_config_option(const char *name, const char *value, newextra); conf->gen.reset_source = source; conf->gen.reset_scontext = context; + conf->gen.reset_srole = srole; } for (stack = conf->gen.stack; stack; stack = stack->prev) { @@ -8215,6 +8282,7 @@ set_config_option(const char *name, const char *value, newextra); stack->source = source; stack->scontext = context; + stack->srole = srole; } } } @@ -8256,6 +8324,7 @@ set_config_option(const char *name, const char *value, newextra = conf->reset_extra; source = conf->gen.reset_source; context = conf->gen.reset_scontext; + srole = conf->gen.reset_srole; } if (prohibitValueChange) @@ -8290,6 +8359,7 @@ set_config_option(const char *name, const char *value, newextra); conf->gen.source = source; conf->gen.scontext = context; + conf->gen.srole = srole; } if (makeDefault) { @@ -8302,6 +8372,7 @@ set_config_option(const char *name, const char *value, newextra); conf->gen.reset_source = source; conf->gen.reset_scontext = context; + conf->gen.reset_srole = srole; } for (stack = conf->gen.stack; stack; stack = stack->prev) { @@ -8312,6 +8383,7 @@ set_config_option(const char *name, const char *value, newextra); stack->source = source; stack->scontext = context; + stack->srole = srole; } } } @@ -9358,17 +9430,19 @@ define_custom_variable(struct config_generic *variable) /* First, apply the reset value if any */ if (pHolder->reset_val) - (void) set_config_option(name, pHolder->reset_val, - pHolder->gen.reset_scontext, - pHolder->gen.reset_source, - GUC_ACTION_SET, true, WARNING, false); + (void) set_config_option_ext(name, pHolder->reset_val, + pHolder->gen.reset_scontext, + pHolder->gen.reset_source, + pHolder->gen.reset_srole, + GUC_ACTION_SET, true, WARNING, false); /* That should not have resulted in stacking anything */ Assert(variable->stack == NULL); /* Now, apply current and stacked values, in the order they were stacked */ reapply_stacked_values(variable, pHolder, pHolder->gen.stack, *(pHolder->variable), - pHolder->gen.scontext, pHolder->gen.source); + pHolder->gen.scontext, pHolder->gen.source, + pHolder->gen.srole); /* Also copy over any saved source-location information */ if (pHolder->gen.sourcefile) @@ -9399,7 +9473,8 @@ reapply_stacked_values(struct config_generic *variable, struct config_string *pHolder, GucStack *stack, const char *curvalue, - GucContext curscontext, GucSource cursource) + GucContext curscontext, GucSource cursource, + Oid cursrole) { const char *name = variable->name; GucStack *oldvarstack = variable->stack; @@ -9409,43 +9484,45 @@ reapply_stacked_values(struct config_generic *variable, /* First, recurse, so that stack items are processed bottom to top */ reapply_stacked_values(variable, pHolder, stack->prev, stack->prior.val.stringval, - stack->scontext, stack->source); + stack->scontext, stack->source, stack->srole); /* See how to apply the passed-in value */ switch (stack->state) { case GUC_SAVE: - (void) set_config_option(name, curvalue, - curscontext, cursource, - GUC_ACTION_SAVE, true, - WARNING, false); + (void) set_config_option_ext(name, curvalue, + curscontext, cursource, cursrole, + GUC_ACTION_SAVE, true, + WARNING, false); break; case GUC_SET: - (void) set_config_option(name, curvalue, - curscontext, cursource, - GUC_ACTION_SET, true, - WARNING, false); + (void) set_config_option_ext(name, curvalue, + curscontext, cursource, cursrole, + GUC_ACTION_SET, true, + WARNING, false); break; case GUC_LOCAL: - (void) set_config_option(name, curvalue, - curscontext, cursource, - GUC_ACTION_LOCAL, true, - WARNING, false); + (void) set_config_option_ext(name, curvalue, + curscontext, cursource, cursrole, + GUC_ACTION_LOCAL, true, + WARNING, false); break; case GUC_SET_LOCAL: /* first, apply the masked value as SET */ - (void) set_config_option(name, stack->masked.val.stringval, - stack->masked_scontext, PGC_S_SESSION, - GUC_ACTION_SET, true, - WARNING, false); + (void) set_config_option_ext(name, stack->masked.val.stringval, + stack->masked_scontext, + PGC_S_SESSION, + stack->masked_srole, + GUC_ACTION_SET, true, + WARNING, false); /* then apply the current value as LOCAL */ - (void) set_config_option(name, curvalue, - curscontext, cursource, - GUC_ACTION_LOCAL, true, - WARNING, false); + (void) set_config_option_ext(name, curvalue, + curscontext, cursource, cursrole, + GUC_ACTION_LOCAL, true, + WARNING, false); break; } @@ -9465,11 +9542,12 @@ reapply_stacked_values(struct config_generic *variable, */ if (curvalue != pHolder->reset_val || curscontext != pHolder->gen.reset_scontext || - cursource != pHolder->gen.reset_source) + cursource != pHolder->gen.reset_source || + cursrole != pHolder->gen.reset_srole) { - (void) set_config_option(name, curvalue, - curscontext, cursource, - GUC_ACTION_SET, true, WARNING, false); + (void) set_config_option_ext(name, curvalue, + curscontext, cursource, cursrole, + GUC_ACTION_SET, true, WARNING, false); variable->stack = NULL; } } @@ -10581,6 +10659,7 @@ _ShowOption(struct config_generic *record, bool use_units) * variable sourceline, integer * variable source, integer * variable scontext, integer +* variable srole, OID */ static void write_one_nondefault_variable(FILE *fp, struct config_generic *gconf) @@ -10647,6 +10726,7 @@ write_one_nondefault_variable(FILE *fp, struct config_generic *gconf) fwrite(&gconf->sourceline, 1, sizeof(gconf->sourceline), fp); fwrite(&gconf->source, 1, sizeof(gconf->source), fp); fwrite(&gconf->scontext, 1, sizeof(gconf->scontext), fp); + fwrite(&gconf->srole, 1, sizeof(gconf->srole), fp); } void @@ -10742,6 +10822,7 @@ read_nondefault_variables(void) int varsourceline; GucSource varsource; GucContext varscontext; + Oid varsrole; /* * Open file @@ -10778,10 +10859,12 @@ read_nondefault_variables(void) elog(FATAL, "invalid format of exec config params file"); if (fread(&varscontext, 1, sizeof(varscontext), fp) != sizeof(varscontext)) elog(FATAL, "invalid format of exec config params file"); + if (fread(&varsrole, 1, sizeof(varsrole), fp) != sizeof(varsrole)) + elog(FATAL, "invalid format of exec config params file"); - (void) set_config_option(varname, varvalue, - varscontext, varsource, - GUC_ACTION_SET, true, 0, true); + (void) set_config_option_ext(varname, varvalue, + varscontext, varsource, varsrole, + GUC_ACTION_SET, true, 0, true); if (varsourcefile[0]) set_config_sourcefile(varname, varsourcefile, varsourceline); @@ -10935,6 +11018,7 @@ estimate_variable_size(struct config_generic *gconf) size = add_size(size, sizeof(gconf->source)); size = add_size(size, sizeof(gconf->scontext)); + size = add_size(size, sizeof(gconf->srole)); return size; } @@ -11080,6 +11164,8 @@ serialize_variable(char **destptr, Size *maxbytes, sizeof(gconf->source)); do_serialize_binary(destptr, maxbytes, &gconf->scontext, sizeof(gconf->scontext)); + do_serialize_binary(destptr, maxbytes, &gconf->srole, + sizeof(gconf->srole)); } /* @@ -11181,6 +11267,7 @@ RestoreGUCState(void *gucstate) int varsourceline; GucSource varsource; GucContext varscontext; + Oid varsrole; char *srcptr = (char *) gucstate; char *srcend; Size len; @@ -11312,12 +11399,15 @@ RestoreGUCState(void *gucstate) &varsource, sizeof(varsource)); read_gucstate_binary(&srcptr, srcend, &varscontext, sizeof(varscontext)); + read_gucstate_binary(&srcptr, srcend, + &varsrole, sizeof(varsrole)); error_context_name_and_value[0] = varname; error_context_name_and_value[1] = varvalue; error_context_callback.arg = &error_context_name_and_value[0]; - result = set_config_option(varname, varvalue, varscontext, varsource, - GUC_ACTION_SET, true, ERROR, true); + result = set_config_option_ext(varname, varvalue, + varscontext, varsource, varsrole, + GUC_ACTION_SET, true, ERROR, true); if (result <= 0) ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), @@ -11590,7 +11680,7 @@ GUCArrayDelete(ArrayType *array, const char *name) /* * Given a GUC array, delete all settings from it that our permission * level allows: if superuser, delete them all; if regular user, only - * those that are PGC_USERSET + * those that are PGC_USERSET or we have permission to set */ ArrayType * GUCArrayReset(ArrayType *array) @@ -11678,14 +11768,16 @@ validate_option_array_item(const char *name, const char *value, * * name is a known GUC variable. Check the value normally, check * permissions normally (i.e., allow if variable is USERSET, or if it's - * SUSET and user is superuser). + * SUSET and user is superuser or holds ACL_SET permissions). * * name is not known, but exists or can be created as a placeholder (i.e., * it has a valid custom name). We allow this case if you're a superuser, * otherwise not. Superusers are assumed to know what they're doing. We * can't allow it for other users, because when the placeholder is - * resolved it might turn out to be a SUSET variable; - * define_custom_variable assumes we checked that. + * resolved it might turn out to be a SUSET variable. (With currently + * available infrastructure, we can actually handle such cases within the + * current session --- but once an entry is made in pg_db_role_setting, + * it's assumed to be fully validated.) * * name is not known and can't be created as a placeholder. Throw error, * unless skipIfNoPermissions is true, in which case return false. @@ -11703,7 +11795,8 @@ 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. */ - if (superuser()) + if (superuser() || + pg_parameter_aclcheck(name, GetUserId(), ACL_SET) == ACLCHECK_OK) return true; if (skipIfNoPermissions) return false; @@ -11715,7 +11808,9 @@ validate_option_array_item(const char *name, const char *value, /* manual permissions check so we can avoid an error being thrown */ if (gconf->context == PGC_USERSET) /* ok */ ; - else if (gconf->context == PGC_SUSET && superuser()) + else if (gconf->context == PGC_SUSET && + (superuser() || + pg_parameter_aclcheck(name, GetUserId(), ACL_SET) == ACLCHECK_OK)) /* ok */ ; else if (skipIfNoPermissions) return false; diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index d5976ecbfb..65eafb463b 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -388,6 +388,11 @@ extern int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload); +extern int set_config_option_ext(const char *name, const char *value, + GucContext context, GucSource source, + Oid srole, + GucAction action, bool changeVal, int elevel, + bool is_reload); extern void AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt); extern char *GetConfigOptionByName(const char *name, const char **varname, bool missing_ok); diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h index ba44f7437b..067d82ada9 100644 --- a/src/include/utils/guc_tables.h +++ b/src/include/utils/guc_tables.h @@ -120,6 +120,8 @@ typedef struct guc_stack /* masked value's source must be PGC_S_SESSION, so no need to store it */ GucContext scontext; /* context that set the prior value */ GucContext masked_scontext; /* context that set the masked value */ + Oid srole; /* role that set the prior value */ + Oid masked_srole; /* role that set the masked value */ config_var_value prior; /* previous value of variable */ config_var_value masked; /* SET value in a GUC_SET_LOCAL entry */ } GucStack; @@ -131,6 +133,10 @@ typedef struct guc_stack * applications may use the long description as well, and will append * it to the short description. (separated by a newline or '. ') * + * srole is the role that set the current value, or BOOTSTRAP_SUPERUSERID + * if the value came from an internal source or the config file. Similarly + * for reset_srole (which is usually BOOTSTRAP_SUPERUSERID, but not always). + * * Note that sourcefile/sourceline are kept here, and not pushed into stacked * values, although in principle they belong with some stacked value if the * active value is session- or transaction-local. This is to avoid bloating @@ -152,6 +158,8 @@ struct config_generic GucSource reset_source; /* source of the reset_value */ GucContext scontext; /* context that set the current value */ GucContext reset_scontext; /* context that set the reset value */ + Oid srole; /* role that set the current value */ + Oid reset_srole; /* role that set the reset value */ GucStack *stack; /* stacked prior values */ void *extra; /* "extra" pointer for current actual value */ char *last_reported; /* if variable is GUC_REPORT, value last sent diff --git a/src/pl/plperl/expected/plperl_init.out b/src/pl/plperl/expected/plperl_init.out index 133828e9f3..4b7e925419 100644 --- a/src/pl/plperl/expected/plperl_init.out +++ b/src/pl/plperl/expected/plperl_init.out @@ -1,4 +1,4 @@ --- test plperl.on_plperl_init errors are fatal +-- test plperl.on_plperl_init -- This test tests setting on_plperl_init after loading plperl LOAD 'plperl'; SET SESSION plperl.on_plperl_init = ' system("/nonesuch"); '; @@ -12,3 +12,29 @@ DO $$ warn 42 $$ language plperl; ERROR: 'system' trapped by operation mask at line 1. CONTEXT: while executing plperl.on_plperl_init PL/Perl anonymous code block +-- +-- Reconnect (to unload plperl), then test setting on_plperl_init +-- as an unprivileged user +-- +\c - +CREATE ROLE regress_plperl_user; +SET ROLE regress_plperl_user; +-- this succeeds, since the GUC isn't known yet +SET SESSION plperl.on_plperl_init = 'test'; +RESET ROLE; +LOAD 'plperl'; +WARNING: permission denied to set parameter "plperl.on_plperl_init" +SHOW plperl.on_plperl_init; + plperl.on_plperl_init +----------------------- + +(1 row) + +DO $$ warn 42 $$ language plperl; +WARNING: 42 at line 1. +-- now we won't be allowed to set it in the first place +SET ROLE regress_plperl_user; +SET SESSION plperl.on_plperl_init = 'test'; +ERROR: permission denied to set parameter "plperl.on_plperl_init" +RESET ROLE; +DROP ROLE regress_plperl_user; diff --git a/src/pl/plperl/sql/plperl_init.sql b/src/pl/plperl/sql/plperl_init.sql index 4ebf3f86eb..2aa3811033 100644 --- a/src/pl/plperl/sql/plperl_init.sql +++ b/src/pl/plperl/sql/plperl_init.sql @@ -1,4 +1,4 @@ --- test plperl.on_plperl_init errors are fatal +-- test plperl.on_plperl_init -- This test tests setting on_plperl_init after loading plperl LOAD 'plperl'; @@ -8,3 +8,34 @@ SET SESSION plperl.on_plperl_init = ' system("/nonesuch"); '; SHOW plperl.on_plperl_init; DO $$ warn 42 $$ language plperl; + +-- +-- Reconnect (to unload plperl), then test setting on_plperl_init +-- as an unprivileged user +-- + +\c - + +CREATE ROLE regress_plperl_user; + +SET ROLE regress_plperl_user; + +-- this succeeds, since the GUC isn't known yet +SET SESSION plperl.on_plperl_init = 'test'; + +RESET ROLE; + +LOAD 'plperl'; + +SHOW plperl.on_plperl_init; + +DO $$ warn 42 $$ language plperl; + +-- now we won't be allowed to set it in the first place +SET ROLE regress_plperl_user; + +SET SESSION plperl.on_plperl_init = 'test'; + +RESET ROLE; + +DROP ROLE regress_plperl_user; diff --git a/src/test/modules/unsafe_tests/expected/guc_privs.out b/src/test/modules/unsafe_tests/expected/guc_privs.out index de4e1b3cdf..54f95b2334 100644 --- a/src/test/modules/unsafe_tests/expected/guc_privs.out +++ b/src/test/modules/unsafe_tests/expected/guc_privs.out @@ -418,6 +418,8 @@ FROM pg_get_object_address('parameter ACL', '{work_mem}', '{}') goa; pg_parameter_acl | work_mem | 0 (1 row) +-- Make a per-role setting that regress_host_resource_admin can't change +ALTER ROLE regress_host_resource_admin SET lc_messages = 'C'; -- Perform some operations as user 'regress_host_resource_admin' SET SESSION AUTHORIZATION regress_host_resource_admin; ALTER SYSTEM SET autovacuum_work_mem = 32; -- ok, privileges have been granted @@ -457,6 +459,40 @@ SELECT set_config ('temp_buffers', '8192', false); -- ok ALTER SYSTEM RESET autovacuum_work_mem; -- ok, privileges have been granted ALTER SYSTEM RESET ALL; -- fail, insufficient privileges ERROR: permission denied to perform ALTER SYSTEM RESET ALL +ALTER ROLE regress_host_resource_admin SET lc_messages = 'POSIX'; -- fail +ERROR: permission denied to set parameter "lc_messages" +ALTER ROLE regress_host_resource_admin SET max_stack_depth = '1MB'; -- ok +SELECT setconfig FROM pg_db_role_setting + WHERE setrole = 'regress_host_resource_admin'::regrole; + setconfig +------------------------------------- + {lc_messages=C,max_stack_depth=1MB} +(1 row) + +ALTER ROLE regress_host_resource_admin RESET max_stack_depth; -- ok +SELECT setconfig FROM pg_db_role_setting + WHERE setrole = 'regress_host_resource_admin'::regrole; + setconfig +----------------- + {lc_messages=C} +(1 row) + +ALTER ROLE regress_host_resource_admin SET max_stack_depth = '1MB'; -- ok +SELECT setconfig FROM pg_db_role_setting + WHERE setrole = 'regress_host_resource_admin'::regrole; + setconfig +------------------------------------- + {lc_messages=C,max_stack_depth=1MB} +(1 row) + +ALTER ROLE regress_host_resource_admin RESET ALL; -- doesn't reset lc_messages +SELECT setconfig FROM pg_db_role_setting + WHERE setrole = 'regress_host_resource_admin'::regrole; + setconfig +----------------- + {lc_messages=C} +(1 row) + -- Check dropping/revoking behavior SET SESSION AUTHORIZATION regress_admin; DROP ROLE regress_host_resource_admin; -- fail, privileges remain diff --git a/src/test/modules/unsafe_tests/sql/guc_privs.sql b/src/test/modules/unsafe_tests/sql/guc_privs.sql index a86b957b9c..6c7733fc39 100644 --- a/src/test/modules/unsafe_tests/sql/guc_privs.sql +++ b/src/test/modules/unsafe_tests/sql/guc_privs.sql @@ -163,6 +163,9 @@ SELECT classid::regclass, objsubid FROM pg_get_object_address('parameter ACL', '{work_mem}', '{}') goa; +-- Make a per-role setting that regress_host_resource_admin can't change +ALTER ROLE regress_host_resource_admin SET lc_messages = 'C'; + -- Perform some operations as user 'regress_host_resource_admin' SET SESSION AUTHORIZATION regress_host_resource_admin; ALTER SYSTEM SET autovacuum_work_mem = 32; -- ok, privileges have been granted @@ -187,6 +190,19 @@ ALTER SYSTEM RESET lc_messages; -- fail, insufficient privileges SELECT set_config ('temp_buffers', '8192', false); -- ok ALTER SYSTEM RESET autovacuum_work_mem; -- ok, privileges have been granted ALTER SYSTEM RESET ALL; -- fail, insufficient privileges +ALTER ROLE regress_host_resource_admin SET lc_messages = 'POSIX'; -- fail +ALTER ROLE regress_host_resource_admin SET max_stack_depth = '1MB'; -- ok +SELECT setconfig FROM pg_db_role_setting + WHERE setrole = 'regress_host_resource_admin'::regrole; +ALTER ROLE regress_host_resource_admin RESET max_stack_depth; -- ok +SELECT setconfig FROM pg_db_role_setting + WHERE setrole = 'regress_host_resource_admin'::regrole; +ALTER ROLE regress_host_resource_admin SET max_stack_depth = '1MB'; -- ok +SELECT setconfig FROM pg_db_role_setting + WHERE setrole = 'regress_host_resource_admin'::regrole; +ALTER ROLE regress_host_resource_admin RESET ALL; -- doesn't reset lc_messages +SELECT setconfig FROM pg_db_role_setting + WHERE setrole = 'regress_host_resource_admin'::regrole; -- Check dropping/revoking behavior SET SESSION AUTHORIZATION regress_admin;