mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-02-08 18:19:08 +08:00
174 lines
5.7 KiB
C
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);
|
||
|
}
|