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