xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_acl.c (revision 41e0a469c3dbc14deb2b200f6ca6f6e00b5865d0)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2023 RackTop Systems, Inc.
26  */
27 
28 /*
29  * SMB server interfaces for ACL conversion (smb_acl_...)
30  *
31  * There are two variants of this interface:
32  * This is the library version.  See also:
33  * $SRC/uts/common/fs/smbsrv/smb_acl.c
34  */
35 
36 #include <stddef.h>
37 #include <strings.h>
38 #include <syslog.h>
39 #include <assert.h>
40 
41 #include <smbsrv/smb.h>
42 #include <smbsrv/smb_sid.h>
43 #include <smbsrv/smb_idmap.h>
44 
45 #define	ACE_ALL_TYPES	0x001F
46 
47 /*
48  * ACE groups within a DACL
49  *
50  * This is from lower to higher ACE order priority
51  */
52 #define	SMB_AG_START		0
53 #define	SMB_AG_ALW_INHRT	0
54 #define	SMB_AG_DNY_INHRT	1
55 #define	SMB_AG_ALW_DRCT		2
56 #define	SMB_AG_DNY_DRCT		3
57 #define	SMB_AG_NUM		4
58 
59 #define	DEFAULT_DACL_ACENUM	2
60 acl_t *acl_alloc(enum acl_type);
61 
62 static idmap_stat smb_fsacl_getsids(smb_idmap_batch_t *, acl_t *);
63 static acl_t *smb_fsacl_null_empty(boolean_t);
64 static boolean_t smb_ace_isvalid(smb_ace_t *, int);
65 static uint16_t smb_ace_len(smb_ace_t *);
66 static uint32_t smb_ace_mask_g2s(uint32_t);
67 static uint16_t smb_ace_flags_tozfs(uint8_t);
68 static uint8_t smb_ace_flags_fromzfs(uint16_t);
69 static boolean_t smb_ace_wellknown_update(const char *, ace_t *);
70 
71 smb_acl_t *
72 smb_acl_alloc(uint8_t revision, uint16_t bsize, uint16_t acecnt)
73 {
74 	smb_acl_t *acl;
75 	int size;
76 
77 	size = sizeof (smb_acl_t) + (acecnt * sizeof (smb_ace_t));
78 	if ((acl = malloc(size)) == NULL)
79 		return (NULL);
80 
81 	acl->sl_revision = revision;
82 	acl->sl_bsize = bsize;
83 	acl->sl_acecnt = acecnt;
84 	acl->sl_aces = (smb_ace_t *)(acl + 1);
85 
86 	list_create(&acl->sl_sorted, sizeof (smb_ace_t),
87 	    offsetof(smb_ace_t, se_sln));
88 	return (acl);
89 }
90 
91 void
92 smb_acl_free(smb_acl_t *acl)
93 {
94 	int i;
95 	void *ace;
96 
97 	if (acl == NULL)
98 		return;
99 
100 	for (i = 0; i < acl->sl_acecnt; i++)
101 		smb_sid_free(acl->sl_aces[i].se_sid);
102 
103 	while ((ace = list_head(&acl->sl_sorted)) != NULL)
104 		list_remove(&acl->sl_sorted, ace);
105 	list_destroy(&acl->sl_sorted);
106 	free(acl);
107 }
108 
109 /*
110  * smb_acl_len
111  *
112  * Returns the size of given ACL in bytes. Note that this
113  * is not an in-memory size, it's the ACL's size as it would
114  * appear on the wire
115  */
116 uint16_t
117 smb_acl_len(smb_acl_t *acl)
118 {
119 	return ((acl) ? acl->sl_bsize : 0);
120 }
121 
122 boolean_t
123 smb_acl_isvalid(smb_acl_t *acl, int which_acl)
124 {
125 	int i;
126 
127 	if (acl->sl_bsize < SMB_ACL_HDRSIZE)
128 		return (B_FALSE);
129 
130 	if (acl->sl_revision != ACL_REVISION) {
131 		/*
132 		 * we are rejecting ACLs with object-specific ACEs for now
133 		 */
134 		return (B_FALSE);
135 	}
136 
137 	for (i = 0; i < acl->sl_acecnt; i++) {
138 		if (!smb_ace_isvalid(&acl->sl_aces[i], which_acl))
139 			return (B_FALSE);
140 	}
141 
142 	return (B_TRUE);
143 }
144 
145 /*
146  * smb_acl_sort
147  *
148  * Sorts the given ACL in place if it needs to be sorted.
149  *
150  * The following is an excerpt from MSDN website.
151  *
152  * Order of ACEs in a DACL
153  *
154  * For Windows NT versions 4.0 and earlier, the preferred order of ACEs
155  * is simple: In a DACL, all access-denied ACEs should precede any
156  * access-allowed ACEs.
157  *
158  * For Windows 2000 or later, the proper order of ACEs is more complicated
159  * because of the introduction of object-specific ACEs and automatic
160  * inheritance.
161  *
162  * The following describes the preferred order:
163  *
164  * To ensure that noninherited ACEs have precedence over inherited ACEs,
165  * place all noninherited ACEs in a group before any inherited ACEs. This
166  * ordering ensures, for example, that a noninherited access-denied ACE
167  * is enforced regardless of any inherited ACE that allows access.
168  * Within the groups of noninherited ACEs and inherited ACEs, order ACEs
169  * according to ACE type, as the following shows:
170  *	. Access-denied ACEs that apply to the object itself
171  *	. Access-denied ACEs that apply to a subobject of the
172  *	  object, such as a property set or property
173  *	. Access-allowed ACEs that apply to the object itself
174  *	. Access-allowed ACEs that apply to a subobject of the object
175  *
176  * So, here is the desired ACE order
177  *
178  * deny-direct, allow-direct, deny-inherited, allow-inherited
179  *
180  * Of course, not all ACE types are required in an ACL.
181  */
182 void
183 smb_acl_sort(smb_acl_t *acl)
184 {
185 	list_t ace_grps[SMB_AG_NUM];
186 	list_t *alist;
187 	smb_ace_t *ace;
188 	uint8_t ace_flags;
189 	int ag, i;
190 
191 	assert(acl);
192 
193 	if (acl->sl_acecnt == 0) {
194 		/*
195 		 * ACL with no entry is a valid ACL and it means
196 		 * no access for anybody.
197 		 */
198 		return;
199 	}
200 
201 	for (i = SMB_AG_START; i < SMB_AG_NUM; i++) {
202 		list_create(&ace_grps[i], sizeof (smb_ace_t),
203 		    offsetof(smb_ace_t, se_sln));
204 	}
205 
206 	for (i = 0, ace = acl->sl_aces; i < acl->sl_acecnt; ++i, ace++) {
207 		ace_flags = ace->se_hdr.se_flags;
208 
209 		switch (ace->se_hdr.se_type) {
210 		case ACCESS_DENIED_ACE_TYPE:
211 			ag = (ace_flags & INHERITED_ACE) ?
212 			    SMB_AG_DNY_INHRT : SMB_AG_DNY_DRCT;
213 			break;
214 
215 		case ACCESS_ALLOWED_ACE_TYPE:
216 			ag = (ace_flags & INHERITED_ACE) ?
217 			    SMB_AG_ALW_INHRT : SMB_AG_ALW_DRCT;
218 			break;
219 
220 		default:
221 			/*
222 			 * This is the lowest priority group so we put
223 			 * evertything unknown here.
224 			 */
225 			ag = SMB_AG_ALW_INHRT;
226 			break;
227 		}
228 
229 		/* Add the ACE to the selected group */
230 		list_insert_tail(&ace_grps[ag], ace);
231 	}
232 
233 	/*
234 	 * start with highest priority ACE group and append
235 	 * the ACEs to the ACL.
236 	 */
237 	for (i = SMB_AG_NUM - 1; i >= SMB_AG_START; i--) {
238 		alist = &ace_grps[i];
239 		while ((ace = list_head(alist)) != NULL) {
240 			list_remove(alist, ace);
241 			list_insert_tail(&acl->sl_sorted, ace);
242 		}
243 		list_destroy(alist);
244 	}
245 }
246 
247 /*
248  * Error handling call-back for smb_idmap_batch_getmappings.
249  * Would be nice if this could report the path, but that's not
250  * passed down here.  For now, use a dtrace fbt probe here.
251  */
252 static void
253 smb_acl_bgm_error(smb_idmap_batch_t *sib, smb_idmap_t *sim)
254 {
255 
256 	if ((sib->sib_flags & SMB_IDMAP_SKIP_ERRS) != 0)
257 		return;
258 
259 	if ((sib->sib_flags & SMB_IDMAP_ID2SID) != 0) {
260 		/*
261 		 * Note: The ID and type we asked idmap to map
262 		 * were saved in *sim_id and sim_idtype.
263 		 */
264 		uid_t id = (sim->sim_id == NULL) ? (uid_t)-1 : *sim->sim_id;
265 		syslog(LOG_ERR, "!smb_acl: Can't get SID for "
266 		    "ID=%u type=%d, status=%d",
267 		    id, sim->sim_idtype, sim->sim_stat);
268 	}
269 
270 	if ((sib->sib_flags & SMB_IDMAP_SID2ID) != 0) {
271 		syslog(LOG_ERR, "!smb_acl: Can't get ID for "
272 		    "SID %s-%u, status=%d",
273 		    sim->sim_domsid, sim->sim_rid, sim->sim_stat);
274 	}
275 }
276 
277 /*
278  * smb_acl_from_zfs
279  *
280  * Converts given ZFS ACL to a Windows ACL.
281  *
282  * A pointer to allocated memory for the Windows ACL will be
283  * returned upon successful conversion.
284  */
285 smb_acl_t *
286 smb_acl_from_zfs(acl_t *zacl)
287 {
288 	ace_t *zace;
289 	int numaces;
290 	smb_acl_t *acl;
291 	smb_ace_t *ace;
292 	smb_idmap_batch_t sib;
293 	smb_idmap_t *sim;
294 	idmap_stat idm_stat;
295 
296 	idm_stat = smb_idmap_batch_create(&sib, zacl->acl_cnt,
297 	    SMB_IDMAP_ID2SID);
298 	if (idm_stat != IDMAP_SUCCESS)
299 		return (NULL);
300 
301 	/*
302 	 * Note that smb_fsacl_getsids sets up references in
303 	 * sib.sib_maps to the zace->a_who fields that live
304 	 * until smb_idmap_batch_destroy is called.
305 	 */
306 	if (smb_fsacl_getsids(&sib, zacl) != IDMAP_SUCCESS) {
307 		smb_idmap_batch_destroy(&sib);
308 		return (NULL);
309 	}
310 
311 	acl = smb_acl_alloc(ACL_REVISION, SMB_ACL_HDRSIZE, zacl->acl_cnt);
312 
313 	sim = sib.sib_maps;
314 	for (numaces = 0, zace = zacl->acl_aclp;
315 	    numaces < zacl->acl_cnt;
316 	    zace++, numaces++, sim++) {
317 		assert(sim->sim_sid);
318 		if (sim->sim_sid == NULL) {
319 			smb_acl_free(acl);
320 			acl = NULL;
321 			break;
322 		}
323 
324 		ace = &acl->sl_aces[numaces];
325 		ace->se_hdr.se_type = zace->a_type;
326 		ace->se_hdr.se_flags = smb_ace_flags_fromzfs(zace->a_flags);
327 		ace->se_mask = zace->a_access_mask;
328 		ace->se_sid = smb_sid_dup(sim->sim_sid);
329 		ace->se_hdr.se_bsize = smb_ace_len(ace);
330 
331 		acl->sl_bsize += ace->se_hdr.se_bsize;
332 	}
333 
334 	smb_idmap_batch_destroy(&sib);
335 	return (acl);
336 }
337 
338 /*
339  * smb_acl_to_zfs
340  *
341  * Converts given Windows ACL to a ZFS ACL.
342  *
343  * fs_acl will contain a pointer to the created ZFS ACL.
344  * The allocated memory should be freed by calling
345  * smb_fsacl_free().
346  *
347  * Since the output parameter, fs_acl, is allocated in this
348  * function, the caller has to make sure *fs_acl is NULL which
349  * means it's not pointing to any memory.
350  */
351 uint32_t
352 smb_acl_to_zfs(smb_acl_t *acl, uint32_t flags, int which_acl, acl_t **fs_acl)
353 {
354 	char sidstr[SMB_SID_STRSZ];
355 	smb_ace_t *ace;
356 	acl_t *zacl;
357 	ace_t *zace;
358 	smb_idmap_batch_t sib;
359 	smb_idmap_t *sim;
360 	idmap_stat idm_stat;
361 	int i;
362 
363 	assert(fs_acl);
364 	assert(*fs_acl == NULL);
365 
366 	if (acl && !smb_acl_isvalid(acl, which_acl))
367 		return (NT_STATUS_INVALID_ACL);
368 
369 	if ((acl == NULL) || (acl->sl_acecnt == 0)) {
370 		if (which_acl == SMB_DACL_SECINFO) {
371 			*fs_acl = smb_fsacl_null_empty(acl == NULL);
372 		}
373 
374 		return (NT_STATUS_SUCCESS);
375 	}
376 
377 	idm_stat = smb_idmap_batch_create(&sib, acl->sl_acecnt,
378 	    SMB_IDMAP_SID2ID);
379 	if (idm_stat != IDMAP_SUCCESS)
380 		return (NT_STATUS_INTERNAL_ERROR);
381 
382 	zacl = smb_fsacl_alloc(acl->sl_acecnt, flags);
383 
384 	zace = zacl->acl_aclp;
385 	ace = acl->sl_aces;
386 	sim = sib.sib_maps;
387 
388 	for (i = 0; i < acl->sl_acecnt; i++, zace++, ace++, sim++) {
389 		zace->a_type = ace->se_hdr.se_type & ACE_ALL_TYPES;
390 		zace->a_access_mask = smb_ace_mask_g2s(ace->se_mask);
391 		zace->a_flags = smb_ace_flags_tozfs(ace->se_hdr.se_flags);
392 		zace->a_who = (uid_t)-1;
393 
394 		smb_sid_tostr(ace->se_sid, sidstr);
395 
396 		if (!smb_ace_wellknown_update(sidstr, zace)) {
397 			sim->sim_id = &zace->a_who;
398 			idm_stat = smb_idmap_batch_getid(sib.sib_idmaph, sim,
399 			    ace->se_sid, SMB_IDMAP_UNKNOWN);
400 
401 			if (idm_stat != IDMAP_SUCCESS) {
402 				smb_fsacl_free(zacl);
403 				smb_idmap_batch_destroy(&sib);
404 				return (NT_STATUS_INTERNAL_ERROR);
405 			}
406 		}
407 	}
408 
409 	idm_stat = smb_idmap_batch_getmappings(&sib, smb_acl_bgm_error);
410 	if (idm_stat != IDMAP_SUCCESS) {
411 		smb_fsacl_free(zacl);
412 		smb_idmap_batch_destroy(&sib);
413 		return (NT_STATUS_NONE_MAPPED);
414 	}
415 
416 	/*
417 	 * Set the ACEs group flag based on the type of ID returned.
418 	 */
419 	zace = zacl->acl_aclp;
420 	ace = acl->sl_aces;
421 	sim = sib.sib_maps;
422 	for (i = 0; i < acl->sl_acecnt; i++, zace++, ace++, sim++) {
423 		if (zace->a_who == (uid_t)-1)
424 			continue;
425 
426 		if (sim->sim_idtype == SMB_IDMAP_GROUP)
427 			zace->a_flags |= ACE_IDENTIFIER_GROUP;
428 	}
429 
430 	smb_idmap_batch_destroy(&sib);
431 
432 	*fs_acl = zacl;
433 	return (NT_STATUS_SUCCESS);
434 }
435 
436 static boolean_t
437 smb_ace_wellknown_update(const char *sid, ace_t *zace)
438 {
439 	struct {
440 		char		*sid;
441 		uint16_t	flags;
442 	} map[] = {
443 		{ NT_WORLD_SIDSTR,			ACE_EVERYONE },
444 		{ NT_BUILTIN_CURRENT_OWNER_SIDSTR,	ACE_OWNER },
445 		{ NT_BUILTIN_CURRENT_GROUP_SIDSTR,
446 			(ACE_GROUP | ACE_IDENTIFIER_GROUP) },
447 	};
448 
449 	int	i;
450 
451 	for (i = 0; i < (sizeof (map) / sizeof (map[0])); ++i) {
452 		if (strcmp(sid, map[i].sid) == 0) {
453 			zace->a_flags |= map[i].flags;
454 			return (B_TRUE);
455 		}
456 	}
457 
458 	return (B_FALSE);
459 }
460 
461 /*
462  * smb_fsacl_getsids
463  *
464  * Batch all the uid/gid in given ZFS ACL to get their corresponding SIDs.
465  * Note: sib is type SMB_IDMAP_ID2SID, zacl->acl_cnt entries.
466  */
467 static idmap_stat
468 smb_fsacl_getsids(smb_idmap_batch_t *sib, acl_t *zacl)
469 {
470 	ace_t *zace;
471 	idmap_stat idm_stat;
472 	smb_idmap_t *sim;
473 	uid_t id;
474 	int i, idtype;
475 
476 	sim = sib->sib_maps;
477 
478 	for (i = 0, zace = zacl->acl_aclp; i < zacl->acl_cnt;
479 	    zace++, i++, sim++) {
480 		id = (uid_t)-1;	/* some types do not need id */
481 		switch (zace->a_flags & ACE_TYPE_FLAGS) {
482 		case ACE_OWNER:
483 			idtype = SMB_IDMAP_OWNERAT;
484 			break;
485 
486 		case (ACE_GROUP | ACE_IDENTIFIER_GROUP):
487 			/* owning group */
488 			idtype = SMB_IDMAP_GROUPAT;
489 			break;
490 
491 		case ACE_IDENTIFIER_GROUP:
492 			/* regular group */
493 			idtype = SMB_IDMAP_GROUP;
494 			id = zace->a_who;
495 			/* for smb_acl_bgm_error ID2SID */
496 			sim->sim_id = &zace->a_who;
497 			break;
498 
499 		case ACE_EVERYONE:
500 			idtype = SMB_IDMAP_EVERYONE;
501 			break;
502 
503 		default:
504 			/* user entry */
505 			idtype = SMB_IDMAP_USER;
506 			id = zace->a_who;
507 			/* for smb_acl_bgm_error ID2SID */
508 			sim->sim_id = &zace->a_who;
509 			break;
510 		}
511 
512 		idm_stat = smb_idmap_batch_getsid(sib->sib_idmaph, sim,
513 		    id, idtype);
514 
515 		if (idm_stat != IDMAP_SUCCESS) {
516 			return (idm_stat);
517 		}
518 	}
519 
520 	idm_stat = smb_idmap_batch_getmappings(sib, smb_acl_bgm_error);
521 	return (idm_stat);
522 }
523 
524 /*
525  * smb_fsacl_null_empty
526  *
527  * NULL DACL means everyone full-access
528  * Empty DACL means everyone full-deny
529  *
530  * ZFS ACL must have at least one entry so smb server has
531  * to simulate the aforementioned expected behavior by adding
532  * an entry in case the requested DACL is null or empty. Adding
533  * a everyone full-deny entry has proved to be problematic in
534  * tests since a deny entry takes precedence over allow entries.
535  * So, instead of adding a everyone full-deny, an owner ACE with
536  * owner implicit permissions will be set.
537  */
538 static acl_t *
539 smb_fsacl_null_empty(boolean_t null)
540 {
541 	acl_t *zacl;
542 	ace_t *zace;
543 
544 	zacl = smb_fsacl_alloc(1, ACL_AUTO_INHERIT);
545 	zace = zacl->acl_aclp;
546 
547 	zace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
548 	if (null) {
549 		zace->a_access_mask = ACE_ALL_PERMS;
550 		zace->a_flags = ACE_EVERYONE;
551 	} else {
552 		zace->a_access_mask = ACE_READ_ACL | ACE_WRITE_ACL |
553 		    ACE_READ_ATTRIBUTES;
554 		zace->a_flags = ACE_OWNER;
555 	}
556 
557 	return (zacl);
558 }
559 
560 /*
561  * FS ACL (acl_t) Functions
562  */
563 acl_t *
564 smb_fsacl_alloc(int acenum, int flags)
565 {
566 	acl_t *acl;
567 
568 	acl = acl_alloc(ACE_T);
569 	acl->acl_cnt = acenum;
570 	if ((acl->acl_aclp = malloc(acl->acl_entry_size * acenum)) == NULL)
571 		return (NULL);
572 
573 	acl->acl_flags = flags;
574 	return (acl);
575 }
576 
577 void
578 smb_fsacl_free(acl_t *acl)
579 {
580 	if (acl)
581 		acl_free(acl);
582 }
583 
584 /*
585  * ACE Functions
586  */
587 
588 /*
589  * This is generic (ACL version 2) vs. object-specific
590  * (ACL version 4) ACE types.
591  */
592 boolean_t
593 smb_ace_is_generic(int type)
594 {
595 	switch (type) {
596 	case ACE_ACCESS_ALLOWED_ACE_TYPE:
597 	case ACE_ACCESS_DENIED_ACE_TYPE:
598 	case ACE_SYSTEM_AUDIT_ACE_TYPE:
599 	case ACE_SYSTEM_ALARM_ACE_TYPE:
600 	case ACE_ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
601 	case ACE_ACCESS_DENIED_CALLBACK_ACE_TYPE:
602 	case ACE_SYSTEM_AUDIT_CALLBACK_ACE_TYPE:
603 	case ACE_SYSTEM_ALARM_CALLBACK_ACE_TYPE:
604 		return (B_TRUE);
605 
606 	default:
607 		break;
608 	}
609 
610 	return (B_FALSE);
611 }
612 
613 boolean_t
614 smb_ace_is_access(int type)
615 {
616 	switch (type) {
617 	case ACE_ACCESS_ALLOWED_ACE_TYPE:
618 	case ACE_ACCESS_DENIED_ACE_TYPE:
619 	case ACE_ACCESS_ALLOWED_COMPOUND_ACE_TYPE:
620 	case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
621 	case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
622 	case ACE_ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
623 	case ACE_ACCESS_DENIED_CALLBACK_ACE_TYPE:
624 	case ACE_ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE:
625 	case ACE_ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE:
626 		return (B_TRUE);
627 
628 	default:
629 		break;
630 	}
631 
632 	return (B_FALSE);
633 }
634 
635 boolean_t
636 smb_ace_is_audit(int type)
637 {
638 	switch (type) {
639 	case ACE_SYSTEM_AUDIT_ACE_TYPE:
640 	case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
641 	case ACE_SYSTEM_AUDIT_CALLBACK_ACE_TYPE:
642 	case ACE_SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE:
643 		return (B_TRUE);
644 
645 	default:
646 		break;
647 	}
648 
649 	return (B_FALSE);
650 }
651 
652 /*
653  * smb_ace_len
654  *
655  * Returns the length of the given ACE as it appears in an
656  * ACL on the wire (i.e. a flat buffer which contains the SID)
657  */
658 static uint16_t
659 smb_ace_len(smb_ace_t *ace)
660 {
661 	assert(ace);
662 	assert(ace->se_sid);
663 
664 	if (ace == NULL)
665 		return (0);
666 
667 	return (SMB_ACE_HDRSIZE + sizeof (ace->se_mask) +
668 	    smb_sid_len(ace->se_sid));
669 }
670 
671 /*
672  * smb_ace_mask_g2s
673  *
674  * Converts generic access bits in the given mask (if any)
675  * to file specific bits. Generic access masks shouldn't be
676  * stored in filesystem ACEs.
677  */
678 static uint32_t
679 smb_ace_mask_g2s(uint32_t mask)
680 {
681 	if (mask & GENERIC_ALL) {
682 		mask &= ~(GENERIC_ALL | GENERIC_READ | GENERIC_WRITE
683 		    | GENERIC_EXECUTE);
684 
685 		mask |= FILE_ALL_ACCESS;
686 		return (mask);
687 	}
688 
689 	if (mask & GENERIC_READ) {
690 		mask &= ~GENERIC_READ;
691 		mask |= FILE_GENERIC_READ;
692 	}
693 
694 	if (mask & GENERIC_WRITE) {
695 		mask &= ~GENERIC_WRITE;
696 		mask |= FILE_GENERIC_WRITE;
697 	}
698 
699 	if (mask & GENERIC_EXECUTE) {
700 		mask &= ~GENERIC_EXECUTE;
701 		mask |= FILE_GENERIC_EXECUTE;
702 	}
703 
704 	return (mask);
705 }
706 
707 /*
708  * smb_ace_flags_tozfs
709  *
710  * This function maps the flags which have different values
711  * in Windows and Solaris. The ones with the same value are
712  * transferred untouched.
713  */
714 static uint16_t
715 smb_ace_flags_tozfs(uint8_t c_flags)
716 {
717 	uint16_t z_flags = 0;
718 
719 	if (c_flags & SUCCESSFUL_ACCESS_ACE_FLAG)
720 		z_flags |= ACE_SUCCESSFUL_ACCESS_ACE_FLAG;
721 
722 	if (c_flags & FAILED_ACCESS_ACE_FLAG)
723 		z_flags |= ACE_FAILED_ACCESS_ACE_FLAG;
724 
725 	if (c_flags & INHERITED_ACE)
726 		z_flags |= ACE_INHERITED_ACE;
727 
728 	z_flags |= (c_flags & ACE_INHERIT_FLAGS);
729 
730 	return (z_flags);
731 }
732 
733 static uint8_t
734 smb_ace_flags_fromzfs(uint16_t z_flags)
735 {
736 	uint8_t c_flags;
737 
738 	c_flags = z_flags & ACE_INHERIT_FLAGS;
739 
740 	if (z_flags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG)
741 		c_flags |= SUCCESSFUL_ACCESS_ACE_FLAG;
742 
743 	if (z_flags & ACE_FAILED_ACCESS_ACE_FLAG)
744 		c_flags |= FAILED_ACCESS_ACE_FLAG;
745 
746 	if (z_flags & ACE_INHERITED_ACE)
747 		c_flags |= INHERITED_ACE;
748 
749 	return (c_flags);
750 }
751 
752 static boolean_t
753 smb_ace_isvalid(smb_ace_t *ace, int which_acl)
754 {
755 	uint16_t min_len;
756 
757 	min_len = sizeof (smb_acehdr_t);
758 
759 	if (ace->se_hdr.se_bsize < min_len)
760 		return (B_FALSE);
761 
762 	if (smb_ace_is_access(ace->se_hdr.se_type) &&
763 	    (which_acl != SMB_DACL_SECINFO))
764 		return (B_FALSE);
765 
766 	if (smb_ace_is_audit(ace->se_hdr.se_type) &&
767 	    (which_acl != SMB_SACL_SECINFO))
768 		return (B_FALSE);
769 
770 	if (smb_ace_is_generic(ace->se_hdr.se_type)) {
771 		if (!smb_sid_isvalid(ace->se_sid))
772 			return (B_FALSE);
773 
774 		min_len += sizeof (ace->se_mask);
775 		min_len += smb_sid_len(ace->se_sid);
776 
777 		if (ace->se_hdr.se_bsize < min_len)
778 			return (B_FALSE);
779 	}
780 
781 	/*
782 	 * object-specific ACE validation will be added later.
783 	 */
784 	return (B_TRUE);
785 }
786