aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-05-30 19:16:28 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2011-05-30 19:16:28 -0400
commitfccef77183aff3c96b92c533a8858122cc46cb3c (patch)
treee15f4d6e69a5f1f4b65d008231f6015ad7be40ef /src
parent6d24189b417dfcedc5815a9f1533555b2dfe891c (diff)
downloadpostgresql-fccef77183aff3c96b92c533a8858122cc46cb3c.tar.gz
postgresql-fccef77183aff3c96b92c533a8858122cc46cb3c.zip
Fix portability bugs in use of credentials control messages for peer auth.
Even though our existing code for handling credentials control messages has been basically unchanged since 2001, it was fundamentally wrong: it did not ensure proper alignment of the supplied buffer, and it was calculating buffer sizes and message sizes incorrectly. This led to failures on platforms where alignment padding is relevant, for instance FreeBSD on 64-bit platforms, as seen in a recent Debian bug report passed on by Martin Pitt (http://bugs.debian.org//cgi-bin/bugreport.cgi?bug=612888). Rewrite to do the message-whacking using the macros specified in RFC 2292, following a suggestion from Theo de Raadt in that thread. Tested by me on Debian/kFreeBSD-amd64; since OpenBSD and NetBSD document the identical CMSG API, it should work there too. Back-patch to all supported branches.
Diffstat (limited to 'src')
-rw-r--r--src/backend/libpq/hba.c58
-rw-r--r--src/interfaces/libpq/fe-auth.c22
2 files changed, 46 insertions, 34 deletions
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 7f1cdd84c34..4d210a7fa58 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -1449,7 +1449,7 @@ static bool
ident_unix(int sock, char *ident_user)
{
#if defined(HAVE_GETPEEREID)
- /* OpenBSD style: */
+ /* OpenBSD (also Mac OS X) style: use getpeereid() */
uid_t uid;
gid_t gid;
struct passwd *pass;
@@ -1508,9 +1508,7 @@ ident_unix(int sock, char *ident_user)
return true;
#elif defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
- struct msghdr msg;
-
-/* Credentials structure */
+ /* Assorted BSDen: use a credentials control message */
#if defined(HAVE_STRUCT_CMSGCRED)
typedef struct cmsgcred Cred;
@@ -1524,36 +1522,35 @@ ident_unix(int sock, char *ident_user)
#define cruid sc_uid
#endif
- Cred *cred;
-
- /* Compute size without padding */
- char cmsgmem[ALIGN(sizeof(struct cmsghdr)) + ALIGN(sizeof(Cred))]; /* for NetBSD */
-
- /* Point to start of first structure */
- struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ union
+ {
+ struct cmsghdr hdr;
+ unsigned char buf[CMSG_SPACE(sizeof(Cred))];
+ } cmsgbuf;
struct iovec iov;
char buf;
+ Cred *cred;
struct passwd *pw;
- memset(&msg, 0, sizeof(msg));
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = (char *) cmsg;
- msg.msg_controllen = sizeof(cmsgmem);
- memset(cmsg, 0, sizeof(cmsgmem));
-
/*
- * 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.
+ * The one character that is received here is not meaningful; its purpose
+ * 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(sock, &msg, 0) < 0 ||
- cmsg->cmsg_len < sizeof(cmsgmem) ||
- cmsg->cmsg_type != SCM_CREDS)
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+ memset(&cmsgbuf, 0, sizeof(cmsgbuf));
+
+ if (recvmsg(sock, &msg, 0) < 0)
{
ereport(LOG,
(errcode_for_socket_access(),
@@ -1561,6 +1558,19 @@ ident_unix(int sock, char *ident_user)
return false;
}
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC) ||
+ cmsg == NULL ||
+ cmsg->cmsg_len < CMSG_LEN(sizeof(Cred)) ||
+ cmsg->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_CREDS)
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("could not get peer credentials: incorrect control message")));
+ return false;
+ }
+
cred = (Cred *) CMSG_DATA(cmsg);
pw = getpwuid(cred->cruid);
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index de2b6f72a74..7cfb6c8f9a5 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -343,11 +343,12 @@ pg_local_sendauth(char *PQerrormsg, PGconn *conn)
struct msghdr msg;
#ifdef HAVE_STRUCT_CMSGCRED
- /* Prevent padding */
- char cmsgmem[sizeof(struct cmsghdr) + sizeof(struct cmsgcred)];
-
- /* Point to start of first structure */
- struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
+ struct cmsghdr *cmsg;
+ union
+ {
+ struct cmsghdr hdr;
+ unsigned char buf[CMSG_SPACE(sizeof(struct cmsgcred))];
+ } cmsgbuf;
#endif
/*
@@ -363,11 +364,12 @@ pg_local_sendauth(char *PQerrormsg, PGconn *conn)
msg.msg_iovlen = 1;
#ifdef HAVE_STRUCT_CMSGCRED
- /* Create control header, FreeBSD */
- msg.msg_control = cmsg;
- msg.msg_controllen = sizeof(cmsgmem);
- memset(cmsg, 0, sizeof(cmsgmem));
- cmsg->cmsg_len = sizeof(cmsgmem);
+ /* FreeBSD needs us to set up a message that will be filled in by kernel */
+ memset(&cmsgbuf, 0, sizeof(cmsgbuf));
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_CREDS;
#endif