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 995106f4a8SDmitry Safonov static const struct nla_policy compat_policy[XFRMA_MAX+1] = { 1005106f4a8SDmitry Safonov [XFRMA_SA] = { .len = XMSGSIZE(compat_xfrm_usersa_info)}, 1015106f4a8SDmitry Safonov [XFRMA_POLICY] = { .len = XMSGSIZE(compat_xfrm_userpolicy_info)}, 1025106f4a8SDmitry Safonov [XFRMA_LASTUSED] = { .type = NLA_U64}, 1035106f4a8SDmitry Safonov [XFRMA_ALG_AUTH_TRUNC] = { .len = sizeof(struct xfrm_algo_auth)}, 1045106f4a8SDmitry Safonov [XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) }, 1055106f4a8SDmitry Safonov [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) }, 1065106f4a8SDmitry Safonov [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) }, 1075106f4a8SDmitry Safonov [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) }, 1085106f4a8SDmitry Safonov [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) }, 1095106f4a8SDmitry Safonov [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) }, 1105106f4a8SDmitry Safonov [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_sec_ctx) }, 1115106f4a8SDmitry Safonov [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) }, 1125106f4a8SDmitry Safonov [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) }, 1135106f4a8SDmitry Safonov [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, 1145106f4a8SDmitry Safonov [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, 1155106f4a8SDmitry Safonov [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) }, 1165106f4a8SDmitry Safonov [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, 1175106f4a8SDmitry Safonov [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, 1185106f4a8SDmitry Safonov [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, 1195106f4a8SDmitry Safonov [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, 1205106f4a8SDmitry Safonov [XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) }, 1215106f4a8SDmitry Safonov [XFRMA_TFCPAD] = { .type = NLA_U32 }, 1225106f4a8SDmitry Safonov [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, 1235106f4a8SDmitry Safonov [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, 1245106f4a8SDmitry Safonov [XFRMA_PROTO] = { .type = NLA_U8 }, 1255106f4a8SDmitry Safonov [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, 1265106f4a8SDmitry Safonov [XFRMA_OFFLOAD_DEV] = { .len = sizeof(struct xfrm_user_offload) }, 1275106f4a8SDmitry Safonov [XFRMA_SET_MARK] = { .type = NLA_U32 }, 1285106f4a8SDmitry Safonov [XFRMA_SET_MARK_MASK] = { .type = NLA_U32 }, 1295106f4a8SDmitry Safonov [XFRMA_IF_ID] = { .type = NLA_U32 }, 1305106f4a8SDmitry Safonov }; 1315106f4a8SDmitry 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: 219ef19e111SDmitry Safonov pr_warn_once("unsupported nlmsg_type %d\n", 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; 237dbd7ae51SDmitry Safonov case XFRMA_UNSPEC: 2385461fc0cSDmitry Safonov case XFRMA_ALG_AUTH: 2395461fc0cSDmitry Safonov case XFRMA_ALG_CRYPT: 2405461fc0cSDmitry Safonov case XFRMA_ALG_COMP: 2415461fc0cSDmitry Safonov case XFRMA_ENCAP: 2425461fc0cSDmitry Safonov case XFRMA_TMPL: 2435461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2445461fc0cSDmitry Safonov case XFRMA_SA: 2455461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, XMSGSIZE(compat_xfrm_usersa_info)); 2465461fc0cSDmitry Safonov case XFRMA_POLICY: 2475461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, XMSGSIZE(compat_xfrm_userpolicy_info)); 2485461fc0cSDmitry Safonov case XFRMA_SEC_CTX: 2495461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2505461fc0cSDmitry Safonov case XFRMA_LTIME_VAL: 2515461fc0cSDmitry Safonov return nla_put_64bit(dst, src->nla_type, nla_len(src), 2525461fc0cSDmitry Safonov nla_data(src), XFRMA_PAD); 2535461fc0cSDmitry Safonov case XFRMA_REPLAY_VAL: 2545461fc0cSDmitry Safonov case XFRMA_REPLAY_THRESH: 2555461fc0cSDmitry Safonov case XFRMA_ETIMER_THRESH: 2565461fc0cSDmitry Safonov case XFRMA_SRCADDR: 2575461fc0cSDmitry Safonov case XFRMA_COADDR: 2585461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2595461fc0cSDmitry Safonov case XFRMA_LASTUSED: 2605461fc0cSDmitry Safonov return nla_put_64bit(dst, src->nla_type, nla_len(src), 2615461fc0cSDmitry Safonov nla_data(src), XFRMA_PAD); 2625461fc0cSDmitry Safonov case XFRMA_POLICY_TYPE: 2635461fc0cSDmitry Safonov case XFRMA_MIGRATE: 2645461fc0cSDmitry Safonov case XFRMA_ALG_AEAD: 2655461fc0cSDmitry Safonov case XFRMA_KMADDRESS: 2665461fc0cSDmitry Safonov case XFRMA_ALG_AUTH_TRUNC: 2675461fc0cSDmitry Safonov case XFRMA_MARK: 2685461fc0cSDmitry Safonov case XFRMA_TFCPAD: 2695461fc0cSDmitry Safonov case XFRMA_REPLAY_ESN_VAL: 2705461fc0cSDmitry Safonov case XFRMA_SA_EXTRA_FLAGS: 2715461fc0cSDmitry Safonov case XFRMA_PROTO: 2725461fc0cSDmitry Safonov case XFRMA_ADDRESS_FILTER: 2735461fc0cSDmitry Safonov case XFRMA_OFFLOAD_DEV: 2745461fc0cSDmitry Safonov case XFRMA_SET_MARK: 2755461fc0cSDmitry Safonov case XFRMA_SET_MARK_MASK: 2765461fc0cSDmitry Safonov case XFRMA_IF_ID: 2775461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2785461fc0cSDmitry Safonov default: 2795461fc0cSDmitry Safonov BUILD_BUG_ON(XFRMA_MAX != XFRMA_IF_ID); 280ef19e111SDmitry Safonov pr_warn_once("unsupported nla_type %d\n", src->nla_type); 2815461fc0cSDmitry Safonov return -EOPNOTSUPP; 2825461fc0cSDmitry Safonov } 2835461fc0cSDmitry Safonov } 2845461fc0cSDmitry Safonov 2855461fc0cSDmitry Safonov /* Take kernel-built (64bit layout) and create 32bit layout for userspace */ 2865461fc0cSDmitry Safonov static int xfrm_xlate64(struct sk_buff *dst, const struct nlmsghdr *nlh_src) 2875461fc0cSDmitry Safonov { 2885461fc0cSDmitry Safonov u16 type = nlh_src->nlmsg_type - XFRM_MSG_BASE; 2895461fc0cSDmitry Safonov const struct nlattr *nla, *attrs; 2905461fc0cSDmitry Safonov struct nlmsghdr *nlh_dst; 2915461fc0cSDmitry Safonov int len, remaining; 2925461fc0cSDmitry Safonov 2935461fc0cSDmitry Safonov nlh_dst = xfrm_nlmsg_put_compat(dst, nlh_src, type); 2945461fc0cSDmitry Safonov if (IS_ERR(nlh_dst)) 2955461fc0cSDmitry Safonov return PTR_ERR(nlh_dst); 2965461fc0cSDmitry Safonov 2975461fc0cSDmitry Safonov attrs = nlmsg_attrdata(nlh_src, xfrm_msg_min[type]); 2985461fc0cSDmitry Safonov len = nlmsg_attrlen(nlh_src, xfrm_msg_min[type]); 2995461fc0cSDmitry Safonov 3005461fc0cSDmitry Safonov nla_for_each_attr(nla, attrs, len, remaining) { 301*4e950506SDmitry Safonov int err; 3025461fc0cSDmitry Safonov 303*4e950506SDmitry Safonov switch (type) { 304*4e950506SDmitry Safonov case XFRM_MSG_NEWSPDINFO: 305*4e950506SDmitry Safonov err = xfrm_nla_cpy(dst, nla, nla_len(nla)); 306*4e950506SDmitry Safonov break; 307*4e950506SDmitry Safonov default: 308*4e950506SDmitry Safonov err = xfrm_xlate64_attr(dst, nla); 309*4e950506SDmitry Safonov break; 310*4e950506SDmitry Safonov } 3115461fc0cSDmitry Safonov if (err) 3125461fc0cSDmitry Safonov return err; 3135461fc0cSDmitry Safonov } 3145461fc0cSDmitry Safonov 3155461fc0cSDmitry Safonov nlmsg_end(dst, nlh_dst); 3165461fc0cSDmitry Safonov 3175461fc0cSDmitry Safonov return 0; 3185461fc0cSDmitry Safonov } 3195461fc0cSDmitry Safonov 3205461fc0cSDmitry Safonov static int xfrm_alloc_compat(struct sk_buff *skb, const struct nlmsghdr *nlh_src) 3215461fc0cSDmitry Safonov { 3225461fc0cSDmitry Safonov u16 type = nlh_src->nlmsg_type - XFRM_MSG_BASE; 3235461fc0cSDmitry Safonov struct sk_buff *new = NULL; 3245461fc0cSDmitry Safonov int err; 3255461fc0cSDmitry Safonov 326ef19e111SDmitry Safonov if (type >= ARRAY_SIZE(xfrm_msg_min)) { 327ef19e111SDmitry Safonov pr_warn_once("unsupported nlmsg_type %d\n", nlh_src->nlmsg_type); 3285461fc0cSDmitry Safonov return -EOPNOTSUPP; 329ef19e111SDmitry Safonov } 3305461fc0cSDmitry Safonov 3315461fc0cSDmitry Safonov if (skb_shinfo(skb)->frag_list == NULL) { 3325461fc0cSDmitry Safonov new = alloc_skb(skb->len + skb_tailroom(skb), GFP_ATOMIC); 3335461fc0cSDmitry Safonov if (!new) 3345461fc0cSDmitry Safonov return -ENOMEM; 3355461fc0cSDmitry Safonov skb_shinfo(skb)->frag_list = new; 3365461fc0cSDmitry Safonov } 3375461fc0cSDmitry Safonov 3385461fc0cSDmitry Safonov err = xfrm_xlate64(skb_shinfo(skb)->frag_list, nlh_src); 3395461fc0cSDmitry Safonov if (err) { 3405461fc0cSDmitry Safonov if (new) { 3415461fc0cSDmitry Safonov kfree_skb(new); 3425461fc0cSDmitry Safonov skb_shinfo(skb)->frag_list = NULL; 3435461fc0cSDmitry Safonov } 3445461fc0cSDmitry Safonov return err; 3455461fc0cSDmitry Safonov } 3465461fc0cSDmitry Safonov 3475461fc0cSDmitry Safonov return 0; 3485461fc0cSDmitry Safonov } 3495461fc0cSDmitry Safonov 3505106f4a8SDmitry Safonov /* Calculates len of translated 64-bit message. */ 3515106f4a8SDmitry Safonov static size_t xfrm_user_rcv_calculate_len64(const struct nlmsghdr *src, 352*4e950506SDmitry Safonov struct nlattr *attrs[XFRMA_MAX + 1], 353*4e950506SDmitry Safonov int maxtype) 3545106f4a8SDmitry Safonov { 3555106f4a8SDmitry Safonov size_t len = nlmsg_len(src); 3565106f4a8SDmitry Safonov 3575106f4a8SDmitry Safonov switch (src->nlmsg_type) { 3585106f4a8SDmitry Safonov case XFRM_MSG_NEWSA: 3595106f4a8SDmitry Safonov case XFRM_MSG_NEWPOLICY: 3605106f4a8SDmitry Safonov case XFRM_MSG_ALLOCSPI: 3615106f4a8SDmitry Safonov case XFRM_MSG_ACQUIRE: 3625106f4a8SDmitry Safonov case XFRM_MSG_UPDPOLICY: 3635106f4a8SDmitry Safonov case XFRM_MSG_UPDSA: 3645106f4a8SDmitry Safonov len += 4; 3655106f4a8SDmitry Safonov break; 3665106f4a8SDmitry Safonov case XFRM_MSG_EXPIRE: 3675106f4a8SDmitry Safonov case XFRM_MSG_POLEXPIRE: 3685106f4a8SDmitry Safonov len += 8; 3695106f4a8SDmitry Safonov break; 370*4e950506SDmitry Safonov case XFRM_MSG_NEWSPDINFO: 371*4e950506SDmitry Safonov /* attirbutes are xfrm_spdattr_type_t, not xfrm_attr_type_t */ 372*4e950506SDmitry Safonov return len; 3735106f4a8SDmitry Safonov default: 3745106f4a8SDmitry Safonov break; 3755106f4a8SDmitry Safonov } 3765106f4a8SDmitry Safonov 377*4e950506SDmitry Safonov /* Unexpected for anything, but XFRM_MSG_NEWSPDINFO, please 378*4e950506SDmitry Safonov * correct both 64=>32-bit and 32=>64-bit translators to copy 379*4e950506SDmitry Safonov * new attributes. 380*4e950506SDmitry Safonov */ 381*4e950506SDmitry Safonov if (WARN_ON_ONCE(maxtype)) 382*4e950506SDmitry Safonov return len; 383*4e950506SDmitry Safonov 3845106f4a8SDmitry Safonov if (attrs[XFRMA_SA]) 3855106f4a8SDmitry Safonov len += 4; 3865106f4a8SDmitry Safonov if (attrs[XFRMA_POLICY]) 3875106f4a8SDmitry Safonov len += 4; 3885106f4a8SDmitry Safonov 3895106f4a8SDmitry Safonov /* XXX: some attrs may need to be realigned 3905106f4a8SDmitry Safonov * if !CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 3915106f4a8SDmitry Safonov */ 3925106f4a8SDmitry Safonov 3935106f4a8SDmitry Safonov return len; 3945106f4a8SDmitry Safonov } 3955106f4a8SDmitry Safonov 3965106f4a8SDmitry Safonov static int xfrm_attr_cpy32(void *dst, size_t *pos, const struct nlattr *src, 3975106f4a8SDmitry Safonov size_t size, int copy_len, int payload) 3985106f4a8SDmitry Safonov { 3995106f4a8SDmitry Safonov struct nlmsghdr *nlmsg = dst; 4005106f4a8SDmitry Safonov struct nlattr *nla; 4015106f4a8SDmitry Safonov 402ef19e111SDmitry Safonov /* xfrm_user_rcv_msg_compat() relies on fact that 32-bit messages 403ef19e111SDmitry Safonov * have the same len or shorted than 64-bit ones. 404ef19e111SDmitry Safonov * 32-bit translation that is bigger than 64-bit original is unexpected. 405ef19e111SDmitry Safonov */ 4065106f4a8SDmitry Safonov if (WARN_ON_ONCE(copy_len > payload)) 4075106f4a8SDmitry Safonov copy_len = payload; 4085106f4a8SDmitry Safonov 4095106f4a8SDmitry Safonov if (size - *pos < nla_attr_size(payload)) 4105106f4a8SDmitry Safonov return -ENOBUFS; 4115106f4a8SDmitry Safonov 4125106f4a8SDmitry Safonov nla = dst + *pos; 4135106f4a8SDmitry Safonov 4145106f4a8SDmitry Safonov memcpy(nla, src, nla_attr_size(copy_len)); 4155106f4a8SDmitry Safonov nla->nla_len = nla_attr_size(payload); 416d1949d04SDmitry Safonov *pos += nla_attr_size(copy_len); 4175106f4a8SDmitry Safonov nlmsg->nlmsg_len += nla->nla_len; 4185106f4a8SDmitry Safonov 4195106f4a8SDmitry Safonov memset(dst + *pos, 0, payload - copy_len); 4205106f4a8SDmitry Safonov *pos += payload - copy_len; 4215106f4a8SDmitry Safonov 4225106f4a8SDmitry Safonov return 0; 4235106f4a8SDmitry Safonov } 4245106f4a8SDmitry Safonov 4255106f4a8SDmitry Safonov static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla, 4265106f4a8SDmitry Safonov size_t *pos, size_t size, 4275106f4a8SDmitry Safonov struct netlink_ext_ack *extack) 4285106f4a8SDmitry Safonov { 4295106f4a8SDmitry Safonov int type = nla_type(nla); 4305106f4a8SDmitry Safonov u16 pol_len32, pol_len64; 4315106f4a8SDmitry Safonov int err; 4325106f4a8SDmitry Safonov 4335106f4a8SDmitry Safonov if (type > XFRMA_MAX) { 4345106f4a8SDmitry Safonov BUILD_BUG_ON(XFRMA_MAX != XFRMA_IF_ID); 4355106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Bad attribute"); 4365106f4a8SDmitry Safonov return -EOPNOTSUPP; 4375106f4a8SDmitry Safonov } 4385106f4a8SDmitry Safonov if (nla_len(nla) < compat_policy[type].len) { 4395106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Attribute bad length"); 4405106f4a8SDmitry Safonov return -EOPNOTSUPP; 4415106f4a8SDmitry Safonov } 4425106f4a8SDmitry Safonov 4435106f4a8SDmitry Safonov pol_len32 = compat_policy[type].len; 4445106f4a8SDmitry Safonov pol_len64 = xfrma_policy[type].len; 4455106f4a8SDmitry Safonov 4465106f4a8SDmitry Safonov /* XFRMA_SA and XFRMA_POLICY - need to know how-to translate */ 4475106f4a8SDmitry Safonov if (pol_len32 != pol_len64) { 4485106f4a8SDmitry Safonov if (nla_len(nla) != compat_policy[type].len) { 4495106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Attribute bad length"); 4505106f4a8SDmitry Safonov return -EOPNOTSUPP; 4515106f4a8SDmitry Safonov } 4525106f4a8SDmitry Safonov err = xfrm_attr_cpy32(dst, pos, nla, size, pol_len32, pol_len64); 4535106f4a8SDmitry Safonov if (err) 4545106f4a8SDmitry Safonov return err; 4555106f4a8SDmitry Safonov } 4565106f4a8SDmitry Safonov 4575106f4a8SDmitry Safonov return xfrm_attr_cpy32(dst, pos, nla, size, nla_len(nla), nla_len(nla)); 4585106f4a8SDmitry Safonov } 4595106f4a8SDmitry Safonov 4605106f4a8SDmitry Safonov static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src, 4615106f4a8SDmitry Safonov struct nlattr *attrs[XFRMA_MAX+1], 462*4e950506SDmitry Safonov size_t size, u8 type, int maxtype, 463*4e950506SDmitry Safonov struct netlink_ext_ack *extack) 4645106f4a8SDmitry Safonov { 4655106f4a8SDmitry Safonov size_t pos; 4665106f4a8SDmitry Safonov int i; 4675106f4a8SDmitry Safonov 4685106f4a8SDmitry Safonov memcpy(dst, src, NLMSG_HDRLEN); 4695106f4a8SDmitry Safonov dst->nlmsg_len = NLMSG_HDRLEN + xfrm_msg_min[type]; 4705106f4a8SDmitry Safonov memset(nlmsg_data(dst), 0, xfrm_msg_min[type]); 4715106f4a8SDmitry Safonov 4725106f4a8SDmitry Safonov switch (src->nlmsg_type) { 4735106f4a8SDmitry Safonov /* Compat message has the same layout as native */ 4745106f4a8SDmitry Safonov case XFRM_MSG_DELSA: 4755106f4a8SDmitry Safonov case XFRM_MSG_GETSA: 4765106f4a8SDmitry Safonov case XFRM_MSG_DELPOLICY: 4775106f4a8SDmitry Safonov case XFRM_MSG_GETPOLICY: 4785106f4a8SDmitry Safonov case XFRM_MSG_FLUSHSA: 4795106f4a8SDmitry Safonov case XFRM_MSG_FLUSHPOLICY: 4805106f4a8SDmitry Safonov case XFRM_MSG_NEWAE: 4815106f4a8SDmitry Safonov case XFRM_MSG_GETAE: 4825106f4a8SDmitry Safonov case XFRM_MSG_REPORT: 4835106f4a8SDmitry Safonov case XFRM_MSG_MIGRATE: 4845106f4a8SDmitry Safonov case XFRM_MSG_NEWSADINFO: 4855106f4a8SDmitry Safonov case XFRM_MSG_GETSADINFO: 4865106f4a8SDmitry Safonov case XFRM_MSG_NEWSPDINFO: 4875106f4a8SDmitry Safonov case XFRM_MSG_GETSPDINFO: 4885106f4a8SDmitry Safonov case XFRM_MSG_MAPPING: 4895106f4a8SDmitry Safonov memcpy(nlmsg_data(dst), nlmsg_data(src), compat_msg_min[type]); 4905106f4a8SDmitry Safonov break; 4915106f4a8SDmitry Safonov /* 4 byte alignment for trailing u64 on native, but not on compat */ 4925106f4a8SDmitry Safonov case XFRM_MSG_NEWSA: 4935106f4a8SDmitry Safonov case XFRM_MSG_NEWPOLICY: 4945106f4a8SDmitry Safonov case XFRM_MSG_UPDSA: 4955106f4a8SDmitry Safonov case XFRM_MSG_UPDPOLICY: 4965106f4a8SDmitry Safonov memcpy(nlmsg_data(dst), nlmsg_data(src), compat_msg_min[type]); 4975106f4a8SDmitry Safonov break; 4985106f4a8SDmitry Safonov case XFRM_MSG_EXPIRE: { 4995106f4a8SDmitry Safonov const struct compat_xfrm_user_expire *src_ue = nlmsg_data(src); 5005106f4a8SDmitry Safonov struct xfrm_user_expire *dst_ue = nlmsg_data(dst); 5015106f4a8SDmitry Safonov 5025106f4a8SDmitry Safonov /* compat_xfrm_user_expire has 4-byte smaller state */ 5035106f4a8SDmitry Safonov memcpy(dst_ue, src_ue, sizeof(src_ue->state)); 5045106f4a8SDmitry Safonov dst_ue->hard = src_ue->hard; 5055106f4a8SDmitry Safonov break; 5065106f4a8SDmitry Safonov } 5075106f4a8SDmitry Safonov case XFRM_MSG_ACQUIRE: { 5085106f4a8SDmitry Safonov const struct compat_xfrm_user_acquire *src_ua = nlmsg_data(src); 5095106f4a8SDmitry Safonov struct xfrm_user_acquire *dst_ua = nlmsg_data(dst); 5105106f4a8SDmitry Safonov 5115106f4a8SDmitry Safonov memcpy(dst_ua, src_ua, offsetof(struct compat_xfrm_user_acquire, aalgos)); 5125106f4a8SDmitry Safonov dst_ua->aalgos = src_ua->aalgos; 5135106f4a8SDmitry Safonov dst_ua->ealgos = src_ua->ealgos; 5145106f4a8SDmitry Safonov dst_ua->calgos = src_ua->calgos; 5155106f4a8SDmitry Safonov dst_ua->seq = src_ua->seq; 5165106f4a8SDmitry Safonov break; 5175106f4a8SDmitry Safonov } 5185106f4a8SDmitry Safonov case XFRM_MSG_POLEXPIRE: { 5195106f4a8SDmitry Safonov const struct compat_xfrm_user_polexpire *src_upe = nlmsg_data(src); 5205106f4a8SDmitry Safonov struct xfrm_user_polexpire *dst_upe = nlmsg_data(dst); 5215106f4a8SDmitry Safonov 5225106f4a8SDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 5235106f4a8SDmitry Safonov memcpy(dst_upe, src_upe, sizeof(src_upe->pol)); 5245106f4a8SDmitry Safonov dst_upe->hard = src_upe->hard; 5255106f4a8SDmitry Safonov break; 5265106f4a8SDmitry Safonov } 5275106f4a8SDmitry Safonov case XFRM_MSG_ALLOCSPI: { 5285106f4a8SDmitry Safonov const struct compat_xfrm_userspi_info *src_usi = nlmsg_data(src); 5295106f4a8SDmitry Safonov struct xfrm_userspi_info *dst_usi = nlmsg_data(dst); 5305106f4a8SDmitry Safonov 5315106f4a8SDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 5325106f4a8SDmitry Safonov memcpy(dst_usi, src_usi, sizeof(src_usi->info)); 5335106f4a8SDmitry Safonov dst_usi->min = src_usi->min; 5345106f4a8SDmitry Safonov dst_usi->max = src_usi->max; 5355106f4a8SDmitry Safonov break; 5365106f4a8SDmitry Safonov } 5375106f4a8SDmitry Safonov default: 5385106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Unsupported message type"); 5395106f4a8SDmitry Safonov return -EOPNOTSUPP; 5405106f4a8SDmitry Safonov } 5415106f4a8SDmitry Safonov pos = dst->nlmsg_len; 5425106f4a8SDmitry Safonov 543*4e950506SDmitry Safonov if (maxtype) { 544*4e950506SDmitry Safonov /* attirbutes are xfrm_spdattr_type_t, not xfrm_attr_type_t */ 545*4e950506SDmitry Safonov WARN_ON_ONCE(src->nlmsg_type != XFRM_MSG_NEWSPDINFO); 546*4e950506SDmitry Safonov 547*4e950506SDmitry Safonov for (i = 1; i <= maxtype; i++) { 548*4e950506SDmitry Safonov int err; 549*4e950506SDmitry Safonov 550*4e950506SDmitry Safonov if (!attrs[i]) 551*4e950506SDmitry Safonov continue; 552*4e950506SDmitry Safonov 553*4e950506SDmitry Safonov /* just copy - no need for translation */ 554*4e950506SDmitry Safonov err = xfrm_attr_cpy32(dst, &pos, attrs[i], size, 555*4e950506SDmitry Safonov nla_len(attrs[i]), nla_len(attrs[i])); 556*4e950506SDmitry Safonov if (err) 557*4e950506SDmitry Safonov return err; 558*4e950506SDmitry Safonov } 559*4e950506SDmitry Safonov return 0; 560*4e950506SDmitry Safonov } 561*4e950506SDmitry Safonov 5625106f4a8SDmitry Safonov for (i = 1; i < XFRMA_MAX + 1; i++) { 5635106f4a8SDmitry Safonov int err; 5645106f4a8SDmitry Safonov 5655106f4a8SDmitry Safonov if (i == XFRMA_PAD) 5665106f4a8SDmitry Safonov continue; 5675106f4a8SDmitry Safonov 5685106f4a8SDmitry Safonov if (!attrs[i]) 5695106f4a8SDmitry Safonov continue; 5705106f4a8SDmitry Safonov 5715106f4a8SDmitry Safonov err = xfrm_xlate32_attr(dst, attrs[i], &pos, size, extack); 5725106f4a8SDmitry Safonov if (err) 5735106f4a8SDmitry Safonov return err; 5745106f4a8SDmitry Safonov } 5755106f4a8SDmitry Safonov 5765106f4a8SDmitry Safonov return 0; 5775106f4a8SDmitry Safonov } 5785106f4a8SDmitry Safonov 5795106f4a8SDmitry Safonov static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32, 5805106f4a8SDmitry Safonov int maxtype, const struct nla_policy *policy, 5815106f4a8SDmitry Safonov struct netlink_ext_ack *extack) 5825106f4a8SDmitry Safonov { 5835106f4a8SDmitry Safonov /* netlink_rcv_skb() checks if a message has full (struct nlmsghdr) */ 5845106f4a8SDmitry Safonov u16 type = h32->nlmsg_type - XFRM_MSG_BASE; 5855106f4a8SDmitry Safonov struct nlattr *attrs[XFRMA_MAX+1]; 5865106f4a8SDmitry Safonov struct nlmsghdr *h64; 5875106f4a8SDmitry Safonov size_t len; 5885106f4a8SDmitry Safonov int err; 5895106f4a8SDmitry Safonov 5905106f4a8SDmitry Safonov BUILD_BUG_ON(ARRAY_SIZE(xfrm_msg_min) != ARRAY_SIZE(compat_msg_min)); 5915106f4a8SDmitry Safonov 5925106f4a8SDmitry Safonov if (type >= ARRAY_SIZE(xfrm_msg_min)) 5935106f4a8SDmitry Safonov return ERR_PTR(-EINVAL); 5945106f4a8SDmitry Safonov 5955106f4a8SDmitry Safonov /* Don't call parse: the message might have only nlmsg header */ 5965106f4a8SDmitry Safonov if ((h32->nlmsg_type == XFRM_MSG_GETSA || 5975106f4a8SDmitry Safonov h32->nlmsg_type == XFRM_MSG_GETPOLICY) && 5985106f4a8SDmitry Safonov (h32->nlmsg_flags & NLM_F_DUMP)) 5995106f4a8SDmitry Safonov return NULL; 6005106f4a8SDmitry Safonov 6015106f4a8SDmitry Safonov err = nlmsg_parse_deprecated(h32, compat_msg_min[type], attrs, 6025106f4a8SDmitry Safonov maxtype ? : XFRMA_MAX, policy ? : compat_policy, extack); 6035106f4a8SDmitry Safonov if (err < 0) 6045106f4a8SDmitry Safonov return ERR_PTR(err); 6055106f4a8SDmitry Safonov 606*4e950506SDmitry Safonov len = xfrm_user_rcv_calculate_len64(h32, attrs, maxtype); 6075106f4a8SDmitry Safonov /* The message doesn't need translation */ 6085106f4a8SDmitry Safonov if (len == nlmsg_len(h32)) 6095106f4a8SDmitry Safonov return NULL; 6105106f4a8SDmitry Safonov 6115106f4a8SDmitry Safonov len += NLMSG_HDRLEN; 612ad37f77fSDmitry Safonov h64 = kvmalloc(len, GFP_KERNEL); 6135106f4a8SDmitry Safonov if (!h64) 6145106f4a8SDmitry Safonov return ERR_PTR(-ENOMEM); 6155106f4a8SDmitry Safonov 616*4e950506SDmitry Safonov err = xfrm_xlate32(h64, h32, attrs, len, type, maxtype, extack); 6175106f4a8SDmitry Safonov if (err < 0) { 6185106f4a8SDmitry Safonov kvfree(h64); 6195106f4a8SDmitry Safonov return ERR_PTR(err); 6205106f4a8SDmitry Safonov } 6215106f4a8SDmitry Safonov 6225106f4a8SDmitry Safonov return h64; 6235106f4a8SDmitry Safonov } 6245106f4a8SDmitry Safonov 62596392ee5SDmitry Safonov static int xfrm_user_policy_compat(u8 **pdata32, int optlen) 62696392ee5SDmitry Safonov { 62796392ee5SDmitry Safonov struct compat_xfrm_userpolicy_info *p = (void *)*pdata32; 62896392ee5SDmitry Safonov u8 *src_templates, *dst_templates; 62996392ee5SDmitry Safonov u8 *data64; 63096392ee5SDmitry Safonov 63196392ee5SDmitry Safonov if (optlen < sizeof(*p)) 63296392ee5SDmitry Safonov return -EINVAL; 63396392ee5SDmitry Safonov 63496392ee5SDmitry Safonov data64 = kmalloc_track_caller(optlen + 4, GFP_USER | __GFP_NOWARN); 63596392ee5SDmitry Safonov if (!data64) 63696392ee5SDmitry Safonov return -ENOMEM; 63796392ee5SDmitry Safonov 63896392ee5SDmitry Safonov memcpy(data64, *pdata32, sizeof(*p)); 63996392ee5SDmitry Safonov memset(data64 + sizeof(*p), 0, 4); 64096392ee5SDmitry Safonov 64196392ee5SDmitry Safonov src_templates = *pdata32 + sizeof(*p); 64296392ee5SDmitry Safonov dst_templates = data64 + sizeof(*p) + 4; 64396392ee5SDmitry Safonov memcpy(dst_templates, src_templates, optlen - sizeof(*p)); 64496392ee5SDmitry Safonov 64596392ee5SDmitry Safonov kfree(*pdata32); 64696392ee5SDmitry Safonov *pdata32 = data64; 64796392ee5SDmitry Safonov return 0; 64896392ee5SDmitry Safonov } 64996392ee5SDmitry Safonov 650c9e7c76dSDmitry Safonov static struct xfrm_translator xfrm_translator = { 651c9e7c76dSDmitry Safonov .owner = THIS_MODULE, 6525461fc0cSDmitry Safonov .alloc_compat = xfrm_alloc_compat, 6535106f4a8SDmitry Safonov .rcv_msg_compat = xfrm_user_rcv_msg_compat, 65496392ee5SDmitry Safonov .xlate_user_policy_sockptr = xfrm_user_policy_compat, 655c9e7c76dSDmitry Safonov }; 656c9e7c76dSDmitry Safonov 657c9e7c76dSDmitry Safonov static int __init xfrm_compat_init(void) 658c9e7c76dSDmitry Safonov { 659c9e7c76dSDmitry Safonov return xfrm_register_translator(&xfrm_translator); 660c9e7c76dSDmitry Safonov } 661c9e7c76dSDmitry Safonov 662c9e7c76dSDmitry Safonov static void __exit xfrm_compat_exit(void) 663c9e7c76dSDmitry Safonov { 664c9e7c76dSDmitry Safonov xfrm_unregister_translator(&xfrm_translator); 665c9e7c76dSDmitry Safonov } 666c9e7c76dSDmitry Safonov 667c9e7c76dSDmitry Safonov module_init(xfrm_compat_init); 668c9e7c76dSDmitry Safonov module_exit(xfrm_compat_exit); 669c9e7c76dSDmitry Safonov MODULE_LICENSE("GPL"); 670c9e7c76dSDmitry Safonov MODULE_AUTHOR("Dmitry Safonov"); 671c9e7c76dSDmitry Safonov MODULE_DESCRIPTION("XFRM 32-bit compatibility layer"); 672