Receiving Sender Credentials
In Figure 14.13(See 9.3.6), we showed another type of data that can be passed along a Unix domain socket as ancillary data: user credentials. Exactly how credentials are packaged up and sent as ancillary data tends to be OS-specific. We describe FreeBSD here, and other Unix variants are similar (usually the challenge is determining which structure to use for the credentials). We describe this feature, even though it is not uniform across systems, because it is an important, yet simple, addition to the Unix domain protocols. When a client and server communicate using these protocols, the server often needs a way to know exactly who the client is, to validate that the client has permission to ask for the service being requested.
FreeBSD passes credentials in a cmsgcred structure, which is defined by including the <sys/socket.h> header.
struct cmsgcred {
pid_t cmcred_pid; /* PID of sending process */
uid_t cmcred_uid; /* real UID of sending process */
uid_t cmcred_euid; /* effective UID of sending process */
gid_t cmcred_gid; /* real GID of sending process */
short cmcred_ngroups; /* number of groups */
gid_t cmcred_groups[CMGROUP_MAX]; /* groups */
};
Normally, CMGROUP_MAX is 16. cmcred_ngroups is always at least 1, with the first element of the array the effective group ID.
This information is always available on a Unix domain socket, although there are often special arrangments the sender must make to have the information included when sending, and there are often special arrangements (e.g., socket options) the receiver must make to get the credentials. On our FreeBSD system, the receiver doesn’t have to do anything special other than call recvmsg with an ancillary buffer large enough to hold the credentials, as we show in Figure 15.14. The sender, however, must include a cmsgcred structure when sending data using sendmsg.
#include "unp.h"
#define CONTROL_LEN (sizeof(struct cmsghdr)+sizeof(struct cmsgcred))
ssize_t read_cred(int fd, void *ptr, size_t nbytes, struct cmsgcred *cmsgcredptr)
{
struct msghdr msg;
struct iovec iov[1];
char control[CONTROL_LEN];
int n;
msg.msg_name=NULL;
msg.msg_namelen=0;
iov[0].iov_base=ptr;
iov[0].iov_len=nbytes;
msg.msg_iov=iov;
msg.msg_iovlen=1;
msg.msg_controllen=sizeof(control);
msg.msg_flags=0;
if((n=recvmsg(fd, &msg, 0))<0)
return n;
cmsgcredptr->cmcred_ngroups=0;
if(cmsgcredptr && msg.msg_controllen>0)
{
struct cmsghdr *cmptr=(struct cmsghdr *) control;
if(cmptr->cmsg_len<CONTROL_LEN)
{
err_quit("control length=%d", cmptr->cmgs_len);
}
if(cmptr->cmsg_level!=SOL_SCOKET)
{
err_quit("control level!= SOL_SOCKET");
}
if(cmptr->cmsg_type!=SCM_CREDS)
{
err_quit("control type != SCM_CREDS");
}
memcpy(cmscredptr, CMSG_DATA(cmptr), sizeof(struct cmsgcred));
}
return n;
}
read_cred function: reads and returns sender’s credentials