Merge remote-tracking branch 'rafal-krypa/issue84' into v1.0.x
[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 <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 240
50 #define CAT_MAX_VALUE 63
51 #define CIPSO_POS(i)   (SMACK_LABEL_LEN + 1 + NUM_LEN + NUM_LEN + i * NUM_LEN)
52 #define CIPSO_MAX_SIZE CIPSO_POS(CAT_MAX_COUNT)
53 #define CIPSO_NUM_LEN_STR "%-4d"
54
55 #define ACCESS_TYPE_R 0x01
56 #define ACCESS_TYPE_W 0x02
57 #define ACCESS_TYPE_X 0x04
58 #define ACCESS_TYPE_A 0x08
59 #define ACCESS_TYPE_T 0x10
60 #define ACCESS_TYPE_L 0x20
61
62 #define DICT_HASH_SIZE 4096
63
64 extern char *smackfs_mnt;
65 extern int smackfs_mnt_dirfd;
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
333         if (smackfs_mnt_dirfd < 0)
334                 return -1;
335
336         fd = openat(smackfs_mnt_dirfd, "access2", O_RDWR);
337         if (fd < 0) {
338                 if (errno != ENOENT)
339                         return -1;
340                 
341                 fd = openat(smackfs_mnt_dirfd, "access", O_RDWR);
342                 if (fd < 0)
343                         return -1;
344                 access2 = 0;
345         }
346
347         if ((code = str_to_access_code(access_type)) < 0)
348                 return -1;
349         access_code_to_str(code, str);
350
351         if (access2)
352                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_LONG_FORMAT,
353                                subject, object, str);
354         else
355                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_SHORT_FORMAT,
356                                subject, object, str);
357
358         if (ret < 0) {
359                 close(fd);
360                 return -1;
361         }
362
363         ret = write(fd, buf, strlen(buf));
364         if (ret < 0) {
365                 close(fd);
366                 return -1;
367         }
368
369         ret = read(fd, buf, 1);
370         close(fd);
371         if (ret < 0)
372                 return -1;
373
374         return buf[0] == '1';
375 }
376
377 int smack_cipso_new(struct smack_cipso **cipso)
378 {
379         struct smack_cipso *result;
380
381         result = calloc(sizeof(struct smack_cipso), 1);
382         if (result == NULL)
383                 return -1;
384
385         *cipso = result;
386         return 0;
387 }
388
389 void smack_cipso_free(struct smack_cipso *cipso)
390 {
391         if (cipso == NULL)
392                 return;
393
394         struct cipso_mapping *mapping = cipso->first;
395         struct cipso_mapping *next_mapping = NULL;
396
397         while (mapping != NULL) {
398                 next_mapping = mapping->next;
399                 free(mapping);
400                 mapping = next_mapping;
401         }
402
403         free(cipso);
404 }
405
406 int smack_cipso_apply(struct smack_cipso *cipso)
407 {
408         struct cipso_mapping *m = NULL;
409         char buf[CIPSO_MAX_SIZE];
410         int fd;
411         int i;
412         int offset;
413
414         if (smackfs_mnt_dirfd < 0)
415                 return -1;
416
417         fd = openat(smackfs_mnt_dirfd, "cipso2", O_WRONLY);
418         if (fd < 0)
419                 return -1;
420
421         memset(buf,0,CIPSO_MAX_SIZE);
422         for (m = cipso->first; m != NULL; m = m->next) {
423                 snprintf(buf, SMACK_LABEL_LEN + 1, "%s", m->label);
424                 offset = strlen(buf) + 1;
425
426                 sprintf(&buf[offset], CIPSO_NUM_LEN_STR, m->level);
427                 offset += NUM_LEN;
428
429                 sprintf(&buf[offset], CIPSO_NUM_LEN_STR, m->ncats);
430                 offset += NUM_LEN;
431
432                 for (i = 0; i < m->ncats; i++){
433                         sprintf(&buf[offset], CIPSO_NUM_LEN_STR, m->cats[i]);
434                         offset += NUM_LEN;
435                 }
436
437                 if (write(fd, buf, offset) < 0) {
438                         close(fd);
439                         return -1;
440                 }
441         }
442
443         close(fd);
444         return 0;
445 }
446
447 int smack_cipso_add_from_file(struct smack_cipso *cipso, int fd)
448 {
449         struct cipso_mapping *mapping = NULL;
450         FILE *file = NULL;
451         char buf[BUF_SIZE];
452         char *label, *level, *cat, *ptr;
453         long int val;
454         int i;
455         int newfd;
456
457         newfd = dup(fd);
458         if (newfd == -1)
459                 return -1;
460
461         file = fdopen(newfd, "r");
462         if (file == NULL) {
463                 close(newfd);
464                 return -1;
465         }
466
467         while (fgets(buf, BUF_SIZE, file) != NULL) {
468                 mapping = calloc(sizeof(struct cipso_mapping), 1);
469                 if (mapping == NULL)
470                         goto err_out;
471
472                 label = strtok_r(buf, " \t\n", &ptr);
473                 level = strtok_r(NULL, " \t\n", &ptr);
474                 cat = strtok_r(NULL, " \t\n", &ptr);
475
476                 if (level == NULL || get_label(mapping->label, label) < 0)
477                         goto err_out;
478
479                 errno = 0;
480                 val = strtol(level, NULL, 10);
481                 if (errno)
482                         goto err_out;
483
484                 if (val < 0 || val > LEVEL_MAX)
485                         goto err_out;
486
487                 mapping->level = val;
488
489                 for (i = 0; i < CAT_MAX_COUNT && cat != NULL; i++) {
490                         errno = 0;
491                         val = strtol(cat, NULL, 10);
492                         if (errno)
493                                 goto err_out;
494
495                         if (val < 0 || val > CAT_MAX_VALUE)
496                                 goto err_out;
497
498                         mapping->cats[i] = val;
499
500                         cat = strtok_r(NULL, " \t\n", &ptr);
501                 }
502
503                 mapping->ncats = i;
504
505                 if (cipso->first == NULL) {
506                         cipso->first = cipso->last = mapping;
507                 } else {
508                         cipso->last->next = mapping;
509                         cipso->last = mapping;
510                 }
511         }
512
513         if (ferror(file))
514                 goto err_out;
515
516         fclose(file);
517         return 0;
518 err_out:
519         fclose(file);
520         free(mapping);
521         return -1;
522 }
523
524 const char *smack_smackfs_path(void)
525 {
526         return smackfs_mnt;
527 }
528
529 ssize_t smack_new_label_from_self(char **label)
530 {
531         char *result;
532         int fd;
533         int ret;
534
535         result = calloc(SMACK_LABEL_LEN + 1, 1);
536         if (result == NULL)
537                 return -1;
538
539         fd = open(SELF_LABEL_FILE, O_RDONLY);
540         if (fd < 0) {
541                 free(result);
542                 return -1;
543         }
544
545         ret = read(fd, result, SMACK_LABEL_LEN);
546         close(fd);
547         if (ret < 0) {
548                 free(result);
549                 return -1;
550         }
551
552         *label = result;
553         return ret;
554 }
555
556 ssize_t smack_new_label_from_socket(int fd, char **label)
557 {
558         char dummy;
559         int ret;
560         socklen_t length = 1;
561         char *result;
562
563         ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, &dummy, &length);
564         if (ret < 0 && errno != ERANGE)
565                 return -1;
566
567         result = calloc(length + 1, 1);
568         if (result == NULL)
569                 return -1;
570
571         ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, result, &length);
572         if (ret < 0) {
573                 free(result);
574                 return -1;
575         }
576
577         *label = result;
578         return length;
579 }
580
581 ssize_t smack_new_label_from_path(const char *path, const char *xattr, 
582                                   int follow, char **label)
583 {
584         char buf[SMACK_LABEL_LEN + 1];
585         char *result;
586         ssize_t ret = 0;
587
588         ret = follow ?
589                 getxattr(path, xattr, buf, SMACK_LABEL_LEN + 1) :
590                 lgetxattr(path, xattr, buf, SMACK_LABEL_LEN + 1);
591         if (ret < 0)
592                 return -1;
593
594         result = calloc(ret + 1, 1);
595         if (result == NULL)
596                 return -1;
597
598         ret = get_label(result, buf);
599         if (ret < 0) {
600                 free(result);
601                 return -1;
602         }
603
604         *label = result;
605         return ret;
606 }
607
608 int smack_set_label_for_self(const char *label)
609 {
610         int len;
611         int fd;
612         int ret;
613
614         len = get_label(NULL, label);
615         if (len < 0)
616                 return -1;
617
618         fd = open(SELF_LABEL_FILE, O_WRONLY);
619         if (fd < 0)
620                 return -1;
621
622         ret = write(fd, label, len);
623         close(fd);
624
625         return (ret < 0) ? -1 : 0;
626 }
627
628 int smack_revoke_subject(const char *subject)
629 {
630         int ret;
631         int fd;
632         int len;
633
634         if (smackfs_mnt_dirfd < 0)
635                 return -1;
636
637         len = get_label(NULL, subject);
638         if (len < 0)
639                 return -1;
640
641         fd = openat(smackfs_mnt_dirfd, "revoke-subject", O_WRONLY);
642         if (fd < 0)
643                 return -1;
644
645         ret = write(fd, subject, len);
646         close(fd);
647
648         return (ret < 0) ? -1 : 0;
649 }
650
651 static int accesses_apply(struct smack_accesses *handle, int clear)
652 {
653         char buf[LOAD_LEN + 1];
654         char allow_str[ACC_LEN + 1];
655         char deny_str[ACC_LEN + 1];
656         struct smack_rule *rule;
657         int ret;
658         int fd;
659         int load_fd;
660         int change_fd;
661         int load2 = 1;
662
663         if (smackfs_mnt_dirfd < 0)
664                 return -1;
665
666         load_fd = openat(smackfs_mnt_dirfd, "load2", O_WRONLY);
667         if (load_fd < 0) {
668                 if (errno != ENOENT)
669                         return -1;
670                 /* fallback */
671                 load_fd = openat(smackfs_mnt_dirfd, "load", O_WRONLY);
672                 /* Try to continue if the file doesn't exist, we might not need it. */
673                 if (load_fd < 0 && errno != ENOENT)
674                         return -1;
675                 load2 = 0;
676         }
677
678         change_fd = openat(smackfs_mnt_dirfd, "change-rule", O_WRONLY);
679         /* Try to continue if the file doesn't exist, we might not need it. */
680         if (change_fd < 0 && errno != ENOENT) {
681                 ret = -1;
682                 goto err_out;
683         }
684
685         for (rule = handle->first; rule != NULL; rule = rule->next) {
686                 /* Fail immediately without doing any further processing
687                    if modify rules are not supported. */
688                 if (rule->deny_code >= 0 && change_fd < 0) {
689                         ret = -1;
690                         goto err_out;
691                 }
692
693                 access_code_to_str(clear ? 0 : rule->allow_code, allow_str);
694
695                 if (rule->deny_code != -1 && !clear) {
696                         access_code_to_str(rule->deny_code, deny_str);
697
698                         fd = change_fd;
699                         ret = snprintf(buf, LOAD_LEN + 1, KERNEL_MODIFY_FORMAT,
700                                        dict_get_label(handle->dict, rule->subject_id),
701                                        dict_get_label(handle->dict, rule->object_id),
702                                        allow_str,
703                                        deny_str);
704                 } else {
705                         fd = load_fd;
706                         if (load2)
707                                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_LONG_FORMAT,
708                                                dict_get_label(handle->dict, rule->subject_id),
709                                                dict_get_label(handle->dict, rule->object_id),
710                                                allow_str);
711                         else {
712                                 if (rule->subject_len > SHORT_LABEL_LEN ||
713                                     rule->object_len > SHORT_LABEL_LEN) {
714                                         ret = -1;
715                                         goto err_out;
716                                 }
717
718                                 ret = snprintf(buf, LOAD_LEN + 1, KERNEL_SHORT_FORMAT,
719                                                dict_get_label(handle->dict, rule->subject_id),
720                                                dict_get_label(handle->dict, rule->object_id),
721                                                allow_str);
722                         }
723                 }
724
725                 if (ret < 0) {
726                         ret = -1;
727                         goto err_out;
728                 }
729
730                 ret = write(fd, buf, ret);
731                 if (ret < 0) {
732                         ret = -1;
733                         goto err_out;
734                 }
735         }
736         ret = 0;
737
738 err_out:
739         if (load_fd >= 0)
740                 close(load_fd);
741         if (change_fd >= 0)
742                 close(change_fd);
743         return ret;
744 }
745
746 static inline ssize_t get_label(char *dest, const char *src)
747 {
748         int i;
749
750         if (!src || src[0] == '\0' || src[0] == '-')
751                 return -1;
752
753         for (i = 0; i < (SMACK_LABEL_LEN + 1) && src[i]; i++) {
754                 if (src[i] <= ' ' || src[i] > '~')
755                         return -1;
756                 switch (src[i]) {
757                 case '/':
758                 case '"':
759                 case '\\':
760                 case '\'':
761                         return -1;
762                 default:
763                         break;
764                 }
765
766                 if (dest)
767                         dest[i] = src[i];
768         }
769
770         if (dest && i < (SMACK_LABEL_LEN + 1))
771                 dest[i] = '\0';
772
773         return i < (SMACK_LABEL_LEN + 1) ? i : -1;
774 }
775
776
777 static inline int str_to_access_code(const char *str)
778 {
779         int i;
780         unsigned int code = 0;
781
782         for (i = 0; str[i] != '\0'; i++) {
783                 switch (str[i]) {
784                 case 'r':
785                 case 'R':
786                         code |= ACCESS_TYPE_R;
787                         break;
788                 case 'w':
789                 case 'W':
790                         code |= ACCESS_TYPE_W;
791                         break;
792                 case 'x':
793                 case 'X':
794                         code |= ACCESS_TYPE_X;
795                         break;
796                 case 'a':
797                 case 'A':
798                         code |= ACCESS_TYPE_A;
799                         break;
800                 case 't':
801                 case 'T':
802                         code |= ACCESS_TYPE_T;
803                         break;
804                 case 'l':
805                 case 'L':
806                         code |= ACCESS_TYPE_L;
807                         break;
808                 case '-':
809                         break;
810                 default:
811                         return -1;
812                 }
813         }
814
815         return code;
816 }
817
818 static inline void access_code_to_str(unsigned int code, char *str)
819 {
820         str[0] = ((code & ACCESS_TYPE_R) != 0) ? 'r' : '-';
821         str[1] = ((code & ACCESS_TYPE_W) != 0) ? 'w' : '-';
822         str[2] = ((code & ACCESS_TYPE_X) != 0) ? 'x' : '-';
823         str[3] = ((code & ACCESS_TYPE_A) != 0) ? 'a' : '-';
824         str[4] = ((code & ACCESS_TYPE_T) != 0) ? 't' : '-';
825         str[5] = ((code & ACCESS_TYPE_L) != 0) ? 'l' : '-';
826         str[6] = '\0';
827 }
828
829 static int dict_create(struct label_dict **dict)
830 {
831         *dict = calloc(1, sizeof(struct label_dict));
832         if (!*dict)
833                 goto err;
834         (*dict)->htab = calloc(1, sizeof(struct hsearch_data));
835         if (!(*dict)->htab)
836                 goto free_dict;
837         (*dict)->labels = calloc(DICT_HASH_SIZE, sizeof(char *));
838         if (!(*dict)->labels)
839                 goto free_htab;
840         if (hcreate_r(DICT_HASH_SIZE, (*dict)->htab) == 0)
841                 goto free_labels;
842         return 0;
843
844 free_labels:
845         free((*dict)->labels);
846 free_htab:
847         free((*dict)->htab);
848 free_dict:
849         free(*dict);
850 err:
851         return -1;
852 }
853
854 static int dict_free(struct label_dict *dict)
855 {
856         int i;
857         for (i = 0; i < (dict->nof_labels); i++)
858                 free((dict->labels)[i]);
859         free(dict->labels);
860         hdestroy_r(dict->htab);
861         free(dict->htab);
862         free(dict);
863         return 0;
864 }
865
866 static ssize_t dict_add_label(struct label_dict *dict, int *id, const char *label)
867 {
868         ENTRY e, *ep;
869         int ret, search;
870
871         ret = get_label(NULL, label);
872
873         if (dict->nof_labels == DICT_HASH_SIZE)
874                 return -2;
875         if (ret == -1)
876                 return -1;
877
878         e.key =  (char *)label;
879         e.data = (void *)(&(dict->labels[dict->nof_labels]));
880
881         search = hsearch_r(e, ENTER, &ep, dict->htab);
882         if (search == 0)
883                 return -2;
884         if (e.data != ep->data) {/*found an existing entry*/
885                 *id = (int)((char **)(ep->data) - dict->labels);
886         } else {/*new entry added*/
887                 ep->key = malloc(ret + 1);
888                 if (!ep->key)
889                         return -3;
890                 ep->key[ret] = '\0';
891                 memcpy(ep->key, label, ret);
892                 dict->labels[dict->nof_labels] = ep->key;
893                 *id = dict->nof_labels++;
894         }
895         return ret;
896 }
897
898 static const char *dict_get_label(const struct label_dict *dict, int id)
899 {
900         if (id < dict->nof_labels)
901                 return dict->labels[id];
902         else
903                 return NULL;
904 }