freebsd-dev/usr.sbin/rwhod/rwhod.c

785 lines
20 KiB
C
Raw Normal View History

/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 1983, 1993 The Regents of the University of California.
* Copyright (c) 2013 Mariusz Zaborski <oshogbo@FreeBSD.org>
* All rights reserved.
1994-05-26 05:23:31 +00:00
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
1994-05-26 05:23:31 +00:00
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char copyright[] =
1994-05-26 05:23:31 +00:00
"@(#) Copyright (c) 1983, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#if 0
#ifndef lint
1994-05-26 05:23:31 +00:00
static char sccsid[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93";
#endif /* not lint */
#endif
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
1994-05-26 05:23:31 +00:00
#include <sys/param.h>
#include <sys/capsicum.h>
#include <sys/ioctl.h>
#include <sys/procdesc.h>
1994-05-26 05:23:31 +00:00
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
1994-05-26 05:23:31 +00:00
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/in.h>
#include <arpa/inet.h>
1994-05-26 05:23:31 +00:00
#include <protocols/rwhod.h>
#include <ctype.h>
#include <capsicum_helpers.h>
#include <err.h>
1994-05-26 05:23:31 +00:00
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
1994-05-26 05:23:31 +00:00
#include <netdb.h>
#include <paths.h>
#include <pwd.h>
1994-05-26 05:23:31 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
2002-07-11 21:40:15 +00:00
#include <timeconv.h>
#include <utmpx.h>
#include <unistd.h>
#define UNPRIV_USER "daemon"
1996-08-25 21:37:11 +00:00
#define UNPRIV_GROUP "daemon"
#define NO_MULTICAST 0 /* multicast modes */
#define PER_INTERFACE_MULTICAST 1
#define SCOPED_MULTICAST 2
#define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */
#define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */
/* (belongs in protocols/rwhod.h) */
int insecure_mode;
int quiet_mode;
int iff_flag = IFF_POINTOPOINT;
int multicast_mode = NO_MULTICAST;
int multicast_scope;
struct sockaddr_in multicast_addr =
{ sizeof(multicast_addr), AF_INET, 0, { 0 }, { 0 } };
1994-05-26 05:23:31 +00:00
/*
* Sleep interval. Don't forget to change the down time check in ruptime
1994-05-26 05:23:31 +00:00
* if this is changed.
*/
#define SL_INTERVAL (3 * 60)
1994-05-26 05:23:31 +00:00
char myname[MAXHOSTNAMELEN];
/*
* We communicate with each neighbor in a list constructed at the time we're
* started up. Neighbors are currently directly connected via a hardware
* interface.
*/
struct neighbor {
struct neighbor *n_next;
char *n_name; /* interface name */
1994-05-26 05:23:31 +00:00
struct sockaddr *n_addr; /* who to send to */
int n_addrlen; /* size of address */
int n_flags; /* should forward?, interface flags */
1994-05-26 05:23:31 +00:00
};
struct neighbor *neighbors;
struct whod mywd;
struct servent *sp;
int s;
int fdp;
pid_t pid_child_receiver;
1994-05-26 05:23:31 +00:00
2002-07-11 21:40:15 +00:00
#define WHDRSIZE (int)(sizeof(mywd) - sizeof(mywd.wd_we))
1994-05-26 05:23:31 +00:00
int configure(int so);
void getboottime(int signo __unused);
void receiver_process(void);
void rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo);
void run_as(uid_t *uid, gid_t *gid);
void quit(const char *msg);
void sender_process(void);
int verify(char *name, int maxlen);
2002-07-11 21:40:15 +00:00
static void usage(void);
1994-05-26 05:23:31 +00:00
#ifdef DEBUG
char *interval(int time, char *updown);
void Sendto(int s, const void *buf, size_t cc, int flags,
const struct sockaddr *to, int tolen);
1994-05-26 05:23:31 +00:00
#define sendto Sendto
#endif
/*
* This version of Berkeley's rwhod has been modified to use IP multicast
* datagrams, under control of a new command-line option:
*
* rwhod -m causes rwhod to use IP multicast (instead of
* broadcast or unicast) on all interfaces that have
* the IFF_MULTICAST flag set in their "ifnet" structs
* (excluding the loopback interface). The multicast
* reports are sent with a time-to-live of 1, to prevent
* forwarding beyond the directly-connected subnet(s).
*
* rwhod -m <ttl> causes rwhod to send IP multicast datagrams with a
* time-to-live of <ttl>, via a SINGLE interface rather
* than all interfaces. <ttl> must be between 0 and
* MAX_MULTICAST_SCOPE, defined below. Note that "-m 1"
* is different than "-m", in that "-m 1" specifies
* transmission on one interface only.
*
* When "-m" is used without a <ttl> argument, the program accepts multicast
* rwhod reports from all multicast-capable interfaces. If a <ttl> argument
* is given, it accepts multicast reports from only one interface, the one
* on which reports are sent (which may be controlled via the host's routing
* table). Regardless of the "-m" option, the program accepts broadcast or
* unicast reports from all interfaces. Thus, this program will hear the
* reports of old, non-multicasting rwhods, but, if multicasting is used,
* those old rwhods won't hear the reports generated by this program.
*
* -- Steve Deering, Stanford University, February 1989
*/
1994-05-26 05:23:31 +00:00
int
2002-07-11 21:40:15 +00:00
main(int argc, char *argv[])
1994-05-26 05:23:31 +00:00
{
int on;
1994-05-26 05:23:31 +00:00
char *cp;
2002-07-11 21:40:15 +00:00
struct sockaddr_in soin;
1996-08-25 21:37:11 +00:00
uid_t unpriv_uid;
gid_t unpriv_gid;
1994-05-26 05:23:31 +00:00
on = 1;
if (getuid())
errx(1, "not super user");
1996-08-25 21:37:11 +00:00
run_as(&unpriv_uid, &unpriv_gid);
argv++;
argc--;
while (argc > 0 && *argv[0] == '-') {
if (strcmp(*argv, "-m") == 0) {
if (argc > 1 && isdigit(*(argv + 1)[0])) {
argv++;
argc--;
multicast_mode = SCOPED_MULTICAST;
multicast_scope = atoi(*argv);
if (multicast_scope > MAX_MULTICAST_SCOPE) {
errx(1, "ttl must not exceed %u",
MAX_MULTICAST_SCOPE);
}
} else {
multicast_mode = PER_INTERFACE_MULTICAST;
}
} else if (strcmp(*argv, "-i") == 0) {
insecure_mode = 1;
} else if (strcmp(*argv, "-l") == 0) {
quiet_mode = 1;
} else if (strcmp(*argv, "-p") == 0) {
iff_flag = 0;
} else {
usage();
}
argv++;
argc--;
}
if (argc > 0)
usage();
1994-05-26 05:23:31 +00:00
#ifndef DEBUG
daemon(1, 0);
#endif
(void) signal(SIGHUP, getboottime);
openlog("rwhod", LOG_PID | LOG_NDELAY, LOG_DAEMON);
sp = getservbyname("who", "udp");
if (sp == NULL) {
syslog(LOG_ERR, "who/udp: unknown service");
exit(1);
}
1994-05-26 05:23:31 +00:00
if (chdir(_PATH_RWHODIR) < 0) {
syslog(LOG_ERR, "%s: %m", _PATH_RWHODIR);
1994-05-26 05:23:31 +00:00
exit(1);
}
/*
* Establish host name as returned by system.
*/
if (gethostname(myname, sizeof(myname) - 1) < 0) {
syslog(LOG_ERR, "gethostname: %m");
exit(1);
}
if ((cp = strchr(myname, '.')) != NULL)
1994-05-26 05:23:31 +00:00
*cp = '\0';
strlcpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname));
1994-05-26 05:23:31 +00:00
getboottime(0);
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
syslog(LOG_ERR, "socket: %m");
exit(1);
}
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
exit(1);
}
2002-07-11 21:40:15 +00:00
memset(&soin, 0, sizeof(soin));
soin.sin_len = sizeof(soin);
soin.sin_family = AF_INET;
soin.sin_port = sp->s_port;
if (bind(s, (struct sockaddr *)&soin, sizeof(soin)) < 0) {
1994-05-26 05:23:31 +00:00
syslog(LOG_ERR, "bind: %m");
exit(1);
}
if (setgid(unpriv_gid) != 0) {
syslog(LOG_ERR, "setgid: %m");
exit(1);
}
if (setgroups(1, &unpriv_gid) != 0) { /* XXX BOGUS groups[0] = egid */
syslog(LOG_ERR, "setgroups: %m");
exit(1);
}
if (setuid(unpriv_uid) != 0) {
syslog(LOG_ERR, "setuid: %m");
exit(1);
}
1994-05-26 05:23:31 +00:00
if (!configure(s))
exit(1);
if (!quiet_mode) {
pid_child_receiver = pdfork(&fdp, 0);
if (pid_child_receiver == 0) {
receiver_process();
} else if (pid_child_receiver > 0) {
sender_process();
} else if (pid_child_receiver == -1) {
if (errno == ENOSYS) {
syslog(LOG_ERR,
"The pdfork(2) system call is not available - kernel too old.");
} else {
syslog(LOG_ERR, "pdfork: %m");
}
exit(1);
}
} else {
receiver_process();
}
}
static void
usage(void)
{
fprintf(stderr, "usage: rwhod [-i] [-p] [-l] [-m [ttl]]\n");
exit(1);
}
void
run_as(uid_t *uid, gid_t *gid)
{
struct passwd *pw;
struct group *gr;
pw = getpwnam(UNPRIV_USER);
if (pw == NULL) {
syslog(LOG_ERR, "getpwnam(%s): %m", UNPRIV_USER);
exit(1);
}
*uid = pw->pw_uid;
gr = getgrnam(UNPRIV_GROUP);
if (gr == NULL) {
syslog(LOG_ERR, "getgrnam(%s): %m", UNPRIV_GROUP);
exit(1);
}
*gid = gr->gr_gid;
}
1994-05-26 05:23:31 +00:00
/*
* Check out host name for unprintables
* and other funnies before allowing a file
* to be created. Sorry, but blanks aren't allowed.
*/
int
verify(char *name, int maxlen)
{
int size;
size = 0;
while (*name != '\0' && size < maxlen - 1) {
if (!isascii((unsigned char)*name) ||
!(isalnum((unsigned char)*name) ||
ispunct((unsigned char)*name))) {
return (0);
}
name++;
size++;
}
*name = '\0';
return (size > 0);
}
void
receiver_process(void)
{
struct sockaddr_in from;
struct stat st;
Change the cap_rights_t type from uint64_t to a structure that we can extend in the future in a backward compatible (API and ABI) way. The cap_rights_t represents capability rights. We used to use one bit to represent one right, but we are running out of spare bits. Currently the new structure provides place for 114 rights (so 50 more than the previous cap_rights_t), but it is possible to grow the structure to hold at least 285 rights, although we can make it even larger if 285 rights won't be enough. The structure definition looks like this: struct cap_rights { uint64_t cr_rights[CAP_RIGHTS_VERSION + 2]; }; The initial CAP_RIGHTS_VERSION is 0. The top two bits in the first element of the cr_rights[] array contain total number of elements in the array - 2. This means if those two bits are equal to 0, we have 2 array elements. The top two bits in all remaining array elements should be 0. The next five bits in all array elements contain array index. Only one bit is used and bit position in this five-bits range defines array index. This means there can be at most five array elements in the future. To define new right the CAPRIGHT() macro must be used. The macro takes two arguments - an array index and a bit to set, eg. #define CAP_PDKILL CAPRIGHT(1, 0x0000000000000800ULL) We still support aliases that combine few rights, but the rights have to belong to the same array element, eg: #define CAP_LOOKUP CAPRIGHT(0, 0x0000000000000400ULL) #define CAP_FCHMOD CAPRIGHT(0, 0x0000000000002000ULL) #define CAP_FCHMODAT (CAP_FCHMOD | CAP_LOOKUP) There is new API to manage the new cap_rights_t structure: cap_rights_t *cap_rights_init(cap_rights_t *rights, ...); void cap_rights_set(cap_rights_t *rights, ...); void cap_rights_clear(cap_rights_t *rights, ...); bool cap_rights_is_set(const cap_rights_t *rights, ...); bool cap_rights_is_valid(const cap_rights_t *rights); void cap_rights_merge(cap_rights_t *dst, const cap_rights_t *src); void cap_rights_remove(cap_rights_t *dst, const cap_rights_t *src); bool cap_rights_contains(const cap_rights_t *big, const cap_rights_t *little); Capability rights to the cap_rights_init(), cap_rights_set(), cap_rights_clear() and cap_rights_is_set() functions are provided by separating them with commas, eg: cap_rights_t rights; cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_FSTAT); There is no need to terminate the list of rights, as those functions are actually macros that take care of the termination, eg: #define cap_rights_set(rights, ...) \ __cap_rights_set((rights), __VA_ARGS__, 0ULL) void __cap_rights_set(cap_rights_t *rights, ...); Thanks to using one bit as an array index we can assert in those functions that there are no two rights belonging to different array elements provided together. For example this is illegal and will be detected, because CAP_LOOKUP belongs to element 0 and CAP_PDKILL to element 1: cap_rights_init(&rights, CAP_LOOKUP | CAP_PDKILL); Providing several rights that belongs to the same array's element this way is correct, but is not advised. It should only be used for aliases definition. This commit also breaks compatibility with some existing Capsicum system calls, but I see no other way to do that. This should be fine as Capsicum is still experimental and this change is not going to 9.x. Sponsored by: The FreeBSD Foundation
2013-09-05 00:09:56 +00:00
cap_rights_t rights;
char path[64];
int dirfd;
struct whod wd;
socklen_t len;
int cc, whod;
time_t t;
len = sizeof(from);
dirfd = open(".", O_RDONLY | O_DIRECTORY);
if (dirfd < 0) {
syslog(LOG_WARNING, "%s: %m", _PATH_RWHODIR);
exit(1);
}
Change the cap_rights_t type from uint64_t to a structure that we can extend in the future in a backward compatible (API and ABI) way. The cap_rights_t represents capability rights. We used to use one bit to represent one right, but we are running out of spare bits. Currently the new structure provides place for 114 rights (so 50 more than the previous cap_rights_t), but it is possible to grow the structure to hold at least 285 rights, although we can make it even larger if 285 rights won't be enough. The structure definition looks like this: struct cap_rights { uint64_t cr_rights[CAP_RIGHTS_VERSION + 2]; }; The initial CAP_RIGHTS_VERSION is 0. The top two bits in the first element of the cr_rights[] array contain total number of elements in the array - 2. This means if those two bits are equal to 0, we have 2 array elements. The top two bits in all remaining array elements should be 0. The next five bits in all array elements contain array index. Only one bit is used and bit position in this five-bits range defines array index. This means there can be at most five array elements in the future. To define new right the CAPRIGHT() macro must be used. The macro takes two arguments - an array index and a bit to set, eg. #define CAP_PDKILL CAPRIGHT(1, 0x0000000000000800ULL) We still support aliases that combine few rights, but the rights have to belong to the same array element, eg: #define CAP_LOOKUP CAPRIGHT(0, 0x0000000000000400ULL) #define CAP_FCHMOD CAPRIGHT(0, 0x0000000000002000ULL) #define CAP_FCHMODAT (CAP_FCHMOD | CAP_LOOKUP) There is new API to manage the new cap_rights_t structure: cap_rights_t *cap_rights_init(cap_rights_t *rights, ...); void cap_rights_set(cap_rights_t *rights, ...); void cap_rights_clear(cap_rights_t *rights, ...); bool cap_rights_is_set(const cap_rights_t *rights, ...); bool cap_rights_is_valid(const cap_rights_t *rights); void cap_rights_merge(cap_rights_t *dst, const cap_rights_t *src); void cap_rights_remove(cap_rights_t *dst, const cap_rights_t *src); bool cap_rights_contains(const cap_rights_t *big, const cap_rights_t *little); Capability rights to the cap_rights_init(), cap_rights_set(), cap_rights_clear() and cap_rights_is_set() functions are provided by separating them with commas, eg: cap_rights_t rights; cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_FSTAT); There is no need to terminate the list of rights, as those functions are actually macros that take care of the termination, eg: #define cap_rights_set(rights, ...) \ __cap_rights_set((rights), __VA_ARGS__, 0ULL) void __cap_rights_set(cap_rights_t *rights, ...); Thanks to using one bit as an array index we can assert in those functions that there are no two rights belonging to different array elements provided together. For example this is illegal and will be detected, because CAP_LOOKUP belongs to element 0 and CAP_PDKILL to element 1: cap_rights_init(&rights, CAP_LOOKUP | CAP_PDKILL); Providing several rights that belongs to the same array's element this way is correct, but is not advised. It should only be used for aliases definition. This commit also breaks compatibility with some existing Capsicum system calls, but I see no other way to do that. This should be fine as Capsicum is still experimental and this change is not going to 9.x. Sponsored by: The FreeBSD Foundation
2013-09-05 00:09:56 +00:00
cap_rights_init(&rights, CAP_CREATE, CAP_FSTAT, CAP_FTRUNCATE,
CAP_LOOKUP, CAP_SEEK, CAP_WRITE);
if (caph_rights_limit(dirfd, &rights) < 0) {
syslog(LOG_WARNING, "cap_rights_limit: %m");
exit(1);
}
if (caph_enter() < 0) {
syslog(LOG_ERR, "cap_enter: %m");
exit(1);
}
for (;;) {
cc = recvfrom(s, &wd, sizeof(wd), 0, (struct sockaddr *)&from,
&len);
1994-05-26 05:23:31 +00:00
if (cc <= 0) {
if (cc < 0 && errno != EINTR)
syslog(LOG_WARNING, "recv: %m");
continue;
}
if (from.sin_port != sp->s_port && !insecure_mode) {
syslog(LOG_WARNING, "%d: bad source port from %s",
ntohs(from.sin_port), inet_ntoa(from.sin_addr));
continue;
}
if (cc < WHDRSIZE) {
syslog(LOG_WARNING, "short packet from %s",
inet_ntoa(from.sin_addr));
1994-05-26 05:23:31 +00:00
continue;
}
if (wd.wd_vers != WHODVERSION)
continue;
if (wd.wd_type != WHODTYPE_STATUS)
continue;
if (!verify(wd.wd_hostname, sizeof(wd.wd_hostname))) {
syslog(LOG_WARNING, "malformed host name from %s",
inet_ntoa(from.sin_addr));
1994-05-26 05:23:31 +00:00
continue;
}
(void) snprintf(path, sizeof(path), "whod.%s", wd.wd_hostname);
1994-05-26 05:23:31 +00:00
/*
* Rather than truncating and growing the file each time,
* use ftruncate if size is less than previous size.
*/
whod = openat(dirfd, path, O_WRONLY | O_CREAT, 0644);
1994-05-26 05:23:31 +00:00
if (whod < 0) {
syslog(LOG_WARNING, "%s: %m", path);
continue;
}
Change the cap_rights_t type from uint64_t to a structure that we can extend in the future in a backward compatible (API and ABI) way. The cap_rights_t represents capability rights. We used to use one bit to represent one right, but we are running out of spare bits. Currently the new structure provides place for 114 rights (so 50 more than the previous cap_rights_t), but it is possible to grow the structure to hold at least 285 rights, although we can make it even larger if 285 rights won't be enough. The structure definition looks like this: struct cap_rights { uint64_t cr_rights[CAP_RIGHTS_VERSION + 2]; }; The initial CAP_RIGHTS_VERSION is 0. The top two bits in the first element of the cr_rights[] array contain total number of elements in the array - 2. This means if those two bits are equal to 0, we have 2 array elements. The top two bits in all remaining array elements should be 0. The next five bits in all array elements contain array index. Only one bit is used and bit position in this five-bits range defines array index. This means there can be at most five array elements in the future. To define new right the CAPRIGHT() macro must be used. The macro takes two arguments - an array index and a bit to set, eg. #define CAP_PDKILL CAPRIGHT(1, 0x0000000000000800ULL) We still support aliases that combine few rights, but the rights have to belong to the same array element, eg: #define CAP_LOOKUP CAPRIGHT(0, 0x0000000000000400ULL) #define CAP_FCHMOD CAPRIGHT(0, 0x0000000000002000ULL) #define CAP_FCHMODAT (CAP_FCHMOD | CAP_LOOKUP) There is new API to manage the new cap_rights_t structure: cap_rights_t *cap_rights_init(cap_rights_t *rights, ...); void cap_rights_set(cap_rights_t *rights, ...); void cap_rights_clear(cap_rights_t *rights, ...); bool cap_rights_is_set(const cap_rights_t *rights, ...); bool cap_rights_is_valid(const cap_rights_t *rights); void cap_rights_merge(cap_rights_t *dst, const cap_rights_t *src); void cap_rights_remove(cap_rights_t *dst, const cap_rights_t *src); bool cap_rights_contains(const cap_rights_t *big, const cap_rights_t *little); Capability rights to the cap_rights_init(), cap_rights_set(), cap_rights_clear() and cap_rights_is_set() functions are provided by separating them with commas, eg: cap_rights_t rights; cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_FSTAT); There is no need to terminate the list of rights, as those functions are actually macros that take care of the termination, eg: #define cap_rights_set(rights, ...) \ __cap_rights_set((rights), __VA_ARGS__, 0ULL) void __cap_rights_set(cap_rights_t *rights, ...); Thanks to using one bit as an array index we can assert in those functions that there are no two rights belonging to different array elements provided together. For example this is illegal and will be detected, because CAP_LOOKUP belongs to element 0 and CAP_PDKILL to element 1: cap_rights_init(&rights, CAP_LOOKUP | CAP_PDKILL); Providing several rights that belongs to the same array's element this way is correct, but is not advised. It should only be used for aliases definition. This commit also breaks compatibility with some existing Capsicum system calls, but I see no other way to do that. This should be fine as Capsicum is still experimental and this change is not going to 9.x. Sponsored by: The FreeBSD Foundation
2013-09-05 00:09:56 +00:00
cap_rights_init(&rights, CAP_FSTAT, CAP_FTRUNCATE, CAP_WRITE);
if (caph_rights_limit(whod, &rights) < 0) {
syslog(LOG_WARNING, "cap_rights_limit: %m");
exit(1);
}
1994-05-26 05:23:31 +00:00
#if ENDIAN != BIG_ENDIAN
{
struct whoent *we;
int i, n;
1994-05-26 05:23:31 +00:00
n = (cc - WHDRSIZE) / sizeof(struct whoent);
1994-05-26 05:23:31 +00:00
/* undo header byte swapping before writing to file */
wd.wd_sendtime = ntohl(wd.wd_sendtime);
for (i = 0; i < 3; i++)
wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]);
wd.wd_boottime = ntohl(wd.wd_boottime);
we = wd.wd_we;
for (i = 0; i < n; i++) {
we->we_idle = ntohl(we->we_idle);
we->we_utmp.out_time =
ntohl(we->we_utmp.out_time);
we++;
}
}
#endif
(void) time(&t);
wd.wd_recvtime = _time_to_int(t);
1994-05-26 05:23:31 +00:00
(void) write(whod, (char *)&wd, cc);
if (fstat(whod, &st) < 0 || st.st_size > cc)
ftruncate(whod, cc);
(void) close(whod);
}
(void) close(dirfd);
1994-05-26 05:23:31 +00:00
}
1996-08-25 21:37:11 +00:00
void
sender_process(void)
1994-05-26 05:23:31 +00:00
{
int sendcount;
1994-05-26 05:23:31 +00:00
double avenrun[3];
time_t now;
int i, cc, status;
struct utmpx *ut;
struct stat stb;
struct neighbor *np;
struct whoent *we, *wend;
1994-05-26 05:23:31 +00:00
sendcount = 0;
for (;;) {
we = mywd.wd_we;
now = time(NULL);
if (sendcount % 10 == 0)
getboottime(0);
sendcount++;
wend = &mywd.wd_we[1024 / sizeof(struct whoent)];
setutxent();
while ((ut = getutxent()) != NULL && we < wend) {
if (ut->ut_type != USER_PROCESS)
continue;
strncpy(we->we_utmp.out_line, ut->ut_line,
sizeof(we->we_utmp.out_line));
strncpy(we->we_utmp.out_name, ut->ut_user,
sizeof(we->we_utmp.out_name));
we->we_utmp.out_time =
htonl(_time_to_time32(ut->ut_tv.tv_sec));
we++;
}
endutxent();
1994-05-26 05:23:31 +00:00
if (chdir(_PATH_DEV) < 0) {
syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV);
exit(1);
}
wend = we;
for (we = mywd.wd_we; we < wend; we++) {
if (stat(we->we_utmp.out_line, &stb) >= 0)
we->we_idle = htonl(now - stb.st_atime);
}
(void) getloadavg(avenrun,
sizeof(avenrun) / sizeof(avenrun[0]));
for (i = 0; i < 3; i++)
mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100));
cc = (char *)wend - (char *)&mywd;
mywd.wd_sendtime = htonl(_time_to_time32(time(NULL)));
mywd.wd_vers = WHODVERSION;
mywd.wd_type = WHODTYPE_STATUS;
if (multicast_mode == SCOPED_MULTICAST) {
(void) sendto(s, (char *)&mywd, cc, 0,
(struct sockaddr *)&multicast_addr,
sizeof(multicast_addr));
} else {
for (np = neighbors; np != NULL; np = np->n_next) {
if (multicast_mode == PER_INTERFACE_MULTICAST &&
(np->n_flags & IFF_MULTICAST) != 0) {
/*
* Select the outgoing interface for the
* multicast.
*/
if (setsockopt(s, IPPROTO_IP,
IP_MULTICAST_IF,
&(((struct sockaddr_in *)np->n_addr)->sin_addr),
sizeof(struct in_addr)) < 0) {
syslog(LOG_ERR,
"setsockopt IP_MULTICAST_IF: %m");
exit(1);
}
(void) sendto(s, (char *)&mywd, cc, 0,
(struct sockaddr *)&multicast_addr,
sizeof(multicast_addr));
} else {
(void) sendto(s, (char *)&mywd, cc, 0,
np->n_addr, np->n_addrlen);
}
}
}
if (chdir(_PATH_RWHODIR) < 0) {
syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR);
exit(1);
}
if (waitpid(pid_child_receiver, &status, WNOHANG) ==
pid_child_receiver) {
break;
}
sleep(SL_INTERVAL);
}
1994-05-26 05:23:31 +00:00
}
void
getboottime(int signo __unused)
1994-05-26 05:23:31 +00:00
{
int mib[2];
size_t size;
struct timeval tm;
mib[0] = CTL_KERN;
mib[1] = KERN_BOOTTIME;
size = sizeof(tm);
if (sysctl(mib, nitems(mib), &tm, &size, NULL, 0) == -1) {
1994-05-26 05:23:31 +00:00
syslog(LOG_ERR, "cannot get boottime: %m");
exit(1);
}
mywd.wd_boottime = htonl(_time_to_time32(tm.tv_sec));
1994-05-26 05:23:31 +00:00
}
void
quit(const char *msg)
1994-05-26 05:23:31 +00:00
{
syslog(LOG_ERR, "%s", msg);
1994-05-26 05:23:31 +00:00
exit(1);
}
void
rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo)
1994-05-26 05:23:31 +00:00
{
struct sockaddr *sa;
int i;
1994-05-26 05:23:31 +00:00
memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
for (i = 0; i < RTAX_MAX && cp < cplim; i++) {
1994-05-26 05:23:31 +00:00
if ((rtinfo->rti_addrs & (1 << i)) == 0)
continue;
sa = (struct sockaddr *)cp;
rtinfo->rti_info[i] = sa;
2004-04-13 11:24:43 +00:00
cp += SA_SIZE(sa);
1994-05-26 05:23:31 +00:00
}
}
/*
* Figure out device configuration and select
* networks which deserve status information.
*/
int
configure(int so)
1994-05-26 05:23:31 +00:00
{
struct neighbor *np;
struct if_msghdr *ifm;
struct ifa_msghdr *ifam;
1994-05-26 05:23:31 +00:00
struct sockaddr_dl *sdl;
size_t needed;
int mib[6], flags, lflags, len;
1994-05-26 05:23:31 +00:00
char *buf, *lim, *next;
struct rt_addrinfo info;
flags = 0;
if (multicast_mode != NO_MULTICAST) {
multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP);
multicast_addr.sin_port = sp->s_port;
}
if (multicast_mode == SCOPED_MULTICAST) {
struct ip_mreq mreq;
unsigned char ttl;
mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
2002-07-11 21:40:15 +00:00
if (setsockopt(so, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&mreq, sizeof(mreq)) < 0) {
syslog(LOG_ERR,
"setsockopt IP_ADD_MEMBERSHIP: %m");
return (0);
}
ttl = multicast_scope;
if (setsockopt(so, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
sizeof(ttl)) < 0) {
syslog(LOG_ERR,
"setsockopt IP_MULTICAST_TTL: %m");
return (0);
}
return (1);
}
1994-05-26 05:23:31 +00:00
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_INET;
mib[4] = NET_RT_IFLIST;
mib[5] = 0;
if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0)
1994-05-26 05:23:31 +00:00
quit("route-sysctl-estimate");
if ((buf = malloc(needed)) == NULL)
quit("malloc");
if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0)
1994-05-26 05:23:31 +00:00
quit("actual retrieval of interface table");
lim = buf + needed;
sdl = NULL; /* XXX just to keep gcc -Wall happy */
for (next = buf; next < lim; next += ifm->ifm_msglen) {
ifm = (struct if_msghdr *)next;
if (ifm->ifm_type == RTM_IFINFO) {
sdl = (struct sockaddr_dl *)(ifm + 1);
flags = ifm->ifm_flags;
continue;
}
if ((flags & IFF_UP) == 0)
continue;
lflags = IFF_BROADCAST | iff_flag;
if (multicast_mode == PER_INTERFACE_MULTICAST)
lflags |= IFF_MULTICAST;
if ((flags & lflags) == 0)
1994-05-26 05:23:31 +00:00
continue;
if (ifm->ifm_type != RTM_NEWADDR)
quit("out of sync parsing NET_RT_IFLIST");
ifam = (struct ifa_msghdr *)ifm;
info.rti_addrs = ifam->ifam_addrs;
rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
&info);
1994-05-26 05:23:31 +00:00
/* gag, wish we could get rid of Internet dependencies */
#define dstaddr info.rti_info[RTAX_BRD]
#define ifaddr info.rti_info[RTAX_IFA]
#define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr
#define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port
1994-05-26 05:23:31 +00:00
if (dstaddr == 0 || dstaddr->sa_family != AF_INET)
continue;
PORT_SA(dstaddr) = sp->s_port;
for (np = neighbors; np != NULL; np = np->n_next) {
1994-05-26 05:23:31 +00:00
if (memcmp(sdl->sdl_data, np->n_name,
sdl->sdl_nlen) == 0 &&
IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr)) {
1994-05-26 05:23:31 +00:00
break;
}
}
1994-05-26 05:23:31 +00:00
if (np != NULL)
continue;
len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1;
np = malloc(len);
1994-05-26 05:23:31 +00:00
if (np == NULL)
quit("malloc of neighbor structure");
memset(np, 0, len);
np->n_flags = flags;
np->n_addr = (struct sockaddr *)(np + 1);
np->n_addrlen = dstaddr->sa_len;
np->n_name = np->n_addrlen + (char *)np->n_addr;
memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen);
memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen);
if (multicast_mode == PER_INTERFACE_MULTICAST &&
(flags & IFF_MULTICAST) != 0 &&
(flags & IFF_LOOPBACK) == 0) {
struct ip_mreq mreq;
memcpy((char *)np->n_addr, (char *)ifaddr,
np->n_addrlen);
mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP);
mreq.imr_interface.s_addr =
((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr;
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&mreq, sizeof(mreq)) < 0) {
syslog(LOG_ERR,
"setsockopt IP_ADD_MEMBERSHIP: %m");
#if 0
/* Fall back to broadcast on this if. */
np->n_flags &= ~IFF_MULTICAST;
#else
free(np);
continue;
#endif
}
}
np->n_next = neighbors;
neighbors = np;
1994-05-26 05:23:31 +00:00
}
free(buf);
return (1);
}
#ifdef DEBUG
void
Sendto(int s, const void *buf, size_t cc, int flags, const struct sockaddr *to,
int tolen)
1994-05-26 05:23:31 +00:00
{
struct whod *w;
struct whoent *we;
struct sockaddr_in *sin;
1994-05-26 05:23:31 +00:00
w = (struct whod *)buf;
sin = (struct sockaddr_in *)to;
1996-08-25 21:37:11 +00:00
printf("sendto %x.%d\n", ntohl(sin->sin_addr.s_addr),
ntohs(sin->sin_port));
1994-05-26 05:23:31 +00:00
printf("hostname %s %s\n", w->wd_hostname,
interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up"));
1994-05-26 05:23:31 +00:00
printf("load %4.2f, %4.2f, %4.2f\n",
ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0,
ntohl(w->wd_loadav[2]) / 100.0);
cc -= WHDRSIZE;
for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) {
time_t t = _time32_to_time(ntohl(we->we_utmp.out_time));
printf("%-8.8s %s:%s %.12s", we->we_utmp.out_name,
w->wd_hostname, we->we_utmp.out_line, ctime(&t) + 4);
1994-05-26 05:23:31 +00:00
we->we_idle = ntohl(we->we_idle) / 60;
if (we->we_idle != 0) {
if (we->we_idle >= 100 * 60)
we->we_idle = 100 * 60 - 1;
1994-05-26 05:23:31 +00:00
if (we->we_idle >= 60)
printf(" %2d", we->we_idle / 60);
else
printf(" ");
printf(":%02d", we->we_idle % 60);
}
printf("\n");
}
}
char *
interval(int time, char *updown)
1994-05-26 05:23:31 +00:00
{
static char resbuf[32];
int days, hours, minutes;
if (time < 0 || time > 3 * 30 * 24 * 60 * 60) {
1994-05-26 05:23:31 +00:00
(void) sprintf(resbuf, " %s ??:??", updown);
return (resbuf);
}
minutes = (time + 59) / 60; /* round to minutes */
hours = minutes / 60;
minutes %= 60;
days = hours / 24;
hours %= 24;
if (days > 0) {
1994-05-26 05:23:31 +00:00
(void) sprintf(resbuf, "%s %2d+%02d:%02d",
updown, days, hours, minutes);
} else {
1994-05-26 05:23:31 +00:00
(void) sprintf(resbuf, "%s %2d:%02d",
updown, hours, minutes);
}
1994-05-26 05:23:31 +00:00
return (resbuf);
}
#endif