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