156055Srwatson/*-
274191Srwatson * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson
356055Srwatson * All rights reserved.
456055Srwatson *
556055Srwatson * Redistribution and use in source and binary forms, with or without
656055Srwatson * modification, are permitted provided that the following conditions
756055Srwatson * are met:
856055Srwatson * 1. Redistributions of source code must retain the above copyright
956055Srwatson *    notice, this list of conditions and the following disclaimer.
1056055Srwatson * 2. Redistributions in binary form must reproduce the above copyright
1156055Srwatson *    notice, this list of conditions and the following disclaimer in the
1256055Srwatson *    documentation and/or other materials provided with the distribution.
1356055Srwatson *
1456055Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1556055Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1656055Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1756055Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1856055Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1956055Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2056055Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2156055Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2256055Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2356055Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2456055Srwatson * SUCH DAMAGE.
2556055Srwatson */
2656055Srwatson/*
2766259Srwatson * acl_from_text: Convert a text-form ACL from a string to an acl_t.
2856055Srwatson */
2956055Srwatson
3092986Sobrien#include <sys/cdefs.h>
3192986Sobrien__FBSDID("$FreeBSD$");
3292986Sobrien
3356055Srwatson#include <sys/types.h>
3475185Stmm#include "namespace.h"
3556055Srwatson#include <sys/acl.h>
3675185Stmm#include "un-namespace.h"
3756055Srwatson#include <sys/errno.h>
38167006Skientzle#include <grp.h>
39167006Skientzle#include <pwd.h>
4056055Srwatson#include <stdio.h>
4156055Srwatson#include <stdlib.h>
4256055Srwatson#include <string.h>
43194955Strasz#include <assert.h>
4456055Srwatson
4556055Srwatson#include "acl_support.h"
4656055Srwatson
4791032Sjedgarstatic acl_tag_t acl_string_to_tag(char *tag, char *qualifier);
4891032Sjedgar
49194955Straszint _nfs4_acl_entry_from_text(acl_t aclp, char *entry);
50194955Straszint _text_could_be_nfs4_acl(const char *entry);
5156055Srwatson
5291032Sjedgarstatic acl_tag_t
5356055Srwatsonacl_string_to_tag(char *tag, char *qualifier)
5456055Srwatson{
5556055Srwatson
5656055Srwatson	if (*qualifier == '\0') {
5756055Srwatson		if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
5856055Srwatson			return (ACL_USER_OBJ);
5956055Srwatson		} else
6056055Srwatson		if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
6156055Srwatson			return (ACL_GROUP_OBJ);
6256055Srwatson		} else
6356055Srwatson		if ((!strcmp(tag, "mask")) || (!strcmp(tag, "m"))) {
6456055Srwatson			return (ACL_MASK);
6556055Srwatson		} else
6656055Srwatson		if ((!strcmp(tag, "other")) || (!strcmp(tag, "o"))) {
6756055Srwatson			return (ACL_OTHER);
6856055Srwatson		} else
6956055Srwatson			return(-1);
7056055Srwatson	} else {
7156055Srwatson		if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
7256055Srwatson			return(ACL_USER);
7356055Srwatson		} else
7456055Srwatson		if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
7556055Srwatson			return(ACL_GROUP);
7656055Srwatson		} else
7756055Srwatson			return(-1);
7856055Srwatson	}
7956055Srwatson}
8056055Srwatson
81194955Straszstatic int
82194955Strasz_posix1e_acl_entry_from_text(acl_t aclp, char *entry)
83194955Strasz{
84194955Strasz	acl_tag_t	 t;
85194955Strasz	acl_perm_t	 p;
86194955Strasz	char		*tag, *qualifier, *permission;
87194955Strasz	uid_t		 id;
88194955Strasz	int		 error;
89194955Strasz
90194955Strasz	assert(_acl_brand(aclp) == ACL_BRAND_POSIX);
91194955Strasz
92194955Strasz	/* Split into three ':' delimited fields. */
93194955Strasz	tag = strsep(&entry, ":");
94194955Strasz	if (tag == NULL) {
95194955Strasz		errno = EINVAL;
96194955Strasz		return (-1);
97194955Strasz	}
98194955Strasz	tag = string_skip_whitespace(tag);
99194955Strasz	if ((*tag == '\0') && (!entry)) {
100194955Strasz		/*
101194955Strasz		 * Is an entirely comment line, skip to next
102194955Strasz		 * comma.
103194955Strasz		 */
104194955Strasz		return (0);
105194955Strasz	}
106194955Strasz	string_trim_trailing_whitespace(tag);
107194955Strasz
108194955Strasz	qualifier = strsep(&entry, ":");
109194955Strasz	if (qualifier == NULL) {
110194955Strasz		errno = EINVAL;
111194955Strasz		return (-1);
112194955Strasz	}
113194955Strasz	qualifier = string_skip_whitespace(qualifier);
114194955Strasz	string_trim_trailing_whitespace(qualifier);
115194955Strasz
116194955Strasz	permission = strsep(&entry, ":");
117194955Strasz	if (permission == NULL || entry) {
118194955Strasz		errno = EINVAL;
119194955Strasz		return (-1);
120194955Strasz	}
121194955Strasz	permission = string_skip_whitespace(permission);
122194955Strasz	string_trim_trailing_whitespace(permission);
123194955Strasz
124194955Strasz	t = acl_string_to_tag(tag, qualifier);
125194955Strasz	if (t == -1) {
126194955Strasz		errno = EINVAL;
127194955Strasz		return (-1);
128194955Strasz	}
129194955Strasz
130194955Strasz	error = _posix1e_acl_string_to_perm(permission, &p);
131194955Strasz	if (error == -1) {
132194955Strasz		errno = EINVAL;
133194955Strasz		return (-1);
134194955Strasz	}
135194955Strasz
136194955Strasz	switch(t) {
137194955Strasz		case ACL_USER_OBJ:
138194955Strasz		case ACL_GROUP_OBJ:
139194955Strasz		case ACL_MASK:
140194955Strasz		case ACL_OTHER:
141194955Strasz			if (*qualifier != '\0') {
142194955Strasz				errno = EINVAL;
143194955Strasz				return (-1);
144194955Strasz			}
145194955Strasz			id = 0;
146194955Strasz			break;
147194955Strasz
148194955Strasz		case ACL_USER:
149194955Strasz		case ACL_GROUP:
150209736Strasz			error = _acl_name_to_id(t, qualifier, &id);
151194955Strasz			if (error == -1)
152194955Strasz				return (-1);
153194955Strasz			break;
154194955Strasz
155194955Strasz		default:
156194955Strasz			errno = EINVAL;
157194955Strasz			return (-1);
158194955Strasz	}
159194955Strasz
160194955Strasz	error = _posix1e_acl_add_entry(aclp, t, id, p);
161194955Strasz	if (error == -1)
162194955Strasz		return (-1);
163194955Strasz
164194955Strasz	return (0);
165194955Strasz}
166194955Strasz
167194955Straszstatic int
168194955Strasz_text_is_nfs4_entry(const char *entry)
169194955Strasz{
170194955Strasz	int count = 0;
171194955Strasz
172194955Strasz	assert(strlen(entry) > 0);
173194955Strasz
174194955Strasz	while (*entry != '\0') {
175194955Strasz		if (*entry == ':' || *entry == '@')
176194955Strasz			count++;
177194955Strasz		entry++;
178194955Strasz	}
179194955Strasz
180194955Strasz	if (count <= 2)
181194955Strasz		return (0);
182194955Strasz
183194955Strasz	return (1);
184194955Strasz}
185194955Strasz
18656055Srwatson/*
18766259Srwatson * acl_from_text -- Convert a string into an ACL.
18866259Srwatson * Postpone most validity checking until the end and call acl_valid() to do
18956055Srwatson * that.
19056055Srwatson */
19156055Srwatsonacl_t
19256055Srwatsonacl_from_text(const char *buf_p)
19356055Srwatson{
19475928Sjedgar	acl_t		 acl;
19575928Sjedgar	char		*mybuf_p, *line, *cur, *notcomment, *comment, *entry;
19675928Sjedgar	int		 error;
19756055Srwatson
19866259Srwatson	/* Local copy we can mess up. */
19956055Srwatson	mybuf_p = strdup(buf_p);
20091034Sjedgar	if (mybuf_p == NULL)
20171142Srwatson		return(NULL);
20256055Srwatson
203194955Strasz	acl = acl_init(3); /* XXX: WTF, 3? */
20491034Sjedgar	if (acl == NULL) {
20556055Srwatson		free(mybuf_p);
20671142Srwatson		return(NULL);
20756055Srwatson	}
20856055Srwatson
20966259Srwatson	/* Outer loop: delimit at \n boundaries. */
21056055Srwatson	cur = mybuf_p;
21156055Srwatson	while ((line = strsep(&cur, "\n"))) {
21266259Srwatson		/* Now split the line on the first # to strip out comments. */
21356055Srwatson		comment = line;
21456055Srwatson		notcomment = strsep(&comment, "#");
21556055Srwatson
21666259Srwatson		/* Inner loop: delimit at ',' boundaries. */
21756055Srwatson		while ((entry = strsep(&notcomment, ","))) {
218194955Strasz
219194955Strasz			/* Skip empty lines. */
220194955Strasz			if (strlen(string_skip_whitespace(entry)) == 0)
22156055Srwatson				continue;
22256055Srwatson
223194955Strasz			if (_acl_brand(acl) == ACL_BRAND_UNKNOWN) {
224194955Strasz				if (_text_is_nfs4_entry(entry))
225194955Strasz					_acl_brand_as(acl, ACL_BRAND_NFS4);
226194955Strasz				else
227194955Strasz					_acl_brand_as(acl, ACL_BRAND_POSIX);
22856055Srwatson			}
22956055Srwatson
230194955Strasz			switch (_acl_brand(acl)) {
231194955Strasz			case ACL_BRAND_NFS4:
232194955Strasz				error = _nfs4_acl_entry_from_text(acl, entry);
23356055Srwatson				break;
23456055Srwatson
235194955Strasz			case ACL_BRAND_POSIX:
236194955Strasz				error = _posix1e_acl_entry_from_text(acl, entry);
23756055Srwatson				break;
23856055Srwatson
23956055Srwatson			default:
240194955Strasz				error = EINVAL;
241194955Strasz				break;
24256055Srwatson			}
24356055Srwatson
244194955Strasz			if (error)
24556055Srwatson				goto error_label;
24656055Srwatson		}
24756055Srwatson	}
24856055Srwatson
24956055Srwatson#if 0
25066259Srwatson	/* XXX Should we only return ACLs valid according to acl_valid? */
25166259Srwatson	/* Verify validity of the ACL we read in. */
25256055Srwatson	if (acl_valid(acl) == -1) {
25356055Srwatson		errno = EINVAL;
25456055Srwatson		goto error_label;
25556055Srwatson	}
25656055Srwatson#endif
25756055Srwatson
258199317Sbrueffer	free(mybuf_p);
25956055Srwatson	return(acl);
26056055Srwatson
26156055Srwatsonerror_label:
26256055Srwatson	acl_free(acl);
26356055Srwatson	free(mybuf_p);
26471142Srwatson	return(NULL);
26556055Srwatson}
26656055Srwatson
267167006Skientzle/*
268167006Skientzle * Given a username/groupname from a text form of an ACL, return the uid/gid
269167006Skientzle * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM
270167006Skientzle * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
271167006Skientzle * MAY HAVE SIDE-EFFECTS
272167006Skientzle */
273209736Straszint
274209736Strasz_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id)
275167006Skientzle{
276167006Skientzle	struct group	*g;
277167006Skientzle	struct passwd	*p;
278167006Skientzle	unsigned long	l;
279167006Skientzle	char 		*endp;
28056055Srwatson
281167006Skientzle	switch(tag) {
282167006Skientzle	case ACL_USER:
283167006Skientzle		p = getpwnam(name);
284167006Skientzle		if (p == NULL) {
285167006Skientzle			l = strtoul(name, &endp, 0);
286167006Skientzle			if (*endp != '\0' || l != (unsigned long)(uid_t)l) {
287167006Skientzle				errno = EINVAL;
288167006Skientzle				return (-1);
289167006Skientzle			}
290167006Skientzle			*id = (uid_t)l;
291167006Skientzle			return (0);
292167006Skientzle		}
293167006Skientzle		*id = p->pw_uid;
294167006Skientzle		return (0);
29556055Srwatson
296167006Skientzle	case ACL_GROUP:
297167006Skientzle		g = getgrnam(name);
298167006Skientzle		if (g == NULL) {
299167006Skientzle			l = strtoul(name, &endp, 0);
300167006Skientzle			if (*endp != '\0' || l != (unsigned long)(gid_t)l) {
301167006Skientzle				errno = EINVAL;
302167006Skientzle				return (-1);
303167006Skientzle			}
304167006Skientzle			*id = (gid_t)l;
305167006Skientzle			return (0);
306167006Skientzle		}
307167006Skientzle		*id = g->gr_gid;
308167006Skientzle		return (0);
309167006Skientzle
310167006Skientzle	default:
311167006Skientzle		return (EINVAL);
312167006Skientzle	}
313167006Skientzle}
314