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