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