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 <jr+netfilter-devel@cedric.unob.cz>
This commit is contained in:
Jan Rafaj
2009-10-12 00:01:31 +02:00
committed by Jan Engelhardt
parent 284c0e9493
commit 9e5c2e7ee9
3 changed files with 74 additions and 9 deletions

View File

@@ -21,8 +21,9 @@ static const struct option pknock_mt_opts[] = {
/* .name, .has_arg, .flag, .val */ /* .name, .has_arg, .flag, .val */
{.name = "knockports", .has_arg = true, .val = 'k'}, {.name = "knockports", .has_arg = true, .val = 'k'},
{.name = "time", .has_arg = true, .val = 't'}, {.name = "time", .has_arg = true, .val = 't'},
{.name = "autoclose", .has_arg = true, .val = 'a'},
{.name = "name", .has_arg = true, .val = 'n'}, {.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 = "closesecret", .has_arg = true, .val = 'z'},
{.name = "strict", .has_arg = false, .val = 'x'}, {.name = "strict", .has_arg = false, .val = 'x'},
{.name = "checkip", .has_arg = false, .val = 'c'}, {.name = "checkip", .has_arg = false, .val = 'c'},
@@ -36,6 +37,9 @@ static void pknock_mt_help(void)
"Matches destination port(s).\n" "Matches destination port(s).\n"
" --time seconds\n" " --time seconds\n"
"Max allowed time between knocks.\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 " " --strict "
"Knocks sequence must be exact.\n" "Knocks sequence must be exact.\n"
" --name rule_name " " --name rule_name "
@@ -106,6 +110,7 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags,
{ {
const char *proto; const char *proto;
struct xt_pknock_mtinfo *info = (void *)(*match)->data; struct xt_pknock_mtinfo *info = (void *)(*match)->data;
unsigned int tmp;
switch (c) { switch (c) {
case 'k': /* --knockports */ case 'k': /* --knockports */
@@ -131,6 +136,18 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags,
*flags |= XT_PKNOCK_TIME; *flags |= XT_PKNOCK_TIME;
break; 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 */ case 'n': /* --name */
if (*flags & XT_PKNOCK_NAME) if (*flags & XT_PKNOCK_NAME)
xtables_error(PARAMETER_PROBLEM, PKNOCK xtables_error(PARAMETER_PROBLEM, PKNOCK
@@ -146,7 +163,7 @@ __pknock_parse(int c, char **argv, int invert, unsigned int *flags,
#endif #endif
break; break;
case 'a': /* --opensecret */ case 'o': /* --opensecret */
if (*flags & XT_PKNOCK_OPENSECRET) if (*flags & XT_PKNOCK_OPENSECRET)
xtables_error(PARAMETER_PROBLEM, PKNOCK xtables_error(PARAMETER_PROBLEM, PKNOCK
"cannot use --opensecret twice.\n"); "cannot use --opensecret twice.\n");
@@ -236,6 +253,9 @@ static void pknock_mt_check(unsigned int flags)
if (flags & XT_PKNOCK_TIME) if (flags & XT_PKNOCK_TIME)
xtables_error(PARAMETER_PROBLEM, PKNOCK xtables_error(PARAMETER_PROBLEM, PKNOCK
"cannot specify --time with --checkip.\n"); "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) if (info->option & XT_PKNOCK_TIME)
printf("time %ld ", (long)info->max_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) if (info->option & XT_PKNOCK_NAME)
printf("name %s ", info->rule_name); printf("name %s ", info->rule_name);
if (info->option & XT_PKNOCK_OPENSECRET) 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) if (info->option & XT_PKNOCK_TIME)
printf("--time %ld ", (long)info->max_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) if (info->option & XT_PKNOCK_NAME)
printf("--name %s ", info->rule_name); printf("--name %s ", info->rule_name);
if (info->option & XT_PKNOCK_OPENSECRET) if (info->option & XT_PKNOCK_OPENSECRET)

View File

@@ -68,6 +68,7 @@ struct xt_pknock_rule {
struct list_head *peer_head; struct list_head *peer_head;
struct proc_dir_entry *status_proc; struct proc_dir_entry *status_proc;
unsigned long max_time; 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 list_head *pos, *n;
const struct peer *peer; const struct peer *peer;
unsigned long time;
unsigned long expir_time; unsigned long expir_time;
const struct list_head *peer_head = v; 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, "expir_time=%ld ", expir_time);
seq_printf(s, "accepted_knock_count=%lu ", seq_printf(s, "accepted_knock_count=%lu ",
(unsigned long)peer->accepted_knock_count); (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"); seq_printf(s, "\n");
} }
@@ -314,6 +325,19 @@ static void update_rule_timer(struct xt_pknock_rule *rule)
add_timer(&rule->timer); 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 * @peer
* @max_time * @max_time
@@ -352,8 +376,10 @@ peer_gc(unsigned long r)
hashtable_for_each_safe(pos, n, rule->peer_head, peer_hashsize, i) { hashtable_for_each_safe(pos, n, rule->peer_head, peer_hashsize, i) {
peer = list_entry(pos, struct peer, head); 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)) is_time_exceeded(peer, rule->max_time)) ||
(peer->status == ST_ALLOWED &&
autoclose_time_passed(peer, rule->autoclose_time)))
{ {
pk_debug("DESTROYED", peer); pk_debug("DESTROYED", peer);
list_del(pos); 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); strncpy(rule->rule_name, info->rule_name, info->rule_name_len);
rule->rule_name_len = info->rule_name_len; rule->rule_name_len = info->rule_name_len;
rule->ref_count = 1; rule->ref_count = 1;
rule->max_time = info->max_time; rule->max_time = info->max_time;
rule->peer_head = alloc_hashtable(peer_hashsize); rule->autoclose_time = info->autoclose_time;
rule->peer_head = alloc_hashtable(peer_hashsize);
if (rule->peer_head == NULL) if (rule->peer_head == NULL)
goto out; goto out;
@@ -994,7 +1021,7 @@ static bool pknock_mt(const struct sk_buff *skb,
} }
} }
#endif #endif
goto out; goto out;
} }
if (is_first_knock(peer, info, hdr.port)) { if (is_first_knock(peer, info, hdr.port)) {
@@ -1008,7 +1035,16 @@ static bool pknock_mt(const struct sk_buff *skb,
} }
out: 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); pk_debug("PASS OK", peer);
spin_unlock_bh(&list_lock); spin_unlock_bh(&list_lock);
return ret; return ret;
@@ -1061,6 +1097,8 @@ static bool pknock_mt_check(const struct xt_mtchk_param *par)
#endif #endif
if (info->option & XT_PKNOCK_TIME) if (info->option & XT_PKNOCK_TIME)
RETURN_ERR("Can't specify --time with --checkip.\n"); 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 #ifdef PK_CRYPTO

View File

@@ -21,6 +21,7 @@ enum {
XT_PKNOCK_CHECKIP = 1 << 4, XT_PKNOCK_CHECKIP = 1 << 4,
XT_PKNOCK_OPENSECRET = 1 << 5, XT_PKNOCK_OPENSECRET = 1 << 5,
XT_PKNOCK_CLOSESECRET = 1 << 6, XT_PKNOCK_CLOSESECRET = 1 << 6,
XT_PKNOCK_AUTOCLOSE = 1 << 7,
/* Can never change these, as they are make up the user protocol. */ /* Can never change these, as they are make up the user protocol. */
XT_PKNOCK_MAX_PORTS = 15, XT_PKNOCK_MAX_PORTS = 15,
@@ -41,6 +42,7 @@ struct xt_pknock_mtinfo {
uint8_t ports_count; /* number of ports */ uint8_t ports_count; /* number of ports */
uint16_t port[XT_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 */ uint32_t max_time; /* max matching time between ports */
uint32_t autoclose_time;
}; };
struct xt_pknock_nl_msg { struct xt_pknock_nl_msg {