1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@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.1 2020/05/16 18:31:47 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 int qualifier_length, error; 95 uid_t id; 96 acl_tag_t tag; 97 98 assert(need_qualifier != NULL); 99 *need_qualifier = 0; 100 101 qualifier_length = strlen(str); 102 103 if (qualifier_length == 0) { 104 warnx("malformed ACL: empty \"qualifier\" field"); 105 return (-1); 106 } 107 108 error = acl_get_tag_type(entry, &tag); 109 if (error) 110 return (error); 111 112 error = _acl_name_to_id(tag, str, &id); 113 if (error) { 114 *need_qualifier = 1; 115 return (0); 116 } 117 118 return (acl_set_qualifier(entry, &id)); 119 } 120 121 static int 122 parse_access_mask(char *str, acl_entry_t entry) 123 { 124 int error; 125 acl_perm_t perm; 126 127 error = _nfs4_parse_access_mask(str, &perm); 128 if (error) 129 return (error); 130 131 error = acl_set_permset(entry, &perm); 132 133 return (error); 134 } 135 136 static int 137 parse_flags(char *str, acl_entry_t entry) 138 { 139 int error; 140 acl_flag_t flags; 141 142 error = _nfs4_parse_flags(str, &flags); 143 if (error) 144 return (error); 145 146 error = acl_set_flagset_np(entry, &flags); 147 148 return (error); 149 } 150 151 static int 152 parse_entry_type(const char *str, acl_entry_t entry) 153 { 154 155 if (strcmp(str, "allow") == 0) 156 return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALLOW)); 157 if (strcmp(str, "deny") == 0) 158 return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_DENY)); 159 if (strcmp(str, "audit") == 0) 160 return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_AUDIT)); 161 if (strcmp(str, "alarm") == 0) 162 return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALARM)); 163 164 warnx("malformed ACL: invalid \"type\" field"); 165 166 return (-1); 167 } 168 169 static int 170 parse_appended_id(char *str, acl_entry_t entry) 171 { 172 int qualifier_length; 173 char *end; 174 id_t id; 175 176 qualifier_length = strlen(str); 177 if (qualifier_length == 0) { 178 warnx("malformed ACL: \"appended id\" field present, " 179 "but empty"); 180 return (-1); 181 } 182 183 id = strtod(str, &end); 184 if (end - str != qualifier_length) { 185 warnx("malformed ACL: appended id is not a number"); 186 return (-1); 187 } 188 189 return (acl_set_qualifier(entry, &id)); 190 } 191 192 static int 193 number_of_colons(const char *str) 194 { 195 int count = 0; 196 197 while (*str != '\0') { 198 if (*str == ':') 199 count++; 200 201 str++; 202 } 203 204 return (count); 205 } 206 207 int 208 _nfs4_acl_entry_from_text(acl_t aclp, char *str) 209 { 210 int error, need_qualifier; 211 acl_entry_t entry; 212 char *field, *qualifier_field = NULL; 213 214 error = acl_create_entry(&aclp, &entry); 215 if (error) 216 return (error); 217 218 assert(_entry_brand(entry) == ACL_BRAND_NFS4); 219 220 if (str == NULL) 221 goto truncated_entry; 222 field = strsep(&str, ":"); 223 224 field = string_skip_whitespace(field); 225 if ((*field == '\0') && (!str)) { 226 /* 227 * Is an entirely comment line, skip to next 228 * comma. 229 */ 230 return (0); 231 } 232 233 error = parse_tag(field, entry, &need_qualifier); 234 if (error) 235 goto malformed_field; 236 237 if (need_qualifier) { 238 if (str == NULL) 239 goto truncated_entry; 240 qualifier_field = field = strsep(&str, ":"); 241 error = parse_qualifier(field, entry, &need_qualifier); 242 if (error) 243 goto malformed_field; 244 } 245 246 if (str == NULL) 247 goto truncated_entry; 248 field = strsep(&str, ":"); 249 error = parse_access_mask(field, entry); 250 if (error) 251 goto malformed_field; 252 253 if (str == NULL) 254 goto truncated_entry; 255 /* Do we have "flags" field? */ 256 if (number_of_colons(str) > 0) { 257 field = strsep(&str, ":"); 258 error = parse_flags(field, entry); 259 if (error) 260 goto malformed_field; 261 } 262 263 if (str == NULL) 264 goto truncated_entry; 265 field = strsep(&str, ":"); 266 error = parse_entry_type(field, entry); 267 if (error) 268 goto malformed_field; 269 270 if (need_qualifier) { 271 if (str == NULL) { 272 warnx("malformed ACL: unknown user or group name " 273 "\"%s\"", qualifier_field); 274 goto truncated_entry; 275 } 276 277 error = parse_appended_id(str, entry); 278 if (error) 279 goto malformed_field; 280 } 281 282 return (0); 283 284 truncated_entry: 285 malformed_field: 286 acl_delete_entry(aclp, entry); 287 errno = EINVAL; 288 return (-1); 289 } 290