// SPDX-License-Identifier: GPL-2.0-or-later /* * INET An implementation of the TCP Authentication Option (TCP-AO). * See RFC5925. * * Authors: Dmitry Safonov * Francesco Ruggeri * Salam Noureddine */ #include #include #include #include static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, const struct in6_addr *saddr, const struct in6_addr *daddr, __be16 sport, __be16 dport, __be32 sisn, __be32 disn) { struct kdf_input_block { u8 counter; u8 label[6]; struct tcp6_ao_context ctx; __be16 outlen; } __packed * tmp; struct tcp_sigpool hp; int err; err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp); if (err) return err; tmp = hp.scratch; tmp->counter = 1; memcpy(tmp->label, "TCP-AO", 6); tmp->ctx.saddr = *saddr; tmp->ctx.daddr = *daddr; tmp->ctx.sport = sport; tmp->ctx.dport = dport; tmp->ctx.sisn = sisn; tmp->ctx.disn = disn; tmp->outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */ err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp); tcp_sigpool_end(&hp); return err; } int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, const struct sk_buff *skb, __be32 sisn, __be32 disn) { const struct ipv6hdr *iph = ipv6_hdr(skb); const struct tcphdr *th = tcp_hdr(skb); return tcp_v6_ao_calc_key(mkt, key, &iph->saddr, &iph->daddr, th->source, th->dest, sisn, disn); } int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, __be32 sisn, __be32 disn, bool send) { if (send) return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_rcv_saddr, &sk->sk_v6_daddr, htons(sk->sk_num), sk->sk_dport, sisn, disn); else return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_daddr, &sk->sk_v6_rcv_saddr, sk->sk_dport, htons(sk->sk_num), disn, sisn); } int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, struct request_sock *req) { struct inet_request_sock *ireq = inet_rsk(req); return tcp_v6_ao_calc_key(mkt, key, &ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr, htons(ireq->ir_num), ireq->ir_rmt_port, htonl(tcp_rsk(req)->snt_isn), htonl(tcp_rsk(req)->rcv_isn)); } struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid) { int l3index = l3mdev_master_ifindex_by_index(sock_net(sk), addr_sk->sk_bound_dev_if); struct in6_addr *addr = &addr_sk->sk_v6_daddr; return tcp_ao_do_lookup(sk, l3index, (union tcp_ao_addr *)addr, AF_INET6, sndid, rcvid); } struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk, struct request_sock *req, int sndid, int rcvid) { struct inet_request_sock *ireq = inet_rsk(req); struct in6_addr *addr = &ireq->ir_v6_rmt_addr; int l3index; l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif); return tcp_ao_do_lookup(sk, l3index, (union tcp_ao_addr *)addr, AF_INET6, sndid, rcvid); } int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp, const struct in6_addr *daddr, const struct in6_addr *saddr, int nbytes) { struct tcp6_pseudohdr *bp; struct scatterlist sg; bp = hp->scratch; /* 1. TCP pseudo-header (RFC2460) */ bp->saddr = *saddr; bp->daddr = *daddr; bp->len = cpu_to_be32(nbytes); bp->protocol = cpu_to_be32(IPPROTO_TCP); sg_init_one(&sg, bp, sizeof(*bp)); ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); return crypto_ahash_update(hp->req); } int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, const struct sock *sk, const struct sk_buff *skb, const u8 *tkey, int hash_offset, u32 sne) { return tcp_ao_hash_skb(AF_INET6, ao_hash, key, sk, skb, tkey, hash_offset, sne); } int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen) { return tcp_parse_ao(sk, cmd, AF_INET6, optval, optlen); } int tcp_v6_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key, struct request_sock *req, const struct sk_buff *skb, int hash_offset, u32 sne) { void *hash_buf = NULL; int err; hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); if (!hash_buf) return -ENOMEM; err = tcp_v6_ao_calc_key_rsk(ao_key, hash_buf, req); if (err) goto out; err = tcp_ao_hash_skb(AF_INET6, ao_hash, ao_key, req_to_sk(req), skb, hash_buf, hash_offset, sne); out: kfree(hash_buf); return err; }