diff options
author | joerg <joerg@FreeBSD.org> | 1997-05-03 22:17:43 +0000 |
---|---|---|
committer | joerg <joerg@FreeBSD.org> | 1997-05-03 22:17:43 +0000 |
commit | 448c1dca859c219c3ca33a924fb16c2fe5aca01f (patch) | |
tree | e1640e6f3cc76b34949b0bc015d8d01b24cd3bcb /usr.sbin/syslogd | |
parent | ff12d1d1773aae3e2ba252b0daa09e91d35e57c1 (diff) | |
download | FreeBSD-src-448c1dca859c219c3ca33a924fb16c2fe5aca01f.zip FreeBSD-src-448c1dca859c219c3ca33a924fb16c2fe5aca01f.tar.gz |
Nobody ever seemed to be interested in reviewing these changes, and i
found that my syslogd is now running them for several months...
Add an option to syslogd to restrict the IP addresses that are allowed
to log to this syslogd. It's too late to develop the inter-syslogd
communications protocol mentioned in the BUGS section, some 10 years
too late. Thus, restricting the IP address range is about the most
effective change we can do if we want to allow incoming syslog
messages at all.
IMHO, we should encourage the system administrators to use this option,
and thus provide a knob in /etc/rc.* for it, defaulting to -a 127.0.0.1/32
(just as a hint about the usage).
Please state opinions about whether to merge this change into 2.2 or
not (i've got it running on RELENG_2_2 anyway).
Diffstat (limited to 'usr.sbin/syslogd')
-rw-r--r-- | usr.sbin/syslogd/syslogd.8 | 80 | ||||
-rw-r--r-- | usr.sbin/syslogd/syslogd.c | 214 |
2 files changed, 284 insertions, 10 deletions
diff --git a/usr.sbin/syslogd/syslogd.8 b/usr.sbin/syslogd/syslogd.8 index 3efab7f..12bcb11 100644 --- a/usr.sbin/syslogd/syslogd.8 +++ b/usr.sbin/syslogd/syslogd.8 @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)syslogd.8 8.1 (Berkeley) 6/6/93 -.\" $Id: syslogd.8,v 1.9 1997/04/26 00:00:32 pst Exp $ +.\" $Id: syslogd.8,v 1.10 1997/04/29 09:09:40 jmg Exp $ .\" .Dd October 12, 1995 .Dt SYSLOGD 8 @@ -40,10 +40,11 @@ .Nd log systems messages .Sh SYNOPSIS .Nm -.Op Fl \&dI +.Op Fl a Ar allowed_peer .Op Fl f Ar config_file .Op Fl m Ar mark_interval .Op Fl p Ar log_socket +.Op Fl s .Sh DESCRIPTION The .Nm @@ -51,6 +52,55 @@ daemon reads and logs messages to the system console, log files, other machines and/or users as specified by its configuration file. The options are as follows: .Bl -tag -width Ds +.It Fl a Ar allowed_peer +Allow +.Ar allowed_peer +to log to this +.Nm syslogd +using UDP datagrams. Multiple +.Fl a +options may be specified. +.Pp +.Ar Allowed_peer +can be any of the following: +.Bl -tag -width "ipaddr/masklen[:service]XX" +.It Ar ipaddr/masklen Ns Op Ar :service +Accept datagrams from +.Ar ipaddr +(in the usual dotted quad notation) with +.Ar masklen +bits being taken into account when doing the address comparision. If +specified, +.Ar service +is the name or number of an UDP service (see +.Xr services 5 ) Ns +the source packet must belong to. A +.Ar service +of +.Ql \&* +allows packets being sent from any UDP port. The default +.Ar service +is +.Ql syslog . +A missing +.Ar masklen +will be substituted by the historic class A or class B netmasks if +.Ar ipaddr +belongs into the address range of class A or B, respectively, or +by 24 otherwise. +.It Ar domainname Ns Op Ar :service +Accept datagrams where the reverse address lookup yields +.Ar domainname +for the sender address. The meaning of +.Ar service +is as explained above. +.It Ar *domainname Ns Op Ar :service +Same as before, except that any source host whose name +.Em ends +in +.Ar domainname +will get permission. +.El .It Fl d Put .Nm @@ -60,8 +110,6 @@ into debugging mode. This is probably only of use to developers working on Specify the pathname of an alternate configuration file; the default is .Pa /etc/syslog.conf . -.It Fl s -Operate in secure mode. Do not listen for log message from remote machines. .It Fl m Select the number of minutes between .Dq mark @@ -70,6 +118,8 @@ messages; the default is 20 minutes. Specify the pathname of an alternate log socket; the default is .Pa /var/run/log . +.It Fl s +Operate in secure mode. Do not listen for log message from remote machines. .El .Pp The @@ -134,12 +184,32 @@ The .Nm command appeared in .Bx 4.3 . +.Pp +The +.Fl s +and +.Fl a +options are +.Fx 2.2 +extensions. .Sh BUGS The ability to log messages received in UDP packets is equivalent to an unauthenticated remote disk-filling service, and should probably be disabled by default. Some sort of .No inter- Ns Nm syslogd -authentication mechanism ought to be worked out. +authentication mechanism ought to be worked out. To prevent the worst +abuse, use of the +.Fl a +option is therefore highly recommended. +.Pp +The +.Fl a +matching algorithm doesn't pretend to be very efficient; use of numeric +IP addresses is faster than domain name comparision. Since the allowed +peer list is being walked linearly, peer groups where frequent messages +are being anticipated from should be put early into the +.Fl a +list. .Pp The log socket was moved from .Pa /dev diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c index 998988b..58dfc82 100644 --- a/usr.sbin/syslogd/syslogd.c +++ b/usr.sbin/syslogd/syslogd.c @@ -39,7 +39,7 @@ static const char copyright[] = static char sccsid[] = "@(#)syslogd.c 8.3 (Berkeley) 4/4/94"; */ static const char rcsid[] = - "$Id: syslogd.c,v 1.23 1997/04/26 00:00:33 pst Exp $"; + "$Id: syslogd.c,v 1.24 1997/04/26 00:03:21 pst Exp $"; #endif /* not lint */ /* @@ -94,11 +94,13 @@ static const char rcsid[] = #include <ctype.h> #include <errno.h> #include <fcntl.h> +#include <regex.h> #include <setjmp.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sysexits.h> #include <unistd.h> #include <utmp.h> #include "pathnames.h" @@ -184,6 +186,26 @@ typedef struct deadq_entry *dq_t; /* + * Struct to hold records of network addresses that are allowed to log + * to us. + */ +struct allowedpeer { + int isnumeric; + u_short port; + union { + struct { + struct in_addr addr; + struct in_addr mask; + } numeric; + char *name; + } u; +#define a_addr u.numeric.addr +#define a_mask u.numeric.mask +#define a_name u.name +}; + + +/* * Intervals at which we flush out "message repeated" messages, * in seconds after previous message is logged. After each flush, * we move to the next interval until we reach the largest. @@ -226,6 +248,10 @@ int SecureMode = 0; /* when true, speak only unix domain socks */ int created_lsock = 0; /* Flag if local socket created */ char bootfile[MAXLINE+1]; /* booted kernel file */ +struct allowedpeer *AllowedPeers; +int NumAllowed = 0; /* # of AllowedPeer entries */ + +int allowaddr __P((char *)); void cfline __P((char *, struct filed *, char *)); char *cvthname __P((struct sockaddr_in *)); void deadq_enter __P((pid_t)); @@ -242,6 +268,7 @@ int p_open __P((char *, pid_t *)); void reapchild __P((int)); char *ttymsg __P((struct iovec *, int, char *, int)); void usage __P((void)); +int validate __P((struct sockaddr_in *, const char *)); void wallmsg __P((struct filed *, struct iovec *)); int waitdaemon __P((int, int, int)); void timedout __P((int)); @@ -255,15 +282,19 @@ main(argc, argv) struct sockaddr_un sunx, fromunix; struct sockaddr_in sin, frominet; FILE *fp; - char *p, line[MSG_BSIZE + 1]; + char *p, *hname, line[MSG_BSIZE + 1]; struct timeval tv, *tvp; pid_t ppid; - while ((ch = getopt(argc, argv, "dsf:m:p:")) != -1) + while ((ch = getopt(argc, argv, "a:dsf:m:p:")) != -1) switch(ch) { case 'd': /* debug */ Debug++; break; + case 'a': /* allow specific network addresses only */ + if (allowaddr(optarg) == -1) + usage(); + break; case 'f': /* configuration file */ ConfFile = optarg; break; @@ -290,6 +321,9 @@ main(argc, argv) } else setlinebuf(stdout); + if (NumAllowed) + endservent(); + consfile.f_type = F_CONSOLE; (void)strcpy(consfile.f_un.f_fname, ctty + sizeof _PATH_DEV - 1); (void)gethostname(LocalHostName, sizeof(LocalHostName)); @@ -421,7 +455,9 @@ main(argc, argv) (struct sockaddr *)&frominet, &len); if (i > 0) { line[i] = '\0'; - printline(cvthname(&frominet), line); + hname = cvthname(&frominet); + if (validate(&frominet, hname)) + printline(hname, line); } else if (i < 0 && errno != EINTR) logerror("recvfrom inet"); } @@ -434,7 +470,7 @@ usage() fprintf(stderr, "usage: syslogd [-ds] [-f conffile] [-m markinterval]" - " [-p logpath]\n"); + " [-p logpath] [-a allowaddr]\n"); exit(1); } @@ -1442,6 +1478,174 @@ timedout(sig) } /* + * Add `s' to the list of allowable peer addresses to accept messages + * from. + * + * `s' is a string in the form: + * + * [*]domainname[:{servicename|portnumber|*}] + * + * or + * + * netaddr/maskbits[:{servicename|portnumber|*}] + * + * Returns -1 on error, 0 if the argument was valid. + */ +int +allowaddr(s) + char *s; +{ + char *cp1, *cp2; + struct allowedpeer ap; + struct servent *se; + regex_t re; + int i; + + if ((cp1 = strrchr(s, ':'))) { + /* service/port provided */ + *cp1++ = '\0'; + if (strlen(cp1) == 1 && *cp1 == '*') + /* any port allowed */ + ap.port = htons(0); + else if ((se = getservbyname(cp1, "udp"))) + ap.port = se->s_port; + else { + ap.port = htons((int)strtol(cp1, &cp2, 0)); + if (*cp2 != '\0') + return -1; /* port not numeric */ + } + } else { + if ((se = getservbyname("syslog", "udp"))) + ap.port = se->s_port; + else + /* sanity, should not happen */ + ap.port = htons(514); + } + + /* the regexp's are ugly, but the cleanest way */ + + if (regcomp(&re, "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+(/[0-9]+)?$", + REG_EXTENDED)) + /* if RE compilation fails, that's an internal error */ + abort(); + if (regexec(&re, s, 0, 0, 0) == 0) { + /* arg `s' is numeric */ + ap.isnumeric = 1; + if ((cp1 = strchr(s, '/')) != NULL) { + *cp1++ = '\0'; + i = atoi(cp1); + if (i < 0 || i > 32) + return -1; + /* convert masklen to netmask */ + ap.a_mask.s_addr = htonl(~((1 << (32 - i)) - 1)); + } + if (ascii2addr(AF_INET, s, &ap.a_addr) == -1) + return -1; + if (cp1 == NULL) { + /* use default netmask */ + if (IN_CLASSA(ntohl(ap.a_addr.s_addr))) + ap.a_mask.s_addr = htonl(IN_CLASSA_NET); + else if (IN_CLASSB(ntohl(ap.a_addr.s_addr))) + ap.a_mask.s_addr = htonl(IN_CLASSB_NET); + else + ap.a_mask.s_addr = htonl(IN_CLASSC_NET); + } + } else { + /* arg `s' is domain name */ + ap.isnumeric = 0; + ap.a_name = s; + } + regfree(&re); + + if (Debug) { + printf("allowaddr: rule %d: ", NumAllowed); + if (ap.isnumeric) { + printf("numeric, "); + printf("addr = %s, ", + addr2ascii(AF_INET, &ap.a_addr, sizeof(struct in_addr), 0)); + printf("mask = %s; ", + addr2ascii(AF_INET, &ap.a_mask, sizeof(struct in_addr), 0)); + } else + printf("domainname = %s; ", ap.a_name); + printf("port = %d\n", ntohs(ap.port)); + } + + if ((AllowedPeers = realloc(AllowedPeers, + ++NumAllowed * sizeof(struct allowedpeer))) + == NULL) { + fprintf(stderr, "Out of memory!\n"); + exit(EX_OSERR); + } + memcpy(&AllowedPeers[NumAllowed - 1], &ap, sizeof(struct allowedpeer)); + return 0; +} + +/* + * Validate that the remote peer has permission to log to us. + */ +int +validate(sin, hname) + struct sockaddr_in *sin; + const char *hname; +{ + int i; + size_t l1, l2; + char *cp, name[MAXHOSTNAMELEN]; + struct allowedpeer *ap; + + if (NumAllowed == 0) + /* traditional behaviour, allow everything */ + return 1; + + strncpy(name, hname, sizeof name); + if (strchr(name, '.') == NULL) { + strncat(name, ".", sizeof name - strlen(name) - 1); + strncat(name, LocalDomain, sizeof name - strlen(name) - 1); + } + dprintf("validate: dgram from IP %s, port %d, name %s;\n", + addr2ascii(AF_INET, &sin->sin_addr, sizeof(struct in_addr), 0), + ntohs(sin->sin_port), name); + + /* now, walk down the list */ + for (i = 0, ap = AllowedPeers; i < NumAllowed; i++, ap++) { + if (ntohs(ap->port) != 0 && ap->port != sin->sin_port) { + dprintf("rejected in rule %d due to port mismatch.\n", i); + continue; + } + + if (ap->isnumeric) { + if ((sin->sin_addr.s_addr & ap->a_mask.s_addr) + != ap->a_addr.s_addr) { + dprintf("rejected in rule %d due to IP mismatch.\n", i); + continue; + } + } else { + cp = ap->a_name; + l1 = strlen(name); + if (*cp == '*') { + /* allow wildmatch */ + cp++; + l2 = strlen(cp); + if (l2 > l1 || memcmp(cp, &name[l1 - l2], l2) != 0) { + dprintf("rejected in rule %d due to name mismatch.\n", i); + continue; + } + } else { + /* exact match */ + l2 = strlen(cp); + if (l2 != l1 || memcmp(cp, name, l1) != 0) { + dprintf("rejected in rule %d due to name mismatch.\n", i); + continue; + } + } + } + dprintf("accepted in rule %d.\n", i); + return 1; /* hooray! */ + } + return 0; +} + +/* * Fairly similar to popen(3), but returns an open descriptor, as * opposed to a FILE *. */ |