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