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