Home | History | Annotate | Line # | Download | only in posix1e
acl_from_text.c revision 1.1
      1 /*-
      2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
      3  *
      4  * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 /*
     29  * acl_from_text: Convert a text-form ACL from a string to an acl_t.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #if 0
     34 __FBSDID("$FreeBSD: head/lib/libc/posix1e/acl_from_text.c 326193 2017-11-25 17:12:48Z pfg $");
     35 #else
     36 __RCSID("$NetBSD: acl_from_text.c,v 1.1 2020/05/16 18:31:47 christos Exp $");
     37 #endif
     38 
     39 #include "namespace.h"
     40 #include <sys/types.h>
     41 #include <sys/acl.h>
     42 #include <errno.h>
     43 #include <grp.h>
     44 #include <pwd.h>
     45 #include <stdio.h>
     46 #include <stdlib.h>
     47 #include <string.h>
     48 #include <assert.h>
     49 
     50 #include "acl_support.h"
     51 
     52 static acl_tag_t acl_string_to_tag(char *tag, char *qualifier);
     53 
     54 int _nfs4_acl_entry_from_text(acl_t aclp, char *entry);
     55 int _text_could_be_nfs4_acl(const char *entry);
     56 
     57 static acl_tag_t
     58 acl_string_to_tag(char *tag, char *qualifier)
     59 {
     60 
     61 	if (*qualifier == '\0') {
     62 		if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
     63 			return (ACL_USER_OBJ);
     64 		} else
     65 		if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
     66 			return (ACL_GROUP_OBJ);
     67 		} else
     68 		if ((!strcmp(tag, "mask")) || (!strcmp(tag, "m"))) {
     69 			return (ACL_MASK);
     70 		} else
     71 		if ((!strcmp(tag, "other")) || (!strcmp(tag, "o"))) {
     72 			return (ACL_OTHER);
     73 		} else
     74 			return(-1);
     75 	} else {
     76 		if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
     77 			return(ACL_USER);
     78 		} else
     79 		if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
     80 			return(ACL_GROUP);
     81 		} else
     82 			return(-1);
     83 	}
     84 }
     85 
     86 static int
     87 _posix1e_acl_entry_from_text(acl_t aclp, char *entry)
     88 {
     89 	acl_tag_t	 t;
     90 	acl_perm_t	 p;
     91 	char		*tag, *qualifier, *permission;
     92 	uid_t		 id;
     93 	int		 error;
     94 
     95 	assert(_acl_brand(aclp) == ACL_BRAND_POSIX);
     96 
     97 	/* Split into three ':' delimited fields. */
     98 	tag = strsep(&entry, ":");
     99 	if (tag == NULL) {
    100 		errno = EINVAL;
    101 		return (-1);
    102 	}
    103 	tag = string_skip_whitespace(tag);
    104 	if ((*tag == '\0') && (!entry)) {
    105 		/*
    106 		 * Is an entirely comment line, skip to next
    107 		 * comma.
    108 		 */
    109 		return (0);
    110 	}
    111 	string_trim_trailing_whitespace(tag);
    112 
    113 	qualifier = strsep(&entry, ":");
    114 	if (qualifier == NULL) {
    115 		errno = EINVAL;
    116 		return (-1);
    117 	}
    118 	qualifier = string_skip_whitespace(qualifier);
    119 	string_trim_trailing_whitespace(qualifier);
    120 
    121 	permission = strsep(&entry, ":");
    122 	if (permission == NULL || entry) {
    123 		errno = EINVAL;
    124 		return (-1);
    125 	}
    126 	permission = string_skip_whitespace(permission);
    127 	string_trim_trailing_whitespace(permission);
    128 
    129 	t = acl_string_to_tag(tag, qualifier);
    130 	if (t == (acl_tag_t)-1) {
    131 		errno = EINVAL;
    132 		return (-1);
    133 	}
    134 
    135 	error = _posix1e_acl_string_to_perm(permission, &p);
    136 	if (error == -1) {
    137 		errno = EINVAL;
    138 		return (-1);
    139 	}
    140 
    141 	switch(t) {
    142 		case ACL_USER_OBJ:
    143 		case ACL_GROUP_OBJ:
    144 		case ACL_MASK:
    145 		case ACL_OTHER:
    146 			if (*qualifier != '\0') {
    147 				errno = EINVAL;
    148 				return (-1);
    149 			}
    150 			id = 0;
    151 			break;
    152 
    153 		case ACL_USER:
    154 		case ACL_GROUP:
    155 			error = _acl_name_to_id(t, qualifier, &id);
    156 			if (error == -1)
    157 				return (-1);
    158 			break;
    159 
    160 		default:
    161 			errno = EINVAL;
    162 			return (-1);
    163 	}
    164 
    165 	error = _posix1e_acl_add_entry(aclp, t, id, p);
    166 	if (error == -1)
    167 		return (-1);
    168 
    169 	return (0);
    170 }
    171 
    172 static int
    173 _text_is_nfs4_entry(const char *entry)
    174 {
    175 	int count = 0;
    176 
    177 	assert(strlen(entry) > 0);
    178 
    179 	while (*entry != '\0') {
    180 		if (*entry == ':' || *entry == '@')
    181 			count++;
    182 		entry++;
    183 	}
    184 
    185 	if (count <= 2)
    186 		return (0);
    187 
    188 	return (1);
    189 }
    190 
    191 /*
    192  * acl_from_text -- Convert a string into an ACL.
    193  * Postpone most validity checking until the end and call acl_valid() to do
    194  * that.
    195  */
    196 acl_t
    197 acl_from_text(const char *buf_p)
    198 {
    199 	acl_t		 acl;
    200 	char		*mybuf_p, *line, *cur, *notcomment, *comment, *entry;
    201 	int		 error;
    202 
    203 	/* Local copy we can mess up. */
    204 	mybuf_p = strdup(buf_p);
    205 	if (mybuf_p == NULL)
    206 		return(NULL);
    207 
    208 	acl = acl_init(3); /* XXX: WTF, 3? */
    209 	if (acl == NULL) {
    210 		free(mybuf_p);
    211 		return(NULL);
    212 	}
    213 
    214 	/* Outer loop: delimit at \n boundaries. */
    215 	cur = mybuf_p;
    216 	while ((line = strsep(&cur, "\n"))) {
    217 		/* Now split the line on the first # to strip out comments. */
    218 		comment = line;
    219 		notcomment = strsep(&comment, "#");
    220 
    221 		/* Inner loop: delimit at ',' boundaries. */
    222 		while ((entry = strsep(&notcomment, ","))) {
    223 
    224 			/* Skip empty lines. */
    225 			if (strlen(string_skip_whitespace(entry)) == 0)
    226 				continue;
    227 
    228 			if (_acl_brand(acl) == ACL_BRAND_UNKNOWN) {
    229 				if (_text_is_nfs4_entry(entry))
    230 					_acl_brand_as(acl, ACL_BRAND_NFS4);
    231 				else
    232 					_acl_brand_as(acl, ACL_BRAND_POSIX);
    233 			}
    234 
    235 			switch (_acl_brand(acl)) {
    236 			case ACL_BRAND_NFS4:
    237 				error = _nfs4_acl_entry_from_text(acl, entry);
    238 				break;
    239 
    240 			case ACL_BRAND_POSIX:
    241 				error = _posix1e_acl_entry_from_text(acl, entry);
    242 				break;
    243 
    244 			default:
    245 				error = EINVAL;
    246 				break;
    247 			}
    248 
    249 			if (error)
    250 				goto error_label;
    251 		}
    252 	}
    253 
    254 #if 0
    255 	/* XXX Should we only return ACLs valid according to acl_valid? */
    256 	/* Verify validity of the ACL we read in. */
    257 	if (acl_valid(acl) == -1) {
    258 		errno = EINVAL;
    259 		goto error_label;
    260 	}
    261 #endif
    262 
    263 	free(mybuf_p);
    264 	return(acl);
    265 
    266 error_label:
    267 	acl_free(acl);
    268 	free(mybuf_p);
    269 	return(NULL);
    270 }
    271 
    272 /*
    273  * Given a username/groupname from a text form of an ACL, return the uid/gid
    274  * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM
    275  * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
    276  * MAY HAVE SIDE-EFFECTS
    277  */
    278 int
    279 _acl_name_to_id(acl_tag_t tag, char *name, uid_t *id)
    280 {
    281 	struct group	*g;
    282 	struct passwd	*p;
    283 	unsigned long	l;
    284 	char 		*endp;
    285 
    286 	switch(tag) {
    287 	case ACL_USER:
    288 		p = getpwnam(name);
    289 		if (p == NULL) {
    290 			l = strtoul(name, &endp, 0);
    291 			if (*endp != '\0' || l != (unsigned long)(uid_t)l) {
    292 				errno = EINVAL;
    293 				return (-1);
    294 			}
    295 			*id = (uid_t)l;
    296 			return (0);
    297 		}
    298 		*id = p->pw_uid;
    299 		return (0);
    300 
    301 	case ACL_GROUP:
    302 		g = getgrnam(name);
    303 		if (g == NULL) {
    304 			l = strtoul(name, &endp, 0);
    305 			if (*endp != '\0' || l != (unsigned long)(gid_t)l) {
    306 				errno = EINVAL;
    307 				return (-1);
    308 			}
    309 			*id = (gid_t)l;
    310 			return (0);
    311 		}
    312 		*id = g->gr_gid;
    313 		return (0);
    314 
    315 	default:
    316 		return (EINVAL);
    317 	}
    318 }
    319