acl_from_text.c revision 194955
1178481Sjb/*-
2178481Sjb * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson
3178481Sjb * All rights reserved.
4178481Sjb *
5178481Sjb * Redistribution and use in source and binary forms, with or without
6178481Sjb * modification, are permitted provided that the following conditions
7178481Sjb * are met:
8178481Sjb * 1. Redistributions of source code must retain the above copyright
9178481Sjb *    notice, this list of conditions and the following disclaimer.
10178481Sjb * 2. Redistributions in binary form must reproduce the above copyright
11178481Sjb *    notice, this list of conditions and the following disclaimer in the
12178481Sjb *    documentation and/or other materials provided with the distribution.
13178481Sjb *
14178481Sjb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15178481Sjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16178481Sjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17178481Sjb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18178481Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19178481Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20178481Sjb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21178481Sjb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22210767Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23178481Sjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24178481Sjb * SUCH DAMAGE.
25178481Sjb */
26178481Sjb/*
27178481Sjb * acl_from_text: Convert a text-form ACL from a string to an acl_t.
28178481Sjb */
29178481Sjb
30178481Sjb#include <sys/cdefs.h>
31178481Sjb__FBSDID("$FreeBSD: head/lib/libc/posix1e/acl_from_text.c 194955 2009-06-25 12:46:59Z trasz $");
32178481Sjb
33178481Sjb#include <sys/types.h>
34178481Sjb#include "namespace.h"
35178481Sjb#include <sys/acl.h>
36178481Sjb#include "un-namespace.h"
37178481Sjb#include <sys/errno.h>
38178481Sjb#include <grp.h>
39178481Sjb#include <pwd.h>
40178481Sjb#include <stdio.h>
41178481Sjb#include <stdlib.h>
42178481Sjb#include <string.h>
43178481Sjb#include <assert.h>
44178481Sjb
45178481Sjb#include "acl_support.h"
46178481Sjb
47178481Sjbstatic int _posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id);
48178481Sjbstatic acl_tag_t acl_string_to_tag(char *tag, char *qualifier);
49178481Sjb
50249656Sedint _nfs4_acl_entry_from_text(acl_t aclp, char *entry);
51178481Sjbint _text_could_be_nfs4_acl(const char *entry);
52178481Sjb
53178481Sjbstatic acl_tag_t
54178481Sjbacl_string_to_tag(char *tag, char *qualifier)
55253678Spfg{
56253661Spfg
57178481Sjb	if (*qualifier == '\0') {
58178481Sjb		if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
59178481Sjb			return (ACL_USER_OBJ);
60178481Sjb		} else
61178481Sjb		if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
62178481Sjb			return (ACL_GROUP_OBJ);
63178481Sjb		} else
64178481Sjb		if ((!strcmp(tag, "mask")) || (!strcmp(tag, "m"))) {
65178481Sjb			return (ACL_MASK);
66178481Sjb		} else
67233407Sgonzo		if ((!strcmp(tag, "other")) || (!strcmp(tag, "o"))) {
68233407Sgonzo			return (ACL_OTHER);
69233407Sgonzo		} else
70233407Sgonzo			return(-1);
71233407Sgonzo	} else {
72233407Sgonzo		if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
73233407Sgonzo			return(ACL_USER);
74233407Sgonzo		} else
75233407Sgonzo		if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
76233407Sgonzo			return(ACL_GROUP);
77233407Sgonzo		} else
78233407Sgonzo			return(-1);
79178481Sjb	}
80178481Sjb}
81178546Sjb
82178481Sjbstatic int
83178481Sjb_posix1e_acl_entry_from_text(acl_t aclp, char *entry)
84178481Sjb{
85178481Sjb	acl_tag_t	 t;
86178481Sjb	acl_perm_t	 p;
87178481Sjb	char		*tag, *qualifier, *permission;
88178481Sjb	uid_t		 id;
89178481Sjb	int		 error;
90178481Sjb
91178481Sjb	assert(_acl_brand(aclp) == ACL_BRAND_POSIX);
92178481Sjb
93178546Sjb	/* Split into three ':' delimited fields. */
94178481Sjb	tag = strsep(&entry, ":");
95178481Sjb	if (tag == NULL) {
96178481Sjb		errno = EINVAL;
97178481Sjb		return (-1);
98178481Sjb	}
99178481Sjb	tag = string_skip_whitespace(tag);
100178481Sjb	if ((*tag == '\0') && (!entry)) {
101178481Sjb		/*
102178481Sjb		 * Is an entirely comment line, skip to next
103178481Sjb		 * comma.
104178546Sjb		 */
105178481Sjb		return (0);
106178481Sjb	}
107178481Sjb	string_trim_trailing_whitespace(tag);
108178481Sjb
109178481Sjb	qualifier = strsep(&entry, ":");
110178481Sjb	if (qualifier == NULL) {
111178481Sjb		errno = EINVAL;
112178481Sjb		return (-1);
113178481Sjb	}
114178481Sjb	qualifier = string_skip_whitespace(qualifier);
115178546Sjb	string_trim_trailing_whitespace(qualifier);
116178481Sjb
117178481Sjb	permission = strsep(&entry, ":");
118178481Sjb	if (permission == NULL || entry) {
119178481Sjb		errno = EINVAL;
120178481Sjb		return (-1);
121178481Sjb	}
122178481Sjb	permission = string_skip_whitespace(permission);
123178546Sjb	string_trim_trailing_whitespace(permission);
124178481Sjb
125178481Sjb	t = acl_string_to_tag(tag, qualifier);
126178481Sjb	if (t == -1) {
127178481Sjb		errno = EINVAL;
128178481Sjb		return (-1);
129178546Sjb	}
130178546Sjb
131178481Sjb	error = _posix1e_acl_string_to_perm(permission, &p);
132178481Sjb	if (error == -1) {
133178481Sjb		errno = EINVAL;
134178481Sjb		return (-1);
135178481Sjb	}
136178481Sjb
137178481Sjb	switch(t) {
138178481Sjb		case ACL_USER_OBJ:
139178481Sjb		case ACL_GROUP_OBJ:
140178481Sjb		case ACL_MASK:
141178481Sjb		case ACL_OTHER:
142178546Sjb			if (*qualifier != '\0') {
143178481Sjb				errno = EINVAL;
144178481Sjb				return (-1);
145178481Sjb			}
146178481Sjb			id = 0;
147178481Sjb			break;
148178546Sjb
149178481Sjb		case ACL_USER:
150178546Sjb		case ACL_GROUP:
151178546Sjb			error = _posix1e_acl_name_to_id(t, qualifier,
152178481Sjb					&id);
153178481Sjb			if (error == -1)
154178481Sjb				return (-1);
155178481Sjb			break;
156178481Sjb
157233407Sgonzo		default:
158233407Sgonzo			errno = EINVAL;
159233407Sgonzo			return (-1);
160233407Sgonzo	}
161233407Sgonzo
162178481Sjb	error = _posix1e_acl_add_entry(aclp, t, id, p);
163178481Sjb	if (error == -1)
164178481Sjb		return (-1);
165178481Sjb
166178481Sjb	return (0);
167178481Sjb}
168178481Sjb
169178481Sjbstatic int
170178481Sjb_text_is_nfs4_entry(const char *entry)
171178481Sjb{
172178481Sjb	int count = 0;
173178481Sjb
174233407Sgonzo	assert(strlen(entry) > 0);
175233407Sgonzo
176233407Sgonzo	while (*entry != '\0') {
177233407Sgonzo		if (*entry == ':' || *entry == '@')
178178481Sjb			count++;
179178481Sjb		entry++;
180178481Sjb	}
181178481Sjb
182178481Sjb	if (count <= 2)
183178481Sjb		return (0);
184178481Sjb
185178481Sjb	return (1);
186178481Sjb}
187178481Sjb
188178481Sjb/*
189178481Sjb * acl_from_text -- Convert a string into an ACL.
190178481Sjb * Postpone most validity checking until the end and call acl_valid() to do
191178481Sjb * that.
192178481Sjb */
193178481Sjbacl_t
194178481Sjbacl_from_text(const char *buf_p)
195178481Sjb{
196178481Sjb	acl_t		 acl;
197178481Sjb	char		*mybuf_p, *line, *cur, *notcomment, *comment, *entry;
198210767Srpaulo	int		 error;
199210767Srpaulo
200210767Srpaulo	/* Local copy we can mess up. */
201210767Srpaulo	mybuf_p = strdup(buf_p);
202210767Srpaulo	if (mybuf_p == NULL)
203210767Srpaulo		return(NULL);
204178481Sjb
205178481Sjb	acl = acl_init(3); /* XXX: WTF, 3? */
206233407Sgonzo	if (acl == NULL) {
207233407Sgonzo		free(mybuf_p);
208233407Sgonzo		return(NULL);
209233407Sgonzo	}
210233407Sgonzo
211233407Sgonzo	/* Outer loop: delimit at \n boundaries. */
212178481Sjb	cur = mybuf_p;
213178481Sjb	while ((line = strsep(&cur, "\n"))) {
214178481Sjb		/* Now split the line on the first # to strip out comments. */
215178481Sjb		comment = line;
216233407Sgonzo		notcomment = strsep(&comment, "#");
217233407Sgonzo
218233407Sgonzo		/* Inner loop: delimit at ',' boundaries. */
219233407Sgonzo		while ((entry = strsep(&notcomment, ","))) {
220233407Sgonzo
221178481Sjb			/* Skip empty lines. */
222178481Sjb			if (strlen(string_skip_whitespace(entry)) == 0)
223178481Sjb				continue;
224178481Sjb
225178481Sjb			if (_acl_brand(acl) == ACL_BRAND_UNKNOWN) {
226178481Sjb				if (_text_is_nfs4_entry(entry))
227178481Sjb					_acl_brand_as(acl, ACL_BRAND_NFS4);
228178481Sjb				else
229178481Sjb					_acl_brand_as(acl, ACL_BRAND_POSIX);
230178481Sjb			}
231178481Sjb
232178481Sjb			switch (_acl_brand(acl)) {
233178481Sjb			case ACL_BRAND_NFS4:
234178481Sjb				error = _nfs4_acl_entry_from_text(acl, entry);
235178481Sjb				break;
236178481Sjb
237178481Sjb			case ACL_BRAND_POSIX:
238178481Sjb				error = _posix1e_acl_entry_from_text(acl, entry);
239178481Sjb				break;
240178481Sjb
241178481Sjb			default:
242178481Sjb				error = EINVAL;
243178481Sjb				break;
244178481Sjb			}
245233407Sgonzo
246233407Sgonzo			if (error)
247233407Sgonzo				goto error_label;
248233407Sgonzo		}
249233407Sgonzo	}
250233407Sgonzo
251233407Sgonzo#if 0
252178481Sjb	/* XXX Should we only return ACLs valid according to acl_valid? */
253178481Sjb	/* Verify validity of the ACL we read in. */
254178481Sjb	if (acl_valid(acl) == -1) {
255178481Sjb		errno = EINVAL;
256178481Sjb		goto error_label;
257233407Sgonzo	}
258233407Sgonzo#endif
259233407Sgonzo
260233407Sgonzo	return(acl);
261233407Sgonzo
262233407Sgonzoerror_label:
263233407Sgonzo	acl_free(acl);
264178481Sjb	free(mybuf_p);
265178481Sjb	return(NULL);
266178481Sjb}
267178481Sjb
268178481Sjb/*
269178481Sjb * Given a username/groupname from a text form of an ACL, return the uid/gid
270178481Sjb * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM
271178481Sjb * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
272178481Sjb * MAY HAVE SIDE-EFFECTS
273233407Sgonzo *
274233407Sgonzo * XXX currently doesn't deal correctly with a numeric uid being passed
275233407Sgonzo * instead of a username.  What is correct behavior here?  Check chown.
276233407Sgonzo */
277233407Sgonzostatic int
278233407Sgonzo_posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id)
279178481Sjb{
280178481Sjb	struct group	*g;
281178481Sjb	struct passwd	*p;
282178481Sjb	unsigned long	l;
283178546Sjb	char 		*endp;
284178481Sjb
285178546Sjb	switch(tag) {
286178546Sjb	case ACL_USER:
287178481Sjb		p = getpwnam(name);
288178481Sjb		if (p == NULL) {
289178481Sjb			l = strtoul(name, &endp, 0);
290178481Sjb			if (*endp != '\0' || l != (unsigned long)(uid_t)l) {
291178481Sjb				errno = EINVAL;
292178481Sjb				return (-1);
293178481Sjb			}
294178481Sjb			*id = (uid_t)l;
295178481Sjb			return (0);
296178481Sjb		}
297178481Sjb		*id = p->pw_uid;
298178481Sjb		return (0);
299178481Sjb
300178481Sjb	case ACL_GROUP:
301178481Sjb		g = getgrnam(name);
302178481Sjb		if (g == NULL) {
303178481Sjb			l = strtoul(name, &endp, 0);
304178481Sjb			if (*endp != '\0' || l != (unsigned long)(gid_t)l) {
305178481Sjb				errno = EINVAL;
306178481Sjb				return (-1);
307178481Sjb			}
308178481Sjb			*id = (gid_t)l;
309178481Sjb			return (0);
310178481Sjb		}
311178481Sjb		*id = g->gr_gid;
312178481Sjb		return (0);
313178481Sjb
314178481Sjb	default:
315178481Sjb		return (EINVAL);
316178481Sjb	}
317178481Sjb}
318178481Sjb