/*1* Copyright (c) 2000 - 2002, 2004 Kungliga Tekniska Högskolan2* (Royal Institute of Technology, Stockholm, Sweden).3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8*9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* 3. Neither the name of the Institute nor the names of its contributors17* may be used to endorse or promote products derived from this software18* without specific prior written permission.19*20* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*/3233#include "krb5_locl.h"34#include <fnmatch.h>3536struct acl_field {37enum { acl_string, acl_fnmatch, acl_retval } type;38union {39const char *cstr;40char **retv;41} u;42struct acl_field *next, **last;43};4445static void46free_retv(struct acl_field *acl)47{48while(acl != NULL) {49if (acl->type == acl_retval) {50if (*acl->u.retv)51free(*acl->u.retv);52*acl->u.retv = NULL;53}54acl = acl->next;55}56}5758static void59acl_free_list(struct acl_field *acl, int retv)60{61struct acl_field *next;62if (retv)63free_retv(acl);64while(acl != NULL) {65next = acl->next;66free(acl);67acl = next;68}69}7071static krb5_error_code72acl_parse_format(krb5_context context,73struct acl_field **acl_ret,74const char *format,75va_list ap)76{77const char *p;78struct acl_field *acl = NULL, *tmp;7980for(p = format; *p != '\0'; p++) {81tmp = malloc(sizeof(*tmp));82if(tmp == NULL) {83krb5_set_error_message(context, ENOMEM,84N_("malloc: out of memory", ""));85acl_free_list(acl, 0);86return ENOMEM;87}88if(*p == 's') {89tmp->type = acl_string;90tmp->u.cstr = va_arg(ap, const char*);91} else if(*p == 'f') {92tmp->type = acl_fnmatch;93tmp->u.cstr = va_arg(ap, const char*);94} else if(*p == 'r') {95tmp->type = acl_retval;96tmp->u.retv = va_arg(ap, char **);97*tmp->u.retv = NULL;98} else {99krb5_set_error_message(context, EINVAL,100N_("Unknown format specifier %c while "101"parsing ACL", "specifier"), *p);102acl_free_list(acl, 0);103free(tmp);104return EINVAL;105}106tmp->next = NULL;107if(acl == NULL)108acl = tmp;109else110*acl->last = tmp;111acl->last = &tmp->next;112}113*acl_ret = acl;114return 0;115}116117static krb5_boolean118acl_match_field(krb5_context context,119const char *string,120struct acl_field *field)121{122if(field->type == acl_string) {123return !strcmp(field->u.cstr, string);124} else if(field->type == acl_fnmatch) {125return !fnmatch(field->u.cstr, string, 0);126} else if(field->type == acl_retval) {127*field->u.retv = strdup(string);128return TRUE;129}130return FALSE;131}132133static krb5_boolean134acl_match_acl(krb5_context context,135struct acl_field *acl,136const char *string)137{138char buf[256];139while(strsep_copy(&string, " \t", buf, sizeof(buf)) != -1) {140if(buf[0] == '\0')141continue; /* skip ws */142if (acl == NULL)143return FALSE;144if(!acl_match_field(context, buf, acl)) {145return FALSE;146}147acl = acl->next;148}149if (acl)150return FALSE;151return TRUE;152}153154/**155* krb5_acl_match_string matches ACL format against a string.156*157* The ACL format has three format specifiers: s, f, and r. Each158* specifier will retrieve one argument from the variable arguments159* for either matching or storing data. The input string is split up160* using " " (space) and "\t" (tab) as a delimiter; multiple and "\t"161* in a row are considered to be the same.162*163* List of format specifiers:164* - s Matches a string using strcmp(3) (case sensitive).165* - f Matches the string with fnmatch(3). Theflags166* argument (the last argument) passed to the fnmatch function is 0.167* - r Returns a copy of the string in the char ** passed in; the copy168* must be freed with free(3). There is no need to free(3) the169* string on error: the function will clean up and set the pointer170* to NULL.171*172* @param context Kerberos 5 context173* @param string string to match with174* @param format format to match175* @param ... parameter to format string176*177* @return Return an error code or 0.178*179*180* @code181* char *s;182*183* ret = krb5_acl_match_string(context, "foo", "s", "foo");184* if (ret)185* krb5_errx(context, 1, "acl didn't match");186* ret = krb5_acl_match_string(context, "foo foo baz/kaka",187* "ss", "foo", &s, "foo/\\*");188* if (ret) {189* // no need to free(s) on error190* assert(s == NULL);191* krb5_errx(context, 1, "acl didn't match");192* }193* free(s);194* @endcode195*196* @sa krb5_acl_match_file197* @ingroup krb5_support198*/199200KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL201krb5_acl_match_string(krb5_context context,202const char *string,203const char *format,204...)205{206krb5_error_code ret;207krb5_boolean found;208struct acl_field *acl;209210va_list ap;211va_start(ap, format);212ret = acl_parse_format(context, &acl, format, ap);213va_end(ap);214if(ret)215return ret;216217found = acl_match_acl(context, acl, string);218acl_free_list(acl, !found);219if (found) {220return 0;221} else {222krb5_set_error_message(context, EACCES, N_("ACL did not match", ""));223return EACCES;224}225}226227/**228* krb5_acl_match_file matches ACL format against each line in a file229* using krb5_acl_match_string(). Lines starting with # are treated230* like comments and ignored.231*232* @param context Kerberos 5 context.233* @param file file with acl listed in the file.234* @param format format to match.235* @param ... parameter to format string.236*237* @return Return an error code or 0.238*239* @sa krb5_acl_match_string240* @ingroup krb5_support241*/242243KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL244krb5_acl_match_file(krb5_context context,245const char *file,246const char *format,247...)248{249krb5_error_code ret;250struct acl_field *acl = NULL;251char buf[256];252va_list ap;253FILE *f;254krb5_boolean found;255256f = fopen(file, "r");257if(f == NULL) {258int save_errno = errno;259rk_strerror_r(save_errno, buf, sizeof(buf));260krb5_set_error_message(context, save_errno,261N_("open(%s): %s", "file, errno"),262file, buf);263return save_errno;264}265rk_cloexec_file(f);266267va_start(ap, format);268ret = acl_parse_format(context, &acl, format, ap);269va_end(ap);270if(ret) {271fclose(f);272return ret;273}274275found = FALSE;276while(fgets(buf, sizeof(buf), f)) {277if(buf[0] == '#')278continue;279if(acl_match_acl(context, acl, buf)) {280found = TRUE;281break;282}283free_retv(acl);284}285286fclose(f);287acl_free_list(acl, !found);288if (found) {289return 0;290} else {291krb5_set_error_message(context, EACCES, N_("ACL did not match", ""));292return EACCES;293}294}295296297