From vova@express.ru  Tue Aug  7 08:29:16 2001
Return-Path: <vova@express.ru>
Received: from vbook.express.ru (vbook.express.ru [212.24.37.106])
	by hub.freebsd.org (Postfix) with ESMTP id F159A37B405
	for <FreeBSD-gnats-submit@freebsd.org>; Tue,  7 Aug 2001 08:29:15 -0700 (PDT)
	(envelope-from vova@express.ru)
Received: from vova by vbook.express.ru with local (Exim 3.31 #2)
	id 15U8nj-0001a9-00
	for FreeBSD-gnats-submit@freebsd.org; Tue, 07 Aug 2001 19:29:47 +0400
Message-Id: <E15U8nj-0001a9-00@vbook.express.ru>
Date: Tue, 07 Aug 2001 19:29:47 +0400
From: vova@express.ru
Sender: "Vladimir B. Grebenschikov" <vova@express.ru>
Reply-To: vova@express.ru
To: FreeBSD-gnats-submit@freebsd.org
Subject: problem with sending creditionals and descriptors in one message through AF_UNIX socket
X-Send-Pr-Version: 3.2

>Number:         29499
>Category:       kern
>Synopsis:       it is not possible to send  creditionals and descriptors in one message through AF_UNIX socket
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    dwmalone
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Aug 07 08:30:05 PDT 2001
>Closed-Date:    Sun Dec 01 13:43:13 PST 2002
>Last-Modified:  Sun Dec 01 13:43:13 PST 2002
>Originator:     Vladimir B. Grebenschikov
>Release:        FreeBSD 4.3-RELEASE i386
>Organization:
TSB "Russian Express"
>Environment:

FreeBSD idx0.express.ru 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Wed Jul 11 14:23:23 MSD 2001     root@idx0.express.ru:/usr/src/sys/compile/IDX0  i386

>Description:

When special message is send with regular data through unix domain socket it
is possible to transfer onle one special message a time (creditionals or file
descriptors), but not possible to send both,
there is CMSG_NXTHDR macro to get next special message.

>How-To-Repeat:

Simple code that allows don't use setuids at all located here
http://pm.kmost.express.ru/~pm/nosuid.tgz
without patch it will not work under freebsd, but will work under linux if
use macro CMSG_NXTHDR from freebsd.

>Fix:

There is very ugly patch (it can lead to fd-leak)

patch assumes sizeof(int) == sizeof(struct file *), that in general not
right.


--- /usr/src/sys/kern/uipc_usrreq.c.orig	Thu May 10 11:56:25 2001
+++ /usr/src/sys/kern/uipc_usrreq.c	Sun Aug  5 17:37:32 2001
@@ -897,6 +901,8 @@
 		}
 		return (EMSGSIZE);
 	}
+
+#if 0
 	/*
 	 * now change each pointer to an fd in the global table to 
 	 * an integer that is the index to the local fd table entry
@@ -909,6 +915,7 @@
 	 * do it in reverse order.
 	 */
 	if (sizeof (struct file *) >= sizeof (int)) {
+#endif
 		fdp = (int *)(cm + 1);
 		rp = (struct file **)CMSG_DATA(cm);
 		for (i = 0; i < newfds; i++) {
@@ -920,6 +927,7 @@
 			unp_rights--;
 			*fdp++ = f;
 		}
+#if 0
 	} else {
 		fdp = (int *)(cm + 1) + newfds - 1;
 		rp = (struct file **)CMSG_DATA(cm) + newfds - 1;
@@ -940,6 +948,7 @@
 	 */
 	cm->cmsg_len = CMSG_LEN(newfds * sizeof(int));
 	rights->m_len = cm->cmsg_len;
+#endif
 	return (0);
 }
 
@@ -969,40 +978,56 @@
 	register int i, fd, *fdp;
 	register struct cmsgcred *cmcred;
 	int oldfds;
-	u_int newlen;
+/*	u_int newlen; */
+	struct cmsghdr *cmend;
 
+#if 0
 	if ((cm->cmsg_type != SCM_RIGHTS && cm->cmsg_type != SCM_CREDS) ||
-	    cm->cmsg_level != SOL_SOCKET || cm->cmsg_len != control->m_len)
+	    cm->cmsg_level != SOL_SOCKET || cm->cmsg_len != control->m_len) {
+uprintf("cmsg_type %d, cmsg_level %d, cmsg_len %d, m_len %d\n", cm->cmsg_type, cm->cmsg_level, cm->cmsg_len, control->m_len);
+		return (EINVAL);
+	}
+#else
+	if (cm->cmsg_level != SOL_SOCKET) {
 		return (EINVAL);
+	}
+#endif
 
+	cmend = (struct cmsghdr *)(((char*)cm) + control->m_len);
+	for(; cm < cmend;
+                     cm = (struct cmsghdr *)(((char*)cm)+cm->cmsg_len)) {
 	/*
 	 * Fill in credential information.
 	 */
-	if (cm->cmsg_type == SCM_CREDS) {
-		cmcred = (struct cmsgcred *)(cm + 1);
-		cmcred->cmcred_pid = p->p_pid;
-		cmcred->cmcred_uid = p->p_cred->p_ruid;
-		cmcred->cmcred_gid = p->p_cred->p_rgid;
-		cmcred->cmcred_euid = p->p_ucred->cr_uid;
-		cmcred->cmcred_ngroups = MIN(p->p_ucred->cr_ngroups,
+/*uprintf("cm = %p, cmsg_type = %d\n", cm, cm->cmsg_type);*/
+		switch(cm->cmsg_type) {
+			case SCM_CREDS:
+			    cmcred = (struct cmsgcred *)(cm + 1);
+			    cmcred->cmcred_pid = p->p_pid;
+			    cmcred->cmcred_uid = p->p_cred->p_ruid;
+			    cmcred->cmcred_gid = p->p_cred->p_rgid;
+			    cmcred->cmcred_euid = p->p_ucred->cr_uid;
+			    cmcred->cmcred_ngroups = MIN(p->p_ucred->cr_ngroups,
 							CMGROUP_MAX);
-		for (i = 0; i < cmcred->cmcred_ngroups; i++)
-			cmcred->cmcred_groups[i] = p->p_ucred->cr_groups[i];
-		return(0);
-	}
+			    for (i = 0; i < cmcred->cmcred_ngroups; i++)
+				    cmcred->cmcred_groups[i] = p->p_ucred->cr_groups[i];
+			    break;
+			case SCM_RIGHTS:
 
-	oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
-	/*
-	 * check that all the FDs passed in refer to legal OPEN files
-	 * If not, reject the entire operation.
-	 */
-	fdp = (int *)(cm + 1);
-	for (i = 0; i < oldfds; i++) {
-		fd = *fdp++;
-		if ((unsigned)fd >= fdescp->fd_nfiles ||
-		    fdescp->fd_ofiles[fd] == NULL)
-			return (EBADF);
-	}
+			oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
+			/*
+	 		* check that all the FDs passed in refer to legal OPEN files
+	 		* If not, reject the entire operation.
+	 		*/
+			fdp = (int *)(cm + 1);
+			for (i = 0; i < oldfds; i++) {
+				fd = *fdp++;
+				if ((unsigned)fd >= fdescp->fd_nfiles ||
+		    		fdescp->fd_ofiles[fd] == NULL)
+					return (EBADF);
+			}
+
+#if 0
 	/*
 	 * Now replace the integer FDs with pointers to
 	 * the associated global file table entry..
@@ -1010,6 +1035,7 @@
 	 * enough, return E2BIG.
 	 */
 	newlen = CMSG_LEN(oldfds * sizeof(struct file *));
+/*uprintf("newlen %d, m_len %d\n", newlen, control->m_len);*/
 	if (newlen > MCLBYTES)
 		return (E2BIG);
 	if (newlen - control->m_len > M_TRAILINGSPACE(control)) {
@@ -1029,34 +1055,42 @@
 	 * differs.
 	 */
 	control->m_len = cm->cmsg_len = newlen;
+#endif
 
-	/*
-	 * Transform the file descriptors into struct file pointers.
-	 * If sizeof (struct file *) is bigger than or equal to sizeof int,
-	 * then do it in reverse order so that the int won't get until
-	 * we're done.
-	 * If sizeof (struct file *) is smaller than sizeof int, then
-	 * do it in forward order.
-	 */
-	if (sizeof (struct file *) >= sizeof (int)) {
-		fdp = (int *)(cm + 1) + oldfds - 1;
-		rp = (struct file **)CMSG_DATA(cm) + oldfds - 1;
-		for (i = 0; i < oldfds; i++) {
-			fp = fdescp->fd_ofiles[*fdp--];
-			*rp-- = fp;
-			fp->f_count++;
-			fp->f_msgcount++;
-			unp_rights++;
-		}
-	} else {
-		fdp = (int *)(cm + 1);
-		rp = (struct file **)CMSG_DATA(cm);
-		for (i = 0; i < oldfds; i++) {
-			fp = fdescp->fd_ofiles[*fdp++];
-			*rp++ = fp;
-			fp->f_count++;
-			fp->f_msgcount++;
-			unp_rights++;
+#if 0
+			/*
+	 		* Transform the file descriptors into struct file pointers.
+	 		* If sizeof (struct file *) is bigger than or equal to sizeof int,
+	 		* then do it in reverse order so that the int won't get until
+	 		* we're done.
+	 		* If sizeof (struct file *) is smaller than sizeof int, then
+	 		* do it in forward order.
+	 		*/
+			if (sizeof (struct file *) >= sizeof (int)) {
+#endif
+				fdp = (int *)(cm + 1) + oldfds - 1;
+				rp = (struct file **)CMSG_DATA(cm) + oldfds - 1;
+				for (i = 0; i < oldfds; i++) {
+					fp = fdescp->fd_ofiles[*fdp--];
+					*rp-- = fp;
+					fp->f_count++;
+					fp->f_msgcount++;
+					unp_rights++;
+				}
+#if 0
+			} else {
+				fdp = (int *)(cm + 1);
+				rp = (struct file **)CMSG_DATA(cm);
+				for (i = 0; i < oldfds; i++) {
+					fp = fdescp->fd_ofiles[*fdp++];
+					*rp++ = fp;
+					fp->f_count++;
+					fp->f_msgcount++;
+					unp_rights++;
+				}
+			}
+#endif
+			break;
 		}
 	}
 	return (0);
>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->dwmalone 
Responsible-Changed-By: dwmalone 
Responsible-Changed-When: Tue Aug 7 08:59:45 PDT 2001 
Responsible-Changed-Why:  
I have a large patch set in this area. Maybe you could take a look at 
it for me and see what you think. It also aims to clean up several 
other problems with control messages and unix domain sockets. 

You can find the patch and a short message explaining it at: 

fetch http://www.maths.tcd.ie/~dwmalone/unix-domain-patch 

Any feedback is welcome. 


http://www.FreeBSD.org/cgi/query-pr.cgi?pr=29499 
State-Changed-From-To: open->closed 
State-Changed-By: dwmalone 
State-Changed-When: Sun Dec 1 13:42:40 PST 2002 
State-Changed-Why:  
This has been fixed in -current and is unlikely to be fixed in -stable 
this late in its lifetime. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=29499 
>Unformatted:
