1 /* $NetBSD: acl.c,v 1.1.1.2 2014/04/24 12:45:49 pettai Exp $ */ 2 3 /* 4 * Copyright (c) 2000 - 2002, 2004 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "krb5_locl.h" 37 #include <fnmatch.h> 38 39 struct acl_field { 40 enum { acl_string, acl_fnmatch, acl_retval } type; 41 union { 42 const char *cstr; 43 char **retv; 44 } u; 45 struct acl_field *next, **last; 46 }; 47 48 static void 49 free_retv(struct acl_field *acl) 50 { 51 while(acl != NULL) { 52 if (acl->type == acl_retval) { 53 if (*acl->u.retv) 54 free(*acl->u.retv); 55 *acl->u.retv = NULL; 56 } 57 acl = acl->next; 58 } 59 } 60 61 static void 62 acl_free_list(struct acl_field *acl, int retv) 63 { 64 struct acl_field *next; 65 if (retv) 66 free_retv(acl); 67 while(acl != NULL) { 68 next = acl->next; 69 free(acl); 70 acl = next; 71 } 72 } 73 74 static krb5_error_code 75 acl_parse_format(krb5_context context, 76 struct acl_field **acl_ret, 77 const char *format, 78 va_list ap) 79 { 80 const char *p; 81 struct acl_field *acl = NULL, *tmp; 82 83 for(p = format; *p != '\0'; p++) { 84 tmp = malloc(sizeof(*tmp)); 85 if(tmp == NULL) { 86 krb5_set_error_message(context, ENOMEM, 87 N_("malloc: out of memory", "")); 88 acl_free_list(acl, 0); 89 return ENOMEM; 90 } 91 if(*p == 's') { 92 tmp->type = acl_string; 93 tmp->u.cstr = va_arg(ap, const char*); 94 } else if(*p == 'f') { 95 tmp->type = acl_fnmatch; 96 tmp->u.cstr = va_arg(ap, const char*); 97 } else if(*p == 'r') { 98 tmp->type = acl_retval; 99 tmp->u.retv = va_arg(ap, char **); 100 *tmp->u.retv = NULL; 101 } else { 102 krb5_set_error_message(context, EINVAL, 103 N_("Unknown format specifier %c while " 104 "parsing ACL", "specifier"), *p); 105 acl_free_list(acl, 0); 106 free(tmp); 107 return EINVAL; 108 } 109 tmp->next = NULL; 110 if(acl == NULL) 111 acl = tmp; 112 else 113 *acl->last = tmp; 114 acl->last = &tmp->next; 115 } 116 *acl_ret = acl; 117 return 0; 118 } 119 120 static krb5_boolean 121 acl_match_field(krb5_context context, 122 const char *string, 123 struct acl_field *field) 124 { 125 if(field->type == acl_string) { 126 return !strcmp(field->u.cstr, string); 127 } else if(field->type == acl_fnmatch) { 128 return !fnmatch(field->u.cstr, string, 0); 129 } else if(field->type == acl_retval) { 130 *field->u.retv = strdup(string); 131 return TRUE; 132 } 133 return FALSE; 134 } 135 136 static krb5_boolean 137 acl_match_acl(krb5_context context, 138 struct acl_field *acl, 139 const char *string) 140 { 141 char buf[256]; 142 while(strsep_copy(&string, " \t", buf, sizeof(buf)) != -1) { 143 if(buf[0] == '\0') 144 continue; /* skip ws */ 145 if (acl == NULL) 146 return FALSE; 147 if(!acl_match_field(context, buf, acl)) { 148 return FALSE; 149 } 150 acl = acl->next; 151 } 152 if (acl) 153 return FALSE; 154 return TRUE; 155 } 156 157 /** 158 * krb5_acl_match_string matches ACL format against a string. 159 * 160 * The ACL format has three format specifiers: s, f, and r. Each 161 * specifier will retrieve one argument from the variable arguments 162 * for either matching or storing data. The input string is split up 163 * using " " (space) and "\t" (tab) as a delimiter; multiple and "\t" 164 * in a row are considered to be the same. 165 * 166 * List of format specifiers: 167 * - s Matches a string using strcmp(3) (case sensitive). 168 * - f Matches the string with fnmatch(3). Theflags 169 * argument (the last argument) passed to the fnmatch function is 0. 170 * - r Returns a copy of the string in the char ** passed in; the copy 171 * must be freed with free(3). There is no need to free(3) the 172 * string on error: the function will clean up and set the pointer 173 * to NULL. 174 * 175 * @param context Kerberos 5 context 176 * @param string string to match with 177 * @param format format to match 178 * @param ... parameter to format string 179 * 180 * @return Return an error code or 0. 181 * 182 * 183 * @code 184 * char *s; 185 * 186 * ret = krb5_acl_match_string(context, "foo", "s", "foo"); 187 * if (ret) 188 * krb5_errx(context, 1, "acl didn't match"); 189 * ret = krb5_acl_match_string(context, "foo foo baz/kaka", 190 * "ss", "foo", &s, "foo/\\*"); 191 * if (ret) { 192 * // no need to free(s) on error 193 * assert(s == NULL); 194 * krb5_errx(context, 1, "acl didn't match"); 195 * } 196 * free(s); 197 * @endcode 198 * 199 * @sa krb5_acl_match_file 200 * @ingroup krb5_support 201 */ 202 203 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 204 krb5_acl_match_string(krb5_context context, 205 const char *string, 206 const char *format, 207 ...) 208 { 209 krb5_error_code ret; 210 krb5_boolean found; 211 struct acl_field *acl; 212 213 va_list ap; 214 va_start(ap, format); 215 ret = acl_parse_format(context, &acl, format, ap); 216 va_end(ap); 217 if(ret) 218 return ret; 219 220 found = acl_match_acl(context, acl, string); 221 acl_free_list(acl, !found); 222 if (found) { 223 return 0; 224 } else { 225 krb5_set_error_message(context, EACCES, N_("ACL did not match", "")); 226 return EACCES; 227 } 228 } 229 230 /** 231 * krb5_acl_match_file matches ACL format against each line in a file 232 * using krb5_acl_match_string(). Lines starting with # are treated 233 * like comments and ignored. 234 * 235 * @param context Kerberos 5 context. 236 * @param file file with acl listed in the file. 237 * @param format format to match. 238 * @param ... parameter to format string. 239 * 240 * @return Return an error code or 0. 241 * 242 * @sa krb5_acl_match_string 243 * @ingroup krb5_support 244 */ 245 246 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 247 krb5_acl_match_file(krb5_context context, 248 const char *file, 249 const char *format, 250 ...) 251 { 252 krb5_error_code ret; 253 struct acl_field *acl; 254 char buf[256]; 255 va_list ap; 256 FILE *f; 257 krb5_boolean found; 258 259 f = fopen(file, "r"); 260 if(f == NULL) { 261 int save_errno = errno; 262 rk_strerror_r(save_errno, buf, sizeof(buf)); 263 krb5_set_error_message(context, save_errno, 264 N_("open(%s): %s", "file, errno"), 265 file, buf); 266 return save_errno; 267 } 268 rk_cloexec_file(f); 269 270 va_start(ap, format); 271 ret = acl_parse_format(context, &acl, format, ap); 272 va_end(ap); 273 if(ret) { 274 fclose(f); 275 return ret; 276 } 277 278 found = FALSE; 279 while(fgets(buf, sizeof(buf), f)) { 280 if(buf[0] == '#') 281 continue; 282 if(acl_match_acl(context, acl, buf)) { 283 found = TRUE; 284 break; 285 } 286 free_retv(acl); 287 } 288 289 fclose(f); 290 acl_free_list(acl, !found); 291 if (found) { 292 return 0; 293 } else { 294 krb5_set_error_message(context, EACCES, N_("ACL did not match", "")); 295 return EACCES; 296 } 297 } 298