libsmack: fix smack_cipso_apply() failing with more than one rule
[framework/security/smack.git] / libsmack / libsmack.c
1 /*
2  * This file is part of libsmack
3  *
4  * Copyright (C) 2010, 2011 Nokia Corporation
5  * Copyright (C) 2011, 2012, 2013 Intel Corporation
6  * Copyright (C) 2012, 2013 Samsung Electronics Co.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1 as published by the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  */
22
23 #include "sys/smack.h"
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/socket.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 #include <limits.h>
34 #include <sys/xattr.h>
35
36 #define ACC_LEN 5
37 #define LOAD_LEN (2 * (SMACK_LABEL_LEN + 1) + 2 * ACC_LEN + 1)
38
39 #define LEVEL_MAX 255
40 #define NUM_LEN 4
41 #define BUF_SIZE 512
42 #define CAT_MAX_COUNT 240
43 #define CAT_MAX_VALUE 63
44 #define CIPSO_POS(i)   (SMACK_LABEL_LEN + 1 + NUM_LEN + NUM_LEN + i * NUM_LEN)
45 #define CIPSO_MAX_SIZE CIPSO_POS(CAT_MAX_COUNT)
46 #define CIPSO_NUM_LEN_STR "%-4d"
47
48 #define KERNEL_LONG_FORMAT "%s %s %s"
49 #define KERNEL_SHORT_FORMAT "%-23s %-23s %5s"
50 #define KERNEL_MODIFY_FORMAT "%s %s %s %s"
51 #define READ_BUF_SIZE LOAD_LEN + 1
52 #define SELF_LABEL_FILE "/proc/self/attr/current"
53 #define ACC_CLEAR "-----"
54
55 #define ACCESS_TYPE_R 0x01
56 #define ACCESS_TYPE_W 0x02
57 #define ACCESS_TYPE_X 0x04
58 #define ACCESS_TYPE_A 0x08
59 #define ACCESS_TYPE_T 0x10
60
61 extern char *smackfs_mnt;
62
63 struct smack_rule {
64         char subject[SMACK_LABEL_LEN + 1];
65         char object[SMACK_LABEL_LEN + 1];
66         int allow_code;
67         int deny_code;
68         struct smack_rule *next;
69 };
70
71 struct smack_accesses {
72         struct smack_rule *first;
73         struct smack_rule *last;
74 };
75
76 struct cipso_mapping {
77         char label[SMACK_LABEL_LEN + 1];
78         int cats[CAT_MAX_VALUE];
79         int ncats;
80         int level;
81         struct cipso_mapping *next;
82 };
83
84 struct smack_cipso {
85         struct cipso_mapping *first;
86         struct cipso_mapping *last;
87 };
88
89 static int accesses_apply(struct smack_accesses *handle, int clear);
90 static ssize_t smack_label_length(const char *label) __attribute__((unused));
91 static inline ssize_t get_label(char *dest, const char *src);
92 static inline int str_to_access_code(const char *str);
93 static inline void access_code_to_str(unsigned code, char *str);
94
95 int smack_accesses_new(struct smack_accesses **accesses)
96 {
97         struct smack_accesses *result;
98
99         result = calloc(sizeof(struct smack_accesses), 1);
100         if (result == NULL)
101                 return -1;
102
103         *accesses = result;
104         return 0;
105 }
106
107 void smack_accesses_free(struct smack_accesses *handle)
108 {
109         if (handle == NULL)
110                 return;
111
112         struct smack_rule *rule = handle->first;
113         struct smack_rule *next_rule = NULL;
114
115         while (rule != NULL) {
116                 next_rule = rule->next;
117                 free(rule);
118                 rule = next_rule;
119         }
120
121         free(handle);
122 }
123
124 int smack_accesses_save(struct smack_accesses *handle, int fd)
125 {
126         struct smack_rule *rule = handle->first;
127         char allow_str[ACC_LEN + 1];
128         char deny_str[ACC_LEN + 1];
129         FILE *file;
130         int ret;
131         int newfd;
132
133         newfd = dup(fd);
134         if (newfd == -1)
135                 return -1;
136
137         file = fdopen(newfd, "w");
138         if (file == NULL) {
139                 close(newfd);
140                 return -1;
141         }
142
143         while (rule) {
144                 access_code_to_str(rule->allow_code, allow_str);
145
146                 if (rule->deny_code != -1) /* modify? */ {
147                         access_code_to_str(rule->deny_code, deny_str);
148
149                         ret = fprintf(file, "%s %s %s %s\n",
150                                       rule->subject, rule->object,
151                                       allow_str, deny_str);
152                 } else {
153                         ret = fprintf(file, "%s %s %s\n",
154                                       rule->subject, rule->object,
155                                       allow_str);
156                 }
157
158                 if (ret < 0) {
159                         fclose(file);
160                         return -1;
161                 }
162
163                 rule = rule->next;
164         }
165
166         fclose(file);
167         return 0;
168 }
169
170 int smack_accesses_apply(struct smack_accesses *handle)
171 {
172         return accesses_apply(handle, 0);
173 }
174
175 int smack_accesses_clear(struct smack_accesses *handle)
176 {
177         return accesses_apply(handle, 1);
178 }
179
180 int smack_accesses_add(struct smack_accesses *handle, const char *subject,
181                        const char *object, const char *access_type)
182 {
183         struct smack_rule *rule = NULL;
184
185         rule = calloc(sizeof(struct smack_rule), 1);
186         if (rule == NULL)
187                 return -1;
188
189         if (get_label(rule->subject, subject) < 0 ||
190             get_label(rule->object, object) < 0) {
191                 free(rule);
192                 return -1;
193         }
194
195         rule->allow_code = str_to_access_code(access_type);
196         rule->deny_code = -1; /* no modify */
197         if (rule->allow_code == -1) {
198                 free(rule);
199                 return -1;
200         }
201
202         if (handle->first == NULL) {
203                 handle->first = handle->last = rule;
204         } else {
205                 handle->last->next = rule;
206                 handle->last = rule;
207         }
208
209         return 0;
210 }
211
212 int smack_accesses_add_modify(struct smack_accesses *handle,
213                               const char *subject,
214                               const char *object,
215                               const char *allow_access_type,
216                               const char *deny_access_type)
217 {
218         struct smack_rule *rule = NULL;
219
220         rule = calloc(sizeof(struct smack_rule), 1);
221         if (rule == NULL)
222                 return -1;
223
224         if (get_label(rule->subject, subject) < 0 ||
225             get_label(rule->object, object) < 0) {
226                 free(rule);
227                 return -1;
228         }
229
230         rule->allow_code = str_to_access_code(allow_access_type);
231         rule->deny_code = str_to_access_code(deny_access_type);
232         if (rule->allow_code == -1 || rule->deny_code == -1) {
233                 free(rule);
234                 return -1;
235         }
236
237         if (handle->first == NULL) {
238                 handle->first = handle->last = rule;
239         } else {
240                 handle->last->next = rule;
241                 handle->last = rule;
242         }
243
244         return 0;
245 }
246
247 int smack_accesses_add_from_file(struct smack_accesses *accesses, int fd)
248 {
249         FILE *file = NULL;
250         char buf[READ_BUF_SIZE];
251         char *ptr;
252         const char *subject, *object, *access, *access2;
253         int newfd;
254         int ret;
255
256         newfd = dup(fd);
257         if (newfd == -1)
258                 return -1;
259
260         file = fdopen(newfd, "r");
261         if (file == NULL) {
262                 close(newfd);
263                 return -1;
264         }
265
266         while (fgets(buf, READ_BUF_SIZE, file) != NULL) {
267                 if (strcmp(buf, "\n") == 0)
268                         continue;
269                 subject = strtok_r(buf, " \t\n", &ptr);
270                 object = strtok_r(NULL, " \t\n", &ptr);
271                 access = strtok_r(NULL, " \t\n", &ptr);
272                 access2 = strtok_r(NULL, " \t\n", &ptr);
273
274                 if (subject == NULL || object == NULL || access == NULL ||
275                     strtok_r(NULL, " \t\n", &ptr) != NULL) {
276                         fclose(file);
277                         return -1;
278                 }
279
280                 if (access2 == NULL)
281                         ret = smack_accesses_add(accesses, subject, object, access);
282                 else
283                         ret = smack_accesses_add_modify(accesses, subject, object, access, access2);
284
285                 if (ret) {
286                         fclose(file);
287                         return -1;
288                 }
289         }
290
291         if (ferror(file)) {
292                 fclose(file);
293                 return -1;
294         }
295
296         fclose(file);
297         return 0;
298 }
299
300 int smack_have_access(const char *subject, const char *object,
301                       const char *access_type)
302 {
303         char buf[LOAD_LEN + 1];
304         char str[ACC_LEN + 1];
305         int code;
306         int ret;
307         int fd;
308         int access2 = 1;
309         char path[PATH_MAX];
310
311         if (!smackfs_mnt)
312                 return -1; 
313
314         snprintf(path, sizeof path, "%s/access2", smackfs_mnt);
315         fd = open(path, O_RDWR);
316         if (fd < 0) {
317                 if (errno != ENOENT)
318                         return -1;
319                 
320                 snprintf(path, sizeof path, "%s/access", smackfs_mnt);
321                 fd = open(path, O_RDWR);
322                 if (fd < 0)
323                         return -1;
324                 access2 = 0;
325         }
326
327         if ((code = str_to_access_code(access_type)) < 0)
328                 return -1;
329         access_code_to_str(code, str);
330
331         if (access2)
332                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_LONG_FORMAT,
333                                subject, object, str);
334         else
335                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_SHORT_FORMAT,
336                                subject, object, str);
337
338         if (ret < 0) {
339                 close(fd);
340                 return -1;
341         }
342
343         ret = write(fd, buf, strlen(buf));
344         if (ret < 0) {
345                 close(fd);
346                 return -1;
347         }
348
349         ret = read(fd, buf, 1);
350         close(fd);
351         if (ret < 0)
352                 return -1;
353
354         return buf[0] == '1';
355 }
356
357 int smack_cipso_new(struct smack_cipso **cipso)
358 {
359         struct smack_cipso *result;
360
361         result = calloc(sizeof(struct smack_cipso), 1);
362         if (result == NULL)
363                 return -1;
364
365         *cipso = result;
366         return 0;
367 }
368
369 void smack_cipso_free(struct smack_cipso *cipso)
370 {
371         if (cipso == NULL)
372                 return;
373
374         struct cipso_mapping *mapping = cipso->first;
375         struct cipso_mapping *next_mapping = NULL;
376
377         while (mapping != NULL) {
378                 next_mapping = mapping->next;
379                 free(mapping);
380                 mapping = next_mapping;
381         }
382
383         free(cipso);
384 }
385
386 int smack_cipso_apply(struct smack_cipso *cipso)
387 {
388         struct cipso_mapping *m = NULL;
389         char buf[CIPSO_MAX_SIZE];
390         int fd;
391         int i;
392         char path[PATH_MAX];
393         int offset;
394
395         if (!smackfs_mnt)
396                 return -1;
397
398         snprintf(path, sizeof path, "%s/cipso2", smackfs_mnt);
399         fd = open(path, O_WRONLY);
400         if (fd < 0)
401                 return -1;
402
403         memset(buf,0,CIPSO_MAX_SIZE);
404         for (m = cipso->first; m != NULL; m = m->next) {
405                 snprintf(buf, SMACK_LABEL_LEN + 1, "%s", m->label);
406                 offset = strlen(buf) + 1;
407
408                 sprintf(&buf[offset], CIPSO_NUM_LEN_STR, m->level);
409                 offset += NUM_LEN;
410
411                 sprintf(&buf[offset], CIPSO_NUM_LEN_STR, m->ncats);
412                 offset += NUM_LEN;
413
414                 for (i = 0; i < m->ncats; i++){
415                         sprintf(&buf[offset], CIPSO_NUM_LEN_STR, m->cats[i]);
416                         offset += NUM_LEN;
417                 }
418
419                 if (write(fd, buf, offset) < 0) {
420                         close(fd);
421                         return -1;
422                 }
423         }
424
425         close(fd);
426         return 0;
427 }
428
429 int smack_cipso_add_from_file(struct smack_cipso *cipso, int fd)
430 {
431         struct cipso_mapping *mapping = NULL;
432         FILE *file = NULL;
433         char buf[BUF_SIZE];
434         char *label, *level, *cat, *ptr;
435         long int val;
436         int i;
437         int newfd;
438
439         newfd = dup(fd);
440         if (newfd == -1)
441                 return -1;
442
443         file = fdopen(newfd, "r");
444         if (file == NULL) {
445                 close(newfd);
446                 return -1;
447         }
448
449         while (fgets(buf, BUF_SIZE, file) != NULL) {
450                 mapping = calloc(sizeof(struct cipso_mapping), 1);
451                 if (mapping == NULL)
452                         goto err_out;
453
454                 label = strtok_r(buf, " \t\n", &ptr);
455                 level = strtok_r(NULL, " \t\n", &ptr);
456                 cat = strtok_r(NULL, " \t\n", &ptr);
457
458                 if (level == NULL || get_label(mapping->label, label) < 0)
459                         goto err_out;
460
461                 errno = 0;
462                 val = strtol(level, NULL, 10);
463                 if (errno)
464                         goto err_out;
465
466                 if (val < 0 || val > LEVEL_MAX)
467                         goto err_out;
468
469                 mapping->level = val;
470
471                 for (i = 0; i < CAT_MAX_COUNT && cat != NULL; i++) {
472                         errno = 0;
473                         val = strtol(cat, NULL, 10);
474                         if (errno)
475                                 goto err_out;
476
477                         if (val < 0 || val > CAT_MAX_VALUE)
478                                 goto err_out;
479
480                         mapping->cats[i] = val;
481
482                         cat = strtok_r(NULL, " \t\n", &ptr);
483                 }
484
485                 mapping->ncats = i;
486
487                 if (cipso->first == NULL) {
488                         cipso->first = cipso->last = mapping;
489                 } else {
490                         cipso->last->next = mapping;
491                         cipso->last = mapping;
492                 }
493         }
494
495         if (ferror(file))
496                 goto err_out;
497
498         fclose(file);
499         return 0;
500 err_out:
501         fclose(file);
502         free(mapping);
503         return -1;
504 }
505
506 const char *smack_smackfs_path(void)
507 {
508         return smackfs_mnt;
509 }
510
511 ssize_t smack_new_label_from_self(char **label)
512 {
513         char *result;
514         int fd;
515         int ret;
516
517         result = calloc(SMACK_LABEL_LEN + 1, 1);
518         if (result == NULL)
519                 return -1;
520
521         fd = open(SELF_LABEL_FILE, O_RDONLY);
522         if (fd < 0) {
523                 free(result);
524                 return -1;
525         }
526
527         ret = read(fd, result, SMACK_LABEL_LEN);
528         close(fd);
529         if (ret < 0) {
530                 free(result);
531                 return -1;
532         }
533
534         *label = result;
535         return ret;
536 }
537
538 ssize_t smack_new_label_from_socket(int fd, char **label)
539 {
540         char dummy;
541         int ret;
542         socklen_t length = 1;
543         char *result;
544
545         ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, &dummy, &length);
546         if (ret < 0 && errno != ERANGE)
547                 return -1;
548
549         result = calloc(length + 1, 1);
550         if (result == NULL)
551                 return -1;
552
553         ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, result, &length);
554         if (ret < 0) {
555                 free(result);
556                 return -1;
557         }
558
559         *label = result;
560         return length;
561 }
562
563 ssize_t smack_new_label_from_path(const char *path, const char *xattr, 
564                                   int follow, char **label)
565 {
566         char *result;
567         ssize_t ret = 0;
568
569         ret = follow ?
570                 getxattr(path, xattr, NULL, 0) :
571                 lgetxattr(path, xattr, NULL, 0);
572         if (ret < 0 && errno != ERANGE)
573                 return -1;
574
575         result = calloc(ret + 1, 1);
576         if (result == NULL)
577                 return -1;
578
579         ret = follow ?
580                 getxattr(path, xattr, result, ret) :
581                 lgetxattr(path, xattr, result, ret);
582         if (ret < 0) {
583                 free(result);
584                 return -1;
585         }
586
587         *label = result;
588         return 0;
589 }
590
591 int smack_set_label_for_self(const char *label)
592 {
593         int len;
594         int fd;
595         int ret;
596
597         len = get_label(NULL, label);
598         if (len < 0)
599                 return -1;
600
601         fd = open(SELF_LABEL_FILE, O_WRONLY);
602         if (fd < 0)
603                 return -1;
604
605         ret = write(fd, label, len);
606         close(fd);
607
608         return (ret < 0) ? -1 : 0;
609 }
610
611 int smack_revoke_subject(const char *subject)
612 {
613         int ret;
614         int fd;
615         int len;
616         char path[PATH_MAX];
617
618         len = get_label(NULL, subject);
619         if (len < 0)
620                 return -1;
621
622         snprintf(path, sizeof path, "%s/revoke-subject", smackfs_mnt);
623         fd = open(path, O_WRONLY);
624         if (fd < 0)
625                 return -1;
626
627         ret = write(fd, subject, len);
628         close(fd);
629
630         return (ret < 0) ? -1 : 0;
631 }
632
633 ssize_t smack_label_length(const char *label)
634 {
635         return get_label(NULL, label);
636 }
637
638 static int accesses_apply(struct smack_accesses *handle, int clear)
639 {
640         char buf[LOAD_LEN + 1];
641         char allow_str[ACC_LEN + 1];
642         char deny_str[ACC_LEN + 1];
643         struct smack_rule *rule;
644         int ret;
645         int fd;
646         int load_fd;
647         int change_fd;
648         int load2 = 1;
649         char path[PATH_MAX];
650
651         if (!smackfs_mnt)
652                 return -1;
653
654         snprintf(path, sizeof path, "%s/load2", smackfs_mnt);
655         load_fd = open(path, O_WRONLY);
656         if (load_fd < 0) {
657                 if (errno != ENOENT)
658                         return -1;
659                 /* fallback */
660                 snprintf(path, sizeof path, "%s/load", smackfs_mnt);
661                 load_fd = open(path, O_WRONLY);
662                 /* Try to continue if the file doesn't exist, we might not need it. */
663                 if (load_fd < 0 && errno != ENOENT)
664                         return -1;
665                 load2 = 0;
666         }
667
668         snprintf(path, sizeof path, "%s/change-rule", smackfs_mnt);
669         change_fd = open(path, O_WRONLY);
670         /* Try to continue if the file doesn't exist, we might not need it. */
671         if (change_fd < 0 && errno != ENOENT) {
672                 ret = -1;
673                 goto err_out;
674         }
675
676         for (rule = handle->first; rule != NULL; rule = rule->next) {
677                 access_code_to_str(clear ? 0 : rule->allow_code, allow_str);
678
679                 if (rule->deny_code != -1 && !clear) {
680                         access_code_to_str(clear ? 0 : rule->deny_code, deny_str);
681
682                         fd = change_fd;
683                         ret = snprintf(buf, LOAD_LEN + 1, KERNEL_MODIFY_FORMAT,
684                                        rule->subject, rule->object,
685                                        allow_str,
686                                        deny_str);
687                 } else {
688                         fd = load_fd;
689                         if (load2)
690                                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_LONG_FORMAT,
691                                                rule->subject, rule->object,
692                                                allow_str);
693                         else
694                                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_SHORT_FORMAT,
695                                                rule->subject, rule->object,
696                                                allow_str);
697                 }
698
699                 if (ret < 0 || fd < 0) {
700                         ret = -1;
701                         goto err_out;
702                 }
703
704                 ret = write(fd, buf, strlen(buf));
705                 if (ret < 0) {
706                         ret = -1;
707                         goto err_out;
708                 }
709         }
710         ret = 0;
711
712 err_out:
713         if (load_fd >= 0)
714                 close(load_fd);
715         if (change_fd >= 0)
716                 close(change_fd);
717         return ret;
718 }
719
720 static inline ssize_t get_label(char *dest, const char *src)
721 {
722         int i;
723
724         if (!src || src[0] == '\0' || src[0] == '-')
725                 return -1;
726
727         for (i = 0; i < (SMACK_LABEL_LEN + 1) && src[i]; i++) {
728                 switch (src[i]) {
729                 case ' ':
730                 case '/':
731                 case '"':
732                 case '\\':
733                 case '\'':
734                         return -1;
735                 default:
736                         break;
737                 }
738
739                 if (dest)
740                         dest[i] = src[i];
741         }
742
743         if (dest && i < (SMACK_LABEL_LEN + 1))
744                 dest[i] = '\0';
745
746         return i < (SMACK_LABEL_LEN + 1) ? i : -1;
747 }
748
749
750 static inline int str_to_access_code(const char *str)
751 {
752         int i;
753         unsigned int code = 0;
754
755         for (i = 0; str[i] != '\0'; i++) {
756                 switch (str[i]) {
757                 case 'r':
758                 case 'R':
759                         code |= ACCESS_TYPE_R;
760                         break;
761                 case 'w':
762                 case 'W':
763                         code |= ACCESS_TYPE_W;
764                         break;
765                 case 'x':
766                 case 'X':
767                         code |= ACCESS_TYPE_X;
768                         break;
769                 case 'a':
770                 case 'A':
771                         code |= ACCESS_TYPE_A;
772                         break;
773                 case 't':
774                 case 'T':
775                         code |= ACCESS_TYPE_T;
776                         break;
777                 case '-':
778                         break;
779                 default:
780                         return -1;
781                 }
782         }
783
784         return code;
785 }
786
787 static inline void access_code_to_str(unsigned int code, char *str)
788 {
789         str[0] = ((code & ACCESS_TYPE_R) != 0) ? 'r' : '-';
790         str[1] = ((code & ACCESS_TYPE_W) != 0) ? 'w' : '-';
791         str[2] = ((code & ACCESS_TYPE_X) != 0) ? 'x' : '-';
792         str[3] = ((code & ACCESS_TYPE_A) != 0) ? 'a' : '-';
793         str[4] = ((code & ACCESS_TYPE_T) != 0) ? 't' : '-';
794         str[5] = '\0';
795 }