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