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