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