xref: /openbmc/linux/fs/nfs_common/nfsacl.c (revision b627b4ed)
1 /*
2  * fs/nfs_common/nfsacl.c
3  *
4  *  Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
5  */
6 
7 /*
8  * The Solaris nfsacl protocol represents some ACLs slightly differently
9  * than POSIX 1003.1e draft 17 does (and we do):
10  *
11  *  - Minimal ACLs always have an ACL_MASK entry, so they have
12  *    four instead of three entries.
13  *  - The ACL_MASK entry in such minimal ACLs always has the same
14  *    permissions as the ACL_GROUP_OBJ entry. (In extended ACLs
15  *    the ACL_MASK and ACL_GROUP_OBJ entries may differ.)
16  *  - The identifier fields of the ACL_USER_OBJ and ACL_GROUP_OBJ
17  *    entries contain the identifiers of the owner and owning group.
18  *    (In POSIX ACLs we always set them to ACL_UNDEFINED_ID).
19  *  - ACL entries in the kernel are kept sorted in ascending order
20  *    of (e_tag, e_id). Solaris ACLs are unsorted.
21  */
22 
23 #include <linux/module.h>
24 #include <linux/fs.h>
25 #include <linux/sunrpc/xdr.h>
26 #include <linux/nfsacl.h>
27 #include <linux/nfs3.h>
28 #include <linux/sort.h>
29 
30 MODULE_LICENSE("GPL");
31 
32 EXPORT_SYMBOL_GPL(nfsacl_encode);
33 EXPORT_SYMBOL_GPL(nfsacl_decode);
34 
35 struct nfsacl_encode_desc {
36 	struct xdr_array2_desc desc;
37 	unsigned int count;
38 	struct posix_acl *acl;
39 	int typeflag;
40 	uid_t uid;
41 	gid_t gid;
42 };
43 
44 static int
45 xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)
46 {
47 	struct nfsacl_encode_desc *nfsacl_desc =
48 		(struct nfsacl_encode_desc *) desc;
49 	__be32 *p = elem;
50 
51 	struct posix_acl_entry *entry =
52 		&nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
53 
54 	*p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);
55 	switch(entry->e_tag) {
56 		case ACL_USER_OBJ:
57 			*p++ = htonl(nfsacl_desc->uid);
58 			break;
59 		case ACL_GROUP_OBJ:
60 			*p++ = htonl(nfsacl_desc->gid);
61 			break;
62 		case ACL_USER:
63 		case ACL_GROUP:
64 			*p++ = htonl(entry->e_id);
65 			break;
66 		default:  /* Solaris depends on that! */
67 			*p++ = 0;
68 			break;
69 	}
70 	*p++ = htonl(entry->e_perm & S_IRWXO);
71 	return 0;
72 }
73 
74 unsigned int
75 nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
76 	      struct posix_acl *acl, int encode_entries, int typeflag)
77 {
78 	int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
79 	struct nfsacl_encode_desc nfsacl_desc = {
80 		.desc = {
81 			.elem_size = 12,
82 			.array_len = encode_entries ? entries : 0,
83 			.xcode = xdr_nfsace_encode,
84 		},
85 		.acl = acl,
86 		.typeflag = typeflag,
87 		.uid = inode->i_uid,
88 		.gid = inode->i_gid,
89 	};
90 	int err;
91 	struct posix_acl *acl2 = NULL;
92 
93 	if (entries > NFS_ACL_MAX_ENTRIES ||
94 	    xdr_encode_word(buf, base, entries))
95 		return -EINVAL;
96 	if (encode_entries && acl && acl->a_count == 3) {
97 		/* Fake up an ACL_MASK entry. */
98 		acl2 = posix_acl_alloc(4, GFP_KERNEL);
99 		if (!acl2)
100 			return -ENOMEM;
101 		/* Insert entries in canonical order: other orders seem
102 		 to confuse Solaris VxFS. */
103 		acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */
104 		acl2->a_entries[1] = acl->a_entries[1];  /* ACL_GROUP_OBJ */
105 		acl2->a_entries[2] = acl->a_entries[1];  /* ACL_MASK */
106 		acl2->a_entries[2].e_tag = ACL_MASK;
107 		acl2->a_entries[3] = acl->a_entries[2];  /* ACL_OTHER */
108 		nfsacl_desc.acl = acl2;
109 	}
110 	err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc);
111 	if (acl2)
112 		posix_acl_release(acl2);
113 	if (!err)
114 		err = 8 + nfsacl_desc.desc.elem_size *
115 			  nfsacl_desc.desc.array_len;
116 	return err;
117 }
118 
119 struct nfsacl_decode_desc {
120 	struct xdr_array2_desc desc;
121 	unsigned int count;
122 	struct posix_acl *acl;
123 };
124 
125 static int
126 xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)
127 {
128 	struct nfsacl_decode_desc *nfsacl_desc =
129 		(struct nfsacl_decode_desc *) desc;
130 	__be32 *p = elem;
131 	struct posix_acl_entry *entry;
132 
133 	if (!nfsacl_desc->acl) {
134 		if (desc->array_len > NFS_ACL_MAX_ENTRIES)
135 			return -EINVAL;
136 		nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL);
137 		if (!nfsacl_desc->acl)
138 			return -ENOMEM;
139 		nfsacl_desc->count = 0;
140 	}
141 
142 	entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
143 	entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT;
144 	entry->e_id = ntohl(*p++);
145 	entry->e_perm = ntohl(*p++);
146 
147 	switch(entry->e_tag) {
148 		case ACL_USER_OBJ:
149 		case ACL_USER:
150 		case ACL_GROUP_OBJ:
151 		case ACL_GROUP:
152 		case ACL_OTHER:
153 			if (entry->e_perm & ~S_IRWXO)
154 				return -EINVAL;
155 			break;
156 		case ACL_MASK:
157 			/* Solaris sometimes sets additonal bits in the mask */
158 			entry->e_perm &= S_IRWXO;
159 			break;
160 		default:
161 			return -EINVAL;
162 	}
163 
164 	return 0;
165 }
166 
167 static int
168 cmp_acl_entry(const void *x, const void *y)
169 {
170 	const struct posix_acl_entry *a = x, *b = y;
171 
172 	if (a->e_tag != b->e_tag)
173 		return a->e_tag - b->e_tag;
174 	else if (a->e_id > b->e_id)
175 		return 1;
176 	else if (a->e_id < b->e_id)
177 		return -1;
178 	else
179 		return 0;
180 }
181 
182 /*
183  * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL.
184  */
185 static int
186 posix_acl_from_nfsacl(struct posix_acl *acl)
187 {
188 	struct posix_acl_entry *pa, *pe,
189 	       *group_obj = NULL, *mask = NULL;
190 
191 	if (!acl)
192 		return 0;
193 
194 	sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry),
195 	     cmp_acl_entry, NULL);
196 
197 	/* Clear undefined identifier fields and find the ACL_GROUP_OBJ
198 	   and ACL_MASK entries. */
199 	FOREACH_ACL_ENTRY(pa, acl, pe) {
200 		switch(pa->e_tag) {
201 			case ACL_USER_OBJ:
202 				pa->e_id = ACL_UNDEFINED_ID;
203 				break;
204 			case ACL_GROUP_OBJ:
205 				pa->e_id = ACL_UNDEFINED_ID;
206 				group_obj = pa;
207 				break;
208 			case ACL_MASK:
209 				mask = pa;
210 				/* fall through */
211 			case ACL_OTHER:
212 				pa->e_id = ACL_UNDEFINED_ID;
213 				break;
214 		}
215 	}
216 	if (acl->a_count == 4 && group_obj && mask &&
217 	    mask->e_perm == group_obj->e_perm) {
218 		/* remove bogus ACL_MASK entry */
219 		memmove(mask, mask+1, (3 - (mask - acl->a_entries)) *
220 				      sizeof(struct posix_acl_entry));
221 		acl->a_count = 3;
222 	}
223 	return 0;
224 }
225 
226 unsigned int
227 nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
228 	      struct posix_acl **pacl)
229 {
230 	struct nfsacl_decode_desc nfsacl_desc = {
231 		.desc = {
232 			.elem_size = 12,
233 			.xcode = pacl ? xdr_nfsace_decode : NULL,
234 		},
235 	};
236 	u32 entries;
237 	int err;
238 
239 	if (xdr_decode_word(buf, base, &entries) ||
240 	    entries > NFS_ACL_MAX_ENTRIES)
241 		return -EINVAL;
242 	nfsacl_desc.desc.array_maxlen = entries;
243 	err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc);
244 	if (err)
245 		return err;
246 	if (pacl) {
247 		if (entries != nfsacl_desc.desc.array_len ||
248 		    posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
249 			posix_acl_release(nfsacl_desc.acl);
250 			return -EINVAL;
251 		}
252 		*pacl = nfsacl_desc.acl;
253 	}
254 	if (aclcnt)
255 		*aclcnt = entries;
256 	return 8 + nfsacl_desc.desc.elem_size *
257 		   nfsacl_desc.desc.array_len;
258 }
259