Skip to content

Commit

Permalink
tcp_diag: report TCP MD5 signing keys and addresses
Browse files Browse the repository at this point in the history
Report TCP MD5 (RFC2385) signing keys, addresses and address prefixes to
processes with CAP_NET_ADMIN requesting INET_DIAG_INFO. Currently it is
not possible to retrieve these from the kernel once they have been
configured on sockets.

Signed-off-by: Ivan Delalande <colona@arista.com>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
colona authored and davem330 committed Sep 2, 2017
1 parent b37e884 commit c03fa9b
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 6 deletions.
1 change: 1 addition & 0 deletions include/uapi/linux/inet_diag.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ enum {
INET_DIAG_MARK,
INET_DIAG_BBRINFO,
INET_DIAG_CLASS_ID,
INET_DIAG_MD5SIG,
__INET_DIAG_MAX,
};

Expand Down
9 changes: 9 additions & 0 deletions include/uapi/linux/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,13 @@ struct tcp_md5sig {
__u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */
};

/* INET_DIAG_MD5SIG */
struct tcp_diag_md5sig {
__u8 tcpm_family;
__u8 tcpm_prefixlen;
__u16 tcpm_keylen;
__be32 tcpm_addr[4];
__u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN];
};

#endif /* _UAPI_LINUX_TCP_H */
109 changes: 103 additions & 6 deletions net/ipv4/tcp_diag.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <linux/tcp.h>

#include <net/netlink.h>
#include <net/tcp.h>

static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
Expand All @@ -36,6 +37,100 @@ static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
tcp_get_info(sk, info);
}

#ifdef CONFIG_TCP_MD5SIG
static void tcp_diag_md5sig_fill(struct tcp_diag_md5sig *info,
const struct tcp_md5sig_key *key)
{
info->tcpm_family = key->family;
info->tcpm_prefixlen = key->prefixlen;
info->tcpm_keylen = key->keylen;
memcpy(info->tcpm_key, key->key, key->keylen);

if (key->family == AF_INET)
info->tcpm_addr[0] = key->addr.a4.s_addr;
#if IS_ENABLED(CONFIG_IPV6)
else if (key->family == AF_INET6)
memcpy(&info->tcpm_addr, &key->addr.a6,
sizeof(info->tcpm_addr));
#endif
}

static int tcp_diag_put_md5sig(struct sk_buff *skb,
const struct tcp_md5sig_info *md5sig)
{
const struct tcp_md5sig_key *key;
struct tcp_diag_md5sig *info;
struct nlattr *attr;
int md5sig_count = 0;

hlist_for_each_entry_rcu(key, &md5sig->head, node)
md5sig_count++;
if (md5sig_count == 0)
return 0;

attr = nla_reserve(skb, INET_DIAG_MD5SIG,
md5sig_count * sizeof(struct tcp_diag_md5sig));
if (!attr)
return -EMSGSIZE;

info = nla_data(attr);
memset(info, 0, md5sig_count * sizeof(struct tcp_diag_md5sig));
hlist_for_each_entry_rcu(key, &md5sig->head, node) {
tcp_diag_md5sig_fill(info++, key);
if (--md5sig_count == 0)
break;
}

return 0;
}
#endif

static int tcp_diag_get_aux(struct sock *sk, bool net_admin,
struct sk_buff *skb)
{
#ifdef CONFIG_TCP_MD5SIG
if (net_admin) {
struct tcp_md5sig_info *md5sig;
int err = 0;

rcu_read_lock();
md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info);
if (md5sig)
err = tcp_diag_put_md5sig(skb, md5sig);
rcu_read_unlock();
if (err < 0)
return err;
}
#endif

return 0;
}

static size_t tcp_diag_get_aux_size(struct sock *sk, bool net_admin)
{
size_t size = 0;

#ifdef CONFIG_TCP_MD5SIG
if (net_admin && sk_fullsock(sk)) {
const struct tcp_md5sig_info *md5sig;
const struct tcp_md5sig_key *key;
size_t md5sig_count = 0;

rcu_read_lock();
md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info);
if (md5sig) {
hlist_for_each_entry_rcu(key, &md5sig->head, node)
md5sig_count++;
}
rcu_read_unlock();
size += nla_total_size(md5sig_count *
sizeof(struct tcp_diag_md5sig));
}
#endif

return size;
}

static void tcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
const struct inet_diag_req_v2 *r, struct nlattr *bc)
{
Expand Down Expand Up @@ -68,13 +163,15 @@ static int tcp_diag_destroy(struct sk_buff *in_skb,
#endif

static const struct inet_diag_handler tcp_diag_handler = {
.dump = tcp_diag_dump,
.dump_one = tcp_diag_dump_one,
.idiag_get_info = tcp_diag_get_info,
.idiag_type = IPPROTO_TCP,
.idiag_info_size = sizeof(struct tcp_info),
.dump = tcp_diag_dump,
.dump_one = tcp_diag_dump_one,
.idiag_get_info = tcp_diag_get_info,
.idiag_get_aux = tcp_diag_get_aux,
.idiag_get_aux_size = tcp_diag_get_aux_size,
.idiag_type = IPPROTO_TCP,
.idiag_info_size = sizeof(struct tcp_info),
#ifdef CONFIG_INET_DIAG_DESTROY
.destroy = tcp_diag_destroy,
.destroy = tcp_diag_destroy,
#endif
};

Expand Down

0 comments on commit c03fa9b

Please sign in to comment.