Tom Lane wrote:
Yeah.  We could make one further refinement: callers that don't care
about acquiring an error string can pass NULL for the errmsg parameter.
That tells PQconninfoParse to throw away the errmsg string anyway.
With that, the minimal case isn't much uglier than your original:
just need a NULL arg tacked onto the call.

True

BTW, the usual method for doing this is just to give the caller back the
errorBuf.data, not incur an additional strdup that could fail.

OK, was entirely sure that was safe.

New patch attached.

Joe
Index: contrib/dblink/dblink.c
===================================================================
RCS file: /opt/src/cvs/pgsql/contrib/dblink/dblink.c,v
retrieving revision 1.74
diff -c -r1.74 dblink.c
*** contrib/dblink/dblink.c	3 Jul 2008 03:56:57 -0000	1.74
--- contrib/dblink/dblink.c	22 Sep 2008 02:09:39 -0000
***************
*** 93,98 ****
--- 93,99 ----
  static HeapTuple get_tuple_of_interest(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pkattvals);
  static Oid	get_relid_from_relname(text *relname_text);
  static char *generate_relation_name(Oid relid);
+ static void dblink_connstr_check(const char *connstr);
  static void dblink_security_check(PGconn *conn, remoteConn *rconn);
  static void dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_msg, bool fail);
  
***************
*** 165,170 ****
--- 166,172 ----
  			else \
  			{ \
  				connstr = conname_or_str; \
+ 				dblink_connstr_check(connstr); \
  				conn = PQconnectdb(connstr); \
  				if (PQstatus(conn) == CONNECTION_BAD) \
  				{ \
***************
*** 229,234 ****
--- 231,239 ----
  
  	if (connname)
  		rconn = (remoteConn *) palloc(sizeof(remoteConn));
+ 
+ 	/* check password in connection string if not superuser */
+ 	dblink_connstr_check(connstr);
  	conn = PQconnectdb(connstr);
  
  	MemoryContextSwitchTo(oldcontext);
***************
*** 246,252 ****
  				 errdetail("%s", msg)));
  	}
  
! 	/* check password used if not superuser */
  	dblink_security_check(conn, rconn);
  
  	if (connname)
--- 251,257 ----
  				 errdetail("%s", msg)));
  	}
  
! 	/* check password actually used if not superuser */
  	dblink_security_check(conn, rconn);
  
  	if (connname)
***************
*** 2252,2257 ****
--- 2257,2293 ----
  }
  
  static void
+ dblink_connstr_check(const char *connstr)
+ {
+ 	if (!superuser())
+ 	{
+ 		PQconninfoOption   *options;
+ 		PQconninfoOption   *option;
+ 		bool				conn_used_password = false;
+ 
+ 		options = PQconninfoParse(connstr, NULL);
+ 		for (option = options; option->keyword != NULL; option++)
+ 		{
+ 			if (strcmp(option->keyword, "password") == 0)
+ 			{
+ 				if (option->val != NULL && option->val[0] != '\0')
+ 				{
+ 					conn_used_password = true;
+ 					break;
+ 				}
+ 			}
+ 		}
+ 		PQconninfoFree(options);
+ 
+ 		if (!conn_used_password)
+ 			ereport(ERROR,
+ 				  (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+ 				   errmsg("password is required"),
+ 				   errdetail("Non-superuser must provide a password in the connection string.")));
+ 	}
+ }
+ 
+ static void
  dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_msg, bool fail)
  {
  	int			level;
Index: doc/src/sgml/libpq.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.263
diff -c -r1.263 libpq.sgml
*** doc/src/sgml/libpq.sgml	19 Sep 2008 20:06:13 -0000	1.263
--- doc/src/sgml/libpq.sgml	22 Sep 2008 02:08:50 -0000
***************
*** 625,630 ****
--- 625,661 ----
      </varlistentry>
  
      <varlistentry>
+      <term><function>PQconninfoParse</function><indexterm><primary>PQconninfoParse</></></term>
+      <listitem>
+       <para>
+        Returns parsed connection options from the provided connection string.
+ 
+ <synopsis>
+ PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg);
+ </synopsis>
+ 
+       <para>
+        Returns a connection options array.  This can be used to determine
+        the <function>PQconnectdb</function> options in the provided
+        connection string.  The return value points to an array of
+        <structname>PQconninfoOption</structname> structures, which ends
+        with an entry having a null <structfield>keyword</> pointer.  The
+        null pointer is returned if an error occurs. In this case,
+        <literal>errmsg</literal> contains the error message. Passing
+        <literal>NULL</literal> for <literal>errmsg</literal> tells
+        <function>PQconninfoParse</function> to throw away the error message.
+       </para>
+ 
+       <para>
+        After processing the options array, free it by passing it to
+        <function>PQconninfoFree</function>.  If this is not done, a small amount of memory
+        is leaked for each call to <function>PQconndefaults</function>.
+       </para>
+ 
+    </listitem>
+     </varlistentry>
+ 
+     <varlistentry>
       <term><function>PQfinish</function><indexterm><primary>PQfinish</></></term>
       <listitem>
        <para>
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /opt/src/cvs/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.21
diff -c -r1.21 exports.txt
*** src/interfaces/libpq/exports.txt	19 Sep 2008 20:06:13 -0000	1.21
--- src/interfaces/libpq/exports.txt	21 Sep 2008 23:32:56 -0000
***************
*** 151,153 ****
--- 151,154 ----
  PQresultInstanceData      149
  PQresultSetInstanceData   150
  PQfireResultCreateEvents  151
+ PQconninfoParse           152
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /opt/src/cvs/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.360
diff -c -r1.360 fe-connect.c
*** src/interfaces/libpq/fe-connect.c	17 Sep 2008 04:31:08 -0000	1.360
--- src/interfaces/libpq/fe-connect.c	22 Sep 2008 02:01:33 -0000
***************
*** 3101,3106 ****
--- 3101,3129 ----
  	return 0;
  }
  
+ /*
+  *		PQconninfoParse
+  *
+  * Parse a string like PQconnectdb() would do and return the
+  * working connection options array.
+  *
+  * NOTE: the returned array is dynamically allocated and should
+  * be freed when no longer needed via PQconninfoFree().
+  */
+ PQconninfoOption *
+ PQconninfoParse(const char *conninfo, char **errmsg)
+ {
+ 	PQExpBufferData		errorBuf;
+ 	bool				password_from_string;
+ 	PQconninfoOption   *connOptions;
+ 
+ 	initPQExpBuffer(&errorBuf);
+ 	connOptions = conninfo_parse(conninfo, &errorBuf, &password_from_string);
+ 	if (!connOptions && errmsg)
+ 		*errmsg = errorBuf.data;
+ 	termPQExpBuffer(&errorBuf);
+ 	return connOptions;
+ }
  
  /*
   * Conninfo parser routine
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /opt/src/cvs/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.143
diff -c -r1.143 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h	17 Sep 2008 04:31:08 -0000	1.143
--- src/interfaces/libpq/libpq-fe.h	22 Sep 2008 01:45:24 -0000
***************
*** 243,248 ****
--- 243,251 ----
  /* get info about connection options known to PQconnectdb */
  extern PQconninfoOption *PQconndefaults(void);
  
+ /* parse connection options in same way as PQconnectdb */
+ extern PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg);
+ 
  /* free the data structure returned by PQconndefaults() */
  extern void PQconninfoFree(PQconninfoOption *connOptions);
  
-- 
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