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