1168404Spjd/*-
2168404Spjd * SPDX-License-Identifier: BSD-3-Clause
3168404Spjd *
4168404Spjd * Copyright (c) 1983, 1993
5168404Spjd *	The Regents of the University of California.  All rights reserved.
6168404Spjd *
7168404Spjd * Redistribution and use in source and binary forms, with or without
8168404Spjd * modification, are permitted provided that the following conditions
9168404Spjd * are met:
10168404Spjd * 1. Redistributions of source code must retain the above copyright
11168404Spjd *    notice, this list of conditions and the following disclaimer.
12168404Spjd * 2. Redistributions in binary form must reproduce the above copyright
13168404Spjd *    notice, this list of conditions and the following disclaimer in the
14168404Spjd *    documentation and/or other materials provided with the distribution.
15168404Spjd * 3. Neither the name of the University nor the names of its contributors
16168404Spjd *    may be used to endorse or promote products derived from this software
17168404Spjd *    without specific prior written permission.
18168404Spjd *
19168404Spjd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20168404Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21168404Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22219089Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23307287Smav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24288549Smav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25297112Smav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26168404Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27168404Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28219089Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29168404Spjd * SUCH DAMAGE.
30168404Spjd */
31168404Spjd
32168404Spjd#include <sys/capsicum.h>
33168404Spjd#include <sys/param.h>
34168404Spjd#include <sys/socket.h>
35168404Spjd#include <netinet/in.h>
36168404Spjd
37219089Spjd#include <capsicum_helpers.h>
38276081Sdelphij#include <ctype.h>
39168404Spjd#include <err.h>
40185029Spjd#include <netdb.h>
41185029Spjd#include <stdio.h>
42185029Spjd#include <stdlib.h>
43168404Spjd#include <string.h>
44277585Sdelphij#include <time.h>
45277585Sdelphij#include <unistd.h>
46307287Smav
47307287Smav#include <libcasper.h>
48168404Spjd#include <casper/cap_syslog.h>
49219089Spjd
50219089Spjd#define	SYSLOG_NAMES
51219089Spjd#include <syslog.h>
52219089Spjd
53219089Spjd#define	sstosa(ss)	((struct sockaddr *)(void *)ss)
54277585Sdelphij
55219089Spjdstruct socks {
56168404Spjd	int sk_sock;
57219089Spjd	int sk_addrlen;
58219089Spjd	struct sockaddr_storage sk_addr;
59219089Spjd};
60219089Spjd
61219089Spjdstatic int	decode(char *, const CODE *);
62219089Spjdstatic int	pencode(char *);
63219089Spjdstatic ssize_t	socksetup(const char *, const char *, const char *,
64219089Spjd		    struct socks **);
65219089Spjdstatic void	logmessage(int, const char *, const char *, const char *,
66219089Spjd		    struct socks *, ssize_t, const char *);
67219089Spjdstatic void	usage(void);
68219089Spjd
69219089Spjdstatic cap_channel_t *capsyslog;
70219089Spjd#ifdef INET6
71219089Spjdstatic int family = PF_UNSPEC;	/* protocol family (IPv4, IPv6 or both) */
72219089Spjd#else
73219089Spjdstatic int family = PF_INET;	/* protocol family (IPv4 only) */
74219089Spjd#endif
75185029Spjdstatic int send_to_all = 0;	/* send message to all IPv4/IPv6 addresses */
76219089Spjd
77185029Spjd/*
78219089Spjd * logger -- read and log utility
79219089Spjd *
80185029Spjd *	Reads from an input and arranges to write the result on the system
81219089Spjd *	log.
82219089Spjd */
83219089Spjdint
84219089Spjdmain(int argc, char *argv[])
85219089Spjd{
86219089Spjd	cap_channel_t *capcas;
87219089Spjd	struct socks *socks;
88185029Spjd	ssize_t nsock;
89219089Spjd	time_t now;
90219089Spjd	int ch, logflags, pri;
91219089Spjd	char *tag, *host, buf[1024], *timestamp, tbuf[26],
92219089Spjd	    *hostname, hbuf[MAXHOSTNAMELEN], *pristr;
93219089Spjd	const char *svcname, *src;
94219089Spjd
95219089Spjd	tag = NULL;
96219089Spjd	host = NULL;
97219089Spjd	hostname = NULL;
98219089Spjd	svcname = "syslog";
99219089Spjd	src = NULL;
100219089Spjd	socks = NULL;
101219089Spjd	pri = LOG_USER | LOG_NOTICE;
102219089Spjd	pristr = NULL;
103219089Spjd	logflags = 0;
104219089Spjd	unsetenv("TZ");
105219089Spjd	while ((ch = getopt(argc, argv, "46Af:H:h:iP:p:S:st:")) != -1)
106219089Spjd		switch((char)ch) {
107219089Spjd		case '4':
108219089Spjd			family = PF_INET;
109219089Spjd			break;
110219089Spjd#ifdef INET6
111219089Spjd		case '6':
112219089Spjd			family = PF_INET6;
113219089Spjd			break;
114219089Spjd#endif
115219089Spjd		case 'A':
116219089Spjd			send_to_all++;
117219089Spjd			break;
118219089Spjd		case 'f':		/* file to log */
119219089Spjd			if (freopen(optarg, "r", stdin) == NULL)
120219089Spjd				err(1, "%s", optarg);
121219089Spjd			setvbuf(stdin, 0, _IONBF, 0);
122219089Spjd			break;
123185029Spjd		case 'H':		/* hostname to set in message header */
124185029Spjd			hostname = optarg;
125219089Spjd			break;
126219089Spjd		case 'h':		/* hostname to deliver to */
127219089Spjd			host = optarg;
128185029Spjd			break;
129185029Spjd		case 'i':		/* log process id also */
130219089Spjd			logflags |= LOG_PID;
131185029Spjd			break;
132219089Spjd		case 'P':		/* service name or port number */
133185029Spjd			svcname = optarg;
134185029Spjd			break;
135185029Spjd		case 'p':		/* priority */
136185029Spjd			pristr = optarg;
137185029Spjd			break;
138185029Spjd		case 's':		/* log to standard error */
139185029Spjd			logflags |= LOG_PERROR;
140185029Spjd			break;
141219089Spjd		case 'S':		/* source address */
142219089Spjd			src = optarg;
143185029Spjd			break;
144185029Spjd		case 't':		/* tag */
145185029Spjd			tag = optarg;
146185029Spjd			break;
147185029Spjd		case '?':
148219089Spjd		default:
149219089Spjd			usage();
150185029Spjd		}
151185029Spjd	argc -= optind;
152185029Spjd	argv += optind;
153185029Spjd
154185029Spjd	if (host) {
155185029Spjd		nsock = socksetup(src, host, svcname, &socks);
156185029Spjd		if (nsock <= 0)
157219089Spjd			errx(1, "socket");
158219089Spjd	} else {
159185029Spjd		if (src)
160185029Spjd			errx(1, "-h option is missing.");
161185029Spjd		nsock = 0;
162185029Spjd	}
163185029Spjd
164185029Spjd	capcas = cap_init();
165219089Spjd	if (capcas == NULL)
166185029Spjd		err(1, "Unable to contact Casper");
167185029Spjd	caph_cache_catpages();
168219089Spjd	caph_cache_tzdata();
169185029Spjd	if (nsock == 0) {
170185029Spjd		if (caph_enter_casper() < 0)
171185029Spjd			err(1, "Unable to enter capability mode");
172185029Spjd	}
173185029Spjd	capsyslog = cap_service_open(capcas, "system.syslog");
174185029Spjd	if (capsyslog == NULL)
175185029Spjd		err(1, "Unable to open system.syslog service");
176185029Spjd	cap_close(capcas);
177185029Spjd
178185029Spjd	if (pristr != NULL)
179219089Spjd		pri = pencode(pristr);
180185029Spjd	if (tag == NULL)
181185029Spjd		tag = getlogin();
182185029Spjd	/* setup for logging */
183185029Spjd	if (host == NULL)
184219089Spjd		cap_openlog(capsyslog, tag, logflags, 0);
185219089Spjd
186219089Spjd	if (hostname == NULL) {
187185029Spjd		hostname = hbuf;
188185029Spjd		(void )gethostname(hbuf, MAXHOSTNAMELEN);
189219089Spjd		*strchrnul(hostname, '.') = '\0';
190185029Spjd	}
191185029Spjd
192185029Spjd	timestamp = tbuf + 4;
193219089Spjd
194219089Spjd	/* log input line if appropriate */
195185029Spjd	if (argc > 0) {
196185029Spjd		char *p, *endp;
197185029Spjd		size_t len;
198185029Spjd
199185029Spjd		(void )time(&now);
200219089Spjd		(void )ctime_r(&now, tbuf);
201219089Spjd		tbuf[19] = '\0';
202185029Spjd
203185029Spjd		for (p = buf, endp = buf + sizeof(buf) - 2; *argv;) {
204219089Spjd			len = strlen(*argv);
205185029Spjd			if (p + len > endp && p > buf) {
206185029Spjd				logmessage(pri, timestamp, hostname, tag,
207185029Spjd				    socks, nsock, buf);
208219089Spjd				p = buf;
209219089Spjd			}
210219089Spjd			if (len > sizeof(buf) - 1)
211219089Spjd				logmessage(pri, timestamp, hostname, tag,
212219089Spjd				    socks, nsock, *argv++);
213219089Spjd			else {
214219089Spjd				if (p != buf)
215219089Spjd					*p++ = ' ';
216219089Spjd				bcopy(*argv++, p, len);
217219089Spjd				*(p += len) = '\0';
218219089Spjd			}
219219089Spjd		}
220219089Spjd		if (p != buf)
221219089Spjd			logmessage(pri, timestamp, hostname, tag, socks, nsock,
222219089Spjd			    buf);
223219089Spjd	} else
224168404Spjd		while (fgets(buf, sizeof(buf), stdin) != NULL) {
225168404Spjd			(void )time(&now);
226168404Spjd			(void )ctime_r(&now, tbuf);
227168404Spjd			tbuf[19] = '\0';
228168404Spjd
229168404Spjd			logmessage(pri, timestamp, hostname, tag, socks, nsock,
230185029Spjd			    buf);
231168404Spjd		}
232168404Spjd	exit(0);
233168404Spjd}
234168404Spjd
235168404Spjdstatic ssize_t
236168404Spjdsocksetup(const char *src, const char *dst, const char *svcname,
237168404Spjd	struct socks **socks)
238168404Spjd{
239168404Spjd	struct addrinfo hints, *res, *res0;
240168404Spjd	struct sockaddr_storage *ss_src[AF_MAX];
241168404Spjd	struct socks *sk;
242168404Spjd	ssize_t nsock = 0;
243168404Spjd	int error, maxs;
244168404Spjd
245168404Spjd	memset(&ss_src[0], 0, sizeof(ss_src));
246168404Spjd	if (src) {
247168404Spjd		char *p, *p0, *hs, *hbuf, *sbuf;
248168404Spjd
249168404Spjd		hbuf = sbuf = NULL;
250168404Spjd		p0 = p = strdup(src);
251168404Spjd		if (p0 == NULL)
252168404Spjd			err(1, "strdup failed");
253168404Spjd		hs = p0;	/* point to search ":" */
254168404Spjd#ifdef INET6
255168404Spjd		/* -S option supports IPv6 addr in "[2001:db8::1]:service". */
256168404Spjd		if (*p0 == '[') {
257168404Spjd			p = strchr(p0, ']');
258168404Spjd			if (p == NULL)
259168404Spjd				errx(1, "\"]\" not found in src addr");
260168404Spjd			*p = '\0';
261168404Spjd			/* hs points just after ']' (':' or '\0'). */
262168404Spjd			hs = p + 1;
263168404Spjd			/*
264168404Spjd			 * p points just after '[' while it points hs
265219089Spjd			 * in the case of [].
266168404Spjd			 */
267219089Spjd			p = ((p0 + 1) == (hs - 1)) ? hs : p0 + 1;
268168404Spjd		}
269168404Spjd#endif
270168404Spjd		if (*p != '\0') {
271168404Spjd			/* (p == hs) means ":514" or "[]:514". */
272197150Spjd			hbuf = (p == hs && *p == ':') ? NULL : p;
273219089Spjd			p = strchr(hs, ':');
274168404Spjd			if (p != NULL) {
275168404Spjd				*p = '\0';
276197150Spjd				sbuf = (*(p + 1) != '\0') ? p + 1 : NULL;
277168404Spjd			}
278168404Spjd		}
279168404Spjd		hints = (struct addrinfo){
280168404Spjd			.ai_family = family,
281168404Spjd			.ai_socktype = SOCK_DGRAM,
282168404Spjd			.ai_flags = AI_PASSIVE
283168404Spjd		};
284219089Spjd		error = getaddrinfo(hbuf, sbuf, &hints, &res0);
285219089Spjd		if (error)
286197150Spjd			errx(1, "%s: %s", gai_strerror(error), src);
287197150Spjd		for (res = res0; res; res = res->ai_next) {
288197150Spjd			switch (res->ai_family) {
289197150Spjd			case AF_INET:
290197150Spjd#ifdef INET6
291197150Spjd			case AF_INET6:
292168404Spjd#endif
293168404Spjd				if (ss_src[res->ai_family] != NULL)
294168404Spjd					continue;
295185029Spjd				ss_src[res->ai_family] =
296168404Spjd				    malloc(sizeof(struct sockaddr_storage));
297168404Spjd				if (ss_src[res->ai_family] == NULL)
298168404Spjd					err(1, "malloc failed");
299168404Spjd				memcpy(ss_src[res->ai_family], res->ai_addr,
300185029Spjd				    res->ai_addrlen);
301168404Spjd			}
302185029Spjd		}
303185029Spjd		freeaddrinfo(res0);
304168404Spjd		free(p0);
305185029Spjd	}
306219089Spjd
307168404Spjd	/* resolve hostname */
308185029Spjd	hints = (struct addrinfo){
309168404Spjd		.ai_family = family,
310168404Spjd		.ai_socktype = SOCK_DGRAM
311168404Spjd	};
312185029Spjd	error = getaddrinfo(dst, svcname, &hints, &res0);
313219089Spjd	if (error == EAI_SERVICE) {
314219089Spjd		warnx("%s/udp: unknown service", svcname);
315168404Spjd		error = getaddrinfo(dst, "514", &hints, &res0);
316168404Spjd	}
317185029Spjd	if (error)
318185029Spjd		errx(1, "%s: %s", gai_strerror(error), dst);
319185029Spjd	/* count max number of sockets we may open */
320185029Spjd	maxs = 0;
321168404Spjd	for (res = res0; res; res = res->ai_next)
322168404Spjd		maxs++;
323168404Spjd	sk = calloc(maxs, sizeof(*sk));
324168404Spjd	if (sk == NULL)
325168404Spjd		errx(1, "couldn't allocate memory for sockets");
326168404Spjd	for (res = res0; res; res = res->ai_next) {
327168404Spjd		int s;
328168404Spjd
329168404Spjd		s = socket(res->ai_family, res->ai_socktype,
330168404Spjd		    res->ai_protocol);
331168404Spjd		if (s < 0)
332168404Spjd			continue;
333168404Spjd		if (src && ss_src[res->ai_family] == NULL)
334168404Spjd			errx(1, "address family mismatch");
335168404Spjd
336168404Spjd		if (ss_src[res->ai_family]) {
337219089Spjd			error = bind(s, sstosa(ss_src[res->ai_family]),
338168404Spjd				    ss_src[res->ai_family]->ss_len);
339168404Spjd			if (error < 0)
340168404Spjd				err(1, "bind");
341168404Spjd		}
342219089Spjd		sk[nsock] = (struct socks){
343168404Spjd			.sk_addrlen = res->ai_addrlen,
344168404Spjd			.sk_sock = s
345168404Spjd		};
346168404Spjd		memcpy(&sk[nsock].sk_addr, res->ai_addr, res->ai_addrlen);
347168404Spjd		nsock++;
348168404Spjd	}
349168404Spjd	freeaddrinfo(res0);
350168404Spjd
351168404Spjd	*socks = sk;
352168404Spjd	return (nsock);
353168404Spjd}
354168404Spjd
355168404Spjd/*
356168404Spjd *  Send the message to syslog, either on the local host, or on a remote host
357168404Spjd */
358168404Spjdstatic void
359168404Spjdlogmessage(int pri, const char *timestamp, const char *hostname,
360168404Spjd    const char *tag, struct socks *sk, ssize_t nsock, const char *buf)
361168404Spjd{
362168404Spjd	char *line;
363168404Spjd	int len, i, lsent;
364168404Spjd
365168404Spjd	if (nsock == 0) {
366168404Spjd		cap_syslog(capsyslog, pri, "%s", buf);
367168404Spjd		return;
368168404Spjd	}
369168404Spjd	if ((len = asprintf(&line, "<%d>%s %s %s: %s", pri, timestamp,
370168404Spjd	    hostname, tag, buf)) == -1)
371168404Spjd		errx(1, "asprintf");
372168404Spjd
373168404Spjd	lsent = -1;
374168404Spjd	for (i = 0; i < nsock; i++) {
375168404Spjd		lsent = sendto(sk[i].sk_sock, line, len, 0,
376168404Spjd			       sstosa(&sk[i].sk_addr), sk[i].sk_addrlen);
377168404Spjd		if (lsent == len && !send_to_all)
378168404Spjd			break;
379168404Spjd	}
380168404Spjd	if (lsent != len) {
381219089Spjd		if (lsent == -1)
382168404Spjd			warn("sendto");
383168404Spjd		else
384168404Spjd			warnx("sendto: short send - %d bytes", lsent);
385168404Spjd	}
386168404Spjd
387185029Spjd	free(line);
388219089Spjd}
389265740Sdelphij
390168404Spjd/*
391168404Spjd *  Decode a symbolic name to a numeric value
392168404Spjd */
393168404Spjdstatic int
394168404Spjdpencode(char *s)
395168404Spjd{
396168404Spjd	char *save;
397168404Spjd	int fac, lev;
398168404Spjd
399288549Smav	for (save = s; *s && *s != '.'; ++s);
400288549Smav	if (*s) {
401168404Spjd		*s = '\0';
402168404Spjd		fac = decode(save, facilitynames);
403172443Spjd		if (fac < 0)
404172443Spjd			errx(1, "unknown facility name: %s", save);
405168404Spjd		*s++ = '.';
406168404Spjd	}
407168404Spjd	else {
408168404Spjd		fac = 0;
409168404Spjd		s = save;
410168404Spjd	}
411168404Spjd	lev = decode(s, prioritynames);
412277585Sdelphij	if (lev < 0)
413277585Sdelphij		errx(1, "unknown priority name: %s", save);
414168404Spjd	return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
415168404Spjd}
416168404Spjd
417168404Spjdstatic int
418168404Spjddecode(char *name, const CODE *codetab)
419168404Spjd{
420277585Sdelphij	const CODE *c;
421168404Spjd
422185029Spjd	if (isdigit(*name))
423185029Spjd		return (atoi(name));
424185029Spjd
425185029Spjd	for (c = codetab; c->c_name; c++)
426219089Spjd		if (!strcasecmp(name, c->c_name))
427197150Spjd			return (c->c_val);
428197150Spjd
429197150Spjd	return (-1);
430197172Spjd}
431197150Spjd
432197150Spjdstatic void
433185029Spjdusage(void)
434168404Spjd{
435168404Spjd	(void)fprintf(stderr, "usage: %s\n",
436168404Spjd	    "logger [-46Ais] [-f file] [-h host] [-P port] [-p pri] [-t tag]\n"
437277585Sdelphij	    "              [-S addr:port] [message ...]"
438277585Sdelphij	    );
439168404Spjd	exit(1);
440168404Spjd}
441168404Spjd