Showing 2 posts
One of the problems of learning new stuff based on trial-and-error iterations is that it is very easy to miss important details... but that's the price to pay when there is no decent documentation available for a given feature. We saw yesterday multiple details about LOCAL_CREDS socket credentials and, as you may deduce, I missed some.
First of all I assumed that setting the LOCAL_CREDS option only affected the next received message (I didn't mention this explicitly in the post though). It turns out that this is incorrect: enabling this option makes the socket transmit credentials information on each message until the option is disabled again.
Secondly, setting the LOCAL_CREDS option on a server socket (one configured with the listen(2) call) results in all sockets created from it through accept(2) to also carry the flag enabled. In other words, it is inherited.
These features are interesting because, when using combined, avoid the need for the synchronization protocol outlined in the previous post — in some cases only. If the credentials are to be transmitted at the very beginning of the connection, the server can follow these steps:
August 28, 2006
·
Tags:
credentials, netbsd, sockets
Continue reading (about
2 minutes)
Socket credentials is a feature that allows a user process to receive the credentials (UID, GID, etc.) of the process at the other end of a communication socket in a safe way. The operating system is in charge of managing this information, sent separately from the data flow, so that the user processes cannot fake it. There are many different implementations of this concept out there as you can imagine.
For some reason I assumed for a long time that NetBSD didn't support any kind of socket credentials. However, I recently discovered that it indeed supports them through the LOCAL_CREDS socket option. Unfortunately it behaves quite differently from other methods. This poses some annoying portability problems in applications not designed in the first place to support it (e.g. D-Bus, the specific program I'm fighting right now).
LOCAL_CREDS works as follows:
#include <sys/param.h>
#include <sys/types.h>
#include <sys/inttypes.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int
main(void)
{
int sv[2];
int on = 1;
ssize_t len;
struct iovec iov;
struct msghdr msg;
struct {
struct cmsghdr hdr;
struct sockcred cred;
gid_t groups[NGROUPS - 1];
} cmsg;
/*
* Create a pair of interconnected sockets for simplicity:
* sv[0] - Receive end (this program).
* sv[1] - Write end (the remote program, theorically).
*/
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1)
err(EXIT_FAILURE, "socketpair");
/*
* Enable the LOCAL_CREDS option on the reception socket.
*/
if (setsockopt(sv[0], 0, LOCAL_CREDS, &on, sizeof(on)) == -1)
err(EXIT_FAILURE, "setsockopt");
/*
* The remote application writes the message AFTER setsockopt
* has been used by the receiver. If you move this above the
* setsockopt call, you will see how it does not work as
* expected.
*/
if (write(sv[1], &on, sizeof(on)) == -1)
err(EXIT_FAILURE, "write");
/*
* Prepare space to receive the credentials message.
*/
iov.iov_base = &on;
iov.iov_len = 1;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &cmsg;
msg.msg_controllen = sizeof(struct cmsghdr) +
SOCKCREDSIZE(NGROUPS);
memset(&cmsg, 0, sizeof(cmsg));
/*
* Receive the message.
*/
len = recvmsg(sv[0], &msg, 0);
if (len < 0)
err(EXIT_FAILURE, "recvmsg");
printf("Got %zu bytesn", len);
/*
* Print out credentials information, if received
* appropriately.
*/
if (cmsg.hdr.cmsg_type == SCM_CREDS) {
printf("UID: %" PRIdMAX "n",
(intmax_t)cmsg.cred.sc_uid);
printf("EUID: %" PRIdMAX "n",
(intmax_t)cmsg.cred.sc_euid);
printf("GID: %" PRIdMAX "n",
(intmax_t)cmsg.cred.sc_gid);
printf("EGID: %" PRIdMAX "n",
(intmax_t)cmsg.cred.sc_egid);
if (cmsg.cred.sc_ngroups > 0) {
int i;
printf("Supplementary groups:");
for (i = 0; i < cmsg.cred.sc_ngroups; i++)
printf(" %" PRIdMAX,
(intmax_t)cmsg.cred.sc_groups[i]);
printf("n");
}
} else
errx(EXIT_FAILURE, "Message did not include credentials");
close(sv[0]);
close(sv[1]);
return EXIT_SUCCESS;
}
August 27, 2006
·
Tags:
credentials, netbsd, sockets
Continue reading (about
4 minutes)