libsmack: parse whole access type string, not only first 5 bytes.
[framework/security/smack.git] / libsmack / libsmack.c
1 /*
2  * This file is part of libsmack
3  *
4  * Copyright (C) 2010 Nokia Corporation
5  * Copyright (C) 2011 Intel Corporation
6  * Copyright (C) 2012 Samsung Electronics Co.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1 as published by the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  *
22  * Authors:
23  * Jarkko Sakkinen <jarkko.sakkinen@intel.com>
24  * Brian McGillion <brian.mcgillion@intel.com>
25  * Passion Zhao <passion.zhao@intel.com>
26  * Rafal Krypa <r.krypa@samsung.com>
27  */
28
29 #include "sys/smack.h"
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/socket.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <unistd.h>
39 #include <limits.h>
40
41 #define ACC_LEN 5
42 #define LOAD_LEN (2 * (SMACK_LABEL_LEN + 1) + ACC_LEN)
43
44 #define LEVEL_MAX 255
45 #define NUM_LEN 4
46 #define BUF_SIZE 512
47 #define CAT_MAX_COUNT 240
48 #define CAT_MAX_VALUE 63
49 #define CIPSO_POS(i)   (SMACK_LABEL_LEN + 1 + NUM_LEN + NUM_LEN + i * NUM_LEN)
50 #define CIPSO_MAX_SIZE CIPSO_POS(CAT_MAX_COUNT)
51 #define CIPSO_NUM_LEN_STR "%-4d"
52
53 #define ACC_R 0x01
54 #define ACC_W 0x02
55 #define ACC_X 0x04
56 #define ACC_A 0x08
57 #define ACC_T 0x10
58
59 #define KERNEL_LONG_FORMAT "%s %s %s"
60 #define KERNEL_SHORT_FORMAT "%-23s %-23s %5s"
61 #define READ_BUF_SIZE LOAD_LEN + 1
62 #define SELF_LABEL_FILE "/proc/self/attr/current"
63
64 extern char *smack_mnt;
65
66 struct smack_rule {
67         char subject[SMACK_LABEL_LEN + 1];
68         char object[SMACK_LABEL_LEN + 1];
69         int access_code;
70         struct smack_rule *next;
71 };
72
73 struct smack_accesses {
74         struct smack_rule *first;
75         struct smack_rule *last;
76 };
77
78 struct cipso_mapping {
79         char label[SMACK_LABEL_LEN + 1];
80         int cats[CAT_MAX_VALUE];
81         int ncats;
82         int level;
83         struct cipso_mapping *next;
84 };
85
86 struct smack_cipso {
87         struct cipso_mapping *first;
88         struct cipso_mapping *last;
89 };
90
91 static int accesses_apply(struct smack_accesses *handle, int clear);
92 static inline int access_type_to_int(const char *access_type);
93 static inline void int_to_access_type_c(unsigned ac, char *str);
94 static inline void int_to_access_type_k(unsigned ac, char *str);
95
96 int smack_accesses_new(struct smack_accesses **accesses)
97 {
98         struct smack_accesses *result;
99
100         result = calloc(sizeof(struct smack_accesses), 1);
101         if (result == NULL)
102                 return -1;
103
104         *accesses = result;
105         return 0;
106 }
107
108 void smack_accesses_free(struct smack_accesses *handle)
109 {
110         if (handle == NULL)
111                 return;
112
113         struct smack_rule *rule = handle->first;
114         struct smack_rule *next_rule = NULL;
115
116         while (rule != NULL) {
117                 next_rule = rule->next;
118                 free(rule);
119                 rule = next_rule;
120         }
121
122         free(handle);
123 }
124
125 int smack_accesses_save(struct smack_accesses *handle, int fd)
126 {
127         struct smack_rule *rule = handle->first;
128         char access_type[ACC_LEN + 1];
129         FILE *file;
130         int ret;
131         int newfd;
132
133         newfd = dup(fd);
134         if (newfd == -1)
135                 return -1;
136
137         file = fdopen(newfd, "w");
138         if (file == NULL) {
139                 close(newfd);
140                 return -1;
141         }
142
143         while (rule) {
144                 int_to_access_type_c(rule->access_code, access_type);
145
146                 ret = fprintf(file, "%s %s %s\n",
147                               rule->subject, rule->object, access_type);
148                 if (ret < 0) {
149                         fclose(file);
150                         return -1;
151                 }
152
153                 rule = rule->next;
154         }
155
156         fclose(file);
157         return 0;
158 }
159
160 int smack_accesses_apply(struct smack_accesses *handle)
161 {
162         return accesses_apply(handle, 0);
163 }
164
165 int smack_accesses_clear(struct smack_accesses *handle)
166 {
167         return accesses_apply(handle, 1);
168 }
169
170 int smack_accesses_add(struct smack_accesses *handle, const char *subject,
171                        const char *object, const char *access_type)
172 {
173         struct smack_rule *rule = NULL;
174
175         rule = calloc(sizeof(struct smack_rule), 1);
176         if (rule == NULL)
177                 return -1;
178
179         strncpy(rule->subject, subject, SMACK_LABEL_LEN + 1);
180         strncpy(rule->object, object, SMACK_LABEL_LEN + 1);
181         rule->access_code = access_type_to_int(access_type);
182
183         if (handle->first == NULL) {
184                 handle->first = handle->last = rule;
185         } else {
186                 handle->last->next = rule;
187                 handle->last = rule;
188         }
189
190         return 0;
191 }
192
193 int smack_accesses_add_from_file(struct smack_accesses *accesses, int fd)
194 {
195         FILE *file = NULL;
196         char buf[READ_BUF_SIZE];
197         char *ptr;
198         const char *subject, *object, *access;
199         int newfd;
200
201         newfd = dup(fd);
202         if (newfd == -1)
203                 return -1;
204
205         file = fdopen(newfd, "r");
206         if (file == NULL) {
207                 close(newfd);
208                 return -1;
209         }
210
211         while (fgets(buf, READ_BUF_SIZE, file) != NULL) {
212                 if (strcmp(buf, "\n") == 0)
213                         continue;
214                 subject = strtok_r(buf, " \t\n", &ptr);
215                 object = strtok_r(NULL, " \t\n", &ptr);
216                 access = strtok_r(NULL, " \t\n", &ptr);
217
218                 if (subject == NULL || object == NULL || access == NULL ||
219                     strtok_r(NULL, " \t\n", &ptr) != NULL) {
220                         errno = EINVAL;
221                         fclose(file);
222                         return -1;
223                 }
224
225                 if (smack_accesses_add(accesses, subject, object, access)) {
226                         fclose(file);
227                         return -1;
228                 }
229         }
230
231         if (ferror(file)) {
232                 fclose(file);
233                 return -1;
234         }
235
236         fclose(file);
237         return 0;
238 }
239
240 int smack_have_access(const char *subject, const char *object,
241                       const char *access_type)
242 {
243         char buf[LOAD_LEN + 1];
244         char access_type_k[ACC_LEN + 1];
245         unsigned access_code;
246         int ret;
247         int fd;
248         int access2 = 1;
249         char path[PATH_MAX];
250
251         if (!smack_mnt) {
252                 errno = EFAULT;
253                 return -1; 
254         }
255         
256         snprintf(path, sizeof path, "%s/access2", smack_mnt);
257         fd = open(path, O_RDWR);
258         if (fd < 0) {
259                 if (errno != ENOENT)
260                         return -1;
261                 
262                 snprintf(path, sizeof path, "%s/access", smack_mnt);
263                 fd = open(path, O_RDWR);
264                 if (fd < 0)
265                         return -1;
266                 access2 = 0;
267         }
268
269         access_code = access_type_to_int(access_type);
270         int_to_access_type_k(access_code, access_type_k);
271
272         if (access2)
273                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_LONG_FORMAT,
274                                subject, object, access_type_k);
275         else
276                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_SHORT_FORMAT,
277                                subject, object, access_type_k);
278
279         if (ret < 0) {
280                 close(fd);
281                 return -1;
282         }
283
284         ret = write(fd, buf, strlen(buf));
285         if (ret < 0) {
286                 close(fd);
287                 return -1;
288         }
289
290         ret = read(fd, buf, 1);
291         close(fd);
292         if (ret < 0)
293                 return -1;
294
295         return buf[0] == '1';
296 }
297 void smack_cipso_free(struct smack_cipso *cipso)
298 {
299         if (cipso == NULL)
300                 return;
301
302         struct cipso_mapping *mapping = cipso->first;
303         struct cipso_mapping *next_mapping = NULL;
304
305         while (mapping != NULL) {
306                 next_mapping = mapping->next;
307                 free(mapping);
308                 mapping = next_mapping;
309         }
310 }
311
312 struct smack_cipso *smack_cipso_new(int fd)
313 {
314         struct smack_cipso *cipso = NULL;
315         struct cipso_mapping *mapping = NULL;
316         FILE *file = NULL;
317         char buf[BUF_SIZE];
318         char *label, *level, *cat, *ptr;
319         long int val;
320         int i;
321         int newfd;
322
323         newfd = dup(fd);
324         if (newfd == -1)
325                 return NULL;
326
327         file = fdopen(newfd, "r");
328         if (file == NULL) {
329                 close(newfd);
330                 return NULL;
331         }
332
333         cipso = calloc(sizeof(struct smack_cipso ), 1);
334         if (cipso == NULL) {
335                 fclose(file);
336                 return NULL;
337         }
338
339         while (fgets(buf, BUF_SIZE, file) != NULL) {
340                 mapping = calloc(sizeof(struct cipso_mapping), 1);
341                 if (mapping == NULL)
342                         goto err_out;
343
344                 label = strtok_r(buf, " \t\n", &ptr);
345                 level = strtok_r(NULL, " \t\n", &ptr);
346                 cat = strtok_r(NULL, " \t\n", &ptr);
347                 if (label == NULL || cat == NULL || level == NULL ||
348                     strlen(label) > SMACK_LABEL_LEN) {
349                         errno = EINVAL;
350                         goto err_out;
351                 }
352
353                 strcpy(mapping->label, label);
354
355                 errno = 0;
356                 val = strtol(level, NULL, 10);
357                 if (errno)
358                         goto err_out;
359
360                 if (val < 0 || val > LEVEL_MAX) {
361                         errno = ERANGE;
362                         goto err_out;
363                 }
364
365                 mapping->level = val;
366
367                 for (i = 0; i < CAT_MAX_COUNT && cat != NULL; i++) {
368                         errno = 0;
369                         val = strtol(cat, NULL, 10);
370                         if (errno)
371                                 goto err_out;
372
373                         if (val < 0 || val > CAT_MAX_VALUE) {
374                                 errno = ERANGE;
375                                 goto err_out;
376                         }
377
378                         mapping->cats[i] = val;
379
380                         cat = strtok_r(NULL, " \t\n", &ptr);
381                 }
382
383                 mapping->ncats = i;
384
385                 if (cipso->first == NULL) {
386                         cipso->first = cipso->last = mapping;
387                 } else {
388                         cipso->last->next = mapping;
389                         cipso->last = mapping;
390                 }
391         }
392
393         if (ferror(file))
394                 goto err_out;
395
396         fclose(file);
397         return cipso;
398 err_out:
399         fclose(file);
400         smack_cipso_free(cipso);
401         free(mapping);
402         return NULL;
403 }
404
405 const char *smack_smackfs_path(void)
406 {
407         return smack_mnt;
408 }
409
410 int smack_cipso_apply(struct smack_cipso *cipso)
411 {
412         struct cipso_mapping *m = NULL;
413         char buf[CIPSO_MAX_SIZE];
414         int fd;
415         int i;
416         char path[PATH_MAX];
417         int offset=0;
418
419         if (!smack_mnt) {
420                 errno = EFAULT;
421                 return -1; 
422         }
423         
424         snprintf(path, sizeof path, "%s/cipso2", smack_mnt);
425         fd = open(path, O_WRONLY);
426         if (fd < 0)
427                 return -1;
428
429         memset(buf,0,CIPSO_MAX_SIZE);
430         for (m = cipso->first; m != NULL; m = m->next) {
431                 snprintf(buf, SMACK_LABEL_LEN + 1, "%s", m->label);
432                 offset += strlen(buf) + 1;
433   
434                 sprintf(&buf[offset], CIPSO_NUM_LEN_STR, m->level);
435                 offset += NUM_LEN;
436   
437                 sprintf(&buf[offset], CIPSO_NUM_LEN_STR, m->ncats);
438                 offset += NUM_LEN;
439  
440                 for (i = 0; i < m->ncats; i++){
441                         sprintf(&buf[offset], CIPSO_NUM_LEN_STR, m->cats[i]);
442                         offset += NUM_LEN;
443                 }
444                 
445                 if (write(fd, buf, offset) < 0) {
446                         close(fd);
447                         return -1;
448                 }
449         }
450
451         close(fd);
452         return 0;
453 }
454
455 int smack_new_label_from_self(char **label)
456 {
457         char *result;
458         int fd;
459         int ret;
460
461         result = calloc(SMACK_LABEL_LEN + 1, 1);
462         if (result == NULL)
463                 return -1;
464
465         fd = open(SELF_LABEL_FILE, O_RDONLY);
466         if (fd < 0) {
467                 free(result);
468                 return -1;
469         }
470
471         ret = read(fd, result, SMACK_LABEL_LEN);
472         close(fd);
473         if (ret < 0) {
474                 free(result);
475                 return -1;
476         }
477
478         *label = result;
479         return 0;
480 }
481
482 int smack_new_label_from_socket(int fd, char **label)
483 {
484         char dummy;
485         int ret;
486         socklen_t length = 1;
487         char *result;
488
489         ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, &dummy, &length);
490         if (ret < 0 && errno != ERANGE)
491                 return -1;
492
493         result = calloc(length + 1, 1);
494         if (result == NULL)
495                 return -1;
496
497         ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, result, &length);
498         if (ret < 0) {
499                 free(result);
500                 return -1;
501         }
502
503         *label = result;
504         return 0;
505 }
506
507 int smack_set_label_for_self(const char *label)
508 {
509         int len;
510         int fd;
511         int ret;
512
513         len = strnlen(label, SMACK_LABEL_LEN + 1);
514         if (len > SMACK_LABEL_LEN)
515                 return -1;
516
517         fd = open(SELF_LABEL_FILE, O_WRONLY);
518         if (fd < 0)
519                 return -1;
520
521         ret = write(fd, label, len);
522         close(fd);
523
524         return (ret < 0) ? -1 : 0;
525 }
526
527 int smack_revoke_subject(const char *subject)
528 {
529         int ret;
530         int fd;
531         int len;
532         char path[PATH_MAX];
533
534         len = strnlen(subject, SMACK_LABEL_LEN + 1);
535         if (len > SMACK_LABEL_LEN)
536                 return -1;
537
538         snprintf(path, sizeof path, "%s/revoke-subject", smack_mnt);
539         fd = open(path, O_WRONLY);
540         if (fd < 0)
541                 return -1;
542
543         ret = write(fd, subject, len);
544         close(fd);
545
546         return (ret < 0) ? -1 : 0;
547 }
548
549 static int accesses_apply(struct smack_accesses *handle, int clear)
550 {
551         char buf[LOAD_LEN + 1];
552         char access_type[ACC_LEN + 1];
553         struct smack_rule *rule;
554         int ret;
555         int fd;
556         int load2 = 1;
557         char path[PATH_MAX];
558
559         if (!smack_mnt) {
560                 errno = EFAULT;
561                 return -1; 
562         }
563         
564         snprintf(path, sizeof path, "%s/load2", smack_mnt);
565         fd = open(path, O_WRONLY);
566         if (fd < 0) {
567                 if (errno != ENOENT)
568                         return -1;
569                 /* fallback */
570                 snprintf(path, sizeof path, "%s/load", smack_mnt);
571                 fd = open(path, O_WRONLY);
572                 if (fd < 0)
573                         return -1;
574                 load2 = 0;
575         }
576
577         if (clear)
578                 strcpy(access_type, "-----");
579
580         for (rule = handle->first; rule != NULL; rule = rule->next) {
581                 if (!clear)
582                         int_to_access_type_k(rule->access_code, access_type);
583
584                 if (load2)
585                         ret = snprintf(buf, LOAD_LEN + 1, KERNEL_LONG_FORMAT,
586                                        rule->subject, rule->object,
587                                        access_type);
588                 else
589                         ret = snprintf(buf, LOAD_LEN + 1, KERNEL_SHORT_FORMAT,
590                                        rule->subject, rule->object,
591                                        access_type);
592                 if (ret < 0) {
593                         close(fd);
594                         return -1;
595                 }
596
597                 ret = write(fd, buf, strlen(buf));
598                 if (ret < 0) {
599                         close(fd);
600                         return -1;
601                 }
602         }
603
604         close(fd);
605         return 0;
606 }
607
608 static inline int access_type_to_int(const char *access_type)
609 {
610         int i;
611         unsigned access;
612
613         access = 0;
614         for (i = 0; access_type[i] != '\0'; i++)
615                 switch (access_type[i]) {
616                 case 'r':
617                 case 'R':
618                         access |= ACC_R;
619                         break;
620                 case 'w':
621                 case 'W':
622                         access |= ACC_W;
623                         break;
624                 case 'x':
625                 case 'X':
626                         access |= ACC_X;
627                         break;
628                 case 'a':
629                 case 'A':
630                         access |= ACC_A;
631                         break;
632                 case 't':
633                 case 'T':
634                         access |= ACC_T;
635                         break;
636                 default:
637                         break;
638                 }
639
640         return access;
641 }
642
643 static inline void int_to_access_type_c(unsigned access, char *str)
644 {
645         int i;
646         i = 0;
647         if ((access & ACC_R) != 0)
648                 str[i++] = 'r';
649         if ((access & ACC_W) != 0)
650                 str[i++] = 'w';
651         if ((access & ACC_X) != 0)
652                 str[i++] = 'x';
653         if ((access & ACC_A) != 0)
654                 str[i++] = 'a';
655         if ((access & ACC_T) != 0)
656                 str[i++] = 't';
657         str[i] = '\0';
658 }
659
660 static inline void int_to_access_type_k(unsigned access, char *str)
661 {
662         str[0] = ((access & ACC_R) != 0) ? 'r' : '-';
663         str[1] = ((access & ACC_W) != 0) ? 'w' : '-';
664         str[2] = ((access & ACC_X) != 0) ? 'x' : '-';
665         str[3] = ((access & ACC_A) != 0) ? 'a' : '-';
666         str[4] = ((access & ACC_T) != 0) ? 't' : '-';
667         str[5] = '\0';
668 }
669
670