summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFernando Fernandez Mancera <ffmancera@riseup.net>2018-08-22 17:03:46 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2018-08-24 09:44:44 +0200
commitaf00174af3ef478d7d8d96d15647ad1bbb0bc919 (patch)
tree20e2cd8b7719c9bd0f9ae9b6f1ca5d6cf7162e7d
parent1ecfaa2a9f5191823a3c3cd19b760fcb69f7d38d (diff)
src: osf: import nfnl_osf.c to load osf fingerprints
Import iptables/utils/nfnl_osf.c into nftables tree with some changes in order to load OS fingerprints automatically from pf.os file. Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--include/linux/netfilter/Makefile.am1
-rw-r--r--include/linux/netfilter/nfnetlink_osf.h119
-rw-r--r--include/osf.h3
-rw-r--r--src/Makefile.am1
-rw-r--r--src/nfnl_osf.c397
-rw-r--r--src/osf.c1
-rw-r--r--src/rule.c6
7 files changed, 527 insertions, 1 deletions
diff --git a/include/linux/netfilter/Makefile.am b/include/linux/netfilter/Makefile.am
index f6a8aa22..2c1de184 100644
--- a/include/linux/netfilter/Makefile.am
+++ b/include/linux/netfilter/Makefile.am
@@ -3,4 +3,5 @@ noinst_HEADERS = nf_conntrack_common.h \
nf_log.h \
nf_nat.h \
nf_tables.h \
+ nfnetlink_osf.h \
nfnetlink.h
diff --git a/include/linux/netfilter/nfnetlink_osf.h b/include/linux/netfilter/nfnetlink_osf.h
new file mode 100644
index 00000000..15a39d29
--- /dev/null
+++ b/include/linux/netfilter/nfnetlink_osf.h
@@ -0,0 +1,119 @@
+#ifndef _NF_OSF_H
+#define _NF_OSF_H
+
+#include <linux/types.h>
+
+#define MAXGENRELEN 32
+
+#define NF_OSF_GENRE (1 << 0)
+#define NF_OSF_TTL (1 << 1)
+#define NF_OSF_LOG (1 << 2)
+#define NF_OSF_INVERT (1 << 3)
+
+#define NF_OSF_LOGLEVEL_ALL 0 /* log all matched fingerprints */
+#define NF_OSF_LOGLEVEL_FIRST 1 /* log only the first matced fingerprint */
+#define NF_OSF_LOGLEVEL_ALL_KNOWN 2 /* do not log unknown packets */
+
+#define NF_OSF_TTL_TRUE 0 /* True ip and fingerprint TTL comparison */
+
+/* Check if ip TTL is less than fingerprint one */
+#define NF_OSF_TTL_LESS 1
+
+/* Do not compare ip and fingerprint TTL at all */
+#define NF_OSF_TTL_NOCHECK 2
+
+#define NF_OSF_FLAGMASK (NF_OSF_GENRE | NF_OSF_TTL | \
+ NF_OSF_LOG | NF_OSF_INVERT)
+/* Wildcard MSS (kind of).
+ * It is used to implement a state machine for the different wildcard values
+ * of the MSS and window sizes.
+ */
+struct nf_osf_wc {
+ __u32 wc;
+ __u32 val;
+};
+
+/* This struct represents IANA options
+ * http://www.iana.org/assignments/tcp-parameters
+ */
+struct nf_osf_opt {
+ __u16 kind, length;
+ struct nf_osf_wc wc;
+};
+
+struct nf_osf_info {
+ char genre[MAXGENRELEN];
+ __u32 len;
+ __u32 flags;
+ __u32 loglevel;
+ __u32 ttl;
+};
+
+struct nf_osf_user_finger {
+ struct nf_osf_wc wss;
+
+ __u8 ttl, df;
+ __u16 ss, mss;
+ __u16 opt_num;
+
+ char genre[MAXGENRELEN];
+ char version[MAXGENRELEN];
+ char subtype[MAXGENRELEN];
+
+ /* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
+ struct nf_osf_opt opt[MAX_IPOPTLEN];
+};
+
+struct nf_osf_nlmsg {
+ struct nf_osf_user_finger f;
+ struct iphdr ip;
+ struct tcphdr tcp;
+};
+
+/* Defines for IANA option kinds */
+enum iana_options {
+ OSFOPT_EOL = 0, /* End of options */
+ OSFOPT_NOP, /* NOP */
+ OSFOPT_MSS, /* Maximum segment size */
+ OSFOPT_WSO, /* Window scale option */
+ OSFOPT_SACKP, /* SACK permitted */
+ OSFOPT_SACK, /* SACK */
+ OSFOPT_ECHO,
+ OSFOPT_ECHOREPLY,
+ OSFOPT_TS, /* Timestamp option */
+ OSFOPT_POCP, /* Partial Order Connection Permitted */
+ OSFOPT_POSP, /* Partial Order Service Profile */
+
+ /* Others are not used in the current OSF */
+ OSFOPT_EMPTY = 255,
+};
+
+/*
+ * Initial window size option state machine: multiple of mss, mtu or
+ * plain numeric value. Can also be made as plain numeric value which
+ * is not a multiple of specified value.
+ */
+enum nf_osf_window_size_options {
+ OSF_WSS_PLAIN = 0,
+ OSF_WSS_MSS,
+ OSF_WSS_MTU,
+ OSF_WSS_MODULO,
+ OSF_WSS_MAX,
+};
+
+enum nf_osf_attr_type {
+ OSF_ATTR_UNSPEC,
+ OSF_ATTR_FINGER,
+ OSF_ATTR_MAX,
+};
+
+/*
+ * Add/remove fingerprint from the kernel.
+ */
+enum nf_osf_msg_types {
+ OSF_MSG_ADD,
+ OSF_MSG_REMOVE,
+ OSF_MSG_MAX,
+};
+
+#endif /* _NF_OSF_H */
diff --git a/include/osf.h b/include/osf.h
index 715b04e8..074ba9a3 100644
--- a/include/osf.h
+++ b/include/osf.h
@@ -3,4 +3,7 @@
struct expr *osf_expr_alloc(const struct location *loc);
+extern bool osf_init;
+extern int nfnl_osf_load_fingerprints(struct netlink_ctx *ctx, int del);
+
#endif /* NFTABLES_OSF_H */
diff --git a/src/Makefile.am b/src/Makefile.am
index ed3640e4..e569029d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -57,6 +57,7 @@ libnftables_la_SOURCES = \
services.c \
mergesort.c \
osf.c \
+ nfnl_osf.c \
tcpopt.c \
socket.c \
libnftables.c
diff --git a/src/nfnl_osf.c b/src/nfnl_osf.c
new file mode 100644
index 00000000..e37510bd
--- /dev/null
+++ b/src/nfnl_osf.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 2005 Evgeniy Polyakov <johnpol@2ka.mxt.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ * Based on iptables/utils/nfnl_osf.c.
+ */
+
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include <linux/unistd.h>
+
+#include <libmnl/libmnl.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_osf.h>
+#include <mnl.h>
+#include <osf.h>
+
+#define OPTDEL ','
+#define OSFPDEL ':'
+#define MAXOPTSTRLEN 128
+
+bool osf_init;
+
+static struct nf_osf_opt IANA_opts[] = {
+ { .kind = 0, .length = 1,},
+ { .kind=1, .length=1,},
+ { .kind=2, .length=4,},
+ { .kind=3, .length=3,},
+ { .kind=4, .length=2,},
+ { .kind=5, .length=1,}, /* SACK length is not defined */
+ { .kind=6, .length=6,},
+ { .kind=7, .length=6,},
+ { .kind=8, .length=10,},
+ { .kind=9, .length=2,},
+ { .kind=10, .length=3,},
+ { .kind=11, .length=1,}, /* CC: Suppose 1 */
+ { .kind=12, .length=1,}, /* the same */
+ { .kind=13, .length=1,}, /* and here too */
+ { .kind=14, .length=3,},
+ { .kind=15, .length=1,}, /* TCP Alternate Checksum Data. Length is not defined */
+ { .kind=16, .length=1,},
+ { .kind=17, .length=1,},
+ { .kind=18, .length=3,},
+ { .kind=19, .length=18,},
+ { .kind=20, .length=1,},
+ { .kind=21, .length=1,},
+ { .kind=22, .length=1,},
+ { .kind=23, .length=1,},
+ { .kind=24, .length=1,},
+ { .kind=25, .length=1,},
+ { .kind=26, .length=1,},
+};
+
+static char *nf_osf_strchr(char *ptr, char c)
+{
+ char *tmp;
+
+ tmp = strchr(ptr, c);
+ if (tmp)
+ *tmp = '\0';
+
+ while (tmp && tmp + 1 && isspace(*(tmp + 1)))
+ tmp++;
+
+ return tmp;
+}
+
+static void nf_osf_parse_opt(struct nf_osf_opt *opt, __u16 *optnum, char *obuf,
+ int olen)
+{
+ int i, op;
+ char *ptr, wc;
+ unsigned long val;
+
+ ptr = &obuf[0];
+ i = 0;
+ while (ptr != NULL && i < olen && *ptr != 0) {
+ val = 0;
+ op = 0;
+ wc = OSF_WSS_PLAIN;
+ switch (obuf[i]) {
+ case 'N':
+ op = OSFOPT_NOP;
+ ptr = nf_osf_strchr(&obuf[i], OPTDEL);
+ if (ptr) {
+ *ptr = '\0';
+ ptr++;
+ i += (int)(ptr - &obuf[i]);
+ } else
+ i++;
+ break;
+ case 'S':
+ op = OSFOPT_SACKP;
+ ptr = nf_osf_strchr(&obuf[i], OPTDEL);
+ if (ptr) {
+ *ptr = '\0';
+ ptr++;
+ i += (int)(ptr - &obuf[i]);
+ } else
+ i++;
+ break;
+ case 'T':
+ op = OSFOPT_TS;
+ ptr = nf_osf_strchr(&obuf[i], OPTDEL);
+ if (ptr) {
+ *ptr = '\0';
+ ptr++;
+ i += (int)(ptr - &obuf[i]);
+ } else
+ i++;
+ break;
+ case 'W':
+ op = OSFOPT_WSO;
+ ptr = nf_osf_strchr(&obuf[i], OPTDEL);
+ if (ptr) {
+ switch (obuf[i + 1]) {
+ case '%':
+ wc = OSF_WSS_MODULO;
+ break;
+ case 'S':
+ wc = OSF_WSS_MSS;
+ break;
+ case 'T':
+ wc = OSF_WSS_MTU;
+ break;
+ default:
+ wc = OSF_WSS_PLAIN;
+ break;
+ }
+
+ *ptr = '\0';
+ ptr++;
+ if (wc)
+ val = strtoul(&obuf[i + 2], NULL, 10);
+ else
+ val = strtoul(&obuf[i + 1], NULL, 10);
+ i += (int)(ptr - &obuf[i]);
+
+ } else
+ i++;
+ break;
+ case 'M':
+ op = OSFOPT_MSS;
+ ptr = nf_osf_strchr(&obuf[i], OPTDEL);
+ if (ptr) {
+ if (obuf[i + 1] == '%')
+ wc = OSF_WSS_MODULO;
+ *ptr = '\0';
+ ptr++;
+ if (wc)
+ val = strtoul(&obuf[i + 2], NULL, 10);
+ else
+ val = strtoul(&obuf[i + 1], NULL, 10);
+ i += (int)(ptr - &obuf[i]);
+ } else
+ i++;
+ break;
+ case 'E':
+ op = OSFOPT_EOL;
+ ptr = nf_osf_strchr(&obuf[i], OPTDEL);
+ if (ptr) {
+ *ptr = '\0';
+ ptr++;
+ i += (int)(ptr - &obuf[i]);
+ } else
+ i++;
+ break;
+ default:
+ op = OSFOPT_EMPTY;
+ ptr = nf_osf_strchr(&obuf[i], OPTDEL);
+ if (ptr) {
+ ptr++;
+ i += (int)(ptr - &obuf[i]);
+ } else
+ i++;
+ break;
+ }
+
+ if (op != OSFOPT_EMPTY) {
+ opt[*optnum].kind = IANA_opts[op].kind;
+ opt[*optnum].length = IANA_opts[op].length;
+ opt[*optnum].wc.wc = wc;
+ opt[*optnum].wc.val = val;
+ (*optnum)++;
+ }
+ }
+}
+
+static int osf_load_line(char *buffer, int len, int del,
+ struct netlink_ctx *ctx)
+{
+ int i, cnt = 0;
+ char obuf[MAXOPTSTRLEN];
+ struct nf_osf_user_finger f;
+ char *pbeg, *pend;
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfg;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+
+ memset(&f, 0, sizeof(struct nf_osf_user_finger));
+
+ if (ctx->debug_mask & NFT_DEBUG_NETLINK)
+ nft_print(ctx->octx, "Loading '%s'.\n", buffer);
+
+ for (i = 0; i < len && buffer[i] != '\0'; ++i) {
+ if (buffer[i] == ':')
+ cnt++;
+ }
+
+ if (cnt != 8) {
+ if (ctx->debug_mask & NFT_DEBUG_NETLINK)
+ nft_print(ctx->octx, "Wrong input line '%s': cnt: %d, must be 8, i: %d, must be %d.\n", buffer, cnt, i, len);
+ return -EINVAL;
+ }
+
+ memset(obuf, 0, sizeof(obuf));
+
+ pbeg = buffer;
+ pend = nf_osf_strchr(pbeg, OSFPDEL);
+ if (pend) {
+ *pend = '\0';
+ if (pbeg[0] == 'S') {
+ f.wss.wc = OSF_WSS_MSS;
+ if (pbeg[1] == '%')
+ f.wss.val = strtoul(&pbeg[2], NULL, 10);
+ else if (pbeg[1] == '*')
+ f.wss.val = 0;
+ else
+ f.wss.val = strtoul(&pbeg[1], NULL, 10);
+ } else if (pbeg[0] == 'T') {
+ f.wss.wc = OSF_WSS_MTU;
+ if (pbeg[1] == '%')
+ f.wss.val = strtoul(&pbeg[2], NULL, 10);
+ else if (pbeg[1] == '*')
+ f.wss.val = 0;
+ else
+ f.wss.val = strtoul(&pbeg[1], NULL, 10);
+ } else if (pbeg[0] == '%') {
+ f.wss.wc = OSF_WSS_MODULO;
+ f.wss.val = strtoul(&pbeg[1], NULL, 10);
+ } else if (isdigit(pbeg[0])) {
+ f.wss.wc = OSF_WSS_PLAIN;
+ f.wss.val = strtoul(&pbeg[0], NULL, 10);
+ }
+
+ pbeg = pend + 1;
+ }
+ pend = nf_osf_strchr(pbeg, OSFPDEL);
+ if (pend) {
+ *pend = '\0';
+ f.ttl = strtoul(pbeg, NULL, 10);
+ pbeg = pend + 1;
+ }
+ pend = nf_osf_strchr(pbeg, OSFPDEL);
+ if (pend) {
+ *pend = '\0';
+ f.df = strtoul(pbeg, NULL, 10);
+ pbeg = pend + 1;
+ }
+ pend = nf_osf_strchr(pbeg, OSFPDEL);
+ if (pend) {
+ *pend = '\0';
+ f.ss = strtoul(pbeg, NULL, 10);
+ pbeg = pend + 1;
+ }
+
+ pend = nf_osf_strchr(pbeg, OSFPDEL);
+ if (pend) {
+ *pend = '\0';
+ cnt = snprintf(obuf, sizeof(obuf), "%s,", pbeg);
+ pbeg = pend + 1;
+ }
+
+ pend = nf_osf_strchr(pbeg, OSFPDEL);
+ if (pend) {
+ *pend = '\0';
+ if (pbeg[0] == '@' || pbeg[0] == '*')
+ cnt = snprintf(f.genre, sizeof(f.genre), "%s", pbeg + 1);
+ else
+ cnt = snprintf(f.genre, sizeof(f.genre), "%s", pbeg);
+ pbeg = pend + 1;
+ }
+
+ pend = nf_osf_strchr(pbeg, OSFPDEL);
+ if (pend) {
+ *pend = '\0';
+ cnt = snprintf(f.version, sizeof(f.version), "%s", pbeg);
+ pbeg = pend + 1;
+ }
+
+ pend = nf_osf_strchr(pbeg, OSFPDEL);
+ if (pend) {
+ *pend = '\0';
+ cnt =
+ snprintf(f.subtype, sizeof(f.subtype), "%s", pbeg);
+ pbeg = pend + 1;
+ }
+
+ nf_osf_parse_opt(f.opt, &f.opt_num, obuf, sizeof(obuf));
+
+ memset(buf, 0, sizeof(buf));
+
+ if (del) {
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_OSF << 8) | OSF_MSG_REMOVE;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_seq = ctx->seqnum;
+
+ nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_UNSPEC;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = 0;
+ } else {
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_OSF << 8) | OSF_MSG_ADD;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
+ nlh->nlmsg_seq = ctx->seqnum;
+
+ nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_UNSPEC;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = 0;
+
+ mnl_attr_put(nlh, OSF_ATTR_FINGER, sizeof(struct nf_osf_user_finger), &f);
+ }
+
+ return nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, 0, NULL);
+}
+
+#define OS_SIGNATURES DEFAULT_INCLUDE_PATH "/nftables/osf/pf.os"
+
+int nfnl_osf_load_fingerprints(struct netlink_ctx *ctx, int del)
+{
+ FILE *inf;
+ int err = 0;
+ char buf[1024];
+
+ if (ctx->debug_mask & NFT_DEBUG_NETLINK)
+ nft_print(ctx->octx, "Opening OS signature file '%s'\n",
+ OS_SIGNATURES);
+
+ inf = fopen(OS_SIGNATURES, "r");
+ if (!inf) {
+ if (ctx->debug_mask & NFT_DEBUG_NETLINK)
+ nft_print(ctx->octx, "Failed to open file '%s'\n",
+ OS_SIGNATURES);
+
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), inf)) {
+ int len;
+
+ if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r')
+ continue;
+
+ len = strlen(buf) - 1;
+
+ if (len <= 0)
+ continue;
+
+ buf[len] = '\0';
+
+ err = osf_load_line(buf, len, del, ctx);
+ if (err)
+ break;
+
+ memset(buf, 0, sizeof(buf));
+ }
+
+ fclose(inf);
+ return err;
+}
diff --git a/src/osf.c b/src/osf.c
index 131d54e4..b8457cc4 100644
--- a/src/osf.c
+++ b/src/osf.c
@@ -26,6 +26,7 @@ struct expr *osf_expr_alloc(const struct location *loc)
const struct datatype *type = &string_type;
struct expr *expr;
+ osf_init = true;
expr = expr_alloc(loc, &osf_expr_ops, type,
BYTEORDER_HOST_ENDIAN, len);
diff --git a/src/rule.c b/src/rule.c
index 570d6671..df35f3e1 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1323,6 +1323,7 @@ static int do_add_set(struct netlink_ctx *ctx, const struct cmd *cmd,
static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
{
uint32_t flags = excl ? NLM_F_EXCL : 0;
+ int err;
if (ctx->octx->echo) {
int ret;
@@ -1341,7 +1342,10 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
case CMD_OBJ_CHAIN:
return netlink_add_chain_batch(ctx, cmd, flags);
case CMD_OBJ_RULE:
- return netlink_add_rule_batch(ctx, cmd, flags | NLM_F_APPEND);
+ err = netlink_add_rule_batch(ctx, cmd, flags | NLM_F_APPEND);
+ if (osf_init)
+ nfnl_osf_load_fingerprints(ctx, 0);
+ return err;
case CMD_OBJ_SET:
return do_add_set(ctx, cmd, flags);
case CMD_OBJ_SETELEM: