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