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