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> 8b6ee8963SEric Dumazet #include <linux/nospec.h> 9c9e7c76dSDmitry Safonov #include <linux/xfrm.h> 10c9e7c76dSDmitry Safonov #include <net/xfrm.h> 11c9e7c76dSDmitry Safonov 125461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cfg { 135461fc0cSDmitry Safonov compat_u64 soft_byte_limit, hard_byte_limit; 145461fc0cSDmitry Safonov compat_u64 soft_packet_limit, hard_packet_limit; 155461fc0cSDmitry Safonov compat_u64 soft_add_expires_seconds, hard_add_expires_seconds; 165461fc0cSDmitry Safonov compat_u64 soft_use_expires_seconds, hard_use_expires_seconds; 175461fc0cSDmitry Safonov }; /* same size on 32bit, but only 4 byte alignment required */ 185461fc0cSDmitry Safonov 195461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cur { 205461fc0cSDmitry Safonov compat_u64 bytes, packets, add_time, use_time; 215461fc0cSDmitry Safonov }; /* same size on 32bit, but only 4 byte alignment required */ 225461fc0cSDmitry Safonov 235461fc0cSDmitry Safonov struct compat_xfrm_userpolicy_info { 245461fc0cSDmitry Safonov struct xfrm_selector sel; 255461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cfg lft; 265461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cur curlft; 275461fc0cSDmitry Safonov __u32 priority, index; 285461fc0cSDmitry Safonov u8 dir, action, flags, share; 295461fc0cSDmitry Safonov /* 4 bytes additional padding on 64bit */ 305461fc0cSDmitry Safonov }; 315461fc0cSDmitry Safonov 325461fc0cSDmitry Safonov struct compat_xfrm_usersa_info { 335461fc0cSDmitry Safonov struct xfrm_selector sel; 345461fc0cSDmitry Safonov struct xfrm_id id; 355461fc0cSDmitry Safonov xfrm_address_t saddr; 365461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cfg lft; 375461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cur curlft; 385461fc0cSDmitry Safonov struct xfrm_stats stats; 395461fc0cSDmitry Safonov __u32 seq, reqid; 405461fc0cSDmitry Safonov u16 family; 415461fc0cSDmitry Safonov u8 mode, replay_window, flags; 425461fc0cSDmitry Safonov /* 4 bytes additional padding on 64bit */ 435461fc0cSDmitry Safonov }; 445461fc0cSDmitry Safonov 455461fc0cSDmitry Safonov struct compat_xfrm_user_acquire { 465461fc0cSDmitry Safonov struct xfrm_id id; 475461fc0cSDmitry Safonov xfrm_address_t saddr; 485461fc0cSDmitry Safonov struct xfrm_selector sel; 495461fc0cSDmitry Safonov struct compat_xfrm_userpolicy_info policy; 505461fc0cSDmitry Safonov /* 4 bytes additional padding on 64bit */ 515461fc0cSDmitry Safonov __u32 aalgos, ealgos, calgos, seq; 525461fc0cSDmitry Safonov }; 535461fc0cSDmitry Safonov 545461fc0cSDmitry Safonov struct compat_xfrm_userspi_info { 555461fc0cSDmitry Safonov struct compat_xfrm_usersa_info info; 565461fc0cSDmitry Safonov /* 4 bytes additional padding on 64bit */ 575461fc0cSDmitry Safonov __u32 min, max; 585461fc0cSDmitry Safonov }; 595461fc0cSDmitry Safonov 605461fc0cSDmitry Safonov struct compat_xfrm_user_expire { 615461fc0cSDmitry Safonov struct compat_xfrm_usersa_info state; 625461fc0cSDmitry Safonov /* 8 bytes additional padding on 64bit */ 635461fc0cSDmitry Safonov u8 hard; 645461fc0cSDmitry Safonov }; 655461fc0cSDmitry Safonov 665461fc0cSDmitry Safonov struct compat_xfrm_user_polexpire { 675461fc0cSDmitry Safonov struct compat_xfrm_userpolicy_info pol; 685461fc0cSDmitry Safonov /* 8 bytes additional padding on 64bit */ 695461fc0cSDmitry Safonov u8 hard; 705461fc0cSDmitry Safonov }; 715461fc0cSDmitry Safonov 725461fc0cSDmitry Safonov #define XMSGSIZE(type) sizeof(struct type) 735461fc0cSDmitry Safonov 745461fc0cSDmitry Safonov static const int compat_msg_min[XFRM_NR_MSGTYPES] = { 755461fc0cSDmitry Safonov [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_usersa_info), 765461fc0cSDmitry Safonov [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 775461fc0cSDmitry Safonov [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 785461fc0cSDmitry Safonov [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userpolicy_info), 795461fc0cSDmitry Safonov [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 805461fc0cSDmitry Safonov [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 815461fc0cSDmitry Safonov [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userspi_info), 825461fc0cSDmitry Safonov [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_acquire), 835461fc0cSDmitry Safonov [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_expire), 845461fc0cSDmitry Safonov [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userpolicy_info), 855461fc0cSDmitry Safonov [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_usersa_info), 865461fc0cSDmitry Safonov [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_polexpire), 875461fc0cSDmitry Safonov [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush), 885461fc0cSDmitry Safonov [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0, 895461fc0cSDmitry Safonov [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 905461fc0cSDmitry Safonov [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 915461fc0cSDmitry Safonov [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 925461fc0cSDmitry Safonov [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 935461fc0cSDmitry Safonov [XFRM_MSG_NEWSADINFO - XFRM_MSG_BASE] = sizeof(u32), 945461fc0cSDmitry Safonov [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), 955461fc0cSDmitry Safonov [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 965461fc0cSDmitry Safonov [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 975461fc0cSDmitry Safonov [XFRM_MSG_MAPPING - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping) 985461fc0cSDmitry Safonov }; 995461fc0cSDmitry Safonov 1005106f4a8SDmitry Safonov static const struct nla_policy compat_policy[XFRMA_MAX+1] = { 1015106f4a8SDmitry Safonov [XFRMA_SA] = { .len = XMSGSIZE(compat_xfrm_usersa_info)}, 1025106f4a8SDmitry Safonov [XFRMA_POLICY] = { .len = XMSGSIZE(compat_xfrm_userpolicy_info)}, 1035106f4a8SDmitry Safonov [XFRMA_LASTUSED] = { .type = NLA_U64}, 1045106f4a8SDmitry Safonov [XFRMA_ALG_AUTH_TRUNC] = { .len = sizeof(struct xfrm_algo_auth)}, 1055106f4a8SDmitry Safonov [XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) }, 1065106f4a8SDmitry Safonov [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) }, 1075106f4a8SDmitry Safonov [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) }, 1085106f4a8SDmitry Safonov [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) }, 1095106f4a8SDmitry Safonov [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) }, 1105106f4a8SDmitry Safonov [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) }, 111*d1e0e61dSLin Ma [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_user_sec_ctx) }, 1125106f4a8SDmitry Safonov [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) }, 1135106f4a8SDmitry Safonov [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) }, 1145106f4a8SDmitry Safonov [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, 1155106f4a8SDmitry Safonov [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, 1165106f4a8SDmitry Safonov [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) }, 1175106f4a8SDmitry Safonov [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, 1185106f4a8SDmitry Safonov [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, 1195106f4a8SDmitry Safonov [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, 1205106f4a8SDmitry Safonov [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, 1215106f4a8SDmitry Safonov [XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) }, 1225106f4a8SDmitry Safonov [XFRMA_TFCPAD] = { .type = NLA_U32 }, 1235106f4a8SDmitry Safonov [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, 1245106f4a8SDmitry Safonov [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, 1255106f4a8SDmitry Safonov [XFRMA_PROTO] = { .type = NLA_U8 }, 1265106f4a8SDmitry Safonov [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, 1275106f4a8SDmitry Safonov [XFRMA_OFFLOAD_DEV] = { .len = sizeof(struct xfrm_user_offload) }, 1285106f4a8SDmitry Safonov [XFRMA_SET_MARK] = { .type = NLA_U32 }, 1295106f4a8SDmitry Safonov [XFRMA_SET_MARK_MASK] = { .type = NLA_U32 }, 1305106f4a8SDmitry Safonov [XFRMA_IF_ID] = { .type = NLA_U32 }, 1314e484b3eSAntony Antony [XFRMA_MTIMER_THRESH] = { .type = NLA_U32 }, 1325106f4a8SDmitry Safonov }; 1335106f4a8SDmitry Safonov 1345461fc0cSDmitry Safonov static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb, 1355461fc0cSDmitry Safonov const struct nlmsghdr *nlh_src, u16 type) 1365461fc0cSDmitry Safonov { 1375461fc0cSDmitry Safonov int payload = compat_msg_min[type]; 1385461fc0cSDmitry Safonov int src_len = xfrm_msg_min[type]; 1395461fc0cSDmitry Safonov struct nlmsghdr *nlh_dst; 1405461fc0cSDmitry Safonov 1415461fc0cSDmitry Safonov /* Compat messages are shorter or equal to native (+padding) */ 1425461fc0cSDmitry Safonov if (WARN_ON_ONCE(src_len < payload)) 1435461fc0cSDmitry Safonov return ERR_PTR(-EMSGSIZE); 1445461fc0cSDmitry Safonov 1455461fc0cSDmitry Safonov nlh_dst = nlmsg_put(skb, nlh_src->nlmsg_pid, nlh_src->nlmsg_seq, 1465461fc0cSDmitry Safonov nlh_src->nlmsg_type, payload, nlh_src->nlmsg_flags); 1475461fc0cSDmitry Safonov if (!nlh_dst) 1485461fc0cSDmitry Safonov return ERR_PTR(-EMSGSIZE); 1495461fc0cSDmitry Safonov 1505461fc0cSDmitry Safonov memset(nlmsg_data(nlh_dst), 0, payload); 1515461fc0cSDmitry Safonov 1525461fc0cSDmitry Safonov switch (nlh_src->nlmsg_type) { 1535461fc0cSDmitry Safonov /* Compat message has the same layout as native */ 1545461fc0cSDmitry Safonov case XFRM_MSG_DELSA: 1555461fc0cSDmitry Safonov case XFRM_MSG_DELPOLICY: 1565461fc0cSDmitry Safonov case XFRM_MSG_FLUSHSA: 1575461fc0cSDmitry Safonov case XFRM_MSG_FLUSHPOLICY: 1585461fc0cSDmitry Safonov case XFRM_MSG_NEWAE: 1595461fc0cSDmitry Safonov case XFRM_MSG_REPORT: 1605461fc0cSDmitry Safonov case XFRM_MSG_MIGRATE: 1615461fc0cSDmitry Safonov case XFRM_MSG_NEWSADINFO: 1625461fc0cSDmitry Safonov case XFRM_MSG_NEWSPDINFO: 1635461fc0cSDmitry Safonov case XFRM_MSG_MAPPING: 1645461fc0cSDmitry Safonov WARN_ON_ONCE(src_len != payload); 1655461fc0cSDmitry Safonov memcpy(nlmsg_data(nlh_dst), nlmsg_data(nlh_src), src_len); 1665461fc0cSDmitry Safonov break; 1675461fc0cSDmitry Safonov /* 4 byte alignment for trailing u64 on native, but not on compat */ 1685461fc0cSDmitry Safonov case XFRM_MSG_NEWSA: 1695461fc0cSDmitry Safonov case XFRM_MSG_NEWPOLICY: 1705461fc0cSDmitry Safonov case XFRM_MSG_UPDSA: 1715461fc0cSDmitry Safonov case XFRM_MSG_UPDPOLICY: 1725461fc0cSDmitry Safonov WARN_ON_ONCE(src_len != payload + 4); 1735461fc0cSDmitry Safonov memcpy(nlmsg_data(nlh_dst), nlmsg_data(nlh_src), payload); 1745461fc0cSDmitry Safonov break; 1755461fc0cSDmitry Safonov case XFRM_MSG_EXPIRE: { 1765461fc0cSDmitry Safonov const struct xfrm_user_expire *src_ue = nlmsg_data(nlh_src); 1775461fc0cSDmitry Safonov struct compat_xfrm_user_expire *dst_ue = nlmsg_data(nlh_dst); 1785461fc0cSDmitry Safonov 1795461fc0cSDmitry Safonov /* compat_xfrm_user_expire has 4-byte smaller state */ 1805461fc0cSDmitry Safonov memcpy(dst_ue, src_ue, sizeof(dst_ue->state)); 1815461fc0cSDmitry Safonov dst_ue->hard = src_ue->hard; 1825461fc0cSDmitry Safonov break; 1835461fc0cSDmitry Safonov } 1845461fc0cSDmitry Safonov case XFRM_MSG_ACQUIRE: { 1855461fc0cSDmitry Safonov const struct xfrm_user_acquire *src_ua = nlmsg_data(nlh_src); 1865461fc0cSDmitry Safonov struct compat_xfrm_user_acquire *dst_ua = nlmsg_data(nlh_dst); 1875461fc0cSDmitry Safonov 1885461fc0cSDmitry Safonov memcpy(dst_ua, src_ua, offsetof(struct compat_xfrm_user_acquire, aalgos)); 1895461fc0cSDmitry Safonov dst_ua->aalgos = src_ua->aalgos; 1905461fc0cSDmitry Safonov dst_ua->ealgos = src_ua->ealgos; 1915461fc0cSDmitry Safonov dst_ua->calgos = src_ua->calgos; 1925461fc0cSDmitry Safonov dst_ua->seq = src_ua->seq; 1935461fc0cSDmitry Safonov break; 1945461fc0cSDmitry Safonov } 1955461fc0cSDmitry Safonov case XFRM_MSG_POLEXPIRE: { 1965461fc0cSDmitry Safonov const struct xfrm_user_polexpire *src_upe = nlmsg_data(nlh_src); 1975461fc0cSDmitry Safonov struct compat_xfrm_user_polexpire *dst_upe = nlmsg_data(nlh_dst); 1985461fc0cSDmitry Safonov 1995461fc0cSDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 2005461fc0cSDmitry Safonov memcpy(dst_upe, src_upe, sizeof(dst_upe->pol)); 2015461fc0cSDmitry Safonov dst_upe->hard = src_upe->hard; 2025461fc0cSDmitry Safonov break; 2035461fc0cSDmitry Safonov } 2045461fc0cSDmitry Safonov case XFRM_MSG_ALLOCSPI: { 2055461fc0cSDmitry Safonov const struct xfrm_userspi_info *src_usi = nlmsg_data(nlh_src); 2065461fc0cSDmitry Safonov struct compat_xfrm_userspi_info *dst_usi = nlmsg_data(nlh_dst); 2075461fc0cSDmitry Safonov 2085461fc0cSDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 2095461fc0cSDmitry Safonov memcpy(dst_usi, src_usi, sizeof(src_usi->info)); 2105461fc0cSDmitry Safonov dst_usi->min = src_usi->min; 2115461fc0cSDmitry Safonov dst_usi->max = src_usi->max; 2125461fc0cSDmitry Safonov break; 2135461fc0cSDmitry Safonov } 2145461fc0cSDmitry Safonov /* Not being sent by kernel */ 2155461fc0cSDmitry Safonov case XFRM_MSG_GETSA: 2165461fc0cSDmitry Safonov case XFRM_MSG_GETPOLICY: 2175461fc0cSDmitry Safonov case XFRM_MSG_GETAE: 2185461fc0cSDmitry Safonov case XFRM_MSG_GETSADINFO: 2195461fc0cSDmitry Safonov case XFRM_MSG_GETSPDINFO: 2205461fc0cSDmitry Safonov default: 221ef19e111SDmitry Safonov pr_warn_once("unsupported nlmsg_type %d\n", nlh_src->nlmsg_type); 2225461fc0cSDmitry Safonov return ERR_PTR(-EOPNOTSUPP); 2235461fc0cSDmitry Safonov } 2245461fc0cSDmitry Safonov 2255461fc0cSDmitry Safonov return nlh_dst; 2265461fc0cSDmitry Safonov } 2275461fc0cSDmitry Safonov 2285461fc0cSDmitry Safonov static int xfrm_nla_cpy(struct sk_buff *dst, const struct nlattr *src, int len) 2295461fc0cSDmitry Safonov { 2305461fc0cSDmitry Safonov return nla_put(dst, src->nla_type, len, nla_data(src)); 2315461fc0cSDmitry Safonov } 2325461fc0cSDmitry Safonov 2335461fc0cSDmitry Safonov static int xfrm_xlate64_attr(struct sk_buff *dst, const struct nlattr *src) 2345461fc0cSDmitry Safonov { 2355461fc0cSDmitry Safonov switch (src->nla_type) { 2365461fc0cSDmitry Safonov case XFRMA_PAD: 2375461fc0cSDmitry Safonov /* Ignore */ 2385461fc0cSDmitry Safonov return 0; 239dbd7ae51SDmitry Safonov case XFRMA_UNSPEC: 2405461fc0cSDmitry Safonov case XFRMA_ALG_AUTH: 2415461fc0cSDmitry Safonov case XFRMA_ALG_CRYPT: 2425461fc0cSDmitry Safonov case XFRMA_ALG_COMP: 2435461fc0cSDmitry Safonov case XFRMA_ENCAP: 2445461fc0cSDmitry Safonov case XFRMA_TMPL: 2455461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2465461fc0cSDmitry Safonov case XFRMA_SA: 2475461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, XMSGSIZE(compat_xfrm_usersa_info)); 2485461fc0cSDmitry Safonov case XFRMA_POLICY: 2495461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, XMSGSIZE(compat_xfrm_userpolicy_info)); 2505461fc0cSDmitry Safonov case XFRMA_SEC_CTX: 2515461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2525461fc0cSDmitry Safonov case XFRMA_LTIME_VAL: 2535461fc0cSDmitry Safonov return nla_put_64bit(dst, src->nla_type, nla_len(src), 2545461fc0cSDmitry Safonov nla_data(src), XFRMA_PAD); 2555461fc0cSDmitry Safonov case XFRMA_REPLAY_VAL: 2565461fc0cSDmitry Safonov case XFRMA_REPLAY_THRESH: 2575461fc0cSDmitry Safonov case XFRMA_ETIMER_THRESH: 2585461fc0cSDmitry Safonov case XFRMA_SRCADDR: 2595461fc0cSDmitry Safonov case XFRMA_COADDR: 2605461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2615461fc0cSDmitry Safonov case XFRMA_LASTUSED: 2625461fc0cSDmitry Safonov return nla_put_64bit(dst, src->nla_type, nla_len(src), 2635461fc0cSDmitry Safonov nla_data(src), XFRMA_PAD); 2645461fc0cSDmitry Safonov case XFRMA_POLICY_TYPE: 2655461fc0cSDmitry Safonov case XFRMA_MIGRATE: 2665461fc0cSDmitry Safonov case XFRMA_ALG_AEAD: 2675461fc0cSDmitry Safonov case XFRMA_KMADDRESS: 2685461fc0cSDmitry Safonov case XFRMA_ALG_AUTH_TRUNC: 2695461fc0cSDmitry Safonov case XFRMA_MARK: 2705461fc0cSDmitry Safonov case XFRMA_TFCPAD: 2715461fc0cSDmitry Safonov case XFRMA_REPLAY_ESN_VAL: 2725461fc0cSDmitry Safonov case XFRMA_SA_EXTRA_FLAGS: 2735461fc0cSDmitry Safonov case XFRMA_PROTO: 2745461fc0cSDmitry Safonov case XFRMA_ADDRESS_FILTER: 2755461fc0cSDmitry Safonov case XFRMA_OFFLOAD_DEV: 2765461fc0cSDmitry Safonov case XFRMA_SET_MARK: 2775461fc0cSDmitry Safonov case XFRMA_SET_MARK_MASK: 2785461fc0cSDmitry Safonov case XFRMA_IF_ID: 2794e484b3eSAntony Antony case XFRMA_MTIMER_THRESH: 2805461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2815461fc0cSDmitry Safonov default: 2824e484b3eSAntony Antony BUILD_BUG_ON(XFRMA_MAX != XFRMA_MTIMER_THRESH); 283ef19e111SDmitry Safonov pr_warn_once("unsupported nla_type %d\n", src->nla_type); 2845461fc0cSDmitry Safonov return -EOPNOTSUPP; 2855461fc0cSDmitry Safonov } 2865461fc0cSDmitry Safonov } 2875461fc0cSDmitry Safonov 2885461fc0cSDmitry Safonov /* Take kernel-built (64bit layout) and create 32bit layout for userspace */ 2895461fc0cSDmitry Safonov static int xfrm_xlate64(struct sk_buff *dst, const struct nlmsghdr *nlh_src) 2905461fc0cSDmitry Safonov { 2915461fc0cSDmitry Safonov u16 type = nlh_src->nlmsg_type - XFRM_MSG_BASE; 2925461fc0cSDmitry Safonov const struct nlattr *nla, *attrs; 2935461fc0cSDmitry Safonov struct nlmsghdr *nlh_dst; 2945461fc0cSDmitry Safonov int len, remaining; 2955461fc0cSDmitry Safonov 2965461fc0cSDmitry Safonov nlh_dst = xfrm_nlmsg_put_compat(dst, nlh_src, type); 2975461fc0cSDmitry Safonov if (IS_ERR(nlh_dst)) 2985461fc0cSDmitry Safonov return PTR_ERR(nlh_dst); 2995461fc0cSDmitry Safonov 3005461fc0cSDmitry Safonov attrs = nlmsg_attrdata(nlh_src, xfrm_msg_min[type]); 3015461fc0cSDmitry Safonov len = nlmsg_attrlen(nlh_src, xfrm_msg_min[type]); 3025461fc0cSDmitry Safonov 3035461fc0cSDmitry Safonov nla_for_each_attr(nla, attrs, len, remaining) { 3044e950506SDmitry Safonov int err; 3055461fc0cSDmitry Safonov 306eb6c59b7SAnastasia Belova switch (nlh_src->nlmsg_type) { 3074e950506SDmitry Safonov case XFRM_MSG_NEWSPDINFO: 3084e950506SDmitry Safonov err = xfrm_nla_cpy(dst, nla, nla_len(nla)); 3094e950506SDmitry Safonov break; 3104e950506SDmitry Safonov default: 3114e950506SDmitry Safonov err = xfrm_xlate64_attr(dst, nla); 3124e950506SDmitry Safonov break; 3134e950506SDmitry Safonov } 3145461fc0cSDmitry Safonov if (err) 3155461fc0cSDmitry Safonov return err; 3165461fc0cSDmitry Safonov } 3175461fc0cSDmitry Safonov 3185461fc0cSDmitry Safonov nlmsg_end(dst, nlh_dst); 3195461fc0cSDmitry Safonov 3205461fc0cSDmitry Safonov return 0; 3215461fc0cSDmitry Safonov } 3225461fc0cSDmitry Safonov 3235461fc0cSDmitry Safonov static int xfrm_alloc_compat(struct sk_buff *skb, const struct nlmsghdr *nlh_src) 3245461fc0cSDmitry Safonov { 3255461fc0cSDmitry Safonov u16 type = nlh_src->nlmsg_type - XFRM_MSG_BASE; 3265461fc0cSDmitry Safonov struct sk_buff *new = NULL; 3275461fc0cSDmitry Safonov int err; 3285461fc0cSDmitry Safonov 329ef19e111SDmitry Safonov if (type >= ARRAY_SIZE(xfrm_msg_min)) { 330ef19e111SDmitry Safonov pr_warn_once("unsupported nlmsg_type %d\n", nlh_src->nlmsg_type); 3315461fc0cSDmitry Safonov return -EOPNOTSUPP; 332ef19e111SDmitry Safonov } 3335461fc0cSDmitry Safonov 3345461fc0cSDmitry Safonov if (skb_shinfo(skb)->frag_list == NULL) { 3355461fc0cSDmitry Safonov new = alloc_skb(skb->len + skb_tailroom(skb), GFP_ATOMIC); 3365461fc0cSDmitry Safonov if (!new) 3375461fc0cSDmitry Safonov return -ENOMEM; 3385461fc0cSDmitry Safonov skb_shinfo(skb)->frag_list = new; 3395461fc0cSDmitry Safonov } 3405461fc0cSDmitry Safonov 3415461fc0cSDmitry Safonov err = xfrm_xlate64(skb_shinfo(skb)->frag_list, nlh_src); 3425461fc0cSDmitry Safonov if (err) { 3435461fc0cSDmitry Safonov if (new) { 3445461fc0cSDmitry Safonov kfree_skb(new); 3455461fc0cSDmitry Safonov skb_shinfo(skb)->frag_list = NULL; 3465461fc0cSDmitry Safonov } 3475461fc0cSDmitry Safonov return err; 3485461fc0cSDmitry Safonov } 3495461fc0cSDmitry Safonov 3505461fc0cSDmitry Safonov return 0; 3515461fc0cSDmitry Safonov } 3525461fc0cSDmitry Safonov 3535106f4a8SDmitry Safonov /* Calculates len of translated 64-bit message. */ 3545106f4a8SDmitry Safonov static size_t xfrm_user_rcv_calculate_len64(const struct nlmsghdr *src, 3554e950506SDmitry Safonov struct nlattr *attrs[XFRMA_MAX + 1], 3564e950506SDmitry Safonov int maxtype) 3575106f4a8SDmitry Safonov { 3585106f4a8SDmitry Safonov size_t len = nlmsg_len(src); 3595106f4a8SDmitry Safonov 3605106f4a8SDmitry Safonov switch (src->nlmsg_type) { 3615106f4a8SDmitry Safonov case XFRM_MSG_NEWSA: 3625106f4a8SDmitry Safonov case XFRM_MSG_NEWPOLICY: 3635106f4a8SDmitry Safonov case XFRM_MSG_ALLOCSPI: 3645106f4a8SDmitry Safonov case XFRM_MSG_ACQUIRE: 3655106f4a8SDmitry Safonov case XFRM_MSG_UPDPOLICY: 3665106f4a8SDmitry Safonov case XFRM_MSG_UPDSA: 3675106f4a8SDmitry Safonov len += 4; 3685106f4a8SDmitry Safonov break; 3695106f4a8SDmitry Safonov case XFRM_MSG_EXPIRE: 3705106f4a8SDmitry Safonov case XFRM_MSG_POLEXPIRE: 3715106f4a8SDmitry Safonov len += 8; 3725106f4a8SDmitry Safonov break; 3734e950506SDmitry Safonov case XFRM_MSG_NEWSPDINFO: 3744e950506SDmitry Safonov /* attirbutes are xfrm_spdattr_type_t, not xfrm_attr_type_t */ 3754e950506SDmitry Safonov return len; 3765106f4a8SDmitry Safonov default: 3775106f4a8SDmitry Safonov break; 3785106f4a8SDmitry Safonov } 3795106f4a8SDmitry Safonov 3804e950506SDmitry Safonov /* Unexpected for anything, but XFRM_MSG_NEWSPDINFO, please 3814e950506SDmitry Safonov * correct both 64=>32-bit and 32=>64-bit translators to copy 3824e950506SDmitry Safonov * new attributes. 3834e950506SDmitry Safonov */ 3844e950506SDmitry Safonov if (WARN_ON_ONCE(maxtype)) 3854e950506SDmitry Safonov return len; 3864e950506SDmitry Safonov 3875106f4a8SDmitry Safonov if (attrs[XFRMA_SA]) 3885106f4a8SDmitry Safonov len += 4; 3895106f4a8SDmitry Safonov if (attrs[XFRMA_POLICY]) 3905106f4a8SDmitry Safonov len += 4; 3915106f4a8SDmitry Safonov 3925106f4a8SDmitry Safonov /* XXX: some attrs may need to be realigned 3935106f4a8SDmitry Safonov * if !CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 3945106f4a8SDmitry Safonov */ 3955106f4a8SDmitry Safonov 3965106f4a8SDmitry Safonov return len; 3975106f4a8SDmitry Safonov } 3985106f4a8SDmitry Safonov 3995106f4a8SDmitry Safonov static int xfrm_attr_cpy32(void *dst, size_t *pos, const struct nlattr *src, 4005106f4a8SDmitry Safonov size_t size, int copy_len, int payload) 4015106f4a8SDmitry Safonov { 4025106f4a8SDmitry Safonov struct nlmsghdr *nlmsg = dst; 4035106f4a8SDmitry Safonov struct nlattr *nla; 4045106f4a8SDmitry Safonov 405ef19e111SDmitry Safonov /* xfrm_user_rcv_msg_compat() relies on fact that 32-bit messages 406ef19e111SDmitry Safonov * have the same len or shorted than 64-bit ones. 407ef19e111SDmitry Safonov * 32-bit translation that is bigger than 64-bit original is unexpected. 408ef19e111SDmitry Safonov */ 4095106f4a8SDmitry Safonov if (WARN_ON_ONCE(copy_len > payload)) 4105106f4a8SDmitry Safonov copy_len = payload; 4115106f4a8SDmitry Safonov 4125106f4a8SDmitry Safonov if (size - *pos < nla_attr_size(payload)) 4135106f4a8SDmitry Safonov return -ENOBUFS; 4145106f4a8SDmitry Safonov 4155106f4a8SDmitry Safonov nla = dst + *pos; 4165106f4a8SDmitry Safonov 4175106f4a8SDmitry Safonov memcpy(nla, src, nla_attr_size(copy_len)); 4185106f4a8SDmitry Safonov nla->nla_len = nla_attr_size(payload); 419d1949d04SDmitry Safonov *pos += nla_attr_size(copy_len); 4205106f4a8SDmitry Safonov nlmsg->nlmsg_len += nla->nla_len; 4215106f4a8SDmitry Safonov 4225106f4a8SDmitry Safonov memset(dst + *pos, 0, payload - copy_len); 4235106f4a8SDmitry Safonov *pos += payload - copy_len; 4245106f4a8SDmitry Safonov 4255106f4a8SDmitry Safonov return 0; 4265106f4a8SDmitry Safonov } 4275106f4a8SDmitry Safonov 4285106f4a8SDmitry Safonov static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla, 4295106f4a8SDmitry Safonov size_t *pos, size_t size, 4305106f4a8SDmitry Safonov struct netlink_ext_ack *extack) 4315106f4a8SDmitry Safonov { 4325106f4a8SDmitry Safonov int type = nla_type(nla); 4335106f4a8SDmitry Safonov u16 pol_len32, pol_len64; 4345106f4a8SDmitry Safonov int err; 4355106f4a8SDmitry Safonov 4365106f4a8SDmitry Safonov if (type > XFRMA_MAX) { 4374e484b3eSAntony Antony BUILD_BUG_ON(XFRMA_MAX != XFRMA_MTIMER_THRESH); 4385106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Bad attribute"); 4395106f4a8SDmitry Safonov return -EOPNOTSUPP; 4405106f4a8SDmitry Safonov } 441b6ee8963SEric Dumazet type = array_index_nospec(type, XFRMA_MAX + 1); 4425106f4a8SDmitry Safonov if (nla_len(nla) < compat_policy[type].len) { 4435106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Attribute bad length"); 4445106f4a8SDmitry Safonov return -EOPNOTSUPP; 4455106f4a8SDmitry Safonov } 4465106f4a8SDmitry Safonov 4475106f4a8SDmitry Safonov pol_len32 = compat_policy[type].len; 4485106f4a8SDmitry Safonov pol_len64 = xfrma_policy[type].len; 4495106f4a8SDmitry Safonov 4505106f4a8SDmitry Safonov /* XFRMA_SA and XFRMA_POLICY - need to know how-to translate */ 4515106f4a8SDmitry Safonov if (pol_len32 != pol_len64) { 4525106f4a8SDmitry Safonov if (nla_len(nla) != compat_policy[type].len) { 4535106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Attribute bad length"); 4545106f4a8SDmitry Safonov return -EOPNOTSUPP; 4555106f4a8SDmitry Safonov } 4565106f4a8SDmitry Safonov err = xfrm_attr_cpy32(dst, pos, nla, size, pol_len32, pol_len64); 4575106f4a8SDmitry Safonov if (err) 4585106f4a8SDmitry Safonov return err; 4595106f4a8SDmitry Safonov } 4605106f4a8SDmitry Safonov 4615106f4a8SDmitry Safonov return xfrm_attr_cpy32(dst, pos, nla, size, nla_len(nla), nla_len(nla)); 4625106f4a8SDmitry Safonov } 4635106f4a8SDmitry Safonov 4645106f4a8SDmitry Safonov static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src, 4655106f4a8SDmitry Safonov struct nlattr *attrs[XFRMA_MAX+1], 4664e950506SDmitry Safonov size_t size, u8 type, int maxtype, 4674e950506SDmitry Safonov struct netlink_ext_ack *extack) 4685106f4a8SDmitry Safonov { 4695106f4a8SDmitry Safonov size_t pos; 4705106f4a8SDmitry Safonov int i; 4715106f4a8SDmitry Safonov 4725106f4a8SDmitry Safonov memcpy(dst, src, NLMSG_HDRLEN); 4735106f4a8SDmitry Safonov dst->nlmsg_len = NLMSG_HDRLEN + xfrm_msg_min[type]; 4745106f4a8SDmitry Safonov memset(nlmsg_data(dst), 0, xfrm_msg_min[type]); 4755106f4a8SDmitry Safonov 4765106f4a8SDmitry Safonov switch (src->nlmsg_type) { 4775106f4a8SDmitry Safonov /* Compat message has the same layout as native */ 4785106f4a8SDmitry Safonov case XFRM_MSG_DELSA: 4795106f4a8SDmitry Safonov case XFRM_MSG_GETSA: 4805106f4a8SDmitry Safonov case XFRM_MSG_DELPOLICY: 4815106f4a8SDmitry Safonov case XFRM_MSG_GETPOLICY: 4825106f4a8SDmitry Safonov case XFRM_MSG_FLUSHSA: 4835106f4a8SDmitry Safonov case XFRM_MSG_FLUSHPOLICY: 4845106f4a8SDmitry Safonov case XFRM_MSG_NEWAE: 4855106f4a8SDmitry Safonov case XFRM_MSG_GETAE: 4865106f4a8SDmitry Safonov case XFRM_MSG_REPORT: 4875106f4a8SDmitry Safonov case XFRM_MSG_MIGRATE: 4885106f4a8SDmitry Safonov case XFRM_MSG_NEWSADINFO: 4895106f4a8SDmitry Safonov case XFRM_MSG_GETSADINFO: 4905106f4a8SDmitry Safonov case XFRM_MSG_NEWSPDINFO: 4915106f4a8SDmitry Safonov case XFRM_MSG_GETSPDINFO: 4925106f4a8SDmitry Safonov case XFRM_MSG_MAPPING: 4935106f4a8SDmitry Safonov memcpy(nlmsg_data(dst), nlmsg_data(src), compat_msg_min[type]); 4945106f4a8SDmitry Safonov break; 4955106f4a8SDmitry Safonov /* 4 byte alignment for trailing u64 on native, but not on compat */ 4965106f4a8SDmitry Safonov case XFRM_MSG_NEWSA: 4975106f4a8SDmitry Safonov case XFRM_MSG_NEWPOLICY: 4985106f4a8SDmitry Safonov case XFRM_MSG_UPDSA: 4995106f4a8SDmitry Safonov case XFRM_MSG_UPDPOLICY: 5005106f4a8SDmitry Safonov memcpy(nlmsg_data(dst), nlmsg_data(src), compat_msg_min[type]); 5015106f4a8SDmitry Safonov break; 5025106f4a8SDmitry Safonov case XFRM_MSG_EXPIRE: { 5035106f4a8SDmitry Safonov const struct compat_xfrm_user_expire *src_ue = nlmsg_data(src); 5045106f4a8SDmitry Safonov struct xfrm_user_expire *dst_ue = nlmsg_data(dst); 5055106f4a8SDmitry Safonov 5065106f4a8SDmitry Safonov /* compat_xfrm_user_expire has 4-byte smaller state */ 5075106f4a8SDmitry Safonov memcpy(dst_ue, src_ue, sizeof(src_ue->state)); 5085106f4a8SDmitry Safonov dst_ue->hard = src_ue->hard; 5095106f4a8SDmitry Safonov break; 5105106f4a8SDmitry Safonov } 5115106f4a8SDmitry Safonov case XFRM_MSG_ACQUIRE: { 5125106f4a8SDmitry Safonov const struct compat_xfrm_user_acquire *src_ua = nlmsg_data(src); 5135106f4a8SDmitry Safonov struct xfrm_user_acquire *dst_ua = nlmsg_data(dst); 5145106f4a8SDmitry Safonov 5155106f4a8SDmitry Safonov memcpy(dst_ua, src_ua, offsetof(struct compat_xfrm_user_acquire, aalgos)); 5165106f4a8SDmitry Safonov dst_ua->aalgos = src_ua->aalgos; 5175106f4a8SDmitry Safonov dst_ua->ealgos = src_ua->ealgos; 5185106f4a8SDmitry Safonov dst_ua->calgos = src_ua->calgos; 5195106f4a8SDmitry Safonov dst_ua->seq = src_ua->seq; 5205106f4a8SDmitry Safonov break; 5215106f4a8SDmitry Safonov } 5225106f4a8SDmitry Safonov case XFRM_MSG_POLEXPIRE: { 5235106f4a8SDmitry Safonov const struct compat_xfrm_user_polexpire *src_upe = nlmsg_data(src); 5245106f4a8SDmitry Safonov struct xfrm_user_polexpire *dst_upe = nlmsg_data(dst); 5255106f4a8SDmitry Safonov 5265106f4a8SDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 5275106f4a8SDmitry Safonov memcpy(dst_upe, src_upe, sizeof(src_upe->pol)); 5285106f4a8SDmitry Safonov dst_upe->hard = src_upe->hard; 5295106f4a8SDmitry Safonov break; 5305106f4a8SDmitry Safonov } 5315106f4a8SDmitry Safonov case XFRM_MSG_ALLOCSPI: { 5325106f4a8SDmitry Safonov const struct compat_xfrm_userspi_info *src_usi = nlmsg_data(src); 5335106f4a8SDmitry Safonov struct xfrm_userspi_info *dst_usi = nlmsg_data(dst); 5345106f4a8SDmitry Safonov 5355106f4a8SDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 5365106f4a8SDmitry Safonov memcpy(dst_usi, src_usi, sizeof(src_usi->info)); 5375106f4a8SDmitry Safonov dst_usi->min = src_usi->min; 5385106f4a8SDmitry Safonov dst_usi->max = src_usi->max; 5395106f4a8SDmitry Safonov break; 5405106f4a8SDmitry Safonov } 5415106f4a8SDmitry Safonov default: 5425106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Unsupported message type"); 5435106f4a8SDmitry Safonov return -EOPNOTSUPP; 5445106f4a8SDmitry Safonov } 5455106f4a8SDmitry Safonov pos = dst->nlmsg_len; 5465106f4a8SDmitry Safonov 5474e950506SDmitry Safonov if (maxtype) { 5484e950506SDmitry Safonov /* attirbutes are xfrm_spdattr_type_t, not xfrm_attr_type_t */ 5494e950506SDmitry Safonov WARN_ON_ONCE(src->nlmsg_type != XFRM_MSG_NEWSPDINFO); 5504e950506SDmitry Safonov 5514e950506SDmitry Safonov for (i = 1; i <= maxtype; i++) { 5524e950506SDmitry Safonov int err; 5534e950506SDmitry Safonov 5544e950506SDmitry Safonov if (!attrs[i]) 5554e950506SDmitry Safonov continue; 5564e950506SDmitry Safonov 5574e950506SDmitry Safonov /* just copy - no need for translation */ 5584e950506SDmitry Safonov err = xfrm_attr_cpy32(dst, &pos, attrs[i], size, 5594e950506SDmitry Safonov nla_len(attrs[i]), nla_len(attrs[i])); 5604e950506SDmitry Safonov if (err) 5614e950506SDmitry Safonov return err; 5624e950506SDmitry Safonov } 5634e950506SDmitry Safonov return 0; 5644e950506SDmitry Safonov } 5654e950506SDmitry Safonov 5665106f4a8SDmitry Safonov for (i = 1; i < XFRMA_MAX + 1; i++) { 5675106f4a8SDmitry Safonov int err; 5685106f4a8SDmitry Safonov 5695106f4a8SDmitry Safonov if (i == XFRMA_PAD) 5705106f4a8SDmitry Safonov continue; 5715106f4a8SDmitry Safonov 5725106f4a8SDmitry Safonov if (!attrs[i]) 5735106f4a8SDmitry Safonov continue; 5745106f4a8SDmitry Safonov 5755106f4a8SDmitry Safonov err = xfrm_xlate32_attr(dst, attrs[i], &pos, size, extack); 5765106f4a8SDmitry Safonov if (err) 5775106f4a8SDmitry Safonov return err; 5785106f4a8SDmitry Safonov } 5795106f4a8SDmitry Safonov 5805106f4a8SDmitry Safonov return 0; 5815106f4a8SDmitry Safonov } 5825106f4a8SDmitry Safonov 5835106f4a8SDmitry Safonov static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32, 5845106f4a8SDmitry Safonov int maxtype, const struct nla_policy *policy, 5855106f4a8SDmitry Safonov struct netlink_ext_ack *extack) 5865106f4a8SDmitry Safonov { 5875106f4a8SDmitry Safonov /* netlink_rcv_skb() checks if a message has full (struct nlmsghdr) */ 5885106f4a8SDmitry Safonov u16 type = h32->nlmsg_type - XFRM_MSG_BASE; 5895106f4a8SDmitry Safonov struct nlattr *attrs[XFRMA_MAX+1]; 5905106f4a8SDmitry Safonov struct nlmsghdr *h64; 5915106f4a8SDmitry Safonov size_t len; 5925106f4a8SDmitry Safonov int err; 5935106f4a8SDmitry Safonov 5945106f4a8SDmitry Safonov BUILD_BUG_ON(ARRAY_SIZE(xfrm_msg_min) != ARRAY_SIZE(compat_msg_min)); 5955106f4a8SDmitry Safonov 5965106f4a8SDmitry Safonov if (type >= ARRAY_SIZE(xfrm_msg_min)) 5975106f4a8SDmitry Safonov return ERR_PTR(-EINVAL); 5985106f4a8SDmitry Safonov 5995106f4a8SDmitry Safonov /* Don't call parse: the message might have only nlmsg header */ 6005106f4a8SDmitry Safonov if ((h32->nlmsg_type == XFRM_MSG_GETSA || 6015106f4a8SDmitry Safonov h32->nlmsg_type == XFRM_MSG_GETPOLICY) && 6025106f4a8SDmitry Safonov (h32->nlmsg_flags & NLM_F_DUMP)) 6035106f4a8SDmitry Safonov return NULL; 6045106f4a8SDmitry Safonov 6055106f4a8SDmitry Safonov err = nlmsg_parse_deprecated(h32, compat_msg_min[type], attrs, 6065106f4a8SDmitry Safonov maxtype ? : XFRMA_MAX, policy ? : compat_policy, extack); 6075106f4a8SDmitry Safonov if (err < 0) 6085106f4a8SDmitry Safonov return ERR_PTR(err); 6095106f4a8SDmitry Safonov 6104e950506SDmitry Safonov len = xfrm_user_rcv_calculate_len64(h32, attrs, maxtype); 6115106f4a8SDmitry Safonov /* The message doesn't need translation */ 6125106f4a8SDmitry Safonov if (len == nlmsg_len(h32)) 6135106f4a8SDmitry Safonov return NULL; 6145106f4a8SDmitry Safonov 6155106f4a8SDmitry Safonov len += NLMSG_HDRLEN; 616ad37f77fSDmitry Safonov h64 = kvmalloc(len, GFP_KERNEL); 6175106f4a8SDmitry Safonov if (!h64) 6185106f4a8SDmitry Safonov return ERR_PTR(-ENOMEM); 6195106f4a8SDmitry Safonov 6204e950506SDmitry Safonov err = xfrm_xlate32(h64, h32, attrs, len, type, maxtype, extack); 6215106f4a8SDmitry Safonov if (err < 0) { 6225106f4a8SDmitry Safonov kvfree(h64); 6235106f4a8SDmitry Safonov return ERR_PTR(err); 6245106f4a8SDmitry Safonov } 6255106f4a8SDmitry Safonov 6265106f4a8SDmitry Safonov return h64; 6275106f4a8SDmitry Safonov } 6285106f4a8SDmitry Safonov 62996392ee5SDmitry Safonov static int xfrm_user_policy_compat(u8 **pdata32, int optlen) 63096392ee5SDmitry Safonov { 63196392ee5SDmitry Safonov struct compat_xfrm_userpolicy_info *p = (void *)*pdata32; 63296392ee5SDmitry Safonov u8 *src_templates, *dst_templates; 63396392ee5SDmitry Safonov u8 *data64; 63496392ee5SDmitry Safonov 63596392ee5SDmitry Safonov if (optlen < sizeof(*p)) 63696392ee5SDmitry Safonov return -EINVAL; 63796392ee5SDmitry Safonov 63896392ee5SDmitry Safonov data64 = kmalloc_track_caller(optlen + 4, GFP_USER | __GFP_NOWARN); 63996392ee5SDmitry Safonov if (!data64) 64096392ee5SDmitry Safonov return -ENOMEM; 64196392ee5SDmitry Safonov 64296392ee5SDmitry Safonov memcpy(data64, *pdata32, sizeof(*p)); 64396392ee5SDmitry Safonov memset(data64 + sizeof(*p), 0, 4); 64496392ee5SDmitry Safonov 64596392ee5SDmitry Safonov src_templates = *pdata32 + sizeof(*p); 64696392ee5SDmitry Safonov dst_templates = data64 + sizeof(*p) + 4; 64796392ee5SDmitry Safonov memcpy(dst_templates, src_templates, optlen - sizeof(*p)); 64896392ee5SDmitry Safonov 64996392ee5SDmitry Safonov kfree(*pdata32); 65096392ee5SDmitry Safonov *pdata32 = data64; 65196392ee5SDmitry Safonov return 0; 65296392ee5SDmitry Safonov } 65396392ee5SDmitry Safonov 654c9e7c76dSDmitry Safonov static struct xfrm_translator xfrm_translator = { 655c9e7c76dSDmitry Safonov .owner = THIS_MODULE, 6565461fc0cSDmitry Safonov .alloc_compat = xfrm_alloc_compat, 6575106f4a8SDmitry Safonov .rcv_msg_compat = xfrm_user_rcv_msg_compat, 65896392ee5SDmitry Safonov .xlate_user_policy_sockptr = xfrm_user_policy_compat, 659c9e7c76dSDmitry Safonov }; 660c9e7c76dSDmitry Safonov 661c9e7c76dSDmitry Safonov static int __init xfrm_compat_init(void) 662c9e7c76dSDmitry Safonov { 663c9e7c76dSDmitry Safonov return xfrm_register_translator(&xfrm_translator); 664c9e7c76dSDmitry Safonov } 665c9e7c76dSDmitry Safonov 666c9e7c76dSDmitry Safonov static void __exit xfrm_compat_exit(void) 667c9e7c76dSDmitry Safonov { 668c9e7c76dSDmitry Safonov xfrm_unregister_translator(&xfrm_translator); 669c9e7c76dSDmitry Safonov } 670c9e7c76dSDmitry Safonov 671c9e7c76dSDmitry Safonov module_init(xfrm_compat_init); 672c9e7c76dSDmitry Safonov module_exit(xfrm_compat_exit); 673c9e7c76dSDmitry Safonov MODULE_LICENSE("GPL"); 674c9e7c76dSDmitry Safonov MODULE_AUTHOR("Dmitry Safonov"); 675c9e7c76dSDmitry Safonov MODULE_DESCRIPTION("XFRM 32-bit compatibility layer"); 676