xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_sd.c (revision 80e2ca8596e3435bc3b76f3c597833ea0a87f85e)
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 
26 /*
27  * This module provides Security Descriptor handling functions.
28  */
29 
30 #include <strings.h>
31 #include <assert.h>
32 #include <smbsrv/ntifs.h>
33 #include <smbsrv/smb_idmap.h>
34 #include <smbsrv/ntstatus.h>
35 #include <smbsrv/libsmb.h>
36 
37 #define	SMB_SHR_ACE_READ_PERMS	(ACE_READ_PERMS | ACE_EXECUTE | ACE_SYNCHRONIZE)
38 #define	SMB_SHR_ACE_CONTROL_PERMS	(ACE_MODIFY_PERMS & (~ACE_DELETE_CHILD))
39 
40 /*
41  * This mapping table is provided to map permissions set by chmod
42  * using 'read_set' and 'modify_set' to what Windows share ACL GUI
43  * expects as Read and Control, respectively.
44  */
45 static struct {
46 	int am_ace_perms;
47 	int am_share_perms;
48 } smb_ace_map[] = {
49 	{ ACE_MODIFY_PERMS,	SMB_SHR_ACE_CONTROL_PERMS },
50 	{ ACE_READ_PERMS,	SMB_SHR_ACE_READ_PERMS }
51 };
52 
53 #define	SMB_ACE_MASK_MAP_SIZE	(sizeof (smb_ace_map)/sizeof (smb_ace_map[0]))
54 
55 static void smb_sd_set_sacl(smb_sd_t *, smb_acl_t *, boolean_t, int);
56 static void smb_sd_set_dacl(smb_sd_t *, smb_acl_t *, boolean_t, int);
57 
58 void
59 smb_sd_init(smb_sd_t *sd, uint8_t revision)
60 {
61 	bzero(sd, sizeof (smb_sd_t));
62 	sd->sd_revision = revision;
63 }
64 
65 /*
66  * smb_sd_term
67  *
68  * Free non-NULL members of 'sd' which has to be in
69  * absolute (pointer) form.
70  */
71 void
72 smb_sd_term(smb_sd_t *sd)
73 {
74 	assert(sd);
75 	assert((sd->sd_control & SE_SELF_RELATIVE) == 0);
76 
77 	smb_sid_free(sd->sd_owner);
78 	smb_sid_free(sd->sd_group);
79 	smb_acl_free(sd->sd_dacl);
80 	smb_acl_free(sd->sd_sacl);
81 
82 	bzero(sd, sizeof (smb_sd_t));
83 }
84 
85 uint32_t
86 smb_sd_len(smb_sd_t *sd, uint32_t secinfo)
87 {
88 	uint32_t length = SMB_SD_HDRSIZE;
89 
90 	if (secinfo & SMB_OWNER_SECINFO)
91 		length += smb_sid_len(sd->sd_owner);
92 
93 	if (secinfo & SMB_GROUP_SECINFO)
94 		length += smb_sid_len(sd->sd_group);
95 
96 	if (secinfo & SMB_DACL_SECINFO)
97 		length += smb_acl_len(sd->sd_dacl);
98 
99 	if (secinfo & SMB_SACL_SECINFO)
100 		length += smb_acl_len(sd->sd_sacl);
101 
102 	return (length);
103 }
104 
105 /*
106  * smb_sd_get_secinfo
107  *
108  * Return the security information mask for the specified security
109  * descriptor.
110  */
111 uint32_t
112 smb_sd_get_secinfo(smb_sd_t *sd)
113 {
114 	uint32_t sec_info = 0;
115 
116 	if (sd == NULL)
117 		return (0);
118 
119 	if (sd->sd_owner)
120 		sec_info |= SMB_OWNER_SECINFO;
121 
122 	if (sd->sd_group)
123 		sec_info |= SMB_GROUP_SECINFO;
124 
125 	if (sd->sd_dacl)
126 		sec_info |= SMB_DACL_SECINFO;
127 
128 	if (sd->sd_sacl)
129 		sec_info |= SMB_SACL_SECINFO;
130 
131 	return (sec_info);
132 }
133 
134 /*
135  * Adjust the Access Mask so that ZFS ACE mask and Windows ACE read mask match.
136  */
137 static int
138 smb_sd_adjust_read_mask(int mask)
139 {
140 	int i;
141 
142 	for (i = 0; i < SMB_ACE_MASK_MAP_SIZE; ++i) {
143 		if (smb_ace_map[i].am_ace_perms == mask)
144 			return (smb_ace_map[i].am_share_perms);
145 	}
146 
147 	return (mask);
148 }
149 
150 /*
151  * Get ZFS acl from the share path via acl_get() method.
152  */
153 static uint32_t
154 smb_sd_read_acl(char *path, smb_fssd_t *fs_sd)
155 {
156 	acl_t *z_acl;
157 	ace_t *z_ace;
158 
159 	fs_sd->sd_gid = fs_sd->sd_uid = 0;
160 	if (acl_trivial(path) != 1)
161 		return (NT_STATUS_INTERNAL_ERROR);
162 
163 	if (acl_get(path, ACL_NO_TRIVIAL, &z_acl) != 0)
164 		return (NT_STATUS_INTERNAL_ERROR);
165 
166 	if ((z_ace = (ace_t *)z_acl->acl_aclp) == NULL)
167 		return (NT_STATUS_INVALID_ACL);
168 
169 	for (int i = 0; i < z_acl->acl_cnt; i++, z_ace++)
170 		z_ace->a_access_mask =
171 		    smb_sd_adjust_read_mask(z_ace->a_access_mask);
172 
173 	fs_sd->sd_zdacl = z_acl;
174 	fs_sd->sd_zsacl = NULL;
175 	return (NT_STATUS_SUCCESS);
176 }
177 
178 /*
179  * smb_sd_read
180  *
181  * Reads ZFS acl from filesystem using acl_get() method. Convert the ZFS acl to
182  * a Win SD and return the Win SD in absolute form.
183  *
184  * NOTE: upon successful return caller MUST free the memory allocated
185  * for the returned SD by calling smb_sd_term().
186  */
187 uint32_t
188 smb_sd_read(char *path, smb_sd_t *sd, uint32_t secinfo)
189 {
190 	smb_fssd_t fs_sd;
191 	uint32_t status = NT_STATUS_SUCCESS;
192 	uint32_t sd_flags;
193 	int error;
194 
195 	sd_flags = SMB_FSSD_FLAGS_DIR;
196 	smb_fssd_init(&fs_sd, secinfo, sd_flags);
197 
198 	error = smb_sd_read_acl(path, &fs_sd);
199 	if (error != NT_STATUS_SUCCESS) {
200 		smb_fssd_term(&fs_sd);
201 		return (error);
202 	}
203 
204 	status = smb_sd_fromfs(&fs_sd, sd);
205 	smb_fssd_term(&fs_sd);
206 
207 	return (status);
208 }
209 
210 /*
211  * Apply ZFS acl to the share path via acl_set() method.
212  * A NULL ACL pointer here represents an error.
213  * Null or empty ACLs are handled in smb_sd_tofs().
214  */
215 static uint32_t
216 smb_sd_write_acl(char *path, smb_fssd_t *fs_sd)
217 {
218 	acl_t *z_acl;
219 	ace_t *z_ace;
220 	uint32_t status = NT_STATUS_SUCCESS;
221 
222 	z_acl = fs_sd->sd_zdacl;
223 	if (z_acl == NULL)
224 		return (NT_STATUS_INVALID_ACL);
225 
226 	z_ace = (ace_t *)z_acl->acl_aclp;
227 	if (z_ace == NULL)
228 		return (NT_STATUS_INVALID_ACL);
229 
230 	fs_sd->sd_gid = fs_sd->sd_uid = 0;
231 	if (acl_set(path, z_acl) != 0)
232 		status = NT_STATUS_INTERNAL_ERROR;
233 
234 	return (status);
235 }
236 
237 /*
238  * smb_sd_write
239  *
240  * Takes a Win SD in absolute form, converts it to
241  * ZFS acl and applies the acl to the share path via acl_set() method.
242  */
243 uint32_t
244 smb_sd_write(char *path, smb_sd_t *sd, uint32_t secinfo)
245 {
246 	smb_fssd_t fs_sd;
247 	uint32_t status = NT_STATUS_SUCCESS;
248 	uint32_t sd_flags;
249 	int error;
250 
251 	sd_flags = SMB_FSSD_FLAGS_DIR;
252 	smb_fssd_init(&fs_sd, secinfo, sd_flags);
253 
254 	error = smb_sd_tofs(sd, &fs_sd);
255 	if (error != NT_STATUS_SUCCESS) {
256 		smb_fssd_term(&fs_sd);
257 		return (error);
258 	}
259 
260 	status = smb_sd_write_acl(path, &fs_sd);
261 	smb_fssd_term(&fs_sd);
262 
263 	return (status);
264 }
265 
266 /*
267  * smb_sd_tofs
268  *
269  * Creates a filesystem security structure based on the given
270  * Windows security descriptor.
271  */
272 uint32_t
273 smb_sd_tofs(smb_sd_t *sd, smb_fssd_t *fs_sd)
274 {
275 	smb_sid_t *sid;
276 	uint32_t status = NT_STATUS_SUCCESS;
277 	uint16_t sd_control;
278 	idmap_stat idm_stat;
279 	int idtype;
280 	int flags = 0;
281 
282 	sd_control = sd->sd_control;
283 
284 	/*
285 	 * ZFS only has one set of flags so for now only
286 	 * Windows DACL flags are taken into account.
287 	 */
288 	if (sd_control & SE_DACL_DEFAULTED)
289 		flags |= ACL_DEFAULTED;
290 	if (sd_control & SE_DACL_AUTO_INHERITED)
291 		flags |= ACL_AUTO_INHERIT;
292 	if (sd_control & SE_DACL_PROTECTED)
293 		flags |= ACL_PROTECTED;
294 
295 	if (fs_sd->sd_flags & SMB_FSSD_FLAGS_DIR)
296 		flags |= ACL_IS_DIR;
297 
298 	/* Owner */
299 	if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) {
300 		sid = sd->sd_owner;
301 		if (!smb_sid_isvalid(sid))
302 			return (NT_STATUS_INVALID_SID);
303 
304 		idtype = SMB_IDMAP_USER;
305 		idm_stat = smb_idmap_getid(sid, &fs_sd->sd_uid, &idtype);
306 		if (idm_stat != IDMAP_SUCCESS) {
307 			return (NT_STATUS_NONE_MAPPED);
308 		}
309 	}
310 
311 	/* Group */
312 	if (fs_sd->sd_secinfo & SMB_GROUP_SECINFO) {
313 		sid = sd->sd_group;
314 		if (!smb_sid_isvalid(sid))
315 			return (NT_STATUS_INVALID_SID);
316 
317 		idtype = SMB_IDMAP_GROUP;
318 		idm_stat = smb_idmap_getid(sid, &fs_sd->sd_gid, &idtype);
319 		if (idm_stat != IDMAP_SUCCESS) {
320 			return (NT_STATUS_NONE_MAPPED);
321 		}
322 	}
323 
324 	/* DACL */
325 	if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) {
326 		if (sd->sd_control & SE_DACL_PRESENT) {
327 			status = smb_acl_to_zfs(sd->sd_dacl, flags,
328 			    SMB_DACL_SECINFO, &fs_sd->sd_zdacl);
329 			if (status != NT_STATUS_SUCCESS)
330 				return (status);
331 		}
332 		else
333 			return (NT_STATUS_INVALID_ACL);
334 	}
335 
336 	/* SACL */
337 	if (fs_sd->sd_secinfo & SMB_SACL_SECINFO) {
338 		if (sd->sd_control & SE_SACL_PRESENT) {
339 			status = smb_acl_to_zfs(sd->sd_sacl, flags,
340 			    SMB_SACL_SECINFO, &fs_sd->sd_zsacl);
341 			if (status != NT_STATUS_SUCCESS) {
342 				return (status);
343 			}
344 		} else {
345 			return (NT_STATUS_INVALID_ACL);
346 		}
347 	}
348 
349 	return (status);
350 }
351 
352 /*
353  * smb_sd_fromfs
354  *
355  * Makes an Windows style security descriptor in absolute form
356  * based on the given filesystem security information.
357  *
358  * Should call smb_sd_term() for the returned sd to free allocated
359  * members.
360  */
361 uint32_t
362 smb_sd_fromfs(smb_fssd_t *fs_sd, smb_sd_t *sd)
363 {
364 	uint32_t status = NT_STATUS_SUCCESS;
365 	smb_acl_t *acl = NULL;
366 	smb_sid_t *sid;
367 	idmap_stat idm_stat;
368 
369 	assert(fs_sd);
370 	assert(sd);
371 
372 	smb_sd_init(sd, SECURITY_DESCRIPTOR_REVISION);
373 
374 	/* Owner */
375 	if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) {
376 		idm_stat = smb_idmap_getsid(fs_sd->sd_uid,
377 		    SMB_IDMAP_USER, &sid);
378 
379 		if (idm_stat != IDMAP_SUCCESS) {
380 			smb_sd_term(sd);
381 			return (NT_STATUS_NONE_MAPPED);
382 		}
383 
384 		sd->sd_owner = sid;
385 	}
386 
387 	/* Group */
388 	if (fs_sd->sd_secinfo & SMB_GROUP_SECINFO) {
389 		idm_stat = smb_idmap_getsid(fs_sd->sd_gid,
390 		    SMB_IDMAP_GROUP, &sid);
391 
392 		if (idm_stat != IDMAP_SUCCESS) {
393 			smb_sd_term(sd);
394 			return (NT_STATUS_NONE_MAPPED);
395 		}
396 
397 		sd->sd_group = sid;
398 	}
399 
400 	/* DACL */
401 	if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) {
402 		if (fs_sd->sd_zdacl != NULL) {
403 			acl = smb_acl_from_zfs(fs_sd->sd_zdacl);
404 			if (acl == NULL) {
405 				smb_sd_term(sd);
406 				return (NT_STATUS_INTERNAL_ERROR);
407 			}
408 
409 			/*
410 			 * Need to sort the ACL before send it to Windows
411 			 * clients. Winodws GUI is sensitive about the order
412 			 * of ACEs.
413 			 */
414 			smb_acl_sort(acl);
415 			smb_sd_set_dacl(sd, acl, B_TRUE,
416 			    fs_sd->sd_zdacl->acl_flags);
417 		} else {
418 			smb_sd_set_dacl(sd, NULL, B_FALSE, 0);
419 		}
420 	}
421 
422 	/* SACL */
423 	if (fs_sd->sd_secinfo & SMB_SACL_SECINFO) {
424 		if (fs_sd->sd_zsacl != NULL) {
425 			acl = smb_acl_from_zfs(fs_sd->sd_zsacl);
426 			if (acl == NULL) {
427 				smb_sd_term(sd);
428 				return (NT_STATUS_INTERNAL_ERROR);
429 			}
430 
431 			smb_sd_set_sacl(sd, acl, B_TRUE,
432 			    fs_sd->sd_zsacl->acl_flags);
433 		} else {
434 			smb_sd_set_sacl(sd, NULL, B_FALSE, 0);
435 		}
436 	}
437 
438 	return (status);
439 }
440 
441 static void
442 smb_sd_set_dacl(smb_sd_t *sd, smb_acl_t *acl, boolean_t present, int flags)
443 {
444 	assert((sd->sd_control & SE_SELF_RELATIVE) == 0);
445 
446 	sd->sd_dacl = acl;
447 
448 	if (flags & ACL_DEFAULTED)
449 		sd->sd_control |= SE_DACL_DEFAULTED;
450 	if (flags & ACL_AUTO_INHERIT)
451 		sd->sd_control |= SE_DACL_AUTO_INHERITED;
452 	if (flags & ACL_PROTECTED)
453 		sd->sd_control |= SE_DACL_PROTECTED;
454 
455 	if (present)
456 		sd->sd_control |= SE_DACL_PRESENT;
457 }
458 
459 static void
460 smb_sd_set_sacl(smb_sd_t *sd, smb_acl_t *acl, boolean_t present, int flags)
461 {
462 	assert((sd->sd_control & SE_SELF_RELATIVE) == 0);
463 
464 	sd->sd_sacl = acl;
465 
466 	if (flags & ACL_DEFAULTED)
467 		sd->sd_control |= SE_SACL_DEFAULTED;
468 	if (flags & ACL_AUTO_INHERIT)
469 		sd->sd_control |= SE_SACL_AUTO_INHERITED;
470 	if (flags & ACL_PROTECTED)
471 		sd->sd_control |= SE_SACL_PROTECTED;
472 
473 	if (present)
474 		sd->sd_control |= SE_SACL_PRESENT;
475 }
476 
477 /*
478  * smb_fssd_init
479  *
480  * Initializes the given FS SD structure.
481  */
482 void
483 smb_fssd_init(smb_fssd_t *fs_sd, uint32_t secinfo, uint32_t flags)
484 {
485 	bzero(fs_sd, sizeof (smb_fssd_t));
486 	fs_sd->sd_secinfo = secinfo;
487 	fs_sd->sd_flags = flags;
488 }
489 
490 /*
491  * smb_fssd_term
492  *
493  * Frees allocated memory for acl fields.
494  */
495 void
496 smb_fssd_term(smb_fssd_t *fs_sd)
497 {
498 	assert(fs_sd);
499 
500 	acl_free(fs_sd->sd_zdacl);
501 	acl_free(fs_sd->sd_zsacl);
502 
503 	bzero(fs_sd, sizeof (smb_fssd_t));
504 }
505