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