1d915a14eSPedro F. Giffuni /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3d915a14eSPedro F. Giffuni *
4391b1d75SRobert Watson * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson
5738824adSRobert Watson * Copyright (c) 2002, 2003 Networks Associates Technology, Inc.
6391b1d75SRobert Watson * All rights reserved.
7391b1d75SRobert Watson *
8391b1d75SRobert Watson * This software was developed by Robert Watson for the TrustedBSD Project.
9391b1d75SRobert Watson *
10f8d08150SRobert Watson * This software was developed for the FreeBSD Project in part by Network
11f8d08150SRobert Watson * Associates Laboratories, the Security Research Division of Network
12f8d08150SRobert Watson * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
13f8d08150SRobert Watson * as part of the DARPA CHATS research program.
14391b1d75SRobert Watson *
15391b1d75SRobert Watson * Redistribution and use in source and binary forms, with or without
16391b1d75SRobert Watson * modification, are permitted provided that the following conditions
17391b1d75SRobert Watson * are met:
18391b1d75SRobert Watson * 1. Redistributions of source code must retain the above copyright
19391b1d75SRobert Watson * notice, this list of conditions and the following disclaimer.
20391b1d75SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright
21391b1d75SRobert Watson * notice, this list of conditions and the following disclaimer in the
22391b1d75SRobert Watson * documentation and/or other materials provided with the distribution.
23391b1d75SRobert Watson *
24391b1d75SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25391b1d75SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26391b1d75SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27391b1d75SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28391b1d75SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29391b1d75SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30391b1d75SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31391b1d75SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32391b1d75SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33391b1d75SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34391b1d75SRobert Watson * SUCH DAMAGE.
35391b1d75SRobert Watson */
36391b1d75SRobert Watson
37391b1d75SRobert Watson #include <sys/types.h>
38391b1d75SRobert Watson #include <sys/queue.h>
39391b1d75SRobert Watson #include <sys/sysctl.h>
40391b1d75SRobert Watson
41391b1d75SRobert Watson #include <dlfcn.h>
42391b1d75SRobert Watson #include <errno.h>
43688dfe45SGarrett Wollman #include <limits.h>
44391b1d75SRobert Watson #include <stdio.h>
45391b1d75SRobert Watson #include <stdlib.h>
46391b1d75SRobert Watson #include <string.h>
4784d9142fSJacques Vidrine #include <unistd.h>
48391b1d75SRobert Watson
49391b1d75SRobert Watson #include <sys/mac.h>
50391b1d75SRobert Watson
51391b1d75SRobert Watson static int internal_initialized;
52391b1d75SRobert Watson
53738824adSRobert Watson /*
54738824adSRobert Watson * Maintain a list of default label preparations for various object
55738824adSRobert Watson * types. Each name will appear only once in the list.
56738824adSRobert Watson *
57738824adSRobert Watson * XXXMAC: Not thread-safe.
58738824adSRobert Watson */
59a2f046e8SRobert Watson static LIST_HEAD(, label_default) label_default_head;
60738824adSRobert Watson struct label_default {
61738824adSRobert Watson char *ld_name;
62738824adSRobert Watson char *ld_labels;
63738824adSRobert Watson LIST_ENTRY(label_default) ld_entries;
64738824adSRobert Watson };
65391b1d75SRobert Watson
66391b1d75SRobert Watson static void
mac_destroy_labels(void)67391b1d75SRobert Watson mac_destroy_labels(void)
68391b1d75SRobert Watson {
69738824adSRobert Watson struct label_default *ld;
70391b1d75SRobert Watson
71738824adSRobert Watson while ((ld = LIST_FIRST(&label_default_head))) {
72738824adSRobert Watson free(ld->ld_name);
73738824adSRobert Watson free(ld->ld_labels);
74738824adSRobert Watson LIST_REMOVE(ld, ld_entries);
75738824adSRobert Watson free(ld);
76391b1d75SRobert Watson }
77391b1d75SRobert Watson }
78391b1d75SRobert Watson
79391b1d75SRobert Watson static void
mac_destroy_internal(void)80391b1d75SRobert Watson mac_destroy_internal(void)
81391b1d75SRobert Watson {
82391b1d75SRobert Watson
83391b1d75SRobert Watson mac_destroy_labels();
84391b1d75SRobert Watson
85391b1d75SRobert Watson internal_initialized = 0;
86391b1d75SRobert Watson }
87391b1d75SRobert Watson
88391b1d75SRobert Watson static int
mac_add_type(const char * name,const char * labels)89738824adSRobert Watson mac_add_type(const char *name, const char *labels)
90391b1d75SRobert Watson {
91738824adSRobert Watson struct label_default *ld, *ld_new;
92738824adSRobert Watson char *name_dup, *labels_dup;
93738824adSRobert Watson
94738824adSRobert Watson /*
95738824adSRobert Watson * Speculatively allocate all the memory now to avoid allocating
96738824adSRobert Watson * later when we will someday hold a mutex.
97738824adSRobert Watson */
98738824adSRobert Watson name_dup = strdup(name);
99738824adSRobert Watson if (name_dup == NULL) {
100738824adSRobert Watson errno = ENOMEM;
101738824adSRobert Watson return (-1);
102738824adSRobert Watson }
103738824adSRobert Watson labels_dup = strdup(labels);
104738824adSRobert Watson if (labels_dup == NULL) {
105738824adSRobert Watson free(name_dup);
106738824adSRobert Watson errno = ENOMEM;
107738824adSRobert Watson return (-1);
108738824adSRobert Watson }
109738824adSRobert Watson ld_new = malloc(sizeof(*ld));
110738824adSRobert Watson if (ld_new == NULL) {
111738824adSRobert Watson free(name_dup);
112738824adSRobert Watson free(labels_dup);
113738824adSRobert Watson errno = ENOMEM;
114738824adSRobert Watson return (-1);
115738824adSRobert Watson }
116738824adSRobert Watson
117738824adSRobert Watson /*
118738824adSRobert Watson * If the type is already present, replace the current entry
119738824adSRobert Watson * rather than add a new instance.
120738824adSRobert Watson */
121738824adSRobert Watson for (ld = LIST_FIRST(&label_default_head); ld != NULL;
122738824adSRobert Watson ld = LIST_NEXT(ld, ld_entries)) {
123738824adSRobert Watson if (strcmp(name, ld->ld_name) == 0)
124738824adSRobert Watson break;
125738824adSRobert Watson }
126738824adSRobert Watson
127738824adSRobert Watson if (ld != NULL) {
128738824adSRobert Watson free(ld->ld_labels);
129738824adSRobert Watson ld->ld_labels = labels_dup;
130738824adSRobert Watson labels_dup = NULL;
131738824adSRobert Watson } else {
132738824adSRobert Watson ld = ld_new;
133738824adSRobert Watson ld->ld_name = name_dup;
134738824adSRobert Watson ld->ld_labels = labels_dup;
135738824adSRobert Watson
136738824adSRobert Watson ld_new = NULL;
137738824adSRobert Watson name_dup = NULL;
138738824adSRobert Watson labels_dup = NULL;
139738824adSRobert Watson
140738824adSRobert Watson LIST_INSERT_HEAD(&label_default_head, ld, ld_entries);
141738824adSRobert Watson }
142738824adSRobert Watson
143738824adSRobert Watson if (name_dup != NULL)
144738824adSRobert Watson free(name_dup);
145738824adSRobert Watson if (labels_dup != NULL)
146738824adSRobert Watson free(labels_dup);
147738824adSRobert Watson if (ld_new != NULL)
148738824adSRobert Watson free(ld_new);
149738824adSRobert Watson
150738824adSRobert Watson return (0);
151738824adSRobert Watson }
152738824adSRobert Watson
153738824adSRobert Watson static char *
next_token(char ** string)154738824adSRobert Watson next_token(char **string)
155738824adSRobert Watson {
156738824adSRobert Watson char *token;
157738824adSRobert Watson
158738824adSRobert Watson token = strsep(string, " \t");
159738824adSRobert Watson while (token != NULL && *token == '\0')
160738824adSRobert Watson token = strsep(string, " \t");
161738824adSRobert Watson
162738824adSRobert Watson return (token);
163738824adSRobert Watson }
164738824adSRobert Watson
165738824adSRobert Watson static int
mac_init_internal(int ignore_errors)166738824adSRobert Watson mac_init_internal(int ignore_errors)
167738824adSRobert Watson {
168738824adSRobert Watson const char *filename;
169391b1d75SRobert Watson char line[LINE_MAX];
170738824adSRobert Watson FILE *file;
171391b1d75SRobert Watson int error;
172391b1d75SRobert Watson
173391b1d75SRobert Watson error = 0;
174391b1d75SRobert Watson
175738824adSRobert Watson LIST_INIT(&label_default_head);
176738824adSRobert Watson
17768ca8363SMark Johnston filename = secure_getenv("MAC_CONFFILE");
17868ca8363SMark Johnston if (filename == NULL)
179738824adSRobert Watson filename = MAC_CONFFILE;
180a93705b0SJilles Tjoelker file = fopen(filename, "re");
181391b1d75SRobert Watson if (file == NULL)
182391b1d75SRobert Watson return (0);
183391b1d75SRobert Watson
184391b1d75SRobert Watson while (fgets(line, LINE_MAX, file)) {
18584d9142fSJacques Vidrine char *comment, *parse, *statement;
186391b1d75SRobert Watson
187391b1d75SRobert Watson if (line[strlen(line)-1] == '\n')
188391b1d75SRobert Watson line[strlen(line)-1] = '\0';
189391b1d75SRobert Watson else {
190738824adSRobert Watson if (ignore_errors)
191738824adSRobert Watson continue;
192391b1d75SRobert Watson fclose(file);
193391b1d75SRobert Watson error = EINVAL;
194391b1d75SRobert Watson goto just_return;
195391b1d75SRobert Watson }
196391b1d75SRobert Watson
197738824adSRobert Watson /* Remove any comment. */
198738824adSRobert Watson comment = line;
199738824adSRobert Watson parse = strsep(&comment, "#");
200391b1d75SRobert Watson
201738824adSRobert Watson /* Blank lines OK. */
202738824adSRobert Watson statement = next_token(&parse);
203738824adSRobert Watson if (statement == NULL)
204391b1d75SRobert Watson continue;
205391b1d75SRobert Watson
206738824adSRobert Watson if (strcmp(statement, "default_labels") == 0) {
207738824adSRobert Watson char *name, *labels;
208738824adSRobert Watson
209738824adSRobert Watson name = next_token(&parse);
210738824adSRobert Watson labels = next_token(&parse);
211738824adSRobert Watson if (name == NULL || labels == NULL ||
212738824adSRobert Watson next_token(&parse) != NULL) {
213738824adSRobert Watson if (ignore_errors)
214391b1d75SRobert Watson continue;
215738824adSRobert Watson error = EINVAL;
216391b1d75SRobert Watson fclose(file);
217391b1d75SRobert Watson goto just_return;
218391b1d75SRobert Watson }
219391b1d75SRobert Watson
220738824adSRobert Watson if (mac_add_type(name, labels) == -1) {
221738824adSRobert Watson if (ignore_errors)
222738824adSRobert Watson continue;
223391b1d75SRobert Watson fclose(file);
224391b1d75SRobert Watson goto just_return;
225391b1d75SRobert Watson }
226738824adSRobert Watson } else if (strcmp(statement, "default_ifnet_labels") == 0 ||
227738824adSRobert Watson strcmp(statement, "default_file_labels") == 0 ||
228738824adSRobert Watson strcmp(statement, "default_process_labels") == 0) {
229738824adSRobert Watson char *labels, *type;
230391b1d75SRobert Watson
231738824adSRobert Watson if (strcmp(statement, "default_ifnet_labels") == 0)
232738824adSRobert Watson type = "ifnet";
233738824adSRobert Watson else if (strcmp(statement, "default_file_labels") == 0)
234738824adSRobert Watson type = "file";
235738824adSRobert Watson else if (strcmp(statement, "default_process_labels") ==
236738824adSRobert Watson 0)
237738824adSRobert Watson type = "process";
238738824adSRobert Watson
239738824adSRobert Watson labels = next_token(&parse);
240738824adSRobert Watson if (labels == NULL || next_token(&parse) != NULL) {
241738824adSRobert Watson if (ignore_errors)
242738824adSRobert Watson continue;
243738824adSRobert Watson error = EINVAL;
244391b1d75SRobert Watson fclose(file);
245391b1d75SRobert Watson goto just_return;
246391b1d75SRobert Watson }
247738824adSRobert Watson
248738824adSRobert Watson if (mac_add_type(type, labels) == -1) {
249738824adSRobert Watson if (ignore_errors)
250738824adSRobert Watson continue;
251738824adSRobert Watson fclose(file);
252738824adSRobert Watson goto just_return;
253391b1d75SRobert Watson }
254391b1d75SRobert Watson } else {
255738824adSRobert Watson if (ignore_errors)
256738824adSRobert Watson continue;
257391b1d75SRobert Watson fclose(file);
258391b1d75SRobert Watson error = EINVAL;
259391b1d75SRobert Watson goto just_return;
260391b1d75SRobert Watson }
261391b1d75SRobert Watson }
262391b1d75SRobert Watson
263391b1d75SRobert Watson fclose(file);
264391b1d75SRobert Watson
265391b1d75SRobert Watson internal_initialized = 1;
266391b1d75SRobert Watson
267391b1d75SRobert Watson just_return:
268391b1d75SRobert Watson if (error != 0)
269391b1d75SRobert Watson mac_destroy_internal();
270391b1d75SRobert Watson return (error);
271391b1d75SRobert Watson }
272391b1d75SRobert Watson
273391b1d75SRobert Watson static int
mac_maybe_init_internal(void)274391b1d75SRobert Watson mac_maybe_init_internal(void)
275391b1d75SRobert Watson {
276391b1d75SRobert Watson
277391b1d75SRobert Watson if (!internal_initialized)
278738824adSRobert Watson return (mac_init_internal(1));
279391b1d75SRobert Watson else
280391b1d75SRobert Watson return (0);
281391b1d75SRobert Watson }
282391b1d75SRobert Watson
283391b1d75SRobert Watson int
mac_reload(void)284391b1d75SRobert Watson mac_reload(void)
285391b1d75SRobert Watson {
286391b1d75SRobert Watson
287391b1d75SRobert Watson if (internal_initialized)
288391b1d75SRobert Watson mac_destroy_internal();
289738824adSRobert Watson return (mac_init_internal(0));
290391b1d75SRobert Watson }
291391b1d75SRobert Watson
292391b1d75SRobert Watson int
mac_free(struct mac * mac)293391b1d75SRobert Watson mac_free(struct mac *mac)
294391b1d75SRobert Watson {
295391b1d75SRobert Watson
296391b1d75SRobert Watson if (mac->m_string != NULL)
297391b1d75SRobert Watson free(mac->m_string);
298391b1d75SRobert Watson free(mac);
299391b1d75SRobert Watson
300391b1d75SRobert Watson return (0);
301391b1d75SRobert Watson }
302391b1d75SRobert Watson
303391b1d75SRobert Watson int
mac_from_text(struct mac ** mac,const char * text)304391b1d75SRobert Watson mac_from_text(struct mac **mac, const char *text)
305391b1d75SRobert Watson {
306391b1d75SRobert Watson
307391b1d75SRobert Watson *mac = (struct mac *) malloc(sizeof(**mac));
308391b1d75SRobert Watson if (*mac == NULL)
309391b1d75SRobert Watson return (ENOMEM);
310391b1d75SRobert Watson
311391b1d75SRobert Watson (*mac)->m_string = strdup(text);
312391b1d75SRobert Watson if ((*mac)->m_string == NULL) {
313391b1d75SRobert Watson free(*mac);
314391b1d75SRobert Watson *mac = NULL;
315391b1d75SRobert Watson return (ENOMEM);
316391b1d75SRobert Watson }
317391b1d75SRobert Watson
318391b1d75SRobert Watson (*mac)->m_buflen = strlen((*mac)->m_string)+1;
319391b1d75SRobert Watson
320391b1d75SRobert Watson return (0);
321391b1d75SRobert Watson }
322391b1d75SRobert Watson
323391b1d75SRobert Watson int
mac_to_text(struct mac * mac,char ** text)3244bae1674SChris Costello mac_to_text(struct mac *mac, char **text)
3254bae1674SChris Costello {
3264bae1674SChris Costello
3274bae1674SChris Costello *text = strdup(mac->m_string);
3284bae1674SChris Costello if (*text == NULL)
3294bae1674SChris Costello return (ENOMEM);
3304bae1674SChris Costello return (0);
3314bae1674SChris Costello }
3324bae1674SChris Costello
3334bae1674SChris Costello int
mac_prepare(struct mac ** mac,const char * elements)334930d4ffaSRobert Watson mac_prepare(struct mac **mac, const char *elements)
335391b1d75SRobert Watson {
336391b1d75SRobert Watson
337391b1d75SRobert Watson if (strlen(elements) >= MAC_MAX_LABEL_BUF_LEN)
338391b1d75SRobert Watson return (EINVAL);
339391b1d75SRobert Watson
340391b1d75SRobert Watson *mac = (struct mac *) malloc(sizeof(**mac));
341391b1d75SRobert Watson if (*mac == NULL)
342391b1d75SRobert Watson return (ENOMEM);
343391b1d75SRobert Watson
344391b1d75SRobert Watson (*mac)->m_string = malloc(MAC_MAX_LABEL_BUF_LEN);
345391b1d75SRobert Watson if ((*mac)->m_string == NULL) {
346391b1d75SRobert Watson free(*mac);
347391b1d75SRobert Watson *mac = NULL;
348391b1d75SRobert Watson return (ENOMEM);
349391b1d75SRobert Watson }
350391b1d75SRobert Watson
351391b1d75SRobert Watson strcpy((*mac)->m_string, elements);
352391b1d75SRobert Watson (*mac)->m_buflen = MAC_MAX_LABEL_BUF_LEN;
353391b1d75SRobert Watson
354391b1d75SRobert Watson return (0);
355391b1d75SRobert Watson }
356391b1d75SRobert Watson
357391b1d75SRobert Watson int
mac_prepare_type(struct mac ** mac,const char * name)358738824adSRobert Watson mac_prepare_type(struct mac **mac, const char *name)
359391b1d75SRobert Watson {
360738824adSRobert Watson struct label_default *ld;
3616e07ce26SRobert Watson int error;
3626e07ce26SRobert Watson
3636e07ce26SRobert Watson error = mac_maybe_init_internal();
3646e07ce26SRobert Watson if (error != 0)
3656e07ce26SRobert Watson return (error);
366391b1d75SRobert Watson
367738824adSRobert Watson for (ld = LIST_FIRST(&label_default_head); ld != NULL;
368738824adSRobert Watson ld = LIST_NEXT(ld, ld_entries)) {
369738824adSRobert Watson if (strcmp(name, ld->ld_name) == 0)
370738824adSRobert Watson return (mac_prepare(mac, ld->ld_labels));
371738824adSRobert Watson }
372391b1d75SRobert Watson
3737ea02dcdSRobert Watson errno = ENOENT;
3747ea02dcdSRobert Watson return (-1); /* XXXMAC: ENOLABEL */
375391b1d75SRobert Watson }
376391b1d75SRobert Watson
377391b1d75SRobert Watson int
mac_prepare_ifnet_label(struct mac ** mac)378391b1d75SRobert Watson mac_prepare_ifnet_label(struct mac **mac)
379391b1d75SRobert Watson {
380391b1d75SRobert Watson
381738824adSRobert Watson return (mac_prepare_type(mac, "ifnet"));
382391b1d75SRobert Watson }
383738824adSRobert Watson
384738824adSRobert Watson int
mac_prepare_file_label(struct mac ** mac)385738824adSRobert Watson mac_prepare_file_label(struct mac **mac)
386738824adSRobert Watson {
387738824adSRobert Watson
388738824adSRobert Watson return (mac_prepare_type(mac, "file"));
389738824adSRobert Watson }
390738824adSRobert Watson
391738824adSRobert Watson int
mac_prepare_packet_label(struct mac ** mac)392738824adSRobert Watson mac_prepare_packet_label(struct mac **mac)
393738824adSRobert Watson {
394738824adSRobert Watson
395738824adSRobert Watson return (mac_prepare_type(mac, "packet"));
396738824adSRobert Watson }
397738824adSRobert Watson
398391b1d75SRobert Watson int
mac_prepare_process_label(struct mac ** mac)399391b1d75SRobert Watson mac_prepare_process_label(struct mac **mac)
400391b1d75SRobert Watson {
401391b1d75SRobert Watson
402738824adSRobert Watson return (mac_prepare_type(mac, "process"));
403391b1d75SRobert Watson }
404391b1d75SRobert Watson
405391b1d75SRobert Watson /*
406391b1d75SRobert Watson * Simply test whether the TrustedBSD/MAC MIB tree is present; if so,
407391b1d75SRobert Watson * return 1 to indicate that the system has MAC enabled overall or for
408391b1d75SRobert Watson * a given policy.
409391b1d75SRobert Watson */
410391b1d75SRobert Watson int
mac_is_present(const char * policyname)411391b1d75SRobert Watson mac_is_present(const char *policyname)
412391b1d75SRobert Watson {
413391b1d75SRobert Watson int mib[5];
414391b1d75SRobert Watson size_t siz;
415391b1d75SRobert Watson char *mibname;
416391b1d75SRobert Watson int error;
417391b1d75SRobert Watson
418391b1d75SRobert Watson if (policyname != NULL) {
419391b1d75SRobert Watson if (policyname[strcspn(policyname, ".=")] != '\0') {
420391b1d75SRobert Watson errno = EINVAL;
421391b1d75SRobert Watson return (-1);
422391b1d75SRobert Watson }
423391b1d75SRobert Watson mibname = malloc(sizeof("security.mac.") - 1 +
424391b1d75SRobert Watson strlen(policyname) + sizeof(".enabled"));
425391b1d75SRobert Watson if (mibname == NULL)
426391b1d75SRobert Watson return (-1);
427391b1d75SRobert Watson strcpy(mibname, "security.mac.");
428391b1d75SRobert Watson strcat(mibname, policyname);
429391b1d75SRobert Watson strcat(mibname, ".enabled");
430391b1d75SRobert Watson siz = 5;
431391b1d75SRobert Watson error = sysctlnametomib(mibname, mib, &siz);
432391b1d75SRobert Watson free(mibname);
433391b1d75SRobert Watson } else {
434391b1d75SRobert Watson siz = 3;
435391b1d75SRobert Watson error = sysctlnametomib("security.mac", mib, &siz);
436391b1d75SRobert Watson }
437391b1d75SRobert Watson if (error == -1) {
438391b1d75SRobert Watson switch (errno) {
439391b1d75SRobert Watson case ENOTDIR:
440391b1d75SRobert Watson case ENOENT:
441391b1d75SRobert Watson return (0);
442391b1d75SRobert Watson default:
443391b1d75SRobert Watson return (error);
444391b1d75SRobert Watson }
445391b1d75SRobert Watson }
446391b1d75SRobert Watson return (1);
447391b1d75SRobert Watson }
448