1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <unistd.h> 31 #include <errno.h> 32 #include <assert.h> 33 #include <string.h> 34 #include <pwd.h> 35 #include <grp.h> 36 #include <ctype.h> 37 #include <err.h> 38 #include <sys/syscall.h> 39 #include <sys/types.h> 40 #include <sys/acl.h> 41 42 #include "acl_support.h" 43 44 #define MAX_ENTRY_LENGTH 512 45 46 /* 47 * Parse the tag field of ACL entry passed as "str". If qualifier 48 * needs to follow, then the variable referenced by "need_qualifier" 49 * is set to 1, otherwise it's set to 0. 50 */ 51 static int 52 parse_tag(const char *str, acl_entry_t entry, int *need_qualifier) 53 { 54 55 assert(need_qualifier != NULL); 56 *need_qualifier = 0; 57 58 if (strcmp(str, "owner@") == 0) 59 return (acl_set_tag_type(entry, ACL_USER_OBJ)); 60 if (strcmp(str, "group@") == 0) 61 return (acl_set_tag_type(entry, ACL_GROUP_OBJ)); 62 if (strcmp(str, "everyone@") == 0) 63 return (acl_set_tag_type(entry, ACL_EVERYONE)); 64 65 *need_qualifier = 1; 66 67 if (strcmp(str, "user") == 0 || strcmp(str, "u") == 0) 68 return (acl_set_tag_type(entry, ACL_USER)); 69 if (strcmp(str, "group") == 0 || strcmp(str, "g") == 0) 70 return (acl_set_tag_type(entry, ACL_GROUP)); 71 72 warnx("malformed ACL: invalid \"tag\" field"); 73 74 return (-1); 75 } 76 77 /* 78 * Parse the qualifier field of ACL entry passed as "str". 79 * If user or group name cannot be resolved, then the variable 80 * referenced by "need_qualifier" is set to 1; it will be checked 81 * later to figure out whether the appended_id is required. 82 */ 83 static int 84 parse_qualifier(char *str, acl_entry_t entry, int *need_qualifier) 85 { 86 int qualifier_length, error; 87 uid_t id; 88 acl_tag_t tag; 89 90 assert(need_qualifier != NULL); 91 *need_qualifier = 0; 92 93 qualifier_length = strlen(str); 94 95 if (qualifier_length == 0) { 96 warnx("malformed ACL: empty \"qualifier\" field"); 97 return (-1); 98 } 99 100 error = acl_get_tag_type(entry, &tag); 101 if (error) 102 return (error); 103 104 error = _acl_name_to_id(tag, str, &id); 105 if (error) { 106 *need_qualifier = 1; 107 return (0); 108 } 109 110 return (acl_set_qualifier(entry, &id)); 111 } 112 113 static int 114 parse_access_mask(char *str, acl_entry_t entry) 115 { 116 int error; 117 acl_perm_t perm; 118 119 error = _nfs4_parse_access_mask(str, &perm); 120 if (error) 121 return (error); 122 123 error = acl_set_permset(entry, &perm); 124 125 return (error); 126 } 127 128 static int 129 parse_flags(char *str, acl_entry_t entry) 130 { 131 int error; 132 acl_flag_t flags; 133 134 error = _nfs4_parse_flags(str, &flags); 135 if (error) 136 return (error); 137 138 error = acl_set_flagset_np(entry, &flags); 139 140 return (error); 141 } 142 143 static int 144 parse_entry_type(const char *str, acl_entry_t entry) 145 { 146 147 if (strcmp(str, "allow") == 0) 148 return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALLOW)); 149 if (strcmp(str, "deny") == 0) 150 return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_DENY)); 151 if (strcmp(str, "audit") == 0) 152 return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_AUDIT)); 153 if (strcmp(str, "alarm") == 0) 154 return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALARM)); 155 156 warnx("malformed ACL: invalid \"type\" field"); 157 158 return (-1); 159 } 160 161 static int 162 parse_appended_id(char *str, acl_entry_t entry) 163 { 164 int qualifier_length; 165 char *end; 166 id_t id; 167 168 qualifier_length = strlen(str); 169 if (qualifier_length == 0) { 170 warnx("malformed ACL: \"appended id\" field present, " 171 "but empty"); 172 return (-1); 173 } 174 175 id = strtod(str, &end); 176 if (end - str != qualifier_length) { 177 warnx("malformed ACL: appended id is not a number"); 178 return (-1); 179 } 180 181 return (acl_set_qualifier(entry, &id)); 182 } 183 184 static int 185 number_of_colons(const char *str) 186 { 187 int count = 0; 188 189 while (*str != '\0') { 190 if (*str == ':') 191 count++; 192 193 str++; 194 } 195 196 return (count); 197 } 198 199 int 200 _nfs4_acl_entry_from_text(acl_t aclp, char *str) 201 { 202 int error, need_qualifier; 203 acl_entry_t entry; 204 char *field, *qualifier_field; 205 206 error = acl_create_entry(&aclp, &entry); 207 if (error) 208 return (error); 209 210 assert(_entry_brand(entry) == ACL_BRAND_NFS4); 211 212 if (str == NULL) 213 goto truncated_entry; 214 field = strsep(&str, ":"); 215 216 field = string_skip_whitespace(field); 217 if ((*field == '\0') && (!str)) { 218 /* 219 * Is an entirely comment line, skip to next 220 * comma. 221 */ 222 return (0); 223 } 224 225 error = parse_tag(field, entry, &need_qualifier); 226 if (error) 227 goto malformed_field; 228 229 if (need_qualifier) { 230 if (str == NULL) 231 goto truncated_entry; 232 qualifier_field = field = strsep(&str, ":"); 233 error = parse_qualifier(field, entry, &need_qualifier); 234 if (error) 235 goto malformed_field; 236 } 237 238 if (str == NULL) 239 goto truncated_entry; 240 field = strsep(&str, ":"); 241 error = parse_access_mask(field, entry); 242 if (error) 243 goto malformed_field; 244 245 if (str == NULL) 246 goto truncated_entry; 247 /* Do we have "flags" field? */ 248 if (number_of_colons(str) > 0) { 249 field = strsep(&str, ":"); 250 error = parse_flags(field, entry); 251 if (error) 252 goto malformed_field; 253 } 254 255 if (str == NULL) 256 goto truncated_entry; 257 field = strsep(&str, ":"); 258 error = parse_entry_type(field, entry); 259 if (error) 260 goto malformed_field; 261 262 if (need_qualifier) { 263 if (str == NULL) { 264 warnx("malformed ACL: unknown user or group name " 265 "\"%s\"", qualifier_field); 266 goto truncated_entry; 267 } 268 269 error = parse_appended_id(str, entry); 270 if (error) 271 goto malformed_field; 272 } 273 274 return (0); 275 276 truncated_entry: 277 malformed_field: 278 acl_delete_entry(aclp, entry); 279 errno = EINVAL; 280 return (-1); 281 } 282