It has been discussed several times in the past that there is no way for
a client to authenticate a server over Unix-domain sockets.  So
depending on circumstances, a local user could easily insert his own
server and collect passwords and data.  Suggestions for possible
remedies included:

You can put the socket file in a sufficiently write-protected directory.
But that would strongly deviate from the default setup, and anyway the
client still cannot readily verify that the server is the right one.

You can also run SSL over Unix-domain sockets.  This is currently
disabled in the code, but it would work just fine.  But it's obviously
kind of awkward, and the connection overhead was noticeable in tests.

Then it was suggested to use the local "ident" mechanism in reverse, so
the client could verify what user the server runs under.  I have
implemented a prototype of this.  You can put, e.g.,

requirepeer=postgres

into the connection parameters, and the connection will be rejected
unless the process at the other end of the socket is running as
postgres.

The patch needs some portability work and possible refactoring because
of that, but before I embark on that, comments on the concept?

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index be788e9..400a6ae 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -449,6 +449,28 @@
          </listitem>
         </varlistentry>
 
+        <varlistentry id="libpq-connect-requirepeer" xreflabel="requirepeer">
+         <term><literal>requirepeer</literal></term>
+         <listitem>
+          <para>
+           For Unix-domain socket connections, if this parameter is
+           set, the client checks at the beginning of the connection
+           that the server process runs under the specified user name,
+           otherwise the connection is aborted with an error.  This
+           parameter can be used to achieve the kind of server
+           authentication that SSL certificates achieve on TCP/IP
+           connections.  (Note that if the Unix-domain socket is
+           in <filename>/tmp</filename> or another publically writable
+           location, any user could start a server there.  Use this
+           parameter to ensure that you are connected to a server run
+           by a trusted user,
+           e.g., <literal>requirepeer=postgres</literal>.)  This
+           option is only supported on some platforms, currently
+           Linux, FreeBSD, NetBSD, OpenBSD, BSD/OS, and Solaris.
+          </para>
+         </listitem>
+        </varlistentry>
+
         <varlistentry id="libpq-connect-krbsrvname" xreflabel="krbsrvname">
          <term><literal>krbsrvname</literal></term>
          <listitem>
@@ -6082,6 +6104,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
     <listitem>
      <para>
       <indexterm>
+       <primary><envar>PGREQUIREPEER</envar></primary>
+      </indexterm>
+      <envar>PGREQUIREPEER</envar> behaves the same as the <xref
+      linkend="libpq-connect-requirepeer"> connection parameter.
+     </para>
+    </listitem>
+
+    <listitem>
+     <para>
+      <indexterm>
        <primary><envar>PGKRBSRVNAME</envar></primary>
       </indexterm>
       <envar>PGKRBSRVNAME</envar>  behaves the same as the <xref
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index ab6cc08..9b8de26 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -214,6 +214,9 @@ static const PQconninfoOption PQconninfoOptions[] = {
 	{"sslcrl", "PGSSLCRL", NULL, NULL,
 	"SSL-Revocation-List", "", 64},
 
+	{"requirepeer", "PGREQUIREPEER", NULL, NULL,
+	"Require-Peer", "", 10},
+
 #if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 	/* Kerberos and GSSAPI authentication support specifying the service name */
 	{"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
@@ -572,6 +575,8 @@ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
 		conn->sslmode = strdup("require");
 	}
 #endif
+	tmp = conninfo_getval(connOptions, "requirepeer");
+	conn->requirepeer = tmp ? strdup(tmp) : NULL;
 #if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 	tmp = conninfo_getval(connOptions, "krbsrvname");
 	conn->krbsrvname = tmp ? strdup(tmp) : NULL;
@@ -1501,6 +1506,44 @@ keep_going:						/* We will come back to here until there is
 				char	   *startpacket;
 				int			packetlen;
 
+				// TODO: currently Linux-only code, needs to be made
+				// portable; see backend/libpq/auth.c
+				if (conn->requirepeer)
+				{
+					struct ucred peercred;
+					ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
+					struct passwd *pass;
+
+					errno = 0;
+					if (getsockopt(conn->sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
+						so_len != sizeof(peercred))
+					{
+						/* We didn't get a valid credentials struct. */
+						appendPQExpBuffer(&conn->errorMessage,
+										  libpq_gettext("could not get peer credentials: %s\n"),
+														SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+						goto error_return;
+					}
+
+					pass = getpwuid(peercred.uid);
+
+					if (pass == NULL)
+					{
+						appendPQExpBuffer(&conn->errorMessage,
+										  libpq_gettext("local user with ID %d does not exist\n"),
+														(int) peercred.uid);
+						goto error_return;
+					}
+
+					if (strcmp(pass->pw_name, conn->requirepeer) != 0)
+					{
+						appendPQExpBuffer(&conn->errorMessage,
+										  libpq_gettext("requirepeer failed (actual: %s != required: %s)\n"),
+														pass->pw_name, conn->requirepeer);
+						goto error_return;
+					}
+				}
+
 #ifdef USE_SSL
 
 				/*
@@ -2300,6 +2343,8 @@ freePGconn(PGconn *conn)
 		free(conn->sslrootcert);
 	if (conn->sslcrl)
 		free(conn->sslcrl);
+	if (conn->requirepeer)
+		free(conn->requirepeer);
 #if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 	if (conn->krbsrvname)
 		free(conn->krbsrvname);
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index e88b3ec..afb15f0 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -305,6 +305,7 @@ struct pg_conn
 	char	   *sslcert;		/* client certificate filename */
 	char	   *sslrootcert;	/* root certificate filename */
 	char	   *sslcrl;			/* certificate revocation list filename */
+	char	   *requirepeer;	/* required peer credentials for local sockets */
 
 #if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 	char	   *krbsrvname;		/* Kerberos service name */
-- 
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