172445Sassar/* 2178825Sdfr * Copyright (c) 2000 - 2002, 2004 Kungliga Tekniska H�gskolan 372445Sassar * (Royal Institute of Technology, Stockholm, Sweden). 472445Sassar * All rights reserved. 572445Sassar * 672445Sassar * Redistribution and use in source and binary forms, with or without 772445Sassar * modification, are permitted provided that the following conditions 872445Sassar * are met: 972445Sassar * 1072445Sassar * 1. Redistributions of source code must retain the above copyright 1172445Sassar * notice, this list of conditions and the following disclaimer. 1272445Sassar * 1372445Sassar * 2. Redistributions in binary form must reproduce the above copyright 1472445Sassar * notice, this list of conditions and the following disclaimer in the 1572445Sassar * documentation and/or other materials provided with the distribution. 1672445Sassar * 1772445Sassar * 3. Neither the name of the Institute nor the names of its contributors 1872445Sassar * may be used to endorse or promote products derived from this software 1972445Sassar * without specific prior written permission. 2072445Sassar * 2172445Sassar * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 2272445Sassar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2372445Sassar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2472445Sassar * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 2572445Sassar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2672445Sassar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2772445Sassar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2872445Sassar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2972445Sassar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3072445Sassar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3172445Sassar * SUCH DAMAGE. 3272445Sassar */ 3372445Sassar 3472445Sassar#include "krb5_locl.h" 3572445Sassar#include <fnmatch.h> 3672445Sassar 37178825SdfrRCSID("$Id: acl.c 22119 2007-12-03 22:02:48Z lha $"); 3872445Sassar 3972445Sassarstruct acl_field { 4072445Sassar enum { acl_string, acl_fnmatch, acl_retval } type; 4172445Sassar union { 4272445Sassar const char *cstr; 4372445Sassar char **retv; 4472445Sassar } u; 4572445Sassar struct acl_field *next, **last; 4672445Sassar}; 4772445Sassar 4872445Sassarstatic void 49178825Sdfrfree_retv(struct acl_field *acl) 5072445Sassar{ 51178825Sdfr while(acl != NULL) { 52178825Sdfr if (acl->type == acl_retval) { 53178825Sdfr if (*acl->u.retv) 54178825Sdfr free(*acl->u.retv); 55178825Sdfr *acl->u.retv = NULL; 56178825Sdfr } 57178825Sdfr acl = acl->next; 58178825Sdfr } 59178825Sdfr} 60178825Sdfr 61178825Sdfrstatic void 62178825Sdfracl_free_list(struct acl_field *acl, int retv) 63178825Sdfr{ 6472445Sassar struct acl_field *next; 65178825Sdfr if (retv) 66178825Sdfr free_retv(acl); 6772445Sassar while(acl != NULL) { 6872445Sassar next = acl->next; 6972445Sassar free(acl); 7072445Sassar acl = next; 7172445Sassar } 7272445Sassar} 7372445Sassar 7472445Sassarstatic krb5_error_code 7572445Sassaracl_parse_format(krb5_context context, 7672445Sassar struct acl_field **acl_ret, 7772445Sassar const char *format, 7872445Sassar va_list ap) 7972445Sassar{ 8072445Sassar const char *p; 8172445Sassar struct acl_field *acl = NULL, *tmp; 8272445Sassar 8372445Sassar for(p = format; *p != '\0'; p++) { 8472445Sassar tmp = malloc(sizeof(*tmp)); 8572445Sassar if(tmp == NULL) { 8678527Sassar krb5_set_error_string(context, "malloc: out of memory"); 87178825Sdfr acl_free_list(acl, 0); 8872445Sassar return ENOMEM; 8972445Sassar } 9072445Sassar if(*p == 's') { 9172445Sassar tmp->type = acl_string; 9272445Sassar tmp->u.cstr = va_arg(ap, const char*); 9372445Sassar } else if(*p == 'f') { 9472445Sassar tmp->type = acl_fnmatch; 9572445Sassar tmp->u.cstr = va_arg(ap, const char*); 9672445Sassar } else if(*p == 'r') { 9772445Sassar tmp->type = acl_retval; 9872445Sassar tmp->u.retv = va_arg(ap, char **); 99178825Sdfr *tmp->u.retv = NULL; 100178825Sdfr } else { 101178825Sdfr krb5_set_error_string(context, "acl_parse_format: " 102178825Sdfr "unknown format specifier %c", *p); 103178825Sdfr acl_free_list(acl, 0); 104178825Sdfr free(tmp); 105178825Sdfr return EINVAL; 10672445Sassar } 10772445Sassar tmp->next = NULL; 10872445Sassar if(acl == NULL) 10972445Sassar acl = tmp; 11072445Sassar else 11172445Sassar *acl->last = tmp; 11272445Sassar acl->last = &tmp->next; 11372445Sassar } 11472445Sassar *acl_ret = acl; 11572445Sassar return 0; 11672445Sassar} 11772445Sassar 11872445Sassarstatic krb5_boolean 11972445Sassaracl_match_field(krb5_context context, 12072445Sassar const char *string, 12172445Sassar struct acl_field *field) 12272445Sassar{ 12372445Sassar if(field->type == acl_string) { 124178825Sdfr return !strcmp(field->u.cstr, string); 12572445Sassar } else if(field->type == acl_fnmatch) { 126178825Sdfr return !fnmatch(field->u.cstr, string, 0); 12772445Sassar } else if(field->type == acl_retval) { 12872445Sassar *field->u.retv = strdup(string); 12972445Sassar return TRUE; 13072445Sassar } 13172445Sassar return FALSE; 13272445Sassar} 13372445Sassar 13472445Sassarstatic krb5_boolean 13572445Sassaracl_match_acl(krb5_context context, 13672445Sassar struct acl_field *acl, 13772445Sassar const char *string) 13872445Sassar{ 13972445Sassar char buf[256]; 140178825Sdfr while(strsep_copy(&string, " \t", buf, sizeof(buf)) != -1) { 14172445Sassar if(buf[0] == '\0') 14272445Sassar continue; /* skip ws */ 143178825Sdfr if (acl == NULL) 144178825Sdfr return FALSE; 14572445Sassar if(!acl_match_field(context, buf, acl)) { 14672445Sassar return FALSE; 14772445Sassar } 148178825Sdfr acl = acl->next; 14972445Sassar } 150178825Sdfr if (acl) 151178825Sdfr return FALSE; 15272445Sassar return TRUE; 15372445Sassar} 15472445Sassar 155178825Sdfr/** 156178825Sdfr * krb5_acl_match_string matches ACL format against a string. 157178825Sdfr * 158178825Sdfr * The ACL format has three format specifiers: s, f, and r. Each 159178825Sdfr * specifier will retrieve one argument from the variable arguments 160178825Sdfr * for either matching or storing data. The input string is split up 161178825Sdfr * using " " (space) and "\t" (tab) as a delimiter; multiple and "\t" 162178825Sdfr * in a row are considered to be the same. 163178825Sdfr * 164178825Sdfr * List of format specifiers: 165178825Sdfr * - s Matches a string using strcmp(3) (case sensitive). 166178825Sdfr * - f Matches the string with fnmatch(3). Theflags 167178825Sdfr * argument (the last argument) passed to the fnmatch function is 0. 168178825Sdfr * - r Returns a copy of the string in the char ** passed in; the copy 169178825Sdfr * must be freed with free(3). There is no need to free(3) the 170178825Sdfr * string on error: the function will clean up and set the pointer 171178825Sdfr * to NULL. 172178825Sdfr * 173178825Sdfr * @param context Kerberos 5 context 174178825Sdfr * @param string string to match with 175178825Sdfr * @param format format to match 176178825Sdfr * @param ... parameter to format string 177178825Sdfr * 178178825Sdfr * @return Return an error code or 0. 179178825Sdfr * 180178825Sdfr * 181178825Sdfr * @code 182178825Sdfr * char *s; 183178825Sdfr * 184178825Sdfr * ret = krb5_acl_match_string(context, "foo", "s", "foo"); 185178825Sdfr * if (ret) 186178825Sdfr * krb5_errx(context, 1, "acl didn't match"); 187178825Sdfr * ret = krb5_acl_match_string(context, "foo foo baz/kaka", 188178825Sdfr * "ss", "foo", &s, "foo/\\*"); 189178825Sdfr * if (ret) { 190178825Sdfr * // no need to free(s) on error 191178825Sdfr * assert(s == NULL); 192178825Sdfr * krb5_errx(context, 1, "acl didn't match"); 193178825Sdfr * } 194178825Sdfr * free(s); 195178825Sdfr * @endcode 196178825Sdfr * 197178825Sdfr * @sa krb5_acl_match_file 198178825Sdfr * @ingroup krb5_support 199178825Sdfr */ 20072445Sassar 201178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION 20272445Sassarkrb5_acl_match_string(krb5_context context, 203102644Snectar const char *string, 20472445Sassar const char *format, 20572445Sassar ...) 20672445Sassar{ 20772445Sassar krb5_error_code ret; 20878527Sassar krb5_boolean found; 20972445Sassar struct acl_field *acl; 21072445Sassar 21172445Sassar va_list ap; 21272445Sassar va_start(ap, format); 21372445Sassar ret = acl_parse_format(context, &acl, format, ap); 21472445Sassar va_end(ap); 21572445Sassar if(ret) 21672445Sassar return ret; 21772445Sassar 218102644Snectar found = acl_match_acl(context, acl, string); 219178825Sdfr acl_free_list(acl, !found); 22078527Sassar if (found) { 22178527Sassar return 0; 22278527Sassar } else { 22378527Sassar krb5_set_error_string(context, "ACL did not match"); 22478527Sassar return EACCES; 22578527Sassar } 22672445Sassar} 22772445Sassar 228178825Sdfr/** 229178825Sdfr * krb5_acl_match_file matches ACL format against each line in a file 230178825Sdfr * using krb5_acl_match_string(). Lines starting with # are treated 231178825Sdfr * like comments and ignored. 232178825Sdfr * 233178825Sdfr * @param context Kerberos 5 context. 234178825Sdfr * @param file file with acl listed in the file. 235178825Sdfr * @param format format to match. 236178825Sdfr * @param ... parameter to format string. 237178825Sdfr * 238178825Sdfr * @return Return an error code or 0. 239178825Sdfr * 240178825Sdfr * @sa krb5_acl_match_string 241178825Sdfr * @ingroup krb5_support 242178825Sdfr */ 243178825Sdfr 244178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION 24572445Sassarkrb5_acl_match_file(krb5_context context, 24672445Sassar const char *file, 24772445Sassar const char *format, 24872445Sassar ...) 24972445Sassar{ 25072445Sassar krb5_error_code ret; 25172445Sassar struct acl_field *acl; 25272445Sassar char buf[256]; 25372445Sassar va_list ap; 25472445Sassar FILE *f; 25578527Sassar krb5_boolean found; 25672445Sassar 25772445Sassar f = fopen(file, "r"); 25878527Sassar if(f == NULL) { 25978527Sassar int save_errno = errno; 26072445Sassar 26178527Sassar krb5_set_error_string(context, "open(%s): %s", file, 26278527Sassar strerror(save_errno)); 26378527Sassar return save_errno; 26478527Sassar } 26578527Sassar 26672445Sassar va_start(ap, format); 26772445Sassar ret = acl_parse_format(context, &acl, format, ap); 26872445Sassar va_end(ap); 26972445Sassar if(ret) { 27072445Sassar fclose(f); 27172445Sassar return ret; 27272445Sassar } 27372445Sassar 27478527Sassar found = FALSE; 27572445Sassar while(fgets(buf, sizeof(buf), f)) { 27672445Sassar if(buf[0] == '#') 27772445Sassar continue; 27872445Sassar if(acl_match_acl(context, acl, buf)) { 27978527Sassar found = TRUE; 28078527Sassar break; 28172445Sassar } 282178825Sdfr free_retv(acl); 28372445Sassar } 28472445Sassar 28572445Sassar fclose(f); 286178825Sdfr acl_free_list(acl, !found); 28778527Sassar if (found) { 28878527Sassar return 0; 28978527Sassar } else { 29078527Sassar krb5_set_error_string(context, "ACL did not match"); 29178527Sassar return EACCES; 29278527Sassar } 29372445Sassar} 294