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