linux/net/xfrm/xfrm_compat.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * XFRM compat layer
   4 * Author: Dmitry Safonov <dima@arista.com>
   5 * Based on code and translator idea by: Florian Westphal <fw@strlen.de>
   6 */
   7#include <linux/compat.h>
   8#include <linux/xfrm.h>
   9#include <net/xfrm.h>
  10
  11struct compat_xfrm_lifetime_cfg {
  12        compat_u64 soft_byte_limit, hard_byte_limit;
  13        compat_u64 soft_packet_limit, hard_packet_limit;
  14        compat_u64 soft_add_expires_seconds, hard_add_expires_seconds;
  15        compat_u64 soft_use_expires_seconds, hard_use_expires_seconds;
  16}; /* same size on 32bit, but only 4 byte alignment required */
  17
  18struct compat_xfrm_lifetime_cur {
  19        compat_u64 bytes, packets, add_time, use_time;
  20}; /* same size on 32bit, but only 4 byte alignment required */
  21
  22struct compat_xfrm_userpolicy_info {
  23        struct xfrm_selector sel;
  24        struct compat_xfrm_lifetime_cfg lft;
  25        struct compat_xfrm_lifetime_cur curlft;
  26        __u32 priority, index;
  27        u8 dir, action, flags, share;
  28        /* 4 bytes additional padding on 64bit */
  29};
  30
  31struct compat_xfrm_usersa_info {
  32        struct xfrm_selector sel;
  33        struct xfrm_id id;
  34        xfrm_address_t saddr;
  35        struct compat_xfrm_lifetime_cfg lft;
  36        struct compat_xfrm_lifetime_cur curlft;
  37        struct xfrm_stats stats;
  38        __u32 seq, reqid;
  39        u16 family;
  40        u8 mode, replay_window, flags;
  41        /* 4 bytes additional padding on 64bit */
  42};
  43
  44struct compat_xfrm_user_acquire {
  45        struct xfrm_id id;
  46        xfrm_address_t saddr;
  47        struct xfrm_selector sel;
  48        struct compat_xfrm_userpolicy_info policy;
  49        /* 4 bytes additional padding on 64bit */
  50        __u32 aalgos, ealgos, calgos, seq;
  51};
  52
  53struct compat_xfrm_userspi_info {
  54        struct compat_xfrm_usersa_info info;
  55        /* 4 bytes additional padding on 64bit */
  56        __u32 min, max;
  57};
  58
  59struct compat_xfrm_user_expire {
  60        struct compat_xfrm_usersa_info state;
  61        /* 8 bytes additional padding on 64bit */
  62        u8 hard;
  63};
  64
  65struct compat_xfrm_user_polexpire {
  66        struct compat_xfrm_userpolicy_info pol;
  67        /* 8 bytes additional padding on 64bit */
  68        u8 hard;
  69};
  70
  71#define XMSGSIZE(type) sizeof(struct type)
  72
  73static const int compat_msg_min[XFRM_NR_MSGTYPES] = {
  74        [XFRM_MSG_NEWSA       - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_usersa_info),
  75        [XFRM_MSG_DELSA       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id),
  76        [XFRM_MSG_GETSA       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id),
  77        [XFRM_MSG_NEWPOLICY   - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userpolicy_info),
  78        [XFRM_MSG_DELPOLICY   - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
  79        [XFRM_MSG_GETPOLICY   - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
  80        [XFRM_MSG_ALLOCSPI    - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userspi_info),
  81        [XFRM_MSG_ACQUIRE     - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_acquire),
  82        [XFRM_MSG_EXPIRE      - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_expire),
  83        [XFRM_MSG_UPDPOLICY   - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userpolicy_info),
  84        [XFRM_MSG_UPDSA       - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_usersa_info),
  85        [XFRM_MSG_POLEXPIRE   - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_polexpire),
  86        [XFRM_MSG_FLUSHSA     - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush),
  87        [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0,
  88        [XFRM_MSG_NEWAE       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id),
  89        [XFRM_MSG_GETAE       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id),
  90        [XFRM_MSG_REPORT      - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report),
  91        [XFRM_MSG_MIGRATE     - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
  92        [XFRM_MSG_NEWSADINFO  - XFRM_MSG_BASE] = sizeof(u32),
  93        [XFRM_MSG_GETSADINFO  - XFRM_MSG_BASE] = sizeof(u32),
  94        [XFRM_MSG_NEWSPDINFO  - XFRM_MSG_BASE] = sizeof(u32),
  95        [XFRM_MSG_GETSPDINFO  - XFRM_MSG_BASE] = sizeof(u32),
  96        [XFRM_MSG_MAPPING     - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping)
  97};
  98
  99static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
 100        [XFRMA_SA]              = { .len = XMSGSIZE(compat_xfrm_usersa_info)},
 101        [XFRMA_POLICY]          = { .len = XMSGSIZE(compat_xfrm_userpolicy_info)},
 102        [XFRMA_LASTUSED]        = { .type = NLA_U64},
 103        [XFRMA_ALG_AUTH_TRUNC]  = { .len = sizeof(struct xfrm_algo_auth)},
 104        [XFRMA_ALG_AEAD]        = { .len = sizeof(struct xfrm_algo_aead) },
 105        [XFRMA_ALG_AUTH]        = { .len = sizeof(struct xfrm_algo) },
 106        [XFRMA_ALG_CRYPT]       = { .len = sizeof(struct xfrm_algo) },
 107        [XFRMA_ALG_COMP]        = { .len = sizeof(struct xfrm_algo) },
 108        [XFRMA_ENCAP]           = { .len = sizeof(struct xfrm_encap_tmpl) },
 109        [XFRMA_TMPL]            = { .len = sizeof(struct xfrm_user_tmpl) },
 110        [XFRMA_SEC_CTX]         = { .len = sizeof(struct xfrm_sec_ctx) },
 111        [XFRMA_LTIME_VAL]       = { .len = sizeof(struct xfrm_lifetime_cur) },
 112        [XFRMA_REPLAY_VAL]      = { .len = sizeof(struct xfrm_replay_state) },
 113        [XFRMA_REPLAY_THRESH]   = { .type = NLA_U32 },
 114        [XFRMA_ETIMER_THRESH]   = { .type = NLA_U32 },
 115        [XFRMA_SRCADDR]         = { .len = sizeof(xfrm_address_t) },
 116        [XFRMA_COADDR]          = { .len = sizeof(xfrm_address_t) },
 117        [XFRMA_POLICY_TYPE]     = { .len = sizeof(struct xfrm_userpolicy_type)},
 118        [XFRMA_MIGRATE]         = { .len = sizeof(struct xfrm_user_migrate) },
 119        [XFRMA_KMADDRESS]       = { .len = sizeof(struct xfrm_user_kmaddress) },
 120        [XFRMA_MARK]            = { .len = sizeof(struct xfrm_mark) },
 121        [XFRMA_TFCPAD]          = { .type = NLA_U32 },
 122        [XFRMA_REPLAY_ESN_VAL]  = { .len = sizeof(struct xfrm_replay_state_esn) },
 123        [XFRMA_SA_EXTRA_FLAGS]  = { .type = NLA_U32 },
 124        [XFRMA_PROTO]           = { .type = NLA_U8 },
 125        [XFRMA_ADDRESS_FILTER]  = { .len = sizeof(struct xfrm_address_filter) },
 126        [XFRMA_OFFLOAD_DEV]     = { .len = sizeof(struct xfrm_user_offload) },
 127        [XFRMA_SET_MARK]        = { .type = NLA_U32 },
 128        [XFRMA_SET_MARK_MASK]   = { .type = NLA_U32 },
 129        [XFRMA_IF_ID]           = { .type = NLA_U32 },
 130};
 131
 132static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb,
 133                        const struct nlmsghdr *nlh_src, u16 type)
 134{
 135        int payload = compat_msg_min[type];
 136        int src_len = xfrm_msg_min[type];
 137        struct nlmsghdr *nlh_dst;
 138
 139        /* Compat messages are shorter or equal to native (+padding) */
 140        if (WARN_ON_ONCE(src_len < payload))
 141                return ERR_PTR(-EMSGSIZE);
 142
 143        nlh_dst = nlmsg_put(skb, nlh_src->nlmsg_pid, nlh_src->nlmsg_seq,
 144                            nlh_src->nlmsg_type, payload, nlh_src->nlmsg_flags);
 145        if (!nlh_dst)
 146                return ERR_PTR(-EMSGSIZE);
 147
 148        memset(nlmsg_data(nlh_dst), 0, payload);
 149
 150        switch (nlh_src->nlmsg_type) {
 151        /* Compat message has the same layout as native */
 152        case XFRM_MSG_DELSA:
 153        case XFRM_MSG_DELPOLICY:
 154        case XFRM_MSG_FLUSHSA:
 155        case XFRM_MSG_FLUSHPOLICY:
 156        case XFRM_MSG_NEWAE:
 157        case XFRM_MSG_REPORT:
 158        case XFRM_MSG_MIGRATE:
 159        case XFRM_MSG_NEWSADINFO:
 160        case XFRM_MSG_NEWSPDINFO:
 161        case XFRM_MSG_MAPPING:
 162                WARN_ON_ONCE(src_len != payload);
 163                memcpy(nlmsg_data(nlh_dst), nlmsg_data(nlh_src), src_len);
 164                break;
 165        /* 4 byte alignment for trailing u64 on native, but not on compat */
 166        case XFRM_MSG_NEWSA:
 167        case XFRM_MSG_NEWPOLICY:
 168        case XFRM_MSG_UPDSA:
 169        case XFRM_MSG_UPDPOLICY:
 170                WARN_ON_ONCE(src_len != payload + 4);
 171                memcpy(nlmsg_data(nlh_dst), nlmsg_data(nlh_src), payload);
 172                break;
 173        case XFRM_MSG_EXPIRE: {
 174                const struct xfrm_user_expire *src_ue  = nlmsg_data(nlh_src);
 175                struct compat_xfrm_user_expire *dst_ue = nlmsg_data(nlh_dst);
 176
 177                /* compat_xfrm_user_expire has 4-byte smaller state */
 178                memcpy(dst_ue, src_ue, sizeof(dst_ue->state));
 179                dst_ue->hard = src_ue->hard;
 180                break;
 181        }
 182        case XFRM_MSG_ACQUIRE: {
 183                const struct xfrm_user_acquire *src_ua  = nlmsg_data(nlh_src);
 184                struct compat_xfrm_user_acquire *dst_ua = nlmsg_data(nlh_dst);
 185
 186                memcpy(dst_ua, src_ua, offsetof(struct compat_xfrm_user_acquire, aalgos));
 187                dst_ua->aalgos = src_ua->aalgos;
 188                dst_ua->ealgos = src_ua->ealgos;
 189                dst_ua->calgos = src_ua->calgos;
 190                dst_ua->seq    = src_ua->seq;
 191                break;
 192        }
 193        case XFRM_MSG_POLEXPIRE: {
 194                const struct xfrm_user_polexpire *src_upe  = nlmsg_data(nlh_src);
 195                struct compat_xfrm_user_polexpire *dst_upe = nlmsg_data(nlh_dst);
 196
 197                /* compat_xfrm_user_polexpire has 4-byte smaller state */
 198                memcpy(dst_upe, src_upe, sizeof(dst_upe->pol));
 199                dst_upe->hard = src_upe->hard;
 200                break;
 201        }
 202        case XFRM_MSG_ALLOCSPI: {
 203                const struct xfrm_userspi_info *src_usi = nlmsg_data(nlh_src);
 204                struct compat_xfrm_userspi_info *dst_usi = nlmsg_data(nlh_dst);
 205
 206                /* compat_xfrm_user_polexpire has 4-byte smaller state */
 207                memcpy(dst_usi, src_usi, sizeof(src_usi->info));
 208                dst_usi->min = src_usi->min;
 209                dst_usi->max = src_usi->max;
 210                break;
 211        }
 212        /* Not being sent by kernel */
 213        case XFRM_MSG_GETSA:
 214        case XFRM_MSG_GETPOLICY:
 215        case XFRM_MSG_GETAE:
 216        case XFRM_MSG_GETSADINFO:
 217        case XFRM_MSG_GETSPDINFO:
 218        default:
 219                pr_warn_once("unsupported nlmsg_type %d\n", nlh_src->nlmsg_type);
 220                return ERR_PTR(-EOPNOTSUPP);
 221        }
 222
 223        return nlh_dst;
 224}
 225
 226static int xfrm_nla_cpy(struct sk_buff *dst, const struct nlattr *src, int len)
 227{
 228        return nla_put(dst, src->nla_type, len, nla_data(src));
 229}
 230
 231static int xfrm_xlate64_attr(struct sk_buff *dst, const struct nlattr *src)
 232{
 233        switch (src->nla_type) {
 234        case XFRMA_PAD:
 235                /* Ignore */
 236                return 0;
 237        case XFRMA_UNSPEC:
 238        case XFRMA_ALG_AUTH:
 239        case XFRMA_ALG_CRYPT:
 240        case XFRMA_ALG_COMP:
 241        case XFRMA_ENCAP:
 242        case XFRMA_TMPL:
 243                return xfrm_nla_cpy(dst, src, nla_len(src));
 244        case XFRMA_SA:
 245                return xfrm_nla_cpy(dst, src, XMSGSIZE(compat_xfrm_usersa_info));
 246        case XFRMA_POLICY:
 247                return xfrm_nla_cpy(dst, src, XMSGSIZE(compat_xfrm_userpolicy_info));
 248        case XFRMA_SEC_CTX:
 249                return xfrm_nla_cpy(dst, src, nla_len(src));
 250        case XFRMA_LTIME_VAL:
 251                return nla_put_64bit(dst, src->nla_type, nla_len(src),
 252                        nla_data(src), XFRMA_PAD);
 253        case XFRMA_REPLAY_VAL:
 254        case XFRMA_REPLAY_THRESH:
 255        case XFRMA_ETIMER_THRESH:
 256        case XFRMA_SRCADDR:
 257        case XFRMA_COADDR:
 258                return xfrm_nla_cpy(dst, src, nla_len(src));
 259        case XFRMA_LASTUSED:
 260                return nla_put_64bit(dst, src->nla_type, nla_len(src),
 261                        nla_data(src), XFRMA_PAD);
 262        case XFRMA_POLICY_TYPE:
 263        case XFRMA_MIGRATE:
 264        case XFRMA_ALG_AEAD:
 265        case XFRMA_KMADDRESS:
 266        case XFRMA_ALG_AUTH_TRUNC:
 267        case XFRMA_MARK:
 268        case XFRMA_TFCPAD:
 269        case XFRMA_REPLAY_ESN_VAL:
 270        case XFRMA_SA_EXTRA_FLAGS:
 271        case XFRMA_PROTO:
 272        case XFRMA_ADDRESS_FILTER:
 273        case XFRMA_OFFLOAD_DEV:
 274        case XFRMA_SET_MARK:
 275        case XFRMA_SET_MARK_MASK:
 276        case XFRMA_IF_ID:
 277                return xfrm_nla_cpy(dst, src, nla_len(src));
 278        default:
 279                BUILD_BUG_ON(XFRMA_MAX != XFRMA_IF_ID);
 280                pr_warn_once("unsupported nla_type %d\n", src->nla_type);
 281                return -EOPNOTSUPP;
 282        }
 283}
 284
 285/* Take kernel-built (64bit layout) and create 32bit layout for userspace */
 286static int xfrm_xlate64(struct sk_buff *dst, const struct nlmsghdr *nlh_src)
 287{
 288        u16 type = nlh_src->nlmsg_type - XFRM_MSG_BASE;
 289        const struct nlattr *nla, *attrs;
 290        struct nlmsghdr *nlh_dst;
 291        int len, remaining;
 292
 293        nlh_dst = xfrm_nlmsg_put_compat(dst, nlh_src, type);
 294        if (IS_ERR(nlh_dst))
 295                return PTR_ERR(nlh_dst);
 296
 297        attrs = nlmsg_attrdata(nlh_src, xfrm_msg_min[type]);
 298        len = nlmsg_attrlen(nlh_src, xfrm_msg_min[type]);
 299
 300        nla_for_each_attr(nla, attrs, len, remaining) {
 301                int err;
 302
 303                switch (type) {
 304                case XFRM_MSG_NEWSPDINFO:
 305                        err = xfrm_nla_cpy(dst, nla, nla_len(nla));
 306                        break;
 307                default:
 308                        err = xfrm_xlate64_attr(dst, nla);
 309                        break;
 310                }
 311                if (err)
 312                        return err;
 313        }
 314
 315        nlmsg_end(dst, nlh_dst);
 316
 317        return 0;
 318}
 319
 320static int xfrm_alloc_compat(struct sk_buff *skb, const struct nlmsghdr *nlh_src)
 321{
 322        u16 type = nlh_src->nlmsg_type - XFRM_MSG_BASE;
 323        struct sk_buff *new = NULL;
 324        int err;
 325
 326        if (type >= ARRAY_SIZE(xfrm_msg_min)) {
 327                pr_warn_once("unsupported nlmsg_type %d\n", nlh_src->nlmsg_type);
 328                return -EOPNOTSUPP;
 329        }
 330
 331        if (skb_shinfo(skb)->frag_list == NULL) {
 332                new = alloc_skb(skb->len + skb_tailroom(skb), GFP_ATOMIC);
 333                if (!new)
 334                        return -ENOMEM;
 335                skb_shinfo(skb)->frag_list = new;
 336        }
 337
 338        err = xfrm_xlate64(skb_shinfo(skb)->frag_list, nlh_src);
 339        if (err) {
 340                if (new) {
 341                        kfree_skb(new);
 342                        skb_shinfo(skb)->frag_list = NULL;
 343                }
 344                return err;
 345        }
 346
 347        return 0;
 348}
 349
 350/* Calculates len of translated 64-bit message. */
 351static size_t xfrm_user_rcv_calculate_len64(const struct nlmsghdr *src,
 352                                            struct nlattr *attrs[XFRMA_MAX + 1],
 353                                            int maxtype)
 354{
 355        size_t len = nlmsg_len(src);
 356
 357        switch (src->nlmsg_type) {
 358        case XFRM_MSG_NEWSA:
 359        case XFRM_MSG_NEWPOLICY:
 360        case XFRM_MSG_ALLOCSPI:
 361        case XFRM_MSG_ACQUIRE:
 362        case XFRM_MSG_UPDPOLICY:
 363        case XFRM_MSG_UPDSA:
 364                len += 4;
 365                break;
 366        case XFRM_MSG_EXPIRE:
 367        case XFRM_MSG_POLEXPIRE:
 368                len += 8;
 369                break;
 370        case XFRM_MSG_NEWSPDINFO:
 371                /* attirbutes are xfrm_spdattr_type_t, not xfrm_attr_type_t */
 372                return len;
 373        default:
 374                break;
 375        }
 376
 377        /* Unexpected for anything, but XFRM_MSG_NEWSPDINFO, please
 378         * correct both 64=>32-bit and 32=>64-bit translators to copy
 379         * new attributes.
 380         */
 381        if (WARN_ON_ONCE(maxtype))
 382                return len;
 383
 384        if (attrs[XFRMA_SA])
 385                len += 4;
 386        if (attrs[XFRMA_POLICY])
 387                len += 4;
 388
 389        /* XXX: some attrs may need to be realigned
 390         * if !CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
 391         */
 392
 393        return len;
 394}
 395
 396static int xfrm_attr_cpy32(void *dst, size_t *pos, const struct nlattr *src,
 397                           size_t size, int copy_len, int payload)
 398{
 399        struct nlmsghdr *nlmsg = dst;
 400        struct nlattr *nla;
 401
 402        /* xfrm_user_rcv_msg_compat() relies on fact that 32-bit messages
 403         * have the same len or shorted than 64-bit ones.
 404         * 32-bit translation that is bigger than 64-bit original is unexpected.
 405         */
 406        if (WARN_ON_ONCE(copy_len > payload))
 407                copy_len = payload;
 408
 409        if (size - *pos < nla_attr_size(payload))
 410                return -ENOBUFS;
 411
 412        nla = dst + *pos;
 413
 414        memcpy(nla, src, nla_attr_size(copy_len));
 415        nla->nla_len = nla_attr_size(payload);
 416        *pos += nla_attr_size(copy_len);
 417        nlmsg->nlmsg_len += nla->nla_len;
 418
 419        memset(dst + *pos, 0, payload - copy_len);
 420        *pos += payload - copy_len;
 421
 422        return 0;
 423}
 424
 425static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla,
 426                             size_t *pos, size_t size,
 427                             struct netlink_ext_ack *extack)
 428{
 429        int type = nla_type(nla);
 430        u16 pol_len32, pol_len64;
 431        int err;
 432
 433        if (type > XFRMA_MAX) {
 434                BUILD_BUG_ON(XFRMA_MAX != XFRMA_IF_ID);
 435                NL_SET_ERR_MSG(extack, "Bad attribute");
 436                return -EOPNOTSUPP;
 437        }
 438        if (nla_len(nla) < compat_policy[type].len) {
 439                NL_SET_ERR_MSG(extack, "Attribute bad length");
 440                return -EOPNOTSUPP;
 441        }
 442
 443        pol_len32 = compat_policy[type].len;
 444        pol_len64 = xfrma_policy[type].len;
 445
 446        /* XFRMA_SA and XFRMA_POLICY - need to know how-to translate */
 447        if (pol_len32 != pol_len64) {
 448                if (nla_len(nla) != compat_policy[type].len) {
 449                        NL_SET_ERR_MSG(extack, "Attribute bad length");
 450                        return -EOPNOTSUPP;
 451                }
 452                err = xfrm_attr_cpy32(dst, pos, nla, size, pol_len32, pol_len64);
 453                if (err)
 454                        return err;
 455        }
 456
 457        return xfrm_attr_cpy32(dst, pos, nla, size, nla_len(nla), nla_len(nla));
 458}
 459
 460static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src,
 461                        struct nlattr *attrs[XFRMA_MAX+1],
 462                        size_t size, u8 type, int maxtype,
 463                        struct netlink_ext_ack *extack)
 464{
 465        size_t pos;
 466        int i;
 467
 468        memcpy(dst, src, NLMSG_HDRLEN);
 469        dst->nlmsg_len = NLMSG_HDRLEN + xfrm_msg_min[type];
 470        memset(nlmsg_data(dst), 0, xfrm_msg_min[type]);
 471
 472        switch (src->nlmsg_type) {
 473        /* Compat message has the same layout as native */
 474        case XFRM_MSG_DELSA:
 475        case XFRM_MSG_GETSA:
 476        case XFRM_MSG_DELPOLICY:
 477        case XFRM_MSG_GETPOLICY:
 478        case XFRM_MSG_FLUSHSA:
 479        case XFRM_MSG_FLUSHPOLICY:
 480        case XFRM_MSG_NEWAE:
 481        case XFRM_MSG_GETAE:
 482        case XFRM_MSG_REPORT:
 483        case XFRM_MSG_MIGRATE:
 484        case XFRM_MSG_NEWSADINFO:
 485        case XFRM_MSG_GETSADINFO:
 486        case XFRM_MSG_NEWSPDINFO:
 487        case XFRM_MSG_GETSPDINFO:
 488        case XFRM_MSG_MAPPING:
 489                memcpy(nlmsg_data(dst), nlmsg_data(src), compat_msg_min[type]);
 490                break;
 491        /* 4 byte alignment for trailing u64 on native, but not on compat */
 492        case XFRM_MSG_NEWSA:
 493        case XFRM_MSG_NEWPOLICY:
 494        case XFRM_MSG_UPDSA:
 495        case XFRM_MSG_UPDPOLICY:
 496                memcpy(nlmsg_data(dst), nlmsg_data(src), compat_msg_min[type]);
 497                break;
 498        case XFRM_MSG_EXPIRE: {
 499                const struct compat_xfrm_user_expire *src_ue = nlmsg_data(src);
 500                struct xfrm_user_expire *dst_ue = nlmsg_data(dst);
 501
 502                /* compat_xfrm_user_expire has 4-byte smaller state */
 503                memcpy(dst_ue, src_ue, sizeof(src_ue->state));
 504                dst_ue->hard = src_ue->hard;
 505                break;
 506        }
 507        case XFRM_MSG_ACQUIRE: {
 508                const struct compat_xfrm_user_acquire *src_ua = nlmsg_data(src);
 509                struct xfrm_user_acquire *dst_ua = nlmsg_data(dst);
 510
 511                memcpy(dst_ua, src_ua, offsetof(struct compat_xfrm_user_acquire, aalgos));
 512                dst_ua->aalgos = src_ua->aalgos;
 513                dst_ua->ealgos = src_ua->ealgos;
 514                dst_ua->calgos = src_ua->calgos;
 515                dst_ua->seq    = src_ua->seq;
 516                break;
 517        }
 518        case XFRM_MSG_POLEXPIRE: {
 519                const struct compat_xfrm_user_polexpire *src_upe = nlmsg_data(src);
 520                struct xfrm_user_polexpire *dst_upe = nlmsg_data(dst);
 521
 522                /* compat_xfrm_user_polexpire has 4-byte smaller state */
 523                memcpy(dst_upe, src_upe, sizeof(src_upe->pol));
 524                dst_upe->hard = src_upe->hard;
 525                break;
 526        }
 527        case XFRM_MSG_ALLOCSPI: {
 528                const struct compat_xfrm_userspi_info *src_usi = nlmsg_data(src);
 529                struct xfrm_userspi_info *dst_usi = nlmsg_data(dst);
 530
 531                /* compat_xfrm_user_polexpire has 4-byte smaller state */
 532                memcpy(dst_usi, src_usi, sizeof(src_usi->info));
 533                dst_usi->min = src_usi->min;
 534                dst_usi->max = src_usi->max;
 535                break;
 536        }
 537        default:
 538                NL_SET_ERR_MSG(extack, "Unsupported message type");
 539                return -EOPNOTSUPP;
 540        }
 541        pos = dst->nlmsg_len;
 542
 543        if (maxtype) {
 544                /* attirbutes are xfrm_spdattr_type_t, not xfrm_attr_type_t */
 545                WARN_ON_ONCE(src->nlmsg_type != XFRM_MSG_NEWSPDINFO);
 546
 547                for (i = 1; i <= maxtype; i++) {
 548                        int err;
 549
 550                        if (!attrs[i])
 551                                continue;
 552
 553                        /* just copy - no need for translation */
 554                        err = xfrm_attr_cpy32(dst, &pos, attrs[i], size,
 555                                        nla_len(attrs[i]), nla_len(attrs[i]));
 556                        if (err)
 557                                return err;
 558                }
 559                return 0;
 560        }
 561
 562        for (i = 1; i < XFRMA_MAX + 1; i++) {
 563                int err;
 564
 565                if (i == XFRMA_PAD)
 566                        continue;
 567
 568                if (!attrs[i])
 569                        continue;
 570
 571                err = xfrm_xlate32_attr(dst, attrs[i], &pos, size, extack);
 572                if (err)
 573                        return err;
 574        }
 575
 576        return 0;
 577}
 578
 579static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32,
 580                        int maxtype, const struct nla_policy *policy,
 581                        struct netlink_ext_ack *extack)
 582{
 583        /* netlink_rcv_skb() checks if a message has full (struct nlmsghdr) */
 584        u16 type = h32->nlmsg_type - XFRM_MSG_BASE;
 585        struct nlattr *attrs[XFRMA_MAX+1];
 586        struct nlmsghdr *h64;
 587        size_t len;
 588        int err;
 589
 590        BUILD_BUG_ON(ARRAY_SIZE(xfrm_msg_min) != ARRAY_SIZE(compat_msg_min));
 591
 592        if (type >= ARRAY_SIZE(xfrm_msg_min))
 593                return ERR_PTR(-EINVAL);
 594
 595        /* Don't call parse: the message might have only nlmsg header */
 596        if ((h32->nlmsg_type == XFRM_MSG_GETSA ||
 597             h32->nlmsg_type == XFRM_MSG_GETPOLICY) &&
 598            (h32->nlmsg_flags & NLM_F_DUMP))
 599                return NULL;
 600
 601        err = nlmsg_parse_deprecated(h32, compat_msg_min[type], attrs,
 602                        maxtype ? : XFRMA_MAX, policy ? : compat_policy, extack);
 603        if (err < 0)
 604                return ERR_PTR(err);
 605
 606        len = xfrm_user_rcv_calculate_len64(h32, attrs, maxtype);
 607        /* The message doesn't need translation */
 608        if (len == nlmsg_len(h32))
 609                return NULL;
 610
 611        len += NLMSG_HDRLEN;
 612        h64 = kvmalloc(len, GFP_KERNEL);
 613        if (!h64)
 614                return ERR_PTR(-ENOMEM);
 615
 616        err = xfrm_xlate32(h64, h32, attrs, len, type, maxtype, extack);
 617        if (err < 0) {
 618                kvfree(h64);
 619                return ERR_PTR(err);
 620        }
 621
 622        return h64;
 623}
 624
 625static int xfrm_user_policy_compat(u8 **pdata32, int optlen)
 626{
 627        struct compat_xfrm_userpolicy_info *p = (void *)*pdata32;
 628        u8 *src_templates, *dst_templates;
 629        u8 *data64;
 630
 631        if (optlen < sizeof(*p))
 632                return -EINVAL;
 633
 634        data64 = kmalloc_track_caller(optlen + 4, GFP_USER | __GFP_NOWARN);
 635        if (!data64)
 636                return -ENOMEM;
 637
 638        memcpy(data64, *pdata32, sizeof(*p));
 639        memset(data64 + sizeof(*p), 0, 4);
 640
 641        src_templates = *pdata32 + sizeof(*p);
 642        dst_templates = data64 + sizeof(*p) + 4;
 643        memcpy(dst_templates, src_templates, optlen - sizeof(*p));
 644
 645        kfree(*pdata32);
 646        *pdata32 = data64;
 647        return 0;
 648}
 649
 650static struct xfrm_translator xfrm_translator = {
 651        .owner                          = THIS_MODULE,
 652        .alloc_compat                   = xfrm_alloc_compat,
 653        .rcv_msg_compat                 = xfrm_user_rcv_msg_compat,
 654        .xlate_user_policy_sockptr      = xfrm_user_policy_compat,
 655};
 656
 657static int __init xfrm_compat_init(void)
 658{
 659        return xfrm_register_translator(&xfrm_translator);
 660}
 661
 662static void __exit xfrm_compat_exit(void)
 663{
 664        xfrm_unregister_translator(&xfrm_translator);
 665}
 666
 667module_init(xfrm_compat_init);
 668module_exit(xfrm_compat_exit);
 669MODULE_LICENSE("GPL");
 670MODULE_AUTHOR("Dmitry Safonov");
 671MODULE_DESCRIPTION("XFRM 32-bit compatibility layer");
 672