599 lines
11 KiB
C
599 lines
11 KiB
C
/*
|
|
* Copyright (C) 1993-2001 by Darren Reed.
|
|
*
|
|
* See the IPFILTER.LICENCE file for details on licencing.
|
|
*/
|
|
#include <sys/types.h>
|
|
#if !defined(__SVR4) && !defined(__svr4__)
|
|
#include <strings.h>
|
|
#else
|
|
#include <sys/byteorder.h>
|
|
#endif
|
|
#include <sys/param.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/tcp.h>
|
|
#include <net/if.h>
|
|
#if __FreeBSD_version >= 300000
|
|
# include <net/if_var.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <stddef.h>
|
|
#include <netdb.h>
|
|
#include <arpa/nameser.h>
|
|
#include <arpa/inet.h>
|
|
#include <resolv.h>
|
|
#include <ctype.h>
|
|
#include <syslog.h>
|
|
#include "ip_compat.h"
|
|
#include "ip_fil.h"
|
|
#include "ipf.h"
|
|
#include "facpri.h"
|
|
|
|
#if !defined(lint)
|
|
static const char sccsid[] = "@(#)parse.c 1.44 6/5/96 (C) 1993-2000 Darren Reed";
|
|
static const char rcsid[] = "@(#)$IPFilter: parse.c,v 2.8 1999/12/28 10:49:46 darrenr Exp $";
|
|
#endif
|
|
|
|
extern struct ipopt_names ionames[], secclass[];
|
|
extern int opts;
|
|
#ifdef USE_INET6
|
|
extern int use_inet6;
|
|
#endif
|
|
|
|
|
|
char *proto = NULL;
|
|
char flagset[] = "FSRPAUEC";
|
|
u_char flags[] = { TH_FIN, TH_SYN, TH_RST, TH_PUSH, TH_ACK, TH_URG,
|
|
TH_ECN, TH_CWR };
|
|
|
|
#ifdef USE_INET6
|
|
void fill6bits __P((int, u_32_t *));
|
|
int count6bits __P((u_32_t *));
|
|
#endif
|
|
|
|
static char thishost[MAXHOSTNAMELEN];
|
|
|
|
|
|
void initparse()
|
|
{
|
|
gethostname(thishost, sizeof(thishost));
|
|
thishost[sizeof(thishost) - 1] = '\0';
|
|
}
|
|
|
|
|
|
int genmask(msk, mskp)
|
|
char *msk;
|
|
u_32_t *mskp;
|
|
{
|
|
char *endptr = NULL;
|
|
#ifdef USE_INET6
|
|
u_32_t addr;
|
|
#endif
|
|
int bits;
|
|
|
|
if (index(msk, '.') || index(msk, 'x') || index(msk, ':')) {
|
|
/* possibly of the form xxx.xxx.xxx.xxx
|
|
* or 0xYYYYYYYY */
|
|
#ifdef USE_INET6
|
|
if (use_inet6) {
|
|
if (inet_pton(AF_INET6, msk, &addr) != 1)
|
|
return -1;
|
|
} else
|
|
#endif
|
|
if (inet_aton(msk, (struct in_addr *)mskp) == 0)
|
|
return -1;
|
|
} else {
|
|
/*
|
|
* set x most significant bits
|
|
*/
|
|
bits = (int)strtol(msk, &endptr, 0);
|
|
#ifdef USE_INET6
|
|
if ((*endptr != '\0') ||
|
|
((bits > 32) && !use_inet6) || (bits < 0) ||
|
|
((bits > 128) && use_inet6))
|
|
#else
|
|
if (*endptr != '\0' || bits > 32 || bits < 0)
|
|
#endif
|
|
return -1;
|
|
#ifdef USE_INET6
|
|
if (use_inet6)
|
|
fill6bits(bits, mskp);
|
|
else
|
|
#endif
|
|
if (bits == 0)
|
|
*mskp = 0;
|
|
else
|
|
*mskp = htonl(0xffffffff << (32 - bits));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_INET6
|
|
void fill6bits(bits, msk)
|
|
int bits;
|
|
u_32_t *msk;
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; bits >= 32 && i < 4 ; ++i, bits -= 32)
|
|
msk[i] = 0xffffffff;
|
|
|
|
if (bits > 0 && i < 4)
|
|
msk[i++] = htonl(0xffffffff << (32 - bits));
|
|
|
|
while (i < 4)
|
|
msk[i++] = 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* returns -1 if neither "hostmask/num" or "hostmask mask addr" are
|
|
* found in the line segments, there is an error processing this information,
|
|
* or there is an error processing ports information.
|
|
*/
|
|
int hostmask(seg, sa, msk, pp, cp, tp, linenum)
|
|
char ***seg;
|
|
u_32_t *sa, *msk;
|
|
u_short *pp, *tp;
|
|
int *cp;
|
|
int linenum;
|
|
{
|
|
struct in_addr maskaddr;
|
|
char *s;
|
|
|
|
/*
|
|
* is it possibly hostname/num ?
|
|
*/
|
|
if ((s = index(**seg, '/')) ||
|
|
((s = index(**seg, ':')) && !index(s + 1, ':'))) {
|
|
*s++ = '\0';
|
|
if (genmask(s, msk) == -1) {
|
|
fprintf(stderr, "%d: bad mask (%s)\n", linenum, s);
|
|
return -1;
|
|
}
|
|
if (hostnum(sa, **seg, linenum) == -1) {
|
|
fprintf(stderr, "%d: bad host (%s)\n", linenum, **seg);
|
|
return -1;
|
|
}
|
|
*sa &= *msk;
|
|
(*seg)++;
|
|
return ports(seg, pp, cp, tp, linenum);
|
|
}
|
|
|
|
/*
|
|
* look for extra segments if "mask" found in right spot
|
|
*/
|
|
if (*(*seg+1) && *(*seg+2) && !strcasecmp(*(*seg+1), "mask")) {
|
|
if (hostnum(sa, **seg, linenum) == -1) {
|
|
fprintf(stderr, "%d: bad host (%s)\n", linenum, **seg);
|
|
return -1;
|
|
}
|
|
(*seg)++;
|
|
(*seg)++;
|
|
if (inet_aton(**seg, &maskaddr) == 0) {
|
|
fprintf(stderr, "%d: bad mask (%s)\n", linenum, **seg);
|
|
return -1;
|
|
}
|
|
*msk = maskaddr.s_addr;
|
|
(*seg)++;
|
|
*sa &= *msk;
|
|
return ports(seg, pp, cp, tp, linenum);
|
|
}
|
|
|
|
if (**seg) {
|
|
if (hostnum(sa, **seg, linenum) == -1) {
|
|
fprintf(stderr, "%d: bad host (%s)\n", linenum, **seg);
|
|
return -1;
|
|
}
|
|
(*seg)++;
|
|
#ifdef USE_INET6
|
|
if (use_inet6) {
|
|
u_32_t k = 0;
|
|
if (sa[0] || sa[1] || sa[2] || sa[3])
|
|
k = 0xffffffff;
|
|
msk[0] = msk[1] = msk[2] = msk[3] = k;
|
|
}
|
|
else
|
|
#endif
|
|
*msk = *sa ? 0xffffffff : 0;
|
|
return ports(seg, pp, cp, tp, linenum);
|
|
}
|
|
fprintf(stderr, "%d: bad host (%s)\n", linenum, **seg);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* returns an ip address as a long var as a result of either a DNS lookup or
|
|
* straight inet_addr() call
|
|
*/
|
|
int hostnum(ipa, host, linenum)
|
|
u_32_t *ipa;
|
|
char *host;
|
|
int linenum;
|
|
{
|
|
struct hostent *hp;
|
|
struct netent *np;
|
|
struct in_addr ip;
|
|
|
|
if (!strcasecmp("any", host))
|
|
return 0;
|
|
#ifdef USE_INET6
|
|
if (use_inet6) {
|
|
if (inet_pton(AF_INET6, host, ipa) == 1)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
#endif
|
|
if (isdigit(*host) && inet_aton(host, &ip)) {
|
|
*ipa = ip.s_addr;
|
|
return 0;
|
|
}
|
|
|
|
if (!strcasecmp("<thishost>", host))
|
|
host = thishost;
|
|
|
|
if (!(hp = gethostbyname(host))) {
|
|
if (!(np = getnetbyname(host))) {
|
|
fprintf(stderr, "%d: can't resolve hostname: %s\n",
|
|
linenum, host);
|
|
return -1;
|
|
}
|
|
*ipa = htonl(np->n_net);
|
|
return 0;
|
|
}
|
|
*ipa = *(u_32_t *)hp->h_addr;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* check for possible presence of the port fields in the line
|
|
*/
|
|
int ports(seg, pp, cp, tp, linenum)
|
|
char ***seg;
|
|
u_short *pp, *tp;
|
|
int *cp;
|
|
int linenum;
|
|
{
|
|
int comp = -1;
|
|
|
|
if (!*seg || !**seg || !***seg)
|
|
return 0;
|
|
if (!strcasecmp(**seg, "port") && *(*seg + 1) && *(*seg + 2)) {
|
|
(*seg)++;
|
|
if (isalnum(***seg) && *(*seg + 2)) {
|
|
if (portnum(**seg, pp, linenum) == 0)
|
|
return -1;
|
|
(*seg)++;
|
|
if (!strcmp(**seg, "<>"))
|
|
comp = FR_OUTRANGE;
|
|
else if (!strcmp(**seg, "><"))
|
|
comp = FR_INRANGE;
|
|
else {
|
|
fprintf(stderr,
|
|
"%d: unknown range operator (%s)\n",
|
|
linenum, **seg);
|
|
return -1;
|
|
}
|
|
(*seg)++;
|
|
if (**seg == NULL) {
|
|
fprintf(stderr, "%d: missing 2nd port value\n",
|
|
linenum);
|
|
return -1;
|
|
}
|
|
if (portnum(**seg, tp, linenum) == 0)
|
|
return -1;
|
|
} else if (!strcmp(**seg, "=") || !strcasecmp(**seg, "eq"))
|
|
comp = FR_EQUAL;
|
|
else if (!strcmp(**seg, "!=") || !strcasecmp(**seg, "ne"))
|
|
comp = FR_NEQUAL;
|
|
else if (!strcmp(**seg, "<") || !strcasecmp(**seg, "lt"))
|
|
comp = FR_LESST;
|
|
else if (!strcmp(**seg, ">") || !strcasecmp(**seg, "gt"))
|
|
comp = FR_GREATERT;
|
|
else if (!strcmp(**seg, "<=") || !strcasecmp(**seg, "le"))
|
|
comp = FR_LESSTE;
|
|
else if (!strcmp(**seg, ">=") || !strcasecmp(**seg, "ge"))
|
|
comp = FR_GREATERTE;
|
|
else {
|
|
fprintf(stderr, "%d: unknown comparator (%s)\n",
|
|
linenum, **seg);
|
|
return -1;
|
|
}
|
|
if (comp != FR_OUTRANGE && comp != FR_INRANGE) {
|
|
(*seg)++;
|
|
if (portnum(**seg, pp, linenum) == 0)
|
|
return -1;
|
|
}
|
|
*cp = comp;
|
|
(*seg)++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* find the port number given by the name, either from getservbyname() or
|
|
* straight atoi(). Return 1 on success, 0 on failure
|
|
*/
|
|
int portnum(name, port, linenum)
|
|
char *name;
|
|
u_short *port;
|
|
int linenum;
|
|
{
|
|
struct servent *sp, *sp2;
|
|
u_short p1 = 0;
|
|
int i;
|
|
|
|
if (isdigit(*name)) {
|
|
if (ratoi(name, &i, 0, USHRT_MAX)) {
|
|
*port = (u_short)i;
|
|
return 1;
|
|
}
|
|
fprintf(stderr, "%d: unknown port \"%s\"\n", linenum, name);
|
|
return 0;
|
|
}
|
|
if (proto != NULL && strcasecmp(proto, "tcp/udp") != 0) {
|
|
sp = getservbyname(name, proto);
|
|
if (sp) {
|
|
*port = ntohs(sp->s_port);
|
|
return 1;
|
|
}
|
|
fprintf(stderr, "%d: unknown service \"%s\".\n", linenum, name);
|
|
return 0;
|
|
}
|
|
sp = getservbyname(name, "tcp");
|
|
if (sp)
|
|
p1 = sp->s_port;
|
|
sp2 = getservbyname(name, "udp");
|
|
if (!sp || !sp2) {
|
|
fprintf(stderr, "%d: unknown tcp/udp service \"%s\".\n",
|
|
linenum, name);
|
|
return 0;
|
|
}
|
|
if (p1 != sp2->s_port) {
|
|
fprintf(stderr, "%d: %s %d/tcp is a different port to ",
|
|
linenum, name, p1);
|
|
fprintf(stderr, "%d: %s %d/udp\n", linenum, name, sp->s_port);
|
|
return 0;
|
|
}
|
|
*port = ntohs(p1);
|
|
return 1;
|
|
}
|
|
|
|
|
|
u_char tcp_flags(flgs, mask, linenum)
|
|
char *flgs;
|
|
u_char *mask;
|
|
int linenum;
|
|
{
|
|
u_char tcpf = 0, tcpfm = 0, *fp = &tcpf;
|
|
char *s, *t;
|
|
|
|
if (*flgs == '0') {
|
|
s = strchr(flgs, '/');
|
|
if (s)
|
|
*s++ = '\0';
|
|
tcpf = strtol(flgs, NULL, 0);
|
|
fp = &tcpfm;
|
|
} else
|
|
s = flgs;
|
|
|
|
for (; *s; s++) {
|
|
if (*s == '/' && fp == &tcpf) {
|
|
fp = &tcpfm;
|
|
if (*(s + 1) == '0')
|
|
break;
|
|
continue;
|
|
}
|
|
if (!(t = index(flagset, *s))) {
|
|
fprintf(stderr, "%d: unknown flag (%c)\n", linenum, *s);
|
|
return 0;
|
|
}
|
|
*fp |= flags[t - flagset];
|
|
}
|
|
|
|
if (s && *s == '0')
|
|
tcpfm = strtol(s, NULL, 0);
|
|
|
|
if (!tcpfm) {
|
|
if (tcpf == TH_SYN)
|
|
tcpfm = 0xff & ~(TH_ECN|TH_CWR);
|
|
else
|
|
tcpfm = 0xff & ~(TH_ECN);
|
|
}
|
|
*mask = tcpfm;
|
|
return tcpf;
|
|
}
|
|
|
|
|
|
/*
|
|
* count consecutive 1's in bit mask. If the mask generated by counting
|
|
* consecutive 1's is different to that passed, return -1, else return #
|
|
* of bits.
|
|
*/
|
|
int countbits(ip)
|
|
u_32_t ip;
|
|
{
|
|
u_32_t ipn;
|
|
int cnt = 0, i, j;
|
|
|
|
ip = ipn = ntohl(ip);
|
|
for (i = 32; i; i--, ipn *= 2)
|
|
if (ipn & 0x80000000)
|
|
cnt++;
|
|
else
|
|
break;
|
|
ipn = 0;
|
|
for (i = 32, j = cnt; i; i--, j--) {
|
|
ipn *= 2;
|
|
if (j > 0)
|
|
ipn++;
|
|
}
|
|
if (ipn == ip)
|
|
return cnt;
|
|
return -1;
|
|
}
|
|
|
|
|
|
#ifdef USE_INET6
|
|
int count6bits(msk)
|
|
u_32_t *msk;
|
|
{
|
|
int i = 0, k;
|
|
u_32_t j;
|
|
|
|
for (k = 3; k >= 0; k--)
|
|
if (msk[k] == 0xffffffff)
|
|
i += 32;
|
|
else {
|
|
for (j = msk[k]; j; j <<= 1)
|
|
if (j & 0x80000000)
|
|
i++;
|
|
}
|
|
return i;
|
|
}
|
|
#endif
|
|
|
|
|
|
char *portname(pr, port)
|
|
int pr, port;
|
|
{
|
|
static char buf[32];
|
|
struct protoent *p = NULL;
|
|
struct servent *sv = NULL, *sv1 = NULL;
|
|
|
|
if (pr == -1) {
|
|
if ((sv = getservbyport(htons(port), "tcp"))) {
|
|
strncpy(buf, sv->s_name, sizeof(buf)-1);
|
|
buf[sizeof(buf)-1] = '\0';
|
|
sv1 = getservbyport(htons(port), "udp");
|
|
sv = strncasecmp(buf, sv->s_name, strlen(buf)) ?
|
|
NULL : sv1;
|
|
}
|
|
if (sv)
|
|
return buf;
|
|
} else if (pr && (p = getprotobynumber(pr))) {
|
|
if ((sv = getservbyport(htons(port), p->p_name))) {
|
|
strncpy(buf, sv->s_name, sizeof(buf)-1);
|
|
buf[sizeof(buf)-1] = '\0';
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
(void) sprintf(buf, "%d", port);
|
|
return buf;
|
|
}
|
|
|
|
|
|
int ratoi(ps, pi, min, max)
|
|
char *ps;
|
|
int *pi, min, max;
|
|
{
|
|
int i;
|
|
char *pe;
|
|
|
|
i = (int)strtol(ps, &pe, 0);
|
|
if (*pe != '\0' || i < min || i > max)
|
|
return 0;
|
|
*pi = i;
|
|
return 1;
|
|
}
|
|
|
|
|
|
int ratoui(ps, pi, min, max)
|
|
char *ps;
|
|
u_int *pi, min, max;
|
|
{
|
|
u_int i;
|
|
char *pe;
|
|
|
|
i = (u_int)strtol(ps, &pe, 0);
|
|
if (*pe != '\0' || i < min || i > max)
|
|
return 0;
|
|
*pi = i;
|
|
return 1;
|
|
}
|
|
|
|
|
|
void printhostmask(v, addr, mask)
|
|
int v;
|
|
u_32_t *addr, *mask;
|
|
{
|
|
struct in_addr ipa;
|
|
int ones;
|
|
|
|
#ifdef USE_INET6
|
|
if (v == 6) {
|
|
ones = count6bits(mask);
|
|
if (ones == 0 && !addr[0] && !addr[1] && !addr[2] && !addr[3])
|
|
printf("any");
|
|
else {
|
|
char ipbuf[64];
|
|
printf("%s/%d",
|
|
inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf)),
|
|
ones);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if (!*addr && !*mask)
|
|
printf("any");
|
|
else {
|
|
ipa.s_addr = *addr;
|
|
printf("%s", inet_ntoa(ipa));
|
|
if ((ones = countbits(*mask)) == -1) {
|
|
ipa.s_addr = *mask;
|
|
printf("/%s", inet_ntoa(ipa));
|
|
} else
|
|
printf("/%d", ones);
|
|
}
|
|
}
|
|
|
|
|
|
void printportcmp(pr, frp)
|
|
int pr;
|
|
frpcmp_t *frp;
|
|
{
|
|
static char *pcmp1[] = { "*", "=", "!=", "<", ">", "<=", ">=",
|
|
"<>", "><"};
|
|
|
|
if (frp->frp_cmp == FR_INRANGE || frp->frp_cmp == FR_OUTRANGE)
|
|
printf(" port %d %s %d", frp->frp_port,
|
|
pcmp1[frp->frp_cmp], frp->frp_top);
|
|
else
|
|
printf(" port %s %s", pcmp1[frp->frp_cmp],
|
|
portname(pr, frp->frp_port));
|
|
}
|
|
|
|
|
|
void printbuf(buf, len, zend)
|
|
char *buf;
|
|
int len, zend;
|
|
{
|
|
char *s, c;
|
|
int i;
|
|
|
|
for (s = buf, i = len; i; i--) {
|
|
c = *s++;
|
|
if (isprint(c))
|
|
putchar(c);
|
|
else
|
|
printf("\\%03o", c);
|
|
if ((c == '\0') && zend)
|
|
break;
|
|
}
|
|
}
|