1/*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/types.h>
25#include <sys/acl.h>
26#include <stdlib.h>
27#include <fcntl.h>
28#include <strings.h>
29#include <errno.h>
30#include <uuid/uuid.h>
31
32/*
33 * Versions of copy_int/copy_ext that retain native endianity.
34 */
35extern ssize_t	acl_copy_ext_native(void *buf_p, acl_t acl, ssize_t size);
36extern acl_t	acl_copy_int_native(const void *buf_p);
37
38struct _filesec {
39	int	fs_valid;
40#define FS_VALID_UID		(1<<0)
41#define FS_VALID_GID		(1<<1)
42#define FS_VALID_UUID		(1<<2)
43#define FS_VALID_GRPUUID	(1<<3)
44#define	FS_VALID_MODE		(1<<4)
45#define FS_VALID_ACL		(1<<5)
46	uid_t	fs_uid;
47	gid_t	fs_gid;
48	uuid_t	fs_uuid;
49	uuid_t	fs_grpuuid;
50	mode_t	fs_mode;
51	size_t	fs_aclsize;
52	void	*fs_aclbuf;
53};
54
55#define FS_ISVALID(_fsec, _bit)	((_fsec)->fs_valid & (_bit))
56#define FS_CLEAR(_fsec, _bit)	((_fsec)->fs_valid &= ~(_bit))
57#define FS_SET(_fsec, _bit)	((_fsec)->fs_valid |= (_bit))
58
59static void	filesec_discard_aclbuf(struct _filesec *fsec);
60
61filesec_t
62filesec_init(void)
63{
64	struct _filesec	*fsp;
65
66	fsp = malloc(sizeof(*fsp));
67	if (fsp != NULL)
68		fsp->fs_valid = 0;
69	return(fsp);
70}
71
72filesec_t
73filesec_dup(filesec_t fsec)
74{
75	struct _filesec *fsp;
76
77	fsp = malloc(sizeof(*fsp));
78	if (fsp != NULL) {
79		*fsp = *fsec;
80		if (FS_ISVALID(fsec, FS_VALID_ACL)) {
81			if (fsec->fs_aclbuf != _FILESEC_REMOVE_ACL) {
82				fsp->fs_aclbuf = malloc(fsec->fs_aclsize);
83				if (fsp->fs_aclbuf == NULL) {
84					free(fsp);
85					return(NULL);
86				}
87				bcopy(fsec->fs_aclbuf, fsp->fs_aclbuf, fsec->fs_aclsize);
88			}
89		}
90	}
91	return(fsp);
92}
93
94void
95filesec_free(filesec_t fsec)
96{
97	filesec_discard_aclbuf(fsec);
98	free(fsec);
99}
100
101int
102filesec_get_property(filesec_t fsec, filesec_property_t property, void *propptr)
103{
104	int		error;
105
106	error = 0;
107	switch(property) {
108	case FILESEC_OWNER:
109		if (!FS_ISVALID(fsec, FS_VALID_UID)) {
110			error = ENOENT;
111			break;
112		}
113		if (propptr != NULL)
114			*(uid_t *)propptr = fsec->fs_uid;
115		break;
116	case FILESEC_GROUP:
117		if (!FS_ISVALID(fsec, FS_VALID_GID)) {
118			error = ENOENT;
119			break;
120		}
121		if (propptr != NULL)
122			*(gid_t *)propptr = fsec->fs_gid;
123		break;
124	case FILESEC_UUID:
125		if (!FS_ISVALID(fsec, FS_VALID_UUID)) {
126			error = ENOENT;
127			break;
128		}
129		if (propptr != NULL)
130			uuid_copy(*(uuid_t *)propptr, fsec->fs_uuid);
131		break;
132	case FILESEC_GRPUUID:
133		if (!FS_ISVALID(fsec, FS_VALID_GRPUUID)) {
134			error = ENOENT;
135			break;
136		}
137		if (propptr != NULL)
138			uuid_copy(*(uuid_t *)propptr, fsec->fs_grpuuid);
139		break;
140	case FILESEC_MODE:
141		if (!FS_ISVALID(fsec, FS_VALID_MODE)) {
142			error = ENOENT;
143			break;
144		}
145		if (propptr != NULL)
146			*(mode_t *)propptr = fsec->fs_mode;
147		break;
148	case FILESEC_ACL:
149		if (!FS_ISVALID(fsec, FS_VALID_ACL)) {
150			error = ENOENT;
151			break;
152		}
153		if (propptr != NULL) {
154			if (fsec->fs_aclbuf == _FILESEC_REMOVE_ACL) {
155				*(acl_t *)propptr = _FILESEC_REMOVE_ACL;
156			} else {
157				*(acl_t *)propptr = acl_copy_int_native(fsec->fs_aclbuf);
158				if (*(acl_t *)propptr == NULL)
159					error = errno;
160			}
161		}
162		break;
163	case FILESEC_ACL_RAW:
164		if (!FS_ISVALID(fsec, FS_VALID_ACL)) {
165			error = ENOENT;
166			break;
167		}
168		if (propptr != NULL)
169			*(void **)propptr = fsec->fs_aclbuf;
170		break;
171	case FILESEC_ACL_ALLOCSIZE:
172		if (!FS_ISVALID(fsec, FS_VALID_ACL)) {
173			error = ENOENT;
174			break;
175		}
176		if (propptr != NULL)
177			*(size_t *)propptr = fsec->fs_aclsize;
178		break;
179	default:
180		error = EINVAL;
181		break;
182	}
183	if (error) {
184		errno = error;
185		return(-1);
186	}
187	return(0);
188}
189
190int
191filesec_set_property(filesec_t fsec, filesec_property_t property, const void *propptr)
192{
193	void	*aclbuf;
194	ssize_t	aclsize, copysize;
195	acl_t	acl;
196	int	error;
197
198	error = 0;
199	switch(property) {
200	case FILESEC_OWNER:
201		if (propptr == _FILESEC_UNSET_PROPERTY) {
202			FS_CLEAR(fsec, FS_VALID_UID);
203		} else {
204			fsec->fs_uid = *(uid_t *)propptr;
205			FS_SET(fsec, FS_VALID_UID);
206		}
207		break;
208	case FILESEC_GROUP:
209		if (propptr == _FILESEC_UNSET_PROPERTY) {
210			FS_CLEAR(fsec, FS_VALID_GID);
211		} else {
212			fsec->fs_gid = *(gid_t *)propptr;
213			FS_SET(fsec, FS_VALID_GID);
214		}
215		break;
216	case FILESEC_UUID:
217		if (propptr == _FILESEC_UNSET_PROPERTY) {
218			FS_CLEAR(fsec, FS_VALID_UUID);
219		} else {
220			uuid_copy(fsec->fs_uuid, *(uuid_t *)propptr);
221			FS_SET(fsec, FS_VALID_UUID);
222		}
223		break;
224	case FILESEC_GRPUUID:
225		if (propptr == _FILESEC_UNSET_PROPERTY) {
226			FS_CLEAR(fsec, FS_VALID_GRPUUID);
227		} else {
228			uuid_copy(fsec->fs_grpuuid, *(uuid_t *)propptr);
229			FS_SET(fsec, FS_VALID_GRPUUID);
230		}
231		break;
232	case FILESEC_MODE:
233		if (propptr == _FILESEC_UNSET_PROPERTY) {
234			FS_CLEAR(fsec, FS_VALID_MODE);
235		} else {
236			fsec->fs_mode = *(mode_t *)propptr;
237			FS_SET(fsec, FS_VALID_MODE);
238		}
239		break;
240	case FILESEC_ACL:
241		if (propptr == _FILESEC_UNSET_PROPERTY) {
242			filesec_discard_aclbuf(fsec);
243			FS_CLEAR(fsec, FS_VALID_ACL);
244		} else if (propptr == _FILESEC_REMOVE_ACL) {
245			filesec_discard_aclbuf(fsec);
246			fsec->fs_aclbuf = _FILESEC_REMOVE_ACL;
247			fsec->fs_aclsize = 0;
248			FS_SET(fsec, FS_VALID_ACL);
249		} else {
250			acl = *(acl_t *)propptr;
251			aclsize = acl_size(acl);
252			if (aclsize < 0) {
253				error = errno;
254				break;
255			}
256			if ((aclbuf = malloc(aclsize)) == NULL) {
257				error = errno;
258				break;
259			}
260			copysize = acl_copy_ext_native(aclbuf, acl, aclsize);
261			if (copysize < 0) {
262				free(aclbuf);
263				error = EINVAL;
264				break;
265			}
266			filesec_discard_aclbuf(fsec);
267			fsec->fs_aclbuf = aclbuf;
268			fsec->fs_aclsize = aclsize;
269			FS_SET(fsec, FS_VALID_ACL);
270		}
271		break;
272	case FILESEC_ACL_RAW:
273		filesec_discard_aclbuf(fsec);
274		if ((propptr != NULL) && (*(void **)propptr != NULL)) {
275			fsec->fs_aclbuf = *(void **)propptr;
276			FS_SET(fsec, FS_VALID_ACL);
277		}
278		break;
279	case FILESEC_ACL_ALLOCSIZE:
280		if (propptr != NULL) {
281			fsec->fs_aclsize = *(size_t *)propptr;
282		} else {
283			error = EINVAL;
284		}
285		break;
286	default:
287		error = EINVAL;
288		break;
289	}
290	if (error != 0) {
291		errno = error;
292		return (-1);
293	}
294	return (0);
295}
296
297int
298filesec_unset_property(filesec_t fsec, filesec_property_t property)
299{
300	return filesec_set_property(fsec, property, _FILESEC_UNSET_PROPERTY);
301}
302
303int
304filesec_query_property(filesec_t fsec, filesec_property_t property, int *validptr)
305{
306	int	valid, error;
307
308	error = 0;
309	switch(property) {
310	case FILESEC_OWNER:
311		valid = FS_ISVALID(fsec, FS_VALID_UID);
312		break;
313	case FILESEC_GROUP:
314		valid = FS_ISVALID(fsec, FS_VALID_GID);
315		break;
316	case FILESEC_UUID:
317		valid = FS_ISVALID(fsec, FS_VALID_UUID);
318		break;
319	case FILESEC_GRPUUID:
320		valid = FS_ISVALID(fsec, FS_VALID_GRPUUID);
321		break;
322	case FILESEC_MODE:
323		valid = FS_ISVALID(fsec, FS_VALID_MODE);
324		break;
325	case FILESEC_ACL:
326	case FILESEC_ACL_RAW:
327	case FILESEC_ACL_ALLOCSIZE:
328		valid = FS_ISVALID(fsec, FS_VALID_ACL);
329		break;
330	default:
331		error = EINVAL;
332		break;
333	}
334
335	if (error != 0) {
336		errno = error;
337		return(-1);
338	}
339	*validptr = valid;
340	return(0);
341}
342
343static void
344filesec_discard_aclbuf(struct _filesec *fsec)
345{
346	if (FS_ISVALID(fsec, FS_VALID_ACL)) {
347		if (fsec->fs_aclbuf != _FILESEC_REMOVE_ACL)
348			free(fsec->fs_aclbuf);
349		fsec->fs_aclbuf = NULL;
350		fsec->fs_aclsize = 0;
351		FS_CLEAR(fsec, FS_VALID_ACL);
352	}
353}
354