nfsacl.c (11406B)
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);