156055Srwatson/*- 274191Srwatson * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson 356055Srwatson * All rights reserved. 456055Srwatson * 556055Srwatson * Redistribution and use in source and binary forms, with or without 656055Srwatson * modification, are permitted provided that the following conditions 756055Srwatson * are met: 856055Srwatson * 1. Redistributions of source code must retain the above copyright 956055Srwatson * notice, this list of conditions and the following disclaimer. 1056055Srwatson * 2. Redistributions in binary form must reproduce the above copyright 1156055Srwatson * notice, this list of conditions and the following disclaimer in the 1256055Srwatson * documentation and/or other materials provided with the distribution. 1356055Srwatson * 1456055Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1556055Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1656055Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1756055Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1856055Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1956055Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2056055Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2156055Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2256055Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2356055Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2456055Srwatson * SUCH DAMAGE. 2556055Srwatson */ 2656055Srwatson/* 2766259Srwatson * acl_from_text: Convert a text-form ACL from a string to an acl_t. 2856055Srwatson */ 2956055Srwatson 3092986Sobrien#include <sys/cdefs.h> 3192986Sobrien__FBSDID("$FreeBSD$"); 3292986Sobrien 3356055Srwatson#include <sys/types.h> 3475185Stmm#include "namespace.h" 3556055Srwatson#include <sys/acl.h> 3675185Stmm#include "un-namespace.h" 3756055Srwatson#include <sys/errno.h> 38167006Skientzle#include <grp.h> 39167006Skientzle#include <pwd.h> 4056055Srwatson#include <stdio.h> 4156055Srwatson#include <stdlib.h> 4256055Srwatson#include <string.h> 43194955Strasz#include <assert.h> 4456055Srwatson 4556055Srwatson#include "acl_support.h" 4656055Srwatson 4791032Sjedgarstatic acl_tag_t acl_string_to_tag(char *tag, char *qualifier); 4891032Sjedgar 49194955Straszint _nfs4_acl_entry_from_text(acl_t aclp, char *entry); 50194955Straszint _text_could_be_nfs4_acl(const char *entry); 5156055Srwatson 5291032Sjedgarstatic acl_tag_t 5356055Srwatsonacl_string_to_tag(char *tag, char *qualifier) 5456055Srwatson{ 5556055Srwatson 5656055Srwatson if (*qualifier == '\0') { 5756055Srwatson if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) { 5856055Srwatson return (ACL_USER_OBJ); 5956055Srwatson } else 6056055Srwatson if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) { 6156055Srwatson return (ACL_GROUP_OBJ); 6256055Srwatson } else 6356055Srwatson if ((!strcmp(tag, "mask")) || (!strcmp(tag, "m"))) { 6456055Srwatson return (ACL_MASK); 6556055Srwatson } else 6656055Srwatson if ((!strcmp(tag, "other")) || (!strcmp(tag, "o"))) { 6756055Srwatson return (ACL_OTHER); 6856055Srwatson } else 6956055Srwatson return(-1); 7056055Srwatson } else { 7156055Srwatson if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) { 7256055Srwatson return(ACL_USER); 7356055Srwatson } else 7456055Srwatson if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) { 7556055Srwatson return(ACL_GROUP); 7656055Srwatson } else 7756055Srwatson return(-1); 7856055Srwatson } 7956055Srwatson} 8056055Srwatson 81194955Straszstatic int 82194955Strasz_posix1e_acl_entry_from_text(acl_t aclp, char *entry) 83194955Strasz{ 84194955Strasz acl_tag_t t; 85194955Strasz acl_perm_t p; 86194955Strasz char *tag, *qualifier, *permission; 87194955Strasz uid_t id; 88194955Strasz int error; 89194955Strasz 90194955Strasz assert(_acl_brand(aclp) == ACL_BRAND_POSIX); 91194955Strasz 92194955Strasz /* Split into three ':' delimited fields. */ 93194955Strasz tag = strsep(&entry, ":"); 94194955Strasz if (tag == NULL) { 95194955Strasz errno = EINVAL; 96194955Strasz return (-1); 97194955Strasz } 98194955Strasz tag = string_skip_whitespace(tag); 99194955Strasz if ((*tag == '\0') && (!entry)) { 100194955Strasz /* 101194955Strasz * Is an entirely comment line, skip to next 102194955Strasz * comma. 103194955Strasz */ 104194955Strasz return (0); 105194955Strasz } 106194955Strasz string_trim_trailing_whitespace(tag); 107194955Strasz 108194955Strasz qualifier = strsep(&entry, ":"); 109194955Strasz if (qualifier == NULL) { 110194955Strasz errno = EINVAL; 111194955Strasz return (-1); 112194955Strasz } 113194955Strasz qualifier = string_skip_whitespace(qualifier); 114194955Strasz string_trim_trailing_whitespace(qualifier); 115194955Strasz 116194955Strasz permission = strsep(&entry, ":"); 117194955Strasz if (permission == NULL || entry) { 118194955Strasz errno = EINVAL; 119194955Strasz return (-1); 120194955Strasz } 121194955Strasz permission = string_skip_whitespace(permission); 122194955Strasz string_trim_trailing_whitespace(permission); 123194955Strasz 124194955Strasz t = acl_string_to_tag(tag, qualifier); 125194955Strasz if (t == -1) { 126194955Strasz errno = EINVAL; 127194955Strasz return (-1); 128194955Strasz } 129194955Strasz 130194955Strasz error = _posix1e_acl_string_to_perm(permission, &p); 131194955Strasz if (error == -1) { 132194955Strasz errno = EINVAL; 133194955Strasz return (-1); 134194955Strasz } 135194955Strasz 136194955Strasz switch(t) { 137194955Strasz case ACL_USER_OBJ: 138194955Strasz case ACL_GROUP_OBJ: 139194955Strasz case ACL_MASK: 140194955Strasz case ACL_OTHER: 141194955Strasz if (*qualifier != '\0') { 142194955Strasz errno = EINVAL; 143194955Strasz return (-1); 144194955Strasz } 145194955Strasz id = 0; 146194955Strasz break; 147194955Strasz 148194955Strasz case ACL_USER: 149194955Strasz case ACL_GROUP: 150209736Strasz error = _acl_name_to_id(t, qualifier, &id); 151194955Strasz if (error == -1) 152194955Strasz return (-1); 153194955Strasz break; 154194955Strasz 155194955Strasz default: 156194955Strasz errno = EINVAL; 157194955Strasz return (-1); 158194955Strasz } 159194955Strasz 160194955Strasz error = _posix1e_acl_add_entry(aclp, t, id, p); 161194955Strasz if (error == -1) 162194955Strasz return (-1); 163194955Strasz 164194955Strasz return (0); 165194955Strasz} 166194955Strasz 167194955Straszstatic int 168194955Strasz_text_is_nfs4_entry(const char *entry) 169194955Strasz{ 170194955Strasz int count = 0; 171194955Strasz 172194955Strasz assert(strlen(entry) > 0); 173194955Strasz 174194955Strasz while (*entry != '\0') { 175194955Strasz if (*entry == ':' || *entry == '@') 176194955Strasz count++; 177194955Strasz entry++; 178194955Strasz } 179194955Strasz 180194955Strasz if (count <= 2) 181194955Strasz return (0); 182194955Strasz 183194955Strasz return (1); 184194955Strasz} 185194955Strasz 18656055Srwatson/* 18766259Srwatson * acl_from_text -- Convert a string into an ACL. 18866259Srwatson * Postpone most validity checking until the end and call acl_valid() to do 18956055Srwatson * that. 19056055Srwatson */ 19156055Srwatsonacl_t 19256055Srwatsonacl_from_text(const char *buf_p) 19356055Srwatson{ 19475928Sjedgar acl_t acl; 19575928Sjedgar char *mybuf_p, *line, *cur, *notcomment, *comment, *entry; 19675928Sjedgar int error; 19756055Srwatson 19866259Srwatson /* Local copy we can mess up. */ 19956055Srwatson mybuf_p = strdup(buf_p); 20091034Sjedgar if (mybuf_p == NULL) 20171142Srwatson return(NULL); 20256055Srwatson 203194955Strasz acl = acl_init(3); /* XXX: WTF, 3? */ 20491034Sjedgar if (acl == NULL) { 20556055Srwatson free(mybuf_p); 20671142Srwatson return(NULL); 20756055Srwatson } 20856055Srwatson 20966259Srwatson /* Outer loop: delimit at \n boundaries. */ 21056055Srwatson cur = mybuf_p; 21156055Srwatson while ((line = strsep(&cur, "\n"))) { 21266259Srwatson /* Now split the line on the first # to strip out comments. */ 21356055Srwatson comment = line; 21456055Srwatson notcomment = strsep(&comment, "#"); 21556055Srwatson 21666259Srwatson /* Inner loop: delimit at ',' boundaries. */ 21756055Srwatson while ((entry = strsep(¬comment, ","))) { 218194955Strasz 219194955Strasz /* Skip empty lines. */ 220194955Strasz if (strlen(string_skip_whitespace(entry)) == 0) 22156055Srwatson continue; 22256055Srwatson 223194955Strasz if (_acl_brand(acl) == ACL_BRAND_UNKNOWN) { 224194955Strasz if (_text_is_nfs4_entry(entry)) 225194955Strasz _acl_brand_as(acl, ACL_BRAND_NFS4); 226194955Strasz else 227194955Strasz _acl_brand_as(acl, ACL_BRAND_POSIX); 22856055Srwatson } 22956055Srwatson 230194955Strasz switch (_acl_brand(acl)) { 231194955Strasz case ACL_BRAND_NFS4: 232194955Strasz error = _nfs4_acl_entry_from_text(acl, entry); 23356055Srwatson break; 23456055Srwatson 235194955Strasz case ACL_BRAND_POSIX: 236194955Strasz error = _posix1e_acl_entry_from_text(acl, entry); 23756055Srwatson break; 23856055Srwatson 23956055Srwatson default: 240194955Strasz error = EINVAL; 241194955Strasz break; 24256055Srwatson } 24356055Srwatson 244194955Strasz if (error) 24556055Srwatson goto error_label; 24656055Srwatson } 24756055Srwatson } 24856055Srwatson 24956055Srwatson#if 0 25066259Srwatson /* XXX Should we only return ACLs valid according to acl_valid? */ 25166259Srwatson /* Verify validity of the ACL we read in. */ 25256055Srwatson if (acl_valid(acl) == -1) { 25356055Srwatson errno = EINVAL; 25456055Srwatson goto error_label; 25556055Srwatson } 25656055Srwatson#endif 25756055Srwatson 258199317Sbrueffer free(mybuf_p); 25956055Srwatson return(acl); 26056055Srwatson 26156055Srwatsonerror_label: 26256055Srwatson acl_free(acl); 26356055Srwatson free(mybuf_p); 26471142Srwatson return(NULL); 26556055Srwatson} 26656055Srwatson 267167006Skientzle/* 268167006Skientzle * Given a username/groupname from a text form of an ACL, return the uid/gid 269167006Skientzle * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM 270167006Skientzle * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE 271167006Skientzle * MAY HAVE SIDE-EFFECTS 272167006Skientzle */ 273209736Straszint 274209736Strasz_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id) 275167006Skientzle{ 276167006Skientzle struct group *g; 277167006Skientzle struct passwd *p; 278167006Skientzle unsigned long l; 279167006Skientzle char *endp; 28056055Srwatson 281167006Skientzle switch(tag) { 282167006Skientzle case ACL_USER: 283167006Skientzle p = getpwnam(name); 284167006Skientzle if (p == NULL) { 285167006Skientzle l = strtoul(name, &endp, 0); 286167006Skientzle if (*endp != '\0' || l != (unsigned long)(uid_t)l) { 287167006Skientzle errno = EINVAL; 288167006Skientzle return (-1); 289167006Skientzle } 290167006Skientzle *id = (uid_t)l; 291167006Skientzle return (0); 292167006Skientzle } 293167006Skientzle *id = p->pw_uid; 294167006Skientzle return (0); 29556055Srwatson 296167006Skientzle case ACL_GROUP: 297167006Skientzle g = getgrnam(name); 298167006Skientzle if (g == NULL) { 299167006Skientzle l = strtoul(name, &endp, 0); 300167006Skientzle if (*endp != '\0' || l != (unsigned long)(gid_t)l) { 301167006Skientzle errno = EINVAL; 302167006Skientzle return (-1); 303167006Skientzle } 304167006Skientzle *id = (gid_t)l; 305167006Skientzle return (0); 306167006Skientzle } 307167006Skientzle *id = g->gr_gid; 308167006Skientzle return (0); 309167006Skientzle 310167006Skientzle default: 311167006Skientzle return (EINVAL); 312167006Skientzle } 313167006Skientzle} 314