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