Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 16 Jan 1997 17:59:17 -0500 (EST)
From:      Bill Paul <wpaul@skynet.ctr.columbia.edu>
To:        current@freebsd.org
Subject:   Let's try this again
Message-ID:  <199701162259.RAA06177@skynet.ctr.columbia.edu>

next in thread | raw e-mail | index | archive | help

Okay, my last attempt as this hack didn't seem to do the job I needed
and met with some criticism when I submitted it for review, so here's
another try.

What I wanted to do was provide an interface for learning the credentials
of a peer process on the other end of an AF_UNIX socket. The result was
an addition to getsockopt() which basically imitated the operation of
identd, except in the kernel. Knowing the server-side socket descriptor,
the code would find the client-side socket via the protocol control block
and then scan the open file table looking for the process that owned it.

This approach has problems. First of all, you need to scan the whole
open file table looking for the client-side descriptor. The more files
that are open in the system, the longer this can take.

Second, it's possible (though unlikely) that there may be more than one
process referencing the client-side socket. If this is the case, it's
not clear which set of credentials to return. Also, the only way to detect
this is to scan the entire open file table all the way through (to look
for duplicate references) instead of just stopping at the first match.

What's really needed is a way to do peer authentication on a per-message
basis instead of on a 'per-socket' basis. The only way I can see to do
this effectively is to use sendmsg()/recvmsg() and send ancillary data
along with the request. The ancillary data can be process credentials,
which the server can extract and use to verify the AUTH_UNIX creds supplied
in the RPC. The trick is to have the kernel fill in the credentials rather
than the client; this way the client can't provide false information.

Appended to this message are some patches for uipc_usrreq.c and sys/socket.h
to implement this feature. A new ancillary control message type is provided
called SCM_CREDS along with a credentials structure, stuct cmsgcred. The 
sending process sets up an empty cmsgcred structure and transmits it,
and the kernel fills in the data related to the sending process in
unp_internalize(). This includes the pid, effective uid and groups -- it's 
essentially the same data found in struct ucred with the addition of the
pid. The information is then transfered to the struct cmsgcred of the
receiving process.

The one problem with this hack is that now socket.h depends on sys/param.h
in order for NGROUPS to be #defined. I'm not sure how to resolve this:
I'm pretty sure adding #include <sys/param.h> to socket.h is not the
correct answer. Possibly the cmsgcred struct needs to be somewhere else,
but I'm not sure where. Comments and suggestions welcome.

-Bill

--
=============================================================================
-Bill Paul            (212) 854-6020 | System Manager, Master of Unix-Fu
Work:         wpaul@ctr.columbia.edu | Center for Telecommunications Research
Home:  wpaul@skynet.ctr.columbia.edu | Columbia University, New York City
=============================================================================
 "It is not I who am crazy; it is I who am mad!" - Ren Hoek, "Space Madness"
=============================================================================

*** uipc_usrreq.c.orig	Thu Jan 16 12:06:18 1997
--- uipc_usrreq.c	Thu Jan 16 13:35:16 1997
***************
*** 697,707 ****
  	register struct file **rp;
  	register struct file *fp;
  	register int i, fd;
  	int oldfds;
  
! 	if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET ||
! 	    cm->cmsg_len != control->m_len)
  		return (EINVAL);
  	oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
  	/*
  	 * check that all the FDs passed in refer to legal OPEN files
--- 697,722 ----
  	register struct file **rp;
  	register struct file *fp;
  	register int i, fd;
+ 	register struct cmsgcred *cmcred;
  	int oldfds;
  
! 	if ((cm->cmsg_type != SCM_RIGHTS && cm->cmsg_type != SCM_CREDS) ||
! 	    cm->cmsg_level != SOL_SOCKET || cm->cmsg_len != control->m_len)
  		return (EINVAL);
+ 
+ 	/*
+ 	 * 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_ucred->cr_uid;
+ 		cmcred->cmcred_ngroups = p->p_ucred->cr_ngroups;
+ 		for (i = 0; i < NGROUPS; i++)
+ 			cmcred->cmcred_groups[i] = p->p_ucred->cr_groups[i];
+ 		return(0);
+ 	}
+ 
  	oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
  	/*
  	 * check that all the FDs passed in refer to legal OPEN files


*** socket.h.old	Mon Dec 23 22:27:27 1996
--- socket.h	Thu Jan 16 13:25:39 1997
***************
*** 289,294 ****
--- 290,308 ----
  /* followed by	u_char  cmsg_data[]; */
  };
  
+ /*
+  * Credentials structure, used to verify the identity of a peer
+  * process that has sent us a message. This is allocated by the
+  * peer process but filled in by the kernel. This prevents the
+  * peer from lying about its identity.
+  */
+ struct cmsgcred {
+ 	pid_t	cmcred_pid;		/* PID of sending process */
+ 	uid_t	cmcred_uid;		/* effective UID of sending process */
+ 	short	cmcred_ngroups;		/* number or groups */
+ 	gid_t	cmcred_groups[NGROUPS];	/* groups */
+ };
+ 
  /* given pointer to struct cmsghdr, return pointer to data */
  #define	CMSG_DATA(cmsg)		((u_char *)((cmsg) + 1))
  
***************
*** 304,309 ****
--- 318,324 ----
  /* "Socket"-level control message types: */
  #define	SCM_RIGHTS	0x01		/* access rights (array of int) */
  #define	SCM_TIMESTAMP	0x02		/* timestamp (struct timeval) */
+ #define	SCM_CREDS	0x03		/* process creds (struct cmsgcred) */
  
  /*
   * 4.3 compat sockaddr, move to compat file later



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199701162259.RAA06177>