]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - lib/libc/posix1e/mac.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / lib / libc / posix1e / mac.c
1 /*
2  * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson
3  * Copyright (c) 2002, 2003 Networks Associates Technology, Inc.
4  * All rights reserved.
5  *
6  * This software was developed by Robert Watson for the TrustedBSD Project.
7  *
8  * This software was developed for the FreeBSD Project in part by Network
9  * Associates Laboratories, the Security Research Division of Network
10  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
11  * as part of the DARPA CHATS research program.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 #include <sys/types.h>
39 #include <sys/queue.h>
40 #include <sys/sysctl.h>
41
42 #include <dlfcn.h>
43 #include <errno.h>
44 #include <limits.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include <sys/mac.h>
51
52 static int      internal_initialized;
53
54 /*
55  * Maintain a list of default label preparations for various object
56  * types.  Each name will appear only once in the list.
57  *
58  * XXXMAC: Not thread-safe.
59  */
60 static LIST_HEAD(, label_default) label_default_head;
61 struct label_default {
62         char                            *ld_name;
63         char                            *ld_labels;
64         LIST_ENTRY(label_default)        ld_entries;
65 };
66
67 static void
68 mac_destroy_labels(void)
69 {
70         struct label_default *ld;
71
72         while ((ld = LIST_FIRST(&label_default_head))) {
73                 free(ld->ld_name);
74                 free(ld->ld_labels);
75                 LIST_REMOVE(ld, ld_entries);
76                 free(ld);
77         }
78 }
79
80 static void
81 mac_destroy_internal(void)
82 {
83
84         mac_destroy_labels();
85
86         internal_initialized = 0;
87 }
88
89 static int
90 mac_add_type(const char *name, const char *labels)
91 {
92         struct label_default *ld, *ld_new;
93         char *name_dup, *labels_dup;
94
95         /*
96          * Speculatively allocate all the memory now to avoid allocating
97          * later when we will someday hold a mutex.
98          */
99         name_dup = strdup(name);
100         if (name_dup == NULL) {
101                 errno = ENOMEM;
102                 return (-1);
103         }
104         labels_dup = strdup(labels);
105         if (labels_dup == NULL) {
106                 free(name_dup);
107                 errno = ENOMEM;
108                 return (-1);
109         }
110         ld_new = malloc(sizeof(*ld));
111         if (ld_new == NULL) {
112                 free(name_dup);
113                 free(labels_dup);
114                 errno = ENOMEM;
115                 return (-1);
116         }
117
118         /*
119          * If the type is already present, replace the current entry
120          * rather than add a new instance.
121          */
122         for (ld = LIST_FIRST(&label_default_head); ld != NULL;
123             ld = LIST_NEXT(ld, ld_entries)) {
124                 if (strcmp(name, ld->ld_name) == 0)
125                         break;
126         }
127
128         if (ld != NULL) {
129                 free(ld->ld_labels);
130                 ld->ld_labels = labels_dup;
131                 labels_dup = NULL;
132         } else {
133                 ld = ld_new;
134                 ld->ld_name = name_dup;
135                 ld->ld_labels = labels_dup;
136
137                 ld_new = NULL;
138                 name_dup = NULL;
139                 labels_dup = NULL;
140
141                 LIST_INSERT_HEAD(&label_default_head, ld, ld_entries);
142         }
143
144         if (name_dup != NULL)
145                 free(name_dup);
146         if (labels_dup != NULL)
147                 free(labels_dup);
148         if (ld_new != NULL)
149                 free(ld_new);
150
151         return (0);
152 }
153
154 static char *
155 next_token(char **string)
156 {
157         char *token;
158
159         token = strsep(string, " \t");
160         while (token != NULL && *token == '\0')
161                 token = strsep(string, " \t");
162
163         return (token);
164 }
165
166 static int
167 mac_init_internal(int ignore_errors)
168 {
169         const char *filename;
170         char line[LINE_MAX];
171         FILE *file;
172         int error;
173
174         error = 0;
175
176         LIST_INIT(&label_default_head);
177
178         if (!issetugid() && getenv("MAC_CONFFILE") != NULL)
179                 filename = getenv("MAC_CONFFILE");
180         else
181                 filename = MAC_CONFFILE;
182         file = fopen(filename, "r");
183         if (file == NULL)
184                 return (0);
185
186         while (fgets(line, LINE_MAX, file)) {
187                 char *comment, *parse, *statement;
188
189                 if (line[strlen(line)-1] == '\n')
190                         line[strlen(line)-1] = '\0';
191                 else {
192                         if (ignore_errors)
193                                 continue;
194                         fclose(file);
195                         error = EINVAL;
196                         goto just_return;
197                 }
198
199                 /* Remove any comment. */
200                 comment = line;
201                 parse = strsep(&comment, "#");
202
203                 /* Blank lines OK. */
204                 statement = next_token(&parse);
205                 if (statement == NULL)
206                         continue;
207
208                 if (strcmp(statement, "default_labels") == 0) {
209                         char *name, *labels;
210
211                         name = next_token(&parse);
212                         labels = next_token(&parse);
213                         if (name == NULL || labels == NULL ||
214                             next_token(&parse) != NULL) {
215                                 if (ignore_errors)
216                                         continue;
217                                 error = EINVAL;
218                                 fclose(file);
219                                 goto just_return;
220                         }
221
222                         if (mac_add_type(name, labels) == -1) {
223                                 if (ignore_errors)
224                                         continue;
225                                 fclose(file);
226                                 goto just_return;
227                         }
228                 } else if (strcmp(statement, "default_ifnet_labels") == 0 ||
229                     strcmp(statement, "default_file_labels") == 0 ||
230                     strcmp(statement, "default_process_labels") == 0) {
231                         char *labels, *type;
232
233                         if (strcmp(statement, "default_ifnet_labels") == 0)
234                                 type = "ifnet";
235                         else if (strcmp(statement, "default_file_labels") == 0)
236                                 type = "file";
237                         else if (strcmp(statement, "default_process_labels") ==
238                             0)
239                                 type = "process";
240
241                         labels = next_token(&parse);
242                         if (labels == NULL || next_token(&parse) != NULL) {
243                                 if (ignore_errors)
244                                         continue;
245                                 error = EINVAL;
246                                 fclose(file);
247                                 goto just_return;
248                         }
249
250                         if (mac_add_type(type, labels) == -1) {
251                                 if (ignore_errors)
252                                         continue;
253                                 fclose(file);
254                                 goto just_return;
255                         }
256                 } else {
257                         if (ignore_errors)
258                                 continue;
259                         fclose(file);
260                         error = EINVAL;
261                         goto just_return;
262                 }
263         }
264
265         fclose(file);
266
267         internal_initialized = 1;
268
269 just_return:
270         if (error != 0)
271                 mac_destroy_internal();
272         return (error);
273 }
274
275 static int
276 mac_maybe_init_internal(void)
277 {
278
279         if (!internal_initialized)
280                 return (mac_init_internal(1));
281         else
282                 return (0);
283 }
284
285 int
286 mac_reload(void)
287 {
288
289         if (internal_initialized)
290                 mac_destroy_internal();
291         return (mac_init_internal(0));
292 }
293
294 int
295 mac_free(struct mac *mac)
296 {
297
298         if (mac->m_string != NULL)
299                 free(mac->m_string);
300         free(mac);
301
302         return (0);
303 }
304
305 int
306 mac_from_text(struct mac **mac, const char *text)
307 {
308
309         *mac = (struct mac *) malloc(sizeof(**mac));
310         if (*mac == NULL)
311                 return (ENOMEM);
312
313         (*mac)->m_string = strdup(text);
314         if ((*mac)->m_string == NULL) {
315                 free(*mac);
316                 *mac = NULL;
317                 return (ENOMEM);
318         }
319
320         (*mac)->m_buflen = strlen((*mac)->m_string)+1;
321
322         return (0);
323 }
324
325 int
326 mac_to_text(struct mac *mac, char **text)
327 {
328
329         *text = strdup(mac->m_string);
330         if (*text == NULL)
331                 return (ENOMEM);
332         return (0);
333 }
334
335 int
336 mac_prepare(struct mac **mac, const char *elements)
337 {
338
339         if (strlen(elements) >= MAC_MAX_LABEL_BUF_LEN)
340                 return (EINVAL);
341
342         *mac = (struct mac *) malloc(sizeof(**mac));
343         if (*mac == NULL)
344                 return (ENOMEM);
345
346         (*mac)->m_string = malloc(MAC_MAX_LABEL_BUF_LEN);
347         if ((*mac)->m_string == NULL) {
348                 free(*mac);
349                 *mac = NULL;
350                 return (ENOMEM);
351         }
352
353         strcpy((*mac)->m_string, elements);
354         (*mac)->m_buflen = MAC_MAX_LABEL_BUF_LEN;
355
356         return (0);
357 }
358
359 int
360 mac_prepare_type(struct mac **mac, const char *name)
361 {
362         struct label_default *ld;
363         int error;
364
365         error = mac_maybe_init_internal();
366         if (error != 0)
367                 return (error);
368
369         for (ld = LIST_FIRST(&label_default_head); ld != NULL;
370             ld = LIST_NEXT(ld, ld_entries)) {
371                 if (strcmp(name, ld->ld_name) == 0)
372                         return (mac_prepare(mac, ld->ld_labels));
373         }
374
375         errno = ENOENT;
376         return (-1);            /* XXXMAC: ENOLABEL */
377 }
378
379 int
380 mac_prepare_ifnet_label(struct mac **mac)
381 {
382
383         return (mac_prepare_type(mac, "ifnet"));
384 }
385
386 int
387 mac_prepare_file_label(struct mac **mac)
388 {
389
390         return (mac_prepare_type(mac, "file"));
391 }
392
393 int
394 mac_prepare_packet_label(struct mac **mac)
395 {
396
397         return (mac_prepare_type(mac, "packet"));
398 }
399
400 int
401 mac_prepare_process_label(struct mac **mac)
402 {
403
404         return (mac_prepare_type(mac, "process"));
405 }
406
407 /*
408  * Simply test whether the TrustedBSD/MAC MIB tree is present; if so,
409  * return 1 to indicate that the system has MAC enabled overall or for
410  * a given policy.
411  */
412 int
413 mac_is_present(const char *policyname)
414 {
415         int mib[5];
416         size_t siz;
417         char *mibname;
418         int error;
419
420         if (policyname != NULL) {
421                 if (policyname[strcspn(policyname, ".=")] != '\0') {
422                         errno = EINVAL;
423                         return (-1);
424                 }
425                 mibname = malloc(sizeof("security.mac.") - 1 +
426                     strlen(policyname) + sizeof(".enabled"));
427                 if (mibname == NULL)
428                         return (-1);
429                 strcpy(mibname, "security.mac.");
430                 strcat(mibname, policyname);
431                 strcat(mibname, ".enabled");
432                 siz = 5;
433                 error = sysctlnametomib(mibname, mib, &siz);
434                 free(mibname);
435         } else {
436                 siz = 3;
437                 error = sysctlnametomib("security.mac", mib, &siz);
438         }
439         if (error == -1) {
440                 switch (errno) {
441                 case ENOTDIR:
442                 case ENOENT:
443                         return (0);
444                 default:
445                         return (error);
446                 }
447         }
448         return (1);
449 }