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 {