xref: /freebsd/lib/libc/posix1e/acl_from_text.c (revision 4f6d5fda)
1515d7c92SRobert Watson /*-
22de14c39SRobert Watson  * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson
3515d7c92SRobert Watson  * All rights reserved.
4515d7c92SRobert Watson  *
5515d7c92SRobert Watson  * Redistribution and use in source and binary forms, with or without
6515d7c92SRobert Watson  * modification, are permitted provided that the following conditions
7515d7c92SRobert Watson  * are met:
8515d7c92SRobert Watson  * 1. Redistributions of source code must retain the above copyright
9515d7c92SRobert Watson  *    notice, this list of conditions and the following disclaimer.
10515d7c92SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
11515d7c92SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
12515d7c92SRobert Watson  *    documentation and/or other materials provided with the distribution.
13515d7c92SRobert Watson  *
14515d7c92SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15515d7c92SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16515d7c92SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17515d7c92SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18515d7c92SRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19515d7c92SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20515d7c92SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21515d7c92SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22515d7c92SRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23515d7c92SRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24515d7c92SRobert Watson  * SUCH DAMAGE.
25515d7c92SRobert Watson  */
26515d7c92SRobert Watson /*
27374c6c0fSRobert Watson  * acl_from_text: Convert a text-form ACL from a string to an acl_t.
28515d7c92SRobert Watson  */
29515d7c92SRobert Watson 
30333fc21eSDavid E. O'Brien #include <sys/cdefs.h>
31333fc21eSDavid E. O'Brien __FBSDID("$FreeBSD$");
32333fc21eSDavid E. O'Brien 
33515d7c92SRobert Watson #include <sys/types.h>
347bd44e92SThomas Moestl #include "namespace.h"
35515d7c92SRobert Watson #include <sys/acl.h>
367bd44e92SThomas Moestl #include "un-namespace.h"
37515d7c92SRobert Watson #include <sys/errno.h>
3848135111STim Kientzle #include <grp.h>
3948135111STim Kientzle #include <pwd.h>
40515d7c92SRobert Watson #include <stdio.h>
41515d7c92SRobert Watson #include <stdlib.h>
42515d7c92SRobert Watson #include <string.h>
43aa015c8eSEdward Tomasz Napierala #include <assert.h>
44515d7c92SRobert Watson 
45515d7c92SRobert Watson #include "acl_support.h"
46515d7c92SRobert Watson 
4748135111STim Kientzle static int _posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id);
48c61eb011SChris D. Faulhaber static acl_tag_t acl_string_to_tag(char *tag, char *qualifier);
49c61eb011SChris D. Faulhaber 
50aa015c8eSEdward Tomasz Napierala int _nfs4_acl_entry_from_text(acl_t aclp, char *entry);
51aa015c8eSEdward Tomasz Napierala int _text_could_be_nfs4_acl(const char *entry);
52515d7c92SRobert Watson 
53c61eb011SChris D. Faulhaber static acl_tag_t
54515d7c92SRobert Watson acl_string_to_tag(char *tag, char *qualifier)
55515d7c92SRobert Watson {
56515d7c92SRobert Watson 
57515d7c92SRobert Watson 	if (*qualifier == '\0') {
58515d7c92SRobert Watson 		if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
59515d7c92SRobert Watson 			return (ACL_USER_OBJ);
60515d7c92SRobert Watson 		} else
61515d7c92SRobert Watson 		if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
62515d7c92SRobert Watson 			return (ACL_GROUP_OBJ);
63515d7c92SRobert Watson 		} else
64515d7c92SRobert Watson 		if ((!strcmp(tag, "mask")) || (!strcmp(tag, "m"))) {
65515d7c92SRobert Watson 			return (ACL_MASK);
66515d7c92SRobert Watson 		} else
67515d7c92SRobert Watson 		if ((!strcmp(tag, "other")) || (!strcmp(tag, "o"))) {
68515d7c92SRobert Watson 			return (ACL_OTHER);
69515d7c92SRobert Watson 		} else
70515d7c92SRobert Watson 			return(-1);
71515d7c92SRobert Watson 	} else {
72515d7c92SRobert Watson 		if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
73515d7c92SRobert Watson 			return(ACL_USER);
74515d7c92SRobert Watson 		} else
75515d7c92SRobert Watson 		if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
76515d7c92SRobert Watson 			return(ACL_GROUP);
77515d7c92SRobert Watson 		} else
78515d7c92SRobert Watson 			return(-1);
79515d7c92SRobert Watson 	}
80515d7c92SRobert Watson }
81515d7c92SRobert Watson 
82aa015c8eSEdward Tomasz Napierala static int
83aa015c8eSEdward Tomasz Napierala _posix1e_acl_entry_from_text(acl_t aclp, char *entry)
84aa015c8eSEdward Tomasz Napierala {
85aa015c8eSEdward Tomasz Napierala 	acl_tag_t	 t;
86aa015c8eSEdward Tomasz Napierala 	acl_perm_t	 p;
87aa015c8eSEdward Tomasz Napierala 	char		*tag, *qualifier, *permission;
88aa015c8eSEdward Tomasz Napierala 	uid_t		 id;
89aa015c8eSEdward Tomasz Napierala 	int		 error;
90aa015c8eSEdward Tomasz Napierala 
91aa015c8eSEdward Tomasz Napierala 	assert(_acl_brand(aclp) == ACL_BRAND_POSIX);
92aa015c8eSEdward Tomasz Napierala 
93aa015c8eSEdward Tomasz Napierala 	/* Split into three ':' delimited fields. */
94aa015c8eSEdward Tomasz Napierala 	tag = strsep(&entry, ":");
95aa015c8eSEdward Tomasz Napierala 	if (tag == NULL) {
96aa015c8eSEdward Tomasz Napierala 		errno = EINVAL;
97aa015c8eSEdward Tomasz Napierala 		return (-1);
98aa015c8eSEdward Tomasz Napierala 	}
99aa015c8eSEdward Tomasz Napierala 	tag = string_skip_whitespace(tag);
100aa015c8eSEdward Tomasz Napierala 	if ((*tag == '\0') && (!entry)) {
101aa015c8eSEdward Tomasz Napierala 		/*
102aa015c8eSEdward Tomasz Napierala 		 * Is an entirely comment line, skip to next
103aa015c8eSEdward Tomasz Napierala 		 * comma.
104aa015c8eSEdward Tomasz Napierala 		 */
105aa015c8eSEdward Tomasz Napierala 		return (0);
106aa015c8eSEdward Tomasz Napierala 	}
107aa015c8eSEdward Tomasz Napierala 	string_trim_trailing_whitespace(tag);
108aa015c8eSEdward Tomasz Napierala 
109aa015c8eSEdward Tomasz Napierala 	qualifier = strsep(&entry, ":");
110aa015c8eSEdward Tomasz Napierala 	if (qualifier == NULL) {
111aa015c8eSEdward Tomasz Napierala 		errno = EINVAL;
112aa015c8eSEdward Tomasz Napierala 		return (-1);
113aa015c8eSEdward Tomasz Napierala 	}
114aa015c8eSEdward Tomasz Napierala 	qualifier = string_skip_whitespace(qualifier);
115aa015c8eSEdward Tomasz Napierala 	string_trim_trailing_whitespace(qualifier);
116aa015c8eSEdward Tomasz Napierala 
117aa015c8eSEdward Tomasz Napierala 	permission = strsep(&entry, ":");
118aa015c8eSEdward Tomasz Napierala 	if (permission == NULL || entry) {
119aa015c8eSEdward Tomasz Napierala 		errno = EINVAL;
120aa015c8eSEdward Tomasz Napierala 		return (-1);
121aa015c8eSEdward Tomasz Napierala 	}
122aa015c8eSEdward Tomasz Napierala 	permission = string_skip_whitespace(permission);
123aa015c8eSEdward Tomasz Napierala 	string_trim_trailing_whitespace(permission);
124aa015c8eSEdward Tomasz Napierala 
125aa015c8eSEdward Tomasz Napierala 	t = acl_string_to_tag(tag, qualifier);
126aa015c8eSEdward Tomasz Napierala 	if (t == -1) {
127aa015c8eSEdward Tomasz Napierala 		errno = EINVAL;
128aa015c8eSEdward Tomasz Napierala 		return (-1);
129aa015c8eSEdward Tomasz Napierala 	}
130aa015c8eSEdward Tomasz Napierala 
131aa015c8eSEdward Tomasz Napierala 	error = _posix1e_acl_string_to_perm(permission, &p);
132aa015c8eSEdward Tomasz Napierala 	if (error == -1) {
133aa015c8eSEdward Tomasz Napierala 		errno = EINVAL;
134aa015c8eSEdward Tomasz Napierala 		return (-1);
135aa015c8eSEdward Tomasz Napierala 	}
136aa015c8eSEdward Tomasz Napierala 
137aa015c8eSEdward Tomasz Napierala 	switch(t) {
138aa015c8eSEdward Tomasz Napierala 		case ACL_USER_OBJ:
139aa015c8eSEdward Tomasz Napierala 		case ACL_GROUP_OBJ:
140aa015c8eSEdward Tomasz Napierala 		case ACL_MASK:
141aa015c8eSEdward Tomasz Napierala 		case ACL_OTHER:
142aa015c8eSEdward Tomasz Napierala 			if (*qualifier != '\0') {
143aa015c8eSEdward Tomasz Napierala 				errno = EINVAL;
144aa015c8eSEdward Tomasz Napierala 				return (-1);
145aa015c8eSEdward Tomasz Napierala 			}
146aa015c8eSEdward Tomasz Napierala 			id = 0;
147aa015c8eSEdward Tomasz Napierala 			break;
148aa015c8eSEdward Tomasz Napierala 
149aa015c8eSEdward Tomasz Napierala 		case ACL_USER:
150aa015c8eSEdward Tomasz Napierala 		case ACL_GROUP:
151aa015c8eSEdward Tomasz Napierala 			error = _posix1e_acl_name_to_id(t, qualifier,
152aa015c8eSEdward Tomasz Napierala 					&id);
153aa015c8eSEdward Tomasz Napierala 			if (error == -1)
154aa015c8eSEdward Tomasz Napierala 				return (-1);
155aa015c8eSEdward Tomasz Napierala 			break;
156aa015c8eSEdward Tomasz Napierala 
157aa015c8eSEdward Tomasz Napierala 		default:
158aa015c8eSEdward Tomasz Napierala 			errno = EINVAL;
159aa015c8eSEdward Tomasz Napierala 			return (-1);
160aa015c8eSEdward Tomasz Napierala 	}
161aa015c8eSEdward Tomasz Napierala 
162aa015c8eSEdward Tomasz Napierala 	error = _posix1e_acl_add_entry(aclp, t, id, p);
163aa015c8eSEdward Tomasz Napierala 	if (error == -1)
164aa015c8eSEdward Tomasz Napierala 		return (-1);
165aa015c8eSEdward Tomasz Napierala 
166aa015c8eSEdward Tomasz Napierala 	return (0);
167aa015c8eSEdward Tomasz Napierala }
168aa015c8eSEdward Tomasz Napierala 
169aa015c8eSEdward Tomasz Napierala static int
170aa015c8eSEdward Tomasz Napierala _text_is_nfs4_entry(const char *entry)
171aa015c8eSEdward Tomasz Napierala {
172aa015c8eSEdward Tomasz Napierala 	int count = 0;
173aa015c8eSEdward Tomasz Napierala 
174aa015c8eSEdward Tomasz Napierala 	assert(strlen(entry) > 0);
175aa015c8eSEdward Tomasz Napierala 
176aa015c8eSEdward Tomasz Napierala 	while (*entry != '\0') {
177aa015c8eSEdward Tomasz Napierala 		if (*entry == ':' || *entry == '@')
178aa015c8eSEdward Tomasz Napierala 			count++;
179aa015c8eSEdward Tomasz Napierala 		entry++;
180aa015c8eSEdward Tomasz Napierala 	}
181aa015c8eSEdward Tomasz Napierala 
182aa015c8eSEdward Tomasz Napierala 	if (count <= 2)
183aa015c8eSEdward Tomasz Napierala 		return (0);
184aa015c8eSEdward Tomasz Napierala 
185aa015c8eSEdward Tomasz Napierala 	return (1);
186aa015c8eSEdward Tomasz Napierala }
187aa015c8eSEdward Tomasz Napierala 
188515d7c92SRobert Watson /*
189374c6c0fSRobert Watson  * acl_from_text -- Convert a string into an ACL.
190374c6c0fSRobert Watson  * Postpone most validity checking until the end and call acl_valid() to do
191515d7c92SRobert Watson  * that.
192515d7c92SRobert Watson  */
193515d7c92SRobert Watson acl_t
194515d7c92SRobert Watson acl_from_text(const char *buf_p)
195515d7c92SRobert Watson {
196515d7c92SRobert Watson 	acl_t		 acl;
197515d7c92SRobert Watson 	char		*mybuf_p, *line, *cur, *notcomment, *comment, *entry;
198515d7c92SRobert Watson 	int		 error;
199515d7c92SRobert Watson 
200374c6c0fSRobert Watson 	/* Local copy we can mess up. */
201515d7c92SRobert Watson 	mybuf_p = strdup(buf_p);
2029fd46b02SChris D. Faulhaber 	if (mybuf_p == NULL)
203f0078215SRobert Watson 		return(NULL);
204515d7c92SRobert Watson 
205aa015c8eSEdward Tomasz Napierala 	acl = acl_init(3); /* XXX: WTF, 3? */
2069fd46b02SChris D. Faulhaber 	if (acl == NULL) {
207515d7c92SRobert Watson 		free(mybuf_p);
208f0078215SRobert Watson 		return(NULL);
209515d7c92SRobert Watson 	}
210515d7c92SRobert Watson 
211374c6c0fSRobert Watson 	/* Outer loop: delimit at \n boundaries. */
212515d7c92SRobert Watson 	cur = mybuf_p;
213515d7c92SRobert Watson 	while ((line = strsep(&cur, "\n"))) {
214374c6c0fSRobert Watson 		/* Now split the line on the first # to strip out comments. */
215515d7c92SRobert Watson 		comment = line;
216515d7c92SRobert Watson 		notcomment = strsep(&comment, "#");
217515d7c92SRobert Watson 
218374c6c0fSRobert Watson 		/* Inner loop: delimit at ',' boundaries. */
219515d7c92SRobert Watson 		while ((entry = strsep(&notcomment, ","))) {
220aa015c8eSEdward Tomasz Napierala 
221aa015c8eSEdward Tomasz Napierala 			/* Skip empty lines. */
222aa015c8eSEdward Tomasz Napierala 			if (strlen(string_skip_whitespace(entry)) == 0)
223515d7c92SRobert Watson 				continue;
224515d7c92SRobert Watson 
225aa015c8eSEdward Tomasz Napierala 			if (_acl_brand(acl) == ACL_BRAND_UNKNOWN) {
226aa015c8eSEdward Tomasz Napierala 				if (_text_is_nfs4_entry(entry))
227aa015c8eSEdward Tomasz Napierala 					_acl_brand_as(acl, ACL_BRAND_NFS4);
228aa015c8eSEdward Tomasz Napierala 				else
229aa015c8eSEdward Tomasz Napierala 					_acl_brand_as(acl, ACL_BRAND_POSIX);
230515d7c92SRobert Watson 			}
231515d7c92SRobert Watson 
232aa015c8eSEdward Tomasz Napierala 			switch (_acl_brand(acl)) {
233aa015c8eSEdward Tomasz Napierala 			case ACL_BRAND_NFS4:
234aa015c8eSEdward Tomasz Napierala 				error = _nfs4_acl_entry_from_text(acl, entry);
235515d7c92SRobert Watson 				break;
236515d7c92SRobert Watson 
237aa015c8eSEdward Tomasz Napierala 			case ACL_BRAND_POSIX:
238aa015c8eSEdward Tomasz Napierala 				error = _posix1e_acl_entry_from_text(acl, entry);
239515d7c92SRobert Watson 				break;
240515d7c92SRobert Watson 
241515d7c92SRobert Watson 			default:
242aa015c8eSEdward Tomasz Napierala 				error = EINVAL;
243aa015c8eSEdward Tomasz Napierala 				break;
244515d7c92SRobert Watson 			}
245515d7c92SRobert Watson 
246aa015c8eSEdward Tomasz Napierala 			if (error)
247515d7c92SRobert Watson 				goto error_label;
248515d7c92SRobert Watson 		}
249515d7c92SRobert Watson 	}
250515d7c92SRobert Watson 
251515d7c92SRobert Watson #if 0
252374c6c0fSRobert Watson 	/* XXX Should we only return ACLs valid according to acl_valid? */
253374c6c0fSRobert Watson 	/* Verify validity of the ACL we read in. */
254515d7c92SRobert Watson 	if (acl_valid(acl) == -1) {
255515d7c92SRobert Watson 		errno = EINVAL;
256515d7c92SRobert Watson 		goto error_label;
257515d7c92SRobert Watson 	}
258515d7c92SRobert Watson #endif
259515d7c92SRobert Watson 
2604f6d5fdaSChristian Brueffer 	free(mybuf_p);
261515d7c92SRobert Watson 	return(acl);
262515d7c92SRobert Watson 
263515d7c92SRobert Watson error_label:
264515d7c92SRobert Watson 	acl_free(acl);
265515d7c92SRobert Watson 	free(mybuf_p);
266f0078215SRobert Watson 	return(NULL);
267515d7c92SRobert Watson }
268515d7c92SRobert Watson 
26948135111STim Kientzle /*
27048135111STim Kientzle  * Given a username/groupname from a text form of an ACL, return the uid/gid
27148135111STim Kientzle  * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM
27248135111STim Kientzle  * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
27348135111STim Kientzle  * MAY HAVE SIDE-EFFECTS
27448135111STim Kientzle  *
27548135111STim Kientzle  * XXX currently doesn't deal correctly with a numeric uid being passed
27648135111STim Kientzle  * instead of a username.  What is correct behavior here?  Check chown.
27748135111STim Kientzle  */
27848135111STim Kientzle static int
27948135111STim Kientzle _posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id)
28048135111STim Kientzle {
28148135111STim Kientzle 	struct group	*g;
28248135111STim Kientzle 	struct passwd	*p;
28348135111STim Kientzle 	unsigned long	l;
28448135111STim Kientzle 	char 		*endp;
285515d7c92SRobert Watson 
28648135111STim Kientzle 	switch(tag) {
28748135111STim Kientzle 	case ACL_USER:
28848135111STim Kientzle 		p = getpwnam(name);
28948135111STim Kientzle 		if (p == NULL) {
29048135111STim Kientzle 			l = strtoul(name, &endp, 0);
29148135111STim Kientzle 			if (*endp != '\0' || l != (unsigned long)(uid_t)l) {
29248135111STim Kientzle 				errno = EINVAL;
29348135111STim Kientzle 				return (-1);
29448135111STim Kientzle 			}
29548135111STim Kientzle 			*id = (uid_t)l;
29648135111STim Kientzle 			return (0);
29748135111STim Kientzle 		}
29848135111STim Kientzle 		*id = p->pw_uid;
29948135111STim Kientzle 		return (0);
300515d7c92SRobert Watson 
30148135111STim Kientzle 	case ACL_GROUP:
30248135111STim Kientzle 		g = getgrnam(name);
30348135111STim Kientzle 		if (g == NULL) {
30448135111STim Kientzle 			l = strtoul(name, &endp, 0);
30548135111STim Kientzle 			if (*endp != '\0' || l != (unsigned long)(gid_t)l) {
30648135111STim Kientzle 				errno = EINVAL;
30748135111STim Kientzle 				return (-1);
30848135111STim Kientzle 			}
30948135111STim Kientzle 			*id = (gid_t)l;
31048135111STim Kientzle 			return (0);
31148135111STim Kientzle 		}
31248135111STim Kientzle 		*id = g->gr_gid;
31348135111STim Kientzle 		return (0);
31448135111STim Kientzle 
31548135111STim Kientzle 	default:
31648135111STim Kientzle 		return (EINVAL);
31748135111STim Kientzle 	}
31848135111STim Kientzle }
319