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