Support for obsfucated TCP: opportunistic encryption of TCP connections From: <> --- include/linux/tcp.h | 17 ++++- include/net/inet_sock.h | 3 + include/net/tcp.h | 18 +++++ net/ipv4/Kconfig | 13 ++++ net/ipv4/tcp.c | 69 ++++++++++++++++++++- net/ipv4/tcp_input.c | 126 ++++++++++++++++++++++++++++++++++++- net/ipv4/tcp_ipv4.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++- net/ipv4/tcp_output.c | 143 ++++++++++++++++++++++++++++++++++++++++++- 8 files changed, 533 insertions(+), 14 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index f633d3a..7f9b61d 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -96,6 +96,7 @@ enum { #define TCP_QUICKACK 12 /* Block/reenable quick acks */ #define TCP_CONGESTION 13 /* Congestion control algorithm */ #define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */ +#define TCP_OBSTCP_SETKEY 15 /* Set Obsfucated TCP system key */ #define TCPI_OPT_TIMESTAMPS 1 #define TCPI_OPT_SACK 2 @@ -227,6 +228,11 @@ struct tcp_options_received { u8 jumbo : 1; /* Jumbo options supported */ int data_offset; /* The true data offset for the packet */ #endif +#ifdef CONFIG_TCP_OBSTCP + u8 obstcp_offered : 1, /* Peer offered OBSTCP */ + obstcp_has_pubkey : 1; /* Peer included a publick key */ + u8 obstcp_pubkey[32]; /* Peer's public key */ +#endif }; struct tcp_request_sock { @@ -403,10 +409,17 @@ struct tcp_sock { u32 probe_seq_end; } mtu_probe; -#ifdef CONFIG_TCP_MD5SIG -/* TCP AF-Specific parts; only used by MD5 Signature support so far */ +#ifdef CONFIG_TCP_OBSTCP + u8 peer_pubkey[32]; /* Peer's public key */ + u8 shared_key[32]; /* Our shared key */ + u8 obstcp_active : 1; /* We are encrypting */ + u8 obstcp_send_pk : 1; /* We should send our pubkey */ +#endif + +/* TCP AF-Specific parts */ struct tcp_sock_af_ops *af_specific; +#ifdef CONFIG_TCP_MD5SIG /* TCP MD5 Signagure Option information */ struct tcp_md5sig_info *md5sig_info; #endif diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 33136ad..5bec46c 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -77,6 +77,9 @@ struct inet_request_sock { #ifdef CONFIG_TCP_JUMBO_OPTIONS u16 jumbo : 1; #endif +#ifdef CONFIG_TCP_JUMBO_OPTIONS + u16 obstcp_offered : 1; +#endif struct ip_options *opt; }; diff --git a/include/net/tcp.h b/include/net/tcp.h index d636091..b96f032 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -166,6 +166,9 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */ #define TCPOPT_JUMBO_OPTIONS 42 /* Jumbo options included */ #define TCPOPT_JUMBO_SUPPORTED 43 /* Jumbo options supported */ +#define TCPOPT_OBSTCP_OFFER 44 /* Obsfucated TCP requested */ +#define TCPOPT_OBSTCP_PUBKEY 45 /* Obsfucated TCP public key */ +#define TCPOPT_OBSTCP_AUTH 46 /* Obsfucated TCP signature */ /* * TCP option lengths @@ -178,6 +181,9 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCPOLEN_MD5SIG 18 #define TCPOLEN_JUMBO_SUPPORTED 2 #define TCPOLEN_JUMBO_OPTIONS 4 +#define TCPOLEN_OBSTCP_OFFER 2 +#define TCPOLEN_OBSTCP_PUBKEY 34 +#define TCPOLEN_OBSTCP_AUTH 19 /* But this is what stacks really send out. */ #define TCPOLEN_TSTAMP_ALIGNED 12 @@ -189,6 +195,9 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCPOLEN_MD5SIG_ALIGNED 20 #define TCPOLEN_JUMBO_SUPPORTED_ALIGNED 4 #define TCPOLEN_JUMBO_OPTIONS_ALIGNED 4 +#define TCPOLEN_OBSTCP_OFFER_ALIGNED 4 +#define TCPOLEN_OBSTCP_PUBKEY_ALIGNED 36 +#define TCPOLEN_OBSTCP_AUTH_ALIGNED 20 /* Flags in tp->nonagle */ #define TCP_NAGLE_OFF 1 /* Nagle's algo is disabled */ @@ -339,7 +348,10 @@ static inline void tcp_clear_options(struct tcp_options_received *rx_opt) { rx_opt->tstamp_ok = rx_opt->sack_ok = rx_opt->wscale_ok = rx_opt->snd_wscale = 0; #ifdef CONFIG_TCP_JUMBO_OPTIONS - rx_opt->jumbo = 0; + rx_opt->jumbo = 0; +#endif +#ifdef CONFIG_TCP_OBSTCP + rx_opt->obstcp_offered = rx_opt->obstcp_has_pubkey = 0; #endif } @@ -1379,6 +1391,10 @@ struct tcp_sock_af_ops { char __user *optval, int optlen); #endif +#ifdef CONFIG_TCP_OBSTCP + int (*calc_obstcp_hash) (u8 *location, struct sock *sk, + struct sk_buff *skb); +#endif }; struct tcp_request_sock_ops { diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 2459a94..b80274c 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -634,6 +634,7 @@ config TCP_MD5SIG config TCP_JUMBO_OPTIONS bool "TCP: Jumbo options support (EXPERIMENTAL)" + depends on EXPERIMENTAL ---help--- This causes the kernel to advertise support for jumbo TCP options. This allows TCP options larger than would normally fit in the @@ -641,5 +642,17 @@ config TCP_JUMBO_OPTIONS If unsure, say N. +config TCP_OBSTCP + bool "TCP: Obsfucated TCP support (EXPERIMENTAL)" + depends on EXPERIMENTAL + select CRYPTO_CURVE25519 + select TCP_JUMBO_OPTIONS + select CIFS + ---help--- + Obsfucated TCP opportunistically encrypts TCP streams between + compatible hosts. It doesn't affect any other TCP connections. + + If unsure, say N. + source "net/ipv4/ipvs/Kconfig" diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 39b629a..9fc9718 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -274,6 +274,16 @@ #include #include +#ifdef CONFIG_TCP_OBSTCP +#include +/* These are write once values. The spinlock is to protect against multiple, + * concurrent attempts at writing */ +char obstcp_keys_setup = 0; +spinlock_t obstcp_keys_lock = SPIN_LOCK_UNLOCKED; +u8 obstcp_private_key[32]; +u8 obstcp_public_key[32]; +#endif + int sysctl_tcp_fin_timeout __read_mostly = TCP_FIN_TIMEOUT; DEFINE_SNMP_STAT(struct tcp_mib, tcp_statistics) __read_mostly; @@ -1239,6 +1249,8 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc, if (!desc->count) break; } + printk(KERN_INFO "copied_seq(4) %u -> %u\n", + tp->copied_seq, seq); tp->copied_seq = seq; tcp_rcv_space_adjust(sk); @@ -1344,6 +1356,8 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, "seq %X\n", *seq, TCP_SKB_CB(skb)->seq); break; } + printk(KERN_INFO "offset = *seq - seq (%u %u)\n", + *seq, TCP_SKB_CB(skb)->seq); offset = *seq - TCP_SKB_CB(skb)->seq; if (tcp_hdr(skb)->syn) offset--; @@ -1414,6 +1428,8 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, tp->ucopy.len = len; + printk(KERN_INFO "copied_seq:%u rcv_nxt:%u\n", + tp->copied_seq, tp->rcv_nxt); BUG_TRAP(tp->copied_seq == tp->rcv_nxt || (flags & (MSG_PEEK | MSG_TRUNC))); @@ -1493,6 +1509,8 @@ do_prequeue: found_ok_skb: /* Ok so how much can we use? */ + printk(KERN_INFO "skb->len - offset (%u %u)\n", + skb->len, offset); used = skb->len - offset; if (len < used) used = len; @@ -1551,6 +1569,7 @@ do_prequeue: } } + printk("copied_seq(in) %u -> %u\n", *seq, *seq + used); *seq += used; copied += used; len -= used; @@ -1981,7 +2000,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level, int val; int err = 0; - /* This is a string value all the others are int's */ + /* These are string values, all the others are int's */ if (optname == TCP_CONGESTION) { char name[TCP_CA_NAME_MAX]; @@ -1999,6 +2018,54 @@ static int do_tcp_setsockopt(struct sock *sk, int level, release_sock(sk); return err; } +#ifdef CONFIG_TCP_OBSTCP + else if (optname == TCP_OBSTCP_SETKEY) { + u8 key[32]; + char set = 0; + static const u8 basepoint[32] = {9}; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (optlen != 32) + return -EINVAL; + if (copy_from_user(key, optval, 32)) + return -EFAULT; + + key[0] &= 248; + key[31] &= 127; + key[31] |= 64; + + spin_lock_bh(&obstcp_keys_lock); + if (!obstcp_keys_setup) { + memcpy(obstcp_private_key, key, 32); + curve25519(obstcp_public_key, key, basepoint); + obstcp_keys_setup = 1; + set = 1; + } + spin_unlock_bh(&obstcp_keys_lock); + + if (!set) + return -ENOSPC; + + printk(KERN_INFO "Obsfucated TCP key set\n"); + printk(KERN_INFO "private: %02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x\n", + key[0], key[1], key[2], + key[3], key[4], key[5], + key[6], key[7], key[8], + key[9], key[10], key[11], + key[12], key[13], key[14], + key[15], key[16], key[17], + key[18], key[19], key[20], + key[21], key[22], key[23], + key[24], key[25], key[26], + key[27], key[28], key[29], + key[30], key[31]); + return err; + } +#endif if (optlen < sizeof(int)) return -EINVAL; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 13df767..f827214 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -72,6 +72,12 @@ #include #include +#ifdef CONFIG_TCP_OBSTCP +#include +extern const u8 obstcp_public_key[]; +extern const u8 obstcp_private_key[]; +#endif + int sysctl_tcp_timestamps __read_mostly = 1; int sysctl_tcp_window_scaling __read_mostly = 1; int sysctl_tcp_sack __read_mostly = 1; @@ -3306,7 +3312,7 @@ void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx, int length = (th->doff * 4) - sizeof(struct tcphdr); #ifdef CONFIG_TCP_JUMBO_OPTIONS - opt_rx->data_offset = length; + opt_rx->data_offset = th->doff * 4; #endif ptr = (unsigned char *)(th + 1); @@ -3324,6 +3330,7 @@ void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx, continue; default: opsize = *ptr++; + printk(KERN_INFO "Option %d %d %d\n", opcode, opsize, length); if (opsize < 2) /* "silly options" */ return; if (opsize > length) @@ -3394,18 +3401,34 @@ void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx, case TCPOPT_JUMBO_OPTIONS: if (opsize == 4) { u16 new_length = ntohs(get_unaligned((__be16 *)ptr)); + printk(KERN_INFO "JUMBO: seen %d\n", new_length); if (new_length >= th->doff * 4 && new_length <= skb->len) { const int delta = new_length - th->doff * 4; length += delta; - TCP_SKB_CB(skb)->end_seq -= delta; + TCP_SKB_CB(skb)->end_seq = + TCP_SKB_CB(skb)->seq + th->syn + th->fin + + skb->len - new_length; opt_rx->data_offset = new_length; + printk("JUMBO: setting %d\n", length); } } break; #endif +#ifdef CONFIG_TCP_OBSTCP + case TCPOPT_OBSTCP_OFFER: + opt_rx->obstcp_offered = 1; + break; + case TCPOPT_OBSTCP_PUBKEY: + printk(KERN_INFO "OBSTCP: seen pubkey option\n"); + if (opsize == 34) { + printk(KERN_INFO "OBSTCP: marking has_pubkey\n"); + opt_rx->obstcp_has_pubkey = 1; + memcpy(opt_rx->obstcp_pubkey, ptr, 32); + } + break; } - +#endif ptr += opsize-2; length -= opsize; } @@ -3911,6 +3934,8 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) local_bh_enable(); if (!skb_copy_datagram_iovec(skb, 0, tp->ucopy.iov, chunk)) { tp->ucopy.len -= chunk; + printk(KERN_INFO "copied_seq: %u -> %u\n", + tp->copied_seq, tp->copied_seq + chunk); tp->copied_seq += chunk; eaten = (chunk == skb->len && !th->fin); tcp_rcv_space_adjust(sk); @@ -4471,6 +4496,8 @@ static void tcp_check_urg(struct sock *sk, struct tcphdr *th) if (tp->urg_seq == tp->copied_seq && tp->urg_data && !sock_flag(sk, SOCK_URGINLINE) && tp->copied_seq != tp->rcv_nxt) { struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); + printk(KERN_INFO "copied_seq(2): %u -> %u\n", + tp->copied_seq, tp->copied_seq + 1); tp->copied_seq++; if (skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq)) { __skb_unlink(skb, &sk->sk_receive_queue); @@ -4531,6 +4558,8 @@ static int tcp_copy_to_iovec(struct sock *sk, struct sk_buff *skb, int hlen) if (!err) { tp->ucopy.len -= chunk; + printk(KERN_INFO "copied_seq(3): %u -> %u\n", + tp->copied_seq, tp->copied_seq + chunk); tp->copied_seq += chunk; tcp_rcv_space_adjust(sk); } @@ -4590,6 +4619,7 @@ static int tcp_dma_try_early_copy(struct sock *sk, struct sk_buff *skb, copied_early = 1; tp->ucopy.len -= chunk; + printk(KERN_INFO "copied_seq in DMA\n"); tp->copied_seq += chunk; tcp_rcv_space_adjust(sk); @@ -4989,10 +5019,53 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, /* Remember, tcp_poll() does not lock socket! * Change state from SYN-SENT only after copied_seq * is initialized. */ + printk(KERN_INFO "copied_seq init to %u\n", tp->rcv_nxt); tp->copied_seq = tp->rcv_nxt; smp_mb(); tcp_set_state(sk, TCP_ESTABLISHED); +#ifdef CONFIG_TCP_OBSTCP + if (tp->rx_opt.obstcp_has_pubkey) { + tp->obstcp_active = 1; + tp->obstcp_send_pk = 1; + memcpy(tp->peer_pubkey, tp->rx_opt.obstcp_pubkey, 32); + curve25519(tp->shared_key, obstcp_private_key, tp->peer_pubkey); + printk(KERN_INFO "OBSTCP: Shared key established\n"); + printk(KERN_INFO "peer key: %02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x\n", + tp->peer_pubkey[0], tp->peer_pubkey[1], tp->peer_pubkey[2], + tp->peer_pubkey[3], tp->peer_pubkey[4], tp->peer_pubkey[5], + tp->peer_pubkey[6], tp->peer_pubkey[7], tp->peer_pubkey[8], + tp->peer_pubkey[9], tp->peer_pubkey[10], tp->peer_pubkey[11], + tp->peer_pubkey[12], tp->peer_pubkey[13], tp->peer_pubkey[14], + tp->peer_pubkey[15], tp->peer_pubkey[16], tp->peer_pubkey[17], + tp->peer_pubkey[18], tp->peer_pubkey[19], tp->peer_pubkey[20], + tp->peer_pubkey[21], tp->peer_pubkey[22], tp->peer_pubkey[23], + tp->peer_pubkey[24], tp->peer_pubkey[25], tp->peer_pubkey[26], + tp->peer_pubkey[27], tp->peer_pubkey[28], tp->peer_pubkey[29], + tp->peer_pubkey[30], tp->peer_pubkey[31]); + printk(KERN_INFO "OBSTCP: %02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x\n", + tp->shared_key[0], tp->shared_key[1], tp->shared_key[2], + tp->shared_key[3], tp->shared_key[4], tp->shared_key[5], + tp->shared_key[6], tp->shared_key[7], tp->shared_key[8], + tp->shared_key[9], tp->shared_key[10], tp->shared_key[11], + tp->shared_key[12], tp->shared_key[13], tp->shared_key[14], + tp->shared_key[15], tp->shared_key[16], tp->shared_key[17], + tp->shared_key[18], tp->shared_key[19], tp->shared_key[20], + tp->shared_key[21], tp->shared_key[22], tp->shared_key[23], + tp->shared_key[24], tp->shared_key[25], tp->shared_key[26], + tp->shared_key[27], tp->shared_key[28], tp->shared_key[29], + tp->shared_key[30], tp->shared_key[31]); + } else { + tp->obstcp_active = 0; + } +#endif + security_inet_conn_established(sk, skb); /* Make sure socket is routed, for correct metrics. */ @@ -5145,6 +5218,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tp->rx_opt.saw_tstamp = 0; + printk(KERN_INFO "rcv_state_process in state %d\n", sk->sk_state); + switch (sk->sk_state) { case TCP_CLOSE: goto discard; @@ -5238,6 +5313,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, switch (sk->sk_state) { case TCP_SYN_RECV: if (acceptable) { + printk(KERN_INFO "copied_seq(2) init to %u\n", + tp->rcv_nxt); tp->copied_seq = tp->rcv_nxt; smp_mb(); tcp_set_state(sk, TCP_ESTABLISHED); @@ -5245,7 +5322,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, /* Note, that this wakeup is only for marginal * crossed SYN case. Passively open sockets - * are not waked up, because sk->sk_sleep == + * are not woken up, because sk->sk_sleep == * NULL and sk->sk_socket == NULL. */ if (sk->sk_socket) @@ -5278,6 +5355,47 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tcp_init_congestion_control(sk); +#ifdef CONFIG_TCP_OBSTCP + if (tp->rx_opt.obstcp_has_pubkey) { + tp->obstcp_active = 1; + memcpy(tp->peer_pubkey, tp->rx_opt.obstcp_pubkey, 32); + curve25519(tp->shared_key, obstcp_private_key, tp->peer_pubkey); + printk(KERN_INFO "OBSTCP: Shared key established\n"); + printk(KERN_INFO "peerkey: %02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x\n", + tp->peer_pubkey[0], tp->peer_pubkey[1], tp->peer_pubkey[2], + tp->peer_pubkey[3], tp->peer_pubkey[4], tp->peer_pubkey[5], + tp->peer_pubkey[6], tp->peer_pubkey[7], tp->peer_pubkey[8], + tp->peer_pubkey[9], tp->peer_pubkey[10], tp->peer_pubkey[11], + tp->peer_pubkey[12], tp->peer_pubkey[13], tp->peer_pubkey[14], + tp->peer_pubkey[15], tp->peer_pubkey[16], tp->peer_pubkey[17], + tp->peer_pubkey[18], tp->peer_pubkey[19], tp->peer_pubkey[20], + tp->peer_pubkey[21], tp->peer_pubkey[22], tp->peer_pubkey[23], + tp->peer_pubkey[24], tp->peer_pubkey[25], tp->peer_pubkey[26], + tp->peer_pubkey[27], tp->peer_pubkey[28], tp->peer_pubkey[29], + tp->peer_pubkey[30], tp->peer_pubkey[31]); + printk(KERN_INFO "OBSTCP: %02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x\n", + tp->shared_key[0], tp->shared_key[1], tp->shared_key[2], + tp->shared_key[3], tp->shared_key[4], tp->shared_key[5], + tp->shared_key[6], tp->shared_key[7], tp->shared_key[8], + tp->shared_key[9], tp->shared_key[10], tp->shared_key[11], + tp->shared_key[12], tp->shared_key[13], tp->shared_key[14], + tp->shared_key[15], tp->shared_key[16], tp->shared_key[17], + tp->shared_key[18], tp->shared_key[19], tp->shared_key[20], + tp->shared_key[21], tp->shared_key[22], tp->shared_key[23], + tp->shared_key[24], tp->shared_key[25], tp->shared_key[26], + tp->shared_key[27], tp->shared_key[28], tp->shared_key[29], + tp->shared_key[30], tp->shared_key[31]); + } else { + tp->obstcp_active = 0; + } +#endif + /* Prevent spurious tcp_cwnd_restart() on * first data packet. */ diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b47256c..775dc5c 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -82,6 +82,12 @@ #include #include +#include "../../fs/cifs/md5.h" + +#ifdef CONFIG_TCP_OBSTCP +#include +#endif + int sysctl_tcp_tw_reuse __read_mostly; int sysctl_tcp_low_latency __read_mostly; @@ -1414,6 +1420,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, struct tcp_md5sig_key *key; #endif + printk(KERN_INFO "In syn_recv_sock\n"); + if (sk_acceptq_is_full(sk)) goto exit_overflow; @@ -1447,6 +1455,10 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newtp->advmss = dst_metric(dst, RTAX_ADVMSS); tcp_initialize_rcv_mss(newsk); +#ifdef CONFIG_TCP_OBSTCP + +#endif + #ifdef CONFIG_TCP_MD5SIG /* Copy over the MD5 key from the original socket */ if ((key = tcp_v4_md5_do_lookup(sk, newinet->daddr)) != NULL) { @@ -1528,6 +1540,139 @@ static __sum16 tcp_v4_checksum_init(struct sk_buff *skb) return 0; } +#ifdef CONFIG_TCP_OBSTCP + +static void +print_bytes(const u8 *bytes, unsigned length) { + unsigned i; + + for (i = 0; i < length; ++i) { + printk("%02x", bytes[i]); + } + printk("\n"); +} + +/* Build an Obsfucated TCP hash for the given packet. It's assumed that the + * hash in the authentication header has already been set to all zeros. */ +static int tcp_obstcp_build_hash(unsigned char *output, + struct sock *sk, struct sk_buff *skb) { + struct tcp_sock *tp = tcp_sk(sk); + struct tcphdr *th = tcp_hdr(skb); + __sum16 old_checksum; + u16 old_sport, old_dport; + int err = 0; + + struct HMACMD5Context ctx; + printk(KERN_INFO "HMAC key: "); + print_bytes(tp->shared_key, 32); + hmac_md5_init_limK_to_64(tp->shared_key, 32, &ctx); + + old_checksum = th->check; + th->check = 0; + old_sport = th->source; + th->source = 0; + old_dport = th->dest; + th->dest = 0; + + printk(KERN_INFO "Hashing: "); + print_bytes((u8 *) th, skb->len); + hmac_md5_update((u8 *) th, skb->len, &ctx); + hmac_md5_final(output, &ctx); + + th->check = old_checksum; + th->source = old_sport; + th->dest = old_dport; + + return err; +} + +static int tcp_obstcp_process(struct sock *sk, struct sk_buff *skb) { + struct tcp_sock *tp = tcp_sk(sk); + struct tcphdr *th = tcp_hdr(skb); + int header_length = (th->doff << 2) - sizeof(struct tcphdr); + u8 *ptr, *auth_header = NULL; + u8 ourhash[16], theirhash[16]; + + /* If OBSTCP isn't active on this connection, accept all packets */ + if (!tp->obstcp_active) return 0; + + /* Walk the TCP header to extract the authenticator header */ + ptr = (unsigned char *) (th + 1); + while (header_length > 0) { + int opcode = *ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + goto done_opts; + case TCPOPT_NOP: + header_length--; + continue; + default: + opsize = *ptr++; + if (opsize < 2) + goto done_opts; + if (opsize > header_length) + goto done_opts; + if (opcode == TCPOPT_JUMBO_OPTIONS) { + u16 new_length; + if (opsize != 4) + goto done_opts; + new_length = ntohs(get_unaligned((__be16 *) ptr)); + if (new_length >= th->doff * 4 && + new_length <= skb->len) { + const int delta = new_length - th->doff * 4; + header_length += delta; + } else { + goto done_opts; + } + } else if (opcode == TCPOPT_OBSTCP_AUTH) { + if (opsize != 19) + goto done_opts; + auth_header = ptr; + goto done_opts; + } + } + + ptr += opsize - 2; + header_length -= opsize; + } + +done_opts: + + if (!auth_header) { + printk(KERN_INFO "Didn't find auth header\n"); + return 1; + } + + memcpy(theirhash, auth_header + 1, 16); + memset(auth_header + 1, 0, 16); + tcp_obstcp_build_hash(ourhash, sk, skb); + if (memcmp(ourhash, theirhash, 16)) { + printk(KERN_INFO "Bad OBSTCP hash\n"); + printk(KERN_INFO "myhash: %02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x\n", + ourhash[0], ourhash[1], ourhash[2], + ourhash[3], ourhash[4], ourhash[5], + ourhash[6], ourhash[7], ourhash[8], + ourhash[9], ourhash[10], ourhash[11], + ourhash[12], ourhash[13], ourhash[14], + ourhash[15]); + printk(KERN_INFO "thhash: %02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x\n", + theirhash[0], theirhash[1], theirhash[2], + theirhash[3], theirhash[4], theirhash[5], + theirhash[6], theirhash[7], theirhash[8], + theirhash[9], theirhash[10], theirhash[11], + theirhash[12], theirhash[13], theirhash[14], + theirhash[15]); + return 1; + } + + return 0; +} +#endif + /* The socket must have it's spinlock held when we get * here. @@ -1552,6 +1697,10 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) #endif if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ +#ifdef CONFIG_TCP_OBSTCP + if (tcp_obstcp_process(sk, skb)) + goto discard; +#endif TCP_CHECK_TIMER(sk); if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) { rsk = sk; @@ -1819,14 +1968,17 @@ struct inet_connection_sock_af_ops ipv4_specific = { #endif }; -#ifdef CONFIG_TCP_MD5SIG static struct tcp_sock_af_ops tcp_sock_ipv4_specific = { +#ifdef CONFIG_TCP_MD5SIG .md5_lookup = tcp_v4_md5_lookup, .calc_md5_hash = tcp_v4_calc_md5_hash, .md5_add = tcp_v4_md5_add_func, .md5_parse = tcp_v4_parse_md5_keys, -}; #endif +#ifdef CONFIG_TCP_OBSTCP + .calc_obstcp_hash = tcp_obstcp_build_hash +#endif +}; /* NOTE: A lot of things set to zero explicitly by call to * sk_alloc() so need not be done here. @@ -1867,9 +2019,7 @@ static int tcp_v4_init_sock(struct sock *sk) icsk->icsk_af_ops = &ipv4_specific; icsk->icsk_sync_mss = tcp_sync_mss; -#ifdef CONFIG_TCP_MD5SIG tp->af_specific = &tcp_sock_ipv4_specific; -#endif sk->sk_sndbuf = sysctl_tcp_wmem[1]; sk->sk_rcvbuf = sysctl_tcp_rmem[1]; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 23e2347..385e2c6 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -41,6 +41,11 @@ #include #include +#ifdef CONFIG_TCP_OBSTCP +extern const u8 obstcp_public_key[]; +extern const u8 obstcp_private_key[]; +#endif + /* People can turn this off for buggy TCP's found in printers etc. */ int sysctl_tcp_retrans_collapse __read_mostly = 1; @@ -348,8 +353,17 @@ static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags) } static void tcp_build_and_update_options(__be32 *ptr, struct tcp_sock *tp, - __u32 tstamp, __u8 **md5_hash) + __u32 tstamp, __u8 **md5_hash +#ifdef CONFIG_TCP_OBSTCP + , const u8 *publickey + , u8 obstcp_auth_flags + , u8 **obstcp_auth +#endif + ) { +#ifdef CONFIG_TCP_OBSTCP + __be32 *const orig_ptr = ptr; +#endif if (tp->rx_opt.tstamp_ok) { *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | @@ -387,6 +401,35 @@ static void tcp_build_and_update_options(__be32 *ptr, struct tcp_sock *tp, *md5_hash = (__u8 *)ptr; } #endif +#ifdef CONFIG_TCP_OBSTCP + if (obstcp_auth) { + *ptr++ = htonl((TCPOPT_NOP << 24) | + (TCPOPT_OBSTCP_AUTH << 16) | + 19 << 8 | + obstcp_auth_flags); + /* Zero the hash area before hashing the packet */ + memset(ptr, 0, 16); + *obstcp_auth = (u8 *) ptr; + ptr += 4; + } + if (publickey) { + /* Add a jumbo options option because the public key is far too + * large to fit in a standard TCP packet */ + const int rfc793_length = ((ptr + 1) - orig_ptr) * 4 + + sizeof(struct tcphdr); + printk(KERN_INFO "Including public key in packet\n"); + *ptr++ = htonl((TCPOPT_JUMBO_OPTIONS << 24) | + (4 << 16) | + (rfc793_length + TCPOLEN_OBSTCP_PUBKEY_ALIGNED)); + + *ptr++ = htonl((TCPOPT_NOP << 24) | + (TCPOPT_NOP << 16) | + (TCPOPT_OBSTCP_PUBKEY << 8) | + TCPOLEN_OBSTCP_PUBKEY); + memcpy(ptr, publickey, 32); + ptr += 8; + } +#endif } /* Construct a tcp options header for a SYN or SYN_ACK packet. @@ -404,8 +447,15 @@ static void tcp_syn_build_options(__be32 *ptr, int mss, int ts, int sack, #ifdef CONFIG_TCP_JUMBO_OPTIONS , int jumbo_support #endif +#ifdef CONFIG_TCP_OBSTCP + , int obstcp_support + , const u8 *publickey +#endif ) { +#ifdef CONFIG_TCP_OBSTCP + __be32 *const orig_ptr = ptr; +#endif /* We always get an MSS option. * The option bytes which will be seen in normal data * packets should timestamps be used, must be in the MSS @@ -467,6 +517,34 @@ static void tcp_syn_build_options(__be32 *ptr, int mss, int ts, int sack, 2); } #endif + +#ifdef CONFIG_TCP_OBSTCP + if (obstcp_support) { + /* Advertise our support for OBSTCP */ + printk(KERN_INFO "Advertising OBSTCP support\n"); + *ptr++ = htonl((TCPOPT_NOP << 24) | + (TCPOPT_NOP << 16) | + (TCPOPT_OBSTCP_OFFER << 8) | + 2); + } + if (publickey) { + /* Add a jumbo options option because the public key is far too + * large to fit in a standard TCP packet */ + const int rfc793_length = ((ptr + 1) - orig_ptr) * 4 + + sizeof(struct tcphdr); + printk(KERN_INFO "Including public key in packet\n"); + *ptr++ = htonl((TCPOPT_JUMBO_OPTIONS << 24) | + (4 << 16) | + (rfc793_length + TCPOLEN_OBSTCP_PUBKEY_ALIGNED)); + + *ptr++ = htonl((TCPOPT_NOP << 24) | + (TCPOPT_NOP << 16) | + (TCPOPT_OBSTCP_PUBKEY << 8) | + TCPOLEN_OBSTCP_PUBKEY); + memcpy(ptr, publickey, 32); + ptr += 8; + } +#endif } /* This routine actually transmits TCP packets queued in by @@ -498,6 +576,11 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, #ifdef CONFIG_TCP_JUMBO_OPTIONS int tcp_header_overflow = 0; #endif +#ifdef CONFIG_TCP_OBSTCP + const u8 *publickey = NULL; + u8 *auth_header; + u8 **obstcp_auth = NULL; +#endif BUG_ON(!skb || !tcp_skb_pcount(skb)); @@ -566,6 +649,27 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, #ifdef CONFIG_TCP_JUMBO_OPTIONS if (unlikely(tcb->flags & TCPCB_FLAG_SYN)) { tcp_header_size += TCPOLEN_JUMBO_SUPPORTED_ALIGNED; +#ifdef CONFIG_TCP_OBSTCP + tcp_header_size += TCPOLEN_OBSTCP_OFFER_ALIGNED; +#endif + } +#endif + +#ifdef CONFIG_TCP_OBSTCP + if (unlikely(tp->obstcp_send_pk)) { + /* This is the first packet after getting a SYN+ACK with a public key + * in it. We already know the shared key, but we need to send our + * public key to the peer in order for them to calculate it */ + printk(KERN_INFO "OBSTCP: need to send key\n"); + tcp_header_size += TCPOLEN_JUMBO_OPTIONS_ALIGNED; + tcp_header_overflow += TCPOLEN_OBSTCP_PUBKEY_ALIGNED; + tp->obstcp_send_pk = 0; + publickey = obstcp_public_key; + } + + if (unlikely(tp->obstcp_active)) { + tcp_header_size += TCPOLEN_OBSTCP_AUTH_ALIGNED; + obstcp_auth = &auth_header; } #endif @@ -620,6 +724,10 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, #ifdef CONFIG_TCP_JUMBO_OPTIONS , 1 #endif +#ifdef CONFIG_TCP_OBSTCP + , 1 + , publickey +#endif ); } else { tcp_build_and_update_options((__be32 *)(th + 1), @@ -627,7 +735,13 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, #ifdef CONFIG_TCP_MD5SIG md5 ? &md5_hash_location : #endif - NULL); + NULL +#ifdef CONFIG_TCP_OBSTCP + , publickey + , 0 + , obstcp_auth +#endif + ); TCP_ECN_send(sk, skb, tcp_header_size); } @@ -643,6 +757,13 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, } #endif +#ifdef CONFIG_TCP_OBSTCP + if (unlikely(obstcp_auth)) { + tp->af_specific->calc_obstcp_hash(auth_header, + sk, skb); + } +#endif + #ifndef CONFIG_TCP_JUMBO_OPTIONS icsk->icsk_af_ops->send_check(sk, skb->len, tcp_header_size, skb); #else @@ -2224,8 +2345,15 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, * zero */ int tcp_header_overflow = 0; #endif +#ifdef CONFIG_TCP_OBSTCP + const u8 *publickey = NULL; +#endif +#ifndef CONFIG_TCP_OBSTCP skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1, GFP_ATOMIC); +#else + skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1, GFP_ATOMIC); +#endif if (skb == NULL) return NULL; @@ -2252,6 +2380,13 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, tcp_header_size += TCPOLEN_JUMBO_SUPPORTED_ALIGNED; } #endif +#ifdef CONFIG_TCP_OBSTCP + if (ireq->obstcp_offered) { + tcp_header_size += TCPOLEN_JUMBO_OPTIONS_ALIGNED; + tcp_header_overflow += TCPOLEN_OBSTCP_PUBKEY_ALIGNED; + publickey = obstcp_public_key; + } +#endif #ifndef CONFIG_TCP_JUMBO_OPTIONS skb_push(skb, tcp_header_size); @@ -2304,6 +2439,10 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, #ifdef CONFIG_TCP_JUMBO_OPTIONS , ireq->jumbo #endif +#ifdef CONFIG_TCP_OBSTCP + , 0 + , publickey +#endif ); th->doff = (tcp_header_size >> 2);