/*************************************************************************\ * Copyright (C) Michael Kerrisk, 2018. * * * * This program is free software. You may use, modify, and redistribute it * * under the terms of the GNU General Public License as published by the * * Free Software Foundation, either version 3 or (at your option) any * * later version. This program is distributed without any warranty. See * * the file COPYING.gpl-v3 for details. * \*************************************************************************/ /* Supplementary program for Chapter 61 */ /* scm_cred_recv.c Used in conjunction with scm_cred_send.c to demonstrate passing of process credentials via a UNIX domain socket. This program receives credentials sent to a UNIX domain socket. Usage is as shown in the usageErr() call below. Credentials can be exchanged over stream or datagram sockets. This program uses stream sockets by default; the "-d" command-line option specifies that datagram sockets should be used instead. This program is Linux-specific. See also scm_multi_recv.c. */ #include "scm_cred.h" int main(int argc, char *argv[]) { int data, lfd, sfd, optval, opt; ssize_t nr; Boolean useDatagramSocket; struct msghdr msgh; struct iovec iov; struct ucred *ucredp, ucred; /* Allocate a char array of suitable size to hold the ancillary data. However, since this buffer is in reality a 'struct cmsghdr', use a union to ensure that it is aligned as required for that structure. Alternatively, we could allocate the buffer using malloc(), which returns a buffer that satisfies the strictest alignment requirements of any type */ union { char buf[CMSG_SPACE(sizeof(struct ucred))]; /* Space large enough to hold a 'ucred' structure */ struct cmsghdr align; } controlMsg; struct cmsghdr *cmsgp; /* Pointer used to iterate through headers in ancillary data */ socklen_t len; /* Parse command-line options */ useDatagramSocket = FALSE; while ((opt = getopt(argc, argv, "d")) != -1) { switch (opt) { case 'd': useDatagramSocket = TRUE; break; default: usageErr("%s [-d]\n" " -d use datagram socket\n", argv[0]); } } /* Create socket bound to a well-known address. In the case where we are using stream sockets, also make the socket a listening socket and accept a connection on the socket. */ if (remove(SOCK_PATH) == -1 && errno != ENOENT) errExit("remove-%s", SOCK_PATH); if (useDatagramSocket) { sfd = unixBind(SOCK_PATH, SOCK_DGRAM); if (sfd == -1) errExit("unixBind"); } else { lfd = unixBind(SOCK_PATH, SOCK_STREAM); if (lfd == -1) errExit("unixBind"); if (listen(lfd, 5) == -1) errExit("listen"); sfd = accept(lfd, NULL, NULL); if (sfd == -1) errExit("accept"); } /* We must set the SO_PASSCRED socket option in order to receive credentials */ optval = 1; if (setsockopt(sfd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) errExit("setsockopt"); /* The 'msg_name' field can be set to point to a buffer where the kernel will place the address of the peer socket. However, we don't need the address of the peer, so we set this field to NULL. */ msgh.msg_name = NULL; msgh.msg_namelen = 0; /* Set fields of 'msgh' to point to buffer used to receive (real) data read by recvmsg() */ msgh.msg_iov = &iov; msgh.msg_iovlen = 1; iov.iov_base = &data; iov.iov_len = sizeof(int); /* Set 'msgh' fields to describe the ancillary data buffer */ msgh.msg_control = controlMsg.buf; msgh.msg_controllen = sizeof(controlMsg.buf); /* Receive real plus ancillary data */ nr = recvmsg(sfd, &msgh, 0); if (nr == -1) errExit("recvmsg"); printf("recvmsg() returned %ld\n", (long) nr); if (nr > 0) printf("Received data = %d\n", data); /* Get the address of the first 'cmsghdr' in the received ancillary data */ cmsgp = CMSG_FIRSTHDR(&msgh); /* Check the validity of the 'cmsghdr' */ if (cmsgp == NULL || cmsgp->cmsg_len != CMSG_LEN(sizeof(struct ucred))) fatal("bad cmsg header / message length"); if (cmsgp->cmsg_level != SOL_SOCKET) fatal("cmsg_level != SOL_SOCKET"); if (cmsgp->cmsg_type != SCM_CREDENTIALS) fatal("cmsg_type != SCM_CREDENTIALS"); /* The data area of the 'cmsghdr' is a 'struct ucred', so assign the address of the data area to a suitable pointer */ ucredp = (struct ucred *) CMSG_DATA(cmsgp); /* Display the credentials from the received data area */ printf("Received credentials pid=%ld, uid=%ld, gid=%ld\n", (long) ucredp->pid, (long) ucredp->uid, (long) ucredp->gid); /* The Linux-specific, read-only SO_PEERCRED socket option returns credential information about the peer, as described in socket(7). This operation can be performed on UNIX domain stream sockets and on UNIX domain sockets (stream or datagram) created with socketpair(). */ len = sizeof(struct ucred); if (getsockopt(sfd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) errExit("getsockopt"); printf("Credentials from SO_PEERCRED: pid=%ld, euid=%ld, egid=%ld\n", (long) ucred.pid, (long) ucred.uid, (long) ucred.gid); exit(EXIT_SUCCESS); }