xref: /minix3/crypto/external/bsd/heimdal/dist/lib/krb5/acl.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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