1c9e7c76dSDmitry Safonov // SPDX-License-Identifier: GPL-2.0 2c9e7c76dSDmitry Safonov /* 3c9e7c76dSDmitry Safonov * XFRM compat layer 4c9e7c76dSDmitry Safonov * Author: Dmitry Safonov <dima@arista.com> 5c9e7c76dSDmitry Safonov * Based on code and translator idea by: Florian Westphal <fw@strlen.de> 6c9e7c76dSDmitry Safonov */ 7c9e7c76dSDmitry Safonov #include <linux/compat.h> 8c9e7c76dSDmitry Safonov #include <linux/xfrm.h> 9c9e7c76dSDmitry Safonov #include <net/xfrm.h> 10c9e7c76dSDmitry Safonov 115461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cfg { 125461fc0cSDmitry Safonov compat_u64 soft_byte_limit, hard_byte_limit; 135461fc0cSDmitry Safonov compat_u64 soft_packet_limit, hard_packet_limit; 145461fc0cSDmitry Safonov compat_u64 soft_add_expires_seconds, hard_add_expires_seconds; 155461fc0cSDmitry Safonov compat_u64 soft_use_expires_seconds, hard_use_expires_seconds; 165461fc0cSDmitry Safonov }; /* same size on 32bit, but only 4 byte alignment required */ 175461fc0cSDmitry Safonov 185461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cur { 195461fc0cSDmitry Safonov compat_u64 bytes, packets, add_time, use_time; 205461fc0cSDmitry Safonov }; /* same size on 32bit, but only 4 byte alignment required */ 215461fc0cSDmitry Safonov 225461fc0cSDmitry Safonov struct compat_xfrm_userpolicy_info { 235461fc0cSDmitry Safonov struct xfrm_selector sel; 245461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cfg lft; 255461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cur curlft; 265461fc0cSDmitry Safonov __u32 priority, index; 275461fc0cSDmitry Safonov u8 dir, action, flags, share; 285461fc0cSDmitry Safonov /* 4 bytes additional padding on 64bit */ 295461fc0cSDmitry Safonov }; 305461fc0cSDmitry Safonov 315461fc0cSDmitry Safonov struct compat_xfrm_usersa_info { 325461fc0cSDmitry Safonov struct xfrm_selector sel; 335461fc0cSDmitry Safonov struct xfrm_id id; 345461fc0cSDmitry Safonov xfrm_address_t saddr; 355461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cfg lft; 365461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cur curlft; 375461fc0cSDmitry Safonov struct xfrm_stats stats; 385461fc0cSDmitry Safonov __u32 seq, reqid; 395461fc0cSDmitry Safonov u16 family; 405461fc0cSDmitry Safonov u8 mode, replay_window, flags; 415461fc0cSDmitry Safonov /* 4 bytes additional padding on 64bit */ 425461fc0cSDmitry Safonov }; 435461fc0cSDmitry Safonov 445461fc0cSDmitry Safonov struct compat_xfrm_user_acquire { 455461fc0cSDmitry Safonov struct xfrm_id id; 465461fc0cSDmitry Safonov xfrm_address_t saddr; 475461fc0cSDmitry Safonov struct xfrm_selector sel; 485461fc0cSDmitry Safonov struct compat_xfrm_userpolicy_info policy; 495461fc0cSDmitry Safonov /* 4 bytes additional padding on 64bit */ 505461fc0cSDmitry Safonov __u32 aalgos, ealgos, calgos, seq; 515461fc0cSDmitry Safonov }; 525461fc0cSDmitry Safonov 535461fc0cSDmitry Safonov struct compat_xfrm_userspi_info { 545461fc0cSDmitry Safonov struct compat_xfrm_usersa_info info; 555461fc0cSDmitry Safonov /* 4 bytes additional padding on 64bit */ 565461fc0cSDmitry Safonov __u32 min, max; 575461fc0cSDmitry Safonov }; 585461fc0cSDmitry Safonov 595461fc0cSDmitry Safonov struct compat_xfrm_user_expire { 605461fc0cSDmitry Safonov struct compat_xfrm_usersa_info state; 615461fc0cSDmitry Safonov /* 8 bytes additional padding on 64bit */ 625461fc0cSDmitry Safonov u8 hard; 635461fc0cSDmitry Safonov }; 645461fc0cSDmitry Safonov 655461fc0cSDmitry Safonov struct compat_xfrm_user_polexpire { 665461fc0cSDmitry Safonov struct compat_xfrm_userpolicy_info pol; 675461fc0cSDmitry Safonov /* 8 bytes additional padding on 64bit */ 685461fc0cSDmitry Safonov u8 hard; 695461fc0cSDmitry Safonov }; 705461fc0cSDmitry Safonov 715461fc0cSDmitry Safonov #define XMSGSIZE(type) sizeof(struct type) 725461fc0cSDmitry Safonov 735461fc0cSDmitry Safonov static const int compat_msg_min[XFRM_NR_MSGTYPES] = { 745461fc0cSDmitry Safonov [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_usersa_info), 755461fc0cSDmitry Safonov [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 765461fc0cSDmitry Safonov [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 775461fc0cSDmitry Safonov [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userpolicy_info), 785461fc0cSDmitry Safonov [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 795461fc0cSDmitry Safonov [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 805461fc0cSDmitry Safonov [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userspi_info), 815461fc0cSDmitry Safonov [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_acquire), 825461fc0cSDmitry Safonov [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_expire), 835461fc0cSDmitry Safonov [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userpolicy_info), 845461fc0cSDmitry Safonov [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_usersa_info), 855461fc0cSDmitry Safonov [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_polexpire), 865461fc0cSDmitry Safonov [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush), 875461fc0cSDmitry Safonov [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0, 885461fc0cSDmitry Safonov [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 895461fc0cSDmitry Safonov [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 905461fc0cSDmitry Safonov [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 915461fc0cSDmitry Safonov [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 925461fc0cSDmitry Safonov [XFRM_MSG_NEWSADINFO - XFRM_MSG_BASE] = sizeof(u32), 935461fc0cSDmitry Safonov [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), 945461fc0cSDmitry Safonov [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 955461fc0cSDmitry Safonov [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 965461fc0cSDmitry Safonov [XFRM_MSG_MAPPING - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping) 975461fc0cSDmitry Safonov }; 985461fc0cSDmitry Safonov 99*5106f4a8SDmitry Safonov static const struct nla_policy compat_policy[XFRMA_MAX+1] = { 100*5106f4a8SDmitry Safonov [XFRMA_SA] = { .len = XMSGSIZE(compat_xfrm_usersa_info)}, 101*5106f4a8SDmitry Safonov [XFRMA_POLICY] = { .len = XMSGSIZE(compat_xfrm_userpolicy_info)}, 102*5106f4a8SDmitry Safonov [XFRMA_LASTUSED] = { .type = NLA_U64}, 103*5106f4a8SDmitry Safonov [XFRMA_ALG_AUTH_TRUNC] = { .len = sizeof(struct xfrm_algo_auth)}, 104*5106f4a8SDmitry Safonov [XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) }, 105*5106f4a8SDmitry Safonov [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) }, 106*5106f4a8SDmitry Safonov [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) }, 107*5106f4a8SDmitry Safonov [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) }, 108*5106f4a8SDmitry Safonov [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) }, 109*5106f4a8SDmitry Safonov [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) }, 110*5106f4a8SDmitry Safonov [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_sec_ctx) }, 111*5106f4a8SDmitry Safonov [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) }, 112*5106f4a8SDmitry Safonov [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) }, 113*5106f4a8SDmitry Safonov [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, 114*5106f4a8SDmitry Safonov [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, 115*5106f4a8SDmitry Safonov [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) }, 116*5106f4a8SDmitry Safonov [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, 117*5106f4a8SDmitry Safonov [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, 118*5106f4a8SDmitry Safonov [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, 119*5106f4a8SDmitry Safonov [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, 120*5106f4a8SDmitry Safonov [XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) }, 121*5106f4a8SDmitry Safonov [XFRMA_TFCPAD] = { .type = NLA_U32 }, 122*5106f4a8SDmitry Safonov [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, 123*5106f4a8SDmitry Safonov [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, 124*5106f4a8SDmitry Safonov [XFRMA_PROTO] = { .type = NLA_U8 }, 125*5106f4a8SDmitry Safonov [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, 126*5106f4a8SDmitry Safonov [XFRMA_OFFLOAD_DEV] = { .len = sizeof(struct xfrm_user_offload) }, 127*5106f4a8SDmitry Safonov [XFRMA_SET_MARK] = { .type = NLA_U32 }, 128*5106f4a8SDmitry Safonov [XFRMA_SET_MARK_MASK] = { .type = NLA_U32 }, 129*5106f4a8SDmitry Safonov [XFRMA_IF_ID] = { .type = NLA_U32 }, 130*5106f4a8SDmitry Safonov }; 131*5106f4a8SDmitry Safonov 1325461fc0cSDmitry Safonov static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb, 1335461fc0cSDmitry Safonov const struct nlmsghdr *nlh_src, u16 type) 1345461fc0cSDmitry Safonov { 1355461fc0cSDmitry Safonov int payload = compat_msg_min[type]; 1365461fc0cSDmitry Safonov int src_len = xfrm_msg_min[type]; 1375461fc0cSDmitry Safonov struct nlmsghdr *nlh_dst; 1385461fc0cSDmitry Safonov 1395461fc0cSDmitry Safonov /* Compat messages are shorter or equal to native (+padding) */ 1405461fc0cSDmitry Safonov if (WARN_ON_ONCE(src_len < payload)) 1415461fc0cSDmitry Safonov return ERR_PTR(-EMSGSIZE); 1425461fc0cSDmitry Safonov 1435461fc0cSDmitry Safonov nlh_dst = nlmsg_put(skb, nlh_src->nlmsg_pid, nlh_src->nlmsg_seq, 1445461fc0cSDmitry Safonov nlh_src->nlmsg_type, payload, nlh_src->nlmsg_flags); 1455461fc0cSDmitry Safonov if (!nlh_dst) 1465461fc0cSDmitry Safonov return ERR_PTR(-EMSGSIZE); 1475461fc0cSDmitry Safonov 1485461fc0cSDmitry Safonov memset(nlmsg_data(nlh_dst), 0, payload); 1495461fc0cSDmitry Safonov 1505461fc0cSDmitry Safonov switch (nlh_src->nlmsg_type) { 1515461fc0cSDmitry Safonov /* Compat message has the same layout as native */ 1525461fc0cSDmitry Safonov case XFRM_MSG_DELSA: 1535461fc0cSDmitry Safonov case XFRM_MSG_DELPOLICY: 1545461fc0cSDmitry Safonov case XFRM_MSG_FLUSHSA: 1555461fc0cSDmitry Safonov case XFRM_MSG_FLUSHPOLICY: 1565461fc0cSDmitry Safonov case XFRM_MSG_NEWAE: 1575461fc0cSDmitry Safonov case XFRM_MSG_REPORT: 1585461fc0cSDmitry Safonov case XFRM_MSG_MIGRATE: 1595461fc0cSDmitry Safonov case XFRM_MSG_NEWSADINFO: 1605461fc0cSDmitry Safonov case XFRM_MSG_NEWSPDINFO: 1615461fc0cSDmitry Safonov case XFRM_MSG_MAPPING: 1625461fc0cSDmitry Safonov WARN_ON_ONCE(src_len != payload); 1635461fc0cSDmitry Safonov memcpy(nlmsg_data(nlh_dst), nlmsg_data(nlh_src), src_len); 1645461fc0cSDmitry Safonov break; 1655461fc0cSDmitry Safonov /* 4 byte alignment for trailing u64 on native, but not on compat */ 1665461fc0cSDmitry Safonov case XFRM_MSG_NEWSA: 1675461fc0cSDmitry Safonov case XFRM_MSG_NEWPOLICY: 1685461fc0cSDmitry Safonov case XFRM_MSG_UPDSA: 1695461fc0cSDmitry Safonov case XFRM_MSG_UPDPOLICY: 1705461fc0cSDmitry Safonov WARN_ON_ONCE(src_len != payload + 4); 1715461fc0cSDmitry Safonov memcpy(nlmsg_data(nlh_dst), nlmsg_data(nlh_src), payload); 1725461fc0cSDmitry Safonov break; 1735461fc0cSDmitry Safonov case XFRM_MSG_EXPIRE: { 1745461fc0cSDmitry Safonov const struct xfrm_user_expire *src_ue = nlmsg_data(nlh_src); 1755461fc0cSDmitry Safonov struct compat_xfrm_user_expire *dst_ue = nlmsg_data(nlh_dst); 1765461fc0cSDmitry Safonov 1775461fc0cSDmitry Safonov /* compat_xfrm_user_expire has 4-byte smaller state */ 1785461fc0cSDmitry Safonov memcpy(dst_ue, src_ue, sizeof(dst_ue->state)); 1795461fc0cSDmitry Safonov dst_ue->hard = src_ue->hard; 1805461fc0cSDmitry Safonov break; 1815461fc0cSDmitry Safonov } 1825461fc0cSDmitry Safonov case XFRM_MSG_ACQUIRE: { 1835461fc0cSDmitry Safonov const struct xfrm_user_acquire *src_ua = nlmsg_data(nlh_src); 1845461fc0cSDmitry Safonov struct compat_xfrm_user_acquire *dst_ua = nlmsg_data(nlh_dst); 1855461fc0cSDmitry Safonov 1865461fc0cSDmitry Safonov memcpy(dst_ua, src_ua, offsetof(struct compat_xfrm_user_acquire, aalgos)); 1875461fc0cSDmitry Safonov dst_ua->aalgos = src_ua->aalgos; 1885461fc0cSDmitry Safonov dst_ua->ealgos = src_ua->ealgos; 1895461fc0cSDmitry Safonov dst_ua->calgos = src_ua->calgos; 1905461fc0cSDmitry Safonov dst_ua->seq = src_ua->seq; 1915461fc0cSDmitry Safonov break; 1925461fc0cSDmitry Safonov } 1935461fc0cSDmitry Safonov case XFRM_MSG_POLEXPIRE: { 1945461fc0cSDmitry Safonov const struct xfrm_user_polexpire *src_upe = nlmsg_data(nlh_src); 1955461fc0cSDmitry Safonov struct compat_xfrm_user_polexpire *dst_upe = nlmsg_data(nlh_dst); 1965461fc0cSDmitry Safonov 1975461fc0cSDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 1985461fc0cSDmitry Safonov memcpy(dst_upe, src_upe, sizeof(dst_upe->pol)); 1995461fc0cSDmitry Safonov dst_upe->hard = src_upe->hard; 2005461fc0cSDmitry Safonov break; 2015461fc0cSDmitry Safonov } 2025461fc0cSDmitry Safonov case XFRM_MSG_ALLOCSPI: { 2035461fc0cSDmitry Safonov const struct xfrm_userspi_info *src_usi = nlmsg_data(nlh_src); 2045461fc0cSDmitry Safonov struct compat_xfrm_userspi_info *dst_usi = nlmsg_data(nlh_dst); 2055461fc0cSDmitry Safonov 2065461fc0cSDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 2075461fc0cSDmitry Safonov memcpy(dst_usi, src_usi, sizeof(src_usi->info)); 2085461fc0cSDmitry Safonov dst_usi->min = src_usi->min; 2095461fc0cSDmitry Safonov dst_usi->max = src_usi->max; 2105461fc0cSDmitry Safonov break; 2115461fc0cSDmitry Safonov } 2125461fc0cSDmitry Safonov /* Not being sent by kernel */ 2135461fc0cSDmitry Safonov case XFRM_MSG_GETSA: 2145461fc0cSDmitry Safonov case XFRM_MSG_GETPOLICY: 2155461fc0cSDmitry Safonov case XFRM_MSG_GETAE: 2165461fc0cSDmitry Safonov case XFRM_MSG_GETSADINFO: 2175461fc0cSDmitry Safonov case XFRM_MSG_GETSPDINFO: 2185461fc0cSDmitry Safonov default: 2195461fc0cSDmitry Safonov WARN_ONCE(1, "unsupported nlmsg_type %d", nlh_src->nlmsg_type); 2205461fc0cSDmitry Safonov return ERR_PTR(-EOPNOTSUPP); 2215461fc0cSDmitry Safonov } 2225461fc0cSDmitry Safonov 2235461fc0cSDmitry Safonov return nlh_dst; 2245461fc0cSDmitry Safonov } 2255461fc0cSDmitry Safonov 2265461fc0cSDmitry Safonov static int xfrm_nla_cpy(struct sk_buff *dst, const struct nlattr *src, int len) 2275461fc0cSDmitry Safonov { 2285461fc0cSDmitry Safonov return nla_put(dst, src->nla_type, len, nla_data(src)); 2295461fc0cSDmitry Safonov } 2305461fc0cSDmitry Safonov 2315461fc0cSDmitry Safonov static int xfrm_xlate64_attr(struct sk_buff *dst, const struct nlattr *src) 2325461fc0cSDmitry Safonov { 2335461fc0cSDmitry Safonov switch (src->nla_type) { 2345461fc0cSDmitry Safonov case XFRMA_PAD: 2355461fc0cSDmitry Safonov /* Ignore */ 2365461fc0cSDmitry Safonov return 0; 2375461fc0cSDmitry Safonov case XFRMA_ALG_AUTH: 2385461fc0cSDmitry Safonov case XFRMA_ALG_CRYPT: 2395461fc0cSDmitry Safonov case XFRMA_ALG_COMP: 2405461fc0cSDmitry Safonov case XFRMA_ENCAP: 2415461fc0cSDmitry Safonov case XFRMA_TMPL: 2425461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2435461fc0cSDmitry Safonov case XFRMA_SA: 2445461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, XMSGSIZE(compat_xfrm_usersa_info)); 2455461fc0cSDmitry Safonov case XFRMA_POLICY: 2465461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, XMSGSIZE(compat_xfrm_userpolicy_info)); 2475461fc0cSDmitry Safonov case XFRMA_SEC_CTX: 2485461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2495461fc0cSDmitry Safonov case XFRMA_LTIME_VAL: 2505461fc0cSDmitry Safonov return nla_put_64bit(dst, src->nla_type, nla_len(src), 2515461fc0cSDmitry Safonov nla_data(src), XFRMA_PAD); 2525461fc0cSDmitry Safonov case XFRMA_REPLAY_VAL: 2535461fc0cSDmitry Safonov case XFRMA_REPLAY_THRESH: 2545461fc0cSDmitry Safonov case XFRMA_ETIMER_THRESH: 2555461fc0cSDmitry Safonov case XFRMA_SRCADDR: 2565461fc0cSDmitry Safonov case XFRMA_COADDR: 2575461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2585461fc0cSDmitry Safonov case XFRMA_LASTUSED: 2595461fc0cSDmitry Safonov return nla_put_64bit(dst, src->nla_type, nla_len(src), 2605461fc0cSDmitry Safonov nla_data(src), XFRMA_PAD); 2615461fc0cSDmitry Safonov case XFRMA_POLICY_TYPE: 2625461fc0cSDmitry Safonov case XFRMA_MIGRATE: 2635461fc0cSDmitry Safonov case XFRMA_ALG_AEAD: 2645461fc0cSDmitry Safonov case XFRMA_KMADDRESS: 2655461fc0cSDmitry Safonov case XFRMA_ALG_AUTH_TRUNC: 2665461fc0cSDmitry Safonov case XFRMA_MARK: 2675461fc0cSDmitry Safonov case XFRMA_TFCPAD: 2685461fc0cSDmitry Safonov case XFRMA_REPLAY_ESN_VAL: 2695461fc0cSDmitry Safonov case XFRMA_SA_EXTRA_FLAGS: 2705461fc0cSDmitry Safonov case XFRMA_PROTO: 2715461fc0cSDmitry Safonov case XFRMA_ADDRESS_FILTER: 2725461fc0cSDmitry Safonov case XFRMA_OFFLOAD_DEV: 2735461fc0cSDmitry Safonov case XFRMA_SET_MARK: 2745461fc0cSDmitry Safonov case XFRMA_SET_MARK_MASK: 2755461fc0cSDmitry Safonov case XFRMA_IF_ID: 2765461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2775461fc0cSDmitry Safonov default: 2785461fc0cSDmitry Safonov BUILD_BUG_ON(XFRMA_MAX != XFRMA_IF_ID); 2795461fc0cSDmitry Safonov WARN_ONCE(1, "unsupported nla_type %d", src->nla_type); 2805461fc0cSDmitry Safonov return -EOPNOTSUPP; 2815461fc0cSDmitry Safonov } 2825461fc0cSDmitry Safonov } 2835461fc0cSDmitry Safonov 2845461fc0cSDmitry Safonov /* Take kernel-built (64bit layout) and create 32bit layout for userspace */ 2855461fc0cSDmitry Safonov static int xfrm_xlate64(struct sk_buff *dst, const struct nlmsghdr *nlh_src) 2865461fc0cSDmitry Safonov { 2875461fc0cSDmitry Safonov u16 type = nlh_src->nlmsg_type - XFRM_MSG_BASE; 2885461fc0cSDmitry Safonov const struct nlattr *nla, *attrs; 2895461fc0cSDmitry Safonov struct nlmsghdr *nlh_dst; 2905461fc0cSDmitry Safonov int len, remaining; 2915461fc0cSDmitry Safonov 2925461fc0cSDmitry Safonov nlh_dst = xfrm_nlmsg_put_compat(dst, nlh_src, type); 2935461fc0cSDmitry Safonov if (IS_ERR(nlh_dst)) 2945461fc0cSDmitry Safonov return PTR_ERR(nlh_dst); 2955461fc0cSDmitry Safonov 2965461fc0cSDmitry Safonov attrs = nlmsg_attrdata(nlh_src, xfrm_msg_min[type]); 2975461fc0cSDmitry Safonov len = nlmsg_attrlen(nlh_src, xfrm_msg_min[type]); 2985461fc0cSDmitry Safonov 2995461fc0cSDmitry Safonov nla_for_each_attr(nla, attrs, len, remaining) { 3005461fc0cSDmitry Safonov int err = xfrm_xlate64_attr(dst, nla); 3015461fc0cSDmitry Safonov 3025461fc0cSDmitry Safonov if (err) 3035461fc0cSDmitry Safonov return err; 3045461fc0cSDmitry Safonov } 3055461fc0cSDmitry Safonov 3065461fc0cSDmitry Safonov nlmsg_end(dst, nlh_dst); 3075461fc0cSDmitry Safonov 3085461fc0cSDmitry Safonov return 0; 3095461fc0cSDmitry Safonov } 3105461fc0cSDmitry Safonov 3115461fc0cSDmitry Safonov static int xfrm_alloc_compat(struct sk_buff *skb, const struct nlmsghdr *nlh_src) 3125461fc0cSDmitry Safonov { 3135461fc0cSDmitry Safonov u16 type = nlh_src->nlmsg_type - XFRM_MSG_BASE; 3145461fc0cSDmitry Safonov struct sk_buff *new = NULL; 3155461fc0cSDmitry Safonov int err; 3165461fc0cSDmitry Safonov 3175461fc0cSDmitry Safonov if (WARN_ON_ONCE(type >= ARRAY_SIZE(xfrm_msg_min))) 3185461fc0cSDmitry Safonov return -EOPNOTSUPP; 3195461fc0cSDmitry Safonov 3205461fc0cSDmitry Safonov if (skb_shinfo(skb)->frag_list == NULL) { 3215461fc0cSDmitry Safonov new = alloc_skb(skb->len + skb_tailroom(skb), GFP_ATOMIC); 3225461fc0cSDmitry Safonov if (!new) 3235461fc0cSDmitry Safonov return -ENOMEM; 3245461fc0cSDmitry Safonov skb_shinfo(skb)->frag_list = new; 3255461fc0cSDmitry Safonov } 3265461fc0cSDmitry Safonov 3275461fc0cSDmitry Safonov err = xfrm_xlate64(skb_shinfo(skb)->frag_list, nlh_src); 3285461fc0cSDmitry Safonov if (err) { 3295461fc0cSDmitry Safonov if (new) { 3305461fc0cSDmitry Safonov kfree_skb(new); 3315461fc0cSDmitry Safonov skb_shinfo(skb)->frag_list = NULL; 3325461fc0cSDmitry Safonov } 3335461fc0cSDmitry Safonov return err; 3345461fc0cSDmitry Safonov } 3355461fc0cSDmitry Safonov 3365461fc0cSDmitry Safonov return 0; 3375461fc0cSDmitry Safonov } 3385461fc0cSDmitry Safonov 339*5106f4a8SDmitry Safonov /* Calculates len of translated 64-bit message. */ 340*5106f4a8SDmitry Safonov static size_t xfrm_user_rcv_calculate_len64(const struct nlmsghdr *src, 341*5106f4a8SDmitry Safonov struct nlattr *attrs[XFRMA_MAX+1]) 342*5106f4a8SDmitry Safonov { 343*5106f4a8SDmitry Safonov size_t len = nlmsg_len(src); 344*5106f4a8SDmitry Safonov 345*5106f4a8SDmitry Safonov switch (src->nlmsg_type) { 346*5106f4a8SDmitry Safonov case XFRM_MSG_NEWSA: 347*5106f4a8SDmitry Safonov case XFRM_MSG_NEWPOLICY: 348*5106f4a8SDmitry Safonov case XFRM_MSG_ALLOCSPI: 349*5106f4a8SDmitry Safonov case XFRM_MSG_ACQUIRE: 350*5106f4a8SDmitry Safonov case XFRM_MSG_UPDPOLICY: 351*5106f4a8SDmitry Safonov case XFRM_MSG_UPDSA: 352*5106f4a8SDmitry Safonov len += 4; 353*5106f4a8SDmitry Safonov break; 354*5106f4a8SDmitry Safonov case XFRM_MSG_EXPIRE: 355*5106f4a8SDmitry Safonov case XFRM_MSG_POLEXPIRE: 356*5106f4a8SDmitry Safonov len += 8; 357*5106f4a8SDmitry Safonov break; 358*5106f4a8SDmitry Safonov default: 359*5106f4a8SDmitry Safonov break; 360*5106f4a8SDmitry Safonov } 361*5106f4a8SDmitry Safonov 362*5106f4a8SDmitry Safonov if (attrs[XFRMA_SA]) 363*5106f4a8SDmitry Safonov len += 4; 364*5106f4a8SDmitry Safonov if (attrs[XFRMA_POLICY]) 365*5106f4a8SDmitry Safonov len += 4; 366*5106f4a8SDmitry Safonov 367*5106f4a8SDmitry Safonov /* XXX: some attrs may need to be realigned 368*5106f4a8SDmitry Safonov * if !CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 369*5106f4a8SDmitry Safonov */ 370*5106f4a8SDmitry Safonov 371*5106f4a8SDmitry Safonov return len; 372*5106f4a8SDmitry Safonov } 373*5106f4a8SDmitry Safonov 374*5106f4a8SDmitry Safonov static int xfrm_attr_cpy32(void *dst, size_t *pos, const struct nlattr *src, 375*5106f4a8SDmitry Safonov size_t size, int copy_len, int payload) 376*5106f4a8SDmitry Safonov { 377*5106f4a8SDmitry Safonov struct nlmsghdr *nlmsg = dst; 378*5106f4a8SDmitry Safonov struct nlattr *nla; 379*5106f4a8SDmitry Safonov 380*5106f4a8SDmitry Safonov if (WARN_ON_ONCE(copy_len > payload)) 381*5106f4a8SDmitry Safonov copy_len = payload; 382*5106f4a8SDmitry Safonov 383*5106f4a8SDmitry Safonov if (size - *pos < nla_attr_size(payload)) 384*5106f4a8SDmitry Safonov return -ENOBUFS; 385*5106f4a8SDmitry Safonov 386*5106f4a8SDmitry Safonov nla = dst + *pos; 387*5106f4a8SDmitry Safonov 388*5106f4a8SDmitry Safonov memcpy(nla, src, nla_attr_size(copy_len)); 389*5106f4a8SDmitry Safonov nla->nla_len = nla_attr_size(payload); 390*5106f4a8SDmitry Safonov *pos += nla_attr_size(payload); 391*5106f4a8SDmitry Safonov nlmsg->nlmsg_len += nla->nla_len; 392*5106f4a8SDmitry Safonov 393*5106f4a8SDmitry Safonov memset(dst + *pos, 0, payload - copy_len); 394*5106f4a8SDmitry Safonov *pos += payload - copy_len; 395*5106f4a8SDmitry Safonov 396*5106f4a8SDmitry Safonov return 0; 397*5106f4a8SDmitry Safonov } 398*5106f4a8SDmitry Safonov 399*5106f4a8SDmitry Safonov static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla, 400*5106f4a8SDmitry Safonov size_t *pos, size_t size, 401*5106f4a8SDmitry Safonov struct netlink_ext_ack *extack) 402*5106f4a8SDmitry Safonov { 403*5106f4a8SDmitry Safonov int type = nla_type(nla); 404*5106f4a8SDmitry Safonov u16 pol_len32, pol_len64; 405*5106f4a8SDmitry Safonov int err; 406*5106f4a8SDmitry Safonov 407*5106f4a8SDmitry Safonov if (type > XFRMA_MAX) { 408*5106f4a8SDmitry Safonov BUILD_BUG_ON(XFRMA_MAX != XFRMA_IF_ID); 409*5106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Bad attribute"); 410*5106f4a8SDmitry Safonov return -EOPNOTSUPP; 411*5106f4a8SDmitry Safonov } 412*5106f4a8SDmitry Safonov if (nla_len(nla) < compat_policy[type].len) { 413*5106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Attribute bad length"); 414*5106f4a8SDmitry Safonov return -EOPNOTSUPP; 415*5106f4a8SDmitry Safonov } 416*5106f4a8SDmitry Safonov 417*5106f4a8SDmitry Safonov pol_len32 = compat_policy[type].len; 418*5106f4a8SDmitry Safonov pol_len64 = xfrma_policy[type].len; 419*5106f4a8SDmitry Safonov 420*5106f4a8SDmitry Safonov /* XFRMA_SA and XFRMA_POLICY - need to know how-to translate */ 421*5106f4a8SDmitry Safonov if (pol_len32 != pol_len64) { 422*5106f4a8SDmitry Safonov if (nla_len(nla) != compat_policy[type].len) { 423*5106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Attribute bad length"); 424*5106f4a8SDmitry Safonov return -EOPNOTSUPP; 425*5106f4a8SDmitry Safonov } 426*5106f4a8SDmitry Safonov err = xfrm_attr_cpy32(dst, pos, nla, size, pol_len32, pol_len64); 427*5106f4a8SDmitry Safonov if (err) 428*5106f4a8SDmitry Safonov return err; 429*5106f4a8SDmitry Safonov } 430*5106f4a8SDmitry Safonov 431*5106f4a8SDmitry Safonov return xfrm_attr_cpy32(dst, pos, nla, size, nla_len(nla), nla_len(nla)); 432*5106f4a8SDmitry Safonov } 433*5106f4a8SDmitry Safonov 434*5106f4a8SDmitry Safonov static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src, 435*5106f4a8SDmitry Safonov struct nlattr *attrs[XFRMA_MAX+1], 436*5106f4a8SDmitry Safonov size_t size, u8 type, struct netlink_ext_ack *extack) 437*5106f4a8SDmitry Safonov { 438*5106f4a8SDmitry Safonov size_t pos; 439*5106f4a8SDmitry Safonov int i; 440*5106f4a8SDmitry Safonov 441*5106f4a8SDmitry Safonov memcpy(dst, src, NLMSG_HDRLEN); 442*5106f4a8SDmitry Safonov dst->nlmsg_len = NLMSG_HDRLEN + xfrm_msg_min[type]; 443*5106f4a8SDmitry Safonov memset(nlmsg_data(dst), 0, xfrm_msg_min[type]); 444*5106f4a8SDmitry Safonov 445*5106f4a8SDmitry Safonov switch (src->nlmsg_type) { 446*5106f4a8SDmitry Safonov /* Compat message has the same layout as native */ 447*5106f4a8SDmitry Safonov case XFRM_MSG_DELSA: 448*5106f4a8SDmitry Safonov case XFRM_MSG_GETSA: 449*5106f4a8SDmitry Safonov case XFRM_MSG_DELPOLICY: 450*5106f4a8SDmitry Safonov case XFRM_MSG_GETPOLICY: 451*5106f4a8SDmitry Safonov case XFRM_MSG_FLUSHSA: 452*5106f4a8SDmitry Safonov case XFRM_MSG_FLUSHPOLICY: 453*5106f4a8SDmitry Safonov case XFRM_MSG_NEWAE: 454*5106f4a8SDmitry Safonov case XFRM_MSG_GETAE: 455*5106f4a8SDmitry Safonov case XFRM_MSG_REPORT: 456*5106f4a8SDmitry Safonov case XFRM_MSG_MIGRATE: 457*5106f4a8SDmitry Safonov case XFRM_MSG_NEWSADINFO: 458*5106f4a8SDmitry Safonov case XFRM_MSG_GETSADINFO: 459*5106f4a8SDmitry Safonov case XFRM_MSG_NEWSPDINFO: 460*5106f4a8SDmitry Safonov case XFRM_MSG_GETSPDINFO: 461*5106f4a8SDmitry Safonov case XFRM_MSG_MAPPING: 462*5106f4a8SDmitry Safonov memcpy(nlmsg_data(dst), nlmsg_data(src), compat_msg_min[type]); 463*5106f4a8SDmitry Safonov break; 464*5106f4a8SDmitry Safonov /* 4 byte alignment for trailing u64 on native, but not on compat */ 465*5106f4a8SDmitry Safonov case XFRM_MSG_NEWSA: 466*5106f4a8SDmitry Safonov case XFRM_MSG_NEWPOLICY: 467*5106f4a8SDmitry Safonov case XFRM_MSG_UPDSA: 468*5106f4a8SDmitry Safonov case XFRM_MSG_UPDPOLICY: 469*5106f4a8SDmitry Safonov memcpy(nlmsg_data(dst), nlmsg_data(src), compat_msg_min[type]); 470*5106f4a8SDmitry Safonov break; 471*5106f4a8SDmitry Safonov case XFRM_MSG_EXPIRE: { 472*5106f4a8SDmitry Safonov const struct compat_xfrm_user_expire *src_ue = nlmsg_data(src); 473*5106f4a8SDmitry Safonov struct xfrm_user_expire *dst_ue = nlmsg_data(dst); 474*5106f4a8SDmitry Safonov 475*5106f4a8SDmitry Safonov /* compat_xfrm_user_expire has 4-byte smaller state */ 476*5106f4a8SDmitry Safonov memcpy(dst_ue, src_ue, sizeof(src_ue->state)); 477*5106f4a8SDmitry Safonov dst_ue->hard = src_ue->hard; 478*5106f4a8SDmitry Safonov break; 479*5106f4a8SDmitry Safonov } 480*5106f4a8SDmitry Safonov case XFRM_MSG_ACQUIRE: { 481*5106f4a8SDmitry Safonov const struct compat_xfrm_user_acquire *src_ua = nlmsg_data(src); 482*5106f4a8SDmitry Safonov struct xfrm_user_acquire *dst_ua = nlmsg_data(dst); 483*5106f4a8SDmitry Safonov 484*5106f4a8SDmitry Safonov memcpy(dst_ua, src_ua, offsetof(struct compat_xfrm_user_acquire, aalgos)); 485*5106f4a8SDmitry Safonov dst_ua->aalgos = src_ua->aalgos; 486*5106f4a8SDmitry Safonov dst_ua->ealgos = src_ua->ealgos; 487*5106f4a8SDmitry Safonov dst_ua->calgos = src_ua->calgos; 488*5106f4a8SDmitry Safonov dst_ua->seq = src_ua->seq; 489*5106f4a8SDmitry Safonov break; 490*5106f4a8SDmitry Safonov } 491*5106f4a8SDmitry Safonov case XFRM_MSG_POLEXPIRE: { 492*5106f4a8SDmitry Safonov const struct compat_xfrm_user_polexpire *src_upe = nlmsg_data(src); 493*5106f4a8SDmitry Safonov struct xfrm_user_polexpire *dst_upe = nlmsg_data(dst); 494*5106f4a8SDmitry Safonov 495*5106f4a8SDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 496*5106f4a8SDmitry Safonov memcpy(dst_upe, src_upe, sizeof(src_upe->pol)); 497*5106f4a8SDmitry Safonov dst_upe->hard = src_upe->hard; 498*5106f4a8SDmitry Safonov break; 499*5106f4a8SDmitry Safonov } 500*5106f4a8SDmitry Safonov case XFRM_MSG_ALLOCSPI: { 501*5106f4a8SDmitry Safonov const struct compat_xfrm_userspi_info *src_usi = nlmsg_data(src); 502*5106f4a8SDmitry Safonov struct xfrm_userspi_info *dst_usi = nlmsg_data(dst); 503*5106f4a8SDmitry Safonov 504*5106f4a8SDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 505*5106f4a8SDmitry Safonov memcpy(dst_usi, src_usi, sizeof(src_usi->info)); 506*5106f4a8SDmitry Safonov dst_usi->min = src_usi->min; 507*5106f4a8SDmitry Safonov dst_usi->max = src_usi->max; 508*5106f4a8SDmitry Safonov break; 509*5106f4a8SDmitry Safonov } 510*5106f4a8SDmitry Safonov default: 511*5106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Unsupported message type"); 512*5106f4a8SDmitry Safonov return -EOPNOTSUPP; 513*5106f4a8SDmitry Safonov } 514*5106f4a8SDmitry Safonov pos = dst->nlmsg_len; 515*5106f4a8SDmitry Safonov 516*5106f4a8SDmitry Safonov for (i = 1; i < XFRMA_MAX + 1; i++) { 517*5106f4a8SDmitry Safonov int err; 518*5106f4a8SDmitry Safonov 519*5106f4a8SDmitry Safonov if (i == XFRMA_PAD) 520*5106f4a8SDmitry Safonov continue; 521*5106f4a8SDmitry Safonov 522*5106f4a8SDmitry Safonov if (!attrs[i]) 523*5106f4a8SDmitry Safonov continue; 524*5106f4a8SDmitry Safonov 525*5106f4a8SDmitry Safonov err = xfrm_xlate32_attr(dst, attrs[i], &pos, size, extack); 526*5106f4a8SDmitry Safonov if (err) 527*5106f4a8SDmitry Safonov return err; 528*5106f4a8SDmitry Safonov } 529*5106f4a8SDmitry Safonov 530*5106f4a8SDmitry Safonov return 0; 531*5106f4a8SDmitry Safonov } 532*5106f4a8SDmitry Safonov 533*5106f4a8SDmitry Safonov static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32, 534*5106f4a8SDmitry Safonov int maxtype, const struct nla_policy *policy, 535*5106f4a8SDmitry Safonov struct netlink_ext_ack *extack) 536*5106f4a8SDmitry Safonov { 537*5106f4a8SDmitry Safonov /* netlink_rcv_skb() checks if a message has full (struct nlmsghdr) */ 538*5106f4a8SDmitry Safonov u16 type = h32->nlmsg_type - XFRM_MSG_BASE; 539*5106f4a8SDmitry Safonov struct nlattr *attrs[XFRMA_MAX+1]; 540*5106f4a8SDmitry Safonov struct nlmsghdr *h64; 541*5106f4a8SDmitry Safonov size_t len; 542*5106f4a8SDmitry Safonov int err; 543*5106f4a8SDmitry Safonov 544*5106f4a8SDmitry Safonov BUILD_BUG_ON(ARRAY_SIZE(xfrm_msg_min) != ARRAY_SIZE(compat_msg_min)); 545*5106f4a8SDmitry Safonov 546*5106f4a8SDmitry Safonov if (type >= ARRAY_SIZE(xfrm_msg_min)) 547*5106f4a8SDmitry Safonov return ERR_PTR(-EINVAL); 548*5106f4a8SDmitry Safonov 549*5106f4a8SDmitry Safonov /* Don't call parse: the message might have only nlmsg header */ 550*5106f4a8SDmitry Safonov if ((h32->nlmsg_type == XFRM_MSG_GETSA || 551*5106f4a8SDmitry Safonov h32->nlmsg_type == XFRM_MSG_GETPOLICY) && 552*5106f4a8SDmitry Safonov (h32->nlmsg_flags & NLM_F_DUMP)) 553*5106f4a8SDmitry Safonov return NULL; 554*5106f4a8SDmitry Safonov 555*5106f4a8SDmitry Safonov err = nlmsg_parse_deprecated(h32, compat_msg_min[type], attrs, 556*5106f4a8SDmitry Safonov maxtype ? : XFRMA_MAX, policy ? : compat_policy, extack); 557*5106f4a8SDmitry Safonov if (err < 0) 558*5106f4a8SDmitry Safonov return ERR_PTR(err); 559*5106f4a8SDmitry Safonov 560*5106f4a8SDmitry Safonov len = xfrm_user_rcv_calculate_len64(h32, attrs); 561*5106f4a8SDmitry Safonov /* The message doesn't need translation */ 562*5106f4a8SDmitry Safonov if (len == nlmsg_len(h32)) 563*5106f4a8SDmitry Safonov return NULL; 564*5106f4a8SDmitry Safonov 565*5106f4a8SDmitry Safonov len += NLMSG_HDRLEN; 566*5106f4a8SDmitry Safonov h64 = kvmalloc(len, GFP_KERNEL | __GFP_ZERO); 567*5106f4a8SDmitry Safonov if (!h64) 568*5106f4a8SDmitry Safonov return ERR_PTR(-ENOMEM); 569*5106f4a8SDmitry Safonov 570*5106f4a8SDmitry Safonov err = xfrm_xlate32(h64, h32, attrs, len, type, extack); 571*5106f4a8SDmitry Safonov if (err < 0) { 572*5106f4a8SDmitry Safonov kvfree(h64); 573*5106f4a8SDmitry Safonov return ERR_PTR(err); 574*5106f4a8SDmitry Safonov } 575*5106f4a8SDmitry Safonov 576*5106f4a8SDmitry Safonov return h64; 577*5106f4a8SDmitry Safonov } 578*5106f4a8SDmitry Safonov 579c9e7c76dSDmitry Safonov static struct xfrm_translator xfrm_translator = { 580c9e7c76dSDmitry Safonov .owner = THIS_MODULE, 5815461fc0cSDmitry Safonov .alloc_compat = xfrm_alloc_compat, 582*5106f4a8SDmitry Safonov .rcv_msg_compat = xfrm_user_rcv_msg_compat, 583c9e7c76dSDmitry Safonov }; 584c9e7c76dSDmitry Safonov 585c9e7c76dSDmitry Safonov static int __init xfrm_compat_init(void) 586c9e7c76dSDmitry Safonov { 587c9e7c76dSDmitry Safonov return xfrm_register_translator(&xfrm_translator); 588c9e7c76dSDmitry Safonov } 589c9e7c76dSDmitry Safonov 590c9e7c76dSDmitry Safonov static void __exit xfrm_compat_exit(void) 591c9e7c76dSDmitry Safonov { 592c9e7c76dSDmitry Safonov xfrm_unregister_translator(&xfrm_translator); 593c9e7c76dSDmitry Safonov } 594c9e7c76dSDmitry Safonov 595c9e7c76dSDmitry Safonov module_init(xfrm_compat_init); 596c9e7c76dSDmitry Safonov module_exit(xfrm_compat_exit); 597c9e7c76dSDmitry Safonov MODULE_LICENSE("GPL"); 598c9e7c76dSDmitry Safonov MODULE_AUTHOR("Dmitry Safonov"); 599c9e7c76dSDmitry Safonov MODULE_DESCRIPTION("XFRM 32-bit compatibility layer"); 600