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