xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_acl.c (revision bbf6f00c25b6a2bed23c35eac6d62998ecdb338c)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stddef.h>
27 #include <strings.h>
28 #include <assert.h>
29 
30 #include <smbsrv/smb.h>
31 #include <smbsrv/smb_sid.h>
32 #include <smbsrv/smb_idmap.h>
33 
34 #define	ACE_ALL_TYPES	0x001F
35 
36 /*
37  * ACE groups within a DACL
38  *
39  * This is from lower to higher ACE order priority
40  */
41 #define	SMB_AG_START		0
42 #define	SMB_AG_ALW_INHRT	0
43 #define	SMB_AG_DNY_INHRT	1
44 #define	SMB_AG_ALW_DRCT		2
45 #define	SMB_AG_DNY_DRCT		3
46 #define	SMB_AG_NUM		4
47 
48 /*
49  * SID for Everyone group: S-1-1-0.
50  */
51 smb_sid_t everyone_sid = {
52 	NT_SID_REVISION,
53 	1,
54 	NT_SECURITY_WORLD_AUTH,
55 	{ 0 }
56 };
57 
58 #define	DEFAULT_DACL_ACENUM	2
59 acl_t *acl_alloc(enum acl_type);
60 
61 static idmap_stat smb_fsacl_getsids(smb_idmap_batch_t *, acl_t *, uid_t, gid_t);
62 static acl_t *smb_fsacl_null_empty(boolean_t);
63 static uint16_t smb_ace_len(smb_ace_t *);
64 static uint32_t smb_ace_mask_g2s(uint32_t);
65 static uint16_t smb_ace_flags_tozfs(uint8_t);
66 static uint8_t smb_ace_flags_fromzfs(uint16_t);
67 
68 smb_acl_t *
69 smb_acl_alloc(uint8_t revision, uint16_t bsize, uint16_t acecnt)
70 {
71 	smb_acl_t *acl;
72 	int size;
73 
74 	size = sizeof (smb_acl_t) + (acecnt * sizeof (smb_ace_t));
75 	if ((acl = malloc(size)) == NULL)
76 		return (NULL);
77 
78 	acl->sl_revision = revision;
79 	acl->sl_bsize = bsize;
80 	acl->sl_acecnt = acecnt;
81 	acl->sl_aces = (smb_ace_t *)(acl + 1);
82 
83 	list_create(&acl->sl_sorted, sizeof (smb_ace_t),
84 	    offsetof(smb_ace_t, se_sln));
85 	return (acl);
86 }
87 
88 void
89 smb_acl_free(smb_acl_t *acl)
90 {
91 	int i;
92 	void *ace;
93 
94 	if (acl == NULL)
95 		return;
96 
97 	for (i = 0; i < acl->sl_acecnt; i++)
98 		smb_sid_free(acl->sl_aces[i].se_sid);
99 
100 	while ((ace = list_head(&acl->sl_sorted)) != NULL)
101 		list_remove(&acl->sl_sorted, ace);
102 	list_destroy(&acl->sl_sorted);
103 	free(acl);
104 
105 }
106 
107 /*
108  * smb_acl_len
109  *
110  * Returns the size of given ACL in bytes. Note that this
111  * is not an in-memory size, it's the ACL's size as it would
112  * appear on the wire
113  */
114 uint16_t
115 smb_acl_len(smb_acl_t *acl)
116 {
117 	return ((acl) ? acl->sl_bsize : 0);
118 }
119 
120 /*ARGSUSED*/
121 boolean_t
122 smb_acl_isvalid(smb_acl_t *acl, int which_acl)
123 {
124 	if (acl->sl_bsize < SMB_ACL_HDRSIZE)
125 		return (B_FALSE);
126 
127 	if (acl->sl_revision != ACL_REVISION) {
128 		/*
129 		 * we are rejecting ACLs with object-specific ACEs for now
130 		 */
131 		return (B_FALSE);
132 	}
133 
134 	return (B_TRUE);
135 }
136 
137 /*
138  * smb_acl_sort
139  *
140  * Sorts the given ACL in place if it needs to be sorted.
141  *
142  * The following is an excerpt from MSDN website.
143  *
144  * Order of ACEs in a DACL
145  *
146  * For Windows NT versions 4.0 and earlier, the preferred order of ACEs
147  * is simple: In a DACL, all access-denied ACEs should precede any
148  * access-allowed ACEs.
149  *
150  * For Windows 2000 or later, the proper order of ACEs is more complicated
151  * because of the introduction of object-specific ACEs and automatic
152  * inheritance.
153  *
154  * The following describes the preferred order:
155  *
156  * To ensure that noninherited ACEs have precedence over inherited ACEs,
157  * place all noninherited ACEs in a group before any inherited ACEs. This
158  * ordering ensures, for example, that a noninherited access-denied ACE
159  * is enforced regardless of any inherited ACE that allows access.
160  * Within the groups of noninherited ACEs and inherited ACEs, order ACEs
161  * according to ACE type, as the following shows:
162  * 	. Access-denied ACEs that apply to the object itself
163  * 	. Access-denied ACEs that apply to a subobject of the
164  *	  object, such as a property set or property
165  * 	. Access-allowed ACEs that apply to the object itself
166  * 	. Access-allowed ACEs that apply to a subobject of the object
167  *
168  * So, here is the desired ACE order
169  *
170  * deny-direct, allow-direct, deny-inherited, allow-inherited
171  *
172  * Of course, not all ACE types are required in an ACL.
173  */
174 void
175 smb_acl_sort(smb_acl_t *acl)
176 {
177 	list_t ace_grps[SMB_AG_NUM];
178 	list_t *alist;
179 	smb_ace_t *ace;
180 	uint8_t ace_flags;
181 	int ag, i;
182 
183 	assert(acl);
184 
185 	if (acl->sl_acecnt == 0) {
186 		/*
187 		 * ACL with no entry is a valid ACL and it means
188 		 * no access for anybody.
189 		 */
190 		return;
191 	}
192 
193 	for (i = SMB_AG_START; i < SMB_AG_NUM; i++) {
194 		list_create(&ace_grps[i], sizeof (smb_ace_t),
195 		    offsetof(smb_ace_t, se_sln));
196 	}
197 
198 	for (i = 0, ace = acl->sl_aces; i < acl->sl_acecnt; ++i, ace++) {
199 		ace_flags = ace->se_hdr.se_flags;
200 
201 		switch (ace->se_hdr.se_type) {
202 		case ACCESS_DENIED_ACE_TYPE:
203 			ag = (ace_flags & INHERITED_ACE) ?
204 			    SMB_AG_DNY_INHRT : SMB_AG_DNY_DRCT;
205 			break;
206 
207 		case ACCESS_ALLOWED_ACE_TYPE:
208 			ag = (ace_flags & INHERITED_ACE) ?
209 			    SMB_AG_ALW_INHRT : SMB_AG_ALW_DRCT;
210 			break;
211 
212 		default:
213 			/*
214 			 * This is the lowest priority group so we put
215 			 * evertything unknown here.
216 			 */
217 			ag = SMB_AG_ALW_INHRT;
218 			break;
219 		}
220 
221 		/* Add the ACE to the selected group */
222 		list_insert_tail(&ace_grps[ag], ace);
223 	}
224 
225 	/*
226 	 * start with highest priority ACE group and append
227 	 * the ACEs to the ACL.
228 	 */
229 	for (i = SMB_AG_NUM - 1; i >= SMB_AG_START; i--) {
230 		alist = &ace_grps[i];
231 		while ((ace = list_head(alist)) != NULL) {
232 			list_remove(alist, ace);
233 			list_insert_tail(&acl->sl_sorted, ace);
234 		}
235 		list_destroy(alist);
236 	}
237 }
238 
239 /*
240  * smb_acl_from_zfs
241  *
242  * Converts given ZFS ACL to a Windows ACL.
243  *
244  * A pointer to allocated memory for the Windows ACL will be
245  * returned upon successful conversion.
246  */
247 smb_acl_t *
248 smb_acl_from_zfs(acl_t *zacl, uid_t uid, gid_t gid)
249 {
250 	ace_t *zace;
251 	int numaces;
252 	smb_acl_t *acl;
253 	smb_ace_t *ace;
254 	smb_idmap_batch_t sib;
255 	smb_idmap_t *sim;
256 	idmap_stat idm_stat;
257 
258 	idm_stat = smb_idmap_batch_create(&sib, zacl->acl_cnt,
259 	    SMB_IDMAP_ID2SID);
260 	if (idm_stat != IDMAP_SUCCESS)
261 		return (NULL);
262 
263 	if (smb_fsacl_getsids(&sib, zacl, uid, gid) != IDMAP_SUCCESS) {
264 		smb_idmap_batch_destroy(&sib);
265 		return (NULL);
266 	}
267 
268 	acl = smb_acl_alloc(ACL_REVISION, SMB_ACL_HDRSIZE, zacl->acl_cnt);
269 
270 	sim = sib.sib_maps;
271 	for (numaces = 0, zace = zacl->acl_aclp;
272 	    numaces < zacl->acl_cnt;
273 	    zace++, numaces++, sim++) {
274 		assert(sim->sim_sid);
275 		if (sim->sim_sid == NULL) {
276 			smb_acl_free(acl);
277 			acl = NULL;
278 			break;
279 		}
280 
281 		ace = &acl->sl_aces[numaces];
282 		ace->se_hdr.se_type = zace->a_type;
283 		ace->se_hdr.se_flags = smb_ace_flags_fromzfs(zace->a_flags);
284 		ace->se_mask = zace->a_access_mask;
285 		ace->se_sid = smb_sid_dup(sim->sim_sid);
286 		ace->se_hdr.se_bsize = smb_ace_len(ace);
287 
288 		acl->sl_bsize += ace->se_hdr.se_bsize;
289 	}
290 
291 	smb_idmap_batch_destroy(&sib);
292 	return (acl);
293 }
294 
295 /*
296  * smb_acl_to_zfs
297  *
298  * Converts given Windows ACL to a ZFS ACL.
299  *
300  * fs_acl will contain a pointer to the created ZFS ACL.
301  * The allocated memory should be freed by calling
302  * smb_fsacl_free().
303  *
304  * Since the output parameter, fs_acl, is allocated in this
305  * function, the caller has to make sure *fs_acl is NULL which
306  * means it's not pointing to any memory.
307  */
308 uint32_t
309 smb_acl_to_zfs(smb_acl_t *acl, uint32_t flags, int which_acl, acl_t **fs_acl)
310 {
311 	smb_ace_t *ace;
312 	acl_t *zacl;
313 	ace_t *zace;
314 	smb_idmap_batch_t sib;
315 	smb_idmap_t *sim;
316 	idmap_stat idm_stat;
317 	int i;
318 
319 	assert(fs_acl);
320 	assert(*fs_acl == NULL);
321 
322 	if (acl && !smb_acl_isvalid(acl, which_acl))
323 		return (NT_STATUS_INVALID_ACL);
324 
325 	if ((acl == NULL) || (acl->sl_acecnt == 0)) {
326 		if (which_acl == SMB_DACL_SECINFO) {
327 			*fs_acl = smb_fsacl_null_empty(acl == NULL);
328 		}
329 
330 		return (NT_STATUS_SUCCESS);
331 	}
332 
333 	idm_stat = smb_idmap_batch_create(&sib, acl->sl_acecnt,
334 	    SMB_IDMAP_SID2ID);
335 	if (idm_stat != IDMAP_SUCCESS)
336 		return (NT_STATUS_INTERNAL_ERROR);
337 
338 	zacl = smb_fsacl_alloc(acl->sl_acecnt, flags);
339 
340 	zace = zacl->acl_aclp;
341 	ace = acl->sl_aces;
342 	sim = sib.sib_maps;
343 
344 	for (i = 0; i < acl->sl_acecnt; i++, zace++, ace++, sim++) {
345 		zace->a_type = ace->se_hdr.se_type & ACE_ALL_TYPES;
346 		zace->a_access_mask = smb_ace_mask_g2s(ace->se_mask);
347 		zace->a_flags = smb_ace_flags_tozfs(ace->se_hdr.se_flags);
348 
349 		if (smb_sid_cmp(ace->se_sid, &everyone_sid))
350 			zace->a_flags |= ACE_EVERYONE;
351 		else {
352 			sim->sim_id = &zace->a_who;
353 			idm_stat = smb_idmap_batch_getid(sib.sib_idmaph, sim,
354 			    ace->se_sid, -1);
355 
356 			if (idm_stat != IDMAP_SUCCESS) {
357 				smb_fsacl_free(zacl);
358 				smb_idmap_batch_destroy(&sib);
359 				return (NT_STATUS_INTERNAL_ERROR);
360 			}
361 		}
362 	}
363 
364 	idm_stat = smb_idmap_batch_getmappings(&sib);
365 	if (idm_stat != IDMAP_SUCCESS) {
366 		smb_fsacl_free(zacl);
367 		smb_idmap_batch_destroy(&sib);
368 		return (NT_STATUS_NONE_MAPPED);
369 	}
370 
371 	/*
372 	 * Set the ACEs group flag based on the type of ID returned.
373 	 */
374 	zace = zacl->acl_aclp;
375 	ace = acl->sl_aces;
376 	sim = sib.sib_maps;
377 	for (i = 0; i < acl->sl_acecnt; i++, zace++, ace++, sim++) {
378 		if (zace->a_flags & ACE_EVERYONE)
379 			continue;
380 
381 		if (sim->sim_idtype == SMB_IDMAP_GROUP)
382 			zace->a_flags |= ACE_IDENTIFIER_GROUP;
383 	}
384 
385 	smb_idmap_batch_destroy(&sib);
386 
387 	*fs_acl = zacl;
388 	return (NT_STATUS_SUCCESS);
389 }
390 
391 /*
392  * smb_fsacl_getsids
393  *
394  * Batch all the uid/gid in given ZFS ACL to get their corresponding SIDs.
395  */
396 static idmap_stat
397 smb_fsacl_getsids(smb_idmap_batch_t *sib, acl_t *zacl, uid_t uid, gid_t gid)
398 {
399 	ace_t *zace;
400 	idmap_stat idm_stat;
401 	smb_idmap_t *sim;
402 	uid_t id;
403 	int i, idtype;
404 
405 	sim = sib->sib_maps;
406 
407 	for (i = 0, zace = zacl->acl_aclp; i < zacl->acl_cnt;
408 	    zace++, i++, sim++) {
409 		switch (zace->a_flags & ACE_TYPE_FLAGS) {
410 		case ACE_OWNER:
411 			id = uid;
412 			idtype = SMB_IDMAP_USER;
413 			break;
414 
415 		case (ACE_GROUP | ACE_IDENTIFIER_GROUP):
416 			/* owning group */
417 			id = gid;
418 			idtype = SMB_IDMAP_GROUP;
419 			break;
420 
421 		case ACE_IDENTIFIER_GROUP:
422 			/* regular group */
423 			id = zace->a_who;
424 			idtype = SMB_IDMAP_GROUP;
425 			break;
426 
427 		case ACE_EVERYONE:
428 			idtype = SMB_IDMAP_EVERYONE;
429 			break;
430 
431 		default:
432 			/* user entry */
433 			id = zace->a_who;
434 			idtype = SMB_IDMAP_USER;
435 		}
436 
437 		idm_stat = smb_idmap_batch_getsid(sib->sib_idmaph, sim,
438 		    id, idtype);
439 
440 		if (idm_stat != IDMAP_SUCCESS) {
441 			return (idm_stat);
442 		}
443 	}
444 
445 	idm_stat = smb_idmap_batch_getmappings(sib);
446 	return (idm_stat);
447 }
448 
449 /*
450  * smb_fsacl_null_empty
451  *
452  * NULL DACL means everyone full-access
453  * Empty DACL means everyone full-deny
454  *
455  * ZFS ACL must have at least one entry so smb server has
456  * to simulate the aforementioned expected behavior by adding
457  * an entry in case the requested DACL is null or empty. Adding
458  * a everyone full-deny entry has proved to be problematic in
459  * tests since a deny entry takes precedence over allow entries.
460  * So, instead of adding a everyone full-deny, an owner ACE with
461  * owner implicit permissions will be set.
462  */
463 static acl_t *
464 smb_fsacl_null_empty(boolean_t null)
465 {
466 	acl_t *zacl;
467 	ace_t *zace;
468 
469 	zacl = smb_fsacl_alloc(1, ACL_AUTO_INHERIT);
470 	zace = zacl->acl_aclp;
471 
472 	zace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
473 	if (null) {
474 		zace->a_access_mask = ACE_ALL_PERMS;
475 		zace->a_flags = ACE_EVERYONE;
476 	} else {
477 		zace->a_access_mask = ACE_READ_ACL | ACE_WRITE_ACL |
478 		    ACE_READ_ATTRIBUTES;
479 		zace->a_flags = ACE_OWNER;
480 	}
481 
482 	return (zacl);
483 }
484 
485 /*
486  * FS ACL (acl_t) Functions
487  */
488 acl_t *
489 smb_fsacl_alloc(int acenum, int flags)
490 {
491 	acl_t *acl;
492 
493 	acl = acl_alloc(ACE_T);
494 	acl->acl_cnt = acenum;
495 	if ((acl->acl_aclp = malloc(acl->acl_entry_size * acenum)) == NULL)
496 		return (NULL);
497 
498 	acl->acl_flags = flags;
499 	return (acl);
500 }
501 
502 void
503 smb_fsacl_free(acl_t *acl)
504 {
505 	if (acl)
506 		acl_free(acl);
507 }
508 
509 /*
510  * ACE Functions
511  */
512 
513 /*
514  * smb_ace_len
515  *
516  * Returns the length of the given ACE as it appears in an
517  * ACL on the wire (i.e. a flat buffer which contains the SID)
518  */
519 static uint16_t
520 smb_ace_len(smb_ace_t *ace)
521 {
522 	assert(ace);
523 	assert(ace->se_sid);
524 
525 	if (ace == NULL)
526 		return (0);
527 
528 	return (SMB_ACE_HDRSIZE + sizeof (ace->se_mask) +
529 	    smb_sid_len(ace->se_sid));
530 }
531 
532 /*
533  * smb_ace_mask_g2s
534  *
535  * Converts generic access bits in the given mask (if any)
536  * to file specific bits. Generic access masks shouldn't be
537  * stored in filesystem ACEs.
538  */
539 static uint32_t
540 smb_ace_mask_g2s(uint32_t mask)
541 {
542 	if (mask & GENERIC_ALL) {
543 		mask &= ~(GENERIC_ALL | GENERIC_READ | GENERIC_WRITE
544 		    | GENERIC_EXECUTE);
545 
546 		mask |= FILE_ALL_ACCESS;
547 		return (mask);
548 	}
549 
550 	if (mask & GENERIC_READ) {
551 		mask &= ~GENERIC_READ;
552 		mask |= FILE_GENERIC_READ;
553 	}
554 
555 	if (mask & GENERIC_WRITE) {
556 		mask &= ~GENERIC_WRITE;
557 		mask |= FILE_GENERIC_WRITE;
558 	}
559 
560 	if (mask & GENERIC_EXECUTE) {
561 		mask &= ~GENERIC_EXECUTE;
562 		mask |= FILE_GENERIC_EXECUTE;
563 	}
564 
565 	return (mask);
566 }
567 
568 /*
569  * smb_ace_flags_tozfs
570  *
571  * This function maps the flags which have different values
572  * in Windows and Solaris. The ones with the same value are
573  * transferred untouched.
574  */
575 static uint16_t
576 smb_ace_flags_tozfs(uint8_t c_flags)
577 {
578 	uint16_t z_flags = 0;
579 
580 	if (c_flags & SUCCESSFUL_ACCESS_ACE_FLAG)
581 		z_flags |= ACE_SUCCESSFUL_ACCESS_ACE_FLAG;
582 
583 	if (c_flags & FAILED_ACCESS_ACE_FLAG)
584 		z_flags |= ACE_FAILED_ACCESS_ACE_FLAG;
585 
586 	if (c_flags & INHERITED_ACE)
587 		z_flags |= ACE_INHERITED_ACE;
588 
589 	z_flags |= (c_flags & ACE_INHERIT_FLAGS);
590 
591 	return (z_flags);
592 }
593 
594 static uint8_t
595 smb_ace_flags_fromzfs(uint16_t z_flags)
596 {
597 	uint8_t c_flags;
598 
599 	c_flags = z_flags & ACE_INHERIT_FLAGS;
600 
601 	if (z_flags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG)
602 		c_flags |= SUCCESSFUL_ACCESS_ACE_FLAG;
603 
604 	if (z_flags & ACE_FAILED_ACCESS_ACE_FLAG)
605 		c_flags |= FAILED_ACCESS_ACE_FLAG;
606 
607 	if (z_flags & ACE_INHERITED_ACE)
608 		c_flags |= INHERITED_ACE;
609 
610 	return (c_flags);
611 }
612