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