Merge remote-tracking branch 'tizen/upstream' into tizen
[platform/upstream/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 <stdint.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <sys/xattr.h>
34 #include <unistd.h>
35 #include <sys/xattr.h>
36
37 #define SELF_LABEL_FILE "/proc/self/attr/current"
38
39 #define SHORT_LABEL_LEN 23
40 #define ACC_LEN 6
41 #define LOAD_LEN (2 * (SMACK_LABEL_LEN + 1) + 2 * ACC_LEN + 1)
42 #define KERNEL_LONG_FORMAT "%s %s %s"
43 #define KERNEL_SHORT_FORMAT "%-23s %-23s %5.5s"
44 #define KERNEL_MODIFY_FORMAT "%s %s %s %s"
45
46 #define LEVEL_MAX 255
47 #define NUM_LEN 4
48 #define BUF_SIZE 512
49 #define CAT_MAX_COUNT 184
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 BITMASK(b)    (1 << ((b) & 7))
55 #define BITSLOT(b)    ((b) >> 3)
56 #define BITSET(a, b)  ((a)[BITSLOT(b)] |= BITMASK(b))
57 #define BITTEST(a, b) ((a)[BITSLOT(b)] & BITMASK(b))
58 #define BITNSLOTS(nb) ((nb + 7) >> 3)
59
60 #define ACCESS_TYPE_R 0x01
61 #define ACCESS_TYPE_W 0x02
62 #define ACCESS_TYPE_X 0x04
63 #define ACCESS_TYPE_A 0x08
64 #define ACCESS_TYPE_T 0x10
65 #define ACCESS_TYPE_L 0x20
66
67 #define ACCESS_TYPE_ALL ((1 << ACC_LEN) - 1)
68
69 #define DICT_HASH_SIZE 4096
70
71 extern char *smackfs_mnt;
72 extern int smackfs_mnt_dirfd;
73
74 extern int init_smackfs_mnt(void);
75
76 union smack_perm {
77         struct {
78                 int8_t allow_code;
79                 int8_t deny_code;
80         };
81         uint16_t allow_deny_code;
82 };
83
84 struct smack_rule {
85         union smack_perm perm;
86         int object_id;
87         struct smack_rule *next_rule;
88 };
89
90 struct smack_label {
91         uint8_t len;
92         int id;
93         char *label;
94         struct smack_rule *first_rule;
95         struct smack_rule *last_rule;
96         struct smack_label *next_label;
97 };
98
99 struct smack_hash_entry {
100         struct smack_label *first;
101         struct smack_label *last;
102 };
103
104 struct smack_accesses {
105         int has_long;
106         int labels_cnt;
107         int labels_alloc;
108         int page_size;
109         struct smack_label **labels;
110         struct smack_hash_entry *label_hash;
111         union smack_perm *merge_perms;
112         int *merge_object_ids;
113 };
114
115 struct cipso_mapping {
116         char label[SMACK_LABEL_LEN + 1];
117         uint8_t cats[BITNSLOTS(CAT_MAX_COUNT)];
118         int ncats;
119         int level;
120         struct cipso_mapping *next;
121 };
122
123 struct smack_cipso {
124         int has_long;
125         struct cipso_mapping *first;
126         struct cipso_mapping *last;
127 };
128
129 typedef int (*getxattr_func)(void*, const char*, void*, size_t);
130 typedef int (*setxattr_func)(const void*, const char*, const void*, size_t, int);
131 typedef int (*removexattr_func)(void*, const char*);
132
133 static inline char* get_xattr_name(enum smack_label_type type);
134
135 struct smack_file_buffer {
136         int fd;
137         int pos;
138         int flush_pos;
139         char *buf;
140 };
141
142 static int open_smackfs_file(const char *long_name, const char *short_name,
143                              mode_t mode, int *use_long);
144 static int accesses_apply(struct smack_accesses *handle, int clear);
145 static int accesses_print(struct smack_accesses *handle,
146                           int clear, int use_long, int multiline,
147                           struct smack_file_buffer *load_buffer,
148                           struct smack_file_buffer *change_buffer);
149 static inline ssize_t get_label(char *dest, const char *src, unsigned int *hash);
150 static inline int str_to_access_code(const char *str);
151 static inline void access_code_to_str(unsigned code, char *str);
152 static struct smack_label *label_add(struct smack_accesses *handle, const char *src);
153
154 int smack_accesses_new(struct smack_accesses **accesses)
155 {
156         struct smack_accesses *result;
157
158         result = calloc(1, sizeof(struct smack_accesses));
159         if (result == NULL)
160                 return -1;
161
162         result->labels_alloc = 128;
163         result->labels = malloc(result->labels_alloc * sizeof(struct smack_label *));
164         if (result->labels == NULL)
165                 goto err_out;
166         result->merge_perms = malloc(result->labels_alloc * sizeof(union smack_perm));
167         if (result->merge_perms == NULL)
168                 goto err_out;
169         result->merge_object_ids = malloc(result->labels_alloc * sizeof(int));
170         if (result->merge_object_ids == NULL)
171                 goto err_out;
172
173         result->label_hash = calloc(DICT_HASH_SIZE, sizeof(struct smack_hash_entry));
174         if (result->label_hash == NULL)
175                 goto err_out;
176
177         result->page_size = sysconf(_SC_PAGESIZE);
178         *accesses = result;
179         return 0;
180
181 err_out:
182         free(result->merge_object_ids);
183         free(result->merge_perms);
184         free(result->labels);
185         free(result);
186         return -1;
187 }
188
189 void smack_accesses_free(struct smack_accesses *handle)
190 {
191         struct smack_rule *rule;
192         struct smack_rule *next_rule;
193         int i;
194
195         if (handle == NULL)
196                 return;
197
198         for (i = 0; i < handle->labels_cnt; ++i) {
199                 rule = handle->labels[i]->first_rule;
200                 while (rule != NULL) {
201                         next_rule = rule->next_rule;
202                         free(rule);
203                         rule = next_rule;
204                 }
205                 free(handle->labels[i]->label);
206                 free(handle->labels[i]);
207         }
208
209         free(handle->label_hash);
210         free(handle->merge_object_ids);
211         free(handle->merge_perms);
212         free(handle->labels);
213         free(handle);
214 }
215
216 int smack_accesses_save(struct smack_accesses *handle, int fd)
217 {
218         struct smack_file_buffer buffer;
219         int ret;
220
221         buffer.fd = fd;
222         buffer.buf = malloc(handle->page_size + LOAD_LEN);
223         if (buffer.buf == NULL)
224                 return -1;
225
226         ret = accesses_print(handle, 0, 1, 1, &buffer, &buffer);
227         free(buffer.buf);
228         return ret;
229 }
230
231 int smack_accesses_apply(struct smack_accesses *handle)
232 {
233         return accesses_apply(handle, 0);
234 }
235
236 int smack_accesses_clear(struct smack_accesses *handle)
237 {
238         return accesses_apply(handle, 1);
239 }
240
241 static int accesses_add(struct smack_accesses *handle, const char *subject,
242                  const char *object, const char *allow_access_type,
243                  const char *deny_access_type)
244 {
245         struct smack_rule *rule;
246         struct smack_label *subject_label;
247         struct smack_label *object_label;
248
249         rule = calloc(sizeof(struct smack_rule), 1);
250         if (rule == NULL)
251                 return -1;
252
253         subject_label = label_add(handle, subject);
254         if (subject_label == NULL)
255                 goto err_out;
256         object_label = label_add(handle, object);
257         if (object_label == NULL)
258                 goto err_out;
259
260         if (subject_label->len > SHORT_LABEL_LEN ||
261             object_label->len > SHORT_LABEL_LEN)
262                 handle->has_long = 1;
263
264         rule->object_id = object_label->id;
265
266         rule->perm.allow_code = str_to_access_code(allow_access_type);
267         if (rule->perm.allow_code == -1)
268                 goto err_out;
269
270         if (deny_access_type != NULL) {
271                 rule->perm.deny_code = str_to_access_code(deny_access_type);
272                 if (rule->perm.deny_code == -1)
273                         goto err_out;
274         } else
275                 rule->perm.deny_code = ACCESS_TYPE_ALL & ~rule->perm.allow_code;
276
277         if (subject_label->first_rule == NULL) {
278                 subject_label->first_rule = subject_label->last_rule = rule;
279         } else {
280                 subject_label->last_rule->next_rule = rule;
281                 subject_label->last_rule = rule;
282         }
283
284         return 0;
285 err_out:
286         free(rule);
287         return -1;
288 }
289
290 int smack_accesses_add(struct smack_accesses *handle, const char *subject,
291                        const char *object, const char *access_type)
292 {
293         return accesses_add(handle, subject, object, access_type, NULL);
294 }
295
296 int smack_accesses_add_modify(struct smack_accesses *handle,
297                               const char *subject,
298                               const char *object,
299                               const char *allow_access_type,
300                               const char *deny_access_type)
301 {
302         return accesses_add(handle, subject, object,
303                 allow_access_type, deny_access_type);
304 }
305
306 int smack_accesses_add_from_file(struct smack_accesses *accesses, int fd)
307 {
308         FILE *file = NULL;
309         char buf[LOAD_LEN + 1];
310         char *ptr;
311         const char *subject, *object, *access, *access2;
312         int newfd;
313         int ret;
314
315         newfd = dup(fd);
316         if (newfd == -1)
317                 return -1;
318
319         file = fdopen(newfd, "r");
320         if (file == NULL) {
321                 close(newfd);
322                 return -1;
323         }
324
325         while (fgets(buf, LOAD_LEN + 1, file) != NULL) {
326                 if (strcmp(buf, "\n") == 0)
327                         continue;
328                 subject = strtok_r(buf, " \t\n", &ptr);
329                 object = strtok_r(NULL, " \t\n", &ptr);
330                 access = strtok_r(NULL, " \t\n", &ptr);
331                 access2 = strtok_r(NULL, " \t\n", &ptr);
332
333                 if (subject == NULL || object == NULL || access == NULL ||
334                     strtok_r(NULL, " \t\n", &ptr) != NULL) {
335                         fclose(file);
336                         return -1;
337                 }
338
339                 if (access2 == NULL)
340                         ret = smack_accesses_add(accesses, subject, object, access);
341                 else
342                         ret = smack_accesses_add_modify(accesses, subject, object, access, access2);
343
344                 if (ret) {
345                         fclose(file);
346                         return -1;
347                 }
348         }
349
350         if (ferror(file)) {
351                 fclose(file);
352                 return -1;
353         }
354
355         fclose(file);
356         return 0;
357 }
358
359 int smack_have_access(const char *subject, const char *object,
360                       const char *access_type)
361 {
362         char buf[LOAD_LEN + 1];
363         char str[ACC_LEN + 1];
364         int code;
365         int ret;
366         int fd;
367         int use_long = 1;
368         ssize_t slen;
369         ssize_t olen;
370
371         if (init_smackfs_mnt())
372                 return -1;
373
374         slen = get_label(NULL, subject, NULL);
375         olen = get_label(NULL, object, NULL);
376
377         if (slen < 0 || olen < 0)
378                 return -1;
379
380         fd = open_smackfs_file("access2", "access", O_RDWR, &use_long);
381         if (fd < 0)
382                 return -1;
383
384         if (!use_long && (slen > SHORT_LABEL_LEN || olen > SHORT_LABEL_LEN))  {
385                 close(fd);
386                 return -1;
387         }
388
389         if ((code = str_to_access_code(access_type)) < 0)
390                 return -1;
391         access_code_to_str(code, str);
392
393         if (use_long)
394                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_LONG_FORMAT,
395                                subject, object, str);
396         else
397                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_SHORT_FORMAT,
398                                subject, object, str);
399
400         if (ret < 0) {
401                 close(fd);
402                 return -1;
403         }
404
405         ret = write(fd, buf, strlen(buf));
406         if (ret < 0) {
407                 close(fd);
408                 return -1;
409         }
410
411         ret = read(fd, buf, 1);
412         close(fd);
413         if (ret < 0)
414                 return -1;
415
416         return buf[0] == '1';
417 }
418
419 int smack_cipso_new(struct smack_cipso **cipso)
420 {
421         struct smack_cipso *result;
422
423         result = calloc(sizeof(struct smack_cipso), 1);
424         if (result == NULL)
425                 return -1;
426
427         *cipso = result;
428         return 0;
429 }
430
431 void smack_cipso_free(struct smack_cipso *cipso)
432 {
433         if (cipso == NULL)
434                 return;
435
436         struct cipso_mapping *mapping = cipso->first;
437         struct cipso_mapping *next_mapping = NULL;
438
439         while (mapping != NULL) {
440                 next_mapping = mapping->next;
441                 free(mapping);
442                 mapping = next_mapping;
443         }
444
445         free(cipso);
446 }
447
448 int smack_cipso_apply(struct smack_cipso *cipso)
449 {
450         struct cipso_mapping *m = NULL;
451         char buf[CIPSO_MAX_SIZE];
452         int fd;
453         int i;
454         int offset;
455         int use_long;
456
457         if (init_smackfs_mnt())
458                 return -1;
459
460         fd = open_smackfs_file("cipso2", "cipso", O_WRONLY, &use_long);
461         if (fd < 0)
462                 return -1;
463
464         if (!use_long && cipso->has_long)
465                 return -1;
466
467         memset(buf,0,CIPSO_MAX_SIZE);
468         for (m = cipso->first; m != NULL; m = m->next) {
469                 offset = (int)snprintf(buf, SMACK_LABEL_LEN + 1, 
470                      use_long ? "%s " : "%-23s ", m->label);
471
472                 sprintf(&buf[offset], CIPSO_NUM_LEN_STR, m->level);
473                 offset += NUM_LEN;
474
475                 sprintf(&buf[offset], CIPSO_NUM_LEN_STR, m->ncats);
476                 offset += NUM_LEN;
477
478                 for (i = 0; i < CAT_MAX_COUNT; i++) {
479                         if (BITTEST(m->cats, i)) {
480                                 sprintf(&buf[offset], CIPSO_NUM_LEN_STR, i + 1);
481                                 offset += NUM_LEN;
482                         }
483                 }
484
485                 if (write(fd, buf, offset) < 0) {
486                         close(fd);
487                         return -1;
488                 }
489         }
490
491         close(fd);
492         return 0;
493 }
494
495 int smack_cipso_add_from_file(struct smack_cipso *cipso, int fd)
496 {
497         struct cipso_mapping *mapping = NULL;
498         FILE *file = NULL;
499         char buf[BUF_SIZE];
500         char *label, *level, *cat, *ptr;
501         long int val;
502         int i;
503         int newfd;
504
505         newfd = dup(fd);
506         if (newfd == -1)
507                 return -1;
508
509         file = fdopen(newfd, "r");
510         if (file == NULL) {
511                 close(newfd);
512                 return -1;
513         }
514
515         while (fgets(buf, BUF_SIZE, file) != NULL) {
516                 mapping = calloc(sizeof(struct cipso_mapping), 1);
517                 if (mapping == NULL)
518                         goto err_out;
519
520                 label = strtok_r(buf, " \t\n", &ptr);
521                 level = strtok_r(NULL, " \t\n", &ptr);
522                 cat = strtok_r(NULL, " \t\n", &ptr);
523
524                 if (level == NULL)
525                         goto err_out;
526
527                 val  = get_label(mapping->label, label, NULL);
528                 if (val < 0)
529                         goto err_out;
530                 if (val > SHORT_LABEL_LEN)
531                         cipso->has_long = 1;
532
533                 errno = 0;
534                 val = strtol(level, NULL, 10);
535                 if (errno)
536                         goto err_out;
537
538                 if (val < 0 || val > LEVEL_MAX)
539                         goto err_out;
540
541                 mapping->level = val;
542
543                 for (i = 0; i < CAT_MAX_COUNT && cat != NULL; i++) {
544                         errno = 0;
545                         val = strtol(cat, NULL, 10);
546                         if (errno)
547                                 goto err_out;
548
549                         if (val <= 0 || val > CAT_MAX_COUNT)
550                                 goto err_out;
551
552                         if (!BITTEST(mapping->cats, val - 1)) {
553                                 BITSET(mapping->cats, val - 1);
554                                 ++(mapping->ncats);
555                         }
556
557                         cat = strtok_r(NULL, " \t\n", &ptr);
558                 }
559
560                 if (cipso->first == NULL) {
561                         cipso->first = cipso->last = mapping;
562                 } else {
563                         cipso->last->next = mapping;
564                         cipso->last = mapping;
565                 }
566         }
567
568         if (ferror(file))
569                 goto err_out;
570
571         fclose(file);
572         return 0;
573 err_out:
574         fclose(file);
575         free(mapping);
576         return -1;
577 }
578
579 const char *smack_smackfs_path(void)
580 {
581         init_smackfs_mnt();
582         return smackfs_mnt;
583 }
584
585 ssize_t smack_new_label_from_self(char **label)
586 {
587         char *result;
588         int fd;
589         int ret;
590
591         result = calloc(SMACK_LABEL_LEN + 1, 1);
592         if (result == NULL)
593                 return -1;
594
595         fd = open(SELF_LABEL_FILE, O_RDONLY);
596         if (fd < 0) {
597                 free(result);
598                 return -1;
599         }
600
601         ret = read(fd, result, SMACK_LABEL_LEN);
602         close(fd);
603         if (ret < 0) {
604                 free(result);
605                 return -1;
606         }
607
608         *label = result;
609         return ret;
610 }
611
612 ssize_t smack_new_label_from_socket(int fd, char **label)
613 {
614         char dummy;
615         int ret;
616         socklen_t length = 1;
617         char *result;
618
619         ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, &dummy, &length);
620         if (ret < 0 && errno != ERANGE)
621                 return -1;
622
623         result = calloc(length + 1, 1);
624         if (result == NULL)
625                 return -1;
626
627         ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, result, &length);
628         if (ret < 0) {
629                 free(result);
630                 return -1;
631         }
632
633         *label = result;
634         return length;
635 }
636
637 ssize_t smack_new_label_from_path(const char *path, const char *xattr, 
638                                   int follow, char **label)
639 {
640         char buf[SMACK_LABEL_LEN + 1];
641         char *result;
642         ssize_t ret = 0;
643
644         ret = follow ?
645                 getxattr(path, xattr, buf, SMACK_LABEL_LEN + 1) :
646                 lgetxattr(path, xattr, buf, SMACK_LABEL_LEN + 1);
647         if (ret < 0)
648                 return -1;
649         buf[ret] = '\0';
650
651         result = calloc(ret + 1, 1);
652         if (result == NULL)
653                 return -1;
654
655         ret = get_label(result, buf, NULL);
656         if (ret < 0) {
657                 free(result);
658                 return -1;
659         }
660
661         *label = result;
662         return ret;
663 }
664
665 int smack_set_label_for_self(const char *label)
666 {
667         int len;
668         int fd;
669         int ret;
670
671         len = get_label(NULL, label, NULL);
672         if (len < 0)
673                 return -1;
674
675         fd = open(SELF_LABEL_FILE, O_WRONLY);
676         if (fd < 0)
677                 return -1;
678
679         ret = write(fd, label, len);
680         close(fd);
681
682         return (ret < 0) ? -1 : 0;
683 }
684
685 int smack_revoke_subject(const char *subject)
686 {
687         int ret;
688         int fd;
689         int len;
690
691         if (init_smackfs_mnt())
692                 return -1;
693
694         len = get_label(NULL, subject, NULL);
695         if (len < 0)
696                 return -1;
697
698         fd = openat(smackfs_mnt_dirfd, "revoke-subject", O_WRONLY);
699         if (fd < 0)
700                 return -1;
701
702         ret = write(fd, subject, len);
703         close(fd);
704
705         return (ret < 0) ? -1 : 0;
706 }
707
708 static int internal_getlabel(void* file, char** label,
709                 enum smack_label_type type,
710                 getxattr_func getfunc)
711 {
712         char* xattr_name = get_xattr_name(type);
713         char value[SMACK_LABEL_LEN + 1];
714         int ret;
715
716         ret = getfunc(file, xattr_name, value, SMACK_LABEL_LEN + 1);
717         if (ret == -1) {
718                 if (errno == ENODATA) {
719                         *label = NULL;
720                         return 0;
721                 }
722                 return -1;
723         }
724
725         value[ret] = '\0';
726         *label = calloc(ret + 1, 1);
727         if (*label == NULL)
728                 return -1;
729         strncpy(*label, value, ret);
730         return 0;
731 }
732
733 static int internal_setlabel(void* file, const char* label,
734                 enum smack_label_type type,
735                 setxattr_func setfunc, removexattr_func removefunc)
736 {
737         char* xattr_name = get_xattr_name(type);
738
739         /* Check validity of labels for LABEL_TRANSMUTE */
740         if (type == SMACK_LABEL_TRANSMUTE && label != NULL) {
741                 if (!strcmp(label, "0"))
742                         label = NULL;
743                 else if (!strcmp(label, "1"))
744                         label = "TRUE";
745                 else
746                         return -1;
747         }
748
749         if (label == NULL || label[0] == '\0') {
750                 return removefunc(file, xattr_name);
751         } else {
752                 int len = strnlen(label, SMACK_LABEL_LEN + 1);
753                 if (len > SMACK_LABEL_LEN)
754                         return -1;
755                 return setfunc(file, xattr_name, label, len, 0);
756         }
757 }
758
759 int smack_getlabel(const char *path, char** label,
760                 enum smack_label_type type)
761 {
762         return internal_getlabel((void*) path, label, type,
763                         (getxattr_func) getxattr);
764 }
765
766 int smack_lgetlabel(const char *path, char** label,
767                 enum smack_label_type type)
768 {
769         return internal_getlabel((void*) path, label, type,
770                         (getxattr_func) lgetxattr);
771 }
772
773 int smack_fgetlabel(int fd, char** label,
774                 enum smack_label_type type)
775 {
776         return internal_getlabel((void*) ((unsigned long) fd), label, type,
777                         (getxattr_func) fgetxattr);
778 }
779
780 int smack_setlabel(const char *path, const char* label,
781                 enum smack_label_type type)
782 {
783         return internal_setlabel((void*) path, label, type,
784                         (setxattr_func) setxattr, (removexattr_func) removexattr);
785 }
786
787 int smack_lsetlabel(const char *path, const char* label,
788                 enum smack_label_type type)
789 {
790         return internal_setlabel((void*) path, label, type,
791                         (setxattr_func) lsetxattr, (removexattr_func) lremovexattr);
792 }
793
794 int smack_fsetlabel(int fd, const char* label,
795                 enum smack_label_type type)
796 {
797         return internal_setlabel((void*) ((unsigned long) fd), label, type,
798                         (setxattr_func) fsetxattr, (removexattr_func) fremovexattr);
799 }
800
801 static int open_smackfs_file(const char *long_name, const char *short_name,
802                              mode_t mode, int *use_long)
803 {
804         int fd;
805
806         fd = openat(smackfs_mnt_dirfd, long_name, mode);
807         if (fd < 0) {
808                 if (errno != ENOENT)
809                         return -1;
810
811                 fd = openat(smackfs_mnt_dirfd, short_name, mode);
812                 if (fd < 0)
813                         return -1;
814
815                 *use_long = 0;
816                 return fd;
817         }
818
819         *use_long = 1;
820         return fd;
821 }
822
823 static inline int check_multiline(int change_fd)
824 {
825         /* This string will be written to kernel Smack "change-rule" interface
826          * to check if it can handle multiple rules in one write.
827          * It consists of two rules, separated by '\n': first that does nothing
828          * and second that has invalid format. If kernel parses only the first
829          * line (pre-3.12 behavior), it won't see the invalid rule and succeed.
830          * If it parses both lines, an error will be returned.
831          */
832         static const char test_str[] = "^ ^ - -\n-";
833         int ret;
834
835         ret = write(change_fd, test_str, sizeof(test_str) - 1);
836         if (ret == -1 && errno == EINVAL)
837                 return 1;
838         return 0;
839 }
840
841 static int accesses_apply(struct smack_accesses *handle, int clear)
842 {
843         int ret;
844         int use_long = 1;
845         int multiline = 0;
846         struct smack_file_buffer load_buffer = {.fd = -1, .buf = NULL};
847         struct smack_file_buffer change_buffer = {.fd = -1, .buf = NULL};
848
849         if (init_smackfs_mnt())
850                 return -1;
851
852         load_buffer.fd = open_smackfs_file("load2", "load", O_WRONLY, &use_long);
853         if (load_buffer.fd < 0)
854                 return -1;
855         load_buffer.buf = malloc(handle->page_size + LOAD_LEN);
856         if (load_buffer.buf == NULL)
857                 goto err_out;
858
859         change_buffer.fd = openat(smackfs_mnt_dirfd, "change-rule", O_WRONLY);
860         if (change_buffer.fd >= 0) {
861                 change_buffer.buf = malloc(handle->page_size + LOAD_LEN);
862                 if (change_buffer.buf == NULL)
863                         goto err_out;
864
865                 multiline = check_multiline(change_buffer.fd);
866         } else {
867                 /* Try to continue if "change-rule" doesn't exist, we might
868                  * not need it. */
869                 if (errno != ENOENT)
870                         goto err_out;
871         }
872
873         ret = accesses_print(handle, clear, use_long, multiline,
874                 &load_buffer, &change_buffer);
875         goto out;
876
877 err_out:
878         ret = -1;
879 out:
880         if (load_buffer.fd >= 0)
881                 close(load_buffer.fd);
882         if (change_buffer.fd >= 0)
883                 close(change_buffer.fd);
884         free(load_buffer.buf);
885         free(change_buffer.buf);
886         return ret;
887 }
888
889 static int buffer_flush(struct smack_file_buffer *buf)
890 {
891         int pos;
892         int ret;
893
894         /* Write buffered bytes to kernel, up to flush_pos */
895         for (pos = 0; pos < buf->flush_pos; ) {
896                 ret = write(buf->fd, buf->buf + pos, buf->flush_pos - pos);
897                 if (ret == -1) {
898                         if (errno != EINTR)
899                                 return -1;
900                 } else
901                         pos += ret;
902         }
903
904         /* Move remaining, not flushed bytes to the buffer start */
905         memcpy(buf->buf, buf->buf + pos, buf->pos - pos);
906         buf->pos -= pos;
907         buf->flush_pos = 0;
908
909         return 0;
910 }
911
912 static inline void rule_print_long(char *buf, int *pos,
913         struct smack_label *subject_label, struct smack_label *object_label,
914         const char *allow_str, const char *deny_str)
915 {
916         memcpy(buf + *pos, subject_label->label, subject_label->len);
917         *pos += subject_label->len;
918         buf[(*pos)++] = ' ';
919         memcpy(buf + *pos, object_label->label, object_label->len);
920         *pos += object_label->len;
921         buf[(*pos)++] = ' ';
922         memcpy(buf + *pos, allow_str, ACC_LEN);
923         *pos += ACC_LEN;
924         if (deny_str != NULL) {
925                 buf[(*pos)++] = ' ';
926                 memcpy(buf + *pos, deny_str, ACC_LEN);
927                 *pos += ACC_LEN;
928         }
929 }
930
931 static int accesses_print(struct smack_accesses *handle, int clear,
932                           int use_long, int multiline,
933                           struct smack_file_buffer *load_buffer,
934                           struct smack_file_buffer *change_buffer)
935 {
936         struct smack_file_buffer *buffer;
937         char allow_str[ACC_LEN + 1];
938         char deny_str[ACC_LEN + 1];
939         struct smack_label *subject_label;
940         struct smack_label *object_label;
941         struct smack_rule *rule;
942         union smack_perm *perm;
943         int merge_cnt;
944         int x;
945         int y;
946
947         if (!use_long && handle->has_long)
948                 return -1;
949
950         load_buffer->pos = 0;
951         change_buffer->pos = 0;
952         bzero(handle->merge_perms, handle->labels_cnt * sizeof(union smack_perm));
953         for (x = 0; x < handle->labels_cnt; ++x) {
954                 subject_label = handle->labels[x];
955                 merge_cnt = 0;
956                 for (rule = subject_label->first_rule; rule != NULL; rule = rule->next_rule) {
957                         perm = &(handle->merge_perms[rule->object_id]);
958                         if (perm->allow_deny_code == 0)
959                                 handle->merge_object_ids[merge_cnt++] = rule->object_id;
960
961                         if (clear) {
962                                 perm->allow_code = 0;
963                                 perm->deny_code  = ACCESS_TYPE_ALL;
964                         } else {
965                                 perm->allow_code |=  rule->perm.allow_code;
966                                 perm->allow_code &= ~rule->perm.deny_code;
967                                 perm->deny_code  &= ~rule->perm.allow_code;
968                                 perm->deny_code  |=  rule->perm.deny_code;
969                         }
970                 }
971
972                 for (y = 0; y < merge_cnt; ++y) {
973                         object_label = handle->labels[handle->merge_object_ids[y]];
974                         perm = &(handle->merge_perms[object_label->id]);
975                         access_code_to_str(perm->allow_code, allow_str);
976
977                         if ((perm->allow_code | perm->deny_code) != ACCESS_TYPE_ALL) {
978                                 /* Fail immediately without doing any further processing
979                                    if modify rules are not supported. */
980                                 if (change_buffer->fd < 0)
981                                         return -1;
982
983                                 buffer = change_buffer;
984                                 buffer->flush_pos = buffer->pos;
985                                 access_code_to_str(perm->deny_code, deny_str);
986                                 rule_print_long(buffer->buf, &(buffer->pos),
987                                         subject_label, object_label, allow_str, deny_str);
988                         } else {
989                                 buffer = load_buffer;
990                                 buffer->flush_pos = buffer->pos;
991                                 if (use_long)
992                                         rule_print_long(buffer->buf, &(buffer->pos),
993                                                 subject_label, object_label, allow_str, NULL);
994                                 else
995                                         buffer->pos += sprintf(buffer->buf + buffer->pos,
996                                                 KERNEL_SHORT_FORMAT,
997                                                 subject_label->label, object_label->label,
998                                                 allow_str);
999                         }
1000                         perm->allow_deny_code = 0;
1001
1002                         if (multiline) {
1003                                 buffer->buf[buffer->pos++] = '\n';
1004                                 if (buffer->pos >= handle->page_size)
1005                                         if (buffer_flush(buffer))
1006                                                 return -1;
1007                         } else {
1008                                 /* When no multi-line is supported, just flush
1009                                  * the rule that was just generated */
1010                                 buffer->flush_pos = buffer->pos;
1011                                 if (buffer_flush(buffer))
1012                                         return -1;
1013                         }
1014                 }
1015         }
1016
1017         if (load_buffer->pos > 0) {
1018                 load_buffer->flush_pos = load_buffer->pos;
1019                 if (buffer_flush(load_buffer))
1020                         return -1;
1021         }
1022         if (change_buffer->pos > 0) {
1023                 change_buffer->flush_pos = change_buffer->pos;
1024                 if (buffer_flush(change_buffer))
1025                         return -1;
1026         }
1027
1028         return 0;
1029 }
1030
1031 static inline ssize_t get_label(char *dest, const char *src, unsigned int *hash)
1032 {
1033         int i;
1034         unsigned int h = 5381;/*DJB2 hashing function magic number*/;
1035
1036         if (!src || src[0] == '\0' || src[0] == '-')
1037                 return -1;
1038
1039         for (i = 0; i < (SMACK_LABEL_LEN + 1) && src[i]; i++) {
1040                 if (src[i] <= ' ' || src[i] > '~')
1041                         return -1;
1042                 switch (src[i]) {
1043                 case '/':
1044                 case '"':
1045                 case '\\':
1046                 case '\'':
1047                         return -1;
1048                 default:
1049                         break;
1050                 }
1051
1052                 if (dest)
1053                         dest[i] = src[i];
1054                 if (hash)
1055                         /* This efficient hash function,
1056                          * created by Daniel J. Bernstein,
1057                          * is known as DJB2 algorithm */
1058                         h = (h << 5) + h + src[i];
1059         }
1060
1061         if (dest && i < (SMACK_LABEL_LEN + 1))
1062                 dest[i] = '\0';
1063         if (hash)
1064                 *hash = h % DICT_HASH_SIZE;
1065
1066         return i < (SMACK_LABEL_LEN + 1) ? i : -1;
1067 }
1068
1069
1070 static inline int str_to_access_code(const char *str)
1071 {
1072         int i;
1073         unsigned int code = 0;
1074
1075         for (i = 0; str[i] != '\0'; i++) {
1076                 switch (str[i]) {
1077                 case 'r':
1078                 case 'R':
1079                         code |= ACCESS_TYPE_R;
1080                         break;
1081                 case 'w':
1082                 case 'W':
1083                         code |= ACCESS_TYPE_W;
1084                         break;
1085                 case 'x':
1086                 case 'X':
1087                         code |= ACCESS_TYPE_X;
1088                         break;
1089                 case 'a':
1090                 case 'A':
1091                         code |= ACCESS_TYPE_A;
1092                         break;
1093                 case 't':
1094                 case 'T':
1095                         code |= ACCESS_TYPE_T;
1096                         break;
1097                 case 'l':
1098                 case 'L':
1099                         code |= ACCESS_TYPE_L;
1100                         break;
1101                 case '-':
1102                         break;
1103                 default:
1104                         return -1;
1105                 }
1106         }
1107
1108         return code;
1109 }
1110
1111 static inline void access_code_to_str(unsigned int code, char *str)
1112 {
1113         str[0] = ((code & ACCESS_TYPE_R) != 0) ? 'r' : '-';
1114         str[1] = ((code & ACCESS_TYPE_W) != 0) ? 'w' : '-';
1115         str[2] = ((code & ACCESS_TYPE_X) != 0) ? 'x' : '-';
1116         str[3] = ((code & ACCESS_TYPE_A) != 0) ? 'a' : '-';
1117         str[4] = ((code & ACCESS_TYPE_T) != 0) ? 't' : '-';
1118         str[5] = ((code & ACCESS_TYPE_L) != 0) ? 'l' : '-';
1119         str[6] = '\0';
1120 }
1121
1122 static inline char* get_xattr_name(enum smack_label_type type)
1123 {
1124         switch (type) {
1125         case SMACK_LABEL_ACCESS:
1126                 return "security.SMACK64";
1127         case SMACK_LABEL_EXEC:
1128                 return "security.SMACK64EXEC";
1129         case SMACK_LABEL_MMAP:
1130                 return "security.SMACK64MMAP";
1131         case SMACK_LABEL_TRANSMUTE:
1132                 return "security.SMACK64TRANSMUTE";
1133         case SMACK_LABEL_IPIN:
1134                 return "security.SMACK64IPIN";
1135         case SMACK_LABEL_IPOUT:
1136                 return "security.SMACK64IPOUT";
1137         default:
1138                 /* Should not reach this point */
1139                 return NULL;
1140         }
1141 }
1142
1143 static inline struct smack_label *
1144 is_label_known(struct smack_accesses *handle, const char *label, int hash)
1145 {
1146         struct smack_label *lab = handle->label_hash[hash].first;
1147         while (lab != NULL && strcmp(label, lab->label) != 0)
1148                 lab = lab->next_label;
1149         return lab;
1150 }
1151
1152 static inline int accesses_resize(struct smack_accesses *handle)
1153 {
1154         struct smack_label **labels;
1155         union smack_perm *merge_perms;
1156         int *merge_object_ids;
1157         int alloc = handle->labels_alloc << 1;
1158
1159         labels = realloc(handle->labels, alloc * sizeof(struct smack_label *));
1160         if (labels == NULL)
1161                 return -1;
1162         handle->labels = labels;
1163
1164         merge_perms = realloc(handle->merge_perms, alloc * sizeof(union smack_perm));
1165         if (merge_perms == NULL)
1166                 return -1;
1167         handle->merge_perms = merge_perms;
1168
1169         merge_object_ids = realloc(handle->merge_object_ids, alloc * sizeof(int));
1170         if (merge_object_ids == NULL)
1171                 return -1;
1172         handle->merge_object_ids = merge_object_ids;
1173
1174         handle->labels_alloc = alloc;
1175         return 0;
1176 }
1177
1178 static struct smack_label *label_add(struct smack_accesses *handle, const char *label)
1179 {
1180         struct smack_hash_entry *hash_entry;
1181         unsigned int hash_value = 0;
1182         struct smack_label *new_label;
1183         int len;
1184
1185         len = get_label(NULL, label, &hash_value);
1186         if (len == -1)
1187                 return NULL;
1188
1189         new_label = is_label_known(handle, label, hash_value);
1190         if (new_label == NULL) {/*no entry added yet*/
1191                 if (handle->labels_cnt == handle->labels_alloc)
1192                         if (accesses_resize(handle))
1193                                 return NULL;
1194
1195                 new_label = malloc(sizeof(struct smack_label));
1196                 if (new_label == NULL)
1197                         return NULL;
1198                 new_label->label = malloc(len + 1);
1199                 if (new_label->label == NULL) {
1200                         free(new_label);
1201                         return NULL;
1202                 }
1203
1204                 memcpy(new_label->label, label, len + 1);
1205                 new_label->id = handle->labels_cnt;
1206                 new_label->len = len;
1207                 new_label->first_rule = NULL;
1208                 new_label->last_rule = NULL;
1209                 new_label->next_label = NULL;
1210                 hash_entry = &(handle->label_hash[hash_value]);
1211                 if (hash_entry->first == NULL) {
1212                         hash_entry->first = new_label;
1213                         hash_entry->last = new_label;
1214                 } else {
1215                         hash_entry->last->next_label = new_label;
1216                         hash_entry->last = new_label;
1217                 }
1218                 handle->labels[handle->labels_cnt++] = new_label;
1219         }
1220
1221         return new_label;
1222 }