aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/syslogd/syslogd.c
diff options
context:
space:
mode:
authorJoerg Wunsch <joerg@FreeBSD.org>1997-05-03 22:17:43 +0000
committerJoerg Wunsch <joerg@FreeBSD.org>1997-05-03 22:17:43 +0000
commit3548606371751ea880f43afc34709b7695c16baf (patch)
tree5fadf5aefd8cf3c1bbe3103960eeb2fdc1b0a1a4 /usr.sbin/syslogd/syslogd.c
parentce34628b4b0387ae060e02684d6d178e75683f83 (diff)
downloadsrc-3548606371751ea880f43afc34709b7695c16baf.tar.gz
src-3548606371751ea880f43afc34709b7695c16baf.zip
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).
Notes
Notes: svn path=/head/; revision=25437
Diffstat (limited to 'usr.sbin/syslogd/syslogd.c')
-rw-r--r--usr.sbin/syslogd/syslogd.c214
1 files changed, 209 insertions, 5 deletions
diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c
index 998988b69a6a..58dfc822bd77 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 *.
*/