Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/lib/krb5/acl.c
34878 views
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
297