xref: /freebsd/sys/fs/nfs/nfs_commonacl.c (revision 076ad2f8)
1 /*-
2  * Copyright (c) 2009 Rick Macklem, University of Guelph
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #ifndef APPLEKEXT
32 #include <fs/nfs/nfsport.h>
33 
34 extern int nfsrv_useacl;
35 #endif
36 
37 static int nfsrv_acemasktoperm(u_int32_t acetype, u_int32_t mask, int owner,
38     enum vtype type, acl_perm_t *permp);
39 
40 /*
41  * Handle xdr for an ace.
42  */
43 APPLESTATIC int
44 nfsrv_dissectace(struct nfsrv_descript *nd, struct acl_entry *acep,
45     int *aceerrp, int *acesizep, NFSPROC_T *p)
46 {
47 	u_int32_t *tl;
48 	int len, gotid = 0, owner = 0, error = 0, aceerr = 0;
49 	u_char *name, namestr[NFSV4_SMALLSTR + 1];
50 	u_int32_t flag, mask, acetype;
51 	gid_t gid;
52 	uid_t uid;
53 
54 	*aceerrp = 0;
55 	acep->ae_flags = 0;
56 	NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
57 	acetype = fxdr_unsigned(u_int32_t, *tl++);
58 	flag = fxdr_unsigned(u_int32_t, *tl++);
59 	mask = fxdr_unsigned(u_int32_t, *tl++);
60 	len = fxdr_unsigned(int, *tl);
61 	if (len < 0) {
62 		error = NFSERR_BADXDR;
63 		goto nfsmout;
64 	} else if (len == 0) {
65 		/* Netapp filers return a 0 length who for nil users */
66 		acep->ae_tag = ACL_UNDEFINED_TAG;
67 		acep->ae_id = ACL_UNDEFINED_ID;
68 		acep->ae_perm = (acl_perm_t)0;
69 		acep->ae_entry_type = ACL_ENTRY_TYPE_DENY;
70 		if (acesizep)
71 			*acesizep = 4 * NFSX_UNSIGNED;
72 		error = 0;
73 		goto nfsmout;
74 	}
75 	if (len > NFSV4_SMALLSTR)
76 		name = malloc(len + 1, M_NFSSTRING, M_WAITOK);
77 	else
78 		name = namestr;
79 	error = nfsrv_mtostr(nd, name, len);
80 	if (error) {
81 		if (len > NFSV4_SMALLSTR)
82 			free(name, M_NFSSTRING);
83 		goto nfsmout;
84 	}
85 	if (len == 6) {
86 		if (!NFSBCMP(name, "OWNER@", 6)) {
87 			acep->ae_tag = ACL_USER_OBJ;
88 			acep->ae_id = ACL_UNDEFINED_ID;
89 			owner = 1;
90 			gotid = 1;
91 		} else if (!NFSBCMP(name, "GROUP@", 6)) {
92 			acep->ae_tag = ACL_GROUP_OBJ;
93 			acep->ae_id = ACL_UNDEFINED_ID;
94 			gotid = 1;
95 		}
96 	} else if (len == 9 && !NFSBCMP(name, "EVERYONE@", 9)) {
97 		acep->ae_tag = ACL_EVERYONE;
98 		acep->ae_id = ACL_UNDEFINED_ID;
99 		gotid = 1;
100 	}
101 	if (gotid == 0) {
102 		if (flag & NFSV4ACE_IDENTIFIERGROUP) {
103 			acep->ae_tag = ACL_GROUP;
104 			aceerr = nfsv4_strtogid(nd, name, len, &gid, p);
105 			if (aceerr == 0)
106 				acep->ae_id = (uid_t)gid;
107 		} else {
108 			acep->ae_tag = ACL_USER;
109 			aceerr = nfsv4_strtouid(nd, name, len, &uid, p);
110 			if (aceerr == 0)
111 				acep->ae_id = uid;
112 		}
113 	}
114 	if (len > NFSV4_SMALLSTR)
115 		free(name, M_NFSSTRING);
116 
117 	if (aceerr == 0) {
118 		/*
119 		 * Handle the flags.
120 		 */
121 		flag &= ~NFSV4ACE_IDENTIFIERGROUP;
122 		if (flag & NFSV4ACE_FILEINHERIT) {
123 			flag &= ~NFSV4ACE_FILEINHERIT;
124 			acep->ae_flags |= ACL_ENTRY_FILE_INHERIT;
125 		}
126 		if (flag & NFSV4ACE_DIRECTORYINHERIT) {
127 			flag &= ~NFSV4ACE_DIRECTORYINHERIT;
128 			acep->ae_flags |= ACL_ENTRY_DIRECTORY_INHERIT;
129 		}
130 		if (flag & NFSV4ACE_NOPROPAGATEINHERIT) {
131 			flag &= ~NFSV4ACE_NOPROPAGATEINHERIT;
132 			acep->ae_flags |= ACL_ENTRY_NO_PROPAGATE_INHERIT;
133 		}
134 		if (flag & NFSV4ACE_INHERITONLY) {
135 			flag &= ~NFSV4ACE_INHERITONLY;
136 			acep->ae_flags |= ACL_ENTRY_INHERIT_ONLY;
137 		}
138 		if (flag & NFSV4ACE_SUCCESSFULACCESS) {
139 			flag &= ~NFSV4ACE_SUCCESSFULACCESS;
140 			acep->ae_flags |= ACL_ENTRY_SUCCESSFUL_ACCESS;
141 		}
142 		if (flag & NFSV4ACE_FAILEDACCESS) {
143 			flag &= ~NFSV4ACE_FAILEDACCESS;
144 			acep->ae_flags |= ACL_ENTRY_FAILED_ACCESS;
145 		}
146 		/*
147 		 * Set ae_entry_type.
148 		 */
149 		if (acetype == NFSV4ACE_ALLOWEDTYPE)
150 			acep->ae_entry_type = ACL_ENTRY_TYPE_ALLOW;
151 		else if (acetype == NFSV4ACE_DENIEDTYPE)
152 			acep->ae_entry_type = ACL_ENTRY_TYPE_DENY;
153 		else if (acetype == NFSV4ACE_AUDITTYPE)
154 			acep->ae_entry_type = ACL_ENTRY_TYPE_AUDIT;
155 		else if (acetype == NFSV4ACE_ALARMTYPE)
156 			acep->ae_entry_type = ACL_ENTRY_TYPE_ALARM;
157 		else
158 			aceerr = NFSERR_ATTRNOTSUPP;
159 	}
160 
161 	/*
162 	 * Now, check for unsupported flag bits.
163 	 */
164 	if (aceerr == 0 && flag != 0)
165 		aceerr = NFSERR_ATTRNOTSUPP;
166 
167 	/*
168 	 * And turn the mask into perm bits.
169 	 */
170 	if (aceerr == 0)
171 		aceerr = nfsrv_acemasktoperm(acetype, mask, owner, VREG,
172 		    &acep->ae_perm);
173 	*aceerrp = aceerr;
174 	if (acesizep)
175 		*acesizep = NFSM_RNDUP(len) + (4 * NFSX_UNSIGNED);
176 	error = 0;
177 nfsmout:
178 	NFSEXITCODE(error);
179 	return (error);
180 }
181 
182 /*
183  * Turn an NFSv4 ace mask into R/W/X flag bits.
184  */
185 static int
186 nfsrv_acemasktoperm(u_int32_t acetype, u_int32_t mask, int owner,
187     enum vtype type, acl_perm_t *permp)
188 {
189 	acl_perm_t perm = 0x0;
190 	int error = 0;
191 
192 	if (mask & NFSV4ACE_READDATA) {
193 		mask &= ~NFSV4ACE_READDATA;
194 		perm |= ACL_READ_DATA;
195 	}
196 	if (mask & NFSV4ACE_LISTDIRECTORY) {
197 		mask &= ~NFSV4ACE_LISTDIRECTORY;
198 		perm |= ACL_LIST_DIRECTORY;
199 	}
200 	if (mask & NFSV4ACE_WRITEDATA) {
201 		mask &= ~NFSV4ACE_WRITEDATA;
202 		perm |= ACL_WRITE_DATA;
203 	}
204 	if (mask & NFSV4ACE_ADDFILE) {
205 		mask &= ~NFSV4ACE_ADDFILE;
206 		perm |= ACL_ADD_FILE;
207 	}
208 	if (mask & NFSV4ACE_APPENDDATA) {
209 		mask &= ~NFSV4ACE_APPENDDATA;
210 		perm |= ACL_APPEND_DATA;
211 	}
212 	if (mask & NFSV4ACE_ADDSUBDIRECTORY) {
213 		mask &= ~NFSV4ACE_ADDSUBDIRECTORY;
214 		perm |= ACL_ADD_SUBDIRECTORY;
215 	}
216 	if (mask & NFSV4ACE_READNAMEDATTR) {
217 		mask &= ~NFSV4ACE_READNAMEDATTR;
218 		perm |= ACL_READ_NAMED_ATTRS;
219 	}
220 	if (mask & NFSV4ACE_WRITENAMEDATTR) {
221 		mask &= ~NFSV4ACE_WRITENAMEDATTR;
222 		perm |= ACL_WRITE_NAMED_ATTRS;
223 	}
224 	if (mask & NFSV4ACE_EXECUTE) {
225 		mask &= ~NFSV4ACE_EXECUTE;
226 		perm |= ACL_EXECUTE;
227 	}
228 	if (mask & NFSV4ACE_SEARCH) {
229 		mask &= ~NFSV4ACE_SEARCH;
230 		perm |= ACL_EXECUTE;
231 	}
232 	if (mask & NFSV4ACE_DELETECHILD) {
233 		mask &= ~NFSV4ACE_DELETECHILD;
234 		perm |= ACL_DELETE_CHILD;
235 	}
236 	if (mask & NFSV4ACE_READATTRIBUTES) {
237 		mask &= ~NFSV4ACE_READATTRIBUTES;
238 		perm |= ACL_READ_ATTRIBUTES;
239 	}
240 	if (mask & NFSV4ACE_WRITEATTRIBUTES) {
241 		mask &= ~NFSV4ACE_WRITEATTRIBUTES;
242 		perm |= ACL_WRITE_ATTRIBUTES;
243 	}
244 	if (mask & NFSV4ACE_DELETE) {
245 		mask &= ~NFSV4ACE_DELETE;
246 		perm |= ACL_DELETE;
247 	}
248 	if (mask & NFSV4ACE_READACL) {
249 		mask &= ~NFSV4ACE_READACL;
250 		perm |= ACL_READ_ACL;
251 	}
252 	if (mask & NFSV4ACE_WRITEACL) {
253 		mask &= ~NFSV4ACE_WRITEACL;
254 		perm |= ACL_WRITE_ACL;
255 	}
256 	if (mask & NFSV4ACE_WRITEOWNER) {
257 		mask &= ~NFSV4ACE_WRITEOWNER;
258 		perm |= ACL_WRITE_OWNER;
259 	}
260 	if (mask & NFSV4ACE_SYNCHRONIZE) {
261 		mask &= ~NFSV4ACE_SYNCHRONIZE;
262 		perm |= ACL_SYNCHRONIZE;
263 	}
264 	if (mask != 0) {
265 		error = NFSERR_ATTRNOTSUPP;
266 		goto out;
267 	}
268 	*permp = perm;
269 
270 out:
271 	NFSEXITCODE(error);
272 	return (error);
273 }
274 
275 /* local functions */
276 static int nfsrv_buildace(struct nfsrv_descript *, u_char *, int,
277     enum vtype, int, int, struct acl_entry *);
278 
279 /*
280  * This function builds an NFS ace.
281  */
282 static int
283 nfsrv_buildace(struct nfsrv_descript *nd, u_char *name, int namelen,
284     enum vtype type, int group, int owner, struct acl_entry *ace)
285 {
286 	u_int32_t *tl, aceflag = 0x0, acemask = 0x0, acetype;
287 	int full_len;
288 
289 	full_len = NFSM_RNDUP(namelen);
290 	NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED + full_len);
291 
292 	/*
293 	 * Fill in the ace type.
294 	 */
295 	if (ace->ae_entry_type & ACL_ENTRY_TYPE_ALLOW)
296 		acetype = NFSV4ACE_ALLOWEDTYPE;
297 	else if (ace->ae_entry_type & ACL_ENTRY_TYPE_DENY)
298 		acetype = NFSV4ACE_DENIEDTYPE;
299 	else if (ace->ae_entry_type & ACL_ENTRY_TYPE_AUDIT)
300 		acetype = NFSV4ACE_AUDITTYPE;
301 	else
302 		acetype = NFSV4ACE_ALARMTYPE;
303 	*tl++ = txdr_unsigned(acetype);
304 
305 	/*
306 	 * Set the flag bits from the ACL.
307 	 */
308 	if (ace->ae_flags & ACL_ENTRY_FILE_INHERIT)
309 		aceflag |= NFSV4ACE_FILEINHERIT;
310 	if (ace->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT)
311 		aceflag |= NFSV4ACE_DIRECTORYINHERIT;
312 	if (ace->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)
313 		aceflag |= NFSV4ACE_NOPROPAGATEINHERIT;
314 	if (ace->ae_flags & ACL_ENTRY_INHERIT_ONLY)
315 		aceflag |= NFSV4ACE_INHERITONLY;
316 	if (ace->ae_flags & ACL_ENTRY_SUCCESSFUL_ACCESS)
317 		aceflag |= NFSV4ACE_SUCCESSFULACCESS;
318 	if (ace->ae_flags & ACL_ENTRY_FAILED_ACCESS)
319 		aceflag |= NFSV4ACE_FAILEDACCESS;
320 	if (group)
321 		aceflag |= NFSV4ACE_IDENTIFIERGROUP;
322 	*tl++ = txdr_unsigned(aceflag);
323 	if (type == VDIR) {
324 		if (ace->ae_perm & ACL_LIST_DIRECTORY)
325 			acemask |= NFSV4ACE_LISTDIRECTORY;
326 		if (ace->ae_perm & ACL_ADD_FILE)
327 			acemask |= NFSV4ACE_ADDFILE;
328 		if (ace->ae_perm & ACL_ADD_SUBDIRECTORY)
329 			acemask |= NFSV4ACE_ADDSUBDIRECTORY;
330 		if (ace->ae_perm & ACL_READ_NAMED_ATTRS)
331 			acemask |= NFSV4ACE_READNAMEDATTR;
332 		if (ace->ae_perm & ACL_WRITE_NAMED_ATTRS)
333 			acemask |= NFSV4ACE_WRITENAMEDATTR;
334 		if (ace->ae_perm & ACL_EXECUTE)
335 			acemask |= NFSV4ACE_SEARCH;
336 		if (ace->ae_perm & ACL_DELETE_CHILD)
337 			acemask |= NFSV4ACE_DELETECHILD;
338 		if (ace->ae_perm & ACL_READ_ATTRIBUTES)
339 			acemask |= NFSV4ACE_READATTRIBUTES;
340 		if (ace->ae_perm & ACL_WRITE_ATTRIBUTES)
341 			acemask |= NFSV4ACE_WRITEATTRIBUTES;
342 		if (ace->ae_perm & ACL_DELETE)
343 			acemask |= NFSV4ACE_DELETE;
344 		if (ace->ae_perm & ACL_READ_ACL)
345 			acemask |= NFSV4ACE_READACL;
346 		if (ace->ae_perm & ACL_WRITE_ACL)
347 			acemask |= NFSV4ACE_WRITEACL;
348 		if (ace->ae_perm & ACL_WRITE_OWNER)
349 			acemask |= NFSV4ACE_WRITEOWNER;
350 		if (ace->ae_perm & ACL_SYNCHRONIZE)
351 			acemask |= NFSV4ACE_SYNCHRONIZE;
352 	} else {
353 		if (ace->ae_perm & ACL_READ_DATA)
354 			acemask |= NFSV4ACE_READDATA;
355 		if (ace->ae_perm & ACL_WRITE_DATA)
356 			acemask |= NFSV4ACE_WRITEDATA;
357 		if (ace->ae_perm & ACL_APPEND_DATA)
358 			acemask |= NFSV4ACE_APPENDDATA;
359 		if (ace->ae_perm & ACL_READ_NAMED_ATTRS)
360 			acemask |= NFSV4ACE_READNAMEDATTR;
361 		if (ace->ae_perm & ACL_WRITE_NAMED_ATTRS)
362 			acemask |= NFSV4ACE_WRITENAMEDATTR;
363 		if (ace->ae_perm & ACL_EXECUTE)
364 			acemask |= NFSV4ACE_EXECUTE;
365 		if (ace->ae_perm & ACL_READ_ATTRIBUTES)
366 			acemask |= NFSV4ACE_READATTRIBUTES;
367 		if (ace->ae_perm & ACL_WRITE_ATTRIBUTES)
368 			acemask |= NFSV4ACE_WRITEATTRIBUTES;
369 		if (ace->ae_perm & ACL_DELETE)
370 			acemask |= NFSV4ACE_DELETE;
371 		if (ace->ae_perm & ACL_READ_ACL)
372 			acemask |= NFSV4ACE_READACL;
373 		if (ace->ae_perm & ACL_WRITE_ACL)
374 			acemask |= NFSV4ACE_WRITEACL;
375 		if (ace->ae_perm & ACL_WRITE_OWNER)
376 			acemask |= NFSV4ACE_WRITEOWNER;
377 		if (ace->ae_perm & ACL_SYNCHRONIZE)
378 			acemask |= NFSV4ACE_SYNCHRONIZE;
379 	}
380 	*tl++ = txdr_unsigned(acemask);
381 	*tl++ = txdr_unsigned(namelen);
382 	if (full_len - namelen)
383 		*(tl + (namelen / NFSX_UNSIGNED)) = 0x0;
384 	NFSBCOPY(name, (caddr_t)tl, namelen);
385 	return (full_len + 4 * NFSX_UNSIGNED);
386 }
387 
388 /*
389  * Build an NFSv4 ACL.
390  */
391 APPLESTATIC int
392 nfsrv_buildacl(struct nfsrv_descript *nd, NFSACL_T *aclp, enum vtype type,
393     NFSPROC_T *p)
394 {
395 	int i, entrycnt = 0, retlen;
396 	u_int32_t *entrycntp;
397 	int isowner, isgroup, namelen, malloced;
398 	u_char *name, namestr[NFSV4_SMALLSTR];
399 
400 	NFSM_BUILD(entrycntp, u_int32_t *, NFSX_UNSIGNED);
401 	retlen = NFSX_UNSIGNED;
402 	/*
403 	 * Loop through the acl entries, building each one.
404 	 */
405 	for (i = 0; i < aclp->acl_cnt; i++) {
406 		isowner = isgroup = malloced = 0;
407 		switch (aclp->acl_entry[i].ae_tag) {
408 		case ACL_USER_OBJ:
409 			isowner = 1;
410 			name = "OWNER@";
411 			namelen = 6;
412 			break;
413 		case ACL_GROUP_OBJ:
414 			isgroup = 1;
415 			name = "GROUP@";
416 			namelen = 6;
417 			break;
418 		case ACL_EVERYONE:
419 			name = "EVERYONE@";
420 			namelen = 9;
421 			break;
422 		case ACL_USER:
423 			name = namestr;
424 			nfsv4_uidtostr(aclp->acl_entry[i].ae_id, &name,
425 			    &namelen, p);
426 			if (name != namestr)
427 				malloced = 1;
428 			break;
429 		case ACL_GROUP:
430 			isgroup = 1;
431 			name = namestr;
432 			nfsv4_gidtostr((gid_t)aclp->acl_entry[i].ae_id, &name,
433 			    &namelen, p);
434 			if (name != namestr)
435 				malloced = 1;
436 			break;
437 		default:
438 			continue;
439 		}
440 		retlen += nfsrv_buildace(nd, name, namelen, type, isgroup,
441 		    isowner, &aclp->acl_entry[i]);
442 		entrycnt++;
443 		if (malloced)
444 			free(name, M_NFSSTRING);
445 	}
446 	*entrycntp = txdr_unsigned(entrycnt);
447 	return (retlen);
448 }
449 
450 /*
451  * Set an NFSv4 acl.
452  */
453 APPLESTATIC int
454 nfsrv_setacl(vnode_t vp, NFSACL_T *aclp, struct ucred *cred,
455     NFSPROC_T *p)
456 {
457 	int error;
458 
459 	if (nfsrv_useacl == 0 || nfs_supportsnfsv4acls(vp) == 0) {
460 		error = NFSERR_ATTRNOTSUPP;
461 		goto out;
462 	}
463 	/*
464 	 * With NFSv4 ACLs, chmod(2) may need to add additional entries.
465 	 * Make sure it has enough room for that - splitting every entry
466 	 * into two and appending "canonical six" entries at the end.
467 	 * Cribbed out of kern/vfs_acl.c - Rick M.
468 	 */
469 	if (aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2) {
470 		error = NFSERR_ATTRNOTSUPP;
471 		goto out;
472 	}
473 	error = VOP_SETACL(vp, ACL_TYPE_NFS4, aclp, cred, p);
474 
475 out:
476 	NFSEXITCODE(error);
477 	return (error);
478 }
479 
480 /*
481  * Compare two NFSv4 acls.
482  * Return 0 if they are the same, 1 if not the same.
483  */
484 APPLESTATIC int
485 nfsrv_compareacl(NFSACL_T *aclp1, NFSACL_T *aclp2)
486 {
487 	int i;
488 	struct acl_entry *acep1, *acep2;
489 
490 	if (aclp1->acl_cnt != aclp2->acl_cnt)
491 		return (1);
492 	acep1 = aclp1->acl_entry;
493 	acep2 = aclp2->acl_entry;
494 	for (i = 0; i < aclp1->acl_cnt; i++) {
495 		if (acep1->ae_tag != acep2->ae_tag)
496 			return (1);
497 		switch (acep1->ae_tag) {
498 		case ACL_GROUP:
499 		case ACL_USER:
500 			if (acep1->ae_id != acep2->ae_id)
501 				return (1);
502 			/* fall through */
503 		case ACL_USER_OBJ:
504 		case ACL_GROUP_OBJ:
505 		case ACL_OTHER:
506 			if (acep1->ae_perm != acep2->ae_perm)
507 				return (1);
508 		}
509 		acep1++;
510 		acep2++;
511 	}
512 	return (0);
513 }
514