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>