On tis, 2010-06-22 at 09:37 +0900, KaiGai Kohei wrote:
> As you described at the source code comments as follows,
> it is not portable except for Linux due to the getsockopt() API.
> 
> +               // TODO: currently Linux-only code, needs to be made
> +               // portable; see backend/libpq/auth.c
> 
> I expect it shall be fixed (using the code come from ident_unix()?)
> before committing.

Updated patch attached.

Note that the code that gets the user ID from the other end of a socket
appears to have two different modes of operation.  On some platforms
(Linux, OpenBSD, Solaris), you call a function and get the answer.  On
some other platforms (other BSDs?), you need to send a packet and read
the answer.  I don't have any possibility to test the latter approach,
and it seemed a bit complicated to code "blindly".  So I have omitted
support for that, but if someone else wants to do the porting, that is
of course possible.

> I'd like to point out one other point.
> It uses getpwuid() to translate a user identifier into a user name,
> but it returns a pointer of the static variable within glibc.
> So, it is not thread-safe. I recommend to use getpwnam_r() instead.

Good catch.  pqGetpwuid() was actually the right function to use.
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 8f0a9cf..6a811c5 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -503,6 +503,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>
@@ -6136,6 +6158,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 ed37bbd..74595e0 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -226,6 +226,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,
@@ -592,6 +595,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;
@@ -1673,6 +1678,85 @@ keep_going:						/* We will come back to here until there is
 				char	   *startpacket;
 				int			packetlen;
 
+#ifdef HAVE_UNIX_SOCKETS
+				if (conn->requirepeer)
+				{
+					char		pwdbuf[BUFSIZ];
+					struct passwd pass_buf;
+					struct passwd *pass;
+					uid_t		uid;
+
+#if defined(HAVE_GETPEEREID)
+					gid_t		gid;
+
+					errno = 0;
+					if (getpeereid(sock, &uid, &gid) != 0)
+					{
+						appendPQExpBuffer(&conn->errorMessage,
+										  libpq_gettext("could not get peer credentials: %s\n"),
+										  pqStrerror(errno, sebuf, sizeof(sebuf)));
+						goto error_return;
+					}
+#elif defined(SO_PEERCRED)
+					struct ucred peercred;
+					ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
+
+					errno = 0;
+					if (getsockopt(conn->sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
+						so_len != sizeof(peercred))
+					{
+						appendPQExpBuffer(&conn->errorMessage,
+										  libpq_gettext("could not get peer credentials: %s\n"),
+										  pqStrerror(errno, sebuf, sizeof(sebuf)));
+						goto error_return;
+					}
+					uid = peercred.uid;
+#elif defined(HAVE_GETPEERUCRED)
+					ucred_t    *ucred;
+
+					ucred = NULL;				/* must be initialized to NULL */
+					if (getpeerucred(sock, &ucred) == -1)
+					{
+						appendPQExpBuffer(&conn->errorMessage,
+										  libpq_gettext("could not get peer credentials: %s\n"),
+										  pqStrerror(errno, sebuf, sizeof(sebuf)));
+						goto error_return;
+					}
+
+					if ((uid = ucred_geteuid(ucred)) == -1)
+					{
+						appendPQExpBuffer(&conn->errorMessage,
+										  libpq_gettext("could not get effective UID from peer credentials: %s\n"),
+										  pqStrerror(errno, sebuf, sizeof(sebuf)));
+						goto error_return;
+					}
+					ucred_free(ucred);
+#else
+					appendPQExpBuffer(&conn->errorMessage,
+									  libpq_gettext("requirepeer parameter is not supported on this platform\n"));
+					goto error_return;
+#endif
+
+					pqGetpwuid(uid, &pass_buf, pwdbuf, sizeof(pwdbuf), &pass);
+
+					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;
+					}
+				}
+#endif /* HAVE_UNIX_SOCKETS */
+
 #ifdef USE_SSL
 
 				/*
@@ -2480,6 +2564,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 23d58ff..5fbe24b 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -309,6 +309,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