1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson 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 * acl_from_text: Convert a text-form ACL from a string to an acl_t. 30 */ 31 32 #include <sys/cdefs.h> 33 #include <sys/types.h> 34 #include "namespace.h" 35 #include <sys/acl.h> 36 #include "un-namespace.h" 37 #include <sys/errno.h> 38 #include <grp.h> 39 #include <pwd.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <assert.h> 44 45 #include "acl_support.h" 46 47 static acl_tag_t acl_string_to_tag(char *tag, char *qualifier); 48 49 int _nfs4_acl_entry_from_text(acl_t aclp, char *entry); 50 int _text_could_be_nfs4_acl(const char *entry); 51 52 static acl_tag_t 53 acl_string_to_tag(char *tag, char *qualifier) 54 { 55 56 if (*qualifier == '\0') { 57 if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) { 58 return (ACL_USER_OBJ); 59 } else 60 if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) { 61 return (ACL_GROUP_OBJ); 62 } else 63 if ((!strcmp(tag, "mask")) || (!strcmp(tag, "m"))) { 64 return (ACL_MASK); 65 } else 66 if ((!strcmp(tag, "other")) || (!strcmp(tag, "o"))) { 67 return (ACL_OTHER); 68 } else 69 return(-1); 70 } else { 71 if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) { 72 return(ACL_USER); 73 } else 74 if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) { 75 return(ACL_GROUP); 76 } else 77 return(-1); 78 } 79 } 80 81 static int 82 _posix1e_acl_entry_from_text(acl_t aclp, char *entry) 83 { 84 acl_tag_t t; 85 acl_perm_t p; 86 char *tag, *qualifier, *permission; 87 uid_t id; 88 int error; 89 90 assert(_acl_brand(aclp) == ACL_BRAND_POSIX); 91 92 /* Split into three ':' delimited fields. */ 93 tag = strsep(&entry, ":"); 94 if (tag == NULL) { 95 errno = EINVAL; 96 return (-1); 97 } 98 tag = string_skip_whitespace(tag); 99 if ((*tag == '\0') && (!entry)) { 100 /* 101 * Is an entirely comment line, skip to next 102 * comma. 103 */ 104 return (0); 105 } 106 string_trim_trailing_whitespace(tag); 107 108 qualifier = strsep(&entry, ":"); 109 if (qualifier == NULL) { 110 errno = EINVAL; 111 return (-1); 112 } 113 qualifier = string_skip_whitespace(qualifier); 114 string_trim_trailing_whitespace(qualifier); 115 116 permission = strsep(&entry, ":"); 117 if (permission == NULL || entry) { 118 errno = EINVAL; 119 return (-1); 120 } 121 permission = string_skip_whitespace(permission); 122 string_trim_trailing_whitespace(permission); 123 124 t = acl_string_to_tag(tag, qualifier); 125 if (t == -1) { 126 errno = EINVAL; 127 return (-1); 128 } 129 130 error = _posix1e_acl_string_to_perm(permission, &p); 131 if (error == -1) { 132 errno = EINVAL; 133 return (-1); 134 } 135 136 switch(t) { 137 case ACL_USER_OBJ: 138 case ACL_GROUP_OBJ: 139 case ACL_MASK: 140 case ACL_OTHER: 141 if (*qualifier != '\0') { 142 errno = EINVAL; 143 return (-1); 144 } 145 id = 0; 146 break; 147 148 case ACL_USER: 149 case ACL_GROUP: 150 error = _acl_name_to_id(t, qualifier, &id); 151 if (error == -1) 152 return (-1); 153 break; 154 155 default: 156 errno = EINVAL; 157 return (-1); 158 } 159 160 error = _posix1e_acl_add_entry(aclp, t, id, p); 161 if (error == -1) 162 return (-1); 163 164 return (0); 165 } 166 167 static int 168 _text_is_nfs4_entry(const char *entry) 169 { 170 int count = 0; 171 172 assert(strlen(entry) > 0); 173 174 while (*entry != '\0') { 175 if (*entry == ':' || *entry == '@') 176 count++; 177 entry++; 178 } 179 180 if (count <= 2) 181 return (0); 182 183 return (1); 184 } 185 186 /* 187 * acl_from_text -- Convert a string into an ACL. 188 * Postpone most validity checking until the end and call acl_valid() to do 189 * that. 190 */ 191 acl_t 192 acl_from_text(const char *buf_p) 193 { 194 acl_t acl; 195 char *mybuf_p, *line, *cur, *notcomment, *comment, *entry; 196 int error; 197 198 /* Local copy we can mess up. */ 199 mybuf_p = strdup(buf_p); 200 if (mybuf_p == NULL) 201 return(NULL); 202 203 acl = acl_init(3); /* XXX: WTF, 3? */ 204 if (acl == NULL) { 205 free(mybuf_p); 206 return(NULL); 207 } 208 209 /* Outer loop: delimit at \n boundaries. */ 210 cur = mybuf_p; 211 while ((line = strsep(&cur, "\n"))) { 212 /* Now split the line on the first # to strip out comments. */ 213 comment = line; 214 notcomment = strsep(&comment, "#"); 215 216 /* Inner loop: delimit at ',' boundaries. */ 217 while ((entry = strsep(¬comment, ","))) { 218 219 /* Skip empty lines. */ 220 if (strlen(string_skip_whitespace(entry)) == 0) 221 continue; 222 223 if (_acl_brand(acl) == ACL_BRAND_UNKNOWN) { 224 if (_text_is_nfs4_entry(entry)) 225 _acl_brand_as(acl, ACL_BRAND_NFS4); 226 else 227 _acl_brand_as(acl, ACL_BRAND_POSIX); 228 } 229 230 switch (_acl_brand(acl)) { 231 case ACL_BRAND_NFS4: 232 error = _nfs4_acl_entry_from_text(acl, entry); 233 break; 234 235 case ACL_BRAND_POSIX: 236 error = _posix1e_acl_entry_from_text(acl, entry); 237 break; 238 239 default: 240 error = EINVAL; 241 break; 242 } 243 244 if (error) 245 goto error_label; 246 } 247 } 248 249 #if 0 250 /* XXX Should we only return ACLs valid according to acl_valid? */ 251 /* Verify validity of the ACL we read in. */ 252 if (acl_valid(acl) == -1) { 253 errno = EINVAL; 254 goto error_label; 255 } 256 #endif 257 258 free(mybuf_p); 259 return(acl); 260 261 error_label: 262 acl_free(acl); 263 free(mybuf_p); 264 return(NULL); 265 } 266 267 /* 268 * Given a username/groupname from a text form of an ACL, return the uid/gid 269 * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM 270 * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE 271 * MAY HAVE SIDE-EFFECTS 272 */ 273 int 274 _acl_name_to_id(acl_tag_t tag, char *name, uid_t *id) 275 { 276 struct group *g; 277 struct passwd *p; 278 unsigned long l; 279 char *endp; 280 281 switch(tag) { 282 case ACL_USER: 283 p = getpwnam(name); 284 if (p == NULL) { 285 l = strtoul(name, &endp, 0); 286 if (*endp != '\0' || l != (unsigned long)(uid_t)l) { 287 errno = EINVAL; 288 return (-1); 289 } 290 *id = (uid_t)l; 291 return (0); 292 } 293 *id = p->pw_uid; 294 return (0); 295 296 case ACL_GROUP: 297 g = getgrnam(name); 298 if (g == NULL) { 299 l = strtoul(name, &endp, 0); 300 if (*endp != '\0' || l != (unsigned long)(gid_t)l) { 301 errno = EINVAL; 302 return (-1); 303 } 304 *id = (gid_t)l; 305 return (0); 306 } 307 *id = g->gr_gid; 308 return (0); 309 310 default: 311 return (EINVAL); 312 } 313 } 314