1/*
2   Unix SMB/Netbios implementation.
3   VFS module to get and set posix acls
4   Copyright (C) Volker Lendecke 2006
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21#include "includes.h"
22
23
24/* prototypes for static functions first - for clarity */
25
26static BOOL smb_ace_to_internal(acl_entry_t posix_ace,
27				struct smb_acl_entry *ace);
28static struct smb_acl_t *smb_acl_to_internal(acl_t acl);
29static int smb_acl_set_mode(acl_entry_t entry, SMB_ACL_PERM_T perm);
30static acl_t smb_acl_to_posix(const struct smb_acl_t *acl);
31
32
33/* public functions - the api */
34
35SMB_ACL_T posixacl_sys_acl_get_file(vfs_handle_struct *handle,
36				    const char *path_p,
37				    SMB_ACL_TYPE_T type)
38{
39	struct smb_acl_t *result;
40	acl_type_t acl_type;
41	acl_t acl;
42
43	switch(type) {
44	case SMB_ACL_TYPE_ACCESS:
45		acl_type = ACL_TYPE_ACCESS;
46		break;
47	case SMB_ACL_TYPE_DEFAULT:
48		acl_type = ACL_TYPE_DEFAULT;
49		break;
50	default:
51		errno = EINVAL;
52		return NULL;
53	}
54
55	acl = acl_get_file(path_p, acl_type);
56
57	if (acl == NULL) {
58		return NULL;
59	}
60
61	result = smb_acl_to_internal(acl);
62	acl_free(acl);
63	return result;
64}
65
66SMB_ACL_T posixacl_sys_acl_get_fd(vfs_handle_struct *handle,
67				  files_struct *fsp,
68				  int fd)
69{
70	struct smb_acl_t *result;
71	acl_t acl = acl_get_fd(fd);
72
73	if (acl == NULL) {
74		return NULL;
75	}
76
77	result = smb_acl_to_internal(acl);
78	acl_free(acl);
79	return result;
80}
81
82int posixacl_sys_acl_set_file(vfs_handle_struct *handle,
83			      const char *name,
84			      SMB_ACL_TYPE_T type,
85			      SMB_ACL_T theacl)
86{
87	int res;
88	acl_type_t acl_type;
89	acl_t acl;
90
91	DEBUG(10, ("Calling acl_set_file: %s, %d\n", name, type));
92
93	switch(type) {
94	case SMB_ACL_TYPE_ACCESS:
95		acl_type = ACL_TYPE_ACCESS;
96		break;
97	case SMB_ACL_TYPE_DEFAULT:
98		acl_type = ACL_TYPE_DEFAULT;
99		break;
100	default:
101		errno = EINVAL;
102		return -1;
103	}
104
105	if ((acl = smb_acl_to_posix(theacl)) == NULL) {
106		return -1;
107	}
108	res = acl_set_file(name, acl_type, acl);
109	if (res != 0) {
110		DEBUG(10, ("acl_set_file failed: %s\n", strerror(errno)));
111	}
112	acl_free(acl);
113	return res;
114}
115
116int posixacl_sys_acl_set_fd(vfs_handle_struct *handle,
117			    files_struct *fsp,
118			    int fd, SMB_ACL_T theacl)
119{
120	int res;
121	acl_t acl = smb_acl_to_posix(theacl);
122	if (acl == NULL) {
123		return -1;
124	}
125	res =  acl_set_fd(fd, acl);
126	acl_free(acl);
127	return res;
128}
129
130int posixacl_sys_acl_delete_def_file(vfs_handle_struct *handle,
131				     const char *path)
132{
133	return acl_delete_def_file(path);
134}
135
136
137/* private functions */
138
139static BOOL smb_ace_to_internal(acl_entry_t posix_ace,
140				struct smb_acl_entry *ace)
141{
142	acl_tag_t tag;
143	acl_permset_t permset;
144
145	if (acl_get_tag_type(posix_ace, &tag) != 0) {
146		DEBUG(0, ("smb_acl_get_tag_type failed\n"));
147		return False;
148	}
149
150	switch(tag) {
151	case ACL_USER:
152		ace->a_type = SMB_ACL_USER;
153		break;
154	case ACL_USER_OBJ:
155		ace->a_type = SMB_ACL_USER_OBJ;
156		break;
157	case ACL_GROUP:
158		ace->a_type = SMB_ACL_GROUP;
159		break;
160	case ACL_GROUP_OBJ:
161		ace->a_type = SMB_ACL_GROUP_OBJ;
162		break;
163	case ACL_OTHER:
164		ace->a_type = SMB_ACL_OTHER;
165		break;
166	case ACL_MASK:
167		ace->a_type = SMB_ACL_MASK;
168		break;
169	default:
170		DEBUG(0, ("unknown tag type %d\n", (unsigned int)tag));
171		return False;
172	}
173	switch(ace->a_type) {
174	case SMB_ACL_USER: {
175		uid_t *puid = (uid_t *)acl_get_qualifier(posix_ace);
176		if (puid == NULL) {
177			DEBUG(0, ("smb_acl_get_qualifier failed\n"));
178			return False;
179		}
180		ace->uid = *puid;
181		acl_free(puid);
182		break;
183	}
184
185	case SMB_ACL_GROUP: {
186		gid_t *pgid = (uid_t *)acl_get_qualifier(posix_ace);
187		if (pgid == NULL) {
188			DEBUG(0, ("smb_acl_get_qualifier failed\n"));
189			return False;
190		}
191		ace->gid = *pgid;
192		acl_free(pgid);
193		break;
194	}
195	default:
196		break;
197	}
198	if (acl_get_permset(posix_ace, &permset) != 0) {
199		DEBUG(0, ("smb_acl_get_mode failed\n"));
200		return False;
201	}
202	ace->a_perm = 0;
203#ifdef HAVE_ACL_GET_PERM_NP
204	ace->a_perm |= (acl_get_perm_np(permset, ACL_READ) ? SMB_ACL_READ : 0);
205	ace->a_perm |= (acl_get_perm_np(permset, ACL_WRITE) ? SMB_ACL_WRITE : 0);
206	ace->a_perm |= (acl_get_perm_np(permset, ACL_EXECUTE) ? SMB_ACL_EXECUTE : 0);
207#else
208	ace->a_perm |= (acl_get_perm(permset, ACL_READ) ? SMB_ACL_READ : 0);
209	ace->a_perm |= (acl_get_perm(permset, ACL_WRITE) ? SMB_ACL_WRITE : 0);
210	ace->a_perm |= (acl_get_perm(permset, ACL_EXECUTE) ? SMB_ACL_EXECUTE : 0);
211#endif
212	return True;
213}
214
215static struct smb_acl_t *smb_acl_to_internal(acl_t acl)
216{
217	struct smb_acl_t *result = SMB_MALLOC_P(struct smb_acl_t);
218	int entry_id = ACL_FIRST_ENTRY;
219	acl_entry_t e;
220	if (result == NULL) {
221		return NULL;
222	}
223	ZERO_STRUCTP(result);
224	while (acl_get_entry(acl, entry_id, &e) == 1) {
225
226		entry_id = ACL_NEXT_ENTRY;
227
228		result = (struct smb_acl_t *)SMB_REALLOC(
229			result, sizeof(struct smb_acl_t) +
230			(sizeof(struct smb_acl_entry) * (result->count+1)));
231		if (result == NULL) {
232			DEBUG(0, ("SMB_REALLOC failed\n"));
233			errno = ENOMEM;
234			return NULL;
235		}
236
237		if (!smb_ace_to_internal(e, &result->acl[result->count])) {
238			SAFE_FREE(result);
239			return NULL;
240		}
241
242		result->count += 1;
243	}
244	return result;
245}
246
247static int smb_acl_set_mode(acl_entry_t entry, SMB_ACL_PERM_T perm)
248{
249        int ret;
250        acl_permset_t permset;
251
252	if ((ret = acl_get_permset(entry, &permset)) != 0) {
253		return ret;
254	}
255        if ((ret = acl_clear_perms(permset)) != 0) {
256                return ret;
257	}
258        if ((perm & SMB_ACL_READ) &&
259	    ((ret = acl_add_perm(permset, ACL_READ)) != 0)) {
260		return ret;
261	}
262        if ((perm & SMB_ACL_WRITE) &&
263	    ((ret = acl_add_perm(permset, ACL_WRITE)) != 0)) {
264		return ret;
265	}
266        if ((perm & SMB_ACL_EXECUTE) &&
267	    ((ret = acl_add_perm(permset, ACL_EXECUTE)) != 0)) {
268		return ret;
269	}
270        return acl_set_permset(entry, permset);
271}
272
273static acl_t smb_acl_to_posix(const struct smb_acl_t *acl)
274{
275	acl_t result;
276	int i;
277
278	result = acl_init(acl->count);
279	if (result == NULL) {
280		DEBUG(10, ("acl_init failed\n"));
281		return NULL;
282	}
283
284	for (i=0; i<acl->count; i++) {
285		const struct smb_acl_entry *entry = &acl->acl[i];
286		acl_entry_t e;
287		acl_tag_t tag;
288
289		if (acl_create_entry(&result, &e) != 0) {
290			DEBUG(1, ("acl_create_entry failed: %s\n",
291				  strerror(errno)));
292			goto fail;
293		}
294
295		switch (entry->a_type) {
296		case SMB_ACL_USER:
297			tag = ACL_USER;
298			break;
299		case SMB_ACL_USER_OBJ:
300			tag = ACL_USER_OBJ;
301			break;
302		case SMB_ACL_GROUP:
303			tag = ACL_GROUP;
304			break;
305		case SMB_ACL_GROUP_OBJ:
306			tag = ACL_GROUP_OBJ;
307			break;
308		case SMB_ACL_OTHER:
309			tag = ACL_OTHER;
310			break;
311		case SMB_ACL_MASK:
312			tag = ACL_MASK;
313			break;
314		default:
315			DEBUG(1, ("Unknown tag value %d\n", entry->a_type));
316			goto fail;
317		}
318
319		if (acl_set_tag_type(e, tag) != 0) {
320			DEBUG(10, ("acl_set_tag_type(%d) failed: %s\n",
321				   tag, strerror(errno)));
322			goto fail;
323		}
324
325		switch (entry->a_type) {
326		case SMB_ACL_USER:
327			if (acl_set_qualifier(e, &entry->uid) != 0) {
328				DEBUG(1, ("acl_set_qualifiier failed: %s\n",
329					  strerror(errno)));
330				goto fail;
331			}
332			break;
333		case SMB_ACL_GROUP:
334			if (acl_set_qualifier(e, &entry->gid) != 0) {
335				DEBUG(1, ("acl_set_qualifiier failed: %s\n",
336					  strerror(errno)));
337				goto fail;
338			}
339			break;
340		default: 	/* Shut up, compiler! :-) */
341			break;
342		}
343
344		if (smb_acl_set_mode(e, entry->a_perm) != 0) {
345			goto fail;
346		}
347	}
348
349	if (acl_valid(result) != 0) {
350		DEBUG(0, ("smb_acl_to_posix: ACL is invalid for set (%s)\n",
351			  strerror(errno)));
352		goto fail;
353	}
354
355	return result;
356
357 fail:
358	if (result != NULL) {
359		acl_free(result);
360	}
361	return NULL;
362}
363
364/* VFS operations structure */
365
366static vfs_op_tuple posixacl_op_tuples[] = {
367	/* Disk operations */
368  {SMB_VFS_OP(posixacl_sys_acl_get_file),
369   SMB_VFS_OP_SYS_ACL_GET_FILE,
370   SMB_VFS_LAYER_TRANSPARENT},
371
372  {SMB_VFS_OP(posixacl_sys_acl_get_fd),
373   SMB_VFS_OP_SYS_ACL_GET_FD,
374   SMB_VFS_LAYER_TRANSPARENT},
375
376  {SMB_VFS_OP(posixacl_sys_acl_set_file),
377   SMB_VFS_OP_SYS_ACL_SET_FILE,
378   SMB_VFS_LAYER_TRANSPARENT},
379
380  {SMB_VFS_OP(posixacl_sys_acl_set_fd),
381   SMB_VFS_OP_SYS_ACL_SET_FD,
382   SMB_VFS_LAYER_TRANSPARENT},
383
384  {SMB_VFS_OP(posixacl_sys_acl_delete_def_file),
385   SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE,
386   SMB_VFS_LAYER_TRANSPARENT},
387
388  {SMB_VFS_OP(NULL),
389   SMB_VFS_OP_NOOP,
390   SMB_VFS_LAYER_NOOP}
391};
392
393NTSTATUS vfs_posixacl_init(void);
394NTSTATUS vfs_posixacl_init(void)
395{
396	return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "posixacl",
397				posixacl_op_tuples);
398}
399