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 <errno.h>
33#include <smbsrv/ntifs.h>
34#include <smbsrv/smb_idmap.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 */
45static 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
55static void smb_sd_set_sacl(smb_sd_t *, smb_acl_t *, boolean_t, int);
56static void smb_sd_set_dacl(smb_sd_t *, smb_acl_t *, boolean_t, int);
57
58void
59smb_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 */
71void
72smb_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
85uint32_t
86smb_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 */
111uint32_t
112smb_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 */
137static int
138smb_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 */
153static uint32_t
154smb_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
161	errno = 0;
162	if (acl_get(path, 0, &z_acl) != 0) {
163		switch (errno) {
164		case EACCES:
165			return (NT_STATUS_ACCESS_DENIED);
166		case ENOENT:
167			return (NT_STATUS_OBJECT_PATH_NOT_FOUND);
168		default:
169			return (NT_STATUS_INTERNAL_ERROR);
170		}
171	}
172
173	if ((z_ace = (ace_t *)z_acl->acl_aclp) == NULL)
174		return (NT_STATUS_INVALID_ACL);
175
176	for (int i = 0; i < z_acl->acl_cnt; i++, z_ace++)
177		z_ace->a_access_mask =
178		    smb_sd_adjust_read_mask(z_ace->a_access_mask);
179
180	fs_sd->sd_zdacl = z_acl;
181	fs_sd->sd_zsacl = NULL;
182	return (NT_STATUS_SUCCESS);
183}
184
185/*
186 * smb_sd_read
187 *
188 * Reads ZFS acl from filesystem using acl_get() method. Convert the ZFS acl to
189 * a Win SD and return the Win SD in absolute form.
190 *
191 * NOTE: upon successful return caller MUST free the memory allocated
192 * for the returned SD by calling smb_sd_term().
193 */
194uint32_t
195smb_sd_read(char *path, smb_sd_t *sd, uint32_t secinfo)
196{
197	smb_fssd_t fs_sd;
198	uint32_t status = NT_STATUS_SUCCESS;
199	uint32_t sd_flags;
200	int error;
201
202	sd_flags = SMB_FSSD_FLAGS_DIR;
203	smb_fssd_init(&fs_sd, secinfo, sd_flags);
204
205	error = smb_sd_read_acl(path, &fs_sd);
206	if (error != NT_STATUS_SUCCESS) {
207		smb_fssd_term(&fs_sd);
208		return (error);
209	}
210
211	status = smb_sd_fromfs(&fs_sd, sd);
212	smb_fssd_term(&fs_sd);
213
214	return (status);
215}
216
217/*
218 * Apply ZFS acl to the share path via acl_set() method.
219 * A NULL ACL pointer here represents an error.
220 * Null or empty ACLs are handled in smb_sd_tofs().
221 */
222static uint32_t
223smb_sd_write_acl(char *path, smb_fssd_t *fs_sd)
224{
225	acl_t *z_acl;
226	ace_t *z_ace;
227	uint32_t status = NT_STATUS_SUCCESS;
228
229	z_acl = fs_sd->sd_zdacl;
230	if (z_acl == NULL)
231		return (NT_STATUS_INVALID_ACL);
232
233	z_ace = (ace_t *)z_acl->acl_aclp;
234	if (z_ace == NULL)
235		return (NT_STATUS_INVALID_ACL);
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 */
250uint32_t
251smb_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 */
279uint32_t
280smb_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 */
368uint32_t
369smb_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);
411			if (acl == NULL) {
412				smb_sd_term(sd);
413				return (NT_STATUS_INTERNAL_ERROR);
414			}
415
416			/*
417			 * Need to sort the ACL before send it to Windows
418			 * clients. Winodws GUI is sensitive about the order
419			 * of ACEs.
420			 */
421			smb_acl_sort(acl);
422			smb_sd_set_dacl(sd, acl, B_TRUE,
423			    fs_sd->sd_zdacl->acl_flags);
424		} else {
425			smb_sd_set_dacl(sd, NULL, B_FALSE, 0);
426		}
427	}
428
429	/* SACL */
430	if (fs_sd->sd_secinfo & SMB_SACL_SECINFO) {
431		if (fs_sd->sd_zsacl != NULL) {
432			acl = smb_acl_from_zfs(fs_sd->sd_zsacl);
433			if (acl == NULL) {
434				smb_sd_term(sd);
435				return (NT_STATUS_INTERNAL_ERROR);
436			}
437
438			smb_sd_set_sacl(sd, acl, B_TRUE,
439			    fs_sd->sd_zsacl->acl_flags);
440		} else {
441			smb_sd_set_sacl(sd, NULL, B_FALSE, 0);
442		}
443	}
444
445	return (status);
446}
447
448static void
449smb_sd_set_dacl(smb_sd_t *sd, smb_acl_t *acl, boolean_t present, int flags)
450{
451	assert((sd->sd_control & SE_SELF_RELATIVE) == 0);
452
453	sd->sd_dacl = acl;
454
455	if (flags & ACL_DEFAULTED)
456		sd->sd_control |= SE_DACL_DEFAULTED;
457	if (flags & ACL_AUTO_INHERIT)
458		sd->sd_control |= SE_DACL_AUTO_INHERITED;
459	if (flags & ACL_PROTECTED)
460		sd->sd_control |= SE_DACL_PROTECTED;
461
462	if (present)
463		sd->sd_control |= SE_DACL_PRESENT;
464}
465
466static void
467smb_sd_set_sacl(smb_sd_t *sd, smb_acl_t *acl, boolean_t present, int flags)
468{
469	assert((sd->sd_control & SE_SELF_RELATIVE) == 0);
470
471	sd->sd_sacl = acl;
472
473	if (flags & ACL_DEFAULTED)
474		sd->sd_control |= SE_SACL_DEFAULTED;
475	if (flags & ACL_AUTO_INHERIT)
476		sd->sd_control |= SE_SACL_AUTO_INHERITED;
477	if (flags & ACL_PROTECTED)
478		sd->sd_control |= SE_SACL_PROTECTED;
479
480	if (present)
481		sd->sd_control |= SE_SACL_PRESENT;
482}
483
484/*
485 * smb_fssd_init
486 *
487 * Initializes the given FS SD structure.
488 */
489void
490smb_fssd_init(smb_fssd_t *fs_sd, uint32_t secinfo, uint32_t flags)
491{
492	bzero(fs_sd, sizeof (smb_fssd_t));
493	fs_sd->sd_secinfo = secinfo;
494	fs_sd->sd_flags = flags;
495}
496
497/*
498 * smb_fssd_term
499 *
500 * Frees allocated memory for acl fields.
501 */
502void
503smb_fssd_term(smb_fssd_t *fs_sd)
504{
505	assert(fs_sd);
506
507	acl_free(fs_sd->sd_zdacl);
508	acl_free(fs_sd->sd_zsacl);
509
510	bzero(fs_sd, sizeof (smb_fssd_t));
511}
512