1515d7c92SRobert Watson /*- 22de14c39SRobert Watson * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson 3515d7c92SRobert Watson * All rights reserved. 4515d7c92SRobert Watson * 5515d7c92SRobert Watson * Redistribution and use in source and binary forms, with or without 6515d7c92SRobert Watson * modification, are permitted provided that the following conditions 7515d7c92SRobert Watson * are met: 8515d7c92SRobert Watson * 1. Redistributions of source code must retain the above copyright 9515d7c92SRobert Watson * notice, this list of conditions and the following disclaimer. 10515d7c92SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright 11515d7c92SRobert Watson * notice, this list of conditions and the following disclaimer in the 12515d7c92SRobert Watson * documentation and/or other materials provided with the distribution. 13515d7c92SRobert Watson * 14515d7c92SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15515d7c92SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16515d7c92SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17515d7c92SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18515d7c92SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19515d7c92SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20515d7c92SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21515d7c92SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22515d7c92SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23515d7c92SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24515d7c92SRobert Watson * SUCH DAMAGE. 25515d7c92SRobert Watson */ 26515d7c92SRobert Watson /* 27374c6c0fSRobert Watson * acl_from_text: Convert a text-form ACL from a string to an acl_t. 28515d7c92SRobert Watson */ 29515d7c92SRobert Watson 30333fc21eSDavid E. O'Brien #include <sys/cdefs.h> 31333fc21eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 32333fc21eSDavid E. O'Brien 33515d7c92SRobert Watson #include <sys/types.h> 347bd44e92SThomas Moestl #include "namespace.h" 35515d7c92SRobert Watson #include <sys/acl.h> 367bd44e92SThomas Moestl #include "un-namespace.h" 37515d7c92SRobert Watson #include <sys/errno.h> 3848135111STim Kientzle #include <grp.h> 3948135111STim Kientzle #include <pwd.h> 40515d7c92SRobert Watson #include <stdio.h> 41515d7c92SRobert Watson #include <stdlib.h> 42515d7c92SRobert Watson #include <string.h> 43515d7c92SRobert Watson 44515d7c92SRobert Watson #include "acl_support.h" 45515d7c92SRobert Watson 4648135111STim Kientzle static int _posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id); 47c61eb011SChris D. Faulhaber static acl_tag_t acl_string_to_tag(char *tag, char *qualifier); 48c61eb011SChris D. Faulhaber static char *string_skip_whitespace(char *string); 49c61eb011SChris D. Faulhaber static void string_trim_trailing_whitespace(char *string); 50c61eb011SChris D. Faulhaber 51515d7c92SRobert Watson static char * 52515d7c92SRobert Watson string_skip_whitespace(char *string) 53515d7c92SRobert Watson { 54515d7c92SRobert Watson 55515d7c92SRobert Watson while (*string && ((*string == ' ') || (*string == '\t'))) { 56515d7c92SRobert Watson string++; 57515d7c92SRobert Watson } 58515d7c92SRobert Watson return (string); 59515d7c92SRobert Watson } 60515d7c92SRobert Watson 61515d7c92SRobert Watson static void 62515d7c92SRobert Watson string_trim_trailing_whitespace(char *string) 63515d7c92SRobert Watson { 64515d7c92SRobert Watson char *end; 65515d7c92SRobert Watson 66515d7c92SRobert Watson if (*string == '\0') 67515d7c92SRobert Watson return; 68515d7c92SRobert Watson 69515d7c92SRobert Watson end = string + strlen(string) - 1; 70515d7c92SRobert Watson 71515d7c92SRobert Watson while (end != string) { 72515d7c92SRobert Watson if ((*end == ' ') || (*end == '\t')) { 73515d7c92SRobert Watson *end = '\0'; 74515d7c92SRobert Watson end--; 75515d7c92SRobert Watson } else { 76515d7c92SRobert Watson return; 77515d7c92SRobert Watson } 78515d7c92SRobert Watson } 79515d7c92SRobert Watson 80515d7c92SRobert Watson return; 81515d7c92SRobert Watson } 82515d7c92SRobert Watson 83c61eb011SChris D. Faulhaber static acl_tag_t 84515d7c92SRobert Watson acl_string_to_tag(char *tag, char *qualifier) 85515d7c92SRobert Watson { 86515d7c92SRobert Watson 87515d7c92SRobert Watson if (*qualifier == '\0') { 88515d7c92SRobert Watson if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) { 89515d7c92SRobert Watson return (ACL_USER_OBJ); 90515d7c92SRobert Watson } else 91515d7c92SRobert Watson if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) { 92515d7c92SRobert Watson return (ACL_GROUP_OBJ); 93515d7c92SRobert Watson } else 94515d7c92SRobert Watson if ((!strcmp(tag, "mask")) || (!strcmp(tag, "m"))) { 95515d7c92SRobert Watson return (ACL_MASK); 96515d7c92SRobert Watson } else 97515d7c92SRobert Watson if ((!strcmp(tag, "other")) || (!strcmp(tag, "o"))) { 98515d7c92SRobert Watson return (ACL_OTHER); 99515d7c92SRobert Watson } else 100515d7c92SRobert Watson return(-1); 101515d7c92SRobert Watson } else { 102515d7c92SRobert Watson if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) { 103515d7c92SRobert Watson return(ACL_USER); 104515d7c92SRobert Watson } else 105515d7c92SRobert Watson if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) { 106515d7c92SRobert Watson return(ACL_GROUP); 107515d7c92SRobert Watson } else 108515d7c92SRobert Watson return(-1); 109515d7c92SRobert Watson } 110515d7c92SRobert Watson } 111515d7c92SRobert Watson 112515d7c92SRobert Watson /* 113374c6c0fSRobert Watson * acl_from_text -- Convert a string into an ACL. 114374c6c0fSRobert Watson * Postpone most validity checking until the end and call acl_valid() to do 115515d7c92SRobert Watson * that. 116515d7c92SRobert Watson */ 117515d7c92SRobert Watson acl_t 118515d7c92SRobert Watson acl_from_text(const char *buf_p) 119515d7c92SRobert Watson { 120515d7c92SRobert Watson acl_tag_t t; 121515d7c92SRobert Watson acl_perm_t p; 122515d7c92SRobert Watson acl_t acl; 123515d7c92SRobert Watson char *mybuf_p, *line, *cur, *notcomment, *comment, *entry; 124515d7c92SRobert Watson char *tag, *qualifier, *permission; 125515d7c92SRobert Watson int error; 1260f626307SChris D. Faulhaber uid_t id; 127515d7c92SRobert Watson 128374c6c0fSRobert Watson /* Local copy we can mess up. */ 129515d7c92SRobert Watson mybuf_p = strdup(buf_p); 1309fd46b02SChris D. Faulhaber if (mybuf_p == NULL) 131f0078215SRobert Watson return(NULL); 132515d7c92SRobert Watson 133515d7c92SRobert Watson acl = acl_init(3); 1349fd46b02SChris D. Faulhaber if (acl == NULL) { 135515d7c92SRobert Watson free(mybuf_p); 136f0078215SRobert Watson return(NULL); 137515d7c92SRobert Watson } 138515d7c92SRobert Watson 139374c6c0fSRobert Watson /* Outer loop: delimit at \n boundaries. */ 140515d7c92SRobert Watson cur = mybuf_p; 141515d7c92SRobert Watson while ((line = strsep(&cur, "\n"))) { 142374c6c0fSRobert Watson /* Now split the line on the first # to strip out comments. */ 143515d7c92SRobert Watson comment = line; 144515d7c92SRobert Watson notcomment = strsep(&comment, "#"); 145515d7c92SRobert Watson 146374c6c0fSRobert Watson /* Inner loop: delimit at ',' boundaries. */ 147515d7c92SRobert Watson while ((entry = strsep(¬comment, ","))) { 148374c6c0fSRobert Watson /* Now split into three ':' delimited fields. */ 149515d7c92SRobert Watson tag = strsep(&entry, ":"); 1509fd46b02SChris D. Faulhaber if (tag == NULL) { 151515d7c92SRobert Watson errno = EINVAL; 152515d7c92SRobert Watson goto error_label; 153515d7c92SRobert Watson } 154515d7c92SRobert Watson tag = string_skip_whitespace(tag); 155515d7c92SRobert Watson if ((*tag == '\0') && (!entry)) { 156515d7c92SRobert Watson /* 157374c6c0fSRobert Watson * Is an entirely comment line, skip to next 158374c6c0fSRobert Watson * comma. 159515d7c92SRobert Watson */ 160515d7c92SRobert Watson continue; 161515d7c92SRobert Watson } 162515d7c92SRobert Watson string_trim_trailing_whitespace(tag); 163515d7c92SRobert Watson 164515d7c92SRobert Watson qualifier = strsep(&entry, ":"); 1659fd46b02SChris D. Faulhaber if (qualifier == NULL) { 166515d7c92SRobert Watson errno = EINVAL; 167515d7c92SRobert Watson goto error_label; 168515d7c92SRobert Watson } 169515d7c92SRobert Watson qualifier = string_skip_whitespace(qualifier); 170515d7c92SRobert Watson string_trim_trailing_whitespace(qualifier); 171515d7c92SRobert Watson 172515d7c92SRobert Watson permission = strsep(&entry, ":"); 1739fd46b02SChris D. Faulhaber if (permission == NULL || entry) { 174515d7c92SRobert Watson errno = EINVAL; 175515d7c92SRobert Watson goto error_label; 176515d7c92SRobert Watson } 177515d7c92SRobert Watson permission = string_skip_whitespace(permission); 178515d7c92SRobert Watson string_trim_trailing_whitespace(permission); 179515d7c92SRobert Watson 180515d7c92SRobert Watson t = acl_string_to_tag(tag, qualifier); 181515d7c92SRobert Watson if (t == -1) { 182515d7c92SRobert Watson errno = EINVAL; 183515d7c92SRobert Watson goto error_label; 184515d7c92SRobert Watson } 185515d7c92SRobert Watson 1862de14c39SRobert Watson error = _posix1e_acl_string_to_perm(permission, &p); 187515d7c92SRobert Watson if (error == -1) { 188515d7c92SRobert Watson errno = EINVAL; 189515d7c92SRobert Watson goto error_label; 190515d7c92SRobert Watson } 191515d7c92SRobert Watson 192515d7c92SRobert Watson switch(t) { 193515d7c92SRobert Watson case ACL_USER_OBJ: 194515d7c92SRobert Watson case ACL_GROUP_OBJ: 195515d7c92SRobert Watson case ACL_MASK: 196515d7c92SRobert Watson case ACL_OTHER: 197515d7c92SRobert Watson if (*qualifier != '\0') { 198515d7c92SRobert Watson errno = EINVAL; 199515d7c92SRobert Watson goto error_label; 200515d7c92SRobert Watson } 201515d7c92SRobert Watson id = 0; 202515d7c92SRobert Watson break; 203515d7c92SRobert Watson 204515d7c92SRobert Watson case ACL_USER: 205515d7c92SRobert Watson case ACL_GROUP: 2062de14c39SRobert Watson error = _posix1e_acl_name_to_id(t, qualifier, 2072de14c39SRobert Watson &id); 208515d7c92SRobert Watson if (error == -1) 209515d7c92SRobert Watson goto error_label; 210515d7c92SRobert Watson break; 211515d7c92SRobert Watson 212515d7c92SRobert Watson default: 213515d7c92SRobert Watson errno = EINVAL; 214515d7c92SRobert Watson goto error_label; 215515d7c92SRobert Watson } 216515d7c92SRobert Watson 2172de14c39SRobert Watson error = _posix1e_acl_add_entry(acl, t, id, p); 218515d7c92SRobert Watson if (error == -1) 219515d7c92SRobert Watson goto error_label; 220515d7c92SRobert Watson } 221515d7c92SRobert Watson } 222515d7c92SRobert Watson 223515d7c92SRobert Watson #if 0 224374c6c0fSRobert Watson /* XXX Should we only return ACLs valid according to acl_valid? */ 225374c6c0fSRobert Watson /* Verify validity of the ACL we read in. */ 226515d7c92SRobert Watson if (acl_valid(acl) == -1) { 227515d7c92SRobert Watson errno = EINVAL; 228515d7c92SRobert Watson goto error_label; 229515d7c92SRobert Watson } 230515d7c92SRobert Watson #endif 231515d7c92SRobert Watson 232515d7c92SRobert Watson return(acl); 233515d7c92SRobert Watson 234515d7c92SRobert Watson error_label: 235515d7c92SRobert Watson acl_free(acl); 236515d7c92SRobert Watson free(mybuf_p); 237f0078215SRobert Watson return(NULL); 238515d7c92SRobert Watson } 239515d7c92SRobert Watson 24048135111STim Kientzle /* 24148135111STim Kientzle * Given a username/groupname from a text form of an ACL, return the uid/gid 24248135111STim Kientzle * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM 24348135111STim Kientzle * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE 24448135111STim Kientzle * MAY HAVE SIDE-EFFECTS 24548135111STim Kientzle * 24648135111STim Kientzle * XXX currently doesn't deal correctly with a numeric uid being passed 24748135111STim Kientzle * instead of a username. What is correct behavior here? Check chown. 24848135111STim Kientzle */ 24948135111STim Kientzle static int 25048135111STim Kientzle _posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id) 25148135111STim Kientzle { 25248135111STim Kientzle struct group *g; 25348135111STim Kientzle struct passwd *p; 25448135111STim Kientzle unsigned long l; 25548135111STim Kientzle char *endp; 256515d7c92SRobert Watson 25748135111STim Kientzle switch(tag) { 25848135111STim Kientzle case ACL_USER: 25948135111STim Kientzle p = getpwnam(name); 26048135111STim Kientzle if (p == NULL) { 26148135111STim Kientzle l = strtoul(name, &endp, 0); 26248135111STim Kientzle if (*endp != '\0' || l != (unsigned long)(uid_t)l) { 26348135111STim Kientzle errno = EINVAL; 26448135111STim Kientzle return (-1); 26548135111STim Kientzle } 26648135111STim Kientzle *id = (uid_t)l; 26748135111STim Kientzle return (0); 26848135111STim Kientzle } 26948135111STim Kientzle *id = p->pw_uid; 27048135111STim Kientzle return (0); 271515d7c92SRobert Watson 27248135111STim Kientzle case ACL_GROUP: 27348135111STim Kientzle g = getgrnam(name); 27448135111STim Kientzle if (g == NULL) { 27548135111STim Kientzle l = strtoul(name, &endp, 0); 27648135111STim Kientzle if (*endp != '\0' || l != (unsigned long)(gid_t)l) { 27748135111STim Kientzle errno = EINVAL; 27848135111STim Kientzle return (-1); 27948135111STim Kientzle } 28048135111STim Kientzle *id = (gid_t)l; 28148135111STim Kientzle return (0); 28248135111STim Kientzle } 28348135111STim Kientzle *id = g->gr_gid; 28448135111STim Kientzle return (0); 28548135111STim Kientzle 28648135111STim Kientzle default: 28748135111STim Kientzle return (EINVAL); 28848135111STim Kientzle } 28948135111STim Kientzle } 290