summaryrefslogtreecommitdiffstats
path: root/usr.sbin/syslogd
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/syslogd')
-rw-r--r--usr.sbin/syslogd/syslogd.880
-rw-r--r--usr.sbin/syslogd/syslogd.c214
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 *.
*/
OpenPOWER on IntegriCloud