linux/fs/nfs_common/nfsacl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * fs/nfs_common/nfsacl.c
   4 *
   5 *  Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
   6 */
   7
   8/*
   9 * The Solaris nfsacl protocol represents some ACLs slightly differently
  10 * than POSIX 1003.1e draft 17 does (and we do):
  11 *
  12 *  - Minimal ACLs always have an ACL_MASK entry, so they have
  13 *    four instead of three entries.
  14 *  - The ACL_MASK entry in such minimal ACLs always has the same
  15 *    permissions as the ACL_GROUP_OBJ entry. (In extended ACLs
  16 *    the ACL_MASK and ACL_GROUP_OBJ entries may differ.)
  17 *  - The identifier fields of the ACL_USER_OBJ and ACL_GROUP_OBJ
  18 *    entries contain the identifiers of the owner and owning group.
  19 *    (In POSIX ACLs we always set them to ACL_UNDEFINED_ID).
  20 *  - ACL entries in the kernel are kept sorted in ascending order
  21 *    of (e_tag, e_id). Solaris ACLs are unsorted.
  22 */
  23
  24#include <linux/module.h>
  25#include <linux/fs.h>
  26#include <linux/gfp.h>
  27#include <linux/sunrpc/xdr.h>
  28#include <linux/nfsacl.h>
  29#include <linux/nfs3.h>
  30#include <linux/sort.h>
  31
  32MODULE_LICENSE("GPL");
  33
  34struct nfsacl_encode_desc {
  35        struct xdr_array2_desc desc;
  36        unsigned int count;
  37        struct posix_acl *acl;
  38        int typeflag;
  39        kuid_t uid;
  40        kgid_t gid;
  41};
  42
  43struct nfsacl_simple_acl {
  44        struct posix_acl acl;
  45        struct posix_acl_entry ace[4];
  46};
  47
  48static int
  49xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)
  50{
  51        struct nfsacl_encode_desc *nfsacl_desc =
  52                (struct nfsacl_encode_desc *) desc;
  53        __be32 *p = elem;
  54
  55        struct posix_acl_entry *entry =
  56                &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
  57
  58        *p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);
  59        switch(entry->e_tag) {
  60                case ACL_USER_OBJ:
  61                        *p++ = htonl(from_kuid(&init_user_ns, nfsacl_desc->uid));
  62                        break;
  63                case ACL_GROUP_OBJ:
  64                        *p++ = htonl(from_kgid(&init_user_ns, nfsacl_desc->gid));
  65                        break;
  66                case ACL_USER:
  67                        *p++ = htonl(from_kuid(&init_user_ns, entry->e_uid));
  68                        break;
  69                case ACL_GROUP:
  70                        *p++ = htonl(from_kgid(&init_user_ns, entry->e_gid));
  71                        break;
  72                default:  /* Solaris depends on that! */
  73                        *p++ = 0;
  74                        break;
  75        }
  76        *p++ = htonl(entry->e_perm & S_IRWXO);
  77        return 0;
  78}
  79
  80/**
  81 * nfsacl_encode - Encode an NFSv3 ACL
  82 *
  83 * @buf: destination xdr_buf to contain XDR encoded ACL
  84 * @base: byte offset in xdr_buf where XDR'd ACL begins
  85 * @inode: inode of file whose ACL this is
  86 * @acl: posix_acl to encode
  87 * @encode_entries: whether to encode ACEs as well
  88 * @typeflag: ACL type: NFS_ACL_DEFAULT or zero
  89 *
  90 * Returns size of encoded ACL in bytes or a negative errno value.
  91 */
  92int nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
  93                  struct posix_acl *acl, int encode_entries, int typeflag)
  94{
  95        int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
  96        struct nfsacl_encode_desc nfsacl_desc = {
  97                .desc = {
  98                        .elem_size = 12,
  99                        .array_len = encode_entries ? entries : 0,
 100                        .xcode = xdr_nfsace_encode,
 101                },
 102                .acl = acl,
 103                .typeflag = typeflag,
 104                .uid = inode->i_uid,
 105                .gid = inode->i_gid,
 106        };
 107        struct nfsacl_simple_acl aclbuf;
 108        int err;
 109
 110        if (entries > NFS_ACL_MAX_ENTRIES ||
 111            xdr_encode_word(buf, base, entries))
 112                return -EINVAL;
 113        if (encode_entries && acl && acl->a_count == 3) {
 114                struct posix_acl *acl2 = &aclbuf.acl;
 115
 116                /* Avoid the use of posix_acl_alloc().  nfsacl_encode() is
 117                 * invoked in contexts where a memory allocation failure is
 118                 * fatal.  Fortunately this fake ACL is small enough to
 119                 * construct on the stack. */
 120                posix_acl_init(acl2, 4);
 121
 122                /* Insert entries in canonical order: other orders seem
 123                 to confuse Solaris VxFS. */
 124                acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */
 125                acl2->a_entries[1] = acl->a_entries[1];  /* ACL_GROUP_OBJ */
 126                acl2->a_entries[2] = acl->a_entries[1];  /* ACL_MASK */
 127                acl2->a_entries[2].e_tag = ACL_MASK;
 128                acl2->a_entries[3] = acl->a_entries[2];  /* ACL_OTHER */
 129                nfsacl_desc.acl = acl2;
 130        }
 131        err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc);
 132        if (!err)
 133                err = 8 + nfsacl_desc.desc.elem_size *
 134                          nfsacl_desc.desc.array_len;
 135        return err;
 136}
 137EXPORT_SYMBOL_GPL(nfsacl_encode);
 138
 139/**
 140 * nfs_stream_encode_acl - Encode an NFSv3 ACL
 141 *
 142 * @xdr: an xdr_stream positioned to receive an encoded ACL
 143 * @inode: inode of file whose ACL this is
 144 * @acl: posix_acl to encode
 145 * @encode_entries: whether to encode ACEs as well
 146 * @typeflag: ACL type: NFS_ACL_DEFAULT or zero
 147 *
 148 * Return values:
 149 *   %false: The ACL could not be encoded
 150 *   %true: @xdr is advanced to the next available position
 151 */
 152bool nfs_stream_encode_acl(struct xdr_stream *xdr, struct inode *inode,
 153                           struct posix_acl *acl, int encode_entries,
 154                           int typeflag)
 155{
 156        const size_t elem_size = XDR_UNIT * 3;
 157        u32 entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
 158        struct nfsacl_encode_desc nfsacl_desc = {
 159                .desc = {
 160                        .elem_size = elem_size,
 161                        .array_len = encode_entries ? entries : 0,
 162                        .xcode = xdr_nfsace_encode,
 163                },
 164                .acl = acl,
 165                .typeflag = typeflag,
 166                .uid = inode->i_uid,
 167                .gid = inode->i_gid,
 168        };
 169        struct nfsacl_simple_acl aclbuf;
 170        unsigned int base;
 171        int err;
 172
 173        if (entries > NFS_ACL_MAX_ENTRIES)
 174                return false;
 175        if (xdr_stream_encode_u32(xdr, entries) < 0)
 176                return false;
 177
 178        if (encode_entries && acl && acl->a_count == 3) {
 179                struct posix_acl *acl2 = &aclbuf.acl;
 180
 181                /* Avoid the use of posix_acl_alloc().  nfsacl_encode() is
 182                 * invoked in contexts where a memory allocation failure is
 183                 * fatal.  Fortunately this fake ACL is small enough to
 184                 * construct on the stack. */
 185                posix_acl_init(acl2, 4);
 186
 187                /* Insert entries in canonical order: other orders seem
 188                 to confuse Solaris VxFS. */
 189                acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */
 190                acl2->a_entries[1] = acl->a_entries[1];  /* ACL_GROUP_OBJ */
 191                acl2->a_entries[2] = acl->a_entries[1];  /* ACL_MASK */
 192                acl2->a_entries[2].e_tag = ACL_MASK;
 193                acl2->a_entries[3] = acl->a_entries[2];  /* ACL_OTHER */
 194                nfsacl_desc.acl = acl2;
 195        }
 196
 197        base = xdr_stream_pos(xdr);
 198        if (!xdr_reserve_space(xdr, XDR_UNIT +
 199                               elem_size * nfsacl_desc.desc.array_len))
 200                return false;
 201        err = xdr_encode_array2(xdr->buf, base, &nfsacl_desc.desc);
 202        if (err)
 203                return false;
 204
 205        return true;
 206}
 207EXPORT_SYMBOL_GPL(nfs_stream_encode_acl);
 208
 209
 210struct nfsacl_decode_desc {
 211        struct xdr_array2_desc desc;
 212        unsigned int count;
 213        struct posix_acl *acl;
 214};
 215
 216static int
 217xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)
 218{
 219        struct nfsacl_decode_desc *nfsacl_desc =
 220                (struct nfsacl_decode_desc *) desc;
 221        __be32 *p = elem;
 222        struct posix_acl_entry *entry;
 223        unsigned int id;
 224
 225        if (!nfsacl_desc->acl) {
 226                if (desc->array_len > NFS_ACL_MAX_ENTRIES)
 227                        return -EINVAL;
 228                nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL);
 229                if (!nfsacl_desc->acl)
 230                        return -ENOMEM;
 231                nfsacl_desc->count = 0;
 232        }
 233
 234        entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
 235        entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT;
 236        id = ntohl(*p++);
 237        entry->e_perm = ntohl(*p++);
 238
 239        switch(entry->e_tag) {
 240                case ACL_USER:
 241                        entry->e_uid = make_kuid(&init_user_ns, id);
 242                        if (!uid_valid(entry->e_uid))
 243                                return -EINVAL;
 244                        break;
 245                case ACL_GROUP:
 246                        entry->e_gid = make_kgid(&init_user_ns, id);
 247                        if (!gid_valid(entry->e_gid))
 248                                return -EINVAL;
 249                        break;
 250                case ACL_USER_OBJ:
 251                case ACL_GROUP_OBJ:
 252                case ACL_OTHER:
 253                        if (entry->e_perm & ~S_IRWXO)
 254                                return -EINVAL;
 255                        break;
 256                case ACL_MASK:
 257                        /* Solaris sometimes sets additional bits in the mask */
 258                        entry->e_perm &= S_IRWXO;
 259                        break;
 260                default:
 261                        return -EINVAL;
 262        }
 263
 264        return 0;
 265}
 266
 267static int
 268cmp_acl_entry(const void *x, const void *y)
 269{
 270        const struct posix_acl_entry *a = x, *b = y;
 271
 272        if (a->e_tag != b->e_tag)
 273                return a->e_tag - b->e_tag;
 274        else if ((a->e_tag == ACL_USER) && uid_gt(a->e_uid, b->e_uid))
 275                return 1;
 276        else if ((a->e_tag == ACL_USER) && uid_lt(a->e_uid, b->e_uid))
 277                return -1;
 278        else if ((a->e_tag == ACL_GROUP) && gid_gt(a->e_gid, b->e_gid))
 279                return 1;
 280        else if ((a->e_tag == ACL_GROUP) && gid_lt(a->e_gid, b->e_gid))
 281                return -1;
 282        else
 283                return 0;
 284}
 285
 286/*
 287 * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL.
 288 */
 289static int
 290posix_acl_from_nfsacl(struct posix_acl *acl)
 291{
 292        struct posix_acl_entry *pa, *pe,
 293               *group_obj = NULL, *mask = NULL;
 294
 295        if (!acl)
 296                return 0;
 297
 298        sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry),
 299             cmp_acl_entry, NULL);
 300
 301        /* Find the ACL_GROUP_OBJ and ACL_MASK entries. */
 302        FOREACH_ACL_ENTRY(pa, acl, pe) {
 303                switch(pa->e_tag) {
 304                        case ACL_USER_OBJ:
 305                                break;
 306                        case ACL_GROUP_OBJ:
 307                                group_obj = pa;
 308                                break;
 309                        case ACL_MASK:
 310                                mask = pa;
 311                                fallthrough;
 312                        case ACL_OTHER:
 313                                break;
 314                }
 315        }
 316        if (acl->a_count == 4 && group_obj && mask &&
 317            mask->e_perm == group_obj->e_perm) {
 318                /* remove bogus ACL_MASK entry */
 319                memmove(mask, mask+1, (3 - (mask - acl->a_entries)) *
 320                                      sizeof(struct posix_acl_entry));
 321                acl->a_count = 3;
 322        }
 323        return 0;
 324}
 325
 326/**
 327 * nfsacl_decode - Decode an NFSv3 ACL
 328 *
 329 * @buf: xdr_buf containing XDR'd ACL data to decode
 330 * @base: byte offset in xdr_buf where XDR'd ACL begins
 331 * @aclcnt: count of ACEs in decoded posix_acl
 332 * @pacl: buffer in which to place decoded posix_acl
 333 *
 334 * Returns the length of the decoded ACL in bytes, or a negative errno value.
 335 */
 336int nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
 337                  struct posix_acl **pacl)
 338{
 339        struct nfsacl_decode_desc nfsacl_desc = {
 340                .desc = {
 341                        .elem_size = 12,
 342                        .xcode = pacl ? xdr_nfsace_decode : NULL,
 343                },
 344        };
 345        u32 entries;
 346        int err;
 347
 348        if (xdr_decode_word(buf, base, &entries) ||
 349            entries > NFS_ACL_MAX_ENTRIES)
 350                return -EINVAL;
 351        nfsacl_desc.desc.array_maxlen = entries;
 352        err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc);
 353        if (err)
 354                return err;
 355        if (pacl) {
 356                if (entries != nfsacl_desc.desc.array_len ||
 357                    posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
 358                        posix_acl_release(nfsacl_desc.acl);
 359                        return -EINVAL;
 360                }
 361                *pacl = nfsacl_desc.acl;
 362        }
 363        if (aclcnt)
 364                *aclcnt = entries;
 365        return 8 + nfsacl_desc.desc.elem_size *
 366                   nfsacl_desc.desc.array_len;
 367}
 368EXPORT_SYMBOL_GPL(nfsacl_decode);
 369
 370/**
 371 * nfs_stream_decode_acl - Decode an NFSv3 ACL
 372 *
 373 * @xdr: an xdr_stream positioned at an encoded ACL
 374 * @aclcnt: OUT: count of ACEs in decoded posix_acl
 375 * @pacl: OUT: a dynamically-allocated buffer containing the decoded posix_acl
 376 *
 377 * Return values:
 378 *   %false: The encoded ACL is not valid
 379 *   %true: @pacl contains a decoded ACL, and @xdr is advanced
 380 *
 381 * On a successful return, caller must release *pacl using posix_acl_release().
 382 */
 383bool nfs_stream_decode_acl(struct xdr_stream *xdr, unsigned int *aclcnt,
 384                           struct posix_acl **pacl)
 385{
 386        const size_t elem_size = XDR_UNIT * 3;
 387        struct nfsacl_decode_desc nfsacl_desc = {
 388                .desc = {
 389                        .elem_size = elem_size,
 390                        .xcode = pacl ? xdr_nfsace_decode : NULL,
 391                },
 392        };
 393        unsigned int base;
 394        u32 entries;
 395
 396        if (xdr_stream_decode_u32(xdr, &entries) < 0)
 397                return false;
 398        if (entries > NFS_ACL_MAX_ENTRIES)
 399                return false;
 400
 401        base = xdr_stream_pos(xdr);
 402        if (!xdr_inline_decode(xdr, XDR_UNIT + elem_size * entries))
 403                return false;
 404        nfsacl_desc.desc.array_maxlen = entries;
 405        if (xdr_decode_array2(xdr->buf, base, &nfsacl_desc.desc))
 406                return false;
 407
 408        if (pacl) {
 409                if (entries != nfsacl_desc.desc.array_len ||
 410                    posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
 411                        posix_acl_release(nfsacl_desc.acl);
 412                        return false;
 413                }
 414                *pacl = nfsacl_desc.acl;
 415        }
 416        if (aclcnt)
 417                *aclcnt = entries;
 418        return true;
 419}
 420EXPORT_SYMBOL_GPL(nfs_stream_decode_acl);
 421