172445Sassar/* 2233294Sstas * Copyright (c) 2000 - 2002, 2004 Kungliga Tekniska H��gskolan 372445Sassar * (Royal Institute of Technology, Stockholm, Sweden). 472445Sassar * All rights reserved. 5233294Sstas * 672445Sassar * Redistribution and use in source and binary forms, with or without 772445Sassar * modification, are permitted provided that the following conditions 872445Sassar * are met: 9233294Sstas * 1072445Sassar * 1. Redistributions of source code must retain the above copyright 1172445Sassar * notice, this list of conditions and the following disclaimer. 12233294Sstas * 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. 16233294Sstas * 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. 20233294Sstas * 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 3772445Sassarstruct acl_field { 3872445Sassar enum { acl_string, acl_fnmatch, acl_retval } type; 3972445Sassar union { 4072445Sassar const char *cstr; 4172445Sassar char **retv; 4272445Sassar } u; 4372445Sassar struct acl_field *next, **last; 4472445Sassar}; 4572445Sassar 4672445Sassarstatic void 47178825Sdfrfree_retv(struct acl_field *acl) 4872445Sassar{ 49178825Sdfr while(acl != NULL) { 50178825Sdfr if (acl->type == acl_retval) { 51178825Sdfr if (*acl->u.retv) 52178825Sdfr free(*acl->u.retv); 53178825Sdfr *acl->u.retv = NULL; 54178825Sdfr } 55178825Sdfr acl = acl->next; 56178825Sdfr } 57178825Sdfr} 58178825Sdfr 59178825Sdfrstatic void 60178825Sdfracl_free_list(struct acl_field *acl, int retv) 61178825Sdfr{ 6272445Sassar struct acl_field *next; 63178825Sdfr if (retv) 64178825Sdfr free_retv(acl); 6572445Sassar while(acl != NULL) { 6672445Sassar next = acl->next; 6772445Sassar free(acl); 6872445Sassar acl = next; 6972445Sassar } 7072445Sassar} 7172445Sassar 7272445Sassarstatic krb5_error_code 7372445Sassaracl_parse_format(krb5_context context, 7472445Sassar struct acl_field **acl_ret, 7572445Sassar const char *format, 7672445Sassar va_list ap) 7772445Sassar{ 7872445Sassar const char *p; 7972445Sassar struct acl_field *acl = NULL, *tmp; 8072445Sassar 8172445Sassar for(p = format; *p != '\0'; p++) { 8272445Sassar tmp = malloc(sizeof(*tmp)); 8372445Sassar if(tmp == NULL) { 84233294Sstas krb5_set_error_message(context, ENOMEM, 85233294Sstas N_("malloc: out of memory", "")); 86178825Sdfr acl_free_list(acl, 0); 8772445Sassar return ENOMEM; 8872445Sassar } 8972445Sassar if(*p == 's') { 9072445Sassar tmp->type = acl_string; 9172445Sassar tmp->u.cstr = va_arg(ap, const char*); 9272445Sassar } else if(*p == 'f') { 9372445Sassar tmp->type = acl_fnmatch; 9472445Sassar tmp->u.cstr = va_arg(ap, const char*); 9572445Sassar } else if(*p == 'r') { 9672445Sassar tmp->type = acl_retval; 9772445Sassar tmp->u.retv = va_arg(ap, char **); 98178825Sdfr *tmp->u.retv = NULL; 99178825Sdfr } else { 100233294Sstas krb5_set_error_message(context, EINVAL, 101233294Sstas N_("Unknown format specifier %c while " 102233294Sstas "parsing ACL", "specifier"), *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; 183233294Sstas * 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 201233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 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 { 223233294Sstas krb5_set_error_message(context, EACCES, N_("ACL did not match", "")); 22478527Sassar return EACCES; 22578527Sassar } 22672445Sassar} 227233294Sstas 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 244233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 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; 260233294Sstas rk_strerror_r(save_errno, buf, sizeof(buf)); 261233294Sstas krb5_set_error_message(context, save_errno, 262233294Sstas N_("open(%s): %s", "file, errno"), 263233294Sstas file, buf); 26478527Sassar return save_errno; 26578527Sassar } 266233294Sstas rk_cloexec_file(f); 26778527Sassar 26872445Sassar va_start(ap, format); 26972445Sassar ret = acl_parse_format(context, &acl, format, ap); 27072445Sassar va_end(ap); 27172445Sassar if(ret) { 27272445Sassar fclose(f); 27372445Sassar return ret; 27472445Sassar } 27572445Sassar 27678527Sassar found = FALSE; 27772445Sassar while(fgets(buf, sizeof(buf), f)) { 27872445Sassar if(buf[0] == '#') 27972445Sassar continue; 28072445Sassar if(acl_match_acl(context, acl, buf)) { 28178527Sassar found = TRUE; 28278527Sassar break; 28372445Sassar } 284178825Sdfr free_retv(acl); 28572445Sassar } 28672445Sassar 28772445Sassar fclose(f); 288178825Sdfr acl_free_list(acl, !found); 28978527Sassar if (found) { 29078527Sassar return 0; 29178527Sassar } else { 292233294Sstas krb5_set_error_message(context, EACCES, N_("ACL did not match", "")); 29378527Sassar return EACCES; 29478527Sassar } 29572445Sassar} 296