4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-02-08 10:09:32 +08:00

174 lines
5.7 KiB
C

/*************************************************************************\
* 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);
}