On Tue, 2012-10-09 at 20:45 -0400, Peter Eisentraut wrote:
> About that plugins directory ($libdir/plugins) ... I don't think we
> ever
> really got that to work sensibly.  I don't remember the original
> design
> discussion, but I have seen a number of explanations offered over the
> years.  It's not clear who decides what to put in there (plugin
> author,
> packager, DBA?), how to put it there (move it, copy it, symlink it? --
> no support in pgxs), and based on what criteria.
> 
> It would seem to be much more in the spirit of things to simply list
> the
> allowed plugins in a GUC variable, like
> 
> some_clever_name_here = $libdir/this, $libdir/that 

Here is a patch, with some_clever_name = user_loadable_libraries.

There are obviously some conflict/transition issues with using
user_loadable_libraries vs the plugins directory.  I have tried to
explain the mechanisms in the documentation, but there are other choices
possible in some situations.

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b7df8ce..e276dd6 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5547,16 +5547,25 @@ <title>Other Defaults</title>
        </para>
 
        <para>
-        Because this is not a superuser-only option, the libraries
-        that can be loaded are restricted to those appearing in the
-        <filename>plugins</> subdirectory of the installation's
-        standard library directory.  (It is the database administrator's
+        Because this is not a superuser-only option, the libraries that can be
+        loaded are restricted.  Either they have to be listed
+        in <xref linkend="guc-user-loadable-libraries"> or they have to be
+        stored in the <filename>plugins</> subdirectory of the installation's
+        standard library directory.  It is the database administrator's
         responsibility to ensure that only <quote>safe</> libraries
-        are installed there.)  Entries in <varname>local_preload_libraries</>
-        can specify this directory explicitly, for example
-        <literal>$libdir/plugins/mylib</literal>, or just specify
-        the library name &mdash; <literal>mylib</literal> would have
-        the same effect as <literal>$libdir/plugins/mylib</literal>.
+        are enabled in these ways.
+       </para>
+
+       <para>
+        Entries in <varname>local_preload_libraries</>
+        can specify the <filename>plugins</filename> directory explicitly, for example
+        <literal>$libdir/plugins/mylib</literal>.  If they don't, but the
+        <filename>plugins</filename> directory exists, then a library name
+        without a slash, such as <literal>mylib</literal> will be expanded
+        to <literal>$libdir/plugins/mylib</literal>.  If
+        the <literal>plugins</literal> directory does not exist, then all
+        listed names would need to be listed
+        in <literal>user_loadable_libraries</literal>.
        </para>
 
        <para>
@@ -5584,6 +5593,27 @@ <title>Other Defaults</title>
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-user-loadable-libraries" xreflabel="user_loadable_libraries">
+      <term><varname>user_loadable_libraries</varname> (<type>string</type>)</term>
+      <indexterm>
+       <primary><varname>user_loadable_libraries</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        This parameter specifies a list of shared libraries that unprivileged
+        users can load using either the <command>LOAD</command> command or by
+        listing them in <xref linkend="guc-local-preload-libraries">.  The
+        library names listed here and the libraries names invoked by one the
+        mentioned methods must match verbatim in order for the loading to be
+        allowed.
+       </para>
+
+       <para>
+        Using this parameter is an alternative to placing those libraries into
+        the <filename>plugins</filename> directory.
+       </para>
+      </listitem>
+      </varlistentry>
      </variablelist>
     </sect2>
    </sect1>
diff --git a/doc/src/sgml/ref/load.sgml b/doc/src/sgml/ref/load.sgml
index f44f313..fc77bd5 100644
--- a/doc/src/sgml/ref/load.sgml
+++ b/doc/src/sgml/ref/load.sgml
@@ -51,11 +51,12 @@ <title>Description</title>
 
   <para>
    Non-superusers can only apply <command>LOAD</> to library files
+   listed in <xref linkend='guc-user-loadable-libraries'> or
    located in <filename>$libdir/plugins/</> &mdash; the specified
    <replaceable class="PARAMETER">filename</replaceable> must begin
-   with exactly that string.  (It is the database administrator's
+   with exactly that string.  It is the database administrator's
    responsibility to ensure that only <quote>safe</> libraries
-   are installed there.)
+   are enabled in these ways.
   </para>
  </refsect1>
 
diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
index 562a7c9..eab1d2b 100644
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -23,6 +23,8 @@
 #endif
 #include "lib/stringinfo.h"
 #include "miscadmin.h"
+#include "nodes/pg_list.h"
+#include "utils/builtins.h"
 #include "utils/dynamic_loader.h"
 #include "utils/hsearch.h"
 
@@ -70,6 +72,7 @@
 #endif
 
 char	   *Dynamic_library_path;
+char	   *user_loadable_libraries_string;
 
 static void *internal_load_library(const char *libname);
 static void incompatible_module_error(const char *libname,
@@ -538,12 +541,44 @@ static void incompatible_module_error(const char *libname,
 static void
 check_restricted_library_name(const char *name)
 {
-	if (strncmp(name, "$libdir/plugins/", 16) != 0 ||
-		first_dir_separator(name + 16) != NULL)
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("access to library \"%s\" is not allowed",
-						name)));
+	char	   *rawstring;
+	List	   *elemlist;
+	ListCell   *l;
+
+	rawstring = pstrdup(user_loadable_libraries_string);
+
+	if (!SplitIdentifierString(rawstring, ',', &elemlist))
+	{
+		pfree(rawstring);
+		list_free(elemlist);
+		ereport(LOG,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("invalid list syntax in parameter \"%s\"",
+						"user_loadable_libraries")));
+		return;
+	}
+
+	foreach(l, elemlist)
+	{
+		char	   *tok = (char *) lfirst(l);
+
+		if (strcmp(tok, name) == 0)
+		{
+			list_free(elemlist);
+			return;
+		}
+	}
+
+	list_free(elemlist);
+
+	if (strncmp(name, "$libdir/plugins/", 16) == 0 &&
+		first_dir_separator(name + 16) == NULL)
+		return;
+
+	ereport(ERROR,
+			(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+			 errmsg("access to library \"%s\" is not allowed",
+					name)));
 }
 
 /*
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 24ca97d..c30f414 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -1241,6 +1241,9 @@
 	List	   *elemlist;
 	int			elevel;
 	ListCell   *l;
+	char		path[MAXPGPATH];
+	struct stat	statbuf;
+	bool		use_plugins_dir = false;
 
 	if (libraries == NULL || libraries[0] == '\0')
 		return;					/* nothing to do */
@@ -1273,6 +1276,10 @@
 #endif
 		elevel = LOG;
 
+	snprintf(path, sizeof(path), "%s/plugins", pkglib_path);
+	if (stat(path, &statbuf) == 0)
+		use_plugins_dir = true;
+
 	foreach(l, elemlist)
 	{
 		char	   *tok = (char *) lfirst(l);
@@ -1281,7 +1288,7 @@
 		filename = pstrdup(tok);
 		canonicalize_path(filename);
 		/* If restricting, insert $libdir/plugins if not mentioned already */
-		if (restricted && first_dir_separator(filename) == NULL)
+		if (restricted && first_dir_separator(filename) == NULL && use_plugins_dir)
 		{
 			char	   *expanded;
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index ac5e4f3..dadfb29 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2739,6 +2739,17 @@ static char *config_enum_get_options(struct config_enum * record,
 	},
 
 	{
+		{"user_loadable_libraries", PGC_SIGHUP, CLIENT_CONN_OTHER,
+			gettext_noop("Lists shared libraries that non-superusers can load using local_preload_libraries or LOAD."),
+			NULL,
+			GUC_LIST_INPUT | GUC_LIST_QUOTE
+		},
+		&user_loadable_libraries_string,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
 		{"search_path", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the schema search order for names that are not schema-qualified."),
 			NULL,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index eeb9b82..486652d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -525,6 +525,7 @@
 
 #dynamic_library_path = '$libdir'
 #local_preload_libraries = ''
+#user_loadable_libraries = ''
 
 
 #------------------------------------------------------------------------------
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index fe4a41b..9d9ae9b 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -629,6 +629,7 @@ extern bool get_call_expr_arg_stable(fmNodePtr expr, int argnum);
  * Routines in dfmgr.c
  */
 extern char *Dynamic_library_path;
+extern char *user_loadable_libraries_string;
 
 extern PGFunction load_external_function(char *filename, char *funcname,
 					   bool signalNotFound, void **filehandle);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to