Re: [patch] fix dblink security hole - Mailing list pgsql-hackers

From Joe Conway
Subject Re: [patch] fix dblink security hole
Date
Msg-id 48D70252.4050108@joeconway.com
Whole thread Raw
In response to Re: [patch] fix dblink security hole  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: [patch] fix dblink security hole  (Tom Lane <tgl@sss.pgh.pa.us>)
Re: [patch] fix dblink security hole  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-hackers
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);


pgsql-hackers by date:

Previous
From: Tom Lane
Date:
Subject: Re: Proposal: move column defaults into pg_attribute along with attacl
Next
From: Stephen Frost
Date:
Subject: Re: Proposal: move column defaults into pg_attribute along with attacl