GUC --- prevent non-super user changes - Mailing list pgsql-hackers

From Bruce Momjian
Subject GUC --- prevent non-super user changes
Date
Msg-id 200306110501.h5B51Fc14049@candle.pha.pa.us
Whole thread Raw
In response to This is not gonna do  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: GUC --- prevent non-super user changes  (Bruce Momjian <pgman@candle.pha.pa.us>)
List pgsql-hackers
Tom Lane wrote:
> The recent change to make log_min_messages SUSET provokes the following
> behavior:
>
> $ export PGOPTIONS="-d 5"
> $ psql
> psql: FATAL:  'log_min_messages': permission denied
> $
>
> Considering that I *am* superuser, this is quite unacceptable.
> If you don't want to revert the change, propose another solution.

Here is a proposed fix for the new SUSET of various variables.  The
solution is to create a new GUC context called PGC_USERLIMIT, which
limits changes by non-super users.  For example, non-super users can
turn on logging, but can't turn it off, and log_min_* logging can have
added output, but not less output.

The first part of the patch prevent client PGOPTIONS from lowering the
debug level.  The second part adds this new GUC context, then allows it
to be set properly.  The tests are in two parts --- the first prevents
non-super users from changing the value inappropriately, and the second
allows postgresql.conf changes to apply to existing backends, i.e.  if
postgresql.conf turns logging off via SET, turning it on via
postgresql.conf should propogate to the client, because the client can't
turn something off that the admin wants turned on --- that is the tricky
part that we have to be able to handle the settings in any order.

Peter, how does this look?  Is reset_val the proper value to test?


--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/tcop/postgres.c,v
retrieving revision 1.346
diff -c -c -r1.346 postgres.c
*** src/backend/tcop/postgres.c    27 May 2003 17:49:46 -0000    1.346
--- src/backend/tcop/postgres.c    11 Jun 2003 04:29:55 -0000
***************
*** 1921,1927 ****
      bool        secure;
      int            errs = 0;
      int            debug_flag = 0;
!     GucContext    ctx;
      GucSource    gucsource;
      char       *tmp;
      int            firstchar;
--- 1921,1927 ----
      bool        secure;
      int            errs = 0;
      int            debug_flag = 0;
!     GucContext    ctx, debug_context;
      GucSource    gucsource;
      char       *tmp;
      int            firstchar;
***************
*** 1996,2002 ****

      /* all options are allowed until '-p' */
      secure = true;
!     ctx = PGC_POSTMASTER;
      gucsource = PGC_S_ARGV;        /* initial switches came from command line */

      while ((flag = getopt(argc, argv, "A:B:c:CD:d:Eef:FiNOPo:p:S:st:v:W:x:-:")) != -1)
--- 1996,2002 ----

      /* all options are allowed until '-p' */
      secure = true;
!     ctx = debug_context = PGC_POSTMASTER;
      gucsource = PGC_S_ARGV;        /* initial switches came from command line */

      while ((flag = getopt(argc, argv, "A:B:c:CD:d:Eef:FiNOPo:p:S:st:v:W:x:-:")) != -1)
***************
*** 2033,2057 ****

              case 'd':            /* debug level */
                  {
!                     debug_flag = atoi(optarg);
!                     /* Set server debugging level. */
!                     if (atoi(optarg) != 0)
                      {
!                         char       *debugstr = palloc(strlen("debug") + strlen(optarg) + 1);
!
!                         sprintf(debugstr, "debug%s", optarg);
!                         SetConfigOption("log_min_messages", debugstr, ctx, gucsource);
!                         pfree(debugstr);
!
                      }
-                     else
-                         /*
-                          * -d0 allows user to prevent postmaster debug
-                          * from propagating to backend.  It would be nice
-                          * to set it to the postgresql.conf value here.
-                          */
-                         SetConfigOption("log_min_messages", "notice",
-                                         ctx, gucsource);
                  }
                  break;

--- 2033,2066 ----

              case 'd':            /* debug level */
                  {
!                     /*
!                      *    Client option can't decrease debug level.
!                      *    We have to do the test here because we group priv and client
!                      *    set GUC calls below, after we know the final debug value.
!                      */
!                     if (ctx != PGC_BACKEND || atoi(optarg) > debug_flag)
                      {
!                         debug_flag = atoi(optarg);
!                         debug_context = ctx;    /* save context for use below */
!                         /* Set server debugging level. */
!                         if (debug_flag != 0)
!                         {
!                             char       *debugstr = palloc(strlen("debug") + strlen(optarg) + 1);
!
!                             sprintf(debugstr, "debug%s", optarg);
!                             SetConfigOption("log_min_messages", debugstr, ctx, gucsource);
!                             pfree(debugstr);
!
!                         }
!                         else
!                             /*
!                              * -d0 allows user to prevent postmaster debug
!                              * from propagating to backend.  It would be nice
!                              * to set it to the postgresql.conf value here.
!                              */
!                             SetConfigOption("log_min_messages", "notice",
!                                             ctx, gucsource);
                      }
                  }
                  break;

***************
*** 2301,2320 ****


      /*
!      * -d is not the same as setting
!      * log_min_messages because it enables other
!      * output options.
       */
      if (debug_flag >= 1)
!         SetConfigOption("log_connections", "true", ctx, gucsource);
      if (debug_flag >= 2)
!         SetConfigOption("log_statement", "true", ctx, gucsource);
      if (debug_flag >= 3)
!         SetConfigOption("debug_print_parse", "true", ctx, gucsource);
      if (debug_flag >= 4)
!         SetConfigOption("debug_print_plan", "true", ctx, gucsource);
      if (debug_flag >= 5)
!         SetConfigOption("debug_print_rewritten", "true", ctx, gucsource);

      /*
       * Process any additional GUC variable settings passed in startup packet.
--- 2310,2328 ----


      /*
!      * -d is not the same as setting log_min_messages because it enables
!      * other output options.
       */
      if (debug_flag >= 1)
!         SetConfigOption("log_connections", "true", debug_context, gucsource);
      if (debug_flag >= 2)
!         SetConfigOption("log_statement", "true", debug_context, gucsource);
      if (debug_flag >= 3)
!         SetConfigOption("debug_print_parse", "true", debug_context, gucsource);
      if (debug_flag >= 4)
!         SetConfigOption("debug_print_plan", "true", debug_context, gucsource);
      if (debug_flag >= 5)
!         SetConfigOption("debug_print_rewritten", "true", debug_context, gucsource);

      /*
       * Process any additional GUC variable settings passed in startup packet.
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/utils/misc/guc.c,v
retrieving revision 1.127
diff -c -c -r1.127 guc.c
*** src/backend/utils/misc/guc.c    28 May 2003 18:19:09 -0000    1.127
--- src/backend/utils/misc/guc.c    11 Jun 2003 04:30:01 -0000
***************
*** 406,416 ****
      },

      {
!         {"log_statement", PGC_SUSET}, &log_statement,
          false, NULL, NULL
      },
      {
!         {"log_duration", PGC_SUSET}, &log_duration,
          false, NULL, NULL
      },
      {
--- 406,416 ----
      },

      {
!         {"log_statement", PGC_USERLIMIT}, &log_statement,
          false, NULL, NULL
      },
      {
!         {"log_duration", PGC_USERLIMIT}, &log_duration,
          false, NULL, NULL
      },
      {
***************
*** 431,449 ****
      },

      {
!         {"log_parser_stats", PGC_SUSET}, &log_parser_stats,
          false, NULL, NULL
      },
      {
!         {"log_planner_stats", PGC_SUSET}, &log_planner_stats,
          false, NULL, NULL
      },
      {
!         {"log_executor_stats", PGC_SUSET}, &log_executor_stats,
          false, NULL, NULL
      },
      {
!         {"log_statement_stats", PGC_SUSET}, &log_statement_stats,
          false, NULL, NULL
      },
  #ifdef BTREE_BUILD_STATS
--- 431,449 ----
      },

      {
!         {"log_parser_stats", PGC_USERLIMIT}, &log_parser_stats,
          false, NULL, NULL
      },
      {
!         {"log_planner_stats", PGC_USERLIMIT}, &log_planner_stats,
          false, NULL, NULL
      },
      {
!         {"log_executor_stats", PGC_USERLIMIT}, &log_executor_stats,
          false, NULL, NULL
      },
      {
!         {"log_statement_stats", PGC_USERLIMIT}, &log_statement_stats,
          false, NULL, NULL
      },
  #ifdef BTREE_BUILD_STATS
***************
*** 797,803 ****
      },

      {
!         {"log_min_error_statement", PGC_SUSET}, &log_min_error_statement_str,
          "panic", assign_min_error_statement, NULL
      },

--- 797,803 ----
      },

      {
!         {"log_min_error_statement", PGC_USERLIMIT}, &log_min_error_statement_str,
          "panic", assign_min_error_statement, NULL
      },

***************
*** 884,890 ****
      },

      {
!         {"log_min_messages", PGC_SUSET}, &log_min_messages_str,
          "notice", assign_log_min_messages, NULL
      },

--- 884,890 ----
      },

      {
!         {"log_min_messages", PGC_USERLIMIT}, &log_min_messages_str,
          "notice", assign_log_min_messages, NULL
      },

***************
*** 1180,1185 ****
--- 1180,1189 ----
                      struct config_string *conf = (struct config_string *) gconf;
                      char       *str;

+                     Assert(conf->gen.context != PGC_USERLIMIT ||
+                            conf->assign_hook == assign_log_min_messages ||
+                            conf->assign_hook == assign_client_min_messages ||
+                            conf->assign_hook == assign_min_error_statement);
                      *conf->variable = NULL;
                      conf->reset_val = NULL;
                      conf->session_val = NULL;
***************
*** 1276,1282 ****
          struct config_generic *gconf = guc_variables[i];

          /* Don't reset non-SET-able values */
!         if (gconf->context != PGC_SUSET && gconf->context != PGC_USERSET)
              continue;
          /* Don't reset if special exclusion from RESET ALL */
          if (gconf->flags & GUC_NO_RESET_ALL)
--- 1280,1288 ----
          struct config_generic *gconf = guc_variables[i];

          /* Don't reset non-SET-able values */
!         if (gconf->context != PGC_SUSET &&
!             gconf->context != PGC_USERLIMIT &&
!             gconf->context != PGC_USERSET)
              continue;
          /* Don't reset if special exclusion from RESET ALL */
          if (gconf->flags & GUC_NO_RESET_ALL)
***************
*** 1810,1815 ****
--- 1816,1822 ----
                  return false;
              }
              break;
+         case PGC_USERLIMIT:    /* USERLIMIT permissions checked below */
          case PGC_USERSET:
              /* always okay */
              break;
***************
*** 1861,1866 ****
--- 1868,1889 ----
                               name);
                          return false;
                      }
+                     /* Limit non-super user changes */
+                     if (record->context == PGC_USERLIMIT &&
+                         source > PGC_S_USERSTART &&
+                         newval < conf->reset_val && !superuser())
+                     {
+                         elog(elevel, "'%s': permission denied\n"
+                                 "Only super-users can set this value to false.",
+                                 name);
+                         return false;
+                     }
+                     /* Allow admin change to override user change */
+                     if (!DoIt && record->context == PGC_USERLIMIT &&
+                         source < PGC_S_USERSTART &&
+                         record->reset_source > PGC_S_USERSTART &&
+                         newval > conf->reset_val && !superuser())
+                             DoIt = true;
                  }
                  else
                  {
***************
*** 1932,1937 ****
--- 1955,1976 ----
                               name, newval, conf->min, conf->max);
                          return false;
                      }
+                     /* Limit non-super user changes */
+                     if (record->context == PGC_USERLIMIT &&
+                         source > PGC_S_USERSTART &&
+                         newval < conf->reset_val && !superuser())
+                     {
+                         elog(elevel, "'%s': permission denied\n"
+                                 "Only super-users can increase this value.",
+                                 name);
+                         return false;
+                     }
+                     /* Allow admin change to override user change */
+                     if (!DoIt && record->context == PGC_USERLIMIT &&
+                         source < PGC_S_USERSTART &&
+                         record->reset_source > PGC_S_USERSTART &&
+                         newval > conf->reset_val && !superuser())
+                             DoIt = true;
                  }
                  else
                  {
***************
*** 2003,2008 ****
--- 2042,2063 ----
                               name, newval, conf->min, conf->max);
                          return false;
                      }
+                     /* Limit non-super user changes */
+                     if (record->context == PGC_USERLIMIT &&
+                         source > PGC_S_USERSTART &&
+                         newval < conf->reset_val && !superuser())
+                     {
+                         elog(elevel, "'%s': permission denied\n"
+                                 "Only super-users can increase this value.",
+                                 name);
+                         return false;
+                     }
+                     /* Allow admin change to override user change */
+                     if (!DoIt && record->context == PGC_USERLIMIT &&
+                         source < PGC_S_USERSTART &&
+                         record->reset_source > PGC_S_USERSTART &&
+                         newval > conf->reset_val && !superuser())
+                             DoIt = true;
                  }
                  else
                  {
***************
*** 2067,2072 ****
--- 2122,2151 ----
                          elog(elevel, "out of memory");
                          return false;
                      }
+
+                     if (*conf->variable)
+                     {
+                         int old_int_value, new_int_value;
+
+                         /* Limit non-super user changes */
+                         assign_msglvl(&old_int_value, conf->reset_val, true, interactive);
+                         assign_msglvl(&new_int_value, newval, true, interactive);
+                         if (record->context == PGC_USERLIMIT &&
+                             source > PGC_S_USERSTART &&
+                             new_int_value > old_int_value && !superuser())
+                         {
+                             elog(elevel, "'%s': permission denied\n"
+                                         "Only super-users can increase this value.",
+                                         name);
+                             return false;
+                         }
+                         /* Allow admin change to override user change */
+                         if (!DoIt && record->context == PGC_USERLIMIT &&
+                             source < PGC_S_USERSTART &&
+                             record->reset_source > PGC_S_USERSTART &&
+                             newval > conf->reset_val && !superuser())
+                                 DoIt = true;
+                         }
                  }
                  else if (conf->reset_val)
                  {
***************
*** 3454,3457 ****


  #include "guc-file.c"
-
--- 3533,3535 ----
Index: src/include/utils/guc.h
===================================================================
RCS file: /cvsroot/pgsql-server/src/include/utils/guc.h,v
retrieving revision 1.31
diff -c -c -r1.31 guc.h
*** src/include/utils/guc.h    6 May 2003 20:26:28 -0000    1.31
--- src/include/utils/guc.h    11 Jun 2003 04:30:02 -0000
***************
*** 48,53 ****
--- 48,56 ----
   * be set in the connection startup packet, because when it is processed
   * we don't yet know if the user is a superuser.
   *
+  * USERLIMIT options can only be manipulated in certain ways by
+  * non-super users.
+  *
   * USERSET options can be set by anyone any time.
   */
  typedef enum
***************
*** 57,62 ****
--- 60,66 ----
      PGC_SIGHUP,
      PGC_BACKEND,
      PGC_SUSET,
+     PGC_USERLIMIT,
      PGC_USERSET
  } GucContext;

***************
*** 76,86 ****
      PGC_S_ENV_VAR = 1,            /* postmaster environment variable */
      PGC_S_FILE = 2,                /* postgresql.conf */
      PGC_S_ARGV = 3,                /* postmaster command line */
!     PGC_S_DATABASE = 4,            /* per-database setting */
!     PGC_S_USER = 5,                /* per-user setting */
!     PGC_S_CLIENT = 6,            /* from client connection request */
!     PGC_S_OVERRIDE = 7,            /* special case to forcibly set default */
!     PGC_S_SESSION = 8            /* SET command */
  } GucSource;


--- 80,95 ----
      PGC_S_ENV_VAR = 1,            /* postmaster environment variable */
      PGC_S_FILE = 2,                /* postgresql.conf */
      PGC_S_ARGV = 3,                /* postmaster command line */
!     PGC_S_USERSTART=4,            /*
!                                  *    Settings below are controlled by users.
!                                  *    This is used by PGC_USERLIMT to prevent
!                                  *    non-super users from changing certain settings.
!                                  */
!     PGC_S_DATABASE = 5,            /* per-database setting */
!     PGC_S_USER = 6,                /* per-user setting */
!     PGC_S_CLIENT = 7,            /* from client connection request */
!     PGC_S_OVERRIDE = 8,            /* special case to forcibly set default */
!     PGC_S_SESSION = 9            /* SET command */
  } GucSource;



pgsql-hackers by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: Feature freeze date
Next
From: Bruce Momjian
Date:
Subject: Re: GUC --- prevent non-super user changes