Home | History | Annotate | Line # | Download | only in posix1e
      1 /*-
      2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
      3  *
      4  * Copyright (c) 2008, 2009 Edward Tomasz Napieraa <trasz (at) FreeBSD.org>
      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 #include <sys/cdefs.h>
     30 #if 0
     31 __FBSDID("$FreeBSD: head/lib/libc/posix1e/acl_from_text_nfs4.c 326193 2017-11-25 17:12:48Z pfg $");
     32 #else
     33 __RCSID("$NetBSD: acl_from_text_nfs4.c,v 1.2 2024/01/20 14:52:48 christos Exp $");
     34 #endif
     35 
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <unistd.h>
     39 #include <errno.h>
     40 #include <assert.h>
     41 #include <string.h>
     42 #include <pwd.h>
     43 #include <grp.h>
     44 #include <ctype.h>
     45 #include <err.h>
     46 #include <sys/syscall.h>
     47 #include <sys/types.h>
     48 #include <sys/acl.h>
     49 
     50 #include "acl_support.h"
     51 
     52 #define MAX_ENTRY_LENGTH 512
     53 
     54 /*
     55  * Parse the tag field of ACL entry passed as "str".  If qualifier
     56  * needs to follow, then the variable referenced by "need_qualifier"
     57  * is set to 1, otherwise it's set to 0.
     58  */
     59 static int
     60 parse_tag(const char *str, acl_entry_t entry, int *need_qualifier)
     61 {
     62 
     63 	assert(need_qualifier != NULL);
     64 	*need_qualifier = 0;
     65 
     66 	if (strcmp(str, "owner@") == 0)
     67 		return (acl_set_tag_type(entry, ACL_USER_OBJ));
     68 	if (strcmp(str, "group@") == 0)
     69 		return (acl_set_tag_type(entry, ACL_GROUP_OBJ));
     70 	if (strcmp(str, "everyone@") == 0)
     71 		return (acl_set_tag_type(entry, ACL_EVERYONE));
     72 
     73 	*need_qualifier = 1;
     74 
     75 	if (strcmp(str, "user") == 0 || strcmp(str, "u") == 0)
     76 		return (acl_set_tag_type(entry, ACL_USER));
     77 	if (strcmp(str, "group") == 0 || strcmp(str, "g") == 0)
     78 		return (acl_set_tag_type(entry, ACL_GROUP));
     79 
     80 	warnx("malformed ACL: invalid \"tag\" field");
     81 
     82 	return (-1);
     83 }
     84 
     85 /*
     86  * Parse the qualifier field of ACL entry passed as "str".
     87  * If user or group name cannot be resolved, then the variable
     88  * referenced by "need_qualifier" is set to 1; it will be checked
     89  * later to figure out whether the appended_id is required.
     90  */
     91 static int
     92 parse_qualifier(char *str, acl_entry_t entry, int *need_qualifier)
     93 {
     94 	size_t qualifier_length;
     95 	int error;
     96 	uid_t id;
     97 	acl_tag_t tag;
     98 
     99 	assert(need_qualifier != NULL);
    100 	*need_qualifier = 0;
    101 
    102 	qualifier_length = strlen(str);
    103 
    104 	if (qualifier_length == 0) {
    105 		warnx("malformed ACL: empty \"qualifier\" field");
    106 		return (-1);
    107 	}
    108 
    109 	error = acl_get_tag_type(entry, &tag);
    110 	if (error)
    111 		return (error);
    112 
    113 	error = _acl_name_to_id(tag, str, &id);
    114 	if (error) {
    115 		*need_qualifier = 1;
    116 		return (0);
    117 	}
    118 
    119 	return (acl_set_qualifier(entry, &id));
    120 }
    121 
    122 static int
    123 parse_access_mask(char *str, acl_entry_t entry)
    124 {
    125 	int error;
    126 	acl_perm_t perm;
    127 
    128 	error = _nfs4_parse_access_mask(str, &perm);
    129 	if (error)
    130 		return (error);
    131 
    132 	error = acl_set_permset(entry, &perm);
    133 
    134 	return (error);
    135 }
    136 
    137 static int
    138 parse_flags(char *str, acl_entry_t entry)
    139 {
    140 	int error;
    141 	acl_flag_t flags;
    142 
    143 	error = _nfs4_parse_flags(str, &flags);
    144 	if (error)
    145 		return (error);
    146 
    147 	error = acl_set_flagset_np(entry, &flags);
    148 
    149 	return (error);
    150 }
    151 
    152 static int
    153 parse_entry_type(const char *str, acl_entry_t entry)
    154 {
    155 
    156 	if (strcmp(str, "allow") == 0)
    157 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALLOW));
    158 	if (strcmp(str, "deny") == 0)
    159 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_DENY));
    160 	if (strcmp(str, "audit") == 0)
    161 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_AUDIT));
    162 	if (strcmp(str, "alarm") == 0)
    163 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALARM));
    164 
    165 	warnx("malformed ACL: invalid \"type\" field");
    166 
    167 	return (-1);
    168 }
    169 
    170 static int
    171 parse_appended_id(char *str, acl_entry_t entry)
    172 {
    173 	size_t qualifier_length;
    174 	char *end;
    175 	id_t id;
    176 
    177 	qualifier_length = strlen(str);
    178 	if (qualifier_length == 0) {
    179 		warnx("malformed ACL: \"appended id\" field present, "
    180 	           "but empty");
    181 		return (-1);
    182 	}
    183 
    184 	id = strtod(str, &end);
    185 	if ((size_t)(end - str) != qualifier_length) {
    186 		warnx("malformed ACL: appended id is not a number");
    187 		return (-1);
    188 	}
    189 
    190 	return (acl_set_qualifier(entry, &id));
    191 }
    192 
    193 static int
    194 number_of_colons(const char *str)
    195 {
    196 	int count = 0;
    197 
    198 	while (*str != '\0') {
    199 		if (*str == ':')
    200 			count++;
    201 
    202 		str++;
    203 	}
    204 
    205 	return (count);
    206 }
    207 
    208 int
    209 _nfs4_acl_entry_from_text(acl_t aclp, char *str)
    210 {
    211 	int error, need_qualifier;
    212 	acl_entry_t entry;
    213 	char *field, *qualifier_field = NULL;
    214 
    215 	error = acl_create_entry(&aclp, &entry);
    216 	if (error)
    217 		return (error);
    218 
    219 	assert(_entry_brand(entry) == ACL_BRAND_NFS4);
    220 
    221 	if (str == NULL)
    222 		goto truncated_entry;
    223 	field = strsep(&str, ":");
    224 
    225 	field = string_skip_whitespace(field);
    226 	if ((*field == '\0') && (!str)) {
    227 		/*
    228 		 * Is an entirely comment line, skip to next
    229 		 * comma.
    230 		 */
    231 		return (0);
    232 	}
    233 
    234 	error = parse_tag(field, entry, &need_qualifier);
    235 	if (error)
    236 		goto malformed_field;
    237 
    238 	if (need_qualifier) {
    239 		if (str == NULL)
    240 			goto truncated_entry;
    241 		qualifier_field = field = strsep(&str, ":");
    242 		error = parse_qualifier(field, entry, &need_qualifier);
    243 		if (error)
    244 			goto malformed_field;
    245 	}
    246 
    247 	if (str == NULL)
    248 		goto truncated_entry;
    249 	field = strsep(&str, ":");
    250 	error = parse_access_mask(field, entry);
    251 	if (error)
    252 		goto malformed_field;
    253 
    254 	if (str == NULL)
    255 		goto truncated_entry;
    256 	/* Do we have "flags" field? */
    257 	if (number_of_colons(str) > 0) {
    258 		field = strsep(&str, ":");
    259 		error = parse_flags(field, entry);
    260 		if (error)
    261 			goto malformed_field;
    262 	}
    263 
    264 	if (str == NULL)
    265 		goto truncated_entry;
    266 	field = strsep(&str, ":");
    267 	error = parse_entry_type(field, entry);
    268 	if (error)
    269 		goto malformed_field;
    270 
    271 	if (need_qualifier) {
    272 		if (str == NULL) {
    273 			warnx("malformed ACL: unknown user or group name "
    274 			    "\"%s\"", qualifier_field);
    275 			goto truncated_entry;
    276 		}
    277 
    278 		error = parse_appended_id(str, entry);
    279 		if (error)
    280 			goto malformed_field;
    281 	}
    282 
    283 	return (0);
    284 
    285 truncated_entry:
    286 malformed_field:
    287 	acl_delete_entry(aclp, entry);
    288 	errno = EINVAL;
    289 	return (-1);
    290 }
    291