From db9bb2778d3685a9361394c694c649316dc867e4 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 29 Sep 2009 14:00:59 +0200 Subject: [PATCH 01/68] pknock: import pknock trunk@463 --- extensions/Kbuild | 1 + extensions/Mbuild | 1 + extensions/libxt_pknock.c | 351 +++++++++++ extensions/xt_pknock.c | 1241 +++++++++++++++++++++++++++++++++++++ extensions/xt_pknock.h | 94 +++ mconfig | 1 + 6 files changed, 1689 insertions(+) create mode 100644 extensions/libxt_pknock.c create mode 100644 extensions/xt_pknock.c create mode 100644 extensions/xt_pknock.h diff --git a/extensions/Kbuild b/extensions/Kbuild index ab3dfdd..b86a1dc 100644 --- a/extensions/Kbuild +++ b/extensions/Kbuild @@ -26,6 +26,7 @@ obj-${build_ipset} += ipset/ obj-${build_ipv4options} += xt_ipv4options.o obj-${build_length2} += xt_length2.o obj-${build_lscan} += xt_lscan.o +obj-${build_pknock} += xt_pknock.o obj-${build_psd} += xt_psd.o obj-${build_quota2} += xt_quota2.o diff --git a/extensions/Mbuild b/extensions/Mbuild index 65ae18f..bc0e1d0 100644 --- a/extensions/Mbuild +++ b/extensions/Mbuild @@ -21,5 +21,6 @@ obj-${build_ipset} += ipset/ obj-${build_ipv4options} += libxt_ipv4options.so obj-${build_length2} += libxt_length2.so obj-${build_lscan} += libxt_lscan.so +obj-${build_pknock} += libxt_pknock.so obj-${build_psd} += libxt_psd.so obj-${build_quota2} += libxt_quota2.so diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c new file mode 100644 index 0000000..581e5bb --- /dev/null +++ b/extensions/libxt_pknock.c @@ -0,0 +1,351 @@ +/* + * Shared library add-on to iptables to add Port Knocking and SPA matching + * support. + * + * (C) 2006-2009 J. Federico Hernandez + * (C) 2006 Luis Floreani + * + * $Id$ + * + * This program is released under the terms of GNU GPL version 2. + */ +#include +#include +#include +#include + +#include +#include +#include +//#include +#include "xt_pknock.h" + +static const struct option pknock_opts[] = { + /* .name, .has_arg, .flag, .val */ + { "knockports", 1, 0, 'k' }, + { "t", 1, 0, 't' }, + { "time", 1, 0, 't' }, + { "name", 1, 0, 'n' }, + { "opensecret", 1, 0, 'a' }, + { "closesecret",1, 0, 'z' }, + { "strict", 0, 0, 'x' }, + { "checkip", 0, 0, 'c' }, + { "chkip", 0, 0, 'c' }, + { .name = NULL } +}; + +/* Function which prints out usage message. */ +static void pknock_help(void) +{ + printf("pknock match options:\n" + " --knockports port[,port,port,...] " + "Matches destination port(s).\n" + " --time seconds\n" + " --t ... " + "Time between port match.\n" + " --secure " + "hmac must be in the packets.\n" + " --strict " + "Knocks sequence must be exact.\n" + " --name rule_name " + "Rule name.\n" + " --checkip " + "Matches if the source ip is in the list.\n" + " --chkip\n"); +} + +static unsigned int +parse_ports(const char *portstring, uint16_t *ports, const char *proto) +{ + char *buffer, *cp, *next; + unsigned int i; + + buffer = strdup(portstring); + if (!buffer) xtables_error(OTHER_PROBLEM, "strdup failed"); + + for (cp=buffer, i=0; cp && idata; + + switch (c) { + case 'k': /* --knockports */ + if (*flags & IPT_PKNOCK_KNOCKPORT) + xtables_error(PARAMETER_PROBLEM, PKNOCK + "cannot use --knockports twice.\n"); + + xtables_check_inverse(argv[optind-1], &invert, &optind, 0); + proto = check_proto(pnum, invflags); + + info->ports_count = parse_ports(optarg, info->port, proto); + info->option |= IPT_PKNOCK_KNOCKPORT; + *flags |= IPT_PKNOCK_KNOCKPORT; +#if DEBUG + printf("ports_count: %d\n", info->ports_count); +#endif + break; + + case 't': /* --time */ + if (*flags & IPT_PKNOCK_TIME) + xtables_error(PARAMETER_PROBLEM, PKNOCK + "cannot use --time twice.\n"); + + xtables_check_inverse(argv[optind-1], &invert, &optind, 0); + + info->max_time = atoi(optarg); + info->option |= IPT_PKNOCK_TIME; + *flags |= IPT_PKNOCK_TIME; + break; + + case 'n': /* --name */ + if (*flags & IPT_PKNOCK_NAME) + xtables_error(PARAMETER_PROBLEM, PKNOCK + "cannot use --name twice.\n"); + + xtables_check_inverse(argv[optind-1], &invert, &optind, 0); + + memset(info->rule_name, 0, IPT_PKNOCK_MAX_BUF_LEN + 1); + strncpy(info->rule_name, optarg, IPT_PKNOCK_MAX_BUF_LEN); + + info->rule_name_len = strlen(info->rule_name); + info->option |= IPT_PKNOCK_NAME; + *flags |= IPT_PKNOCK_NAME; +#if DEBUG + printf("info->rule_name: %s\n", info->rule_name); +#endif + break; + + case 'a': /* --opensecret */ + if (*flags & IPT_PKNOCK_OPENSECRET) + xtables_error(PARAMETER_PROBLEM, PKNOCK + "cannot use --opensecret twice.\n"); + + xtables_check_inverse(argv[optind-1], &invert, &optind, 0); + + memset(info->open_secret, 0, IPT_PKNOCK_MAX_PASSWD_LEN + 1); + strncpy(info->open_secret, optarg, IPT_PKNOCK_MAX_PASSWD_LEN); + + info->open_secret_len = strlen(info->open_secret); + info->option |= IPT_PKNOCK_OPENSECRET; + *flags |= IPT_PKNOCK_OPENSECRET; + break; + + case 'z': /* --closesecret */ + if (*flags & IPT_PKNOCK_CLOSESECRET) + xtables_error(PARAMETER_PROBLEM, PKNOCK + "cannot use --closesecret twice.\n"); + + xtables_check_inverse(argv[optind-1], &invert, &optind, 0); + + memset(info->close_secret, 0, IPT_PKNOCK_MAX_PASSWD_LEN + 1); + strncpy(info->close_secret, optarg, IPT_PKNOCK_MAX_PASSWD_LEN); + + info->close_secret_len = strlen(info->close_secret); + info->option |= IPT_PKNOCK_CLOSESECRET; + *flags |= IPT_PKNOCK_CLOSESECRET; + break; + + case 'c': /* --checkip */ + if (*flags & IPT_PKNOCK_CHECKIP) + xtables_error(PARAMETER_PROBLEM, PKNOCK + "cannot use --checkip twice.\n"); + + xtables_check_inverse(argv[optind-1], &invert, &optind, 0); + + info->option |= IPT_PKNOCK_CHECKIP; + *flags |= IPT_PKNOCK_CHECKIP; + break; + + case 'x': /* --strict */ + if (*flags & IPT_PKNOCK_STRICT) + xtables_error(PARAMETER_PROBLEM, PKNOCK + "cannot use --strict twice.\n"); + + xtables_check_inverse(argv[optind-1], &invert, &optind, 0); + + info->option |= IPT_PKNOCK_STRICT; + *flags |= IPT_PKNOCK_STRICT; + break; + + default: + return 0; + } + + if (invert) + xtables_error(PARAMETER_PROBLEM, PKNOCK "does not support invert."); + + return 1; +} + +static int pknock_parse(int c, char **argv, int invert, unsigned int *flags, + const void *e, struct xt_entry_match **match) +{ + const struct ipt_entry *entry = e; + return __pknock_parse(c, argv, invert, flags, match, + entry->ip.proto, entry->ip.invflags); +} + +/* Final check. */ +static void pknock_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, PKNOCK "expection an option.\n"); + + if (!(flags & IPT_PKNOCK_NAME)) + xtables_error(PARAMETER_PROBLEM, PKNOCK + "--name option is required.\n"); + + if (flags & IPT_PKNOCK_KNOCKPORT) { + if (flags & IPT_PKNOCK_CHECKIP) { + xtables_error(PARAMETER_PROBLEM, PKNOCK + "cannot specify --knockports with --checkip.\n"); + } + if ((flags & IPT_PKNOCK_OPENSECRET) + && !(flags & IPT_PKNOCK_CLOSESECRET)) + { + xtables_error(PARAMETER_PROBLEM, PKNOCK + "--opensecret must go with --closesecret.\n"); + } + if ((flags & IPT_PKNOCK_CLOSESECRET) + && !(flags & IPT_PKNOCK_OPENSECRET)) + { + xtables_error(PARAMETER_PROBLEM, PKNOCK + "--closesecret must go with --opensecret.\n"); + } + } + + if (flags & IPT_PKNOCK_CHECKIP) { + if (flags & IPT_PKNOCK_KNOCKPORT) { + xtables_error(PARAMETER_PROBLEM, PKNOCK + "cannot specify --checkip with --knockports.\n"); + } + if ((flags & IPT_PKNOCK_OPENSECRET) + || (flags & IPT_PKNOCK_CLOSESECRET)) + { + xtables_error(PARAMETER_PROBLEM, PKNOCK + "cannot specify --opensecret and" + " --closesecret with --checkip.\n"); + } + if (flags & IPT_PKNOCK_TIME) { + xtables_error(PARAMETER_PROBLEM, PKNOCK + "cannot specify --time with --checkip.\n"); + } + } +} + +/* Prints out the matchinfo. */ +static void pknock_print(const void *ip, + const struct xt_entry_match *match, int numeric) +{ + const struct ipt_pknock *info; + int i; + + info = (const struct ipt_pknock *)match->data; + + printf("pknock "); + if (info->option & IPT_PKNOCK_KNOCKPORT) { + printf("knockports "); + for (i=0; iports_count; i++) + printf("%s%d", i ? "," : "", info->port[i]); + printf(" "); + } + if (info->option & IPT_PKNOCK_TIME) + printf("time %ld ", info->max_time); + if (info->option & IPT_PKNOCK_NAME) + printf("name %s ", info->rule_name); + if (info->option & IPT_PKNOCK_OPENSECRET) + printf("opensecret "); + if (info->option & IPT_PKNOCK_CLOSESECRET) + printf("closesecret "); +} + +/* Saves the union ipt_matchinfo in parsable form to stdout. */ +static void pknock_save(const void *ip, const struct xt_entry_match *match) +{ + int i; + const struct ipt_pknock *info = (const struct ipt_pknock *)match->data; + + if (info->option & IPT_PKNOCK_KNOCKPORT) { + printf("--knockports "); + for (i=0; iports_count; i++) + printf("%s%d", i ? "," : "", info->port[i]); + printf(" "); + } + if (info->option & IPT_PKNOCK_TIME) + printf("--time %ld ", info->max_time); + if (info->option & IPT_PKNOCK_NAME) + printf("--name %s ", info->rule_name); + if (info->option & IPT_PKNOCK_OPENSECRET) + printf("--opensecret "); + if (info->option & IPT_PKNOCK_CLOSESECRET) + printf("--closesecret "); + if (info->option & IPT_PKNOCK_STRICT) + printf("--strict "); + if (info->option & IPT_PKNOCK_CHECKIP) + printf("--checkip "); +} + +static struct xtables_match pknock_match = { + .name = "pknock", + .version = XTABLES_VERSION, + .family = AF_INET, + .size = XT_ALIGN(sizeof (struct ipt_pknock)), + .userspacesize = XT_ALIGN(sizeof (struct ipt_pknock)), + .help = pknock_help, + .parse = pknock_parse, + .final_check = pknock_check, + .print = pknock_print, + .save = pknock_save, + .extra_opts = pknock_opts +}; + +void _init(void) +{ + xtables_register_match(&pknock_match); +} diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c new file mode 100644 index 0000000..da4c170 --- /dev/null +++ b/extensions/xt_pknock.c @@ -0,0 +1,1241 @@ +/* + * Kernel module to implement Port Knocking and SPA matching support. + * + * (C) 2006-2009 J. Federico Hernandez Scarso + * (C) 2006 Luis A. Floreani + * + * $Id$ + * + * This program is released under the terms of GNU GPL version 2. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +//#include +#include "xt_pknock.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("J. Federico Hernandez Scarso, Luis A. Floreani"); +MODULE_DESCRIPTION("netfilter match for Port Knocking and SPA"); + +enum { + GC_EXPIRATION_TIME = 65000, /* in msecs */ + DEFAULT_RULE_HASH_SIZE = 8, + DEFAULT_PEER_HASH_SIZE = 16, +}; + +#define hashtable_for_each_safe(pos, n, head, size, i) \ + for ((i) = 0; (i) < (size); (i)++) \ + list_for_each_safe((pos), (n), (&head[(i)])) + +#if DEBUG + #define DEBUGP(msg, peer) printk(KERN_INFO PKNOCK \ + "(S) peer: %u.%u.%u.%u - %s.\n", \ + NIPQUAD((peer)->ip), msg) + #define duprintf(format, args...) printk(format, ## args); +#else + #define DEBUGP(msg, peer) + #define duprintf(format, args...) +#endif + +static uint32_t ipt_pknock_hash_rnd; + +static unsigned int rule_hashsize = DEFAULT_RULE_HASH_SIZE; +static unsigned int peer_hashsize = DEFAULT_PEER_HASH_SIZE; +static unsigned int ipt_pknock_gc_expir_time = GC_EXPIRATION_TIME; +static int nl_multicast_group = -1; + +static struct list_head *rule_hashtable = NULL; +static struct proc_dir_entry *pde = NULL; + +static DEFINE_SPINLOCK(list_lock); + +static struct ipt_pknock_crypto crypto = { + .algo = "hmac(sha256)", + .tfm = NULL, + .size = 0 +}; + +module_param(rule_hashsize, int, S_IRUGO); +module_param(peer_hashsize, int, S_IRUGO); +module_param(ipt_pknock_gc_expir_time, int, S_IRUGO); +module_param(nl_multicast_group, int, S_IRUGO); + +/** + * Calculates a value from 0 to max from a hash of the arguments. + * + * @key + * @len: length + * @initval + * @max + * @return: a 32 bits index + */ +static uint32_t +pknock_hash(const void *key, uint32_t len, uint32_t initval, uint32_t max) +{ + return jhash(key, len, initval) % max; +} + +/** + * @return: the epoch minute + */ +static int +get_epoch_minute(void) +{ + struct timespec t = CURRENT_TIME; + return (int)(t.tv_sec/60); +} + +/** + * Alloc a hashtable with n buckets. + * + * @size + * @return: hashtable + */ +static struct list_head * +alloc_hashtable(int size) +{ + struct list_head *hash = NULL; + unsigned int i; + + if ((hash = kmalloc(sizeof(*hash) * size, GFP_ATOMIC)) == NULL) { + printk(KERN_ERR PKNOCK + "kmalloc() error in alloc_hashtable() function.\n"); + return NULL; + } + + for (i = 0; i < size; i++) + INIT_LIST_HEAD(&hash[i]); + + return hash; +} + +/** + * This function converts the status from integer to string. + * + * @status + * @return: status + */ +static inline const char * +status_itoa(enum status status) +{ + switch (status) { + case ST_INIT: return "INIT"; + case ST_MATCHING: return "MATCHING"; + case ST_ALLOWED: return "ALLOWED"; + default: return "UNKNOWN"; + } +} + +/** + * @s + * @pos + * @return: private value used by the iterator + */ +static void * +pknock_seq_start(struct seq_file *s, loff_t *pos) +{ + struct proc_dir_entry *pde = s->private; + struct ipt_pknock_rule *rule = pde->data; + + spin_lock_bh(&list_lock); + + if (*pos >= peer_hashsize) + return NULL; + + return rule->peer_head + *pos; +} + +/** + * @s + * @v + * @pos + * @return: next value for the iterator + */ +static void * +pknock_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct proc_dir_entry *pde = s->private; + struct ipt_pknock_rule *rule = pde->data; + + (*pos)++; + if (*pos >= peer_hashsize) + return NULL; + + return rule->peer_head + *pos; +} + +/** + * @s + * @v + */ +static void +pknock_seq_stop(struct seq_file *s, void *v) +{ + spin_unlock_bh(&list_lock); +} + +/** + * @s + * @v + * @return: 0 if OK + */ +static int +pknock_seq_show(struct seq_file *s, void *v) +{ + struct list_head *pos = NULL, *n = NULL; + struct peer *peer = NULL; + unsigned long expir_time = 0; + uint32_t ip; + + struct list_head *peer_head = (struct list_head *)v; + + struct proc_dir_entry *pde = s->private; + struct ipt_pknock_rule *rule = pde->data; + + list_for_each_safe(pos, n, peer_head) { + peer = list_entry(pos, struct peer, head); + ip = htonl(peer->ip); + expir_time = time_before(jiffies/HZ, + peer->timestamp + rule->max_time) + ? ((peer->timestamp + rule->max_time)-(jiffies/HZ)) : 0; + + seq_printf(s, "src=%u.%u.%u.%u ", NIPQUAD(ip)); + seq_printf(s, "proto=%s ", (peer->proto == IPPROTO_TCP) ? + "TCP" : "UDP"); + seq_printf(s, "status=%s ", status_itoa(peer->status)); + seq_printf(s, "expir_time=%ld ", expir_time); + seq_printf(s, "next_port_id=%d ", peer->id_port_knocked-1); + seq_printf(s, "\n"); + } + + return 0; +} + +static struct seq_operations pknock_seq_ops = { + .start = pknock_seq_start, + .next = pknock_seq_next, + .stop = pknock_seq_stop, + .show = pknock_seq_show +}; + +/** + * @inode + * @file + */ +static int +pknock_proc_open(struct inode *inode, struct file *file) +{ + int ret = seq_open(file, &pknock_seq_ops); + if (!ret) { + struct seq_file *sf = file->private_data; + sf->private = PDE(inode); + } + return ret; +} + +static struct file_operations pknock_proc_ops = { + .owner = THIS_MODULE, + .open = pknock_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release +}; + +/** + * It updates the rule timer to execute garbage collector. + * + * @rule + */ +static inline void +update_rule_timer(struct ipt_pknock_rule *rule) +{ + if (timer_pending(&rule->timer)) + del_timer(&rule->timer); + + rule->timer.expires = jiffies + msecs_to_jiffies(ipt_pknock_gc_expir_time); + add_timer(&rule->timer); +} + +/** + * @peer + * @max_time + * @return: 1 time exceeded, 0 still valid + */ +static inline bool +is_time_exceeded(struct peer *peer, int max_time) +{ + return peer && time_after(jiffies/HZ, peer->timestamp + max_time); +} + +/** + * @peer + * @return: 1 has logged, 0 otherwise + */ +static inline bool +has_logged_during_this_minute(const struct peer *peer) +{ + return peer && (peer->login_min == get_epoch_minute()); +} + +/** + * Garbage collector. It removes the old entries after timer has expired. + * + * @r: rule + */ +static void +peer_gc(unsigned long r) +{ + int i; + struct ipt_pknock_rule *rule = (struct ipt_pknock_rule *)r; + struct peer *peer = NULL; + struct list_head *pos = NULL, *n = NULL; + + hashtable_for_each_safe(pos, n, rule->peer_head, peer_hashsize, i) { + peer = list_entry(pos, struct peer, head); + + if (!has_logged_during_this_minute(peer) && + is_time_exceeded(peer, rule->max_time)) + { + DEBUGP("DESTROYED", peer); + list_del(pos); + kfree(peer); + } + } +} + +/** + * Compares length and name equality for the rules. + * + * @info + * @rule + * @return: 0 equals, 1 otherwise + */ +static inline int +rulecmp(const struct ipt_pknock *info, const struct ipt_pknock_rule *rule) +{ + if (info->rule_name_len != rule->rule_name_len) + return 1; + if (strncmp(info->rule_name, rule->rule_name, info->rule_name_len) != 0) + return 1; + return 0; +} + +/** + * Search the rule and returns a pointer if it exists. + * + * @info + * @return: rule or NULL + */ +static inline struct ipt_pknock_rule * +search_rule(const struct ipt_pknock *info) +{ + struct ipt_pknock_rule *rule = NULL; + struct list_head *pos = NULL, *n = NULL; + int hash = pknock_hash(info->rule_name, info->rule_name_len, + ipt_pknock_hash_rnd, rule_hashsize); + + list_for_each_safe(pos, n, &rule_hashtable[hash]) { + rule = list_entry(pos, struct ipt_pknock_rule, head); + if (rulecmp(info, rule) == 0) + return rule; + } + return NULL; +} + +/** + * It adds a rule to list only if it doesn't exist. + * + * @info + * @return: 1 success, 0 failure + */ +static bool +add_rule(struct ipt_pknock *info) +{ + struct ipt_pknock_rule *rule = NULL; + struct list_head *pos = NULL, *n = NULL; + int hash = pknock_hash(info->rule_name, info->rule_name_len, + ipt_pknock_hash_rnd, rule_hashsize); + + list_for_each_safe(pos, n, &rule_hashtable[hash]) { + rule = list_entry(pos, struct ipt_pknock_rule, head); + + if (rulecmp(info, rule) == 0) { + rule->ref_count++; +#if DEBUG + if (info->option & IPT_PKNOCK_CHECKIP) { + printk(KERN_DEBUG PKNOCK "add_rule() (AC)" + " rule found: %s - " + "ref_count: %d\n", + rule->rule_name, + rule->ref_count); + } +#endif + return true; + } + } + + if ((rule = kmalloc(sizeof (*rule), GFP_ATOMIC)) == NULL) { + printk(KERN_ERR PKNOCK "kmalloc() error in add_rule().\n"); + return false; + } + + INIT_LIST_HEAD(&rule->head); + + memset(rule->rule_name, 0, IPT_PKNOCK_MAX_BUF_LEN + 1); + strncpy(rule->rule_name, info->rule_name, info->rule_name_len); + rule->rule_name_len = info->rule_name_len; + + rule->ref_count = 1; + rule->max_time = info->max_time; + + if (!(rule->peer_head = alloc_hashtable(peer_hashsize))) { + printk(KERN_ERR PKNOCK "alloc_hashtable() error in add_rule().\n"); + return false; + } + + init_timer(&rule->timer); + rule->timer.function = peer_gc; + rule->timer.data = (unsigned long)rule; + + rule->status_proc = create_proc_entry(info->rule_name, 0, pde); + if (!rule->status_proc) { + printk(KERN_ERR PKNOCK "create_proc_entry() error in add_rule()" + " function.\n"); + kfree(rule); + return false; + } + + rule->status_proc->proc_fops = &pknock_proc_ops; + rule->status_proc->data = rule; + + list_add(&rule->head, &rule_hashtable[hash]); +#if DEBUG + printk(KERN_INFO PKNOCK "(A) rule_name: %s - created.\n", rule->rule_name); +#endif + return true; +} + +/** + * It removes a rule only if it exists. + * + * @info + */ +static void +remove_rule(struct ipt_pknock *info) +{ + struct ipt_pknock_rule *rule = NULL; + struct list_head *pos = NULL, *n = NULL; + struct peer *peer = NULL; + int i; +#if DEBUG + int found = 0; +#endif + int hash = pknock_hash(info->rule_name, info->rule_name_len, + ipt_pknock_hash_rnd, rule_hashsize); + + if (list_empty(&rule_hashtable[hash])) return; + + list_for_each_safe(pos, n, &rule_hashtable[hash]) { + rule = list_entry(pos, struct ipt_pknock_rule, head); + + if (rulecmp(info, rule) == 0) { +#if DEBUG + found = 1; +#endif + rule->ref_count--; + break; + } + } +#if DEBUG + if (!found) { + printk(KERN_INFO PKNOCK "(N) rule not found: %s.\n", info->rule_name); + return; + } +#endif + if (rule && rule->ref_count == 0) { + hashtable_for_each_safe(pos, n, rule->peer_head, peer_hashsize, i) { + peer = list_entry(pos, struct peer, head); + + if (peer != NULL) { + DEBUGP("DELETED", peer); + list_del(pos); + kfree(peer); + } + } + + if (rule->status_proc) + remove_proc_entry(info->rule_name, pde); +#if DEBUG + printk(KERN_INFO PKNOCK "(D) rule deleted: %s.\n", rule->rule_name); +#endif + if (timer_pending(&rule->timer)) + del_timer(&rule->timer); + + list_del(&rule->head); + kfree(rule->peer_head); + kfree(rule); + } +} + +/** + * If peer status exist in the list it returns peer status, if not it returns NULL. + * + * @rule + * @ip + * @return: peer or NULL + */ +static inline struct peer * +get_peer(struct ipt_pknock_rule *rule, uint32_t ip) +{ + struct peer *peer = NULL; + struct list_head *pos = NULL, *n = NULL; + int hash; + + ip = ntohl(ip); + + hash = pknock_hash(&ip, sizeof(ip), ipt_pknock_hash_rnd, peer_hashsize); + + list_for_each_safe(pos, n, &rule->peer_head[hash]) { + peer = list_entry(pos, struct peer, head); + if (peer->ip == ip) return peer; + } + return NULL; +} + +/** + * Reset the knock sequence status of the peer. + * + * @peer + */ +static inline void +reset_knock_status(struct peer *peer) +{ + peer->id_port_knocked = 1; + peer->status = ST_INIT; +} + +/** + * It creates a new peer matching status. + * + * @rule + * @ip + * @proto + * @return: peer or NULL + */ +static inline struct peer * +new_peer(uint32_t ip, uint8_t proto) +{ + struct peer *peer = NULL; + + if ((peer = kmalloc(sizeof (*peer), GFP_ATOMIC)) == NULL) { + printk(KERN_ERR PKNOCK "kmalloc() error in new_peer().\n"); + return NULL; + } + + INIT_LIST_HEAD(&peer->head); + peer->ip = ntohl(ip); + peer->proto = proto; + peer->timestamp = jiffies/HZ; + peer->login_min = 0; + reset_knock_status(peer); + + return peer; +} + +/** + * It adds a new peer matching status to the list. + * + * @peer + * @rule + */ +static inline void +add_peer(struct peer *peer, struct ipt_pknock_rule *rule) +{ + int hash = pknock_hash(&peer->ip, sizeof(peer->ip), + ipt_pknock_hash_rnd, peer_hashsize); + list_add(&peer->head, &rule->peer_head[hash]); +} + +/** + * It removes a peer matching status. + * + * @peer + */ +static inline void +remove_peer(struct peer *peer) +{ + list_del(&peer->head); + if (peer) kfree(peer); +} + +/** + * @peer + * @info + * @port + * @return: 1 success, 0 failure + */ +static inline bool +is_first_knock(const struct peer *peer, const struct ipt_pknock *info, + uint16_t port) +{ + return (peer == NULL && info->port[0] == port) ? 1 : 0; +} + +/** + * @peer + * @info + * @port + * @return: 1 success, 0 failure + */ +static inline bool +is_wrong_knock(const struct peer *peer, const struct ipt_pknock *info, + uint16_t port) +{ + return peer && (info->port[peer->id_port_knocked-1] != port); +} + +/** + * @peer + * @info + * @return: 1 success, 0 failure + */ +static inline bool +is_last_knock(const struct peer *peer, const struct ipt_pknock *info) +{ + return peer && (peer->id_port_knocked-1 == info->ports_count); +} + +/** + * @peer + * @return: 1 success, 0 failure + */ +static inline bool +is_allowed(const struct peer *peer) +{ + return peer && (peer->status == ST_ALLOWED); +} + +/** + * Sends a message to user space through netlink sockets. + * + * @info + * @peer + * @return: 1 success, 0 otherwise + */ +static bool +msg_to_userspace_nl(const struct ipt_pknock *info, + const struct peer *peer, int multicast_group) +{ + struct cn_msg *m; + struct ipt_pknock_nl_msg msg; + + m = kmalloc(sizeof(*m) + sizeof(msg), GFP_ATOMIC); + if (!m) { + printk(KERN_ERR PKNOCK "kmalloc() error in " + "msg_to_userspace_nl().\n"); + return false; + } + + memset(m, 0, sizeof(*m) + sizeof(msg)); + m->seq = 0; + m->len = sizeof(msg); + + msg.peer_ip = peer->ip; + scnprintf(msg.rule_name, info->rule_name_len + 1, info->rule_name); + + memcpy(m + 1, (char *)&msg, m->len); + + cn_netlink_send(m, multicast_group, GFP_ATOMIC); + + kfree(m); + return true; +} + +/** + * Transforms a sequence of characters to hexadecimal. + * + * @out: the hexadecimal result + * @crypt: the original sequence + * @size + */ +static void +crypt_to_hex(char *out, char *crypt, int size) +{ + int i; + for (i=0; i < size; i++) { + unsigned char c = crypt[i]; + *out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1); + *out++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1); + } +} + +/** + * Checks that the payload has the hmac(secret+ipsrc+epoch_min). + * + * @secret + * @secret_len + * @ipsrc + * @payload + * @payload_len + * @return: 1 success, 0 failure + */ +static int +has_secret(unsigned char *secret, int secret_len, uint32_t ipsrc, + unsigned char *payload, int payload_len) +{ + struct scatterlist sg[2]; + char result[64]; // 64 bytes * 8 = 512 bits + char *hexresult = NULL; + int hexa_size; + int ret = 0; + int epoch_min; + + if (payload_len == 0) + return 0; + + /* + * hexa: 4bits + * ascii: 8bits + * hexa = ascii * 2 + */ + hexa_size = crypto.size * 2; + + /* + 1 cause we MUST add NULL in the payload */ + if (payload_len != hexa_size + 1) + return 0; + + hexresult = kmalloc(sizeof(char) * hexa_size, GFP_ATOMIC); + if (hexresult == NULL) { + printk(KERN_ERR PKNOCK "kmalloc() error in has_secret().\n"); + return 0; + } + + memset(result, 0, 64); + memset(hexresult, 0, (sizeof(char) * hexa_size)); + + epoch_min = get_epoch_minute(); + + sg_set_buf(&sg[0], &ipsrc, sizeof(ipsrc)); + sg_set_buf(&sg[1], &epoch_min, sizeof(epoch_min)); + + ret = crypto_hash_setkey(crypto.tfm, secret, secret_len); + if (ret) { + printk("crypto_hash_setkey() failed ret=%d\n", ret); + return ret; + } + + /* + * The third parameter is the number of bytes INSIDE the sg! + * 4 bytes IP (32 bits) + + * 4 bytes int epoch_min (32 bits) + */ + ret = crypto_hash_digest(&crypto.desc, sg, 8, result); + if (ret) { + printk("crypto_hash_digest() failed ret=%d\n", ret); + return ret; + } + + crypt_to_hex(hexresult, result, crypto.size); + + if (memcmp(hexresult, payload, hexa_size) != 0) { +#if DEBUG + printk(KERN_INFO PKNOCK "secret match failed\n"); +#endif + goto out; + } + + ret = 1; + +out: + if (hexresult != NULL) kfree(hexresult); + return ret; +} + +/** + * If the peer pass the security policy. + * + * @peer + * @info + * @payload + * @payload_len + * @return: 1 if pass security, 0 otherwise + */ +static bool +pass_security(struct peer *peer, const struct ipt_pknock *info, + unsigned char *payload, int payload_len) +{ + if (is_allowed(peer)) + return true; + + /* The peer can't log more than once during the same minute. */ + if (has_logged_during_this_minute(peer)) { + DEBUGP("BLOCKED", peer); + return false; + } + /* Check for OPEN secret */ + if (!has_secret((unsigned char *)info->open_secret, + (int)info->open_secret_len, htonl(peer->ip), + payload, payload_len)) + { + return false; + } + return true; +} + +/** + * It updates the peer matching status. + * + * @peer + * @info + * @rule + * @hdr + * @return: 1 if allowed, 0 otherwise + */ +static bool +update_peer(struct peer *peer, const struct ipt_pknock *info, + struct ipt_pknock_rule *rule, + const struct transport_data *hdr) +{ + unsigned long time; + + if (is_wrong_knock(peer, info, hdr->port)) { + DEBUGP("DIDN'T MATCH", peer); + /* Peer must start the sequence from scratch. */ + if (info->option & IPT_PKNOCK_STRICT) + reset_knock_status(peer); + + return false; + } + + /* If security is needed. */ + if (info->option & IPT_PKNOCK_OPENSECRET ) { + if (hdr->proto != IPPROTO_UDP) + return false; + + if (!pass_security(peer, info, hdr->payload, hdr->payload_len)) { + return false; + } + } + + /* Just update the timer when there is a state change. */ + update_rule_timer(rule); + + peer->id_port_knocked++; + + if (is_last_knock(peer, info)) { + peer->status = ST_ALLOWED; + + DEBUGP("ALLOWED", peer); + + if (nl_multicast_group > 0) + msg_to_userspace_nl(info, peer, nl_multicast_group); + + peer->login_min = get_epoch_minute(); + return true; + } + + /* Controls the max matching time between ports. */ + if (info->option & IPT_PKNOCK_TIME) { + time = jiffies/HZ; + + if (is_time_exceeded(peer, info->max_time)) { +#if DEBUG + DEBUGP("TIME EXCEEDED", peer); + DEBUGP("DESTROYED", peer); + printk(KERN_INFO PKNOCK "max_time: %ld - time: %ld\n", + peer->timestamp + info->max_time, + time); +#endif + remove_peer(peer); + return false; + } + peer->timestamp = time; + } + DEBUGP("MATCHING", peer); + peer->status = ST_MATCHING; + return false; +} + +/** + * Make the peer no more ALLOWED sending a payload with a special secret for + * closure. + * + * @peer + * @info + * @payload + * @payload_len + * @return: 1 if close knock, 0 otherwise + */ +static inline bool +is_close_knock(const struct peer *peer, const struct ipt_pknock *info, + unsigned char *payload, int payload_len) +{ + /* Check for CLOSE secret. */ + if (has_secret((unsigned char *)info->close_secret, + (int)info->close_secret_len, htonl(peer->ip), + payload, payload_len)) + { + DEBUGP("RESET", peer); + return true; + } + return false; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) +static bool +#else +static int +#endif +match(const struct sk_buff *skb, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) + const struct xt_match_param *par +#else + const struct net_device *in, + const struct net_device *out, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) + const struct xt_match *match, +#endif + const void *matchinfo, + int offset, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) + unsigned int protoff, +#endif + bool *hotdrop +#endif +) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) + const struct ipt_pknock *info = par->matchinfo; +#else + const struct ipt_pknock *info = matchinfo; +#endif + struct ipt_pknock_rule *rule = NULL; + struct peer *peer = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + struct iphdr *iph = ip_hdr(skb); +#else + struct iphdr *iph = skb->nh.iph; +#endif + int hdr_len = 0; + __be16 _ports[2], *pptr = NULL; + struct transport_data hdr = {0, 0, 0, NULL}; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) + bool ret = false; +#else + int ret = 0; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) + pptr = skb_header_pointer(skb, par->thoff, sizeof _ports, &_ports); +#else + pptr = skb_header_pointer(skb, protoff, sizeof _ports, &_ports); +#endif + + if (pptr == NULL) { + /* We've been asked to examine this packet, and we + * can't. Hence, no choice but to drop. + */ + duprintf("Dropping evil offset=0 tinygram.\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) + *par->hotdrop = true; +#else + *hotdrop = true; +#endif + return false; +#else + *hotdrop = 1; + return 0; +#endif + } + + hdr.port = ntohs(pptr[1]); + + switch ((hdr.proto = iph->protocol)) { + case IPPROTO_TCP: + break; + + case IPPROTO_UDP: + hdr_len = (iph->ihl * 4) + sizeof(struct udphdr); + break; + + default: + printk(KERN_INFO PKNOCK + "IP payload protocol is neither tcp nor udp.\n"); + return false; + } + + spin_lock_bh(&list_lock); + + /* Searches a rule from the list depending on info structure options. */ + if ((rule = search_rule(info)) == NULL) { + printk(KERN_INFO PKNOCK "The rule %s doesn't exist.\n", + info->rule_name); + goto out; + } + + /* Gives the peer matching status added to rule depending on ip src. */ + peer = get_peer(rule, iph->saddr); + + if (info->option & IPT_PKNOCK_CHECKIP) { + ret = is_allowed(peer); + goto out; + } + + if (iph->protocol == IPPROTO_UDP) { + hdr.payload = (void *)iph + hdr_len; + hdr.payload_len = skb->len - hdr_len; + } + + /* Sets, updates, removes or checks the peer matching status. */ + if (info->option & IPT_PKNOCK_KNOCKPORT) { + if ((ret = is_allowed(peer))) { + if (info->option & IPT_PKNOCK_CLOSESECRET && + iph->protocol == IPPROTO_UDP) + { + if (is_close_knock(peer, info, hdr.payload, hdr.payload_len)) + { + reset_knock_status(peer); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) + ret = false; +#else + ret = 0; +#endif + } + } + goto out; + } + + if (is_first_knock(peer, info, hdr.port)) { + peer = new_peer(iph->saddr, iph->protocol); + add_peer(peer, rule); + } + + if (peer == NULL) goto out; + + update_peer(peer, info, rule, &hdr); + } + +out: +#if DEBUG + if (ret) DEBUGP("PASS OK", peer); +#endif + spin_unlock_bh(&list_lock); + return ret; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) +#define RETURN_ERR(err) do { printk(KERN_ERR PKNOCK err); return false; } while (0) +#else +#define RETURN_ERR(err) do { printk(KERN_ERR PKNOCK err); return 0; } while (0) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) +static bool +#else +static int +#endif +checkentry( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) + const struct xt_mtchk_param *par +#else + const char *tablename, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) + const void *ip, +#else + const struct ipt_ip *ip, +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) + const struct xt_match *match, +#endif + void *matchinfo, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) + unsigned int matchsize, +#endif + unsigned int hook_mask +#endif +) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) + struct ipt_pknock *info = par->matchinfo; +#else + struct ipt_pknock *info = matchinfo; +#endif + + /* Singleton. */ + if (!rule_hashtable) { + if (!(rule_hashtable = alloc_hashtable(rule_hashsize))) + RETURN_ERR("alloc_hashtable() error in checkentry()\n"); + + get_random_bytes(&ipt_pknock_hash_rnd, sizeof (ipt_pknock_hash_rnd)); + } + + if (!add_rule(info)) + RETURN_ERR("add_rule() error in checkentry() function.\n"); + + if (!(info->option & IPT_PKNOCK_NAME)) + RETURN_ERR("You must specify --name option.\n"); + + if ((info->option & IPT_PKNOCK_OPENSECRET) && (info->ports_count != 1)) + RETURN_ERR("--opensecret must have just one knock port\n"); + + if (info->option & IPT_PKNOCK_KNOCKPORT) { + if (info->option & IPT_PKNOCK_CHECKIP) { + RETURN_ERR("Can't specify --knockports with --checkip.\n"); + } + if ((info->option & IPT_PKNOCK_OPENSECRET) && + !(info->option & IPT_PKNOCK_CLOSESECRET)) + { + RETURN_ERR("--opensecret must go with --closesecret.\n"); + } + if ((info->option & IPT_PKNOCK_CLOSESECRET) && + !(info->option & IPT_PKNOCK_OPENSECRET)) + { + RETURN_ERR("--closesecret must go with --opensecret.\n"); + } + } + + if (info->option & IPT_PKNOCK_CHECKIP) { + if (info->option & IPT_PKNOCK_KNOCKPORT) + { + RETURN_ERR("Can't specify --checkip with --knockports.\n"); + } + if ((info->option & IPT_PKNOCK_OPENSECRET) || + (info->option & IPT_PKNOCK_CLOSESECRET)) + { + RETURN_ERR("Can't specify --opensecret and --closesecret" + " with --checkip.\n"); + } + if (info->option & IPT_PKNOCK_TIME) + RETURN_ERR("Can't specify --time with --checkip.\n"); + } + + if (info->option & IPT_PKNOCK_OPENSECRET) { + if (info->open_secret_len == info->close_secret_len) { + if (memcmp(info->open_secret, info->close_secret, + info->open_secret_len) == 0) + { + RETURN_ERR("opensecret & closesecret cannot be equal.\n"); + } + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) + return true; +#else + return 1; +#endif +} + +static void +destroy( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) + const struct xt_mtdtor_param *par +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) + const struct xt_match *match, void *matchinfo +#else + void *matchinfo, unsigned int matchsize +#endif +#endif +) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) + struct ipt_pknock *info = par->matchinfo; +#else + struct ipt_pknock *info = matchinfo; +#endif + /* Removes a rule only if it exits and ref_count is equal to 0. */ + remove_rule(info); +} + +static struct xt_match ipt_pknock_match __read_mostly = { + .name = "pknock", +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) + .family = NFPROTO_IPV4, +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) + .matchsize = sizeof (struct ipt_pknock), +#endif + .match = match, + .checkentry = checkentry, + .destroy = destroy, + .me = THIS_MODULE +}; + +static int __init ipt_pknock_init(void) +{ + printk(KERN_INFO PKNOCK "register.\n"); + + if (request_module(crypto.algo) < 0) { + printk(KERN_ERR PKNOCK "request_module('%s') error.\n", + crypto.algo); + return -1; + } + + crypto.tfm = crypto_alloc_hash(crypto.algo, 0, CRYPTO_ALG_ASYNC); + + if (crypto.tfm == NULL) { + printk(KERN_ERR PKNOCK "failed to load transform for %s\n", + crypto.algo); + return -1; + } + + crypto.size = crypto_hash_digestsize(crypto.tfm); + crypto.desc.tfm = crypto.tfm; + crypto.desc.flags = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + if (!(pde = proc_mkdir("ipt_pknock", init_net.proc_net))) { +#else + if (!(pde = proc_mkdir("ipt_pknock", proc_net))) { +#endif + printk(KERN_ERR PKNOCK "proc_mkdir() error in _init().\n"); + return -1; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) + return xt_register_match(&ipt_pknock_match); +#else + return ipt_register_match(&ipt_pknock_match); +#endif +} + +static void __exit ipt_pknock_fini(void) +{ + printk(KERN_INFO PKNOCK "unregister.\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + remove_proc_entry("ipt_pknock", init_net.proc_net); +#else + remove_proc_entry("ipt_pknock", proc_net); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) + xt_unregister_match(&ipt_pknock_match); +#else + ipt_unregister_match(&ipt_pknock_match); +#endif + kfree(rule_hashtable); + + if (crypto.tfm != NULL) crypto_free_hash(crypto.tfm); +} + +module_init(ipt_pknock_init); +module_exit(ipt_pknock_fini); diff --git a/extensions/xt_pknock.h b/extensions/xt_pknock.h new file mode 100644 index 0000000..3d0f467 --- /dev/null +++ b/extensions/xt_pknock.h @@ -0,0 +1,94 @@ +/* + * Kernel module to implement Port Knocking and SPA matching support. + * + * (C) 2006-2008 J. Federico Hernandez + * (C) 2006 Luis Floreani + * + * $Id$ + * + * This program is released under the terms of GNU GPL version 2. + */ +#ifndef _IPT_PKNOCK_H +#define _IPT_PKNOCK_H + +#define PKNOCK "ipt_pknock: " + +#define IPT_PKNOCK_KNOCKPORT 0x01 +#define IPT_PKNOCK_TIME 0x02 +#define IPT_PKNOCK_NAME 0x04 +#define IPT_PKNOCK_STRICT 0x08 +#define IPT_PKNOCK_CHECKIP 0x10 +#define IPT_PKNOCK_OPENSECRET 0x20 +#define IPT_PKNOCK_CLOSESECRET 0x40 + +#define IPT_PKNOCK_MAX_PORTS 15 +#define IPT_PKNOCK_MAX_BUF_LEN 32 +#define IPT_PKNOCK_MAX_PASSWD_LEN 32 + +#define DEBUG 1 + +struct ipt_pknock { + char rule_name[IPT_PKNOCK_MAX_BUF_LEN + 1]; + int rule_name_len; + char open_secret[IPT_PKNOCK_MAX_PASSWD_LEN + 1]; + int open_secret_len; + char close_secret[IPT_PKNOCK_MAX_PASSWD_LEN + 1]; + int close_secret_len; + uint8_t ports_count; /* number of ports */ + uint16_t port[IPT_PKNOCK_MAX_PORTS]; /* port[,port,port,...] */ + unsigned long max_time; /* max matching time between ports */ + uint8_t option; /* --time, --knock-port, ... */ +}; + +struct ipt_pknock_nl_msg { + char rule_name[IPT_PKNOCK_MAX_BUF_LEN + 1]; + uint32_t peer_ip; +}; + +enum status {ST_INIT=1, ST_MATCHING, ST_ALLOWED}; + +#ifdef __KERNEL__ +#include +#include + +struct peer { + struct list_head head; + uint32_t ip; + uint8_t proto; + uint32_t id_port_knocked; + enum status status; + unsigned long timestamp; + int login_min; /* the login epoch minute */ +}; + +#include + +struct ipt_pknock_rule { + struct list_head head; + char rule_name[IPT_PKNOCK_MAX_BUF_LEN + 1]; + int rule_name_len; + unsigned int ref_count; + struct timer_list timer; /* garbage collector timer */ + struct list_head *peer_head; + struct proc_dir_entry *status_proc; + unsigned long max_time; /* max matching time between ports */ +}; + +#include + +struct ipt_pknock_crypto { + char *algo; + struct crypto_hash *tfm; + int size; + struct hash_desc desc; +}; + +struct transport_data { + uint8_t proto; + uint16_t port; /* destination port */ + int payload_len; + unsigned char *payload; +}; + +#endif /* __KERNEL__ */ +#endif /* _IPT_PKNOCK_H */ diff --git a/mconfig b/mconfig index cbeaa69..b10731b 100644 --- a/mconfig +++ b/mconfig @@ -21,5 +21,6 @@ build_ipset=m build_ipv4options=m build_length2=m build_lscan=m +build_pknock=m build_psd=m build_quota2=m From af2bbf03525a882db14fbfe5d08401e2452250ff Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Tue, 1 Sep 2009 19:52:48 +0200 Subject: [PATCH 02/68] pknock: fit to Xtables-addons API --- extensions/xt_pknock.c | 133 ++--------------------------------------- 1 file changed, 6 insertions(+), 127 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index da4c170..fdacad8 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -31,6 +31,7 @@ #include //#include #include "xt_pknock.h" +#include "compat_xtables.h" MODULE_LICENSE("GPL"); MODULE_AUTHOR("J. Federico Hernandez Scarso, Luis A. Floreani"); @@ -900,72 +901,25 @@ is_close_knock(const struct peer *peer, const struct ipt_pknock *info, return false; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -static bool -#else -static int -#endif -match(const struct sk_buff *skb, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) - const struct xt_match_param *par -#else - const struct net_device *in, - const struct net_device *out, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) - const struct xt_match *match, -#endif - const void *matchinfo, - int offset, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) - unsigned int protoff, -#endif - bool *hotdrop -#endif -) +static bool match(const struct sk_buff *skb, const struct xt_match_param *par) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) const struct ipt_pknock *info = par->matchinfo; -#else - const struct ipt_pknock *info = matchinfo; -#endif struct ipt_pknock_rule *rule = NULL; struct peer *peer = NULL; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) struct iphdr *iph = ip_hdr(skb); -#else - struct iphdr *iph = skb->nh.iph; -#endif int hdr_len = 0; __be16 _ports[2], *pptr = NULL; struct transport_data hdr = {0, 0, 0, NULL}; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) bool ret = false; -#else - int ret = 0; -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) pptr = skb_header_pointer(skb, par->thoff, sizeof _ports, &_ports); -#else - pptr = skb_header_pointer(skb, protoff, sizeof _ports, &_ports); -#endif - if (pptr == NULL) { /* We've been asked to examine this packet, and we * can't. Hence, no choice but to drop. */ duprintf("Dropping evil offset=0 tinygram.\n"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) *par->hotdrop = true; -#else - *hotdrop = true; -#endif return false; -#else - *hotdrop = 1; - return 0; -#endif } hdr.port = ntohs(pptr[1]); @@ -1015,11 +969,7 @@ match(const struct sk_buff *skb, if (is_close_knock(peer, info, hdr.payload, hdr.payload_len)) { reset_knock_status(peer); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ret = false; -#else - ret = 0; -#endif } } goto out; @@ -1043,43 +993,11 @@ out: return ret; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) #define RETURN_ERR(err) do { printk(KERN_ERR PKNOCK err); return false; } while (0) -#else -#define RETURN_ERR(err) do { printk(KERN_ERR PKNOCK err); return 0; } while (0) -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -static bool -#else -static int -#endif -checkentry( -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) - const struct xt_mtchk_param *par -#else - const char *tablename, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) - const void *ip, -#else - const struct ipt_ip *ip, -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) - const struct xt_match *match, -#endif - void *matchinfo, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) - unsigned int matchsize, -#endif - unsigned int hook_mask -#endif -) +static bool checkentry(const struct xt_mtchk_param *par) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) struct ipt_pknock *info = par->matchinfo; -#else - struct ipt_pknock *info = matchinfo; -#endif /* Singleton. */ if (!rule_hashtable) { @@ -1139,43 +1057,20 @@ checkentry( } } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) return true; -#else - return 1; -#endif } -static void -destroy( -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) - const struct xt_mtdtor_param *par -#else -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) - const struct xt_match *match, void *matchinfo -#else - void *matchinfo, unsigned int matchsize -#endif -#endif -) +static void destroy(const struct xt_mtdtor_param *par) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) struct ipt_pknock *info = par->matchinfo; -#else - struct ipt_pknock *info = matchinfo; -#endif /* Removes a rule only if it exits and ref_count is equal to 0. */ remove_rule(info); } static struct xt_match ipt_pknock_match __read_mostly = { .name = "pknock", -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) .family = NFPROTO_IPV4, -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) .matchsize = sizeof (struct ipt_pknock), -#endif .match = match, .checkentry = checkentry, .destroy = destroy, @@ -1204,34 +1099,18 @@ static int __init ipt_pknock_init(void) crypto.desc.tfm = crypto.tfm; crypto.desc.flags = 0; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) - if (!(pde = proc_mkdir("ipt_pknock", init_net.proc_net))) { -#else - if (!(pde = proc_mkdir("ipt_pknock", proc_net))) { -#endif + if (!(pde = proc_mkdir("ipt_pknock", init_net__proc_net))) { printk(KERN_ERR PKNOCK "proc_mkdir() error in _init().\n"); return -1; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) return xt_register_match(&ipt_pknock_match); -#else - return ipt_register_match(&ipt_pknock_match); -#endif } static void __exit ipt_pknock_fini(void) { printk(KERN_INFO PKNOCK "unregister.\n"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) - remove_proc_entry("ipt_pknock", init_net.proc_net); -#else - remove_proc_entry("ipt_pknock", proc_net); -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) + remove_proc_entry("ipt_pknock", init_net__proc_net); xt_unregister_match(&ipt_pknock_match); -#else - ipt_unregister_match(&ipt_pknock_match); -#endif kfree(rule_hashtable); if (crypto.tfm != NULL) crypto_free_hash(crypto.tfm); From b04317576bfb51df1eb347be48ca91f3e40ced9d Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Tue, 1 Sep 2009 19:52:48 +0200 Subject: [PATCH 03/68] pknock: add MODULE_ALIAS for pknock Needed for autoloading. --- extensions/xt_pknock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index fdacad8..a99e1ac 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -36,6 +36,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("J. Federico Hernandez Scarso, Luis A. Floreani"); MODULE_DESCRIPTION("netfilter match for Port Knocking and SPA"); +MODULE_ALIAS("ipt_pknock"); enum { GC_EXPIRATION_TIME = 65000, /* in msecs */ From c0dc0858de868bde56343d7bd94eb24c74644290 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Tue, 1 Sep 2009 19:52:48 +0200 Subject: [PATCH 04/68] pknock: renaming of functions and prefix-name --- extensions/xt_pknock.c | 31 ++++++++++++++++--------------- extensions/xt_pknock.h | 8 ++++---- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index a99e1ac..a833878 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -902,7 +902,8 @@ is_close_knock(const struct peer *peer, const struct ipt_pknock *info, return false; } -static bool match(const struct sk_buff *skb, const struct xt_match_param *par) +static bool pknock_mt(const struct sk_buff *skb, + const struct xt_match_param *par) { const struct ipt_pknock *info = par->matchinfo; struct ipt_pknock_rule *rule = NULL; @@ -996,7 +997,7 @@ out: #define RETURN_ERR(err) do { printk(KERN_ERR PKNOCK err); return false; } while (0) -static bool checkentry(const struct xt_mtchk_param *par) +static bool pknock_mt_check(const struct xt_mtchk_param *par) { struct ipt_pknock *info = par->matchinfo; @@ -1061,24 +1062,24 @@ static bool checkentry(const struct xt_mtchk_param *par) return true; } -static void destroy(const struct xt_mtdtor_param *par) +static void pknock_mt_destroy(const struct xt_mtdtor_param *par) { struct ipt_pknock *info = par->matchinfo; /* Removes a rule only if it exits and ref_count is equal to 0. */ remove_rule(info); } -static struct xt_match ipt_pknock_match __read_mostly = { +static struct xt_match xt_pknock_mt_reg __read_mostly = { .name = "pknock", .family = NFPROTO_IPV4, .matchsize = sizeof (struct ipt_pknock), - .match = match, - .checkentry = checkentry, - .destroy = destroy, + .match = pknock_mt, + .checkentry = pknock_mt_check, + .destroy = pknock_mt_destroy, .me = THIS_MODULE }; -static int __init ipt_pknock_init(void) +static int __init xt_pknock_mt_init(void) { printk(KERN_INFO PKNOCK "register.\n"); @@ -1100,22 +1101,22 @@ static int __init ipt_pknock_init(void) crypto.desc.tfm = crypto.tfm; crypto.desc.flags = 0; - if (!(pde = proc_mkdir("ipt_pknock", init_net__proc_net))) { + if (!(pde = proc_mkdir("xt_pknock", init_net__proc_net))) { printk(KERN_ERR PKNOCK "proc_mkdir() error in _init().\n"); return -1; } - return xt_register_match(&ipt_pknock_match); + return xt_register_match(&xt_pknock_mt_reg); } -static void __exit ipt_pknock_fini(void) +static void __exit xt_pknock_mt_exit(void) { printk(KERN_INFO PKNOCK "unregister.\n"); - remove_proc_entry("ipt_pknock", init_net__proc_net); - xt_unregister_match(&ipt_pknock_match); + remove_proc_entry("xt_pknock", init_net__proc_net); + xt_unregister_match(&xt_pknock_mt_reg); kfree(rule_hashtable); if (crypto.tfm != NULL) crypto_free_hash(crypto.tfm); } -module_init(ipt_pknock_init); -module_exit(ipt_pknock_fini); +module_init(xt_pknock_mt_init); +module_exit(xt_pknock_mt_exit); diff --git a/extensions/xt_pknock.h b/extensions/xt_pknock.h index 3d0f467..57f036a 100644 --- a/extensions/xt_pknock.h +++ b/extensions/xt_pknock.h @@ -8,10 +8,10 @@ * * This program is released under the terms of GNU GPL version 2. */ -#ifndef _IPT_PKNOCK_H -#define _IPT_PKNOCK_H +#ifndef _XT_PKNOCK_H +#define _XT_PKNOCK_H -#define PKNOCK "ipt_pknock: " +#define PKNOCK "xt_pknock: " #define IPT_PKNOCK_KNOCKPORT 0x01 #define IPT_PKNOCK_TIME 0x02 @@ -91,4 +91,4 @@ struct transport_data { }; #endif /* __KERNEL__ */ -#endif /* _IPT_PKNOCK_H */ +#endif /* _XT_PKNOCK_H */ From afbeb2fb47f132e62fa0377283db32a8a7b35a96 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 29 Sep 2009 15:17:26 +0200 Subject: [PATCH 05/68] pknock: improve sizeof(struct ipt_pknock) by using "even" field sizes The field sizes were 33, which makes for lots of padding holes. --- extensions/xt_pknock.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/xt_pknock.h b/extensions/xt_pknock.h index 57f036a..e5051d3 100644 --- a/extensions/xt_pknock.h +++ b/extensions/xt_pknock.h @@ -22,8 +22,8 @@ #define IPT_PKNOCK_CLOSESECRET 0x40 #define IPT_PKNOCK_MAX_PORTS 15 -#define IPT_PKNOCK_MAX_BUF_LEN 32 -#define IPT_PKNOCK_MAX_PASSWD_LEN 32 +#define IPT_PKNOCK_MAX_BUF_LEN 31 +#define IPT_PKNOCK_MAX_PASSWD_LEN 31 #define DEBUG 1 From cee3d25d493e2ddbb9aa2888e3ce35d628c17af4 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 29 Sep 2009 19:07:44 +0200 Subject: [PATCH 06/68] pknock: improve sizeof(struct ipt_pknock) by relayouting members Stuffing struct holes as reported by pahole(1). --- extensions/xt_pknock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/xt_pknock.h b/extensions/xt_pknock.h index e5051d3..171b34f 100644 --- a/extensions/xt_pknock.h +++ b/extensions/xt_pknock.h @@ -34,10 +34,10 @@ struct ipt_pknock { int open_secret_len; char close_secret[IPT_PKNOCK_MAX_PASSWD_LEN + 1]; int close_secret_len; + uint8_t option; /* --time, --knock-port, ... */ uint8_t ports_count; /* number of ports */ uint16_t port[IPT_PKNOCK_MAX_PORTS]; /* port[,port,port,...] */ unsigned long max_time; /* max matching time between ports */ - uint8_t option; /* --time, --knock-port, ... */ }; struct ipt_pknock_nl_msg { From d8dc72d151c5d76f17f991430223c3ca44a1322d Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 29 Sep 2009 19:12:13 +0200 Subject: [PATCH 07/68] pknock: only use size-fixated types Types with variadic size are a no-no. --- extensions/libxt_pknock.c | 4 ++-- extensions/xt_pknock.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index 581e5bb..3e671a9 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -296,7 +296,7 @@ static void pknock_print(const void *ip, printf(" "); } if (info->option & IPT_PKNOCK_TIME) - printf("time %ld ", info->max_time); + printf("time %ld ", (long)info->max_time); if (info->option & IPT_PKNOCK_NAME) printf("name %s ", info->rule_name); if (info->option & IPT_PKNOCK_OPENSECRET) @@ -318,7 +318,7 @@ static void pknock_save(const void *ip, const struct xt_entry_match *match) printf(" "); } if (info->option & IPT_PKNOCK_TIME) - printf("--time %ld ", info->max_time); + printf("--time %ld ", (long)info->max_time); if (info->option & IPT_PKNOCK_NAME) printf("--name %s ", info->rule_name); if (info->option & IPT_PKNOCK_OPENSECRET) diff --git a/extensions/xt_pknock.h b/extensions/xt_pknock.h index 171b34f..a798edb 100644 --- a/extensions/xt_pknock.h +++ b/extensions/xt_pknock.h @@ -29,15 +29,15 @@ struct ipt_pknock { char rule_name[IPT_PKNOCK_MAX_BUF_LEN + 1]; - int rule_name_len; + uint32_t rule_name_len; char open_secret[IPT_PKNOCK_MAX_PASSWD_LEN + 1]; - int open_secret_len; + uint32_t open_secret_len; char close_secret[IPT_PKNOCK_MAX_PASSWD_LEN + 1]; - int close_secret_len; + uint32_t close_secret_len; uint8_t option; /* --time, --knock-port, ... */ uint8_t ports_count; /* number of ports */ uint16_t port[IPT_PKNOCK_MAX_PORTS]; /* port[,port,port,...] */ - unsigned long max_time; /* max matching time between ports */ + uint32_t max_time; /* max matching time between ports */ }; struct ipt_pknock_nl_msg { From 75f80fa8f8001930386d66defacc3df9e87daedb Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 29 Sep 2009 19:32:18 +0200 Subject: [PATCH 08/68] pknock: move struct ipt_pknock_crypto to xt_pknock.c It is only used in xt_pknock.c, and only once, so also make it an anonymous struct. --- extensions/xt_pknock.c | 7 ++++++- extensions/xt_pknock.h | 9 --------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index a833878..6bb13ca 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -70,7 +70,12 @@ static struct proc_dir_entry *pde = NULL; static DEFINE_SPINLOCK(list_lock); -static struct ipt_pknock_crypto crypto = { +static struct { + char *algo; + struct crypto_hash *tfm; + int size; + struct hash_desc desc; +} crypto = { .algo = "hmac(sha256)", .tfm = NULL, .size = 0 diff --git a/extensions/xt_pknock.h b/extensions/xt_pknock.h index a798edb..4b542a4 100644 --- a/extensions/xt_pknock.h +++ b/extensions/xt_pknock.h @@ -74,15 +74,6 @@ struct ipt_pknock_rule { unsigned long max_time; /* max matching time between ports */ }; -#include - -struct ipt_pknock_crypto { - char *algo; - struct crypto_hash *tfm; - int size; - struct hash_desc desc; -}; - struct transport_data { uint8_t proto; uint16_t port; /* destination port */ From 45515a0afd611e98bae838e11628b4445179d777 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 29 Sep 2009 19:38:53 +0200 Subject: [PATCH 09/68] pknock: bump internal match revision number Do this so that previous iptables installations do not mistakenly operate with our new xt_pknock. --- extensions/libxt_pknock.c | 1 + extensions/xt_pknock.c | 1 + 2 files changed, 2 insertions(+) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index 3e671a9..3e5f044 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -334,6 +334,7 @@ static void pknock_save(const void *ip, const struct xt_entry_match *match) static struct xtables_match pknock_match = { .name = "pknock", .version = XTABLES_VERSION, + .revision = 1, .family = AF_INET, .size = XT_ALIGN(sizeof (struct ipt_pknock)), .userspacesize = XT_ALIGN(sizeof (struct ipt_pknock)), diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 6bb13ca..15e043e 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -1076,6 +1076,7 @@ static void pknock_mt_destroy(const struct xt_mtdtor_param *par) static struct xt_match xt_pknock_mt_reg __read_mostly = { .name = "pknock", + .revision = 1, .family = NFPROTO_IPV4, .matchsize = sizeof (struct ipt_pknock), .match = pknock_mt, From 9928c864ab83210faabd0f68901aec653ab71707 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 29 Sep 2009 19:42:36 +0200 Subject: [PATCH 10/68] pknock: drop evil-tinygram warning messages The duprintf macro then also becomes unneeded. --- extensions/xt_pknock.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 15e043e..1d3fb08 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -52,10 +52,8 @@ enum { #define DEBUGP(msg, peer) printk(KERN_INFO PKNOCK \ "(S) peer: %u.%u.%u.%u - %s.\n", \ NIPQUAD((peer)->ip), msg) - #define duprintf(format, args...) printk(format, ## args); #else #define DEBUGP(msg, peer) - #define duprintf(format, args...) #endif static uint32_t ipt_pknock_hash_rnd; @@ -924,7 +922,6 @@ static bool pknock_mt(const struct sk_buff *skb, /* We've been asked to examine this packet, and we * can't. Hence, no choice but to drop. */ - duprintf("Dropping evil offset=0 tinygram.\n"); *par->hotdrop = true; return false; } From 1de82a88a154a7856506f5644795aa165294c9bc Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 29 Sep 2009 22:58:35 +0200 Subject: [PATCH 11/68] pknock: avoid accidental deletion of rules If DEBUG was not defined, and no rule was effectively found, the "rule" variable may still be non-NULL, and possibly cause deletion. Therefore, always check for the rule having been found. --- extensions/xt_pknock.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 1d3fb08..d2bae1c 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -451,9 +451,7 @@ remove_rule(struct ipt_pknock *info) struct list_head *pos = NULL, *n = NULL; struct peer *peer = NULL; int i; -#if DEBUG int found = 0; -#endif int hash = pknock_hash(info->rule_name, info->rule_name_len, ipt_pknock_hash_rnd, rule_hashsize); @@ -463,19 +461,17 @@ remove_rule(struct ipt_pknock *info) rule = list_entry(pos, struct ipt_pknock_rule, head); if (rulecmp(info, rule) == 0) { -#if DEBUG found = 1; -#endif rule->ref_count--; break; } } -#if DEBUG if (!found) { +#if DEBUG printk(KERN_INFO PKNOCK "(N) rule not found: %s.\n", info->rule_name); +#endif return; } -#endif if (rule && rule->ref_count == 0) { hashtable_for_each_safe(pos, n, rule->peer_head, peer_hashsize, i) { peer = list_entry(pos, struct peer, head); From 19067393b622aa25bb537b5eec76822c82e8e050 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Tue, 1 Sep 2009 19:52:48 +0200 Subject: [PATCH 12/68] pknock: replace printks by pr_debug The printk calls were protected using #if DEBUG anyway, so they can just be changed to pr_debug. pr_debug also depends on #if DEBUG, and takes care of the module prefix. --- extensions/xt_pknock.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index d2bae1c..817e36a 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -8,6 +8,7 @@ * * This program is released under the terms of GNU GPL version 2. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include @@ -53,7 +54,7 @@ enum { "(S) peer: %u.%u.%u.%u - %s.\n", \ NIPQUAD((peer)->ip), msg) #else - #define DEBUGP(msg, peer) + #define DEBUGP(msg, peer) do {} while (false); #endif static uint32_t ipt_pknock_hash_rnd; @@ -385,15 +386,13 @@ add_rule(struct ipt_pknock *info) if (rulecmp(info, rule) == 0) { rule->ref_count++; -#if DEBUG if (info->option & IPT_PKNOCK_CHECKIP) { - printk(KERN_DEBUG PKNOCK "add_rule() (AC)" + pr_debug("add_rule() (AC)" " rule found: %s - " "ref_count: %d\n", rule->rule_name, rule->ref_count); } -#endif return true; } } @@ -433,9 +432,7 @@ add_rule(struct ipt_pknock *info) rule->status_proc->data = rule; list_add(&rule->head, &rule_hashtable[hash]); -#if DEBUG - printk(KERN_INFO PKNOCK "(A) rule_name: %s - created.\n", rule->rule_name); -#endif + pr_debug("(A) rule_name: %s - created.\n", rule->rule_name); return true; } @@ -467,9 +464,7 @@ remove_rule(struct ipt_pknock *info) } } if (!found) { -#if DEBUG - printk(KERN_INFO PKNOCK "(N) rule not found: %s.\n", info->rule_name); -#endif + pr_debug("(N) rule not found: %s.\n", info->rule_name); return; } if (rule && rule->ref_count == 0) { @@ -485,9 +480,7 @@ remove_rule(struct ipt_pknock *info) if (rule->status_proc) remove_proc_entry(info->rule_name, pde); -#if DEBUG - printk(KERN_INFO PKNOCK "(D) rule deleted: %s.\n", rule->rule_name); -#endif + pr_debug("(D) rule deleted: %s.\n", rule->rule_name); if (timer_pending(&rule->timer)) del_timer(&rule->timer); @@ -758,9 +751,7 @@ has_secret(unsigned char *secret, int secret_len, uint32_t ipsrc, crypt_to_hex(hexresult, result, crypto.size); if (memcmp(hexresult, payload, hexa_size) != 0) { -#if DEBUG - printk(KERN_INFO PKNOCK "secret match failed\n"); -#endif + pr_debug("secret match failed\n"); goto out; } @@ -859,13 +850,11 @@ update_peer(struct peer *peer, const struct ipt_pknock *info, time = jiffies/HZ; if (is_time_exceeded(peer, info->max_time)) { -#if DEBUG DEBUGP("TIME EXCEEDED", peer); DEBUGP("DESTROYED", peer); - printk(KERN_INFO PKNOCK "max_time: %ld - time: %ld\n", + pr_debug("max_time: %ld - time: %ld\n", peer->timestamp + info->max_time, time); -#endif remove_peer(peer); return false; } @@ -986,9 +975,8 @@ static bool pknock_mt(const struct sk_buff *skb, } out: -#if DEBUG - if (ret) DEBUGP("PASS OK", peer); -#endif + if (ret != 0) + DEBUGP("PASS OK", peer); spin_unlock_bh(&list_lock); return ret; } From e5ffb39750a5acd91a738a44b01e3d4529c2c23a Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 29 Sep 2009 23:06:40 +0200 Subject: [PATCH 13/68] pknock: rename DEBUGP to pk_debug and always enable Since pr_debug depends on #if DEBUG anyway, there is no need to contain DEBUGP/pk_debug within a further if. Rename from DEBUGP to pk_debug, because DEBUGP is flagged by Xtables-addons (- and that's good so, because often DEBUGPs should have been pr_debug instead). --- extensions/xt_pknock.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 817e36a..72f81f6 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -49,13 +49,9 @@ enum { for ((i) = 0; (i) < (size); (i)++) \ list_for_each_safe((pos), (n), (&head[(i)])) -#if DEBUG - #define DEBUGP(msg, peer) printk(KERN_INFO PKNOCK \ +#define pk_debug(msg, peer) pr_debug( \ "(S) peer: %u.%u.%u.%u - %s.\n", \ NIPQUAD((peer)->ip), msg) -#else - #define DEBUGP(msg, peer) do {} while (false); -#endif static uint32_t ipt_pknock_hash_rnd; @@ -321,7 +317,7 @@ peer_gc(unsigned long r) if (!has_logged_during_this_minute(peer) && is_time_exceeded(peer, rule->max_time)) { - DEBUGP("DESTROYED", peer); + pk_debug("DESTROYED", peer); list_del(pos); kfree(peer); } @@ -472,7 +468,7 @@ remove_rule(struct ipt_pknock *info) peer = list_entry(pos, struct peer, head); if (peer != NULL) { - DEBUGP("DELETED", peer); + pk_debug("DELETED", peer); list_del(pos); kfree(peer); } @@ -780,7 +776,7 @@ pass_security(struct peer *peer, const struct ipt_pknock *info, /* The peer can't log more than once during the same minute. */ if (has_logged_during_this_minute(peer)) { - DEBUGP("BLOCKED", peer); + pk_debug("BLOCKED", peer); return false; } /* Check for OPEN secret */ @@ -810,7 +806,7 @@ update_peer(struct peer *peer, const struct ipt_pknock *info, unsigned long time; if (is_wrong_knock(peer, info, hdr->port)) { - DEBUGP("DIDN'T MATCH", peer); + pk_debug("DIDN'T MATCH", peer); /* Peer must start the sequence from scratch. */ if (info->option & IPT_PKNOCK_STRICT) reset_knock_status(peer); @@ -836,7 +832,7 @@ update_peer(struct peer *peer, const struct ipt_pknock *info, if (is_last_knock(peer, info)) { peer->status = ST_ALLOWED; - DEBUGP("ALLOWED", peer); + pk_debug("ALLOWED", peer); if (nl_multicast_group > 0) msg_to_userspace_nl(info, peer, nl_multicast_group); @@ -850,8 +846,8 @@ update_peer(struct peer *peer, const struct ipt_pknock *info, time = jiffies/HZ; if (is_time_exceeded(peer, info->max_time)) { - DEBUGP("TIME EXCEEDED", peer); - DEBUGP("DESTROYED", peer); + pk_debug("TIME EXCEEDED", peer); + pk_debug("DESTROYED", peer); pr_debug("max_time: %ld - time: %ld\n", peer->timestamp + info->max_time, time); @@ -860,7 +856,7 @@ update_peer(struct peer *peer, const struct ipt_pknock *info, } peer->timestamp = time; } - DEBUGP("MATCHING", peer); + pk_debug("MATCHING", peer); peer->status = ST_MATCHING; return false; } @@ -884,7 +880,7 @@ is_close_knock(const struct peer *peer, const struct ipt_pknock *info, (int)info->close_secret_len, htonl(peer->ip), payload, payload_len)) { - DEBUGP("RESET", peer); + pk_debug("RESET", peer); return true; } return false; @@ -976,7 +972,7 @@ static bool pknock_mt(const struct sk_buff *skb, out: if (ret != 0) - DEBUGP("PASS OK", peer); + pk_debug("PASS OK", peer); spin_unlock_bh(&list_lock); return ret; } From 713c31e8f7b6239ebefd12fc39d11040d55cac02 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Tue, 1 Sep 2009 19:52:48 +0200 Subject: [PATCH 14/68] pknock: guard off crypto code from kernels before 2.6.19 --- extensions/xt_pknock.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 72f81f6..87653a3 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -34,6 +34,10 @@ #include "xt_pknock.h" #include "compat_xtables.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) +# define PK_CRYPTO 1 +#endif + MODULE_LICENSE("GPL"); MODULE_AUTHOR("J. Federico Hernandez Scarso, Luis A. Floreani"); MODULE_DESCRIPTION("netfilter match for Port Knocking and SPA"); @@ -65,6 +69,7 @@ static struct proc_dir_entry *pde = NULL; static DEFINE_SPINLOCK(list_lock); +#ifdef PK_CRYPTO static struct { char *algo; struct crypto_hash *tfm; @@ -75,6 +80,7 @@ static struct { .tfm = NULL, .size = 0 }; +#endif module_param(rule_hashsize, int, S_IRUGO); module_param(peer_hashsize, int, S_IRUGO); @@ -660,6 +666,7 @@ msg_to_userspace_nl(const struct ipt_pknock *info, return true; } +#ifdef PK_CRYPTO /** * Transforms a sequence of characters to hexadecimal. * @@ -788,6 +795,7 @@ pass_security(struct peer *peer, const struct ipt_pknock *info, } return true; } +#endif /* PK_CRYPTO */ /** * It updates the peer matching status. @@ -814,6 +822,7 @@ update_peer(struct peer *peer, const struct ipt_pknock *info, return false; } +#ifdef PK_CRYPTO /* If security is needed. */ if (info->option & IPT_PKNOCK_OPENSECRET ) { if (hdr->proto != IPPROTO_UDP) @@ -823,6 +832,7 @@ update_peer(struct peer *peer, const struct ipt_pknock *info, return false; } } +#endif /* Just update the timer when there is a state change. */ update_rule_timer(rule); @@ -861,6 +871,7 @@ update_peer(struct peer *peer, const struct ipt_pknock *info, return false; } +#ifdef PK_CRYPTO /** * Make the peer no more ALLOWED sending a payload with a special secret for * closure. @@ -885,6 +896,7 @@ is_close_knock(const struct peer *peer, const struct ipt_pknock *info, } return false; } +#endif /* PK_CRYPTO */ static bool pknock_mt(const struct sk_buff *skb, const struct xt_match_param *par) @@ -914,8 +926,13 @@ static bool pknock_mt(const struct sk_buff *skb, break; case IPPROTO_UDP: +#ifdef PK_CRYPTO hdr_len = (iph->ihl * 4) + sizeof(struct udphdr); break; +#else + pr_debug("UDP protocol not supported\n"); + return false; +#endif default: printk(KERN_INFO PKNOCK @@ -948,6 +965,7 @@ static bool pknock_mt(const struct sk_buff *skb, /* Sets, updates, removes or checks the peer matching status. */ if (info->option & IPT_PKNOCK_KNOCKPORT) { if ((ret = is_allowed(peer))) { +#ifdef PK_CRYPTO if (info->option & IPT_PKNOCK_CLOSESECRET && iph->protocol == IPPROTO_UDP) { @@ -957,6 +975,7 @@ static bool pknock_mt(const struct sk_buff *skb, ret = false; } } +#endif goto out; } @@ -997,13 +1016,16 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) if (!(info->option & IPT_PKNOCK_NAME)) RETURN_ERR("You must specify --name option.\n"); +#ifdef PK_CRYPTO if ((info->option & IPT_PKNOCK_OPENSECRET) && (info->ports_count != 1)) RETURN_ERR("--opensecret must have just one knock port\n"); +#endif if (info->option & IPT_PKNOCK_KNOCKPORT) { if (info->option & IPT_PKNOCK_CHECKIP) { RETURN_ERR("Can't specify --knockports with --checkip.\n"); } +#ifdef PK_CRYPTO if ((info->option & IPT_PKNOCK_OPENSECRET) && !(info->option & IPT_PKNOCK_CLOSESECRET)) { @@ -1014,6 +1036,7 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) { RETURN_ERR("--closesecret must go with --opensecret.\n"); } +#endif } if (info->option & IPT_PKNOCK_CHECKIP) { @@ -1021,16 +1044,19 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) { RETURN_ERR("Can't specify --checkip with --knockports.\n"); } +#ifdef PK_CRYPTO if ((info->option & IPT_PKNOCK_OPENSECRET) || (info->option & IPT_PKNOCK_CLOSESECRET)) { RETURN_ERR("Can't specify --opensecret and --closesecret" " with --checkip.\n"); } +#endif if (info->option & IPT_PKNOCK_TIME) RETURN_ERR("Can't specify --time with --checkip.\n"); } +#ifdef PK_CRYPTO if (info->option & IPT_PKNOCK_OPENSECRET) { if (info->open_secret_len == info->close_secret_len) { if (memcmp(info->open_secret, info->close_secret, @@ -1040,6 +1066,7 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) } } } +#endif return true; } @@ -1066,6 +1093,7 @@ static int __init xt_pknock_mt_init(void) { printk(KERN_INFO PKNOCK "register.\n"); +#ifdef PK_CRYPTO if (request_module(crypto.algo) < 0) { printk(KERN_ERR PKNOCK "request_module('%s') error.\n", crypto.algo); @@ -1083,6 +1111,9 @@ static int __init xt_pknock_mt_init(void) crypto.size = crypto_hash_digestsize(crypto.tfm); crypto.desc.tfm = crypto.tfm; crypto.desc.flags = 0; +#else + pr_info("No crypto support for < 2.6.19\n"); +#endif if (!(pde = proc_mkdir("xt_pknock", init_net__proc_net))) { printk(KERN_ERR PKNOCK "proc_mkdir() error in _init().\n"); @@ -1098,7 +1129,9 @@ static void __exit xt_pknock_mt_exit(void) xt_unregister_match(&xt_pknock_mt_reg); kfree(rule_hashtable); +#ifdef PK_CRYPTO if (crypto.tfm != NULL) crypto_free_hash(crypto.tfm); +#endif } module_init(xt_pknock_mt_init); From db1057158f4080406ab8d034e0497db8013bef2a Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 30 Sep 2009 00:05:42 +0200 Subject: [PATCH 15/68] pknock: return -ENXIO for problems during init Some functions like the crypto init or proc_mkdir do not return an error value. Replace the -1, which is actually EPERM, and looks a little out of place, and use ENXIO instead. (After all, the requested objects were not there after the request of initialization.) --- extensions/xt_pknock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 87653a3..0b0bcf1 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -1097,7 +1097,7 @@ static int __init xt_pknock_mt_init(void) if (request_module(crypto.algo) < 0) { printk(KERN_ERR PKNOCK "request_module('%s') error.\n", crypto.algo); - return -1; + return -ENXIO; } crypto.tfm = crypto_alloc_hash(crypto.algo, 0, CRYPTO_ALG_ASYNC); @@ -1105,7 +1105,7 @@ static int __init xt_pknock_mt_init(void) if (crypto.tfm == NULL) { printk(KERN_ERR PKNOCK "failed to load transform for %s\n", crypto.algo); - return -1; + return -ENXIO; } crypto.size = crypto_hash_digestsize(crypto.tfm); @@ -1117,7 +1117,7 @@ static int __init xt_pknock_mt_init(void) if (!(pde = proc_mkdir("xt_pknock", init_net__proc_net))) { printk(KERN_ERR PKNOCK "proc_mkdir() error in _init().\n"); - return -1; + return -ENXIO; } return xt_register_match(&xt_pknock_mt_reg); } From aaad5f53ac3c463f79281c6f6e48a1475a987250 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 30 Sep 2009 00:09:07 +0200 Subject: [PATCH 16/68] pknock: remove pointless sizeof(char) sizeof(char) is defined to be 1, anyway. --- extensions/xt_pknock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 0b0bcf1..60953ba 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -720,14 +720,14 @@ has_secret(unsigned char *secret, int secret_len, uint32_t ipsrc, if (payload_len != hexa_size + 1) return 0; - hexresult = kmalloc(sizeof(char) * hexa_size, GFP_ATOMIC); + hexresult = kmalloc(hexa_size, GFP_ATOMIC); if (hexresult == NULL) { printk(KERN_ERR PKNOCK "kmalloc() error in has_secret().\n"); return 0; } memset(result, 0, 64); - memset(hexresult, 0, (sizeof(char) * hexa_size)); + memset(hexresult, 0, hexa_size); epoch_min = get_epoch_minute(); From 40b30f7f2792bc4277bab2dfbf15293f4ff92bff Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 30 Sep 2009 00:22:32 +0200 Subject: [PATCH 17/68] pknock: add const qualifiers --- extensions/xt_pknock.c | 41 +++++++++++++++++++++-------------------- extensions/xt_pknock.h | 2 +- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 60953ba..f4ee786 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -71,7 +71,7 @@ static DEFINE_SPINLOCK(list_lock); #ifdef PK_CRYPTO static struct { - char *algo; + const char *algo; struct crypto_hash *tfm; int size; struct hash_desc desc; @@ -161,8 +161,8 @@ status_itoa(enum status status) static void * pknock_seq_start(struct seq_file *s, loff_t *pos) { - struct proc_dir_entry *pde = s->private; - struct ipt_pknock_rule *rule = pde->data; + const struct proc_dir_entry *pde = s->private; + const struct ipt_pknock_rule *rule = pde->data; spin_lock_bh(&list_lock); @@ -181,8 +181,8 @@ pknock_seq_start(struct seq_file *s, loff_t *pos) static void * pknock_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct proc_dir_entry *pde = s->private; - struct ipt_pknock_rule *rule = pde->data; + const struct proc_dir_entry *pde = s->private; + const struct ipt_pknock_rule *rule = pde->data; (*pos)++; if (*pos >= peer_hashsize) @@ -209,15 +209,15 @@ pknock_seq_stop(struct seq_file *s, void *v) static int pknock_seq_show(struct seq_file *s, void *v) { - struct list_head *pos = NULL, *n = NULL; - struct peer *peer = NULL; + const struct list_head *pos = NULL, *n = NULL; + const struct peer *peer = NULL; unsigned long expir_time = 0; uint32_t ip; - struct list_head *peer_head = (struct list_head *)v; + const struct list_head *peer_head = (struct list_head *)v; - struct proc_dir_entry *pde = s->private; - struct ipt_pknock_rule *rule = pde->data; + const struct proc_dir_entry *pde = s->private; + const struct ipt_pknock_rule *rule = pde->data; list_for_each_safe(pos, n, peer_head) { peer = list_entry(pos, struct peer, head); @@ -238,7 +238,7 @@ pknock_seq_show(struct seq_file *s, void *v) return 0; } -static struct seq_operations pknock_seq_ops = { +static const struct seq_operations pknock_seq_ops = { .start = pknock_seq_start, .next = pknock_seq_next, .stop = pknock_seq_stop, @@ -260,7 +260,7 @@ pknock_proc_open(struct inode *inode, struct file *file) return ret; } -static struct file_operations pknock_proc_ops = { +static const struct file_operations pknock_proc_ops = { .owner = THIS_MODULE, .open = pknock_proc_open, .read = seq_read, @@ -289,7 +289,7 @@ update_rule_timer(struct ipt_pknock_rule *rule) * @return: 1 time exceeded, 0 still valid */ static inline bool -is_time_exceeded(struct peer *peer, int max_time) +is_time_exceeded(const struct peer *peer, int max_time) { return peer && time_after(jiffies/HZ, peer->timestamp + max_time); } @@ -675,7 +675,7 @@ msg_to_userspace_nl(const struct ipt_pknock *info, * @size */ static void -crypt_to_hex(char *out, char *crypt, int size) +crypt_to_hex(char *out, const char *crypt, int size) { int i; for (i=0; i < size; i++) { @@ -696,8 +696,8 @@ crypt_to_hex(char *out, char *crypt, int size) * @return: 1 success, 0 failure */ static int -has_secret(unsigned char *secret, int secret_len, uint32_t ipsrc, - unsigned char *payload, int payload_len) +has_secret(const unsigned char *secret, int secret_len, uint32_t ipsrc, + const unsigned char *payload, int payload_len) { struct scatterlist sg[2]; char result[64]; // 64 bytes * 8 = 512 bits @@ -776,7 +776,7 @@ out: */ static bool pass_security(struct peer *peer, const struct ipt_pknock *info, - unsigned char *payload, int payload_len) + const unsigned char *payload, int payload_len) { if (is_allowed(peer)) return true; @@ -884,7 +884,7 @@ update_peer(struct peer *peer, const struct ipt_pknock *info, */ static inline bool is_close_knock(const struct peer *peer, const struct ipt_pknock *info, - unsigned char *payload, int payload_len) + const unsigned char *payload, int payload_len) { /* Check for CLOSE secret. */ if (has_secret((unsigned char *)info->close_secret, @@ -904,9 +904,10 @@ static bool pknock_mt(const struct sk_buff *skb, const struct ipt_pknock *info = par->matchinfo; struct ipt_pknock_rule *rule = NULL; struct peer *peer = NULL; - struct iphdr *iph = ip_hdr(skb); + const struct iphdr *iph = ip_hdr(skb); int hdr_len = 0; - __be16 _ports[2], *pptr = NULL; + __be16 _ports[2]; + const __be16 *pptr = NULL; struct transport_data hdr = {0, 0, 0, NULL}; bool ret = false; diff --git a/extensions/xt_pknock.h b/extensions/xt_pknock.h index 4b542a4..8a47c2c 100644 --- a/extensions/xt_pknock.h +++ b/extensions/xt_pknock.h @@ -78,7 +78,7 @@ struct transport_data { uint8_t proto; uint16_t port; /* destination port */ int payload_len; - unsigned char *payload; + const unsigned char *payload; }; #endif /* __KERNEL__ */ From 3058e42a2127ad0ca146abca5865d7c36daa9726 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 30 Sep 2009 00:26:35 +0200 Subject: [PATCH 18/68] pknock: remove pointless casts --- extensions/xt_pknock.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index f4ee786..9456e45 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -109,7 +109,7 @@ static int get_epoch_minute(void) { struct timespec t = CURRENT_TIME; - return (int)(t.tv_sec/60); + return t.tv_sec / 60; } /** @@ -214,7 +214,7 @@ pknock_seq_show(struct seq_file *s, void *v) unsigned long expir_time = 0; uint32_t ip; - const struct list_head *peer_head = (struct list_head *)v; + const struct list_head *peer_head = v; const struct proc_dir_entry *pde = s->private; const struct ipt_pknock_rule *rule = pde->data; @@ -658,7 +658,7 @@ msg_to_userspace_nl(const struct ipt_pknock *info, msg.peer_ip = peer->ip; scnprintf(msg.rule_name, info->rule_name_len + 1, info->rule_name); - memcpy(m + 1, (char *)&msg, m->len); + memcpy(m + 1, &msg, m->len); cn_netlink_send(m, multicast_group, GFP_ATOMIC); @@ -787,8 +787,8 @@ pass_security(struct peer *peer, const struct ipt_pknock *info, return false; } /* Check for OPEN secret */ - if (!has_secret((unsigned char *)info->open_secret, - (int)info->open_secret_len, htonl(peer->ip), + if (!has_secret(info->open_secret, + info->open_secret_len, htonl(peer->ip), payload, payload_len)) { return false; @@ -887,8 +887,8 @@ is_close_knock(const struct peer *peer, const struct ipt_pknock *info, const unsigned char *payload, int payload_len) { /* Check for CLOSE secret. */ - if (has_secret((unsigned char *)info->close_secret, - (int)info->close_secret_len, htonl(peer->ip), + if (has_secret(info->close_secret, + info->close_secret_len, htonl(peer->ip), payload, payload_len)) { pk_debug("RESET", peer); From c8dde526f2f54df755cced13d6cc376e423e7dee Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 30 Sep 2009 00:57:23 +0200 Subject: [PATCH 19/68] pknock: use unsigned qualifiers where they are due --- extensions/xt_pknock.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 9456e45..2d063d7 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -73,7 +73,7 @@ static DEFINE_SPINLOCK(list_lock); static struct { const char *algo; struct crypto_hash *tfm; - int size; + unsigned int size; struct hash_desc desc; } crypto = { .algo = "hmac(sha256)", @@ -105,7 +105,7 @@ pknock_hash(const void *key, uint32_t len, uint32_t initval, uint32_t max) /** * @return: the epoch minute */ -static int +static unsigned int get_epoch_minute(void) { struct timespec t = CURRENT_TIME; @@ -119,7 +119,7 @@ get_epoch_minute(void) * @return: hashtable */ static struct list_head * -alloc_hashtable(int size) +alloc_hashtable(unsigned int size) { struct list_head *hash = NULL; unsigned int i; @@ -289,7 +289,7 @@ update_rule_timer(struct ipt_pknock_rule *rule) * @return: 1 time exceeded, 0 still valid */ static inline bool -is_time_exceeded(const struct peer *peer, int max_time) +is_time_exceeded(const struct peer *peer, unsigned int max_time) { return peer && time_after(jiffies/HZ, peer->timestamp + max_time); } @@ -312,7 +312,7 @@ has_logged_during_this_minute(const struct peer *peer) static void peer_gc(unsigned long r) { - int i; + unsigned int i; struct ipt_pknock_rule *rule = (struct ipt_pknock_rule *)r; struct peer *peer = NULL; struct list_head *pos = NULL, *n = NULL; @@ -358,7 +358,7 @@ search_rule(const struct ipt_pknock *info) { struct ipt_pknock_rule *rule = NULL; struct list_head *pos = NULL, *n = NULL; - int hash = pknock_hash(info->rule_name, info->rule_name_len, + unsigned int hash = pknock_hash(info->rule_name, info->rule_name_len, ipt_pknock_hash_rnd, rule_hashsize); list_for_each_safe(pos, n, &rule_hashtable[hash]) { @@ -380,7 +380,7 @@ add_rule(struct ipt_pknock *info) { struct ipt_pknock_rule *rule = NULL; struct list_head *pos = NULL, *n = NULL; - int hash = pknock_hash(info->rule_name, info->rule_name_len, + unsigned int hash = pknock_hash(info->rule_name, info->rule_name_len, ipt_pknock_hash_rnd, rule_hashsize); list_for_each_safe(pos, n, &rule_hashtable[hash]) { @@ -449,9 +449,9 @@ remove_rule(struct ipt_pknock *info) struct ipt_pknock_rule *rule = NULL; struct list_head *pos = NULL, *n = NULL; struct peer *peer = NULL; - int i; + unsigned int i = 0; int found = 0; - int hash = pknock_hash(info->rule_name, info->rule_name_len, + unsigned int hash = pknock_hash(info->rule_name, info->rule_name_len, ipt_pknock_hash_rnd, rule_hashsize); if (list_empty(&rule_hashtable[hash])) return; @@ -504,7 +504,7 @@ get_peer(struct ipt_pknock_rule *rule, uint32_t ip) { struct peer *peer = NULL; struct list_head *pos = NULL, *n = NULL; - int hash; + unsigned int hash; ip = ntohl(ip); @@ -566,7 +566,7 @@ new_peer(uint32_t ip, uint8_t proto) static inline void add_peer(struct peer *peer, struct ipt_pknock_rule *rule) { - int hash = pknock_hash(&peer->ip, sizeof(peer->ip), + unsigned int hash = pknock_hash(&peer->ip, sizeof(peer->ip), ipt_pknock_hash_rnd, peer_hashsize); list_add(&peer->head, &rule->peer_head[hash]); } @@ -675,9 +675,9 @@ msg_to_userspace_nl(const struct ipt_pknock *info, * @size */ static void -crypt_to_hex(char *out, const char *crypt, int size) +crypt_to_hex(char *out, const char *crypt, unsigned int size) { - int i; + unsigned int i; for (i=0; i < size; i++) { unsigned char c = crypt[i]; *out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1); @@ -696,15 +696,15 @@ crypt_to_hex(char *out, const char *crypt, int size) * @return: 1 success, 0 failure */ static int -has_secret(const unsigned char *secret, int secret_len, uint32_t ipsrc, - const unsigned char *payload, int payload_len) +has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, + const unsigned char *payload, unsigned int payload_len) { struct scatterlist sg[2]; char result[64]; // 64 bytes * 8 = 512 bits char *hexresult = NULL; - int hexa_size; + unsigned int hexa_size; int ret = 0; - int epoch_min; + unsigned int epoch_min; if (payload_len == 0) return 0; @@ -776,7 +776,7 @@ out: */ static bool pass_security(struct peer *peer, const struct ipt_pknock *info, - const unsigned char *payload, int payload_len) + const unsigned char *payload, unsigned int payload_len) { if (is_allowed(peer)) return true; @@ -884,7 +884,7 @@ update_peer(struct peer *peer, const struct ipt_pknock *info, */ static inline bool is_close_knock(const struct peer *peer, const struct ipt_pknock *info, - const unsigned char *payload, int payload_len) + const unsigned char *payload, unsigned int payload_len) { /* Check for CLOSE secret. */ if (has_secret(info->close_secret, @@ -905,7 +905,7 @@ static bool pknock_mt(const struct sk_buff *skb, struct ipt_pknock_rule *rule = NULL; struct peer *peer = NULL; const struct iphdr *iph = ip_hdr(skb); - int hdr_len = 0; + unsigned int hdr_len = 0; __be16 _ports[2]; const __be16 *pptr = NULL; struct transport_data hdr = {0, 0, 0, NULL}; From 4b5f0b12d9e06ad8139d019967e9588b73f999a6 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 30 Sep 2009 00:48:24 +0200 Subject: [PATCH 20/68] pknock: remove initialization of data where not needed bss will be set to zero anyway. For automatic variables, the preinit is not always needed (gcc will warn about it). --- extensions/xt_pknock.c | 46 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 2d063d7..1f9e3d8 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -64,8 +64,8 @@ static unsigned int peer_hashsize = DEFAULT_PEER_HASH_SIZE; static unsigned int ipt_pknock_gc_expir_time = GC_EXPIRATION_TIME; static int nl_multicast_group = -1; -static struct list_head *rule_hashtable = NULL; -static struct proc_dir_entry *pde = NULL; +static struct list_head *rule_hashtable; +static struct proc_dir_entry *pde; static DEFINE_SPINLOCK(list_lock); @@ -121,7 +121,7 @@ get_epoch_minute(void) static struct list_head * alloc_hashtable(unsigned int size) { - struct list_head *hash = NULL; + struct list_head *hash; unsigned int i; if ((hash = kmalloc(sizeof(*hash) * size, GFP_ATOMIC)) == NULL) { @@ -209,9 +209,9 @@ pknock_seq_stop(struct seq_file *s, void *v) static int pknock_seq_show(struct seq_file *s, void *v) { - const struct list_head *pos = NULL, *n = NULL; - const struct peer *peer = NULL; - unsigned long expir_time = 0; + const struct list_head *pos, *n; + const struct peer *peer; + unsigned long expir_time; uint32_t ip; const struct list_head *peer_head = v; @@ -314,8 +314,8 @@ peer_gc(unsigned long r) { unsigned int i; struct ipt_pknock_rule *rule = (struct ipt_pknock_rule *)r; - struct peer *peer = NULL; - struct list_head *pos = NULL, *n = NULL; + struct peer *peer; + struct list_head *pos, *n; hashtable_for_each_safe(pos, n, rule->peer_head, peer_hashsize, i) { peer = list_entry(pos, struct peer, head); @@ -356,8 +356,8 @@ rulecmp(const struct ipt_pknock *info, const struct ipt_pknock_rule *rule) static inline struct ipt_pknock_rule * search_rule(const struct ipt_pknock *info) { - struct ipt_pknock_rule *rule = NULL; - struct list_head *pos = NULL, *n = NULL; + struct ipt_pknock_rule *rule; + struct list_head *pos, *n; unsigned int hash = pknock_hash(info->rule_name, info->rule_name_len, ipt_pknock_hash_rnd, rule_hashsize); @@ -378,8 +378,8 @@ search_rule(const struct ipt_pknock *info) static bool add_rule(struct ipt_pknock *info) { - struct ipt_pknock_rule *rule = NULL; - struct list_head *pos = NULL, *n = NULL; + struct ipt_pknock_rule *rule; + struct list_head *pos, *n; unsigned int hash = pknock_hash(info->rule_name, info->rule_name_len, ipt_pknock_hash_rnd, rule_hashsize); @@ -447,9 +447,9 @@ static void remove_rule(struct ipt_pknock *info) { struct ipt_pknock_rule *rule = NULL; - struct list_head *pos = NULL, *n = NULL; - struct peer *peer = NULL; - unsigned int i = 0; + struct list_head *pos, *n; + struct peer *peer; + unsigned int i; int found = 0; unsigned int hash = pknock_hash(info->rule_name, info->rule_name_len, ipt_pknock_hash_rnd, rule_hashsize); @@ -502,8 +502,8 @@ remove_rule(struct ipt_pknock *info) static inline struct peer * get_peer(struct ipt_pknock_rule *rule, uint32_t ip) { - struct peer *peer = NULL; - struct list_head *pos = NULL, *n = NULL; + struct peer *peer; + struct list_head *pos, *n; unsigned int hash; ip = ntohl(ip); @@ -540,7 +540,7 @@ reset_knock_status(struct peer *peer) static inline struct peer * new_peer(uint32_t ip, uint8_t proto) { - struct peer *peer = NULL; + struct peer *peer; if ((peer = kmalloc(sizeof (*peer), GFP_ATOMIC)) == NULL) { printk(KERN_ERR PKNOCK "kmalloc() error in new_peer().\n"); @@ -701,9 +701,9 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, { struct scatterlist sg[2]; char result[64]; // 64 bytes * 8 = 512 bits - char *hexresult = NULL; + char *hexresult; unsigned int hexa_size; - int ret = 0; + int ret; unsigned int epoch_min; if (payload_len == 0) @@ -902,12 +902,12 @@ static bool pknock_mt(const struct sk_buff *skb, const struct xt_match_param *par) { const struct ipt_pknock *info = par->matchinfo; - struct ipt_pknock_rule *rule = NULL; - struct peer *peer = NULL; + struct ipt_pknock_rule *rule; + struct peer *peer; const struct iphdr *iph = ip_hdr(skb); unsigned int hdr_len = 0; __be16 _ports[2]; - const __be16 *pptr = NULL; + const __be16 *pptr; struct transport_data hdr = {0, 0, 0, NULL}; bool ret = false; From d116000784cc37dddd6b6d2aba04da85532cf85c Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 30 Sep 2009 01:11:24 +0200 Subject: [PATCH 21/68] pknock: fix return values and memleak in has_secret First, there is a memory leak - when the crypto functions fail, hexresult was not freed. Second, in that error case, the return value is simply passed up to the caller, ignoring the different meanings of 0 and 1 between crypto and pknock. --- extensions/xt_pknock.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 1f9e3d8..a24303c 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -737,7 +737,8 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, ret = crypto_hash_setkey(crypto.tfm, secret, secret_len); if (ret) { printk("crypto_hash_setkey() failed ret=%d\n", ret); - return ret; + ret = 0; + goto out; } /* @@ -748,20 +749,19 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, ret = crypto_hash_digest(&crypto.desc, sg, 8, result); if (ret) { printk("crypto_hash_digest() failed ret=%d\n", ret); - return ret; + ret = 0; + goto out; } crypt_to_hex(hexresult, result, crypto.size); if (memcmp(hexresult, payload, hexa_size) != 0) { pr_debug("secret match failed\n"); - goto out; + ret = 0; } - ret = 1; - -out: - if (hexresult != NULL) kfree(hexresult); + out: + kfree(hexresult); return ret; } From 79d54ebd7ce32f7caf57dc707aad0c781d29335d Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 30 Sep 2009 01:16:08 +0200 Subject: [PATCH 22/68] pknock: split long instructions, esp. allocations in if() --- extensions/xt_pknock.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index a24303c..b3f266a 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -124,7 +124,8 @@ alloc_hashtable(unsigned int size) struct list_head *hash; unsigned int i; - if ((hash = kmalloc(sizeof(*hash) * size, GFP_ATOMIC)) == NULL) { + hash = kmalloc(sizeof(*hash) * size, GFP_ATOMIC); + if (hash == NULL) { printk(KERN_ERR PKNOCK "kmalloc() error in alloc_hashtable() function.\n"); return NULL; @@ -399,7 +400,8 @@ add_rule(struct ipt_pknock *info) } } - if ((rule = kmalloc(sizeof (*rule), GFP_ATOMIC)) == NULL) { + rule = kmalloc(sizeof(*rule), GFP_ATOMIC); + if (rule == NULL) { printk(KERN_ERR PKNOCK "kmalloc() error in add_rule().\n"); return false; } @@ -412,8 +414,8 @@ add_rule(struct ipt_pknock *info) rule->ref_count = 1; rule->max_time = info->max_time; - - if (!(rule->peer_head = alloc_hashtable(peer_hashsize))) { + rule->peer_head = alloc_hashtable(peer_hashsize); + if (rule->peer_head == NULL) { printk(KERN_ERR PKNOCK "alloc_hashtable() error in add_rule().\n"); return false; } @@ -540,9 +542,9 @@ reset_knock_status(struct peer *peer) static inline struct peer * new_peer(uint32_t ip, uint8_t proto) { - struct peer *peer; + struct peer *peer = kmalloc(sizeof(*peer), GFP_ATOMIC); - if ((peer = kmalloc(sizeof (*peer), GFP_ATOMIC)) == NULL) { + if (peer == NULL) { printk(KERN_ERR PKNOCK "kmalloc() error in new_peer().\n"); return NULL; } @@ -921,8 +923,9 @@ static bool pknock_mt(const struct sk_buff *skb, } hdr.port = ntohs(pptr[1]); + hdr.proto = iph->protocol; - switch ((hdr.proto = iph->protocol)) { + switch (hdr.proto) { case IPPROTO_TCP: break; @@ -944,7 +947,8 @@ static bool pknock_mt(const struct sk_buff *skb, spin_lock_bh(&list_lock); /* Searches a rule from the list depending on info structure options. */ - if ((rule = search_rule(info)) == NULL) { + rule = search_rule(info); + if (rule == NULL) { printk(KERN_INFO PKNOCK "The rule %s doesn't exist.\n", info->rule_name); goto out; @@ -1005,7 +1009,8 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) /* Singleton. */ if (!rule_hashtable) { - if (!(rule_hashtable = alloc_hashtable(rule_hashsize))) + rule_hashtable = alloc_hashtable(rule_hashsize); + if (rule_hashtable == NULL) RETURN_ERR("alloc_hashtable() error in checkentry()\n"); get_random_bytes(&ipt_pknock_hash_rnd, sizeof (ipt_pknock_hash_rnd)); @@ -1116,7 +1121,8 @@ static int __init xt_pknock_mt_init(void) pr_info("No crypto support for < 2.6.19\n"); #endif - if (!(pde = proc_mkdir("xt_pknock", init_net__proc_net))) { + pde = proc_mkdir("xt_pknock", init_net__proc_net); + if (pde == NULL) { printk(KERN_ERR PKNOCK "proc_mkdir() error in _init().\n"); return -ENXIO; } From e436948ebd2dd0a0b6974cc21d8b091babb199c6 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 30 Sep 2009 01:26:06 +0200 Subject: [PATCH 23/68] pknock: use explicit tests for non-bool types --- extensions/libxt_pknock.c | 13 ++++++++----- extensions/xt_pknock.c | 30 ++++++++++++++++-------------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index 3e5f044..b752005 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -61,16 +61,19 @@ parse_ports(const char *portstring, uint16_t *ports, const char *proto) unsigned int i; buffer = strdup(portstring); - if (!buffer) xtables_error(OTHER_PROBLEM, "strdup failed"); + if (buffer == NULL) + xtables_error(OTHER_PROBLEM, "strdup failed"); - for (cp=buffer, i=0; cp && iprivate_data; sf->private = PDE(inode); } @@ -292,7 +292,8 @@ update_rule_timer(struct ipt_pknock_rule *rule) static inline bool is_time_exceeded(const struct peer *peer, unsigned int max_time) { - return peer && time_after(jiffies/HZ, peer->timestamp + max_time); + return peer != NULL && time_after(jiffies / HZ, + peer->timestamp + max_time); } /** @@ -302,7 +303,7 @@ is_time_exceeded(const struct peer *peer, unsigned int max_time) static inline bool has_logged_during_this_minute(const struct peer *peer) { - return peer && (peer->login_min == get_epoch_minute()); + return peer != NULL && peer->login_min == get_epoch_minute(); } /** @@ -425,7 +426,7 @@ add_rule(struct ipt_pknock *info) rule->timer.data = (unsigned long)rule; rule->status_proc = create_proc_entry(info->rule_name, 0, pde); - if (!rule->status_proc) { + if (rule->status_proc == NULL) { printk(KERN_ERR PKNOCK "create_proc_entry() error in add_rule()" " function.\n"); kfree(rule); @@ -471,7 +472,7 @@ remove_rule(struct ipt_pknock *info) pr_debug("(N) rule not found: %s.\n", info->rule_name); return; } - if (rule && rule->ref_count == 0) { + if (rule != NULL && rule->ref_count == 0) { hashtable_for_each_safe(pos, n, rule->peer_head, peer_hashsize, i) { peer = list_entry(pos, struct peer, head); @@ -482,7 +483,7 @@ remove_rule(struct ipt_pknock *info) } } - if (rule->status_proc) + if (rule->status_proc != NULL) remove_proc_entry(info->rule_name, pde); pr_debug("(D) rule deleted: %s.\n", rule->rule_name); if (timer_pending(&rule->timer)) @@ -582,7 +583,8 @@ static inline void remove_peer(struct peer *peer) { list_del(&peer->head); - if (peer) kfree(peer); + if (peer != NULL) + kfree(peer); } /** @@ -608,7 +610,7 @@ static inline bool is_wrong_knock(const struct peer *peer, const struct ipt_pknock *info, uint16_t port) { - return peer && (info->port[peer->id_port_knocked-1] != port); + return peer != NULL && info->port[peer->id_port_knocked-1] != port; } /** @@ -619,7 +621,7 @@ is_wrong_knock(const struct peer *peer, const struct ipt_pknock *info, static inline bool is_last_knock(const struct peer *peer, const struct ipt_pknock *info) { - return peer && (peer->id_port_knocked-1 == info->ports_count); + return peer != NULL && peer->id_port_knocked - 1 == info->ports_count; } /** @@ -629,7 +631,7 @@ is_last_knock(const struct peer *peer, const struct ipt_pknock *info) static inline bool is_allowed(const struct peer *peer) { - return peer && (peer->status == ST_ALLOWED); + return peer != NULL && peer->status == ST_ALLOWED; } /** @@ -647,7 +649,7 @@ msg_to_userspace_nl(const struct ipt_pknock *info, struct ipt_pknock_nl_msg msg; m = kmalloc(sizeof(*m) + sizeof(msg), GFP_ATOMIC); - if (!m) { + if (m == NULL) { printk(KERN_ERR PKNOCK "kmalloc() error in " "msg_to_userspace_nl().\n"); return false; @@ -737,7 +739,7 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, sg_set_buf(&sg[1], &epoch_min, sizeof(epoch_min)); ret = crypto_hash_setkey(crypto.tfm, secret, secret_len); - if (ret) { + if (ret != 0) { printk("crypto_hash_setkey() failed ret=%d\n", ret); ret = 0; goto out; @@ -749,7 +751,7 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, * 4 bytes int epoch_min (32 bits) */ ret = crypto_hash_digest(&crypto.desc, sg, 8, result); - if (ret) { + if (ret != 0) { printk("crypto_hash_digest() failed ret=%d\n", ret); ret = 0; goto out; @@ -1008,7 +1010,7 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) struct ipt_pknock *info = par->matchinfo; /* Singleton. */ - if (!rule_hashtable) { + if (rule_hashtable == NULL) { rule_hashtable = alloc_hashtable(rule_hashsize); if (rule_hashtable == NULL) RETURN_ERR("alloc_hashtable() error in checkentry()\n"); From 58839b9b86e583a774c7920b7ba2d0ccc4179f8b Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 30 Sep 2009 01:29:11 +0200 Subject: [PATCH 24/68] pknock: use bools for two-state ints --- extensions/xt_pknock.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index eb93760..f65f9b1 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -339,14 +339,14 @@ peer_gc(unsigned long r) * @rule * @return: 0 equals, 1 otherwise */ -static inline int +static inline bool rulecmp(const struct ipt_pknock *info, const struct ipt_pknock_rule *rule) { if (info->rule_name_len != rule->rule_name_len) - return 1; + return true; if (strncmp(info->rule_name, rule->rule_name, info->rule_name_len) != 0) - return 1; - return 0; + return true; + return false; } /** @@ -365,7 +365,7 @@ search_rule(const struct ipt_pknock *info) list_for_each_safe(pos, n, &rule_hashtable[hash]) { rule = list_entry(pos, struct ipt_pknock_rule, head); - if (rulecmp(info, rule) == 0) + if (rulecmp(info, rule)) return rule; } return NULL; @@ -388,7 +388,7 @@ add_rule(struct ipt_pknock *info) list_for_each_safe(pos, n, &rule_hashtable[hash]) { rule = list_entry(pos, struct ipt_pknock_rule, head); - if (rulecmp(info, rule) == 0) { + if (rulecmp(info, rule)) { rule->ref_count++; if (info->option & IPT_PKNOCK_CHECKIP) { pr_debug("add_rule() (AC)" @@ -462,7 +462,7 @@ remove_rule(struct ipt_pknock *info) list_for_each_safe(pos, n, &rule_hashtable[hash]) { rule = list_entry(pos, struct ipt_pknock_rule, head); - if (rulecmp(info, rule) == 0) { + if (rulecmp(info, rule)) { found = 1; rule->ref_count--; break; @@ -597,7 +597,7 @@ static inline bool is_first_knock(const struct peer *peer, const struct ipt_pknock *info, uint16_t port) { - return (peer == NULL && info->port[0] == port) ? 1 : 0; + return peer == NULL && info->port[0] == port; } /** @@ -699,7 +699,7 @@ crypt_to_hex(char *out, const char *crypt, unsigned int size) * @payload_len * @return: 1 success, 0 failure */ -static int +static bool has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, const unsigned char *payload, unsigned int payload_len) { @@ -708,10 +708,11 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, char *hexresult; unsigned int hexa_size; int ret; + bool fret = false; unsigned int epoch_min; if (payload_len == 0) - return 0; + return false; /* * hexa: 4bits @@ -722,12 +723,12 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, /* + 1 cause we MUST add NULL in the payload */ if (payload_len != hexa_size + 1) - return 0; + return false; hexresult = kmalloc(hexa_size, GFP_ATOMIC); if (hexresult == NULL) { printk(KERN_ERR PKNOCK "kmalloc() error in has_secret().\n"); - return 0; + return false; } memset(result, 0, 64); @@ -741,7 +742,6 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, ret = crypto_hash_setkey(crypto.tfm, secret, secret_len); if (ret != 0) { printk("crypto_hash_setkey() failed ret=%d\n", ret); - ret = 0; goto out; } @@ -753,7 +753,6 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, ret = crypto_hash_digest(&crypto.desc, sg, 8, result); if (ret != 0) { printk("crypto_hash_digest() failed ret=%d\n", ret); - ret = 0; goto out; } @@ -761,12 +760,13 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, if (memcmp(hexresult, payload, hexa_size) != 0) { pr_debug("secret match failed\n"); - ret = 0; + } else { + fret = true; } out: kfree(hexresult); - return ret; + return fret; } /** @@ -1003,7 +1003,7 @@ out: return ret; } -#define RETURN_ERR(err) do { printk(KERN_ERR PKNOCK err); return false; } while (0) +#define RETURN_ERR(err) do { printk(KERN_ERR PKNOCK err); return false; } while (false) static bool pknock_mt_check(const struct xt_mtchk_param *par) { From 28ecf45548d10faf09e4ae9f65e701dc5ffe6fb8 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Thu, 1 Oct 2009 02:11:35 +0200 Subject: [PATCH 25/68] pknock: redo inline hints The compiler is usually smart enough to decide this on its own. Some functions however do not warrant even the hint keyword. --- extensions/xt_pknock.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index f65f9b1..0945947 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -96,7 +96,7 @@ module_param(nl_multicast_group, int, S_IRUGO); * @max * @return: a 32 bits index */ -static uint32_t +static inline uint32_t pknock_hash(const void *key, uint32_t len, uint32_t initval, uint32_t max) { return jhash(key, len, initval) % max; @@ -105,8 +105,7 @@ pknock_hash(const void *key, uint32_t len, uint32_t initval, uint32_t max) /** * @return: the epoch minute */ -static unsigned int -get_epoch_minute(void) +static inline unsigned int get_epoch_minute(void) { struct timespec t = CURRENT_TIME; return t.tv_sec / 60; @@ -274,8 +273,7 @@ static const struct file_operations pknock_proc_ops = { * * @rule */ -static inline void -update_rule_timer(struct ipt_pknock_rule *rule) +static void update_rule_timer(struct ipt_pknock_rule *rule) { if (timer_pending(&rule->timer)) del_timer(&rule->timer); @@ -355,8 +353,7 @@ rulecmp(const struct ipt_pknock *info, const struct ipt_pknock_rule *rule) * @info * @return: rule or NULL */ -static inline struct ipt_pknock_rule * -search_rule(const struct ipt_pknock *info) +static struct ipt_pknock_rule *search_rule(const struct ipt_pknock *info) { struct ipt_pknock_rule *rule; struct list_head *pos, *n; @@ -502,8 +499,7 @@ remove_rule(struct ipt_pknock *info) * @ip * @return: peer or NULL */ -static inline struct peer * -get_peer(struct ipt_pknock_rule *rule, uint32_t ip) +static struct peer *get_peer(struct ipt_pknock_rule *rule, uint32_t ip) { struct peer *peer; struct list_head *pos, *n; @@ -525,8 +521,7 @@ get_peer(struct ipt_pknock_rule *rule, uint32_t ip) * * @peer */ -static inline void -reset_knock_status(struct peer *peer) +static void reset_knock_status(struct peer *peer) { peer->id_port_knocked = 1; peer->status = ST_INIT; @@ -540,8 +535,7 @@ reset_knock_status(struct peer *peer) * @proto * @return: peer or NULL */ -static inline struct peer * -new_peer(uint32_t ip, uint8_t proto) +static struct peer *new_peer(uint32_t ip, uint8_t proto) { struct peer *peer = kmalloc(sizeof(*peer), GFP_ATOMIC); @@ -566,8 +560,7 @@ new_peer(uint32_t ip, uint8_t proto) * @peer * @rule */ -static inline void -add_peer(struct peer *peer, struct ipt_pknock_rule *rule) +static void add_peer(struct peer *peer, struct ipt_pknock_rule *rule) { unsigned int hash = pknock_hash(&peer->ip, sizeof(peer->ip), ipt_pknock_hash_rnd, peer_hashsize); @@ -579,8 +572,7 @@ add_peer(struct peer *peer, struct ipt_pknock_rule *rule) * * @peer */ -static inline void -remove_peer(struct peer *peer) +static void remove_peer(struct peer *peer) { list_del(&peer->head); if (peer != NULL) @@ -886,7 +878,7 @@ update_peer(struct peer *peer, const struct ipt_pknock *info, * @payload_len * @return: 1 if close knock, 0 otherwise */ -static inline bool +static bool is_close_knock(const struct peer *peer, const struct ipt_pknock *info, const unsigned char *payload, unsigned int payload_len) { From 628d137f12cd8d15d19b18d506c0939973e1e256 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 3 Oct 2009 00:00:37 +0200 Subject: [PATCH 26/68] pknock: rename struct ipt_pknock -> xt_pknock_mtinfo Streamline symbols; use xt_*_mtinfo as other modules do. --- extensions/libxt_pknock.c | 12 ++++---- extensions/xt_pknock.c | 62 +++++++++++++++++++-------------------- extensions/xt_pknock.h | 6 ++-- 3 files changed, 39 insertions(+), 41 deletions(-) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index b752005..8163f10 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -115,7 +115,7 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, uint16_t invflags) { const char *proto; - struct ipt_pknock *info = (struct ipt_pknock *) (*match)->data; + struct xt_pknock_mtinfo *info = (void *)(*match)->data; switch (c) { case 'k': /* --knockports */ @@ -286,11 +286,9 @@ static void pknock_check(unsigned int flags) static void pknock_print(const void *ip, const struct xt_entry_match *match, int numeric) { - const struct ipt_pknock *info; + const struct xt_pknock_mtinfo *info = (void *)match->data; int i; - info = (const struct ipt_pknock *)match->data; - printf("pknock "); if (info->option & IPT_PKNOCK_KNOCKPORT) { printf("knockports "); @@ -312,7 +310,7 @@ static void pknock_print(const void *ip, static void pknock_save(const void *ip, const struct xt_entry_match *match) { int i; - const struct ipt_pknock *info = (const struct ipt_pknock *)match->data; + const struct xt_pknock_mtinfo *info = (void *)match->data; if (info->option & IPT_PKNOCK_KNOCKPORT) { printf("--knockports "); @@ -339,8 +337,8 @@ static struct xtables_match pknock_match = { .version = XTABLES_VERSION, .revision = 1, .family = AF_INET, - .size = XT_ALIGN(sizeof (struct ipt_pknock)), - .userspacesize = XT_ALIGN(sizeof (struct ipt_pknock)), + .size = XT_ALIGN(sizeof(struct xt_pknock_mtinfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_pknock_mtinfo)), .help = pknock_help, .parse = pknock_parse, .final_check = pknock_check, diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 0945947..fb7e484 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -162,7 +162,7 @@ static void * pknock_seq_start(struct seq_file *s, loff_t *pos) { const struct proc_dir_entry *pde = s->private; - const struct ipt_pknock_rule *rule = pde->data; + const struct xt_pknock_rule *rule = pde->data; spin_lock_bh(&list_lock); @@ -182,7 +182,7 @@ static void * pknock_seq_next(struct seq_file *s, void *v, loff_t *pos) { const struct proc_dir_entry *pde = s->private; - const struct ipt_pknock_rule *rule = pde->data; + const struct xt_pknock_rule *rule = pde->data; (*pos)++; if (*pos >= peer_hashsize) @@ -217,7 +217,7 @@ pknock_seq_show(struct seq_file *s, void *v) const struct list_head *peer_head = v; const struct proc_dir_entry *pde = s->private; - const struct ipt_pknock_rule *rule = pde->data; + const struct xt_pknock_rule *rule = pde->data; list_for_each_safe(pos, n, peer_head) { peer = list_entry(pos, struct peer, head); @@ -273,7 +273,7 @@ static const struct file_operations pknock_proc_ops = { * * @rule */ -static void update_rule_timer(struct ipt_pknock_rule *rule) +static void update_rule_timer(struct xt_pknock_rule *rule) { if (timer_pending(&rule->timer)) del_timer(&rule->timer); @@ -313,7 +313,7 @@ static void peer_gc(unsigned long r) { unsigned int i; - struct ipt_pknock_rule *rule = (struct ipt_pknock_rule *)r; + struct xt_pknock_rule *rule = (struct xt_pknock_rule *)r; struct peer *peer; struct list_head *pos, *n; @@ -338,7 +338,7 @@ peer_gc(unsigned long r) * @return: 0 equals, 1 otherwise */ static inline bool -rulecmp(const struct ipt_pknock *info, const struct ipt_pknock_rule *rule) +rulecmp(const struct xt_pknock_mtinfo *info, const struct xt_pknock_rule *rule) { if (info->rule_name_len != rule->rule_name_len) return true; @@ -353,15 +353,15 @@ rulecmp(const struct ipt_pknock *info, const struct ipt_pknock_rule *rule) * @info * @return: rule or NULL */ -static struct ipt_pknock_rule *search_rule(const struct ipt_pknock *info) +static struct xt_pknock_rule *search_rule(const struct xt_pknock_mtinfo *info) { - struct ipt_pknock_rule *rule; + struct xt_pknock_rule *rule; struct list_head *pos, *n; unsigned int hash = pknock_hash(info->rule_name, info->rule_name_len, ipt_pknock_hash_rnd, rule_hashsize); list_for_each_safe(pos, n, &rule_hashtable[hash]) { - rule = list_entry(pos, struct ipt_pknock_rule, head); + rule = list_entry(pos, struct xt_pknock_rule, head); if (rulecmp(info, rule)) return rule; } @@ -375,15 +375,15 @@ static struct ipt_pknock_rule *search_rule(const struct ipt_pknock *info) * @return: 1 success, 0 failure */ static bool -add_rule(struct ipt_pknock *info) +add_rule(struct xt_pknock_mtinfo *info) { - struct ipt_pknock_rule *rule; + struct xt_pknock_rule *rule; struct list_head *pos, *n; unsigned int hash = pknock_hash(info->rule_name, info->rule_name_len, ipt_pknock_hash_rnd, rule_hashsize); list_for_each_safe(pos, n, &rule_hashtable[hash]) { - rule = list_entry(pos, struct ipt_pknock_rule, head); + rule = list_entry(pos, struct xt_pknock_rule, head); if (rulecmp(info, rule)) { rule->ref_count++; @@ -444,9 +444,9 @@ add_rule(struct ipt_pknock *info) * @info */ static void -remove_rule(struct ipt_pknock *info) +remove_rule(struct xt_pknock_mtinfo *info) { - struct ipt_pknock_rule *rule = NULL; + struct xt_pknock_rule *rule = NULL; struct list_head *pos, *n; struct peer *peer; unsigned int i; @@ -457,7 +457,7 @@ remove_rule(struct ipt_pknock *info) if (list_empty(&rule_hashtable[hash])) return; list_for_each_safe(pos, n, &rule_hashtable[hash]) { - rule = list_entry(pos, struct ipt_pknock_rule, head); + rule = list_entry(pos, struct xt_pknock_rule, head); if (rulecmp(info, rule)) { found = 1; @@ -499,7 +499,7 @@ remove_rule(struct ipt_pknock *info) * @ip * @return: peer or NULL */ -static struct peer *get_peer(struct ipt_pknock_rule *rule, uint32_t ip) +static struct peer *get_peer(struct xt_pknock_rule *rule, uint32_t ip) { struct peer *peer; struct list_head *pos, *n; @@ -560,7 +560,7 @@ static struct peer *new_peer(uint32_t ip, uint8_t proto) * @peer * @rule */ -static void add_peer(struct peer *peer, struct ipt_pknock_rule *rule) +static void add_peer(struct peer *peer, struct xt_pknock_rule *rule) { unsigned int hash = pknock_hash(&peer->ip, sizeof(peer->ip), ipt_pknock_hash_rnd, peer_hashsize); @@ -586,7 +586,7 @@ static void remove_peer(struct peer *peer) * @return: 1 success, 0 failure */ static inline bool -is_first_knock(const struct peer *peer, const struct ipt_pknock *info, +is_first_knock(const struct peer *peer, const struct xt_pknock_mtinfo *info, uint16_t port) { return peer == NULL && info->port[0] == port; @@ -599,7 +599,7 @@ is_first_knock(const struct peer *peer, const struct ipt_pknock *info, * @return: 1 success, 0 failure */ static inline bool -is_wrong_knock(const struct peer *peer, const struct ipt_pknock *info, +is_wrong_knock(const struct peer *peer, const struct xt_pknock_mtinfo *info, uint16_t port) { return peer != NULL && info->port[peer->id_port_knocked-1] != port; @@ -611,7 +611,7 @@ is_wrong_knock(const struct peer *peer, const struct ipt_pknock *info, * @return: 1 success, 0 failure */ static inline bool -is_last_knock(const struct peer *peer, const struct ipt_pknock *info) +is_last_knock(const struct peer *peer, const struct xt_pknock_mtinfo *info) { return peer != NULL && peer->id_port_knocked - 1 == info->ports_count; } @@ -634,11 +634,11 @@ is_allowed(const struct peer *peer) * @return: 1 success, 0 otherwise */ static bool -msg_to_userspace_nl(const struct ipt_pknock *info, +msg_to_userspace_nl(const struct xt_pknock_mtinfo *info, const struct peer *peer, int multicast_group) { struct cn_msg *m; - struct ipt_pknock_nl_msg msg; + struct xt_pknock_nl_msg msg; m = kmalloc(sizeof(*m) + sizeof(msg), GFP_ATOMIC); if (m == NULL) { @@ -771,7 +771,7 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, * @return: 1 if pass security, 0 otherwise */ static bool -pass_security(struct peer *peer, const struct ipt_pknock *info, +pass_security(struct peer *peer, const struct xt_pknock_mtinfo *info, const unsigned char *payload, unsigned int payload_len) { if (is_allowed(peer)) @@ -803,8 +803,8 @@ pass_security(struct peer *peer, const struct ipt_pknock *info, * @return: 1 if allowed, 0 otherwise */ static bool -update_peer(struct peer *peer, const struct ipt_pknock *info, - struct ipt_pknock_rule *rule, +update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, + struct xt_pknock_rule *rule, const struct transport_data *hdr) { unsigned long time; @@ -879,7 +879,7 @@ update_peer(struct peer *peer, const struct ipt_pknock *info, * @return: 1 if close knock, 0 otherwise */ static bool -is_close_knock(const struct peer *peer, const struct ipt_pknock *info, +is_close_knock(const struct peer *peer, const struct xt_pknock_mtinfo *info, const unsigned char *payload, unsigned int payload_len) { /* Check for CLOSE secret. */ @@ -897,8 +897,8 @@ is_close_knock(const struct peer *peer, const struct ipt_pknock *info, static bool pknock_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct ipt_pknock *info = par->matchinfo; - struct ipt_pknock_rule *rule; + const struct xt_pknock_mtinfo *info = par->matchinfo; + struct xt_pknock_rule *rule; struct peer *peer; const struct iphdr *iph = ip_hdr(skb); unsigned int hdr_len = 0; @@ -999,7 +999,7 @@ out: static bool pknock_mt_check(const struct xt_mtchk_param *par) { - struct ipt_pknock *info = par->matchinfo; + struct xt_pknock_mtinfo *info = par->matchinfo; /* Singleton. */ if (rule_hashtable == NULL) { @@ -1073,7 +1073,7 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) static void pknock_mt_destroy(const struct xt_mtdtor_param *par) { - struct ipt_pknock *info = par->matchinfo; + struct xt_pknock_mtinfo *info = par->matchinfo; /* Removes a rule only if it exits and ref_count is equal to 0. */ remove_rule(info); } @@ -1082,7 +1082,7 @@ static struct xt_match xt_pknock_mt_reg __read_mostly = { .name = "pknock", .revision = 1, .family = NFPROTO_IPV4, - .matchsize = sizeof (struct ipt_pknock), + .matchsize = sizeof(struct xt_pknock_mtinfo), .match = pknock_mt, .checkentry = pknock_mt_check, .destroy = pknock_mt_destroy, diff --git a/extensions/xt_pknock.h b/extensions/xt_pknock.h index 8a47c2c..38105aa 100644 --- a/extensions/xt_pknock.h +++ b/extensions/xt_pknock.h @@ -27,7 +27,7 @@ #define DEBUG 1 -struct ipt_pknock { +struct xt_pknock_mtinfo { char rule_name[IPT_PKNOCK_MAX_BUF_LEN + 1]; uint32_t rule_name_len; char open_secret[IPT_PKNOCK_MAX_PASSWD_LEN + 1]; @@ -40,7 +40,7 @@ struct ipt_pknock { uint32_t max_time; /* max matching time between ports */ }; -struct ipt_pknock_nl_msg { +struct xt_pknock_nl_msg { char rule_name[IPT_PKNOCK_MAX_BUF_LEN + 1]; uint32_t peer_ip; }; @@ -63,7 +63,7 @@ struct peer { #include -struct ipt_pknock_rule { +struct xt_pknock_rule { struct list_head head; char rule_name[IPT_PKNOCK_MAX_BUF_LEN + 1]; int rule_name_len; From 4a350ddd030cb5c2a3186c83d84631a63e337430 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 3 Oct 2009 00:11:37 +0200 Subject: [PATCH 27/68] pknock: move more kernel parts from include file to .c All of this does not seem to have a userspace part. --- extensions/xt_pknock.c | 46 ++++++++++++++++++++++++++++++++++++++++++ extensions/xt_pknock.h | 37 --------------------------------- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index fb7e484..6200b5e 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -22,7 +22,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -38,6 +40,50 @@ # define PK_CRYPTO 1 #endif +enum status { + ST_INIT = 1, + ST_MATCHING, + ST_ALLOWED, +}; + +/** + * @login_min: the login epoch minute + */ +struct peer { + struct list_head head; + uint32_t ip; + uint8_t proto; + uint32_t id_port_knocked; + enum status status; + unsigned long timestamp; + int login_min; +}; + +/** + * @timer: garbage collector timer + * @max_time: max matching time between ports + */ +struct xt_pknock_rule { + struct list_head head; + char rule_name[IPT_PKNOCK_MAX_BUF_LEN + 1]; + int rule_name_len; + unsigned int ref_count; + struct timer_list timer; + struct list_head *peer_head; + struct proc_dir_entry *status_proc; + unsigned long max_time; +}; + +/** + * @port: destination port + */ +struct transport_data { + uint8_t proto; + uint16_t port; + int payload_len; + const unsigned char *payload; +}; + MODULE_LICENSE("GPL"); MODULE_AUTHOR("J. Federico Hernandez Scarso, Luis A. Floreani"); MODULE_DESCRIPTION("netfilter match for Port Knocking and SPA"); diff --git a/extensions/xt_pknock.h b/extensions/xt_pknock.h index 38105aa..6c502ed 100644 --- a/extensions/xt_pknock.h +++ b/extensions/xt_pknock.h @@ -45,41 +45,4 @@ struct xt_pknock_nl_msg { uint32_t peer_ip; }; -enum status {ST_INIT=1, ST_MATCHING, ST_ALLOWED}; - -#ifdef __KERNEL__ -#include -#include - -struct peer { - struct list_head head; - uint32_t ip; - uint8_t proto; - uint32_t id_port_knocked; - enum status status; - unsigned long timestamp; - int login_min; /* the login epoch minute */ -}; - -#include - -struct xt_pknock_rule { - struct list_head head; - char rule_name[IPT_PKNOCK_MAX_BUF_LEN + 1]; - int rule_name_len; - unsigned int ref_count; - struct timer_list timer; /* garbage collector timer */ - struct list_head *peer_head; - struct proc_dir_entry *status_proc; - unsigned long max_time; /* max matching time between ports */ -}; - -struct transport_data { - uint8_t proto; - uint16_t port; /* destination port */ - int payload_len; - const unsigned char *payload; -}; - -#endif /* __KERNEL__ */ #endif /* _XT_PKNOCK_H */ From f474c7d4d4538ee3605d8d726a172f155ad5e4ee Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 3 Oct 2009 00:23:42 +0200 Subject: [PATCH 28/68] pknock: rename IPT_PKNOCK_* -> XT_PKNOCK_* --- extensions/libxt_pknock.c | 104 +++++++++++++++++++------------------- extensions/xt_pknock.c | 46 ++++++++--------- extensions/xt_pknock.h | 33 ++++++------ 3 files changed, 93 insertions(+), 90 deletions(-) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index 8163f10..d750944 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -64,7 +64,7 @@ parse_ports(const char *portstring, uint16_t *ports, const char *proto) if (buffer == NULL) xtables_error(OTHER_PROBLEM, "strdup failed"); - for (cp=buffer, i=0; cp != NULL && iports_count = parse_ports(optarg, info->port, proto); - info->option |= IPT_PKNOCK_KNOCKPORT; - *flags |= IPT_PKNOCK_KNOCKPORT; + info->option |= XT_PKNOCK_KNOCKPORT; + *flags |= XT_PKNOCK_KNOCKPORT; #if DEBUG printf("ports_count: %d\n", info->ports_count); #endif break; case 't': /* --time */ - if (*flags & IPT_PKNOCK_TIME) + if (*flags & XT_PKNOCK_TIME) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --time twice.\n"); xtables_check_inverse(argv[optind-1], &invert, &optind, 0); info->max_time = atoi(optarg); - info->option |= IPT_PKNOCK_TIME; - *flags |= IPT_PKNOCK_TIME; + info->option |= XT_PKNOCK_TIME; + *flags |= XT_PKNOCK_TIME; break; case 'n': /* --name */ - if (*flags & IPT_PKNOCK_NAME) + if (*flags & XT_PKNOCK_NAME) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --name twice.\n"); xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - memset(info->rule_name, 0, IPT_PKNOCK_MAX_BUF_LEN + 1); - strncpy(info->rule_name, optarg, IPT_PKNOCK_MAX_BUF_LEN); + memset(info->rule_name, 0, XT_PKNOCK_MAX_BUF_LEN + 1); + strncpy(info->rule_name, optarg, XT_PKNOCK_MAX_BUF_LEN); info->rule_name_len = strlen(info->rule_name); - info->option |= IPT_PKNOCK_NAME; - *flags |= IPT_PKNOCK_NAME; + info->option |= XT_PKNOCK_NAME; + *flags |= XT_PKNOCK_NAME; #if DEBUG printf("info->rule_name: %s\n", info->rule_name); #endif break; case 'a': /* --opensecret */ - if (*flags & IPT_PKNOCK_OPENSECRET) + if (*flags & XT_PKNOCK_OPENSECRET) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --opensecret twice.\n"); xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - memset(info->open_secret, 0, IPT_PKNOCK_MAX_PASSWD_LEN + 1); - strncpy(info->open_secret, optarg, IPT_PKNOCK_MAX_PASSWD_LEN); + memset(info->open_secret, 0, XT_PKNOCK_MAX_PASSWD_LEN + 1); + strncpy(info->open_secret, optarg, XT_PKNOCK_MAX_PASSWD_LEN); info->open_secret_len = strlen(info->open_secret); - info->option |= IPT_PKNOCK_OPENSECRET; - *flags |= IPT_PKNOCK_OPENSECRET; + info->option |= XT_PKNOCK_OPENSECRET; + *flags |= XT_PKNOCK_OPENSECRET; break; case 'z': /* --closesecret */ - if (*flags & IPT_PKNOCK_CLOSESECRET) + if (*flags & XT_PKNOCK_CLOSESECRET) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --closesecret twice.\n"); xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - memset(info->close_secret, 0, IPT_PKNOCK_MAX_PASSWD_LEN + 1); - strncpy(info->close_secret, optarg, IPT_PKNOCK_MAX_PASSWD_LEN); + memset(info->close_secret, 0, XT_PKNOCK_MAX_PASSWD_LEN + 1); + strncpy(info->close_secret, optarg, XT_PKNOCK_MAX_PASSWD_LEN); info->close_secret_len = strlen(info->close_secret); - info->option |= IPT_PKNOCK_CLOSESECRET; - *flags |= IPT_PKNOCK_CLOSESECRET; + info->option |= XT_PKNOCK_CLOSESECRET; + *flags |= XT_PKNOCK_CLOSESECRET; break; case 'c': /* --checkip */ - if (*flags & IPT_PKNOCK_CHECKIP) + if (*flags & XT_PKNOCK_CHECKIP) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --checkip twice.\n"); xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - info->option |= IPT_PKNOCK_CHECKIP; - *flags |= IPT_PKNOCK_CHECKIP; + info->option |= XT_PKNOCK_CHECKIP; + *flags |= XT_PKNOCK_CHECKIP; break; case 'x': /* --strict */ - if (*flags & IPT_PKNOCK_STRICT) + if (*flags & XT_PKNOCK_STRICT) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --strict twice.\n"); xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - info->option |= IPT_PKNOCK_STRICT; - *flags |= IPT_PKNOCK_STRICT; + info->option |= XT_PKNOCK_STRICT; + *flags |= XT_PKNOCK_STRICT; break; default: @@ -240,42 +240,42 @@ static void pknock_check(unsigned int flags) if (!flags) xtables_error(PARAMETER_PROBLEM, PKNOCK "expection an option.\n"); - if (!(flags & IPT_PKNOCK_NAME)) + if (!(flags & XT_PKNOCK_NAME)) xtables_error(PARAMETER_PROBLEM, PKNOCK "--name option is required.\n"); - if (flags & IPT_PKNOCK_KNOCKPORT) { - if (flags & IPT_PKNOCK_CHECKIP) { + if (flags & XT_PKNOCK_KNOCKPORT) { + if (flags & XT_PKNOCK_CHECKIP) { xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot specify --knockports with --checkip.\n"); } - if ((flags & IPT_PKNOCK_OPENSECRET) - && !(flags & IPT_PKNOCK_CLOSESECRET)) + if ((flags & XT_PKNOCK_OPENSECRET) + && !(flags & XT_PKNOCK_CLOSESECRET)) { xtables_error(PARAMETER_PROBLEM, PKNOCK "--opensecret must go with --closesecret.\n"); } - if ((flags & IPT_PKNOCK_CLOSESECRET) - && !(flags & IPT_PKNOCK_OPENSECRET)) + if ((flags & XT_PKNOCK_CLOSESECRET) + && !(flags & XT_PKNOCK_OPENSECRET)) { xtables_error(PARAMETER_PROBLEM, PKNOCK "--closesecret must go with --opensecret.\n"); } } - if (flags & IPT_PKNOCK_CHECKIP) { - if (flags & IPT_PKNOCK_KNOCKPORT) { + if (flags & XT_PKNOCK_CHECKIP) { + if (flags & XT_PKNOCK_KNOCKPORT) { xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot specify --checkip with --knockports.\n"); } - if ((flags & IPT_PKNOCK_OPENSECRET) - || (flags & IPT_PKNOCK_CLOSESECRET)) + if ((flags & XT_PKNOCK_OPENSECRET) + || (flags & XT_PKNOCK_CLOSESECRET)) { xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot specify --opensecret and" " --closesecret with --checkip.\n"); } - if (flags & IPT_PKNOCK_TIME) { + if (flags & XT_PKNOCK_TIME) { xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot specify --time with --checkip.\n"); } @@ -290,19 +290,19 @@ static void pknock_print(const void *ip, int i; printf("pknock "); - if (info->option & IPT_PKNOCK_KNOCKPORT) { + if (info->option & XT_PKNOCK_KNOCKPORT) { printf("knockports "); for (i=0; iports_count; i++) printf("%s%d", i ? "," : "", info->port[i]); printf(" "); } - if (info->option & IPT_PKNOCK_TIME) + if (info->option & XT_PKNOCK_TIME) printf("time %ld ", (long)info->max_time); - if (info->option & IPT_PKNOCK_NAME) + if (info->option & XT_PKNOCK_NAME) printf("name %s ", info->rule_name); - if (info->option & IPT_PKNOCK_OPENSECRET) + if (info->option & XT_PKNOCK_OPENSECRET) printf("opensecret "); - if (info->option & IPT_PKNOCK_CLOSESECRET) + if (info->option & XT_PKNOCK_CLOSESECRET) printf("closesecret "); } @@ -312,23 +312,23 @@ static void pknock_save(const void *ip, const struct xt_entry_match *match) int i; const struct xt_pknock_mtinfo *info = (void *)match->data; - if (info->option & IPT_PKNOCK_KNOCKPORT) { + if (info->option & XT_PKNOCK_KNOCKPORT) { printf("--knockports "); for (i=0; iports_count; i++) printf("%s%d", i ? "," : "", info->port[i]); printf(" "); } - if (info->option & IPT_PKNOCK_TIME) + if (info->option & XT_PKNOCK_TIME) printf("--time %ld ", (long)info->max_time); - if (info->option & IPT_PKNOCK_NAME) + if (info->option & XT_PKNOCK_NAME) printf("--name %s ", info->rule_name); - if (info->option & IPT_PKNOCK_OPENSECRET) + if (info->option & XT_PKNOCK_OPENSECRET) printf("--opensecret "); - if (info->option & IPT_PKNOCK_CLOSESECRET) + if (info->option & XT_PKNOCK_CLOSESECRET) printf("--closesecret "); - if (info->option & IPT_PKNOCK_STRICT) + if (info->option & XT_PKNOCK_STRICT) printf("--strict "); - if (info->option & IPT_PKNOCK_CHECKIP) + if (info->option & XT_PKNOCK_CHECKIP) printf("--checkip "); } diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 6200b5e..f6706d4 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -65,7 +65,7 @@ struct peer { */ struct xt_pknock_rule { struct list_head head; - char rule_name[IPT_PKNOCK_MAX_BUF_LEN + 1]; + char rule_name[XT_PKNOCK_MAX_BUF_LEN+1]; int rule_name_len; unsigned int ref_count; struct timer_list timer; @@ -433,7 +433,7 @@ add_rule(struct xt_pknock_mtinfo *info) if (rulecmp(info, rule)) { rule->ref_count++; - if (info->option & IPT_PKNOCK_CHECKIP) { + if (info->option & XT_PKNOCK_CHECKIP) { pr_debug("add_rule() (AC)" " rule found: %s - " "ref_count: %d\n", @@ -452,7 +452,7 @@ add_rule(struct xt_pknock_mtinfo *info) INIT_LIST_HEAD(&rule->head); - memset(rule->rule_name, 0, IPT_PKNOCK_MAX_BUF_LEN + 1); + memset(rule->rule_name, 0, XT_PKNOCK_MAX_BUF_LEN + 1); strncpy(rule->rule_name, info->rule_name, info->rule_name_len); rule->rule_name_len = info->rule_name_len; @@ -858,7 +858,7 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, if (is_wrong_knock(peer, info, hdr->port)) { pk_debug("DIDN'T MATCH", peer); /* Peer must start the sequence from scratch. */ - if (info->option & IPT_PKNOCK_STRICT) + if (info->option & XT_PKNOCK_STRICT) reset_knock_status(peer); return false; @@ -866,7 +866,7 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, #ifdef PK_CRYPTO /* If security is needed. */ - if (info->option & IPT_PKNOCK_OPENSECRET ) { + if (info->option & XT_PKNOCK_OPENSECRET ) { if (hdr->proto != IPPROTO_UDP) return false; @@ -894,7 +894,7 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, } /* Controls the max matching time between ports. */ - if (info->option & IPT_PKNOCK_TIME) { + if (info->option & XT_PKNOCK_TIME) { time = jiffies/HZ; if (is_time_exceeded(peer, info->max_time)) { @@ -997,7 +997,7 @@ static bool pknock_mt(const struct sk_buff *skb, /* Gives the peer matching status added to rule depending on ip src. */ peer = get_peer(rule, iph->saddr); - if (info->option & IPT_PKNOCK_CHECKIP) { + if (info->option & XT_PKNOCK_CHECKIP) { ret = is_allowed(peer); goto out; } @@ -1008,10 +1008,10 @@ static bool pknock_mt(const struct sk_buff *skb, } /* Sets, updates, removes or checks the peer matching status. */ - if (info->option & IPT_PKNOCK_KNOCKPORT) { + if (info->option & XT_PKNOCK_KNOCKPORT) { if ((ret = is_allowed(peer))) { #ifdef PK_CRYPTO - if (info->option & IPT_PKNOCK_CLOSESECRET && + if (info->option & XT_PKNOCK_CLOSESECRET && iph->protocol == IPPROTO_UDP) { if (is_close_knock(peer, info, hdr.payload, hdr.payload_len)) @@ -1059,51 +1059,51 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) if (!add_rule(info)) RETURN_ERR("add_rule() error in checkentry() function.\n"); - if (!(info->option & IPT_PKNOCK_NAME)) + if (!(info->option & XT_PKNOCK_NAME)) RETURN_ERR("You must specify --name option.\n"); #ifdef PK_CRYPTO - if ((info->option & IPT_PKNOCK_OPENSECRET) && (info->ports_count != 1)) + if ((info->option & XT_PKNOCK_OPENSECRET) && (info->ports_count != 1)) RETURN_ERR("--opensecret must have just one knock port\n"); #endif - if (info->option & IPT_PKNOCK_KNOCKPORT) { - if (info->option & IPT_PKNOCK_CHECKIP) { + if (info->option & XT_PKNOCK_KNOCKPORT) { + if (info->option & XT_PKNOCK_CHECKIP) { RETURN_ERR("Can't specify --knockports with --checkip.\n"); } #ifdef PK_CRYPTO - if ((info->option & IPT_PKNOCK_OPENSECRET) && - !(info->option & IPT_PKNOCK_CLOSESECRET)) + if ((info->option & XT_PKNOCK_OPENSECRET) && + !(info->option & XT_PKNOCK_CLOSESECRET)) { RETURN_ERR("--opensecret must go with --closesecret.\n"); } - if ((info->option & IPT_PKNOCK_CLOSESECRET) && - !(info->option & IPT_PKNOCK_OPENSECRET)) + if ((info->option & XT_PKNOCK_CLOSESECRET) && + !(info->option & XT_PKNOCK_OPENSECRET)) { RETURN_ERR("--closesecret must go with --opensecret.\n"); } #endif } - if (info->option & IPT_PKNOCK_CHECKIP) { - if (info->option & IPT_PKNOCK_KNOCKPORT) + if (info->option & XT_PKNOCK_CHECKIP) { + if (info->option & XT_PKNOCK_KNOCKPORT) { RETURN_ERR("Can't specify --checkip with --knockports.\n"); } #ifdef PK_CRYPTO - if ((info->option & IPT_PKNOCK_OPENSECRET) || - (info->option & IPT_PKNOCK_CLOSESECRET)) + if ((info->option & XT_PKNOCK_OPENSECRET) || + (info->option & XT_PKNOCK_CLOSESECRET)) { RETURN_ERR("Can't specify --opensecret and --closesecret" " with --checkip.\n"); } #endif - if (info->option & IPT_PKNOCK_TIME) + if (info->option & XT_PKNOCK_TIME) RETURN_ERR("Can't specify --time with --checkip.\n"); } #ifdef PK_CRYPTO - if (info->option & IPT_PKNOCK_OPENSECRET) { + if (info->option & XT_PKNOCK_OPENSECRET) { if (info->open_secret_len == info->close_secret_len) { if (memcmp(info->open_secret, info->close_secret, info->open_secret_len) == 0) diff --git a/extensions/xt_pknock.h b/extensions/xt_pknock.h index 6c502ed..bb82c07 100644 --- a/extensions/xt_pknock.h +++ b/extensions/xt_pknock.h @@ -13,35 +13,38 @@ #define PKNOCK "xt_pknock: " -#define IPT_PKNOCK_KNOCKPORT 0x01 -#define IPT_PKNOCK_TIME 0x02 -#define IPT_PKNOCK_NAME 0x04 -#define IPT_PKNOCK_STRICT 0x08 -#define IPT_PKNOCK_CHECKIP 0x10 -#define IPT_PKNOCK_OPENSECRET 0x20 -#define IPT_PKNOCK_CLOSESECRET 0x40 +enum { + XT_PKNOCK_KNOCKPORT = 1 << 0, + XT_PKNOCK_TIME = 1 << 1, + XT_PKNOCK_NAME = 1 << 2, + XT_PKNOCK_STRICT = 1 << 3, + XT_PKNOCK_CHECKIP = 1 << 4, + XT_PKNOCK_OPENSECRET = 1 << 5, + XT_PKNOCK_CLOSESECRET = 1 << 6, -#define IPT_PKNOCK_MAX_PORTS 15 -#define IPT_PKNOCK_MAX_BUF_LEN 31 -#define IPT_PKNOCK_MAX_PASSWD_LEN 31 + /* Can never change these, as they are make up the user protocol. */ + XT_PKNOCK_MAX_PORTS = 15, + XT_PKNOCK_MAX_BUF_LEN = 31, + XT_PKNOCK_MAX_PASSWD_LEN = 31, +}; #define DEBUG 1 struct xt_pknock_mtinfo { - char rule_name[IPT_PKNOCK_MAX_BUF_LEN + 1]; + char rule_name[XT_PKNOCK_MAX_BUF_LEN+1]; uint32_t rule_name_len; - char open_secret[IPT_PKNOCK_MAX_PASSWD_LEN + 1]; + char open_secret[XT_PKNOCK_MAX_PASSWD_LEN+1]; uint32_t open_secret_len; - char close_secret[IPT_PKNOCK_MAX_PASSWD_LEN + 1]; + char close_secret[XT_PKNOCK_MAX_PASSWD_LEN+1]; uint32_t close_secret_len; uint8_t option; /* --time, --knock-port, ... */ uint8_t ports_count; /* number of ports */ - uint16_t port[IPT_PKNOCK_MAX_PORTS]; /* port[,port,port,...] */ + uint16_t port[XT_PKNOCK_MAX_PORTS]; /* port[,port,port,...] */ uint32_t max_time; /* max matching time between ports */ }; struct xt_pknock_nl_msg { - char rule_name[IPT_PKNOCK_MAX_BUF_LEN + 1]; + char rule_name[XT_PKNOCK_MAX_BUF_LEN+1]; uint32_t peer_ip; }; From ba9a3efec1ad7afff19dc6f17137d8e1ee7f5ce7 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 3 Oct 2009 01:05:12 +0200 Subject: [PATCH 29/68] pknock: use preincrements where possible Preincrements are much less error-prone and even do not need extra parentheses. --- extensions/libxt_pknock.c | 6 +++--- extensions/xt_pknock.c | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index d750944..149a43a 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -64,7 +64,7 @@ parse_ports(const char *portstring, uint16_t *ports, const char *proto) if (buffer == NULL) xtables_error(OTHER_PROBLEM, "strdup failed"); - for (cp=buffer, i=0; cp != NULL && ioption & XT_PKNOCK_KNOCKPORT) { printf("knockports "); - for (i=0; iports_count; i++) + for (i = 0; i < info->ports_count; ++i) printf("%s%d", i ? "," : "", info->port[i]); printf(" "); } @@ -314,7 +314,7 @@ static void pknock_save(const void *ip, const struct xt_entry_match *match) if (info->option & XT_PKNOCK_KNOCKPORT) { printf("--knockports "); - for (i=0; iports_count; i++) + for (i = 0; i < info->ports_count; ++i) printf("%s%d", i ? "," : "", info->port[i]); printf(" "); } diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index f6706d4..6949908 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -96,7 +96,7 @@ enum { }; #define hashtable_for_each_safe(pos, n, head, size, i) \ - for ((i) = 0; (i) < (size); (i)++) \ + for ((i) = 0; (i) < (size); ++(i)) \ list_for_each_safe((pos), (n), (&head[(i)])) #define pk_debug(msg, peer) pr_debug( \ @@ -176,7 +176,7 @@ alloc_hashtable(unsigned int size) return NULL; } - for (i = 0; i < size; i++) + for (i = 0; i < size; ++i) INIT_LIST_HEAD(&hash[i]); return hash; @@ -230,7 +230,7 @@ pknock_seq_next(struct seq_file *s, void *v, loff_t *pos) const struct proc_dir_entry *pde = s->private; const struct xt_pknock_rule *rule = pde->data; - (*pos)++; + ++*pos; if (*pos >= peer_hashsize) return NULL; @@ -432,7 +432,7 @@ add_rule(struct xt_pknock_mtinfo *info) rule = list_entry(pos, struct xt_pknock_rule, head); if (rulecmp(info, rule)) { - rule->ref_count++; + ++rule->ref_count; if (info->option & XT_PKNOCK_CHECKIP) { pr_debug("add_rule() (AC)" " rule found: %s - " @@ -720,7 +720,7 @@ static void crypt_to_hex(char *out, const char *crypt, unsigned int size) { unsigned int i; - for (i=0; i < size; i++) { + for (i = 0; i < size; ++i) { unsigned char c = crypt[i]; *out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1); *out++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1); @@ -879,7 +879,7 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, /* Just update the timer when there is a state change. */ update_rule_timer(rule); - peer->id_port_knocked++; + ++peer->id_port_knocked; if (is_last_knock(peer, info)) { peer->status = ST_ALLOWED; From 13c73af79619296db1dd01443fd0dfee7f00cfc6 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 3 Oct 2009 01:09:35 +0200 Subject: [PATCH 30/68] pknock: remove unneeded braces --- extensions/libxt_pknock.c | 15 +++------------ extensions/xt_pknock.c | 24 +++++------------------- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index 149a43a..eef8f71 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -245,40 +245,31 @@ static void pknock_check(unsigned int flags) "--name option is required.\n"); if (flags & XT_PKNOCK_KNOCKPORT) { - if (flags & XT_PKNOCK_CHECKIP) { + if (flags & XT_PKNOCK_CHECKIP) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot specify --knockports with --checkip.\n"); - } if ((flags & XT_PKNOCK_OPENSECRET) && !(flags & XT_PKNOCK_CLOSESECRET)) - { xtables_error(PARAMETER_PROBLEM, PKNOCK "--opensecret must go with --closesecret.\n"); - } if ((flags & XT_PKNOCK_CLOSESECRET) && !(flags & XT_PKNOCK_OPENSECRET)) - { xtables_error(PARAMETER_PROBLEM, PKNOCK "--closesecret must go with --opensecret.\n"); - } } if (flags & XT_PKNOCK_CHECKIP) { - if (flags & XT_PKNOCK_KNOCKPORT) { + if (flags & XT_PKNOCK_KNOCKPORT) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot specify --checkip with --knockports.\n"); - } if ((flags & XT_PKNOCK_OPENSECRET) || (flags & XT_PKNOCK_CLOSESECRET)) - { xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot specify --opensecret and" " --closesecret with --checkip.\n"); - } - if (flags & XT_PKNOCK_TIME) { + if (flags & XT_PKNOCK_TIME) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot specify --time with --checkip.\n"); - } } } diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 6949908..5ad962c 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -796,11 +796,10 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, crypt_to_hex(hexresult, result, crypto.size); - if (memcmp(hexresult, payload, hexa_size) != 0) { + if (memcmp(hexresult, payload, hexa_size) != 0) pr_debug("secret match failed\n"); - } else { + else fret = true; - } out: kfree(hexresult); @@ -832,9 +831,8 @@ pass_security(struct peer *peer, const struct xt_pknock_mtinfo *info, if (!has_secret(info->open_secret, info->open_secret_len, htonl(peer->ip), payload, payload_len)) - { return false; - } + return true; } #endif /* PK_CRYPTO */ @@ -870,9 +868,8 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, if (hdr->proto != IPPROTO_UDP) return false; - if (!pass_security(peer, info, hdr->payload, hdr->payload_len)) { + if (!pass_security(peer, info, hdr->payload, hdr->payload_len)) return false; - } } #endif @@ -1068,35 +1065,26 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) #endif if (info->option & XT_PKNOCK_KNOCKPORT) { - if (info->option & XT_PKNOCK_CHECKIP) { + if (info->option & XT_PKNOCK_CHECKIP) RETURN_ERR("Can't specify --knockports with --checkip.\n"); - } #ifdef PK_CRYPTO if ((info->option & XT_PKNOCK_OPENSECRET) && !(info->option & XT_PKNOCK_CLOSESECRET)) - { RETURN_ERR("--opensecret must go with --closesecret.\n"); - } if ((info->option & XT_PKNOCK_CLOSESECRET) && !(info->option & XT_PKNOCK_OPENSECRET)) - { RETURN_ERR("--closesecret must go with --opensecret.\n"); - } #endif } if (info->option & XT_PKNOCK_CHECKIP) { if (info->option & XT_PKNOCK_KNOCKPORT) - { RETURN_ERR("Can't specify --checkip with --knockports.\n"); - } #ifdef PK_CRYPTO if ((info->option & XT_PKNOCK_OPENSECRET) || (info->option & XT_PKNOCK_CLOSESECRET)) - { RETURN_ERR("Can't specify --opensecret and --closesecret" " with --checkip.\n"); - } #endif if (info->option & XT_PKNOCK_TIME) RETURN_ERR("Can't specify --time with --checkip.\n"); @@ -1107,9 +1095,7 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) if (info->open_secret_len == info->close_secret_len) { if (memcmp(info->open_secret, info->close_secret, info->open_secret_len) == 0) - { RETURN_ERR("opensecret & closesecret cannot be equal.\n"); - } } } #endif From 1f3150bdf935e4e518d24f8893d5a3fb0dfaf703 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 3 Oct 2009 01:43:00 +0200 Subject: [PATCH 31/68] pknock: remove trailing whitespace --- extensions/libxt_pknock.c | 10 +++++----- extensions/xt_pknock.c | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index eef8f71..0167f7c 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -71,7 +71,7 @@ parse_ports(const char *portstring, uint16_t *ports, const char *proto) *next++ = '\0'; ports[i] = xtables_parse_port(cp, proto); } - + if (cp != NULL) xtables_error(PARAMETER_PROBLEM, "too many ports specified"); @@ -109,7 +109,7 @@ check_proto(uint16_t pnum, uint8_t invflags) } /* Function which parses command options; returns true if it ate an option */ -static int +static int __pknock_parse(int c, char **argv, int invert, unsigned int *flags, struct xt_entry_match **match, uint16_t pnum, uint16_t invflags) @@ -230,7 +230,7 @@ static int pknock_parse(int c, char **argv, int invert, unsigned int *flags, const void *e, struct xt_entry_match **match) { const struct ipt_entry *entry = e; - return __pknock_parse(c, argv, invert, flags, match, + return __pknock_parse(c, argv, invert, flags, match, entry->ip.proto, entry->ip.invflags); } @@ -274,7 +274,7 @@ static void pknock_check(unsigned int flags) } /* Prints out the matchinfo. */ -static void pknock_print(const void *ip, +static void pknock_print(const void *ip, const struct xt_entry_match *match, int numeric) { const struct xt_pknock_mtinfo *info = (void *)match->data; @@ -338,7 +338,7 @@ static struct xtables_match pknock_match = { .extra_opts = pknock_opts }; -void _init(void) +void _init(void) { xtables_register_match(&pknock_match); } diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 5ad962c..8d0b392 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -171,7 +171,7 @@ alloc_hashtable(unsigned int size) hash = kmalloc(sizeof(*hash) * size, GFP_ATOMIC); if (hash == NULL) { - printk(KERN_ERR PKNOCK + printk(KERN_ERR PKNOCK "kmalloc() error in alloc_hashtable() function.\n"); return NULL; } @@ -366,7 +366,7 @@ peer_gc(unsigned long r) hashtable_for_each_safe(pos, n, rule->peer_head, peer_hashsize, i) { peer = list_entry(pos, struct peer, head); - if (!has_logged_during_this_minute(peer) && + if (!has_logged_during_this_minute(peer) && is_time_exceeded(peer, rule->max_time)) { pk_debug("DESTROYED", peer); @@ -976,7 +976,7 @@ static bool pknock_mt(const struct sk_buff *skb, #endif default: - printk(KERN_INFO PKNOCK + printk(KERN_INFO PKNOCK "IP payload protocol is neither tcp nor udp.\n"); return false; } @@ -986,7 +986,7 @@ static bool pknock_mt(const struct sk_buff *skb, /* Searches a rule from the list depending on info structure options. */ rule = search_rule(info); if (rule == NULL) { - printk(KERN_INFO PKNOCK "The rule %s doesn't exist.\n", + printk(KERN_INFO PKNOCK "The rule %s doesn't exist.\n", info->rule_name); goto out; } @@ -998,17 +998,17 @@ static bool pknock_mt(const struct sk_buff *skb, ret = is_allowed(peer); goto out; } - + if (iph->protocol == IPPROTO_UDP) { hdr.payload = (void *)iph + hdr_len; hdr.payload_len = skb->len - hdr_len; } - + /* Sets, updates, removes or checks the peer matching status. */ if (info->option & XT_PKNOCK_KNOCKPORT) { if ((ret = is_allowed(peer))) { #ifdef PK_CRYPTO - if (info->option & XT_PKNOCK_CLOSESECRET && + if (info->option & XT_PKNOCK_CLOSESECRET && iph->protocol == IPPROTO_UDP) { if (is_close_knock(peer, info, hdr.payload, hdr.payload_len)) @@ -1069,10 +1069,10 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) RETURN_ERR("Can't specify --knockports with --checkip.\n"); #ifdef PK_CRYPTO if ((info->option & XT_PKNOCK_OPENSECRET) && - !(info->option & XT_PKNOCK_CLOSESECRET)) + !(info->option & XT_PKNOCK_CLOSESECRET)) RETURN_ERR("--opensecret must go with --closesecret.\n"); if ((info->option & XT_PKNOCK_CLOSESECRET) && - !(info->option & XT_PKNOCK_OPENSECRET)) + !(info->option & XT_PKNOCK_OPENSECRET)) RETURN_ERR("--closesecret must go with --opensecret.\n"); #endif } @@ -1135,7 +1135,7 @@ static int __init xt_pknock_mt_init(void) crypto.tfm = crypto_alloc_hash(crypto.algo, 0, CRYPTO_ALG_ASYNC); if (crypto.tfm == NULL) { - printk(KERN_ERR PKNOCK "failed to load transform for %s\n", + printk(KERN_ERR PKNOCK "failed to load transform for %s\n", crypto.algo); return -ENXIO; } From 35b00f8c3f179c5256349384d41607b18963afe2 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 3 Oct 2009 13:08:26 +0200 Subject: [PATCH 32/68] pknock: remove redundant --t parameter One can already specify --t on the command line as a synonym for --time because getopt already allows unambiguous abbrevations. --- extensions/libxt_pknock.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index 0167f7c..e85b74f 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -23,7 +23,6 @@ static const struct option pknock_opts[] = { /* .name, .has_arg, .flag, .val */ { "knockports", 1, 0, 'k' }, - { "t", 1, 0, 't' }, { "time", 1, 0, 't' }, { "name", 1, 0, 'n' }, { "opensecret", 1, 0, 'a' }, @@ -41,7 +40,6 @@ static void pknock_help(void) " --knockports port[,port,port,...] " "Matches destination port(s).\n" " --time seconds\n" - " --t ... " "Time between port match.\n" " --secure " "hmac must be in the packets.\n" From 00199427ac742a4b7852d41edc65d7e64393efb3 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Tue, 1 Sep 2009 19:52:48 +0200 Subject: [PATCH 33/68] pknock: rename symbols in libxt_pknock --- extensions/libxt_pknock.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index e85b74f..1e768b8 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -20,7 +20,7 @@ //#include #include "xt_pknock.h" -static const struct option pknock_opts[] = { +static const struct option pknock_mt_opts[] = { /* .name, .has_arg, .flag, .val */ { "knockports", 1, 0, 'k' }, { "time", 1, 0, 't' }, @@ -33,8 +33,7 @@ static const struct option pknock_opts[] = { { .name = NULL } }; -/* Function which prints out usage message. */ -static void pknock_help(void) +static void pknock_mt_help(void) { printf("pknock match options:\n" " --knockports port[,port,port,...] " @@ -224,7 +223,7 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, return 1; } -static int pknock_parse(int c, char **argv, int invert, unsigned int *flags, +static int pknock_mt_parse(int c, char **argv, int invert, unsigned int *flags, const void *e, struct xt_entry_match **match) { const struct ipt_entry *entry = e; @@ -232,8 +231,7 @@ static int pknock_parse(int c, char **argv, int invert, unsigned int *flags, entry->ip.proto, entry->ip.invflags); } -/* Final check. */ -static void pknock_check(unsigned int flags) +static void pknock_mt_check(unsigned int flags) { if (!flags) xtables_error(PARAMETER_PROBLEM, PKNOCK "expection an option.\n"); @@ -271,8 +269,7 @@ static void pknock_check(unsigned int flags) } } -/* Prints out the matchinfo. */ -static void pknock_print(const void *ip, +static void pknock_mt_print(const void *ip, const struct xt_entry_match *match, int numeric) { const struct xt_pknock_mtinfo *info = (void *)match->data; @@ -295,8 +292,7 @@ static void pknock_print(const void *ip, printf("closesecret "); } -/* Saves the union ipt_matchinfo in parsable form to stdout. */ -static void pknock_save(const void *ip, const struct xt_entry_match *match) +static void pknock_mt_save(const void *ip, const struct xt_entry_match *match) { int i; const struct xt_pknock_mtinfo *info = (void *)match->data; @@ -321,22 +317,22 @@ static void pknock_save(const void *ip, const struct xt_entry_match *match) printf("--checkip "); } -static struct xtables_match pknock_match = { +static struct xtables_match pknock_mt_reg = { .name = "pknock", .version = XTABLES_VERSION, .revision = 1, .family = AF_INET, .size = XT_ALIGN(sizeof(struct xt_pknock_mtinfo)), .userspacesize = XT_ALIGN(sizeof(struct xt_pknock_mtinfo)), - .help = pknock_help, - .parse = pknock_parse, - .final_check = pknock_check, - .print = pknock_print, - .save = pknock_save, - .extra_opts = pknock_opts + .help = pknock_mt_help, + .parse = pknock_mt_parse, + .final_check = pknock_mt_check, + .print = pknock_mt_print, + .save = pknock_mt_save, + .extra_opts = pknock_mt_opts, }; -void _init(void) +static __attribute__((constructor)) void pknock_mt_ldr(void) { - xtables_register_match(&pknock_match); + xtables_register_match(&pknock_mt_reg); } From e05a4d9586d63d91ac60705b55b92e53bb3b1746 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 3 Oct 2009 13:33:51 +0200 Subject: [PATCH 34/68] pknock: use sizeof() where meaningful --- extensions/libxt_pknock.c | 12 ++++++------ extensions/xt_pknock.c | 7 ++++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index 1e768b8..b3b6c5f 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -150,8 +150,8 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - memset(info->rule_name, 0, XT_PKNOCK_MAX_BUF_LEN + 1); - strncpy(info->rule_name, optarg, XT_PKNOCK_MAX_BUF_LEN); + memset(info->rule_name, 0, sizeof(info->rule_name)); + strncpy(info->rule_name, optarg, sizeof(info->rule_name) - 1); info->rule_name_len = strlen(info->rule_name); info->option |= XT_PKNOCK_NAME; @@ -168,8 +168,8 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - memset(info->open_secret, 0, XT_PKNOCK_MAX_PASSWD_LEN + 1); - strncpy(info->open_secret, optarg, XT_PKNOCK_MAX_PASSWD_LEN); + memset(info->open_secret, 0, sizeof(info->open_secret)); + strncpy(info->open_secret, optarg, sizeof(info->open_secret) - 1); info->open_secret_len = strlen(info->open_secret); info->option |= XT_PKNOCK_OPENSECRET; @@ -183,8 +183,8 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - memset(info->close_secret, 0, XT_PKNOCK_MAX_PASSWD_LEN + 1); - strncpy(info->close_secret, optarg, XT_PKNOCK_MAX_PASSWD_LEN); + memset(info->close_secret, 0, sizeof(info->close_secret)); + strncpy(info->close_secret, optarg, sizeof(info->close_secret) - 1); info->close_secret_len = strlen(info->close_secret); info->option |= XT_PKNOCK_CLOSESECRET; diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 8d0b392..6c5cd62 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -452,7 +452,7 @@ add_rule(struct xt_pknock_mtinfo *info) INIT_LIST_HEAD(&rule->head); - memset(rule->rule_name, 0, XT_PKNOCK_MAX_BUF_LEN + 1); + memset(rule->rule_name, 0, sizeof(rule->rule_name)); strncpy(rule->rule_name, info->rule_name, info->rule_name_len); rule->rule_name_len = info->rule_name_len; @@ -769,7 +769,7 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, return false; } - memset(result, 0, 64); + memset(result, 0, sizeof(result)); memset(hexresult, 0, hexa_size); epoch_min = get_epoch_minute(); @@ -788,7 +788,8 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, * 4 bytes IP (32 bits) + * 4 bytes int epoch_min (32 bits) */ - ret = crypto_hash_digest(&crypto.desc, sg, 8, result); + ret = crypto_hash_digest(&crypto.desc, sg, + sizeof(ipsrc) + sizeof(epoch_min), result); if (ret != 0) { printk("crypto_hash_digest() failed ret=%d\n", ret); goto out; From 0d8ae29a60d492445f480f951ca3402fee2d95ec Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Tue, 1 Sep 2009 19:52:48 +0200 Subject: [PATCH 35/68] pknock: "strict" and "checkip" flags were not displayed in `iptables -L` --- doc/changelog.txt | 3 +++ extensions/libxt_pknock.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/doc/changelog.txt b/doc/changelog.txt index 8e95275..dc96fa5 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -1,6 +1,9 @@ - build: compile fixes for 2.6.31-rt +- added reworked xt_pknock module + Changes from pknock v0.5: + - pknock: "strict" and "checkip" flags were not displayed in `iptables -L` Xtables-addons 1.18 (September 09 2009) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index b3b6c5f..412e291 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -290,6 +290,10 @@ static void pknock_mt_print(const void *ip, printf("opensecret "); if (info->option & XT_PKNOCK_CLOSESECRET) printf("closesecret "); + if (info->option & XT_PKNOCK_STRICT) + printf("strict "); + if (info->option & XT_PKNOCK_CHECKIP) + printf("checkip "); } static void pknock_mt_save(const void *ip, const struct xt_entry_match *match) From 654862af212011232a0546a2abe6083acc0f70b1 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 3 Oct 2009 18:09:42 +0200 Subject: [PATCH 36/68] pknock: use more efficient method to obtain seconds --- extensions/xt_pknock.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 6c5cd62..d678250 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -153,8 +153,7 @@ pknock_hash(const void *key, uint32_t len, uint32_t initval, uint32_t max) */ static inline unsigned int get_epoch_minute(void) { - struct timespec t = CURRENT_TIME; - return t.tv_sec / 60; + return get_seconds() / 60; } /** From a819458cbaaddcef874cd6c1eb3472f316ee8455 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Tue, 1 Sep 2009 19:52:48 +0200 Subject: [PATCH 37/68] pknock: do call sg_init_table --- extensions/xt_pknock.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index d678250..fa0ae2c 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -773,6 +773,9 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, epoch_min = get_epoch_minute(); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) + sg_init_table(sg, ARRAY_SIZE(sg)); +#endif sg_set_buf(&sg[0], &ipsrc, sizeof(ipsrc)); sg_set_buf(&sg[1], &epoch_min, sizeof(epoch_min)); From 181a9cab43b6847b88273ee93ddea396c885e128 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Tue, 1 Sep 2009 19:52:48 +0200 Subject: [PATCH 38/68] pknock: store peer address in network byte order The conversions from and to host order can be saved, as nothing really depends on them. --- extensions/xt_pknock.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index fa0ae2c..5efa942 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -257,7 +257,6 @@ pknock_seq_show(struct seq_file *s, void *v) const struct list_head *pos, *n; const struct peer *peer; unsigned long expir_time; - uint32_t ip; const struct list_head *peer_head = v; @@ -266,12 +265,11 @@ pknock_seq_show(struct seq_file *s, void *v) list_for_each_safe(pos, n, peer_head) { peer = list_entry(pos, struct peer, head); - ip = htonl(peer->ip); expir_time = time_before(jiffies/HZ, peer->timestamp + rule->max_time) ? ((peer->timestamp + rule->max_time)-(jiffies/HZ)) : 0; - seq_printf(s, "src=%u.%u.%u.%u ", NIPQUAD(ip)); + seq_printf(s, "src=%u.%u.%u.%u ", NIPQUAD(peer->ip)); seq_printf(s, "proto=%s ", (peer->proto == IPPROTO_TCP) ? "TCP" : "UDP"); seq_printf(s, "status=%s ", status_itoa(peer->status)); @@ -550,8 +548,6 @@ static struct peer *get_peer(struct xt_pknock_rule *rule, uint32_t ip) struct list_head *pos, *n; unsigned int hash; - ip = ntohl(ip); - hash = pknock_hash(&ip, sizeof(ip), ipt_pknock_hash_rnd, peer_hashsize); list_for_each_safe(pos, n, &rule->peer_head[hash]) { @@ -590,7 +586,7 @@ static struct peer *new_peer(uint32_t ip, uint8_t proto) } INIT_LIST_HEAD(&peer->head); - peer->ip = ntohl(ip); + peer->ip = ip; peer->proto = proto; peer->timestamp = jiffies/HZ; peer->login_min = 0; @@ -832,7 +828,7 @@ pass_security(struct peer *peer, const struct xt_pknock_mtinfo *info, } /* Check for OPEN secret */ if (!has_secret(info->open_secret, - info->open_secret_len, htonl(peer->ip), + info->open_secret_len, peer->ip, payload, payload_len)) return false; @@ -930,7 +926,7 @@ is_close_knock(const struct peer *peer, const struct xt_pknock_mtinfo *info, { /* Check for CLOSE secret. */ if (has_secret(info->close_secret, - info->close_secret_len, htonl(peer->ip), + info->close_secret_len, peer->ip, payload, payload_len)) { pk_debug("RESET", peer); From 5f290cd4a047b5d792149e46a4366b061700a0d8 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 3 Oct 2009 23:21:54 +0200 Subject: [PATCH 39/68] pknock: mark peer address as __be32 Since the address is now stored in its original network byte order, let's annotate that this is indeed the case. --- extensions/xt_pknock.c | 6 +++--- extensions/xt_pknock.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 5efa942..37794fc 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -51,7 +51,7 @@ enum status { */ struct peer { struct list_head head; - uint32_t ip; + __be32 ip; uint8_t proto; uint32_t id_port_knocked; enum status status; @@ -542,7 +542,7 @@ remove_rule(struct xt_pknock_mtinfo *info) * @ip * @return: peer or NULL */ -static struct peer *get_peer(struct xt_pknock_rule *rule, uint32_t ip) +static struct peer *get_peer(struct xt_pknock_rule *rule, __be32 ip) { struct peer *peer; struct list_head *pos, *n; @@ -576,7 +576,7 @@ static void reset_knock_status(struct peer *peer) * @proto * @return: peer or NULL */ -static struct peer *new_peer(uint32_t ip, uint8_t proto) +static struct peer *new_peer(__be32 ip, uint8_t proto) { struct peer *peer = kmalloc(sizeof(*peer), GFP_ATOMIC); diff --git a/extensions/xt_pknock.h b/extensions/xt_pknock.h index bb82c07..38fb62d 100644 --- a/extensions/xt_pknock.h +++ b/extensions/xt_pknock.h @@ -45,7 +45,7 @@ struct xt_pknock_mtinfo { struct xt_pknock_nl_msg { char rule_name[XT_PKNOCK_MAX_BUF_LEN+1]; - uint32_t peer_ip; + __be32 peer_ip; }; #endif /* _XT_PKNOCK_H */ From da6a9b932c7c2da1f07ea57e1f175a32a6aa1da6 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 3 Oct 2009 23:27:33 +0200 Subject: [PATCH 40/68] pknock: use NIPQUAD_FMT --- extensions/xt_pknock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 37794fc..42be247 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -100,7 +100,7 @@ enum { list_for_each_safe((pos), (n), (&head[(i)])) #define pk_debug(msg, peer) pr_debug( \ - "(S) peer: %u.%u.%u.%u - %s.\n", \ + "(S) peer: " NIPQUAD_FMT " - %s.\n", \ NIPQUAD((peer)->ip), msg) static uint32_t ipt_pknock_hash_rnd; @@ -269,7 +269,7 @@ pknock_seq_show(struct seq_file *s, void *v) peer->timestamp + rule->max_time) ? ((peer->timestamp + rule->max_time)-(jiffies/HZ)) : 0; - seq_printf(s, "src=%u.%u.%u.%u ", NIPQUAD(peer->ip)); + seq_printf(s, "src=" NIPQUAD_FMT " ", NIPQUAD(peer->ip)); seq_printf(s, "proto=%s ", (peer->proto == IPPROTO_TCP) ? "TCP" : "UDP"); seq_printf(s, "status=%s ", status_itoa(peer->status)); From 3c3adcbd077c7f16fb0db472a81b9d2ea6272fb3 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sun, 4 Oct 2009 00:08:34 +0200 Subject: [PATCH 41/68] pknock: remove some extraneous messages and source lines - $Id$ in a comment is pointless. - remove unneeded/commented-out includes. - remove warning messages on allocation failure (if that happens it is almost too late anyway; not wanting to spam the logs) - remove warning messages about unhandled packets (attacker filling up logs) --- extensions/libxt_pknock.c | 4 ---- extensions/xt_pknock.c | 39 +++++++-------------------------------- 2 files changed, 7 insertions(+), 36 deletions(-) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index 412e291..3dfbdb0 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -5,8 +5,6 @@ * (C) 2006-2009 J. Federico Hernandez * (C) 2006 Luis Floreani * - * $Id$ - * * This program is released under the terms of GNU GPL version 2. */ #include @@ -17,7 +15,6 @@ #include #include #include -//#include #include "xt_pknock.h" static const struct option pknock_mt_opts[] = { @@ -105,7 +102,6 @@ check_proto(uint16_t pnum, uint8_t invflags) xtables_error(PARAMETER_PROBLEM, PKNOCK "only works with TCP and UDP."); } -/* Function which parses command options; returns true if it ate an option */ static int __pknock_parse(int c, char **argv, int invert, unsigned int *flags, struct xt_entry_match **match, uint16_t pnum, diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 42be247..3680059 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -4,8 +4,6 @@ * (C) 2006-2009 J. Federico Hernandez Scarso * (C) 2006 Luis A. Floreani * - * $Id$ - * * This program is released under the terms of GNU GPL version 2. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -31,8 +29,6 @@ #include #include -#include -//#include #include "xt_pknock.h" #include "compat_xtables.h" @@ -169,12 +165,8 @@ alloc_hashtable(unsigned int size) unsigned int i; hash = kmalloc(sizeof(*hash) * size, GFP_ATOMIC); - if (hash == NULL) { - printk(KERN_ERR PKNOCK - "kmalloc() error in alloc_hashtable() function.\n"); + if (hash == NULL) return NULL; - } - for (i = 0; i < size; ++i) INIT_LIST_HEAD(&hash[i]); @@ -442,10 +434,8 @@ add_rule(struct xt_pknock_mtinfo *info) } rule = kmalloc(sizeof(*rule), GFP_ATOMIC); - if (rule == NULL) { - printk(KERN_ERR PKNOCK "kmalloc() error in add_rule().\n"); + if (rule == NULL) return false; - } INIT_LIST_HEAD(&rule->head); @@ -456,10 +446,8 @@ add_rule(struct xt_pknock_mtinfo *info) rule->ref_count = 1; rule->max_time = info->max_time; rule->peer_head = alloc_hashtable(peer_hashsize); - if (rule->peer_head == NULL) { - printk(KERN_ERR PKNOCK "alloc_hashtable() error in add_rule().\n"); + if (rule->peer_head == NULL) return false; - } init_timer(&rule->timer); rule->timer.function = peer_gc; @@ -467,8 +455,6 @@ add_rule(struct xt_pknock_mtinfo *info) rule->status_proc = create_proc_entry(info->rule_name, 0, pde); if (rule->status_proc == NULL) { - printk(KERN_ERR PKNOCK "create_proc_entry() error in add_rule()" - " function.\n"); kfree(rule); return false; } @@ -580,10 +566,8 @@ static struct peer *new_peer(__be32 ip, uint8_t proto) { struct peer *peer = kmalloc(sizeof(*peer), GFP_ATOMIC); - if (peer == NULL) { - printk(KERN_ERR PKNOCK "kmalloc() error in new_peer().\n"); + if (peer == NULL) return NULL; - } INIT_LIST_HEAD(&peer->head); peer->ip = ip; @@ -682,11 +666,8 @@ msg_to_userspace_nl(const struct xt_pknock_mtinfo *info, struct xt_pknock_nl_msg msg; m = kmalloc(sizeof(*m) + sizeof(msg), GFP_ATOMIC); - if (m == NULL) { - printk(KERN_ERR PKNOCK "kmalloc() error in " - "msg_to_userspace_nl().\n"); + if (m == NULL) return false; - } memset(m, 0, sizeof(*m) + sizeof(msg)); m->seq = 0; @@ -759,10 +740,8 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, return false; hexresult = kmalloc(hexa_size, GFP_ATOMIC); - if (hexresult == NULL) { - printk(KERN_ERR PKNOCK "kmalloc() error in has_secret().\n"); + if (hexresult == NULL) return false; - } memset(result, 0, sizeof(result)); memset(hexresult, 0, hexa_size); @@ -975,8 +954,7 @@ static bool pknock_mt(const struct sk_buff *skb, #endif default: - printk(KERN_INFO PKNOCK - "IP payload protocol is neither tcp nor udp.\n"); + pr_debug("IP payload protocol is neither tcp nor udp.\n"); return false; } @@ -1122,8 +1100,6 @@ static struct xt_match xt_pknock_mt_reg __read_mostly = { static int __init xt_pknock_mt_init(void) { - printk(KERN_INFO PKNOCK "register.\n"); - #ifdef PK_CRYPTO if (request_module(crypto.algo) < 0) { printk(KERN_ERR PKNOCK "request_module('%s') error.\n", @@ -1156,7 +1132,6 @@ static int __init xt_pknock_mt_init(void) static void __exit xt_pknock_mt_exit(void) { - printk(KERN_INFO PKNOCK "unregister.\n"); remove_proc_entry("xt_pknock", init_net__proc_net); xt_unregister_match(&xt_pknock_mt_reg); kfree(rule_hashtable); From 1bc7f1be67e9ddd5fbffcee5fda59c9f76ed878d Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Tue, 1 Sep 2009 19:52:48 +0200 Subject: [PATCH 42/68] pknock: add module parameter descriptions --- extensions/xt_pknock.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 3680059..f805925 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -86,7 +86,7 @@ MODULE_DESCRIPTION("netfilter match for Port Knocking and SPA"); MODULE_ALIAS("ipt_pknock"); enum { - GC_EXPIRATION_TIME = 65000, /* in msecs */ + DEFAULT_GC_EXPIRATION_TIME = 65000, /* in msecs */ DEFAULT_RULE_HASH_SIZE = 8, DEFAULT_PEER_HASH_SIZE = 16, }; @@ -103,7 +103,7 @@ static uint32_t ipt_pknock_hash_rnd; static unsigned int rule_hashsize = DEFAULT_RULE_HASH_SIZE; static unsigned int peer_hashsize = DEFAULT_PEER_HASH_SIZE; -static unsigned int ipt_pknock_gc_expir_time = GC_EXPIRATION_TIME; +static unsigned int gc_expir_time = DEFAULT_GC_EXPIRATION_TIME; static int nl_multicast_group = -1; static struct list_head *rule_hashtable; @@ -125,9 +125,13 @@ static struct { #endif module_param(rule_hashsize, int, S_IRUGO); +MODULE_PARM_DESC(rule_hashsize, "Buckets in rule hash table (default: 8)"); module_param(peer_hashsize, int, S_IRUGO); -module_param(ipt_pknock_gc_expir_time, int, S_IRUGO); +MODULE_PARM_DESC(peer_hashsize, "Buckets in peer hash table (default: 16)"); +module_param(gc_expir_time, int, S_IRUGO); +MODULE_PARM_DESC(gc_expir_time, "Time until garbage collection after valid knock packet (default: 65000 msec)"); module_param(nl_multicast_group, int, S_IRUGO); +MODULE_PARM_DESC(nl_multicast_group, "Netlink multicast group number for pknock messages"); /** * Calculates a value from 0 to max from a hash of the arguments. @@ -313,7 +317,7 @@ static void update_rule_timer(struct xt_pknock_rule *rule) if (timer_pending(&rule->timer)) del_timer(&rule->timer); - rule->timer.expires = jiffies + msecs_to_jiffies(ipt_pknock_gc_expir_time); + rule->timer.expires = jiffies + msecs_to_jiffies(gc_expir_time); add_timer(&rule->timer); } From e0276b4875c51a9d401b14d075208cdb8d05956e Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Tue, 1 Sep 2009 19:52:48 +0200 Subject: [PATCH 43/68] pknock: disallow running peer_gc too early It is no longer possible to specify gc_expir_time with a time lower than its default value (65000 msecs). This is to avoid running peer_gc() earlier than 1 minute [well, 65 s actually] in the future, which would otherwise render anti-spoof protection in SPA mode non-functional. --- doc/changelog.txt | 2 ++ extensions/xt_pknock.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/doc/changelog.txt b/doc/changelog.txt index dc96fa5..494818a 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -4,6 +4,8 @@ - added reworked xt_pknock module Changes from pknock v0.5: - pknock: "strict" and "checkip" flags were not displayed in `iptables -L` + - pknock: the GC expire time's lower bound is now the default gc time + (65000 msec) to avoid rendering anti-spoof protection in SPA mode useless Xtables-addons 1.18 (September 09 2009) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index f805925..97ccec3 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -1104,6 +1104,8 @@ static struct xt_match xt_pknock_mt_reg __read_mostly = { static int __init xt_pknock_mt_init(void) { + if (gc_expir_time < DEFAULT_GC_EXPIRATION_TIME) + gc_expir_time = DEFAULT_GC_EXPIRATION_TIME; #ifdef PK_CRYPTO if (request_module(crypto.algo) < 0) { printk(KERN_ERR PKNOCK "request_module('%s') error.\n", From c2c86f4b3d8e22e1308ac37db3574236367abc81 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 7 Oct 2009 00:17:38 +0200 Subject: [PATCH 44/68] pknock: remove old-style intraposition negation support --- extensions/libxt_pknock.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/extensions/libxt_pknock.c b/extensions/libxt_pknock.c index 3dfbdb0..e1c4cb5 100644 --- a/extensions/libxt_pknock.c +++ b/extensions/libxt_pknock.c @@ -115,8 +115,6 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, if (*flags & XT_PKNOCK_KNOCKPORT) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --knockports twice.\n"); - - xtables_check_inverse(argv[optind-1], &invert, &optind, 0); proto = check_proto(pnum, invflags); info->ports_count = parse_ports(optarg, info->port, proto); @@ -131,9 +129,6 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, if (*flags & XT_PKNOCK_TIME) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --time twice.\n"); - - xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - info->max_time = atoi(optarg); info->option |= XT_PKNOCK_TIME; *flags |= XT_PKNOCK_TIME; @@ -143,9 +138,6 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, if (*flags & XT_PKNOCK_NAME) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --name twice.\n"); - - xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - memset(info->rule_name, 0, sizeof(info->rule_name)); strncpy(info->rule_name, optarg, sizeof(info->rule_name) - 1); @@ -161,9 +153,6 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, if (*flags & XT_PKNOCK_OPENSECRET) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --opensecret twice.\n"); - - xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - memset(info->open_secret, 0, sizeof(info->open_secret)); strncpy(info->open_secret, optarg, sizeof(info->open_secret) - 1); @@ -176,9 +165,6 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, if (*flags & XT_PKNOCK_CLOSESECRET) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --closesecret twice.\n"); - - xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - memset(info->close_secret, 0, sizeof(info->close_secret)); strncpy(info->close_secret, optarg, sizeof(info->close_secret) - 1); @@ -191,9 +177,6 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, if (*flags & XT_PKNOCK_CHECKIP) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --checkip twice.\n"); - - xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - info->option |= XT_PKNOCK_CHECKIP; *flags |= XT_PKNOCK_CHECKIP; break; @@ -202,9 +185,6 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, if (*flags & XT_PKNOCK_STRICT) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --strict twice.\n"); - - xtables_check_inverse(argv[optind-1], &invert, &optind, 0); - info->option |= XT_PKNOCK_STRICT; *flags |= XT_PKNOCK_STRICT; break; From 204b612e230bc8afdceed5d0443da27d4f4b83c8 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 7 Oct 2009 00:40:19 +0200 Subject: [PATCH 45/68] pknock: add_rule must happen after info struct checks This avoids unnecessarily creating pknock rules if there is an error with any of the match parameters. --- extensions/xt_pknock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 97ccec3..bb65312 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -1034,9 +1034,6 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) get_random_bytes(&ipt_pknock_hash_rnd, sizeof (ipt_pknock_hash_rnd)); } - if (!add_rule(info)) - RETURN_ERR("add_rule() error in checkentry() function.\n"); - if (!(info->option & XT_PKNOCK_NAME)) RETURN_ERR("You must specify --name option.\n"); @@ -1081,6 +1078,9 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) } #endif + if (!add_rule(info)) + RETURN_ERR("add_rule() error in checkentry() function.\n"); + return true; } From 8e812620f04e639d44024d8d8212de94cb6dee9a Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 7 Oct 2009 00:59:37 +0200 Subject: [PATCH 46/68] pknock: avoid crash on memory allocation failure and fix memleak If rule->peer_head==NULL due to an unsuccessful allocation, peer_gc (and perhaps other places) may crash when they try to access it. Since I see no deferred retry for allocation, the only option is to fail in add_rule, clean it up, and return false instead. Independent of that problem, it also needs to free peer_head in case the status_proc allocation fails. --- extensions/xt_pknock.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index bb65312..394a45e 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -451,17 +451,15 @@ add_rule(struct xt_pknock_mtinfo *info) rule->max_time = info->max_time; rule->peer_head = alloc_hashtable(peer_hashsize); if (rule->peer_head == NULL) - return false; + goto out; init_timer(&rule->timer); rule->timer.function = peer_gc; rule->timer.data = (unsigned long)rule; rule->status_proc = create_proc_entry(info->rule_name, 0, pde); - if (rule->status_proc == NULL) { - kfree(rule); - return false; - } + if (rule->status_proc == NULL) + goto out; rule->status_proc->proc_fops = &pknock_proc_ops; rule->status_proc->data = rule; @@ -469,6 +467,10 @@ add_rule(struct xt_pknock_mtinfo *info) list_add(&rule->head, &rule_hashtable[hash]); pr_debug("(A) rule_name: %s - created.\n", rule->rule_name); return true; + out: + kfree(rule->peer_head); + kfree(rule); + return false; } /** From 370f47b0006bad8a91c2ea1d84ea07c2bca6c76b Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 7 Oct 2009 23:59:05 +0200 Subject: [PATCH 47/68] pknock: reduce indent in remove_rule --- extensions/xt_pknock.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 394a45e..7e50162 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -504,27 +504,28 @@ remove_rule(struct xt_pknock_mtinfo *info) pr_debug("(N) rule not found: %s.\n", info->rule_name); return; } - if (rule != NULL && rule->ref_count == 0) { - hashtable_for_each_safe(pos, n, rule->peer_head, peer_hashsize, i) { - peer = list_entry(pos, struct peer, head); + if (rule == NULL || rule->ref_count != 0) + return; - if (peer != NULL) { - pk_debug("DELETED", peer); - list_del(pos); - kfree(peer); - } + hashtable_for_each_safe(pos, n, rule->peer_head, peer_hashsize, i) { + peer = list_entry(pos, struct peer, head); + + if (peer != NULL) { + pk_debug("DELETED", peer); + list_del(pos); + kfree(peer); } - - if (rule->status_proc != NULL) - remove_proc_entry(info->rule_name, pde); - pr_debug("(D) rule deleted: %s.\n", rule->rule_name); - if (timer_pending(&rule->timer)) - del_timer(&rule->timer); - - list_del(&rule->head); - kfree(rule->peer_head); - kfree(rule); } + + if (rule->status_proc != NULL) + remove_proc_entry(info->rule_name, pde); + pr_debug("(D) rule deleted: %s.\n", rule->rule_name); + if (timer_pending(&rule->timer)) + del_timer(&rule->timer); + + list_del(&rule->head); + kfree(rule->peer_head); + kfree(rule); } /** From f1dc7f960c78a612e0a35dbba8a85fd038ba71ce Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 7 Oct 2009 23:59:41 +0200 Subject: [PATCH 48/68] pknock: peer can never be NULL in the hash iterator Given that pos is non-NULL, list_entry won't make it NULL. --- extensions/xt_pknock.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/extensions/xt_pknock.c b/extensions/xt_pknock.c index 7e50162..8d466ad 100644 --- a/extensions/xt_pknock.c +++ b/extensions/xt_pknock.c @@ -509,12 +509,9 @@ remove_rule(struct xt_pknock_mtinfo *info) hashtable_for_each_safe(pos, n, rule->peer_head, peer_hashsize, i) { peer = list_entry(pos, struct peer, head); - - if (peer != NULL) { - pk_debug("DELETED", peer); - list_del(pos); - kfree(peer); - } + pk_debug("DELETED", peer); + list_del(pos); + kfree(peer); } if (rule->status_proc != NULL) From 5184176c3b7b0749fe1e489386243daad2fd1f28 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 9 Oct 2009 17:50:05 +0200 Subject: [PATCH 49/68] pknock: move into separate directory There will be a number of pknock-related tools, e.g. the trigger programs, so move that out to its own directory. --- configure.ac | 2 +- extensions/Kbuild | 2 +- extensions/Mbuild | 2 +- extensions/pknock/Kbuild | 5 +++++ extensions/pknock/Makefile.am | 3 +++ extensions/pknock/Mbuild | 3 +++ extensions/{ => pknock}/libxt_pknock.c | 0 extensions/{ => pknock}/xt_pknock.c | 0 extensions/{ => pknock}/xt_pknock.h | 0 9 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 extensions/pknock/Kbuild create mode 100644 extensions/pknock/Makefile.am create mode 100644 extensions/pknock/Mbuild rename extensions/{ => pknock}/libxt_pknock.c (100%) rename extensions/{ => pknock}/xt_pknock.c (100%) rename extensions/{ => pknock}/xt_pknock.h (100%) diff --git a/configure.ac b/configure.ac index 4644153..5a97edf 100644 --- a/configure.ac +++ b/configure.ac @@ -105,5 +105,5 @@ AC_SUBST([ksourcedir]) AC_SUBST([xtlibdir]) AC_CONFIG_FILES([Makefile Makefile.iptrules Makefile.mans extensions/Makefile extensions/ACCOUNT/Makefile - extensions/ipset/Makefile]) + extensions/ipset/Makefile extensions/pknock/Makefile]) AC_OUTPUT diff --git a/extensions/Kbuild b/extensions/Kbuild index b86a1dc..5588c2c 100644 --- a/extensions/Kbuild +++ b/extensions/Kbuild @@ -26,7 +26,7 @@ obj-${build_ipset} += ipset/ obj-${build_ipv4options} += xt_ipv4options.o obj-${build_length2} += xt_length2.o obj-${build_lscan} += xt_lscan.o -obj-${build_pknock} += xt_pknock.o +obj-${build_pknock} += pknock/ obj-${build_psd} += xt_psd.o obj-${build_quota2} += xt_quota2.o diff --git a/extensions/Mbuild b/extensions/Mbuild index bc0e1d0..533a703 100644 --- a/extensions/Mbuild +++ b/extensions/Mbuild @@ -21,6 +21,6 @@ obj-${build_ipset} += ipset/ obj-${build_ipv4options} += libxt_ipv4options.so obj-${build_length2} += libxt_length2.so obj-${build_lscan} += libxt_lscan.so -obj-${build_pknock} += libxt_pknock.so +obj-${build_pknock} += pknock/ obj-${build_psd} += libxt_psd.so obj-${build_quota2} += libxt_quota2.so diff --git a/extensions/pknock/Kbuild b/extensions/pknock/Kbuild new file mode 100644 index 0000000..6318ca5 --- /dev/null +++ b/extensions/pknock/Kbuild @@ -0,0 +1,5 @@ +# -*- Makefile -*- + +EXTRA_CFLAGS = -I${src}/.. + +obj-m += xt_pknock.o diff --git a/extensions/pknock/Makefile.am b/extensions/pknock/Makefile.am new file mode 100644 index 0000000..af3f625 --- /dev/null +++ b/extensions/pknock/Makefile.am @@ -0,0 +1,3 @@ +# -*- Makefile -*- + +include ../../Makefile.extra diff --git a/extensions/pknock/Mbuild b/extensions/pknock/Mbuild new file mode 100644 index 0000000..7a46751 --- /dev/null +++ b/extensions/pknock/Mbuild @@ -0,0 +1,3 @@ +# -*- Makefile -*- + +obj-${build_pknock} += libxt_pknock.so diff --git a/extensions/libxt_pknock.c b/extensions/pknock/libxt_pknock.c similarity index 100% rename from extensions/libxt_pknock.c rename to extensions/pknock/libxt_pknock.c diff --git a/extensions/xt_pknock.c b/extensions/pknock/xt_pknock.c similarity index 100% rename from extensions/xt_pknock.c rename to extensions/pknock/xt_pknock.c diff --git a/extensions/xt_pknock.h b/extensions/pknock/xt_pknock.h similarity index 100% rename from extensions/xt_pknock.h rename to extensions/pknock/xt_pknock.h From 6bb354d3f07eb7ebe889311d1042d3a48157453b Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 9 Oct 2009 18:11:49 +0200 Subject: [PATCH 50/68] pknock: add Kconfig file --- extensions/pknock/xt_pknock.Kconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 extensions/pknock/xt_pknock.Kconfig diff --git a/extensions/pknock/xt_pknock.Kconfig b/extensions/pknock/xt_pknock.Kconfig new file mode 100644 index 0000000..7969c38 --- /dev/null +++ b/extensions/pknock/xt_pknock.Kconfig @@ -0,0 +1,13 @@ +config NETFILTER_XT_MATCH_PKNOCK + tristate "Port knocking match support" + depends on NETFILTER_XTABLES && CONNECTOR + ---help--- + pknock match implements so-called Port Knocking, a stealthy system + for network authentication: client sends packets to selected, closed + ports on target machine in a specific sequence. The target machine + (which has pknock match rule set up) then decides whether to + unblock or block (again) its protected port with listening + service. This can be, for instance, used to avoid brute force attacks + on ssh or ftp services. + + For more informations go to: http://portknocko.berlios.de/ From 82a8524f1aeb2bc23b814f65c138211da261d3d8 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 9 Oct 2009 18:15:27 +0200 Subject: [PATCH 51/68] pknock: add CONNECTOR guards --- INSTALL | 2 ++ extensions/pknock/xt_pknock.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/INSTALL b/INSTALL index 0b30e06..7a8449a 100644 --- a/INSTALL +++ b/INSTALL @@ -19,6 +19,8 @@ Supported configurations for this release - CONFIG_NF_CONNTRACK or CONFIG_IP_NF_CONNTRACK - CONFIG_NF_CONNTRACK_MARK or CONFIG_IP_NF_CONNTRACK_MARK enabled =y or as module (=m) + - CONFIG_CONNECTOR y/m if you wish to receive userspace + notifications from pknock through netlink/connector Extra notes: diff --git a/extensions/pknock/xt_pknock.c b/extensions/pknock/xt_pknock.c index 8d466ad..cf60949 100644 --- a/extensions/pknock/xt_pknock.c +++ b/extensions/pknock/xt_pknock.c @@ -666,6 +666,7 @@ static bool msg_to_userspace_nl(const struct xt_pknock_mtinfo *info, const struct peer *peer, int multicast_group) { +#if defined(CONFIG_CONNECTOR) || defined(CONFIG_CONNECTOR_MODULE) struct cn_msg *m; struct xt_pknock_nl_msg msg; @@ -685,6 +686,7 @@ msg_to_userspace_nl(const struct xt_pknock_mtinfo *info, cn_netlink_send(m, multicast_group, GFP_ATOMIC); kfree(m); +#endif return true; } @@ -1104,6 +1106,12 @@ static struct xt_match xt_pknock_mt_reg __read_mostly = { static int __init xt_pknock_mt_init(void) { +#if !defined(CONFIG_CONNECTOR) && !defined(CONFIG_CONNECTOR_MODULE) + if (nl_multicast_group != -1) + pr_info("CONFIG_CONNECTOR not present; " + "netlink messages disabled\n"); +#endif + if (gc_expir_time < DEFAULT_GC_EXPIRATION_TIME) gc_expir_time = DEFAULT_GC_EXPIRATION_TIME; #ifdef PK_CRYPTO From 08f6a82bdc12db7540007db2a9a84b653c6a0402 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Sun, 11 Oct 2009 00:05:17 +0200 Subject: [PATCH 52/68] pknock: avoid fillup of peer table during DDoS In TCP --strict mode, forget the peer which sent the wrong knock in a sequence, rather than resetting its status to ST_INIT. This avoids filling up the peer table (which would lead to pknock DoS) in case of a DDoS attack performed by a set of port-scanning malicious hosts. --- extensions/pknock/xt_pknock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/pknock/xt_pknock.c b/extensions/pknock/xt_pknock.c index cf60949..03135d1 100644 --- a/extensions/pknock/xt_pknock.c +++ b/extensions/pknock/xt_pknock.c @@ -841,7 +841,7 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, pk_debug("DIDN'T MATCH", peer); /* Peer must start the sequence from scratch. */ if (info->option & XT_PKNOCK_STRICT) - reset_knock_status(peer); + remove_peer(peer); return false; } From 2cab32596a19eaeb8fba2d03aa8a2ee16db683da Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Mon, 12 Oct 2009 00:01:28 +0200 Subject: [PATCH 53/68] pknock: replace id_port_knocked with accepted_knock_count Gives a better sense of the member's use. Signed-off-by: Jan Rafaj --- extensions/pknock/xt_pknock.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/extensions/pknock/xt_pknock.c b/extensions/pknock/xt_pknock.c index 03135d1..090b0ab 100644 --- a/extensions/pknock/xt_pknock.c +++ b/extensions/pknock/xt_pknock.c @@ -49,7 +49,7 @@ struct peer { struct list_head head; __be32 ip; uint8_t proto; - uint32_t id_port_knocked; + uint32_t accepted_knock_count; enum status status; unsigned long timestamp; int login_min; @@ -270,7 +270,8 @@ pknock_seq_show(struct seq_file *s, void *v) "TCP" : "UDP"); seq_printf(s, "status=%s ", status_itoa(peer->status)); seq_printf(s, "expir_time=%ld ", expir_time); - seq_printf(s, "next_port_id=%d ", peer->id_port_knocked-1); + seq_printf(s, "accepted_knock_count=%lu ", + (unsigned long)peer->accepted_knock_count); seq_printf(s, "\n"); } @@ -554,7 +555,7 @@ static struct peer *get_peer(struct xt_pknock_rule *rule, __be32 ip) */ static void reset_knock_status(struct peer *peer) { - peer->id_port_knocked = 1; + peer->accepted_knock_count = 0; peer->status = ST_INIT; } @@ -631,7 +632,7 @@ static inline bool is_wrong_knock(const struct peer *peer, const struct xt_pknock_mtinfo *info, uint16_t port) { - return peer != NULL && info->port[peer->id_port_knocked-1] != port; + return peer != NULL && info->port[peer->accepted_knock_count] != port; } /** @@ -642,7 +643,7 @@ is_wrong_knock(const struct peer *peer, const struct xt_pknock_mtinfo *info, static inline bool is_last_knock(const struct peer *peer, const struct xt_pknock_mtinfo *info) { - return peer != NULL && peer->id_port_knocked - 1 == info->ports_count; + return peer != NULL && peer->accepted_knock_count == info->ports_count; } /** @@ -860,7 +861,7 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, /* Just update the timer when there is a state change. */ update_rule_timer(rule); - ++peer->id_port_knocked; + ++peer->accepted_knock_count; if (is_last_knock(peer, info)) { peer->status = ST_ALLOWED; From f6281d498c0bfde2b1b8e4c041803aa138a1c9e1 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Mon, 12 Oct 2009 00:01:29 +0200 Subject: [PATCH 54/68] pknock: use kernel's get_seconds() directly instead of wrapper Use kernel's get_seconds() directly instead of the get_epoch_minute() wrapper. Signed-off-by: Jan Rafaj --- extensions/pknock/xt_pknock.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/extensions/pknock/xt_pknock.c b/extensions/pknock/xt_pknock.c index 090b0ab..f9a9ade 100644 --- a/extensions/pknock/xt_pknock.c +++ b/extensions/pknock/xt_pknock.c @@ -43,7 +43,7 @@ enum status { }; /** - * @login_min: the login epoch minute + * @login_sec: seconds at login since the epoch */ struct peer { struct list_head head; @@ -52,7 +52,7 @@ struct peer { uint32_t accepted_knock_count; enum status status; unsigned long timestamp; - int login_min; + unsigned long login_sec; }; /** @@ -148,14 +148,6 @@ pknock_hash(const void *key, uint32_t len, uint32_t initval, uint32_t max) return jhash(key, len, initval) % max; } -/** - * @return: the epoch minute - */ -static inline unsigned int get_epoch_minute(void) -{ - return get_seconds() / 60; -} - /** * Alloc a hashtable with n buckets. * @@ -341,7 +333,7 @@ is_time_exceeded(const struct peer *peer, unsigned int max_time) static inline bool has_logged_during_this_minute(const struct peer *peer) { - return peer != NULL && peer->login_min == get_epoch_minute(); + return peer != NULL && peer->login_sec / 60 == get_seconds() / 60; } /** @@ -578,7 +570,7 @@ static struct peer *new_peer(__be32 ip, uint8_t proto) peer->ip = ip; peer->proto = proto; peer->timestamp = jiffies/HZ; - peer->login_min = 0; + peer->login_sec = 0; reset_knock_status(peer); return peer; @@ -753,7 +745,7 @@ has_secret(const unsigned char *secret, unsigned int secret_len, uint32_t ipsrc, memset(result, 0, sizeof(result)); memset(hexresult, 0, hexa_size); - epoch_min = get_epoch_minute(); + epoch_min = get_seconds() / 60; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) sg_init_table(sg, ARRAY_SIZE(sg)); @@ -867,11 +859,11 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, peer->status = ST_ALLOWED; pk_debug("ALLOWED", peer); + peer->login_sec = get_seconds(); if (nl_multicast_group > 0) msg_to_userspace_nl(info, peer, nl_multicast_group); - peer->login_min = get_epoch_minute(); return true; } From 7070678ab40c77620c6158d9cfceafc4837123a1 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 12 Oct 2009 00:48:37 +0200 Subject: [PATCH 55/68] pknock: remove redundant --chkip parameter Similar to commit v1.18-56-g35b00f8, one option for one thing really is enough. --- extensions/pknock/libxt_pknock.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/pknock/libxt_pknock.c b/extensions/pknock/libxt_pknock.c index e1c4cb5..b1ea6c7 100644 --- a/extensions/pknock/libxt_pknock.c +++ b/extensions/pknock/libxt_pknock.c @@ -26,7 +26,6 @@ static const struct option pknock_mt_opts[] = { { "closesecret",1, 0, 'z' }, { "strict", 0, 0, 'x' }, { "checkip", 0, 0, 'c' }, - { "chkip", 0, 0, 'c' }, { .name = NULL } }; @@ -45,7 +44,7 @@ static void pknock_mt_help(void) "Rule name.\n" " --checkip " "Matches if the source ip is in the list.\n" - " --chkip\n"); + ); } static unsigned int From 4d94687c15fa7e21e90689c9371a3d294752421e Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 12 Oct 2009 00:50:55 +0200 Subject: [PATCH 56/68] pknock: use C99 initializers for pknock_mt_opts --- extensions/pknock/libxt_pknock.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/extensions/pknock/libxt_pknock.c b/extensions/pknock/libxt_pknock.c index b1ea6c7..5da9080 100644 --- a/extensions/pknock/libxt_pknock.c +++ b/extensions/pknock/libxt_pknock.c @@ -19,14 +19,14 @@ static const struct option pknock_mt_opts[] = { /* .name, .has_arg, .flag, .val */ - { "knockports", 1, 0, 'k' }, - { "time", 1, 0, 't' }, - { "name", 1, 0, 'n' }, - { "opensecret", 1, 0, 'a' }, - { "closesecret",1, 0, 'z' }, - { "strict", 0, 0, 'x' }, - { "checkip", 0, 0, 'c' }, - { .name = NULL } + {.name = "knockports", .has_arg = true, .val = 'k'}, + {.name = "time", .has_arg = true, .val = 't'}, + {.name = "name", .has_arg = true, .val = 'n'}, + {.name = "opensecret", .has_arg = true, .val = 'a'}, + {.name = "closesecret", .has_arg = true, .val = 'z'}, + {.name = "strict", .has_arg = false, .val = 'x'}, + {.name = "checkip", .has_arg = false, .val = 'c'}, + {NULL}, }; static void pknock_mt_help(void) From 36035d52d31612c9998cc709c97c3e4bef6921ab Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Mon, 12 Oct 2009 00:01:30 +0200 Subject: [PATCH 57/68] pknock: purge void --secure option from help text Signed-off-by: Jan Rafaj --- extensions/pknock/libxt_pknock.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/extensions/pknock/libxt_pknock.c b/extensions/pknock/libxt_pknock.c index 5da9080..081f792 100644 --- a/extensions/pknock/libxt_pknock.c +++ b/extensions/pknock/libxt_pknock.c @@ -36,8 +36,6 @@ static void pknock_mt_help(void) "Matches destination port(s).\n" " --time seconds\n" "Time between port match.\n" - " --secure " - "hmac must be in the packets.\n" " --strict " "Knocks sequence must be exact.\n" " --name rule_name " From 5284b5db92dd93bd807e611f7a2e4ec76a7664f5 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 12 Oct 2009 00:56:55 +0200 Subject: [PATCH 58/68] pknock: remove redundant final check for any-option Just below the check that was just removed, the presence of --name is tested for, as --name is always required. That makes the (flags == 0) check superfluous, because non-zeroness is ensured by the name check. --- extensions/pknock/libxt_pknock.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/extensions/pknock/libxt_pknock.c b/extensions/pknock/libxt_pknock.c index 081f792..11318fa 100644 --- a/extensions/pknock/libxt_pknock.c +++ b/extensions/pknock/libxt_pknock.c @@ -206,9 +206,6 @@ static int pknock_mt_parse(int c, char **argv, int invert, unsigned int *flags, static void pknock_mt_check(unsigned int flags) { - if (!flags) - xtables_error(PARAMETER_PROBLEM, PKNOCK "expection an option.\n"); - if (!(flags & XT_PKNOCK_NAME)) xtables_error(PARAMETER_PROBLEM, PKNOCK "--name option is required.\n"); From 284c0e949396afd8156b48dac36d57e76dabbca1 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Mon, 12 Oct 2009 00:01:30 +0200 Subject: [PATCH 59/68] pknock: provide better help text for --time option Signed-off-by: Jan Rafaj --- extensions/pknock/libxt_pknock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/pknock/libxt_pknock.c b/extensions/pknock/libxt_pknock.c index 11318fa..b3e1bc3 100644 --- a/extensions/pknock/libxt_pknock.c +++ b/extensions/pknock/libxt_pknock.c @@ -35,7 +35,7 @@ static void pknock_mt_help(void) " --knockports port[,port,port,...] " "Matches destination port(s).\n" " --time seconds\n" - "Time between port match.\n" + "Max allowed time between knocks.\n" " --strict " "Knocks sequence must be exact.\n" " --name rule_name " From 9e5c2e7ee92ddf3f94cf6e815a2078c5b6634bb1 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Mon, 12 Oct 2009 00:01:31 +0200 Subject: [PATCH 60/68] pknock: implement automatic closing Added the optional "--autoclose" parameter (takes minutes) that closes the pknock-opened rule in a specified time. Signed-off-by: Jan Rafaj --- extensions/pknock/libxt_pknock.c | 29 ++++++++++++++++-- extensions/pknock/xt_pknock.c | 52 +++++++++++++++++++++++++++----- extensions/pknock/xt_pknock.h | 2 ++ 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/extensions/pknock/libxt_pknock.c b/extensions/pknock/libxt_pknock.c index b3e1bc3..0027236 100644 --- a/extensions/pknock/libxt_pknock.c +++ b/extensions/pknock/libxt_pknock.c @@ -21,8 +21,9 @@ static const struct option pknock_mt_opts[] = { /* .name, .has_arg, .flag, .val */ {.name = "knockports", .has_arg = true, .val = 'k'}, {.name = "time", .has_arg = true, .val = 't'}, + {.name = "autoclose", .has_arg = true, .val = 'a'}, {.name = "name", .has_arg = true, .val = 'n'}, - {.name = "opensecret", .has_arg = true, .val = 'a'}, + {.name = "opensecret", .has_arg = true, .val = 'o'}, {.name = "closesecret", .has_arg = true, .val = 'z'}, {.name = "strict", .has_arg = false, .val = 'x'}, {.name = "checkip", .has_arg = false, .val = 'c'}, @@ -36,6 +37,9 @@ static void pknock_mt_help(void) "Matches destination port(s).\n" " --time seconds\n" "Max allowed time between knocks.\n" + " --autoclose minutes\n" + "Time after which to automatically close opened\n" + "\t\t\t\t\tport(s).\n" " --strict " "Knocks sequence must be exact.\n" " --name rule_name " @@ -106,6 +110,7 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, { const char *proto; struct xt_pknock_mtinfo *info = (void *)(*match)->data; + unsigned int tmp; switch (c) { case 'k': /* --knockports */ @@ -131,6 +136,18 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, *flags |= XT_PKNOCK_TIME; break; + case 'a': /* --autoclose */ + if (*flags & XT_PKNOCK_AUTOCLOSE) + xtables_error(PARAMETER_PROBLEM, PKNOCK + "cannot use --autoclose twice.\n"); + if (!xtables_strtoui(optarg, NULL, &tmp, 0, ~0U)) + xtables_param_act(XTF_BAD_VALUE, PKNOCK, + "--autoclose", optarg); + info->autoclose_time = tmp; + info->option |= XT_PKNOCK_AUTOCLOSE; + *flags |= XT_PKNOCK_AUTOCLOSE; + break; + case 'n': /* --name */ if (*flags & XT_PKNOCK_NAME) xtables_error(PARAMETER_PROBLEM, PKNOCK @@ -146,7 +163,7 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, #endif break; - case 'a': /* --opensecret */ + case 'o': /* --opensecret */ if (*flags & XT_PKNOCK_OPENSECRET) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --opensecret twice.\n"); @@ -236,6 +253,9 @@ static void pknock_mt_check(unsigned int flags) if (flags & XT_PKNOCK_TIME) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot specify --time with --checkip.\n"); + if (flags & XT_PKNOCK_AUTOCLOSE) + xtables_error(PARAMETER_PROBLEM, PKNOCK + "cannot specify --autoclose with --checkip.\n"); } } @@ -254,6 +274,8 @@ static void pknock_mt_print(const void *ip, } if (info->option & XT_PKNOCK_TIME) printf("time %ld ", (long)info->max_time); + if (info->option & XT_PKNOCK_AUTOCLOSE) + printf("autoclose %lu ", (unsigned long)info->autoclose_time); if (info->option & XT_PKNOCK_NAME) printf("name %s ", info->rule_name); if (info->option & XT_PKNOCK_OPENSECRET) @@ -279,6 +301,9 @@ static void pknock_mt_save(const void *ip, const struct xt_entry_match *match) } if (info->option & XT_PKNOCK_TIME) printf("--time %ld ", (long)info->max_time); + if (info->option & XT_PKNOCK_AUTOCLOSE) + printf("--autoclose %lu ", + (unsigned long)info->autoclose_time); if (info->option & XT_PKNOCK_NAME) printf("--name %s ", info->rule_name); if (info->option & XT_PKNOCK_OPENSECRET) diff --git a/extensions/pknock/xt_pknock.c b/extensions/pknock/xt_pknock.c index f9a9ade..9fe8ae4 100644 --- a/extensions/pknock/xt_pknock.c +++ b/extensions/pknock/xt_pknock.c @@ -68,6 +68,7 @@ struct xt_pknock_rule { struct list_head *peer_head; struct proc_dir_entry *status_proc; unsigned long max_time; + unsigned long autoclose_time; }; /** @@ -244,6 +245,7 @@ pknock_seq_show(struct seq_file *s, void *v) { const struct list_head *pos, *n; const struct peer *peer; + unsigned long time; unsigned long expir_time; const struct list_head *peer_head = v; @@ -264,6 +266,15 @@ pknock_seq_show(struct seq_file *s, void *v) seq_printf(s, "expir_time=%ld ", expir_time); seq_printf(s, "accepted_knock_count=%lu ", (unsigned long)peer->accepted_knock_count); + if (rule->autoclose_time != 0) { + time = 0; + if (time_before(get_seconds(), peer->login_sec + + rule->autoclose_time * 60)) + time = peer->login_sec + + rule->autoclose_time * 60 - + get_seconds(); + seq_printf(s, "autoclose_time=%lu [secs] ", time); + } seq_printf(s, "\n"); } @@ -314,6 +325,19 @@ static void update_rule_timer(struct xt_pknock_rule *rule) add_timer(&rule->timer); } +/** + * @peer + * @autoclose_time + * + * Returns true if autoclose due, or false if still valid. + */ +static inline bool +autoclose_time_passed(const struct peer *peer, unsigned int autoclose_time) +{ + return peer != NULL && autoclose_time != 0 && time_after(get_seconds(), + peer->login_sec + autoclose_time * 60); +} + /** * @peer * @max_time @@ -352,8 +376,10 @@ peer_gc(unsigned long r) hashtable_for_each_safe(pos, n, rule->peer_head, peer_hashsize, i) { peer = list_entry(pos, struct peer, head); - if (!has_logged_during_this_minute(peer) && - is_time_exceeded(peer, rule->max_time)) + if ((!has_logged_during_this_minute(peer) && + is_time_exceeded(peer, rule->max_time)) || + (peer->status == ST_ALLOWED && + autoclose_time_passed(peer, rule->autoclose_time))) { pk_debug("DESTROYED", peer); list_del(pos); @@ -440,9 +466,10 @@ add_rule(struct xt_pknock_mtinfo *info) strncpy(rule->rule_name, info->rule_name, info->rule_name_len); rule->rule_name_len = info->rule_name_len; - rule->ref_count = 1; - rule->max_time = info->max_time; - rule->peer_head = alloc_hashtable(peer_hashsize); + rule->ref_count = 1; + rule->max_time = info->max_time; + rule->autoclose_time = info->autoclose_time; + rule->peer_head = alloc_hashtable(peer_hashsize); if (rule->peer_head == NULL) goto out; @@ -994,7 +1021,7 @@ static bool pknock_mt(const struct sk_buff *skb, } } #endif - goto out; + goto out; } if (is_first_knock(peer, info, hdr.port)) { @@ -1008,7 +1035,16 @@ static bool pknock_mt(const struct sk_buff *skb, } out: - if (ret != 0) + /* Handle cur.peer matching and deletion after autoclose_time passed */ + if (ret && autoclose_time_passed(peer, rule->autoclose_time)) { + pk_debug("AUTOCLOSE TIME PASSED => BLOCKED", peer); + ret = false; + if (iph->protocol == IPPROTO_TCP || + !has_logged_during_this_minute(peer)) + remove_peer(peer); + } + + if (ret) pk_debug("PASS OK", peer); spin_unlock_bh(&list_lock); return ret; @@ -1061,6 +1097,8 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) #endif if (info->option & XT_PKNOCK_TIME) RETURN_ERR("Can't specify --time with --checkip.\n"); + if (info->option & XT_PKNOCK_AUTOCLOSE) + RETURN_ERR("Can't specify --autoclose with --checkip.\n"); } #ifdef PK_CRYPTO diff --git a/extensions/pknock/xt_pknock.h b/extensions/pknock/xt_pknock.h index 38fb62d..d44905b 100644 --- a/extensions/pknock/xt_pknock.h +++ b/extensions/pknock/xt_pknock.h @@ -21,6 +21,7 @@ enum { XT_PKNOCK_CHECKIP = 1 << 4, XT_PKNOCK_OPENSECRET = 1 << 5, XT_PKNOCK_CLOSESECRET = 1 << 6, + XT_PKNOCK_AUTOCLOSE = 1 << 7, /* Can never change these, as they are make up the user protocol. */ XT_PKNOCK_MAX_PORTS = 15, @@ -41,6 +42,7 @@ struct xt_pknock_mtinfo { uint8_t ports_count; /* number of ports */ uint16_t port[XT_PKNOCK_MAX_PORTS]; /* port[,port,port,...] */ uint32_t max_time; /* max matching time between ports */ + uint32_t autoclose_time; }; struct xt_pknock_nl_msg { From 98e5dfd6ef7ece74fb329209247e0c636837eee4 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 12 Oct 2009 01:21:26 +0200 Subject: [PATCH 61/68] pknock: remove some #ifdef PK_CRYPTO guards A number of guarded places do not use any crypto functions. So the userspace does not pass in an info with --opensecret/--closesecret defined. --- extensions/pknock/xt_pknock.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/extensions/pknock/xt_pknock.c b/extensions/pknock/xt_pknock.c index 9fe8ae4..33b9aaf 100644 --- a/extensions/pknock/xt_pknock.c +++ b/extensions/pknock/xt_pknock.c @@ -866,7 +866,6 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, return false; } -#ifdef PK_CRYPTO /* If security is needed. */ if (info->option & XT_PKNOCK_OPENSECRET ) { if (hdr->proto != IPPROTO_UDP) @@ -875,7 +874,6 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, if (!pass_security(peer, info, hdr->payload, hdr->payload_len)) return false; } -#endif /* Just update the timer when there is a state change. */ update_rule_timer(rule); @@ -1010,7 +1008,6 @@ static bool pknock_mt(const struct sk_buff *skb, /* Sets, updates, removes or checks the peer matching status. */ if (info->option & XT_PKNOCK_KNOCKPORT) { if ((ret = is_allowed(peer))) { -#ifdef PK_CRYPTO if (info->option & XT_PKNOCK_CLOSESECRET && iph->protocol == IPPROTO_UDP) { @@ -1020,7 +1017,6 @@ static bool pknock_mt(const struct sk_buff *skb, ret = false; } } -#endif goto out; } @@ -1068,40 +1064,37 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) if (!(info->option & XT_PKNOCK_NAME)) RETURN_ERR("You must specify --name option.\n"); -#ifdef PK_CRYPTO +#ifndef PK_CRYPTO + if (info->option & (XT_PKNOCK_OPENSECRET | XT_PKNOCK_CLOSESECRET)) + RETURN_ERR("No crypto support available; " + "cannot use opensecret/closescret\n"); +#endif if ((info->option & XT_PKNOCK_OPENSECRET) && (info->ports_count != 1)) RETURN_ERR("--opensecret must have just one knock port\n"); -#endif - if (info->option & XT_PKNOCK_KNOCKPORT) { if (info->option & XT_PKNOCK_CHECKIP) RETURN_ERR("Can't specify --knockports with --checkip.\n"); -#ifdef PK_CRYPTO if ((info->option & XT_PKNOCK_OPENSECRET) && !(info->option & XT_PKNOCK_CLOSESECRET)) RETURN_ERR("--opensecret must go with --closesecret.\n"); if ((info->option & XT_PKNOCK_CLOSESECRET) && !(info->option & XT_PKNOCK_OPENSECRET)) RETURN_ERR("--closesecret must go with --opensecret.\n"); -#endif } if (info->option & XT_PKNOCK_CHECKIP) { if (info->option & XT_PKNOCK_KNOCKPORT) RETURN_ERR("Can't specify --checkip with --knockports.\n"); -#ifdef PK_CRYPTO if ((info->option & XT_PKNOCK_OPENSECRET) || (info->option & XT_PKNOCK_CLOSESECRET)) RETURN_ERR("Can't specify --opensecret and --closesecret" " with --checkip.\n"); -#endif if (info->option & XT_PKNOCK_TIME) RETURN_ERR("Can't specify --time with --checkip.\n"); if (info->option & XT_PKNOCK_AUTOCLOSE) RETURN_ERR("Can't specify --autoclose with --checkip.\n"); } -#ifdef PK_CRYPTO if (info->option & XT_PKNOCK_OPENSECRET) { if (info->open_secret_len == info->close_secret_len) { if (memcmp(info->open_secret, info->close_secret, @@ -1109,7 +1102,6 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) RETURN_ERR("opensecret & closesecret cannot be equal.\n"); } } -#endif if (!add_rule(info)) RETURN_ERR("add_rule() error in checkentry() function.\n"); From 2b2b6246f0f2ded4d351aa9e7e3fe7b8a81059c6 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Mon, 12 Oct 2009 00:01:32 +0200 Subject: [PATCH 62/68] pknock: Make non-zero time mandatory for TCP mode This avoids DDoS on the first-in-sequence TCP knockport, which would otherwise fill up the peer table permanently - especially if the user does not specify --autoclose - and would thus cause permanent pknock DoS. Signed-off-by: Jan Rafaj --- extensions/pknock/libxt_pknock.c | 6 ++++++ extensions/pknock/xt_pknock.c | 2 ++ 2 files changed, 8 insertions(+) diff --git a/extensions/pknock/libxt_pknock.c b/extensions/pknock/libxt_pknock.c index 0027236..c6ad809 100644 --- a/extensions/pknock/libxt_pknock.c +++ b/extensions/pknock/libxt_pknock.c @@ -132,6 +132,9 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags, xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot use --time twice.\n"); info->max_time = atoi(optarg); + if (info->max_time == 0) + xtables_error(PARAMETER_PROBLEM, PKNOCK + "--time number must be > 0.\n"); info->option |= XT_PKNOCK_TIME; *flags |= XT_PKNOCK_TIME; break; @@ -256,6 +259,9 @@ static void pknock_mt_check(unsigned int flags) if (flags & XT_PKNOCK_AUTOCLOSE) xtables_error(PARAMETER_PROBLEM, PKNOCK "cannot specify --autoclose with --checkip.\n"); + } else if (!(flags & (XT_PKNOCK_OPENSECRET | XT_PKNOCK_TIME))) { + xtables_error(PARAMETER_PROBLEM, PKNOCK + "you must specify --time.\n"); } } diff --git a/extensions/pknock/xt_pknock.c b/extensions/pknock/xt_pknock.c index 33b9aaf..44d3fdc 100644 --- a/extensions/pknock/xt_pknock.c +++ b/extensions/pknock/xt_pknock.c @@ -1093,6 +1093,8 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par) RETURN_ERR("Can't specify --time with --checkip.\n"); if (info->option & XT_PKNOCK_AUTOCLOSE) RETURN_ERR("Can't specify --autoclose with --checkip.\n"); + } else if (!(info->option & (XT_PKNOCK_OPENSECRET | XT_PKNOCK_TIME))) { + RETURN_ERR("you must specify --time.\n"); } if (info->option & XT_PKNOCK_OPENSECRET) { From a9214277463c162a7ff9930711a1b91c4d5f13b8 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Mon, 12 Oct 2009 00:01:33 +0200 Subject: [PATCH 63/68] pknock: display only pknock mode and state relevant information in procfs Signed-off-by: Jan Rafaj --- extensions/pknock/xt_pknock.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/extensions/pknock/xt_pknock.c b/extensions/pknock/xt_pknock.c index 44d3fdc..429f487 100644 --- a/extensions/pknock/xt_pknock.c +++ b/extensions/pknock/xt_pknock.c @@ -43,6 +43,7 @@ enum status { }; /** + * @timestamp: seconds, but not since epoch (uses jiffies/HZ) * @login_sec: seconds at login since the epoch */ struct peer { @@ -246,8 +247,6 @@ pknock_seq_show(struct seq_file *s, void *v) const struct list_head *pos, *n; const struct peer *peer; unsigned long time; - unsigned long expir_time; - const struct list_head *peer_head = v; const struct proc_dir_entry *pde = s->private; @@ -255,18 +254,22 @@ pknock_seq_show(struct seq_file *s, void *v) list_for_each_safe(pos, n, peer_head) { peer = list_entry(pos, struct peer, head); - expir_time = time_before(jiffies/HZ, - peer->timestamp + rule->max_time) - ? ((peer->timestamp + rule->max_time)-(jiffies/HZ)) : 0; seq_printf(s, "src=" NIPQUAD_FMT " ", NIPQUAD(peer->ip)); seq_printf(s, "proto=%s ", (peer->proto == IPPROTO_TCP) ? "TCP" : "UDP"); seq_printf(s, "status=%s ", status_itoa(peer->status)); - seq_printf(s, "expir_time=%ld ", expir_time); seq_printf(s, "accepted_knock_count=%lu ", (unsigned long)peer->accepted_knock_count); - if (rule->autoclose_time != 0) { + if (peer->status == ST_MATCHING) { + time = 0; + if (time_before(jiffies / HZ, peer->timestamp + + rule->max_time)) + time = peer->timestamp + rule->max_time - + jiffies / HZ; + seq_printf(s, "expir_time=%lu [secs] ", time); + } + if (peer->status == ST_ALLOWED && rule->autoclose_time != 0) { time = 0; if (time_before(get_seconds(), peer->login_sec + rule->autoclose_time * 60)) From 6de54e5fcde605bb2a86d266124f4253561ce80c Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 12 Oct 2009 17:01:10 +0200 Subject: [PATCH 64/68] pknock: shrink struct peer --- extensions/pknock/xt_pknock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/pknock/xt_pknock.c b/extensions/pknock/xt_pknock.c index 429f487..4aad76f 100644 --- a/extensions/pknock/xt_pknock.c +++ b/extensions/pknock/xt_pknock.c @@ -49,11 +49,11 @@ enum status { struct peer { struct list_head head; __be32 ip; - uint8_t proto; uint32_t accepted_knock_count; - enum status status; unsigned long timestamp; unsigned long login_sec; + enum status status; + uint8_t proto; }; /** From 71beab548d8b60ac9057e82477140fdc4d38c97d Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Mon, 12 Oct 2009 00:01:34 +0200 Subject: [PATCH 65/68] pknock: change comments and debug statements to give more appropriate help Renames: update_rule_timer() -> update_gc_rule_timer() is_time_exceeded() -> is_interknock_time_exceeded() Changed some debug messages and comments to give more appropriate meanings. Signed-off-by: Jan Rafaj --- extensions/pknock/xt_pknock.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/extensions/pknock/xt_pknock.c b/extensions/pknock/xt_pknock.c index 4aad76f..e8dd654 100644 --- a/extensions/pknock/xt_pknock.c +++ b/extensions/pknock/xt_pknock.c @@ -319,7 +319,7 @@ static const struct file_operations pknock_proc_ops = { * * @rule */ -static void update_rule_timer(struct xt_pknock_rule *rule) +static void update_rule_gc_timer(struct xt_pknock_rule *rule) { if (timer_pending(&rule->timer)) del_timer(&rule->timer); @@ -347,7 +347,7 @@ autoclose_time_passed(const struct peer *peer, unsigned int autoclose_time) * @return: 1 time exceeded, 0 still valid */ static inline bool -is_time_exceeded(const struct peer *peer, unsigned int max_time) +is_interknock_time_exceeded(const struct peer *peer, unsigned int max_time) { return peer != NULL && time_after(jiffies / HZ, peer->timestamp + max_time); @@ -364,7 +364,7 @@ has_logged_during_this_minute(const struct peer *peer) } /** - * Garbage collector. It removes the old entries after timer has expired. + * Garbage collector. It removes the old entries after tis timers have expired. * * @r: rule */ @@ -380,7 +380,7 @@ peer_gc(unsigned long r) peer = list_entry(pos, struct peer, head); if ((!has_logged_during_this_minute(peer) && - is_time_exceeded(peer, rule->max_time)) || + is_interknock_time_exceeded(peer, rule->max_time)) || (peer->status == ST_ALLOWED && autoclose_time_passed(peer, rule->autoclose_time))) { @@ -831,7 +831,7 @@ pass_security(struct peer *peer, const struct xt_pknock_mtinfo *info, /* The peer can't log more than once during the same minute. */ if (has_logged_during_this_minute(peer)) { - pk_debug("BLOCKED", peer); + pk_debug("DENIED (anti-spoof protection)", peer); return false; } /* Check for OPEN secret */ @@ -845,13 +845,15 @@ pass_security(struct peer *peer, const struct xt_pknock_mtinfo *info, #endif /* PK_CRYPTO */ /** - * It updates the peer matching status. + * Validates the peer and updates the peer status for an initiating or + * in-sequence knock packet. * * @peer * @info * @rule * @hdr - * @return: 1 if allowed, 0 otherwise + * + * Returns true if allowed, false otherwise. */ static bool update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, @@ -878,8 +880,8 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, return false; } - /* Just update the timer when there is a state change. */ - update_rule_timer(rule); + /* Update the gc timer when there is a state change. */ + update_rule_gc_timer(rule); ++peer->accepted_knock_count; @@ -895,13 +897,13 @@ update_peer(struct peer *peer, const struct xt_pknock_mtinfo *info, return true; } - /* Controls the max matching time between ports. */ + /* Immediate control over the maximum time between knocks. */ if (info->option & XT_PKNOCK_TIME) { time = jiffies/HZ; - if (is_time_exceeded(peer, info->max_time)) { - pk_debug("TIME EXCEEDED", peer); - pk_debug("DESTROYED", peer); + if (is_interknock_time_exceeded(peer, info->max_time)) { + pk_debug("ST_MATCHING knock received after interknock " + "time passed => destroyed", peer); pr_debug("max_time: %ld - time: %ld\n", peer->timestamp + info->max_time, time); @@ -935,7 +937,7 @@ is_close_knock(const struct peer *peer, const struct xt_pknock_mtinfo *info, info->close_secret_len, peer->ip, payload, payload_len)) { - pk_debug("RESET", peer); + pk_debug("BLOCKED", peer); return true; } return false; From b132101b63a044a42dfb7e9ed825de4099f60ba9 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Mon, 12 Oct 2009 00:01:35 +0200 Subject: [PATCH 66/68] pknock: check interknock time only for !ST_ALLOWED peers Fixes a bug whereby an ST_ALLOWED peer existing for a time greater than gc_expir_time would be gc-deleted, because both !has_logged_during_this_minute(peer) and is_interknock_time_exceeded(peer, rule->max_time) would be satisfied for ST_ALLOWED hosts. We also no longer test for !has_logged_during_this_minute(peer) in peer_gc(), since there is really no need for this: the anti-spoof minute check is performed (and subsequent remove_peer(peer) called if needed) for each passing UDP-mode peer with expired autoclose in pknock_mt(), given that --autoclose has been specified. If autoclose has not been set, it will be subject to reset_knock_status(peer) called from knock_mt() upon receiving the first closing secret - so it is still guaranteed to disappear at the closest opportunity. Signed-off-by: Jan Rafaj --- extensions/pknock/xt_pknock.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/extensions/pknock/xt_pknock.c b/extensions/pknock/xt_pknock.c index e8dd654..50789e5 100644 --- a/extensions/pknock/xt_pknock.c +++ b/extensions/pknock/xt_pknock.c @@ -376,15 +376,20 @@ peer_gc(unsigned long r) struct peer *peer; struct list_head *pos, *n; + pr_debug("(S) running %s\n", __func__); hashtable_for_each_safe(pos, n, rule->peer_head, peer_hashsize, i) { peer = list_entry(pos, struct peer, head); - if ((!has_logged_during_this_minute(peer) && + /* + * Remove any peer whose (inter-knock) max_time + * or autoclose_time passed. + */ + if ((peer->status != ST_ALLOWED && is_interknock_time_exceeded(peer, rule->max_time)) || (peer->status == ST_ALLOWED && autoclose_time_passed(peer, rule->autoclose_time))) { - pk_debug("DESTROYED", peer); + pk_debug("GC-DELETED", peer); list_del(pos); kfree(peer); } From af21c1d369c6312be1fd31b48e1b02b7b15d08a5 Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Mon, 12 Oct 2009 00:01:35 +0200 Subject: [PATCH 67/68] pknock: preserve time/autoclose values for rules added in arbitrary order Handle specific case when someone put a (master) --opensecret --closesecret rule after a --checkip rule with the same name. Signed-off-by: Jan Rafaj --- extensions/pknock/xt_pknock.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/extensions/pknock/xt_pknock.c b/extensions/pknock/xt_pknock.c index 50789e5..9bfcdf4 100644 --- a/extensions/pknock/xt_pknock.c +++ b/extensions/pknock/xt_pknock.c @@ -453,6 +453,12 @@ add_rule(struct xt_pknock_mtinfo *info) if (rulecmp(info, rule)) { ++rule->ref_count; + + if (info->option & XT_PKNOCK_OPENSECRET) { + rule->max_time = info->max_time; + rule->autoclose_time = info->autoclose_time; + } + if (info->option & XT_PKNOCK_CHECKIP) { pr_debug("add_rule() (AC)" " rule found: %s - " From c7036a88f1e50d9ad1143f9a634de6a2376e5766 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 12 Oct 2009 18:40:34 +0200 Subject: [PATCH 68/68] doc: pknock changelog --- doc/changelog.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/changelog.txt b/doc/changelog.txt index 494818a..e541322 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -6,6 +6,14 @@ - pknock: "strict" and "checkip" flags were not displayed in `iptables -L` - pknock: the GC expire time's lower bound is now the default gc time (65000 msec) to avoid rendering anti-spoof protection in SPA mode useless + - pknock: avoid crash on memory allocation failure and fix memleak + - pknock: avoid fillup of peer table during DDoS + - pknock: automatic closing of ports + - pknock: make non-zero time mandatory for TCP mode + - pknock: display only pknock mode and state relevant information in procfs + - pknock: check interknock time only for !ST_ALLOWED peers + - pknock: preserve time/autoclose values for rules added in + reverse/arbitrary order Xtables-addons 1.18 (September 09 2009)