Patch: use SCM_CREDS authentication over PF_LOCAL sockets

From: wollman(at)LCS(dot)MIT(dot)EDU
To: pgsql-patches(at)postgresql(dot)org
Subject: Patch: use SCM_CREDS authentication over PF_LOCAL sockets
Date: 2001-08-12 19:17:42
Message-ID: 200108121917.f7CJHgN55360@mintaka.lcs.mit.edu
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-patches

The following set of patches (relative to 7.1.2 release) implement
SCM_CREDS authentication for local connections. On systems which
support it, this mechanism should be used instead of `trust' for local
connections.

-GAWollman

------------------------------------------------------------------------
--- src/backend/libpq/auth.c Wed Mar 21 22:59:30 2001
+++ src/backend/libpq/auth.c Sun Aug 12 14:46:35 2001
@@ -26,6 +26,11 @@
#include <ctype.h>

#include <sys/types.h> /* needed by in.h on Ultrix */
+#include <sys/socket.h> /* for SCM_CREDS */
+#ifdef SCM_CREDS
+#include <sys/uio.h> /* for struct iovec */
+#include <errno.h>
+#endif
#include <netinet/in.h>
#include <arpa/inet.h>

@@ -42,6 +47,7 @@
static int handle_done_auth(void *arg, PacketLen len, void *pkt);
static int handle_krb4_auth(void *arg, PacketLen len, void *pkt);
static int handle_krb5_auth(void *arg, PacketLen len, void *pkt);
+static int handle_local_auth(void *arg, PacketLen len, void *pkt);
static int handle_password_auth(void *arg, PacketLen len, void *pkt);
static int readPasswordPacket(void *arg, PacketLen len, void *pkt);
static int pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt);
@@ -322,6 +328,98 @@
#endif /* KRB5 */


+#ifdef SCM_CREDS
+static int
+pg_local_recvauth(Port *port)
+{
+ struct msghdr msg;
+ struct {
+ struct cmsghdr hdr;
+ struct cmsgcred cred;
+ } cmsg;
+ struct iovec iov;
+ char buf;
+ char namebuf[SM_USER + 1];
+ struct passwd *pw;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = (char *)&cmsg;
+ msg.msg_controllen = sizeof cmsg;
+ msg.msg_flags = 0;
+
+ /*
+ * The one character which is received here is not meaningful;
+ * its purposes is only to make sure that recvmsg() blocks
+ * long enough for the other side to send its credentials.
+ */
+ iov.iov_base = &buf;
+ iov.iov_len = 1;
+
+ if (recvmsg(port->sock, &msg, 0) < 0) {
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "pg_local_recvauth: error receiving credentials: %s\n",
+ strerror(errno));
+errout:
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+
+ return STATUS_ERROR;
+ }
+
+ /*
+ * Make sure we got the right kind of message.
+ */
+ if (cmsg.hdr.cmsg_len != sizeof cmsg
+ || cmsg.hdr.cmsg_level != SOL_SOCKET
+ || cmsg.hdr.cmsg_type != SCM_CREDS) {
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "pg_local_recvauth: protocol error receiving credentials\n");
+ goto errout;
+ }
+
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "pg_local_recvauth: pid %lu, uid %lu\n",
+ (unsigned long)cmsg.cred.cmcred_pid,
+ (unsigned long)cmsg.cred.cmcred_uid);
+ pqdebug("%s", PQerrormsg);
+
+ strncpy(namebuf, port->user, SM_USER);
+ namebuf[SM_USER] = '\0';
+
+ pw = getpwnam(namebuf);
+ if (pw == NULL) {
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "pg_local_recvauth: unknown local user %s\n",
+ namebuf);
+ goto errout;
+ }
+
+ if (pw->pw_uid != cmsg.cred.cmcred_uid) {
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "pg_local_recvauth: %s's uid %lu != real uid %lu\n",
+ namebuf, (unsigned long)pw->pw_uid,
+ (unsigned long)cmsg.cred.cmcred_uid);
+ goto errout;
+ }
+ return STATUS_OK;
+}
+
+#else
+static int
+pg_local_recvauth(Port *port)
+{
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "pg_local_recvauth: credential passing not implemented on this server.\n");
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+
+ return STATUS_ERROR;
+}
+#endif /* SCM_CREDS */
+
/*
* Handle a v0 password packet.
*/
@@ -439,6 +537,9 @@
case uaCrypt:
authmethod = "Password";
break;
+ case uaLocalCred:
+ authmethod = "Local credential";
+ break;
}

sprintf(buffer, "%s authentication failed for user '%s'",
@@ -545,6 +646,10 @@
areq = AUTH_REQ_CRYPT;
auth_handler = handle_password_auth;
break;
+ case uaLocalCred:
+ areq = AUTH_REQ_LOCALCRED;
+ auth_handler = handle_local_auth;
+ break;
}

/* Tell the frontend what we want next. */
@@ -668,6 +773,24 @@


/*
+ * Called when we have told the front end that it should use local
+ * credential authentication.
+ */
+
+static int
+handle_local_auth(void *arg, PacketLen len, void *pkt)
+{
+ Port *port = (Port *) arg;
+
+ if (pg_local_recvauth(port) != STATUS_OK)
+ auth_failed(port);
+ else
+ sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
+
+ return STATUS_OK;
+}
+
+/*
* Called when we have received the password packet.
*/

@@ -777,6 +900,10 @@

case uaTrust:
status = STATUS_OK;
+ break;
+
+ case uaLocalCred: /* can't happen */
+ status = STATUS_ERROR;
break;

case uaIdent:
--- src/backend/libpq/hba.c Fri Feb 9 21:31:26 2001
+++ src/backend/libpq/hba.c Sun Aug 12 14:10:28 2001
@@ -125,6 +125,8 @@
*userauth_p = uaReject;
else if (strcmp(buf, "crypt") == 0)
*userauth_p = uaCrypt;
+ else if (strcmp(buf, "local") == 0)
+ *userauth_p = uaLocalCred;
else
{
*error_p = true;
@@ -279,6 +281,14 @@
*/

read_hba_entry2(file, &port->auth_method, port->auth_arg, error_p);
+
+ /*
+ * Disallow authentication methods which require a PF_LOCAL
+ * socket.
+ */
+ if (!*error_p &&
+ (port->auth_method == uaLocalCred))
+ *error_p = true;

if (*error_p)
goto syntax;
--- src/backend/libpq/pg_hba.conf.sample Tue Nov 21 15:44:32 2000
+++ src/backend/libpq/pg_hba.conf.sample Sun Aug 12 14:17:41 2001
@@ -96,6 +96,12 @@
# trust: No authentication is done. Trust that the user has the
# authority to use whatever username he specifies.
#
+# local: Use the credential-passing feature of local-domain sockets
+# (available only on certain operating systems) to
+# authenticate the user. Allow the user access if his
+# real UID matches the UID in the system password file
+# for the requested username.
+#
# password: Authentication is done by matching a password supplied
# in clear by the host. If AUTH_ARGUMENT is specified then
# the password is compared with the user's entry in that
@@ -123,7 +129,7 @@
#
# reject: Reject the connection.
#
-# Local (UNIX socket) connections support only AUTHTYPEs "trust",
+# Local (UNIX socket) connections support only AUTHTYPEs "trust", "local",
# "password", "crypt", and "reject".


@@ -137,9 +143,18 @@
#
# host all 127.0.0.1 255.255.255.255 trust
#
-# The same, over Unix-socket connections:
+# Allow any user to connect to any database over a local socket,
+# provided that the user's real UID is the same as the requested
+# database username's UID:
+#
+# local all local
+#
+# On operating systems where credential passing over local sockets
+# is not available, allow any user to connect to any database over
+# a local socket under any username:
#
# local all trust
+#
#
# Allow any user from any host with IP address 192.168.93.x to
# connect to database "template1" as the same username that ident on that
--- src/include/libpq/hba.h Wed Mar 21 23:00:47 2001
+++ src/include/libpq/hba.h Sun Aug 12 13:33:30 2001
@@ -35,7 +35,8 @@
uaTrust,
uaIdent,
uaPassword,
- uaCrypt
+ uaCrypt,
+ uaLocalCred
} UserAuth;

typedef struct Port hbaPort;
--- src/include/libpq/pqcomm.h Wed Mar 21 23:00:48 2001
+++ src/include/libpq/pqcomm.h Sun Aug 12 13:35:16 2001
@@ -90,7 +90,7 @@
/* The earliest and latest frontend/backend protocol version supported. */

#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(0,0)
-#define PG_PROTOCOL_LATEST PG_PROTOCOL(2,0)
+#define PG_PROTOCOL_LATEST PG_PROTOCOL(2,1)

/*
* All packets sent to the postmaster start with the length. This is omitted
@@ -132,6 +132,7 @@
#define AUTH_REQ_KRB5 2 /* Kerberos V5 */
#define AUTH_REQ_PASSWORD 3 /* Password */
#define AUTH_REQ_CRYPT 4 /* Encrypted password */
+#define AUTH_REQ_LOCALCRED 5 /* PF_LOCAL credentials */

typedef uint32 AuthRequest;

--- src/interfaces/libpq/fe-auth.c Wed Mar 21 23:01:25 2001
+++ src/interfaces/libpq/fe-auth.c Sun Aug 12 14:50:44 2001
@@ -43,6 +43,11 @@
#ifndef MAXHOSTNAMELEN
#include <netdb.h> /* for MAXHOSTNAMELEN on some */
#endif
+#include <sys/socket.h> /* for SCM_CREDS */
+#ifdef SCM_CREDS
+#include <sys/uio.h>
+#include <sys/errno.h>
+#endif
#include <pwd.h>
#endif

@@ -430,6 +435,52 @@

#endif /* KRB5 */

+#ifdef SCM_CREDS
+static int
+pg_local_sendauth(char *PQerrormsg, PGconn *conn)
+{
+ char buf;
+ struct iovec iov;
+ struct {
+ struct cmsghdr hdr;
+ struct cmsgcred cred;
+ } cmsg;
+ struct msghdr msg;
+
+ /*
+ * The backend doesn't care what we send here, but it wants
+ * exactly one character to force recvmsg() to block and wait
+ * for us.
+ */
+ buf = '\0';
+ iov.iov_base = &buf;
+ iov.iov_len = 1;
+
+ cmsg.hdr.cmsg_len = sizeof cmsg;
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_CREDS;
+ /*
+ * cmsg.cred will get filled in with the correct information
+ * by the kernel when this message is sent.
+ */
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsg;
+ msg.msg_controllen = sizeof cmsg;
+ msg.msg_flags = 0;
+
+ if (sendmsg(conn->sock, &msg, 0) == -1) {
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "pg_local_sendauth: sendmsg: %s", strerror(errno));
+ return STATUS_ERROR;
+ }
+ return STATUS_OK;
+}
+#endif
+
static int
pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
{
@@ -507,6 +558,17 @@
}

break;
+
+ case AUTH_REQ_LOCALCRED:
+#ifdef SCM_CREDS
+ if (pg_local_sendauth(PQerrormsg, conn) != STATUS_OK)
+ return STATUS_ERROR;
+ break;
+#else
+ (void) sprintf(PQerrormsg,
+ "fe_sendauth: local authentication not supported\n");
+ return STATUS_ERROR;
+#endif

default:
(void) sprintf(PQerrormsg,

Responses

Browse pgsql-patches by date

  From Date Subject
Next Message Ian Lance Taylor 2001-08-12 19:19:32 Re: Re: [PATCHES] Select parser at runtime
Previous Message Tom Lane 2001-08-12 17:31:23 Re: bytea_ops