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> 43aa015c8eSEdward Tomasz Napierala #include <assert.h> 44515d7c92SRobert Watson 45515d7c92SRobert Watson #include "acl_support.h" 46515d7c92SRobert Watson 4748135111STim Kientzle static int _posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id); 48c61eb011SChris D. Faulhaber static acl_tag_t acl_string_to_tag(char *tag, char *qualifier); 49c61eb011SChris D. Faulhaber 50aa015c8eSEdward Tomasz Napierala int _nfs4_acl_entry_from_text(acl_t aclp, char *entry); 51aa015c8eSEdward Tomasz Napierala int _text_could_be_nfs4_acl(const char *entry); 52515d7c92SRobert Watson 53c61eb011SChris D. Faulhaber static acl_tag_t 54515d7c92SRobert Watson acl_string_to_tag(char *tag, char *qualifier) 55515d7c92SRobert Watson { 56515d7c92SRobert Watson 57515d7c92SRobert Watson if (*qualifier == '\0') { 58515d7c92SRobert Watson if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) { 59515d7c92SRobert Watson return (ACL_USER_OBJ); 60515d7c92SRobert Watson } else 61515d7c92SRobert Watson if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) { 62515d7c92SRobert Watson return (ACL_GROUP_OBJ); 63515d7c92SRobert Watson } else 64515d7c92SRobert Watson if ((!strcmp(tag, "mask")) || (!strcmp(tag, "m"))) { 65515d7c92SRobert Watson return (ACL_MASK); 66515d7c92SRobert Watson } else 67515d7c92SRobert Watson if ((!strcmp(tag, "other")) || (!strcmp(tag, "o"))) { 68515d7c92SRobert Watson return (ACL_OTHER); 69515d7c92SRobert Watson } else 70515d7c92SRobert Watson return(-1); 71515d7c92SRobert Watson } else { 72515d7c92SRobert Watson if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) { 73515d7c92SRobert Watson return(ACL_USER); 74515d7c92SRobert Watson } else 75515d7c92SRobert Watson if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) { 76515d7c92SRobert Watson return(ACL_GROUP); 77515d7c92SRobert Watson } else 78515d7c92SRobert Watson return(-1); 79515d7c92SRobert Watson } 80515d7c92SRobert Watson } 81515d7c92SRobert Watson 82aa015c8eSEdward Tomasz Napierala static int 83aa015c8eSEdward Tomasz Napierala _posix1e_acl_entry_from_text(acl_t aclp, char *entry) 84aa015c8eSEdward Tomasz Napierala { 85aa015c8eSEdward Tomasz Napierala acl_tag_t t; 86aa015c8eSEdward Tomasz Napierala acl_perm_t p; 87aa015c8eSEdward Tomasz Napierala char *tag, *qualifier, *permission; 88aa015c8eSEdward Tomasz Napierala uid_t id; 89aa015c8eSEdward Tomasz Napierala int error; 90aa015c8eSEdward Tomasz Napierala 91aa015c8eSEdward Tomasz Napierala assert(_acl_brand(aclp) == ACL_BRAND_POSIX); 92aa015c8eSEdward Tomasz Napierala 93aa015c8eSEdward Tomasz Napierala /* Split into three ':' delimited fields. */ 94aa015c8eSEdward Tomasz Napierala tag = strsep(&entry, ":"); 95aa015c8eSEdward Tomasz Napierala if (tag == NULL) { 96aa015c8eSEdward Tomasz Napierala errno = EINVAL; 97aa015c8eSEdward Tomasz Napierala return (-1); 98aa015c8eSEdward Tomasz Napierala } 99aa015c8eSEdward Tomasz Napierala tag = string_skip_whitespace(tag); 100aa015c8eSEdward Tomasz Napierala if ((*tag == '\0') && (!entry)) { 101aa015c8eSEdward Tomasz Napierala /* 102aa015c8eSEdward Tomasz Napierala * Is an entirely comment line, skip to next 103aa015c8eSEdward Tomasz Napierala * comma. 104aa015c8eSEdward Tomasz Napierala */ 105aa015c8eSEdward Tomasz Napierala return (0); 106aa015c8eSEdward Tomasz Napierala } 107aa015c8eSEdward Tomasz Napierala string_trim_trailing_whitespace(tag); 108aa015c8eSEdward Tomasz Napierala 109aa015c8eSEdward Tomasz Napierala qualifier = strsep(&entry, ":"); 110aa015c8eSEdward Tomasz Napierala if (qualifier == NULL) { 111aa015c8eSEdward Tomasz Napierala errno = EINVAL; 112aa015c8eSEdward Tomasz Napierala return (-1); 113aa015c8eSEdward Tomasz Napierala } 114aa015c8eSEdward Tomasz Napierala qualifier = string_skip_whitespace(qualifier); 115aa015c8eSEdward Tomasz Napierala string_trim_trailing_whitespace(qualifier); 116aa015c8eSEdward Tomasz Napierala 117aa015c8eSEdward Tomasz Napierala permission = strsep(&entry, ":"); 118aa015c8eSEdward Tomasz Napierala if (permission == NULL || entry) { 119aa015c8eSEdward Tomasz Napierala errno = EINVAL; 120aa015c8eSEdward Tomasz Napierala return (-1); 121aa015c8eSEdward Tomasz Napierala } 122aa015c8eSEdward Tomasz Napierala permission = string_skip_whitespace(permission); 123aa015c8eSEdward Tomasz Napierala string_trim_trailing_whitespace(permission); 124aa015c8eSEdward Tomasz Napierala 125aa015c8eSEdward Tomasz Napierala t = acl_string_to_tag(tag, qualifier); 126aa015c8eSEdward Tomasz Napierala if (t == -1) { 127aa015c8eSEdward Tomasz Napierala errno = EINVAL; 128aa015c8eSEdward Tomasz Napierala return (-1); 129aa015c8eSEdward Tomasz Napierala } 130aa015c8eSEdward Tomasz Napierala 131aa015c8eSEdward Tomasz Napierala error = _posix1e_acl_string_to_perm(permission, &p); 132aa015c8eSEdward Tomasz Napierala if (error == -1) { 133aa015c8eSEdward Tomasz Napierala errno = EINVAL; 134aa015c8eSEdward Tomasz Napierala return (-1); 135aa015c8eSEdward Tomasz Napierala } 136aa015c8eSEdward Tomasz Napierala 137aa015c8eSEdward Tomasz Napierala switch(t) { 138aa015c8eSEdward Tomasz Napierala case ACL_USER_OBJ: 139aa015c8eSEdward Tomasz Napierala case ACL_GROUP_OBJ: 140aa015c8eSEdward Tomasz Napierala case ACL_MASK: 141aa015c8eSEdward Tomasz Napierala case ACL_OTHER: 142aa015c8eSEdward Tomasz Napierala if (*qualifier != '\0') { 143aa015c8eSEdward Tomasz Napierala errno = EINVAL; 144aa015c8eSEdward Tomasz Napierala return (-1); 145aa015c8eSEdward Tomasz Napierala } 146aa015c8eSEdward Tomasz Napierala id = 0; 147aa015c8eSEdward Tomasz Napierala break; 148aa015c8eSEdward Tomasz Napierala 149aa015c8eSEdward Tomasz Napierala case ACL_USER: 150aa015c8eSEdward Tomasz Napierala case ACL_GROUP: 151aa015c8eSEdward Tomasz Napierala error = _posix1e_acl_name_to_id(t, qualifier, 152aa015c8eSEdward Tomasz Napierala &id); 153aa015c8eSEdward Tomasz Napierala if (error == -1) 154aa015c8eSEdward Tomasz Napierala return (-1); 155aa015c8eSEdward Tomasz Napierala break; 156aa015c8eSEdward Tomasz Napierala 157aa015c8eSEdward Tomasz Napierala default: 158aa015c8eSEdward Tomasz Napierala errno = EINVAL; 159aa015c8eSEdward Tomasz Napierala return (-1); 160aa015c8eSEdward Tomasz Napierala } 161aa015c8eSEdward Tomasz Napierala 162aa015c8eSEdward Tomasz Napierala error = _posix1e_acl_add_entry(aclp, t, id, p); 163aa015c8eSEdward Tomasz Napierala if (error == -1) 164aa015c8eSEdward Tomasz Napierala return (-1); 165aa015c8eSEdward Tomasz Napierala 166aa015c8eSEdward Tomasz Napierala return (0); 167aa015c8eSEdward Tomasz Napierala } 168aa015c8eSEdward Tomasz Napierala 169aa015c8eSEdward Tomasz Napierala static int 170aa015c8eSEdward Tomasz Napierala _text_is_nfs4_entry(const char *entry) 171aa015c8eSEdward Tomasz Napierala { 172aa015c8eSEdward Tomasz Napierala int count = 0; 173aa015c8eSEdward Tomasz Napierala 174aa015c8eSEdward Tomasz Napierala assert(strlen(entry) > 0); 175aa015c8eSEdward Tomasz Napierala 176aa015c8eSEdward Tomasz Napierala while (*entry != '\0') { 177aa015c8eSEdward Tomasz Napierala if (*entry == ':' || *entry == '@') 178aa015c8eSEdward Tomasz Napierala count++; 179aa015c8eSEdward Tomasz Napierala entry++; 180aa015c8eSEdward Tomasz Napierala } 181aa015c8eSEdward Tomasz Napierala 182aa015c8eSEdward Tomasz Napierala if (count <= 2) 183aa015c8eSEdward Tomasz Napierala return (0); 184aa015c8eSEdward Tomasz Napierala 185aa015c8eSEdward Tomasz Napierala return (1); 186aa015c8eSEdward Tomasz Napierala } 187aa015c8eSEdward Tomasz Napierala 188515d7c92SRobert Watson /* 189374c6c0fSRobert Watson * acl_from_text -- Convert a string into an ACL. 190374c6c0fSRobert Watson * Postpone most validity checking until the end and call acl_valid() to do 191515d7c92SRobert Watson * that. 192515d7c92SRobert Watson */ 193515d7c92SRobert Watson acl_t 194515d7c92SRobert Watson acl_from_text(const char *buf_p) 195515d7c92SRobert Watson { 196515d7c92SRobert Watson acl_t acl; 197515d7c92SRobert Watson char *mybuf_p, *line, *cur, *notcomment, *comment, *entry; 198515d7c92SRobert Watson int error; 199515d7c92SRobert Watson 200374c6c0fSRobert Watson /* Local copy we can mess up. */ 201515d7c92SRobert Watson mybuf_p = strdup(buf_p); 2029fd46b02SChris D. Faulhaber if (mybuf_p == NULL) 203f0078215SRobert Watson return(NULL); 204515d7c92SRobert Watson 205aa015c8eSEdward Tomasz Napierala acl = acl_init(3); /* XXX: WTF, 3? */ 2069fd46b02SChris D. Faulhaber if (acl == NULL) { 207515d7c92SRobert Watson free(mybuf_p); 208f0078215SRobert Watson return(NULL); 209515d7c92SRobert Watson } 210515d7c92SRobert Watson 211374c6c0fSRobert Watson /* Outer loop: delimit at \n boundaries. */ 212515d7c92SRobert Watson cur = mybuf_p; 213515d7c92SRobert Watson while ((line = strsep(&cur, "\n"))) { 214374c6c0fSRobert Watson /* Now split the line on the first # to strip out comments. */ 215515d7c92SRobert Watson comment = line; 216515d7c92SRobert Watson notcomment = strsep(&comment, "#"); 217515d7c92SRobert Watson 218374c6c0fSRobert Watson /* Inner loop: delimit at ',' boundaries. */ 219515d7c92SRobert Watson while ((entry = strsep(¬comment, ","))) { 220aa015c8eSEdward Tomasz Napierala 221aa015c8eSEdward Tomasz Napierala /* Skip empty lines. */ 222aa015c8eSEdward Tomasz Napierala if (strlen(string_skip_whitespace(entry)) == 0) 223515d7c92SRobert Watson continue; 224515d7c92SRobert Watson 225aa015c8eSEdward Tomasz Napierala if (_acl_brand(acl) == ACL_BRAND_UNKNOWN) { 226aa015c8eSEdward Tomasz Napierala if (_text_is_nfs4_entry(entry)) 227aa015c8eSEdward Tomasz Napierala _acl_brand_as(acl, ACL_BRAND_NFS4); 228aa015c8eSEdward Tomasz Napierala else 229aa015c8eSEdward Tomasz Napierala _acl_brand_as(acl, ACL_BRAND_POSIX); 230515d7c92SRobert Watson } 231515d7c92SRobert Watson 232aa015c8eSEdward Tomasz Napierala switch (_acl_brand(acl)) { 233aa015c8eSEdward Tomasz Napierala case ACL_BRAND_NFS4: 234aa015c8eSEdward Tomasz Napierala error = _nfs4_acl_entry_from_text(acl, entry); 235515d7c92SRobert Watson break; 236515d7c92SRobert Watson 237aa015c8eSEdward Tomasz Napierala case ACL_BRAND_POSIX: 238aa015c8eSEdward Tomasz Napierala error = _posix1e_acl_entry_from_text(acl, entry); 239515d7c92SRobert Watson break; 240515d7c92SRobert Watson 241515d7c92SRobert Watson default: 242aa015c8eSEdward Tomasz Napierala error = EINVAL; 243aa015c8eSEdward Tomasz Napierala break; 244515d7c92SRobert Watson } 245515d7c92SRobert Watson 246aa015c8eSEdward Tomasz Napierala if (error) 247515d7c92SRobert Watson goto error_label; 248515d7c92SRobert Watson } 249515d7c92SRobert Watson } 250515d7c92SRobert Watson 251515d7c92SRobert Watson #if 0 252374c6c0fSRobert Watson /* XXX Should we only return ACLs valid according to acl_valid? */ 253374c6c0fSRobert Watson /* Verify validity of the ACL we read in. */ 254515d7c92SRobert Watson if (acl_valid(acl) == -1) { 255515d7c92SRobert Watson errno = EINVAL; 256515d7c92SRobert Watson goto error_label; 257515d7c92SRobert Watson } 258515d7c92SRobert Watson #endif 259515d7c92SRobert Watson 2604f6d5fdaSChristian Brueffer free(mybuf_p); 261515d7c92SRobert Watson return(acl); 262515d7c92SRobert Watson 263515d7c92SRobert Watson error_label: 264515d7c92SRobert Watson acl_free(acl); 265515d7c92SRobert Watson free(mybuf_p); 266f0078215SRobert Watson return(NULL); 267515d7c92SRobert Watson } 268515d7c92SRobert Watson 26948135111STim Kientzle /* 27048135111STim Kientzle * Given a username/groupname from a text form of an ACL, return the uid/gid 27148135111STim Kientzle * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM 27248135111STim Kientzle * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE 27348135111STim Kientzle * MAY HAVE SIDE-EFFECTS 27448135111STim Kientzle * 27548135111STim Kientzle * XXX currently doesn't deal correctly with a numeric uid being passed 27648135111STim Kientzle * instead of a username. What is correct behavior here? Check chown. 27748135111STim Kientzle */ 27848135111STim Kientzle static int 27948135111STim Kientzle _posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id) 28048135111STim Kientzle { 28148135111STim Kientzle struct group *g; 28248135111STim Kientzle struct passwd *p; 28348135111STim Kientzle unsigned long l; 28448135111STim Kientzle char *endp; 285515d7c92SRobert Watson 28648135111STim Kientzle switch(tag) { 28748135111STim Kientzle case ACL_USER: 28848135111STim Kientzle p = getpwnam(name); 28948135111STim Kientzle if (p == NULL) { 29048135111STim Kientzle l = strtoul(name, &endp, 0); 29148135111STim Kientzle if (*endp != '\0' || l != (unsigned long)(uid_t)l) { 29248135111STim Kientzle errno = EINVAL; 29348135111STim Kientzle return (-1); 29448135111STim Kientzle } 29548135111STim Kientzle *id = (uid_t)l; 29648135111STim Kientzle return (0); 29748135111STim Kientzle } 29848135111STim Kientzle *id = p->pw_uid; 29948135111STim Kientzle return (0); 300515d7c92SRobert Watson 30148135111STim Kientzle case ACL_GROUP: 30248135111STim Kientzle g = getgrnam(name); 30348135111STim Kientzle if (g == NULL) { 30448135111STim Kientzle l = strtoul(name, &endp, 0); 30548135111STim Kientzle if (*endp != '\0' || l != (unsigned long)(gid_t)l) { 30648135111STim Kientzle errno = EINVAL; 30748135111STim Kientzle return (-1); 30848135111STim Kientzle } 30948135111STim Kientzle *id = (gid_t)l; 31048135111STim Kientzle return (0); 31148135111STim Kientzle } 31248135111STim Kientzle *id = g->gr_gid; 31348135111STim Kientzle return (0); 31448135111STim Kientzle 31548135111STim Kientzle default: 31648135111STim Kientzle return (EINVAL); 31748135111STim Kientzle } 31848135111STim Kientzle } 319