2f85b3f4a85a1afaa225869a2c605e2f8ca4e5e8
[platform/upstream/buxton.git] / src / security / smack.c
1 /*
2  * This file is part of buxton.
3  *
4  * Copyright (C) 2013 Intel Corporation
5  *
6  * buxton is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1
9  * of the License, or (at your option) any later version.
10  */
11
12 #ifdef HAVE_CONFIG_H
13         #include "config.h"
14 #endif
15
16 #include <errno.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <sys/inotify.h>
20
21 #include "buxton.h"
22 #include "buxtonkey.h"
23 #include "configurator.h"
24 #include "direct.h"
25 #include "hashmap.h"
26 #include "log.h"
27 #include "smack.h"
28 #include "util.h"
29
30 static Hashmap *_smackrules = NULL;
31 /* set to true unless Smack support is not detected by the daemon */
32 static bool have_smack = true;
33
34 #define smack_check() do { if (!have_smack) { return true; } } while (0);
35
36
37 bool buxton_smack_enabled(void)
38 {
39         return have_smack;
40 }
41
42 bool buxton_cache_smack_rules(void)
43 {
44         smack_check();
45
46         FILE *load_file = NULL;
47         char *rule_pair = NULL;
48         int ret = true;
49         bool have_rules = false;
50         struct stat buf;
51
52         if (_smackrules) {
53                 hashmap_free(_smackrules);
54         }
55
56         _smackrules = hashmap_new(string_hash_func, string_compare_func);
57
58         if (!_smackrules) {
59                 abort();
60         }
61
62         /* FIXME: should check for a proper mount point instead */
63         if ((stat(SMACK_MOUNT_DIR, &buf) == -1) || !S_ISDIR(buf.st_mode)) {
64                 buxton_log("Smack filesystem not detected; disabling Smack checks\n");
65                 have_smack = false;
66                 goto end;
67         }
68
69         load_file = fopen(buxton_smack_load_file(), "r");
70
71         if (!load_file) {
72                 switch (errno) {
73                 case ENOENT:
74                         buxton_log("Smackfs load2 file not found; disabling Smack checks\n");
75                         have_smack = false;
76                         goto end;
77                 default:
78                         buxton_log("fopen(): %m\n");
79                         ret = false;
80                         goto end;
81                 }
82         }
83
84         do {
85                 int r;
86                 int chars;
87                 BuxtonKeyAccessType *accesstype;
88
89                 char subject[SMACK_LABEL_LEN+1] = { 0, };
90                 char object[SMACK_LABEL_LEN+1] = { 0, };
91                 char access[ACC_LEN] = { 0, };
92
93                 /* read contents from load2 */
94                 chars = fscanf(load_file, "%s %s %s\n", subject, object, access);
95
96                 if (ferror(load_file)) {
97                         buxton_log("fscanf(): %m\n");
98                         ret = false;
99                         goto end;
100                 }
101
102                 if (!have_rules && chars == EOF && feof(load_file)) {
103                         buxton_debug("No loaded Smack rules found\n");
104                         goto end;
105                 }
106
107                 if (chars != 3) {
108                         buxton_log("Corrupt load file detected\n");
109                         ret = false;
110                         goto end;
111                 }
112
113                 have_rules = true;
114
115                 r = asprintf(&rule_pair, "%s %s", subject, object);
116                 if (r == -1) {
117                         abort();
118                 }
119
120                 accesstype = malloc0(sizeof(BuxtonKeyAccessType));
121                 if (!accesstype) {
122                         abort();
123                 }
124
125                 *accesstype = ACCESS_NONE;
126
127                 if (strchr(access, 'r')) {
128                         *accesstype |= ACCESS_READ;
129                 }
130
131                 if (strchr(access, 'w')) {
132                         *accesstype |= ACCESS_WRITE;
133                 }
134
135                 hashmap_put(_smackrules, rule_pair, accesstype);
136
137         } while (!feof(load_file));
138
139 end:
140         if (load_file) {
141                 fclose(load_file);
142         }
143
144         return ret;
145 }
146
147 bool buxton_check_smack_access(BuxtonString *subject, BuxtonString *object, BuxtonKeyAccessType request)
148 {
149         smack_check();
150
151         _cleanup_free_ char *key = NULL;
152         int r;
153         BuxtonKeyAccessType *access;
154
155         assert(subject);
156         assert(object);
157         assert((request == ACCESS_READ) || (request == ACCESS_WRITE));
158         assert(_smackrules);
159
160         buxton_debug("Subject: %s\n", subject->value);
161         buxton_debug("Object: %s\n", object->value);
162
163         /* check the builtin Smack rules first */
164         if (streq(subject->value, "*")) {
165                 return false;
166         }
167
168         if (streq(object->value, "@") || streq(subject->value, "@")) {
169                 return true;
170         }
171
172         if (streq(object->value, "*")) {
173                 return true;
174         }
175
176         if (streq(subject->value, object->value)) {
177                 return true;
178         }
179
180         if (request == ACCESS_READ) {
181                 if (streq(object->value, "_")) {
182                         return true;
183                 }
184                 if (streq(subject->value, "^")) {
185                         return true;
186                 }
187         }
188
189         /* finally, check the loaded rules */
190         r = asprintf(&key, "%s %s", subject->value, object->value);
191         if (r == -1) {
192                 abort();
193         }
194
195         buxton_debug("Key: %s\n", key);
196
197         access = hashmap_get(_smackrules, key);
198         if (!access) {
199                 /* A null value is not an error, since clients may try to
200                  * read/write keys with labels that are not in the loaded
201                  * rule set. In this situation, access is simply denied,
202                  * because there are no further rules to consider.
203                  */
204                 buxton_debug("Value of key '%s' is NULL\n", key);
205                 return false;
206         }
207
208         /* After debugging, change this code to: */
209         /* return ((*access) & request); */
210         if (access) {
211                 buxton_debug("Value: %x\n", *access);
212         }
213
214         if (request == ACCESS_READ && (*access) & request) {
215                 buxton_debug("Read access granted!\n");
216                 return true;
217         }
218
219         if (request == ACCESS_WRITE && ((*access) & ACCESS_READ && (*access) & ACCESS_WRITE)) {
220                 buxton_debug("Write access granted!\n");
221                 return true;
222         }
223
224         buxton_debug("Access denied!\n");
225         return false;
226 }
227
228 int buxton_watch_smack_rules(void)
229 {
230         if (!have_smack) {
231                 errno = 0;
232                 return -1;
233         }
234
235         int fd;
236
237         fd = inotify_init1(IN_NONBLOCK);
238         if (fd < 0) {
239                 buxton_log("inotify_init(): %m\n");
240                 return -1;
241         }
242         if (inotify_add_watch(fd, buxton_smack_load_file(), IN_CLOSE_WRITE) < 0) {
243                 buxton_log("inotify_add_watch(): %m\n");
244                 return -1;
245         }
246         return fd;
247 }
248
249 /*
250  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
251  *
252  * Local variables:
253  * c-basic-offset: 8
254  * tab-width: 8
255  * indent-tabs-mode: t
256  * End:
257  *
258  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
259  * :indentSize=8:tabSize=8:noTabs=false:
260  */