xref: /freebsd/sys/fs/nfs/nfs_commonacl.c (revision 8e2a90ac8089e168f71d797f8b3b67659f72c7af)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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 #include <fs/nfs/nfsport.h>
32 
33 extern int nfsrv_useacl;
34 
35 static int nfsrv_acemasktoperm(u_int32_t acetype, u_int32_t mask, int owner,
36     __enum_uint8(vtype) type, acl_perm_t *permp);
37 
38 /*
39  * Handle xdr for an ace.
40  */
41 int
nfsrv_dissectace(struct nfsrv_descript * nd,struct acl_entry * acep,bool server,int * aceerrp,int * acesizep,NFSPROC_T * p)42 nfsrv_dissectace(struct nfsrv_descript *nd, struct acl_entry *acep,
43     bool server, int *aceerrp, int *acesizep, NFSPROC_T *p)
44 {
45 	u_int32_t *tl;
46 	int len, gotid = 0, owner = 0, error = 0, aceerr = 0;
47 	u_char *name, namestr[NFSV4_SMALLSTR + 1];
48 	u_int32_t flag, mask, acetype;
49 	gid_t gid;
50 	uid_t uid;
51 
52 	*aceerrp = 0;
53 	acep->ae_flags = 0;
54 	NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
55 	acetype = fxdr_unsigned(u_int32_t, *tl++);
56 	flag = fxdr_unsigned(u_int32_t, *tl++);
57 	mask = fxdr_unsigned(u_int32_t, *tl++);
58 	len = fxdr_unsigned(int, *tl);
59 	/*
60 	 * The RFCs do not specify a limit to the length of the "who", but
61 	 * NFSV4_OPAQUELIMIT (1024) should be sufficient.
62 	 */
63 	if (len < 0 || len > NFSV4_OPAQUELIMIT) {
64 		error = NFSERR_BADXDR;
65 		goto nfsmout;
66 	} else if (len == 0) {
67 		/* Netapp filers return a 0 length who for nil users */
68 		acep->ae_tag = ACL_UNDEFINED_TAG;
69 		acep->ae_id = ACL_UNDEFINED_ID;
70 		acep->ae_perm = (acl_perm_t)0;
71 		acep->ae_entry_type = ACL_ENTRY_TYPE_DENY;
72 		if (acesizep)
73 			*acesizep = 4 * NFSX_UNSIGNED;
74 		error = 0;
75 		goto nfsmout;
76 	}
77 	if (len > NFSV4_SMALLSTR)
78 		name = malloc(len + 1, M_NFSSTRING, M_WAITOK);
79 	else
80 		name = namestr;
81 	error = nfsrv_mtostr(nd, name, len);
82 	if (error) {
83 		if (len > NFSV4_SMALLSTR)
84 			free(name, M_NFSSTRING);
85 		goto nfsmout;
86 	}
87 	if (len == 6) {
88 		if (!NFSBCMP(name, "OWNER@", 6)) {
89 			acep->ae_tag = ACL_USER_OBJ;
90 			acep->ae_id = ACL_UNDEFINED_ID;
91 			owner = 1;
92 			gotid = 1;
93 		} else if (!NFSBCMP(name, "GROUP@", 6)) {
94 			acep->ae_tag = ACL_GROUP_OBJ;
95 			acep->ae_id = ACL_UNDEFINED_ID;
96 			gotid = 1;
97 		}
98 	} else if (len == 9 && !NFSBCMP(name, "EVERYONE@", 9)) {
99 		acep->ae_tag = ACL_EVERYONE;
100 		acep->ae_id = ACL_UNDEFINED_ID;
101 		gotid = 1;
102 	}
103 	if (gotid == 0) {
104 		if (flag & NFSV4ACE_IDENTIFIERGROUP) {
105 			acep->ae_tag = ACL_GROUP;
106 			aceerr = nfsv4_strtogid(nd, name, len, &gid);
107 			if (aceerr == 0)
108 				acep->ae_id = (uid_t)gid;
109 		} else {
110 			acep->ae_tag = ACL_USER;
111 			aceerr = nfsv4_strtouid(nd, name, len, &uid);
112 			if (aceerr == 0)
113 				acep->ae_id = uid;
114 		}
115 	}
116 	if (len > NFSV4_SMALLSTR)
117 		free(name, M_NFSSTRING);
118 
119 	if (aceerr == 0) {
120 		/*
121 		 * Handle the flags.
122 		 */
123 		flag &= ~NFSV4ACE_IDENTIFIERGROUP;
124 		if (flag & NFSV4ACE_FILEINHERIT) {
125 			flag &= ~NFSV4ACE_FILEINHERIT;
126 			acep->ae_flags |= ACL_ENTRY_FILE_INHERIT;
127 		}
128 		if (flag & NFSV4ACE_DIRECTORYINHERIT) {
129 			flag &= ~NFSV4ACE_DIRECTORYINHERIT;
130 			acep->ae_flags |= ACL_ENTRY_DIRECTORY_INHERIT;
131 		}
132 		if (flag & NFSV4ACE_NOPROPAGATEINHERIT) {
133 			flag &= ~NFSV4ACE_NOPROPAGATEINHERIT;
134 			acep->ae_flags |= ACL_ENTRY_NO_PROPAGATE_INHERIT;
135 		}
136 		if (flag & NFSV4ACE_INHERITONLY) {
137 			flag &= ~NFSV4ACE_INHERITONLY;
138 			acep->ae_flags |= ACL_ENTRY_INHERIT_ONLY;
139 		}
140 		if (flag & NFSV4ACE_SUCCESSFULACCESS) {
141 			flag &= ~NFSV4ACE_SUCCESSFULACCESS;
142 			acep->ae_flags |= ACL_ENTRY_SUCCESSFUL_ACCESS;
143 		}
144 		if (flag & NFSV4ACE_FAILEDACCESS) {
145 			flag &= ~NFSV4ACE_FAILEDACCESS;
146 			acep->ae_flags |= ACL_ENTRY_FAILED_ACCESS;
147 		}
148 		/*
149 		 * Set ae_entry_type.
150 		 */
151 		if (acetype == NFSV4ACE_ALLOWEDTYPE)
152 			acep->ae_entry_type = ACL_ENTRY_TYPE_ALLOW;
153 		else if (acetype == NFSV4ACE_DENIEDTYPE)
154 			acep->ae_entry_type = ACL_ENTRY_TYPE_DENY;
155 		else if (!server && acetype == NFSV4ACE_AUDITTYPE)
156 			acep->ae_entry_type = ACL_ENTRY_TYPE_AUDIT;
157 		else if (!server && acetype == NFSV4ACE_ALARMTYPE)
158 			acep->ae_entry_type = ACL_ENTRY_TYPE_ALARM;
159 		else
160 			aceerr = NFSERR_ATTRNOTSUPP;
161 	}
162 
163 	/*
164 	 * Now, check for unsupported flag bits.
165 	 */
166 	if (aceerr == 0 && flag != 0)
167 		aceerr = NFSERR_ATTRNOTSUPP;
168 
169 	/*
170 	 * And turn the mask into perm bits.
171 	 */
172 	if (aceerr == 0)
173 		aceerr = nfsrv_acemasktoperm(acetype, mask, owner, VREG,
174 		    &acep->ae_perm);
175 	*aceerrp = aceerr;
176 	if (acesizep)
177 		*acesizep = NFSM_RNDUP(len) + (4 * NFSX_UNSIGNED);
178 	error = 0;
179 nfsmout:
180 	NFSEXITCODE(error);
181 	return (error);
182 }
183 
184 /*
185  * Turn an NFSv4 ace mask into R/W/X flag bits.
186  */
187 static int
nfsrv_acemasktoperm(u_int32_t acetype,u_int32_t mask,int owner,__enum_uint8 (vtype)type,acl_perm_t * permp)188 nfsrv_acemasktoperm(u_int32_t acetype, u_int32_t mask, int owner,
189     __enum_uint8(vtype) type, acl_perm_t *permp)
190 {
191 	acl_perm_t perm = 0x0;
192 	int error = 0;
193 
194 	if (mask & NFSV4ACE_READDATA) {
195 		mask &= ~NFSV4ACE_READDATA;
196 		perm |= ACL_READ_DATA;
197 	}
198 	if (mask & NFSV4ACE_LISTDIRECTORY) {
199 		mask &= ~NFSV4ACE_LISTDIRECTORY;
200 		perm |= ACL_LIST_DIRECTORY;
201 	}
202 	if (mask & NFSV4ACE_WRITEDATA) {
203 		mask &= ~NFSV4ACE_WRITEDATA;
204 		perm |= ACL_WRITE_DATA;
205 	}
206 	if (mask & NFSV4ACE_ADDFILE) {
207 		mask &= ~NFSV4ACE_ADDFILE;
208 		perm |= ACL_ADD_FILE;
209 	}
210 	if (mask & NFSV4ACE_APPENDDATA) {
211 		mask &= ~NFSV4ACE_APPENDDATA;
212 		perm |= ACL_APPEND_DATA;
213 	}
214 	if (mask & NFSV4ACE_ADDSUBDIRECTORY) {
215 		mask &= ~NFSV4ACE_ADDSUBDIRECTORY;
216 		perm |= ACL_ADD_SUBDIRECTORY;
217 	}
218 	if (mask & NFSV4ACE_READNAMEDATTR) {
219 		mask &= ~NFSV4ACE_READNAMEDATTR;
220 		perm |= ACL_READ_NAMED_ATTRS;
221 	}
222 	if (mask & NFSV4ACE_WRITENAMEDATTR) {
223 		mask &= ~NFSV4ACE_WRITENAMEDATTR;
224 		perm |= ACL_WRITE_NAMED_ATTRS;
225 	}
226 	if (mask & NFSV4ACE_EXECUTE) {
227 		mask &= ~NFSV4ACE_EXECUTE;
228 		perm |= ACL_EXECUTE;
229 	}
230 	if (mask & NFSV4ACE_SEARCH) {
231 		mask &= ~NFSV4ACE_SEARCH;
232 		perm |= ACL_EXECUTE;
233 	}
234 	if (mask & NFSV4ACE_DELETECHILD) {
235 		mask &= ~NFSV4ACE_DELETECHILD;
236 		perm |= ACL_DELETE_CHILD;
237 	}
238 	if (mask & NFSV4ACE_READATTRIBUTES) {
239 		mask &= ~NFSV4ACE_READATTRIBUTES;
240 		perm |= ACL_READ_ATTRIBUTES;
241 	}
242 	if (mask & NFSV4ACE_WRITEATTRIBUTES) {
243 		mask &= ~NFSV4ACE_WRITEATTRIBUTES;
244 		perm |= ACL_WRITE_ATTRIBUTES;
245 	}
246 	if (mask & NFSV4ACE_DELETE) {
247 		mask &= ~NFSV4ACE_DELETE;
248 		perm |= ACL_DELETE;
249 	}
250 	if (mask & NFSV4ACE_READACL) {
251 		mask &= ~NFSV4ACE_READACL;
252 		perm |= ACL_READ_ACL;
253 	}
254 	if (mask & NFSV4ACE_WRITEACL) {
255 		mask &= ~NFSV4ACE_WRITEACL;
256 		perm |= ACL_WRITE_ACL;
257 	}
258 	if (mask & NFSV4ACE_WRITEOWNER) {
259 		mask &= ~NFSV4ACE_WRITEOWNER;
260 		perm |= ACL_WRITE_OWNER;
261 	}
262 	if (mask & NFSV4ACE_SYNCHRONIZE) {
263 		mask &= ~NFSV4ACE_SYNCHRONIZE;
264 		perm |= ACL_SYNCHRONIZE;
265 	}
266 	if (mask != 0) {
267 		error = NFSERR_ATTRNOTSUPP;
268 		goto out;
269 	}
270 	*permp = perm;
271 
272 out:
273 	NFSEXITCODE(error);
274 	return (error);
275 }
276 
277 /* local functions */
278 static int nfsrv_buildace(struct nfsrv_descript *, u_char *, int,
279     __enum_uint8(vtype), int, int, struct acl_entry *);
280 
281 /*
282  * This function builds an NFS ace.
283  */
284 static int
nfsrv_buildace(struct nfsrv_descript * nd,u_char * name,int namelen,__enum_uint8 (vtype)type,int group,int owner,struct acl_entry * ace)285 nfsrv_buildace(struct nfsrv_descript *nd, u_char *name, int namelen,
286     __enum_uint8(vtype) type, int group, int owner, struct acl_entry *ace)
287 {
288 	u_int32_t *tl, aceflag = 0x0, acemask = 0x0, acetype;
289 	int full_len;
290 
291 	full_len = NFSM_RNDUP(namelen);
292 	NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED + full_len);
293 
294 	/*
295 	 * Fill in the ace type.
296 	 */
297 	if (ace->ae_entry_type & ACL_ENTRY_TYPE_ALLOW)
298 		acetype = NFSV4ACE_ALLOWEDTYPE;
299 	else if (ace->ae_entry_type & ACL_ENTRY_TYPE_DENY)
300 		acetype = NFSV4ACE_DENIEDTYPE;
301 	else if (ace->ae_entry_type & ACL_ENTRY_TYPE_AUDIT)
302 		acetype = NFSV4ACE_AUDITTYPE;
303 	else
304 		acetype = NFSV4ACE_ALARMTYPE;
305 	*tl++ = txdr_unsigned(acetype);
306 
307 	/*
308 	 * Set the flag bits from the ACL.
309 	 */
310 	if (ace->ae_flags & ACL_ENTRY_FILE_INHERIT)
311 		aceflag |= NFSV4ACE_FILEINHERIT;
312 	if (ace->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT)
313 		aceflag |= NFSV4ACE_DIRECTORYINHERIT;
314 	if (ace->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)
315 		aceflag |= NFSV4ACE_NOPROPAGATEINHERIT;
316 	if (ace->ae_flags & ACL_ENTRY_INHERIT_ONLY)
317 		aceflag |= NFSV4ACE_INHERITONLY;
318 	if (ace->ae_flags & ACL_ENTRY_SUCCESSFUL_ACCESS)
319 		aceflag |= NFSV4ACE_SUCCESSFULACCESS;
320 	if (ace->ae_flags & ACL_ENTRY_FAILED_ACCESS)
321 		aceflag |= NFSV4ACE_FAILEDACCESS;
322 	if (group)
323 		aceflag |= NFSV4ACE_IDENTIFIERGROUP;
324 	*tl++ = txdr_unsigned(aceflag);
325 	if (type == VDIR) {
326 		if (ace->ae_perm & ACL_LIST_DIRECTORY)
327 			acemask |= NFSV4ACE_LISTDIRECTORY;
328 		if (ace->ae_perm & ACL_ADD_FILE)
329 			acemask |= NFSV4ACE_ADDFILE;
330 		if (ace->ae_perm & ACL_ADD_SUBDIRECTORY)
331 			acemask |= NFSV4ACE_ADDSUBDIRECTORY;
332 		if (ace->ae_perm & ACL_READ_NAMED_ATTRS)
333 			acemask |= NFSV4ACE_READNAMEDATTR;
334 		if (ace->ae_perm & ACL_WRITE_NAMED_ATTRS)
335 			acemask |= NFSV4ACE_WRITENAMEDATTR;
336 		if (ace->ae_perm & ACL_EXECUTE)
337 			acemask |= NFSV4ACE_SEARCH;
338 		if (ace->ae_perm & ACL_DELETE_CHILD)
339 			acemask |= NFSV4ACE_DELETECHILD;
340 		if (ace->ae_perm & ACL_READ_ATTRIBUTES)
341 			acemask |= NFSV4ACE_READATTRIBUTES;
342 		if (ace->ae_perm & ACL_WRITE_ATTRIBUTES)
343 			acemask |= NFSV4ACE_WRITEATTRIBUTES;
344 		if (ace->ae_perm & ACL_DELETE)
345 			acemask |= NFSV4ACE_DELETE;
346 		if (ace->ae_perm & ACL_READ_ACL)
347 			acemask |= NFSV4ACE_READACL;
348 		if (ace->ae_perm & ACL_WRITE_ACL)
349 			acemask |= NFSV4ACE_WRITEACL;
350 		if (ace->ae_perm & ACL_WRITE_OWNER)
351 			acemask |= NFSV4ACE_WRITEOWNER;
352 		if (ace->ae_perm & ACL_SYNCHRONIZE)
353 			acemask |= NFSV4ACE_SYNCHRONIZE;
354 	} else {
355 		acemask = nfs_aceperm(ace->ae_perm);
356 	}
357 	*tl++ = txdr_unsigned(acemask);
358 	*tl++ = txdr_unsigned(namelen);
359 	if (full_len - namelen)
360 		*(tl + (namelen / NFSX_UNSIGNED)) = 0x0;
361 	NFSBCOPY(name, (caddr_t)tl, namelen);
362 	return (full_len + 4 * NFSX_UNSIGNED);
363 }
364 
365 /*
366  * Convert ae_perm to NFSv4 ACL acemask4 for regular files.
367  */
368 uint32_t
nfs_aceperm(acl_perm_t ae_perm)369 nfs_aceperm(acl_perm_t ae_perm)
370 {
371 	uint32_t acemask = 0x0;
372 
373 	if (ae_perm & ACL_READ_DATA)
374 		acemask |= NFSV4ACE_READDATA;
375 	if (ae_perm & ACL_WRITE_DATA)
376 		acemask |= NFSV4ACE_WRITEDATA;
377 	if (ae_perm & ACL_APPEND_DATA)
378 		acemask |= NFSV4ACE_APPENDDATA;
379 	if (ae_perm & ACL_READ_NAMED_ATTRS)
380 		acemask |= NFSV4ACE_READNAMEDATTR;
381 	if (ae_perm & ACL_WRITE_NAMED_ATTRS)
382 		acemask |= NFSV4ACE_WRITENAMEDATTR;
383 	if (ae_perm & ACL_EXECUTE)
384 		acemask |= NFSV4ACE_EXECUTE;
385 	if (ae_perm & ACL_READ_ATTRIBUTES)
386 		acemask |= NFSV4ACE_READATTRIBUTES;
387 	if (ae_perm & ACL_WRITE_ATTRIBUTES)
388 		acemask |= NFSV4ACE_WRITEATTRIBUTES;
389 	if (ae_perm & ACL_DELETE)
390 		acemask |= NFSV4ACE_DELETE;
391 	if (ae_perm & ACL_READ_ACL)
392 		acemask |= NFSV4ACE_READACL;
393 	if (ae_perm & ACL_WRITE_ACL)
394 		acemask |= NFSV4ACE_WRITEACL;
395 	if (ae_perm & ACL_WRITE_OWNER)
396 		acemask |= NFSV4ACE_WRITEOWNER;
397 	if (ae_perm & ACL_SYNCHRONIZE)
398 		acemask |= NFSV4ACE_SYNCHRONIZE;
399 	return (acemask);
400 }
401 
402 /*
403  * Build an NFSv4 ACL.
404  */
405 int
nfsrv_buildacl(struct nfsrv_descript * nd,NFSACL_T * aclp,__enum_uint8 (vtype)type,NFSPROC_T * p)406 nfsrv_buildacl(struct nfsrv_descript *nd, NFSACL_T *aclp, __enum_uint8(vtype) type,
407     NFSPROC_T *p)
408 {
409 	int i, entrycnt = 0, retlen;
410 	u_int32_t *entrycntp;
411 	int isowner, isgroup, namelen, malloced;
412 	u_char *name, namestr[NFSV4_SMALLSTR];
413 
414 	NFSM_BUILD(entrycntp, u_int32_t *, NFSX_UNSIGNED);
415 	retlen = NFSX_UNSIGNED;
416 	/*
417 	 * Loop through the acl entries, building each one.
418 	 */
419 	for (i = 0; i < aclp->acl_cnt; i++) {
420 		isowner = isgroup = malloced = 0;
421 		switch (aclp->acl_entry[i].ae_tag) {
422 		case ACL_USER_OBJ:
423 			isowner = 1;
424 			name = "OWNER@";
425 			namelen = 6;
426 			break;
427 		case ACL_GROUP_OBJ:
428 			isgroup = 1;
429 			name = "GROUP@";
430 			namelen = 6;
431 			break;
432 		case ACL_EVERYONE:
433 			name = "EVERYONE@";
434 			namelen = 9;
435 			break;
436 		case ACL_USER:
437 			name = namestr;
438 			nfsv4_uidtostr(aclp->acl_entry[i].ae_id, &name,
439 			    &namelen);
440 			if (name != namestr)
441 				malloced = 1;
442 			break;
443 		case ACL_GROUP:
444 			isgroup = 1;
445 			name = namestr;
446 			nfsv4_gidtostr((gid_t)aclp->acl_entry[i].ae_id, &name,
447 			    &namelen);
448 			if (name != namestr)
449 				malloced = 1;
450 			break;
451 		default:
452 			continue;
453 		}
454 		retlen += nfsrv_buildace(nd, name, namelen, type, isgroup,
455 		    isowner, &aclp->acl_entry[i]);
456 		entrycnt++;
457 		if (malloced)
458 			free(name, M_NFSSTRING);
459 	}
460 	*entrycntp = txdr_unsigned(entrycnt);
461 	return (retlen);
462 }
463 
464 /*
465  * Compare two NFSv4 acls.
466  * Return 0 if they are the same, 1 if not the same.
467  */
468 int
nfsrv_compareacl(NFSACL_T * aclp1,NFSACL_T * aclp2)469 nfsrv_compareacl(NFSACL_T *aclp1, NFSACL_T *aclp2)
470 {
471 	int i;
472 	struct acl_entry *acep1, *acep2;
473 
474 	if (aclp1->acl_cnt != aclp2->acl_cnt)
475 		return (1);
476 	acep1 = aclp1->acl_entry;
477 	acep2 = aclp2->acl_entry;
478 	for (i = 0; i < aclp1->acl_cnt; i++) {
479 		if (acep1->ae_tag != acep2->ae_tag)
480 			return (1);
481 		switch (acep1->ae_tag) {
482 		case ACL_GROUP:
483 		case ACL_USER:
484 			if (acep1->ae_id != acep2->ae_id)
485 				return (1);
486 			/* fall through */
487 		case ACL_USER_OBJ:
488 		case ACL_GROUP_OBJ:
489 		case ACL_OTHER:
490 			if (acep1->ae_perm != acep2->ae_perm)
491 				return (1);
492 		}
493 		acep1++;
494 		acep2++;
495 	}
496 	return (0);
497 }
498