Move cipso_free,cipso_new,cipso_apply from utils/common.c to libsmack/libsmack.c
[framework/security/smack.git] / libsmack / libsmack.c
1 /*
2  * This file is part of libsmack
3  *
4  * Copyright (C) 2010 Nokia Corporation
5  * Copyright (C) 2011 Intel Corporation
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19  * 02110-1301 USA
20  *
21  * Authors:
22  * Jarkko Sakkinen <jarkko.sakkinen@intel.com>
23  * Brian McGillion <brian.mcgillion@intel.com>
24  */
25
26 #include "sys/smack.h"
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/socket.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36
37 #define LABEL_LEN 255
38 #define ACC_LEN 5
39 #define LOAD_LEN (2 * (LABEL_LEN + 1) + ACC_LEN)
40
41 #define LEVEL_MAX 255
42 #define NUM_LEN 4
43 #define BUF_SIZE 512
44 #define CAT_MAX_COUNT 240
45 #define CAT_MAX_VALUE 63
46 #define CIPSO_POS(i)   (LABEL_LEN + 1 + NUM_LEN + NUM_LEN + i * NUM_LEN)
47 #define CIPSO_MAX_SIZE CIPSO_POS(CAT_MAX_COUNT)
48 #define CIPSO_NUM_LEN_STR "%-4d"
49
50 #define ACC_R 0x01
51 #define ACC_W 0x02
52 #define ACC_X 0x04
53 #define ACC_A 0x08
54 #define ACC_T 0x10
55
56 #define KERNEL_LONG_FORMAT "%s %s %s"
57 #define KERNEL_SHORT_FORMAT "%-23s %-23s %5s"
58 #define READ_BUF_SIZE LOAD_LEN + 1
59 #define SMACKFS_MNT "/smack"
60 #define SELF_LABEL_FILE "/proc/self/attr/current"
61
62 struct smack_rule {
63         char subject[LABEL_LEN + 1];
64         char object[LABEL_LEN + 1];
65         int access_code;
66         struct smack_rule *next;
67 };
68
69 struct smack_accesses {
70         struct smack_rule *first;
71         struct smack_rule *last;
72 };
73
74 struct cipso_mapping {
75         char label[LABEL_LEN + 1];
76         int cats[CAT_MAX_VALUE];
77         int ncats;
78         int level;
79         struct cipso_mapping *next;
80 };
81
82 struct smack_cipso {
83         struct cipso_mapping *first;
84         struct cipso_mapping *last;
85 };
86
87 static int accesses_apply(struct smack_accesses *handle, int clear);
88 static inline int access_type_to_int(const char *access_type);
89 static inline void int_to_access_type_c(unsigned ac, char *str);
90 static inline void int_to_access_type_k(unsigned ac, char *str);
91
92 int smack_accesses_new(struct smack_accesses **accesses)
93 {
94         struct smack_accesses *result;
95
96         result = calloc(sizeof(struct smack_accesses), 1);
97         if (result == NULL)
98                 return -1;
99
100         *accesses = result;
101         return 0;
102 }
103
104 void smack_accesses_free(struct smack_accesses *handle)
105 {
106         if (handle == NULL)
107                 return;
108
109         struct smack_rule *rule = handle->first;
110         struct smack_rule *next_rule = NULL;
111
112         while (rule != NULL) {
113                 next_rule = rule->next;
114                 free(rule);
115                 rule = next_rule;
116         }
117
118         free(handle);
119 }
120
121 int smack_accesses_save(struct smack_accesses *handle, int fd)
122 {
123         struct smack_rule *rule = handle->first;
124         char access_type[ACC_LEN + 1];
125         FILE *file;
126         int ret;
127         int newfd;
128
129         newfd = dup(fd);
130         if (newfd == -1)
131                 return -1;
132
133         file = fdopen(newfd, "w");
134         if (file == NULL) {
135                 close(newfd);
136                 return -1;
137         }
138
139         while (rule) {
140                 int_to_access_type_c(rule->access_code, access_type);
141
142                 ret = fprintf(file, "%s %s %s\n",
143                               rule->subject, rule->object, access_type);
144                 if (ret < 0) {
145                         fclose(file);
146                         return -1;
147                 }
148
149                 rule = rule->next;
150         }
151
152         fclose(file);
153         return 0;
154 }
155
156 int smack_accesses_apply(struct smack_accesses *handle)
157 {
158         return accesses_apply(handle, 0);
159 }
160
161 int smack_accesses_clear(struct smack_accesses *handle)
162 {
163         return accesses_apply(handle, 1);
164 }
165
166 int smack_accesses_add(struct smack_accesses *handle, const char *subject,
167                        const char *object, const char *access_type)
168 {
169         struct smack_rule *rule = NULL;
170
171         rule = calloc(sizeof(struct smack_rule), 1);
172         if (rule == NULL)
173                 return -1;
174
175         strncpy(rule->subject, subject, LABEL_LEN + 1);
176         strncpy(rule->object, object, LABEL_LEN + 1);
177         rule->access_code = access_type_to_int(access_type);
178
179         if (handle->first == NULL) {
180                 handle->first = handle->last = rule;
181         } else {
182                 handle->last->next = rule;
183                 handle->last = rule;
184         }
185
186         return 0;
187 }
188
189 int smack_accesses_add_from_file(struct smack_accesses *accesses, int fd)
190 {
191         FILE *file = NULL;
192         char buf[READ_BUF_SIZE];
193         char *ptr;
194         const char *subject, *object, *access;
195         int newfd;
196
197         newfd = dup(fd);
198         if (newfd == -1)
199                 return -1;
200
201         file = fdopen(newfd, "r");
202         if (file == NULL) {
203                 close(newfd);
204                 return -1;
205         }
206
207         while (fgets(buf, READ_BUF_SIZE, file) != NULL) {
208                 if (strcmp(buf, "\n") == 0)
209                         continue;
210                 subject = strtok_r(buf, " \t\n", &ptr);
211                 object = strtok_r(NULL, " \t\n", &ptr);
212                 access = strtok_r(NULL, " \t\n", &ptr);
213
214                 if (subject == NULL || object == NULL || access == NULL ||
215                     strtok_r(NULL, " \t\n", &ptr) != NULL) {
216                         errno = EINVAL;
217                         fclose(file);
218                         return -1;
219                 }
220
221                 if (smack_accesses_add(accesses, subject, object, access)) {
222                         fclose(file);
223                         return -1;
224                 }
225         }
226
227         if (ferror(file)) {
228                 fclose(file);
229                 return -1;
230         }
231
232         fclose(file);
233         return 0;
234 }
235
236 int smack_have_access(const char *subject, const char *object,
237                       const char *access_type)
238 {
239         char buf[LOAD_LEN + 1];
240         char access_type_k[ACC_LEN + 1];
241         unsigned access_code;
242         int ret;
243         int fd;
244         int access2 = 1;
245
246         access_code = access_type_to_int(access_type);
247         int_to_access_type_k(access_code, access_type_k);
248
249         fd = open(SMACKFS_MNT "/access2", O_RDWR);
250         if (fd < 0) {
251                 if (errno != ENOENT)
252                         return -1;
253                 fd = open(SMACKFS_MNT "/access", O_RDWR);
254                 if (fd < 0)
255                         return -1;
256                 access2 = 0;
257         }
258
259         if (access2)
260                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_LONG_FORMAT,
261                                subject, object, access_type_k);
262         else
263                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_SHORT_FORMAT,
264                                subject, object, access_type_k);
265
266         if (ret < 0) {
267                 close(fd);
268                 return -1;
269         }
270
271         ret = write(fd, buf, strlen(buf));
272         if (ret < 0) {
273                 close(fd);
274                 return -1;
275         }
276
277         ret = read(fd, buf, 1);
278         close(fd);
279         if (ret < 0)
280                 return -1;
281
282         return buf[0] == '1';
283 }
284 void smack_cipso_free(struct smack_cipso *cipso)
285 {
286         if (cipso == NULL)
287                 return;
288
289         struct cipso_mapping *mapping = cipso->first;
290         struct cipso_mapping *next_mapping = NULL;
291
292         while (mapping != NULL) {
293                 next_mapping = mapping->next;
294                 free(mapping);
295                 mapping = next_mapping;
296         }
297 }
298
299 struct smack_cipso *smack_cipso_new(int fd)
300 {
301         struct smack_cipso *cipso = NULL;
302         struct cipso_mapping *mapping = NULL;
303         FILE *file = NULL;
304         char buf[BUF_SIZE];
305         char *label, *level, *cat, *ptr;
306         long int val;
307         int i;
308         int newfd;
309
310         newfd = dup(fd);
311         if (newfd == -1)
312                 return NULL;
313
314         file = fdopen(newfd, "r");
315         if (file == NULL) {
316                 close(newfd);
317                 return NULL;
318         }
319
320         cipso = calloc(sizeof(struct smack_cipso ), 1);
321         if (cipso == NULL) {
322                 fclose(file);
323                 return NULL;
324         }
325
326         while (fgets(buf, BUF_SIZE, file) != NULL) {
327                 mapping = calloc(sizeof(struct cipso_mapping), 1);
328                 if (mapping == NULL)
329                         goto err_out;
330
331                 label = strtok_r(buf, " \t\n", &ptr);
332                 level = strtok_r(NULL, " \t\n", &ptr);
333                 cat = strtok_r(NULL, " \t\n", &ptr);
334                 if (label == NULL || cat == NULL || level == NULL ||
335                     strlen(label) > LABEL_LEN) {
336                         errno = EINVAL;
337                         goto err_out;
338                 }
339
340                 strcpy(mapping->label, label);
341
342                 errno = 0;
343                 val = strtol(level, NULL, 10);
344                 if (errno)
345                         goto err_out;
346
347                 if (val < 0 || val > LEVEL_MAX) {
348                         errno = ERANGE;
349                         goto err_out;
350                 }
351
352                 mapping->level = val;
353
354                 for (i = 0; i < CAT_MAX_COUNT && cat != NULL; i++) {
355                         errno = 0;
356                         val = strtol(cat, NULL, 10);
357                         if (errno)
358                                 goto err_out;
359
360                         if (val < 0 || val > CAT_MAX_VALUE) {
361                                 errno = ERANGE;
362                                 goto err_out;
363                         }
364
365                         mapping->cats[i] = val;
366
367                         cat = strtok_r(NULL, " \t\n", &ptr);
368                 }
369
370                 mapping->ncats = i;
371
372                 if (cipso->first == NULL) {
373                         cipso->first = cipso->last = mapping;
374                 } else {
375                         cipso->last->next = mapping;
376                         cipso->last = mapping;
377                 }
378         }
379
380         if (ferror(file))
381                 goto err_out;
382
383         fclose(file);
384         return cipso;
385 err_out:
386         fclose(file);
387         smack_cipso_free(cipso);
388         free(mapping);
389         return NULL;
390 }
391
392 int smack_cipso_apply(struct smack_cipso *cipso)
393 {
394         struct cipso_mapping *m = NULL;
395         char buf[CIPSO_MAX_SIZE];
396         int fd;
397         int i;
398
399         fd = open(SMACKFS_MNT "/cipso2", O_WRONLY);
400         if (fd < 0)
401                 return -1;
402
403         for (m = cipso->first; m != NULL; m = m->next) {
404                 sprintf(buf, "%s ", m->label);
405                 sprintf(&buf[LABEL_LEN + 1], CIPSO_NUM_LEN_STR, m->level);
406                 sprintf(&buf[LABEL_LEN + 1 + NUM_LEN], CIPSO_NUM_LEN_STR, m->ncats);
407
408                 for (i = 0; i < m->ncats; i++)
409                         sprintf(&buf[CIPSO_POS(i)], CIPSO_NUM_LEN_STR, m->cats[i]);
410
411                 if (write(fd, buf, strlen(buf)) < 0) {
412                         close(fd);
413                         return -1;
414                 }
415         }
416
417         close(fd);
418         return 0;
419 }
420 int smack_new_label_from_self(char **label)
421 {
422         char *result;
423         int fd;
424         int ret;
425
426         result = calloc(LABEL_LEN + 1, 1);
427         if (result == NULL)
428                 return -1;
429
430         fd = open(SELF_LABEL_FILE, O_RDONLY);
431         if (fd < 0) {
432                 free(result);
433                 return -1;
434         }
435
436         ret = read(fd, result, LABEL_LEN);
437         close(fd);
438         if (ret < 0) {
439                 free(result);
440                 return -1;
441         }
442
443         *label = result;
444         return 0;
445 }
446
447 int smack_new_label_from_socket(int fd, char **label)
448 {
449         char dummy;
450         int ret;
451         socklen_t length = 1;
452         char *result;
453
454         ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, &dummy, &length);
455         if (ret < 0 && errno != ERANGE)
456                 return -1;
457
458         result = calloc(length + 1, 1);
459         if (result == NULL)
460                 return -1;
461
462         ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, result, &length);
463         if (ret < 0) {
464                 free(result);
465                 return -1;
466         }
467
468         *label = result;
469         return 0;
470 }
471
472 static int accesses_apply(struct smack_accesses *handle, int clear)
473 {
474         char buf[LOAD_LEN + 1];
475         char access_type[ACC_LEN + 1];
476         struct smack_rule *rule;
477         int ret;
478         int fd;
479         int load2 = 1;
480
481         fd = open(SMACKFS_MNT "/load2", O_WRONLY);
482         if (fd < 0) {
483                 if (errno != ENOENT)
484                         return -1;
485                 /* fallback */
486                 fd = open(SMACKFS_MNT "/load", O_WRONLY);
487                 if (fd < 0)
488                         return -1;
489                 load2 = 0;
490         }
491
492         if (clear)
493                 strcpy(access_type, "-----");
494
495         for (rule = handle->first; rule != NULL; rule = rule->next) {
496                 if (!clear)
497                         int_to_access_type_k(rule->access_code, access_type);
498
499                 if (load2)
500                         ret = snprintf(buf, LOAD_LEN + 1, KERNEL_LONG_FORMAT,
501                                        rule->subject, rule->object,
502                                        access_type);
503                 else
504                         ret = snprintf(buf, LOAD_LEN + 1, KERNEL_SHORT_FORMAT,
505                                        rule->subject, rule->object,
506                                        access_type);
507                 if (ret < 0) {
508                         close(fd);
509                         return -1;
510                 }
511
512                 ret = write(fd, buf, strlen(buf));
513                 if (ret < 0) {
514                         close(fd);
515                         return -1;
516                 }
517         }
518
519         close(fd);
520         return 0;
521 }
522
523 static inline int access_type_to_int(const char *access_type)
524 {
525         int i;
526         unsigned access;
527
528         access = 0;
529         for (i = 0; i < ACC_LEN && access_type[i] != '\0'; i++)
530                 switch (access_type[i]) {
531                 case 'r':
532                 case 'R':
533                         access |= ACC_R;
534                         break;
535                 case 'w':
536                 case 'W':
537                         access |= ACC_W;
538                         break;
539                 case 'x':
540                 case 'X':
541                         access |= ACC_X;
542                         break;
543                 case 'a':
544                 case 'A':
545                         access |= ACC_A;
546                         break;
547                 case 't':
548                 case 'T':
549                         access |= ACC_T;
550                         break;
551                 default:
552                         break;
553                 }
554
555         return access;
556 }
557
558 static inline void int_to_access_type_c(unsigned access, char *str)
559 {
560         int i;
561         i = 0;
562         if ((access & ACC_R) != 0)
563                 str[i++] = 'r';
564         if ((access & ACC_W) != 0)
565                 str[i++] = 'w';
566         if ((access & ACC_X) != 0)
567                 str[i++] = 'x';
568         if ((access & ACC_A) != 0)
569                 str[i++] = 'a';
570         if ((access & ACC_T) != 0)
571                 str[i++] = 't';
572         str[i] = '\0';
573 }
574
575 static inline void int_to_access_type_k(unsigned access, char *str)
576 {
577         str[0] = ((access & ACC_R) != 0) ? 'r' : '-';
578         str[1] = ((access & ACC_W) != 0) ? 'w' : '-';
579         str[2] = ((access & ACC_X) != 0) ? 'x' : '-';
580         str[3] = ((access & ACC_A) != 0) ? 'a' : '-';
581         str[4] = ((access & ACC_T) != 0) ? 't' : '-';
582         str[5] = '\0';
583 }
584
585