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