Skip to content

Commit 1d6dd87

Browse files
committed
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.
1 parent f064a4f commit 1d6dd87

File tree

2 files changed

+46
-34
lines changed

2 files changed

+46
-34
lines changed

src/backend/libpq/hba.c

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,7 +1453,7 @@ static bool
14531453
ident_unix(int sock, char *ident_user)
14541454
{
14551455
#if defined(HAVE_GETPEEREID)
1456-
/* OpenBSD style: */
1456+
/* OpenBSD (also Mac OS X) style: use getpeereid() */
14571457
uid_t uid;
14581458
gid_t gid;
14591459
struct passwd *pass;
@@ -1512,9 +1512,7 @@ ident_unix(int sock, char *ident_user)
15121512

15131513
return true;
15141514
#elif defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
1515-
struct msghdr msg;
1516-
1517-
/* Credentials structure */
1515+
/* Assorted BSDen: use a credentials control message */
15181516
#if defined(HAVE_STRUCT_CMSGCRED)
15191517
typedef struct cmsgcred Cred;
15201518

@@ -1528,43 +1526,55 @@ ident_unix(int sock, char *ident_user)
15281526

15291527
#define cruid sc_uid
15301528
#endif
1531-
Cred *cred;
1532-
1533-
/* Compute size without padding */
1534-
char cmsgmem[ALIGN(sizeof(struct cmsghdr)) + ALIGN(sizeof(Cred))]; /* for NetBSD */
1535-
1536-
/* Point to start of first structure */
1537-
struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
15381529

1530+
struct msghdr msg;
1531+
struct cmsghdr *cmsg;
1532+
union
1533+
{
1534+
struct cmsghdr hdr;
1535+
unsigned char buf[CMSG_SPACE(sizeof(Cred))];
1536+
} cmsgbuf;
15391537
struct iovec iov;
15401538
char buf;
1539+
Cred *cred;
15411540
struct passwd *pw;
15421541

1543-
memset(&msg, 0, sizeof(msg));
1544-
msg.msg_iov = &iov;
1545-
msg.msg_iovlen = 1;
1546-
msg.msg_control = (char *) cmsg;
1547-
msg.msg_controllen = sizeof(cmsgmem);
1548-
memset(cmsg, 0, sizeof(cmsgmem));
1549-
15501542
/*
1551-
* The one character which is received here is not meaningful; its
1552-
* purposes is only to make sure that recvmsg() blocks long enough for the
1553-
* other side to send its credentials.
1543+
* The one character that is received here is not meaningful; its purpose
1544+
* is only to make sure that recvmsg() blocks long enough for the other
1545+
* side to send its credentials.
15541546
*/
15551547
iov.iov_base = &buf;
15561548
iov.iov_len = 1;
15571549

1558-
if (recvmsg(sock, &msg, 0) < 0 ||
1559-
cmsg->cmsg_len < sizeof(cmsgmem) ||
1560-
cmsg->cmsg_type != SCM_CREDS)
1550+
memset(&msg, 0, sizeof(msg));
1551+
msg.msg_iov = &iov;
1552+
msg.msg_iovlen = 1;
1553+
msg.msg_control = &cmsgbuf.buf;
1554+
msg.msg_controllen = sizeof(cmsgbuf.buf);
1555+
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
1556+
1557+
if (recvmsg(sock, &msg, 0) < 0)
15611558
{
15621559
ereport(LOG,
15631560
(errcode_for_socket_access(),
15641561
errmsg("could not get peer credentials: %m")));
15651562
return false;
15661563
}
15671564

1565+
cmsg = CMSG_FIRSTHDR(&msg);
1566+
if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC) ||
1567+
cmsg == NULL ||
1568+
cmsg->cmsg_len < CMSG_LEN(sizeof(Cred)) ||
1569+
cmsg->cmsg_level != SOL_SOCKET ||
1570+
cmsg->cmsg_type != SCM_CREDS)
1571+
{
1572+
ereport(LOG,
1573+
(errcode(ERRCODE_PROTOCOL_VIOLATION),
1574+
errmsg("could not get peer credentials: incorrect control message")));
1575+
return false;
1576+
}
1577+
15681578
cred = (Cred *) CMSG_DATA(cmsg);
15691579

15701580
pw = getpwuid(cred->cruid);

src/interfaces/libpq/fe-auth.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -709,11 +709,12 @@ pg_local_sendauth(PGconn *conn)
709709
struct msghdr msg;
710710

711711
#ifdef HAVE_STRUCT_CMSGCRED
712-
/* Prevent padding */
713-
char cmsgmem[sizeof(struct cmsghdr) + sizeof(struct cmsgcred)];
714-
715-
/* Point to start of first structure */
716-
struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
712+
struct cmsghdr *cmsg;
713+
union
714+
{
715+
struct cmsghdr hdr;
716+
unsigned char buf[CMSG_SPACE(sizeof(struct cmsgcred))];
717+
} cmsgbuf;
717718
#endif
718719

719720
/*
@@ -729,11 +730,12 @@ pg_local_sendauth(PGconn *conn)
729730
msg.msg_iovlen = 1;
730731

731732
#ifdef HAVE_STRUCT_CMSGCRED
732-
/* Create control header, FreeBSD */
733-
msg.msg_control = cmsg;
734-
msg.msg_controllen = sizeof(cmsgmem);
735-
memset(cmsg, 0, sizeof(cmsgmem));
736-
cmsg->cmsg_len = sizeof(cmsgmem);
733+
/* FreeBSD needs us to set up a message that will be filled in by kernel */
734+
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
735+
msg.msg_control = &cmsgbuf.buf;
736+
msg.msg_controllen = sizeof(cmsgbuf.buf);
737+
cmsg = CMSG_FIRSTHDR(&msg);
738+
cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
737739
cmsg->cmsg_level = SOL_SOCKET;
738740
cmsg->cmsg_type = SCM_CREDS;
739741
#endif

0 commit comments

Comments
 (0)