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