1515d7c92SRobert Watson /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3d915a14eSPedro F. Giffuni *
42de14c39SRobert Watson * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson
5515d7c92SRobert Watson * All rights reserved.
6515d7c92SRobert Watson *
7515d7c92SRobert Watson * Redistribution and use in source and binary forms, with or without
8515d7c92SRobert Watson * modification, are permitted provided that the following conditions
9515d7c92SRobert Watson * are met:
10515d7c92SRobert Watson * 1. Redistributions of source code must retain the above copyright
11515d7c92SRobert Watson * notice, this list of conditions and the following disclaimer.
12515d7c92SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright
13515d7c92SRobert Watson * notice, this list of conditions and the following disclaimer in the
14515d7c92SRobert Watson * documentation and/or other materials provided with the distribution.
15515d7c92SRobert Watson *
16515d7c92SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17515d7c92SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18515d7c92SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19515d7c92SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20515d7c92SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21515d7c92SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22515d7c92SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23515d7c92SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24515d7c92SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25515d7c92SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26515d7c92SRobert Watson * SUCH DAMAGE.
27515d7c92SRobert Watson */
28515d7c92SRobert Watson /*
29374c6c0fSRobert Watson * acl_from_text: Convert a text-form ACL from a string to an acl_t.
30515d7c92SRobert Watson */
31515d7c92SRobert Watson
32515d7c92SRobert Watson #include <sys/types.h>
337bd44e92SThomas Moestl #include "namespace.h"
34515d7c92SRobert Watson #include <sys/acl.h>
357bd44e92SThomas Moestl #include "un-namespace.h"
36515d7c92SRobert Watson #include <sys/errno.h>
3748135111STim Kientzle #include <grp.h>
3848135111STim Kientzle #include <pwd.h>
39515d7c92SRobert Watson #include <stdio.h>
40515d7c92SRobert Watson #include <stdlib.h>
41515d7c92SRobert Watson #include <string.h>
42aa015c8eSEdward Tomasz Napierala #include <assert.h>
43515d7c92SRobert Watson
44515d7c92SRobert Watson #include "acl_support.h"
45515d7c92SRobert Watson
46c61eb011SChris D. Faulhaber static acl_tag_t acl_string_to_tag(char *tag, char *qualifier);
47c61eb011SChris D. Faulhaber
48aa015c8eSEdward Tomasz Napierala int _nfs4_acl_entry_from_text(acl_t aclp, char *entry);
49aa015c8eSEdward Tomasz Napierala int _text_could_be_nfs4_acl(const char *entry);
50515d7c92SRobert Watson
51c61eb011SChris D. Faulhaber static acl_tag_t
acl_string_to_tag(char * tag,char * qualifier)52515d7c92SRobert Watson acl_string_to_tag(char *tag, char *qualifier)
53515d7c92SRobert Watson {
54515d7c92SRobert Watson
55515d7c92SRobert Watson if (*qualifier == '\0') {
56515d7c92SRobert Watson if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
57515d7c92SRobert Watson return (ACL_USER_OBJ);
58515d7c92SRobert Watson } else
59515d7c92SRobert Watson if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
60515d7c92SRobert Watson return (ACL_GROUP_OBJ);
61515d7c92SRobert Watson } else
62515d7c92SRobert Watson if ((!strcmp(tag, "mask")) || (!strcmp(tag, "m"))) {
63515d7c92SRobert Watson return (ACL_MASK);
64515d7c92SRobert Watson } else
65515d7c92SRobert Watson if ((!strcmp(tag, "other")) || (!strcmp(tag, "o"))) {
66515d7c92SRobert Watson return (ACL_OTHER);
67515d7c92SRobert Watson } else
68515d7c92SRobert Watson return(-1);
69515d7c92SRobert Watson } else {
70515d7c92SRobert Watson if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
71515d7c92SRobert Watson return(ACL_USER);
72515d7c92SRobert Watson } else
73515d7c92SRobert Watson if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
74515d7c92SRobert Watson return(ACL_GROUP);
75515d7c92SRobert Watson } else
76515d7c92SRobert Watson return(-1);
77515d7c92SRobert Watson }
78515d7c92SRobert Watson }
79515d7c92SRobert Watson
80aa015c8eSEdward Tomasz Napierala static int
_posix1e_acl_entry_from_text(acl_t aclp,char * entry)81aa015c8eSEdward Tomasz Napierala _posix1e_acl_entry_from_text(acl_t aclp, char *entry)
82aa015c8eSEdward Tomasz Napierala {
83aa015c8eSEdward Tomasz Napierala acl_tag_t t;
84aa015c8eSEdward Tomasz Napierala acl_perm_t p;
85aa015c8eSEdward Tomasz Napierala char *tag, *qualifier, *permission;
86aa015c8eSEdward Tomasz Napierala uid_t id;
87aa015c8eSEdward Tomasz Napierala int error;
88aa015c8eSEdward Tomasz Napierala
89aa015c8eSEdward Tomasz Napierala assert(_acl_brand(aclp) == ACL_BRAND_POSIX);
90aa015c8eSEdward Tomasz Napierala
91aa015c8eSEdward Tomasz Napierala /* Split into three ':' delimited fields. */
92aa015c8eSEdward Tomasz Napierala tag = strsep(&entry, ":");
93aa015c8eSEdward Tomasz Napierala if (tag == NULL) {
94aa015c8eSEdward Tomasz Napierala errno = EINVAL;
95aa015c8eSEdward Tomasz Napierala return (-1);
96aa015c8eSEdward Tomasz Napierala }
97aa015c8eSEdward Tomasz Napierala tag = string_skip_whitespace(tag);
98aa015c8eSEdward Tomasz Napierala if ((*tag == '\0') && (!entry)) {
99aa015c8eSEdward Tomasz Napierala /*
100aa015c8eSEdward Tomasz Napierala * Is an entirely comment line, skip to next
101aa015c8eSEdward Tomasz Napierala * comma.
102aa015c8eSEdward Tomasz Napierala */
103aa015c8eSEdward Tomasz Napierala return (0);
104aa015c8eSEdward Tomasz Napierala }
105aa015c8eSEdward Tomasz Napierala string_trim_trailing_whitespace(tag);
106aa015c8eSEdward Tomasz Napierala
107aa015c8eSEdward Tomasz Napierala qualifier = strsep(&entry, ":");
108aa015c8eSEdward Tomasz Napierala if (qualifier == NULL) {
109aa015c8eSEdward Tomasz Napierala errno = EINVAL;
110aa015c8eSEdward Tomasz Napierala return (-1);
111aa015c8eSEdward Tomasz Napierala }
112aa015c8eSEdward Tomasz Napierala qualifier = string_skip_whitespace(qualifier);
113aa015c8eSEdward Tomasz Napierala string_trim_trailing_whitespace(qualifier);
114aa015c8eSEdward Tomasz Napierala
115aa015c8eSEdward Tomasz Napierala permission = strsep(&entry, ":");
116aa015c8eSEdward Tomasz Napierala if (permission == NULL || entry) {
117aa015c8eSEdward Tomasz Napierala errno = EINVAL;
118aa015c8eSEdward Tomasz Napierala return (-1);
119aa015c8eSEdward Tomasz Napierala }
120aa015c8eSEdward Tomasz Napierala permission = string_skip_whitespace(permission);
121aa015c8eSEdward Tomasz Napierala string_trim_trailing_whitespace(permission);
122aa015c8eSEdward Tomasz Napierala
123aa015c8eSEdward Tomasz Napierala t = acl_string_to_tag(tag, qualifier);
124aa015c8eSEdward Tomasz Napierala if (t == -1) {
125aa015c8eSEdward Tomasz Napierala errno = EINVAL;
126aa015c8eSEdward Tomasz Napierala return (-1);
127aa015c8eSEdward Tomasz Napierala }
128aa015c8eSEdward Tomasz Napierala
129aa015c8eSEdward Tomasz Napierala error = _posix1e_acl_string_to_perm(permission, &p);
130aa015c8eSEdward Tomasz Napierala if (error == -1) {
131aa015c8eSEdward Tomasz Napierala errno = EINVAL;
132aa015c8eSEdward Tomasz Napierala return (-1);
133aa015c8eSEdward Tomasz Napierala }
134aa015c8eSEdward Tomasz Napierala
135aa015c8eSEdward Tomasz Napierala switch(t) {
136aa015c8eSEdward Tomasz Napierala case ACL_USER_OBJ:
137aa015c8eSEdward Tomasz Napierala case ACL_GROUP_OBJ:
138aa015c8eSEdward Tomasz Napierala case ACL_MASK:
139aa015c8eSEdward Tomasz Napierala case ACL_OTHER:
140aa015c8eSEdward Tomasz Napierala if (*qualifier != '\0') {
141aa015c8eSEdward Tomasz Napierala errno = EINVAL;
142aa015c8eSEdward Tomasz Napierala return (-1);
143aa015c8eSEdward Tomasz Napierala }
144aa015c8eSEdward Tomasz Napierala id = 0;
145aa015c8eSEdward Tomasz Napierala break;
146aa015c8eSEdward Tomasz Napierala
147aa015c8eSEdward Tomasz Napierala case ACL_USER:
148aa015c8eSEdward Tomasz Napierala case ACL_GROUP:
149f59646c2SEdward Tomasz Napierala error = _acl_name_to_id(t, qualifier, &id);
150aa015c8eSEdward Tomasz Napierala if (error == -1)
151aa015c8eSEdward Tomasz Napierala return (-1);
152aa015c8eSEdward Tomasz Napierala break;
153aa015c8eSEdward Tomasz Napierala
154aa015c8eSEdward Tomasz Napierala default:
155aa015c8eSEdward Tomasz Napierala errno = EINVAL;
156aa015c8eSEdward Tomasz Napierala return (-1);
157aa015c8eSEdward Tomasz Napierala }
158aa015c8eSEdward Tomasz Napierala
159aa015c8eSEdward Tomasz Napierala error = _posix1e_acl_add_entry(aclp, t, id, p);
160aa015c8eSEdward Tomasz Napierala if (error == -1)
161aa015c8eSEdward Tomasz Napierala return (-1);
162aa015c8eSEdward Tomasz Napierala
163aa015c8eSEdward Tomasz Napierala return (0);
164aa015c8eSEdward Tomasz Napierala }
165aa015c8eSEdward Tomasz Napierala
166aa015c8eSEdward Tomasz Napierala static int
_text_is_nfs4_entry(const char * entry)167aa015c8eSEdward Tomasz Napierala _text_is_nfs4_entry(const char *entry)
168aa015c8eSEdward Tomasz Napierala {
169aa015c8eSEdward Tomasz Napierala int count = 0;
170aa015c8eSEdward Tomasz Napierala
171aa015c8eSEdward Tomasz Napierala assert(strlen(entry) > 0);
172aa015c8eSEdward Tomasz Napierala
173aa015c8eSEdward Tomasz Napierala while (*entry != '\0') {
174aa015c8eSEdward Tomasz Napierala if (*entry == ':' || *entry == '@')
175aa015c8eSEdward Tomasz Napierala count++;
176aa015c8eSEdward Tomasz Napierala entry++;
177aa015c8eSEdward Tomasz Napierala }
178aa015c8eSEdward Tomasz Napierala
179aa015c8eSEdward Tomasz Napierala if (count <= 2)
180aa015c8eSEdward Tomasz Napierala return (0);
181aa015c8eSEdward Tomasz Napierala
182aa015c8eSEdward Tomasz Napierala return (1);
183aa015c8eSEdward Tomasz Napierala }
184aa015c8eSEdward Tomasz Napierala
185515d7c92SRobert Watson /*
186374c6c0fSRobert Watson * acl_from_text -- Convert a string into an ACL.
187374c6c0fSRobert Watson * Postpone most validity checking until the end and call acl_valid() to do
188515d7c92SRobert Watson * that.
189515d7c92SRobert Watson */
190515d7c92SRobert Watson acl_t
acl_from_text(const char * buf_p)191515d7c92SRobert Watson acl_from_text(const char *buf_p)
192515d7c92SRobert Watson {
193515d7c92SRobert Watson acl_t acl;
194515d7c92SRobert Watson char *mybuf_p, *line, *cur, *notcomment, *comment, *entry;
195515d7c92SRobert Watson int error;
196515d7c92SRobert Watson
197374c6c0fSRobert Watson /* Local copy we can mess up. */
198515d7c92SRobert Watson mybuf_p = strdup(buf_p);
1999fd46b02SChris D. Faulhaber if (mybuf_p == NULL)
200f0078215SRobert Watson return(NULL);
201515d7c92SRobert Watson
202aa015c8eSEdward Tomasz Napierala acl = acl_init(3); /* XXX: WTF, 3? */
2039fd46b02SChris D. Faulhaber if (acl == NULL) {
204515d7c92SRobert Watson free(mybuf_p);
205f0078215SRobert Watson return(NULL);
206515d7c92SRobert Watson }
207515d7c92SRobert Watson
208374c6c0fSRobert Watson /* Outer loop: delimit at \n boundaries. */
209515d7c92SRobert Watson cur = mybuf_p;
210515d7c92SRobert Watson while ((line = strsep(&cur, "\n"))) {
211374c6c0fSRobert Watson /* Now split the line on the first # to strip out comments. */
212515d7c92SRobert Watson comment = line;
213515d7c92SRobert Watson notcomment = strsep(&comment, "#");
214515d7c92SRobert Watson
215374c6c0fSRobert Watson /* Inner loop: delimit at ',' boundaries. */
216515d7c92SRobert Watson while ((entry = strsep(¬comment, ","))) {
217aa015c8eSEdward Tomasz Napierala
218aa015c8eSEdward Tomasz Napierala /* Skip empty lines. */
219aa015c8eSEdward Tomasz Napierala if (strlen(string_skip_whitespace(entry)) == 0)
220515d7c92SRobert Watson continue;
221515d7c92SRobert Watson
222aa015c8eSEdward Tomasz Napierala if (_acl_brand(acl) == ACL_BRAND_UNKNOWN) {
223aa015c8eSEdward Tomasz Napierala if (_text_is_nfs4_entry(entry))
224aa015c8eSEdward Tomasz Napierala _acl_brand_as(acl, ACL_BRAND_NFS4);
225aa015c8eSEdward Tomasz Napierala else
226aa015c8eSEdward Tomasz Napierala _acl_brand_as(acl, ACL_BRAND_POSIX);
227515d7c92SRobert Watson }
228515d7c92SRobert Watson
229aa015c8eSEdward Tomasz Napierala switch (_acl_brand(acl)) {
230aa015c8eSEdward Tomasz Napierala case ACL_BRAND_NFS4:
231aa015c8eSEdward Tomasz Napierala error = _nfs4_acl_entry_from_text(acl, entry);
232515d7c92SRobert Watson break;
233515d7c92SRobert Watson
234aa015c8eSEdward Tomasz Napierala case ACL_BRAND_POSIX:
235aa015c8eSEdward Tomasz Napierala error = _posix1e_acl_entry_from_text(acl, entry);
236515d7c92SRobert Watson break;
237515d7c92SRobert Watson
238515d7c92SRobert Watson default:
239aa015c8eSEdward Tomasz Napierala error = EINVAL;
240aa015c8eSEdward Tomasz Napierala break;
241515d7c92SRobert Watson }
242515d7c92SRobert Watson
243aa015c8eSEdward Tomasz Napierala if (error)
244515d7c92SRobert Watson goto error_label;
245515d7c92SRobert Watson }
246515d7c92SRobert Watson }
247515d7c92SRobert Watson
248515d7c92SRobert Watson #if 0
249374c6c0fSRobert Watson /* XXX Should we only return ACLs valid according to acl_valid? */
250374c6c0fSRobert Watson /* Verify validity of the ACL we read in. */
251515d7c92SRobert Watson if (acl_valid(acl) == -1) {
252515d7c92SRobert Watson errno = EINVAL;
253515d7c92SRobert Watson goto error_label;
254515d7c92SRobert Watson }
255515d7c92SRobert Watson #endif
256515d7c92SRobert Watson
2574f6d5fdaSChristian Brueffer free(mybuf_p);
258515d7c92SRobert Watson return(acl);
259515d7c92SRobert Watson
260515d7c92SRobert Watson error_label:
261515d7c92SRobert Watson acl_free(acl);
262515d7c92SRobert Watson free(mybuf_p);
263f0078215SRobert Watson return(NULL);
264515d7c92SRobert Watson }
265515d7c92SRobert Watson
26648135111STim Kientzle /*
26748135111STim Kientzle * Given a username/groupname from a text form of an ACL, return the uid/gid
26848135111STim Kientzle * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM
26948135111STim Kientzle * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
27048135111STim Kientzle * MAY HAVE SIDE-EFFECTS
27148135111STim Kientzle */
272f59646c2SEdward Tomasz Napierala int
_acl_name_to_id(acl_tag_t tag,char * name,uid_t * id)273f59646c2SEdward Tomasz Napierala _acl_name_to_id(acl_tag_t tag, char *name, uid_t *id)
27448135111STim Kientzle {
27548135111STim Kientzle struct group *g;
27648135111STim Kientzle struct passwd *p;
27748135111STim Kientzle unsigned long l;
27848135111STim Kientzle char *endp;
279515d7c92SRobert Watson
28048135111STim Kientzle switch(tag) {
28148135111STim Kientzle case ACL_USER:
28248135111STim Kientzle p = getpwnam(name);
28348135111STim Kientzle if (p == NULL) {
28448135111STim Kientzle l = strtoul(name, &endp, 0);
28548135111STim Kientzle if (*endp != '\0' || l != (unsigned long)(uid_t)l) {
28648135111STim Kientzle errno = EINVAL;
28748135111STim Kientzle return (-1);
28848135111STim Kientzle }
28948135111STim Kientzle *id = (uid_t)l;
29048135111STim Kientzle return (0);
29148135111STim Kientzle }
29248135111STim Kientzle *id = p->pw_uid;
29348135111STim Kientzle return (0);
294515d7c92SRobert Watson
29548135111STim Kientzle case ACL_GROUP:
29648135111STim Kientzle g = getgrnam(name);
29748135111STim Kientzle if (g == NULL) {
29848135111STim Kientzle l = strtoul(name, &endp, 0);
29948135111STim Kientzle if (*endp != '\0' || l != (unsigned long)(gid_t)l) {
30048135111STim Kientzle errno = EINVAL;
30148135111STim Kientzle return (-1);
30248135111STim Kientzle }
30348135111STim Kientzle *id = (gid_t)l;
30448135111STim Kientzle return (0);
30548135111STim Kientzle }
30648135111STim Kientzle *id = g->gr_gid;
30748135111STim Kientzle return (0);
30848135111STim Kientzle
30948135111STim Kientzle default:
31048135111STim Kientzle return (EINVAL);
31148135111STim Kientzle }
31248135111STim Kientzle }
313