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