acl_from_text.c revision 194955
1178481Sjb/*- 2178481Sjb * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson 3178481Sjb * All rights reserved. 4178481Sjb * 5178481Sjb * Redistribution and use in source and binary forms, with or without 6178481Sjb * modification, are permitted provided that the following conditions 7178481Sjb * are met: 8178481Sjb * 1. Redistributions of source code must retain the above copyright 9178481Sjb * notice, this list of conditions and the following disclaimer. 10178481Sjb * 2. Redistributions in binary form must reproduce the above copyright 11178481Sjb * notice, this list of conditions and the following disclaimer in the 12178481Sjb * documentation and/or other materials provided with the distribution. 13178481Sjb * 14178481Sjb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15178481Sjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16178481Sjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17178481Sjb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18178481Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19178481Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20178481Sjb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21178481Sjb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22210767Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23178481Sjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24178481Sjb * SUCH DAMAGE. 25178481Sjb */ 26178481Sjb/* 27178481Sjb * acl_from_text: Convert a text-form ACL from a string to an acl_t. 28178481Sjb */ 29178481Sjb 30178481Sjb#include <sys/cdefs.h> 31178481Sjb__FBSDID("$FreeBSD: head/lib/libc/posix1e/acl_from_text.c 194955 2009-06-25 12:46:59Z trasz $"); 32178481Sjb 33178481Sjb#include <sys/types.h> 34178481Sjb#include "namespace.h" 35178481Sjb#include <sys/acl.h> 36178481Sjb#include "un-namespace.h" 37178481Sjb#include <sys/errno.h> 38178481Sjb#include <grp.h> 39178481Sjb#include <pwd.h> 40178481Sjb#include <stdio.h> 41178481Sjb#include <stdlib.h> 42178481Sjb#include <string.h> 43178481Sjb#include <assert.h> 44178481Sjb 45178481Sjb#include "acl_support.h" 46178481Sjb 47178481Sjbstatic int _posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id); 48178481Sjbstatic acl_tag_t acl_string_to_tag(char *tag, char *qualifier); 49178481Sjb 50249656Sedint _nfs4_acl_entry_from_text(acl_t aclp, char *entry); 51178481Sjbint _text_could_be_nfs4_acl(const char *entry); 52178481Sjb 53178481Sjbstatic acl_tag_t 54178481Sjbacl_string_to_tag(char *tag, char *qualifier) 55253678Spfg{ 56253661Spfg 57178481Sjb if (*qualifier == '\0') { 58178481Sjb if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) { 59178481Sjb return (ACL_USER_OBJ); 60178481Sjb } else 61178481Sjb if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) { 62178481Sjb return (ACL_GROUP_OBJ); 63178481Sjb } else 64178481Sjb if ((!strcmp(tag, "mask")) || (!strcmp(tag, "m"))) { 65178481Sjb return (ACL_MASK); 66178481Sjb } else 67233407Sgonzo if ((!strcmp(tag, "other")) || (!strcmp(tag, "o"))) { 68233407Sgonzo return (ACL_OTHER); 69233407Sgonzo } else 70233407Sgonzo return(-1); 71233407Sgonzo } else { 72233407Sgonzo if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) { 73233407Sgonzo return(ACL_USER); 74233407Sgonzo } else 75233407Sgonzo if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) { 76233407Sgonzo return(ACL_GROUP); 77233407Sgonzo } else 78233407Sgonzo return(-1); 79178481Sjb } 80178481Sjb} 81178546Sjb 82178481Sjbstatic int 83178481Sjb_posix1e_acl_entry_from_text(acl_t aclp, char *entry) 84178481Sjb{ 85178481Sjb acl_tag_t t; 86178481Sjb acl_perm_t p; 87178481Sjb char *tag, *qualifier, *permission; 88178481Sjb uid_t id; 89178481Sjb int error; 90178481Sjb 91178481Sjb assert(_acl_brand(aclp) == ACL_BRAND_POSIX); 92178481Sjb 93178546Sjb /* Split into three ':' delimited fields. */ 94178481Sjb tag = strsep(&entry, ":"); 95178481Sjb if (tag == NULL) { 96178481Sjb errno = EINVAL; 97178481Sjb return (-1); 98178481Sjb } 99178481Sjb tag = string_skip_whitespace(tag); 100178481Sjb if ((*tag == '\0') && (!entry)) { 101178481Sjb /* 102178481Sjb * Is an entirely comment line, skip to next 103178481Sjb * comma. 104178546Sjb */ 105178481Sjb return (0); 106178481Sjb } 107178481Sjb string_trim_trailing_whitespace(tag); 108178481Sjb 109178481Sjb qualifier = strsep(&entry, ":"); 110178481Sjb if (qualifier == NULL) { 111178481Sjb errno = EINVAL; 112178481Sjb return (-1); 113178481Sjb } 114178481Sjb qualifier = string_skip_whitespace(qualifier); 115178546Sjb string_trim_trailing_whitespace(qualifier); 116178481Sjb 117178481Sjb permission = strsep(&entry, ":"); 118178481Sjb if (permission == NULL || entry) { 119178481Sjb errno = EINVAL; 120178481Sjb return (-1); 121178481Sjb } 122178481Sjb permission = string_skip_whitespace(permission); 123178546Sjb string_trim_trailing_whitespace(permission); 124178481Sjb 125178481Sjb t = acl_string_to_tag(tag, qualifier); 126178481Sjb if (t == -1) { 127178481Sjb errno = EINVAL; 128178481Sjb return (-1); 129178546Sjb } 130178546Sjb 131178481Sjb error = _posix1e_acl_string_to_perm(permission, &p); 132178481Sjb if (error == -1) { 133178481Sjb errno = EINVAL; 134178481Sjb return (-1); 135178481Sjb } 136178481Sjb 137178481Sjb switch(t) { 138178481Sjb case ACL_USER_OBJ: 139178481Sjb case ACL_GROUP_OBJ: 140178481Sjb case ACL_MASK: 141178481Sjb case ACL_OTHER: 142178546Sjb if (*qualifier != '\0') { 143178481Sjb errno = EINVAL; 144178481Sjb return (-1); 145178481Sjb } 146178481Sjb id = 0; 147178481Sjb break; 148178546Sjb 149178481Sjb case ACL_USER: 150178546Sjb case ACL_GROUP: 151178546Sjb error = _posix1e_acl_name_to_id(t, qualifier, 152178481Sjb &id); 153178481Sjb if (error == -1) 154178481Sjb return (-1); 155178481Sjb break; 156178481Sjb 157233407Sgonzo default: 158233407Sgonzo errno = EINVAL; 159233407Sgonzo return (-1); 160233407Sgonzo } 161233407Sgonzo 162178481Sjb error = _posix1e_acl_add_entry(aclp, t, id, p); 163178481Sjb if (error == -1) 164178481Sjb return (-1); 165178481Sjb 166178481Sjb return (0); 167178481Sjb} 168178481Sjb 169178481Sjbstatic int 170178481Sjb_text_is_nfs4_entry(const char *entry) 171178481Sjb{ 172178481Sjb int count = 0; 173178481Sjb 174233407Sgonzo assert(strlen(entry) > 0); 175233407Sgonzo 176233407Sgonzo while (*entry != '\0') { 177233407Sgonzo if (*entry == ':' || *entry == '@') 178178481Sjb count++; 179178481Sjb entry++; 180178481Sjb } 181178481Sjb 182178481Sjb if (count <= 2) 183178481Sjb return (0); 184178481Sjb 185178481Sjb return (1); 186178481Sjb} 187178481Sjb 188178481Sjb/* 189178481Sjb * acl_from_text -- Convert a string into an ACL. 190178481Sjb * Postpone most validity checking until the end and call acl_valid() to do 191178481Sjb * that. 192178481Sjb */ 193178481Sjbacl_t 194178481Sjbacl_from_text(const char *buf_p) 195178481Sjb{ 196178481Sjb acl_t acl; 197178481Sjb char *mybuf_p, *line, *cur, *notcomment, *comment, *entry; 198210767Srpaulo int error; 199210767Srpaulo 200210767Srpaulo /* Local copy we can mess up. */ 201210767Srpaulo mybuf_p = strdup(buf_p); 202210767Srpaulo if (mybuf_p == NULL) 203210767Srpaulo return(NULL); 204178481Sjb 205178481Sjb acl = acl_init(3); /* XXX: WTF, 3? */ 206233407Sgonzo if (acl == NULL) { 207233407Sgonzo free(mybuf_p); 208233407Sgonzo return(NULL); 209233407Sgonzo } 210233407Sgonzo 211233407Sgonzo /* Outer loop: delimit at \n boundaries. */ 212178481Sjb cur = mybuf_p; 213178481Sjb while ((line = strsep(&cur, "\n"))) { 214178481Sjb /* Now split the line on the first # to strip out comments. */ 215178481Sjb comment = line; 216233407Sgonzo notcomment = strsep(&comment, "#"); 217233407Sgonzo 218233407Sgonzo /* Inner loop: delimit at ',' boundaries. */ 219233407Sgonzo while ((entry = strsep(¬comment, ","))) { 220233407Sgonzo 221178481Sjb /* Skip empty lines. */ 222178481Sjb if (strlen(string_skip_whitespace(entry)) == 0) 223178481Sjb continue; 224178481Sjb 225178481Sjb if (_acl_brand(acl) == ACL_BRAND_UNKNOWN) { 226178481Sjb if (_text_is_nfs4_entry(entry)) 227178481Sjb _acl_brand_as(acl, ACL_BRAND_NFS4); 228178481Sjb else 229178481Sjb _acl_brand_as(acl, ACL_BRAND_POSIX); 230178481Sjb } 231178481Sjb 232178481Sjb switch (_acl_brand(acl)) { 233178481Sjb case ACL_BRAND_NFS4: 234178481Sjb error = _nfs4_acl_entry_from_text(acl, entry); 235178481Sjb break; 236178481Sjb 237178481Sjb case ACL_BRAND_POSIX: 238178481Sjb error = _posix1e_acl_entry_from_text(acl, entry); 239178481Sjb break; 240178481Sjb 241178481Sjb default: 242178481Sjb error = EINVAL; 243178481Sjb break; 244178481Sjb } 245233407Sgonzo 246233407Sgonzo if (error) 247233407Sgonzo goto error_label; 248233407Sgonzo } 249233407Sgonzo } 250233407Sgonzo 251233407Sgonzo#if 0 252178481Sjb /* XXX Should we only return ACLs valid according to acl_valid? */ 253178481Sjb /* Verify validity of the ACL we read in. */ 254178481Sjb if (acl_valid(acl) == -1) { 255178481Sjb errno = EINVAL; 256178481Sjb goto error_label; 257233407Sgonzo } 258233407Sgonzo#endif 259233407Sgonzo 260233407Sgonzo return(acl); 261233407Sgonzo 262233407Sgonzoerror_label: 263233407Sgonzo acl_free(acl); 264178481Sjb free(mybuf_p); 265178481Sjb return(NULL); 266178481Sjb} 267178481Sjb 268178481Sjb/* 269178481Sjb * Given a username/groupname from a text form of an ACL, return the uid/gid 270178481Sjb * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM 271178481Sjb * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE 272178481Sjb * MAY HAVE SIDE-EFFECTS 273233407Sgonzo * 274233407Sgonzo * XXX currently doesn't deal correctly with a numeric uid being passed 275233407Sgonzo * instead of a username. What is correct behavior here? Check chown. 276233407Sgonzo */ 277233407Sgonzostatic int 278233407Sgonzo_posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id) 279178481Sjb{ 280178481Sjb struct group *g; 281178481Sjb struct passwd *p; 282178481Sjb unsigned long l; 283178546Sjb char *endp; 284178481Sjb 285178546Sjb switch(tag) { 286178546Sjb case ACL_USER: 287178481Sjb p = getpwnam(name); 288178481Sjb if (p == NULL) { 289178481Sjb l = strtoul(name, &endp, 0); 290178481Sjb if (*endp != '\0' || l != (unsigned long)(uid_t)l) { 291178481Sjb errno = EINVAL; 292178481Sjb return (-1); 293178481Sjb } 294178481Sjb *id = (uid_t)l; 295178481Sjb return (0); 296178481Sjb } 297178481Sjb *id = p->pw_uid; 298178481Sjb return (0); 299178481Sjb 300178481Sjb case ACL_GROUP: 301178481Sjb g = getgrnam(name); 302178481Sjb if (g == NULL) { 303178481Sjb l = strtoul(name, &endp, 0); 304178481Sjb if (*endp != '\0' || l != (unsigned long)(gid_t)l) { 305178481Sjb errno = EINVAL; 306178481Sjb return (-1); 307178481Sjb } 308178481Sjb *id = (gid_t)l; 309178481Sjb return (0); 310178481Sjb } 311178481Sjb *id = g->gr_gid; 312178481Sjb return (0); 313178481Sjb 314178481Sjb default: 315178481Sjb return (EINVAL); 316178481Sjb } 317178481Sjb} 318178481Sjb