1 /* This file is part of GNU Pies
2 Copyright (C) 2009-2020 Sergey Poznyakoff
3
4 GNU Pies is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Pies is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include "pies.h"
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/time.h>
24 #include <sys/un.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <netdb.h>
28
29 struct pies_sockaddr
30 {
31 unsigned netmask;
32 int salen;
33 struct sockaddr sa;
34 };
35
36 enum name_match
37 {
38 match_none,
39 match_user_name,
40 match_group_name
41 };
42
43 struct acl_entry
44 {
45 grecs_locus_t locus;
46 int allow;
47 int authenticated;
48 pies_acl_t acl;
49 enum name_match name_match;
50 char **names;
51 struct grecs_list *sockaddrs;
52 };
53
54 struct pies_acl
55 {
56 char *name;
57 size_t refcnt;
58 grecs_locus_t locus;
59 struct grecs_list *list;
60 };
61
62
63
64 /* ACL creation */
65 void
grecs_locus_point_copy(struct grecs_locus_point * dst,struct grecs_locus_point * src)66 grecs_locus_point_copy (struct grecs_locus_point *dst,
67 struct grecs_locus_point *src)
68 {
69 dst->file = grecs_strdup (src->file);
70 dst->line = src->line;
71 dst->col = src->col;
72 }
73
74 void
grecs_locus_copy(struct grecs_locus * dst,struct grecs_locus * src)75 grecs_locus_copy (struct grecs_locus *dst, struct grecs_locus *src)
76 {
77 grecs_locus_point_copy (&dst->beg, &src->beg);
78 grecs_locus_point_copy (&dst->end, &src->end);
79 }
80
81 void
grecs_locus_point_free(struct grecs_locus_point * p)82 grecs_locus_point_free (struct grecs_locus_point *p)
83 {
84 grecs_free (p->file);
85 }
86
87 void
grecs_locus_free(struct grecs_locus * loc)88 grecs_locus_free (struct grecs_locus *loc)
89 {
90 grecs_locus_point_free (&loc->beg);
91 grecs_locus_point_free (&loc->end);
92 }
93
94 static void
acl_free_entry(void * p)95 acl_free_entry (void *p)
96 {
97 struct acl_entry *ent = p;
98 pies_acl_free (ent->acl);
99 grecs_locus_free (&ent->locus);
100 grecs_list_free (ent->sockaddrs);
101 if (ent->names)
102 {
103 size_t i;
104
105 for (i = 0; ent->names[i]; i++)
106 free (ent->names[i]);
107 free (ent->names);
108 }
109 free (ent);
110 }
111
112 void
pies_acl_use(pies_acl_t acl)113 pies_acl_use (pies_acl_t acl)
114 {
115 ++acl->refcnt;
116 }
117
118 pies_acl_t
pies_acl_create(const char * name,grecs_locus_t * locus)119 pies_acl_create (const char *name, grecs_locus_t *locus)
120 {
121 pies_acl_t acl = grecs_malloc (sizeof (acl[0]));
122 acl->name = name ? grecs_strdup (name) : NULL;
123 acl->refcnt = 0;
124 grecs_locus_copy (&acl->locus, locus);
125 acl->list = grecs_list_create ();
126 acl->list->free_entry = acl_free_entry;
127 pies_acl_use (acl);
128 return acl;
129 }
130
131 void
pies_acl_destroy(pies_acl_t * pacl)132 pies_acl_destroy (pies_acl_t *pacl)
133 {
134 if (pacl && *pacl && (*pacl)->refcnt)
135 {
136 pies_acl_t acl = *pacl;
137 if (--acl->refcnt == 0)
138 {
139 free (acl->name);
140 grecs_locus_free (&acl->locus);
141 grecs_list_free (acl->list);
142 free (acl);
143 *pacl = NULL;
144 }
145 }
146 }
147
148 void
pies_acl_free(pies_acl_t acl)149 pies_acl_free (pies_acl_t acl)
150 {
151 pies_acl_destroy (&acl);
152 }
153
154 static struct pies_sockaddr *
create_acl_sockaddr(int family,int len)155 create_acl_sockaddr (int family, int len)
156 {
157 struct pies_sockaddr *p = grecs_zalloc (sizeof (*p));
158 p->salen = len;
159 p->sa.sa_family = family;
160 return p;
161 }
162
163 /* allow|deny [all|authenticated|group <grp: list>|user <usr: list>]
164 [acl <name: string>] [from <addr: list>] */
165
166 static int
_parse_token(struct acl_entry * entry,const grecs_value_t * value)167 _parse_token (struct acl_entry *entry, const grecs_value_t *value)
168 {
169 if (strcmp (value->v.string, "all") == 0
170 || strcmp (value->v.string, "any") == 0)
171 /* nothing */ ;
172 else if (strcmp (value->v.string, "auth") == 0
173 || strcmp (value->v.string, "authenticated") == 0)
174 entry->authenticated = 1;
175 else
176 return 1;
177 return 0;
178 }
179
180 static int
_parse_sockaddr(struct acl_entry * entry,const grecs_value_t * value)181 _parse_sockaddr (struct acl_entry *entry, const grecs_value_t *value)
182 {
183 struct pies_sockaddr *sptr;
184 const char *string;
185
186 if (grecs_assert_value_type (value, GRECS_TYPE_STRING, &entry->locus))
187 return 1;
188
189 string = value->v.string;
190
191 if (string[0] == '/')
192 {
193 size_t len;
194 struct sockaddr_un *s_un;
195
196 len = strlen (string);
197 if (len >= sizeof (s_un->sun_path))
198 {
199 grecs_error (&entry->locus, 0,
200 _("socket name too long: %s"), string);
201 return 1;
202 }
203 sptr = create_acl_sockaddr (AF_UNIX, sizeof (s_un));
204 s_un = (struct sockaddr_un *) &sptr->sa;
205 memcpy (s_un->sun_path, string, len);
206 s_un->sun_path[len] = 0;
207 }
208 else
209 {
210 struct in_addr addr;
211 struct sockaddr_in *s_in;
212 char *p = strchr (string, '/');
213
214 if (p)
215 *p = 0;
216
217 if (inet_aton (string, &addr) == 0)
218 {
219 struct hostent *hp = gethostbyname (string);
220 if (!hp)
221 {
222 grecs_error (&entry->locus, 0,
223 _("cannot resolve host name: %s"), string);
224 if (p)
225 *p = '/';
226 return 1;
227 }
228 memcpy (&addr.s_addr, hp->h_addr, sizeof (addr.s_addr));
229 }
230 addr.s_addr = ntohl (addr.s_addr);
231
232 sptr = create_acl_sockaddr (AF_INET, sizeof (s_in));
233 s_in = (struct sockaddr_in *) &sptr->sa;
234 s_in->sin_addr = addr;
235
236 if (p)
237 {
238 *p++ = '/';
239 char *q;
240 unsigned netlen;
241
242 netlen = strtoul (p, &q, 10);
243 if (*q == 0)
244 {
245 if (netlen == 0)
246 sptr->netmask = 0;
247 else
248 {
249 sptr->netmask = 0xfffffffful >> (32 - netlen);
250 sptr->netmask <<= (32 - netlen);
251 }
252 }
253 else if (*q == '.')
254 {
255 struct in_addr addr;
256
257 if (inet_aton (p, &addr) == 0)
258 {
259 grecs_error (&entry->locus, 0,
260 _("invalid netmask: %s"), p);
261 return 1;
262 }
263 sptr->netmask = addr.s_addr;
264 }
265 else
266 {
267 grecs_error (&entry->locus, 0, _("invalid netmask: %s"), p);
268 return 1;
269 }
270 }
271 else
272 sptr->netmask = 0xfffffffful;
273 }
274 grecs_list_append (entry->sockaddrs, sptr);
275 return 0;
276 }
277
278 static int
sockaddr_cmp(void const * a,void const * b)279 sockaddr_cmp (void const *a, void const *b)
280 {
281 struct pies_sockaddr const *ap = a;
282 struct pies_sockaddr const *bp = b;
283
284 if (ap->netmask != bp->netmask)
285 return 1;
286 if (ap->salen != bp->salen)
287 return 1;
288 return memcmp (&ap->sa, &bp->sa, ap->salen);
289 }
290
291 static void
sockaddr_free(void * p)292 sockaddr_free (void *p)
293 {
294 free (p);
295 }
296
297 static int
_parse_from(struct acl_entry * entry,size_t argc,grecs_value_t ** argv)298 _parse_from (struct acl_entry *entry, size_t argc, grecs_value_t **argv)
299 {
300 if (argc == 0)
301 return 0;
302 else if (argv[0]->type == GRECS_TYPE_LIST)
303 {
304 grecs_error (&entry->locus, 0, _("expected \"from\", but found list"));
305 return 1;
306 }
307 else if (strcmp (argv[0]->v.string, "from"))
308 {
309 grecs_error (&entry->locus, 0, _("expected \"from\", but found \"%s\""),
310 argv[0]->v.string);
311 return 1;
312 }
313 argc--;
314 argv++;
315
316 if (argc == 0)
317 {
318 grecs_error (&entry->locus, 0,
319 _("unexpected end of statement after \"from\""));
320 return 1;
321 }
322
323 entry->sockaddrs = grecs_list_create ();
324 entry->sockaddrs->cmp = sockaddr_cmp;
325 entry->sockaddrs->free_entry = sockaddr_free;
326 if (argv[0]->type == GRECS_TYPE_STRING)
327 {
328 if (_parse_sockaddr (entry, argv[0]))
329 return 1;
330 }
331 else
332 {
333 int rc = 0;
334 struct grecs_list_entry *ep;
335
336 for (ep = argv[0]->v.list->head; ep; ep = ep->next)
337 rc += _parse_sockaddr (entry, (const grecs_value_t*) ep->data);
338 if (rc)
339 return rc;
340 }
341
342 if (argc - 1)
343 {
344 grecs_warning (&entry->locus, 0, _("junk after from-list"));
345 return 1;
346 }
347 return 0;
348 }
349
350 static int
_parse_sub_acl(struct acl_entry * entry,size_t argc,grecs_value_t ** argv)351 _parse_sub_acl (struct acl_entry *entry, size_t argc, grecs_value_t **argv)
352 {
353 if (argc == 0)
354 return 0;
355 if (strcmp (argv[0]->v.string, "acl") == 0)
356 {
357 argc--;
358 argv++;
359 if (argc == 0)
360 {
361 grecs_error (&entry->locus, 0,
362 _("expected ACL name, but found end of statement"));
363 return 1;
364 }
365
366 if (argv[0]->type != GRECS_TYPE_STRING)
367 {
368 grecs_error (&entry->locus, 0,
369 _("expected string, but found list"));
370 return 1;
371 }
372
373 entry->acl = pies_acl_lookup (argv[0]->v.string);
374
375 if (!entry->acl)
376 {
377 grecs_error (&entry->locus, 0, _("ACL not defined: %s"),
378 argv[0]->v.string);
379 return 1;
380 }
381 pies_acl_use (entry->acl);
382
383 argc--;
384 argv++;
385 }
386 return _parse_from (entry, argc, argv);
387 }
388
389 static int
_parse_group(struct acl_entry * entry,size_t argc,grecs_value_t ** argv)390 _parse_group (struct acl_entry *entry, size_t argc, grecs_value_t **argv)
391 {
392 if (strcmp (argv[0]->v.string, "group") == 0)
393 entry->name_match = match_group_name;
394 else if (strcmp (argv[0]->v.string, "user") == 0)
395 entry->name_match = match_user_name;
396 else
397 entry->name_match = match_none;
398
399 if (entry->name_match != match_none)
400 {
401 argc--;
402 argv++;
403 if (argc == 0)
404 {
405 grecs_error (&entry->locus, 0,
406 _("expected identity list, but found end of statement"));
407 return 1;
408 }
409 if (argv[0]->type == GRECS_TYPE_STRING)
410 {
411 entry->names = grecs_calloc (2, sizeof (entry->names[0]));
412 entry->names[0] = grecs_strdup (argv[0]->v.string);
413 entry->names[1] = NULL;
414 }
415 else
416 {
417 size_t i;
418 struct grecs_list_entry *ep;
419 entry->names = grecs_calloc (argv[0]->v.list->count + 1,
420 sizeof (entry->names[0]));
421 for (i = 0, ep = argv[0]->v.list->head; ep; ep = ep->next, ++i)
422 entry->names[i] = grecs_strdup (ep->data);
423 entry->names[i] = NULL;
424 }
425 argc--;
426 argv++;
427 }
428 return _parse_sub_acl (entry, argc, argv);
429 }
430
431 static int
_parse_acl(struct acl_entry * entry,size_t argc,grecs_value_t ** argv)432 _parse_acl (struct acl_entry *entry, size_t argc, grecs_value_t **argv)
433 {
434 if (grecs_assert_value_type (argv[0], GRECS_TYPE_STRING, &entry->locus))
435 return 1;
436 else if (_parse_token (entry, argv[0]) == 0)
437 return _parse_sub_acl (entry, argc - 1, argv + 1);
438 else
439 return _parse_group (entry, argc, argv);
440 }
441
442 int
parse_acl_line(grecs_locus_t * locus,int allow,pies_acl_t acl,grecs_value_t * value)443 parse_acl_line (grecs_locus_t *locus, int allow, pies_acl_t acl,
444 grecs_value_t *value)
445 {
446 struct acl_entry *entry = grecs_zalloc (sizeof (*entry));
447
448 grecs_locus_copy (&entry->locus, locus);
449 entry->allow = allow;
450
451 if (value)
452 switch (value->type)
453 {
454 case GRECS_TYPE_STRING:
455 if (_parse_token (entry, value))
456 {
457 grecs_error (&entry->locus, 0, _("unknown word: %s"),
458 value->v.string);
459 return 1;
460 }
461 break;
462
463 case GRECS_TYPE_ARRAY:
464 if (_parse_acl (entry, value->v.arg.c, value->v.arg.v))
465 return 1;
466 break;
467
468 case GRECS_TYPE_LIST:
469 grecs_error (locus, 0, _("unexpected list"));
470 return 1;
471 }
472 grecs_list_append (acl->list, entry);
473 return 0;
474 }
475
476 static int
acl_entry_cmp(void const * a,void const * b)477 acl_entry_cmp (void const *a, void const *b)
478 {
479 struct acl_entry const *ap = a;
480 struct acl_entry const *bp = b;
481 size_t i;
482
483 if (ap->allow != bp->allow)
484 return 1;
485 if (ap->authenticated != bp->authenticated)
486 return 1;
487 if (pies_acl_cmp (ap->acl, bp->acl))
488 return 1;
489 if (ap->name_match != bp->name_match)
490 return 1;
491
492 if (ap->names && bp->names)
493 {
494 for (i = 0; ap->names[i]; i++)
495 if (!bp->names[i] || strcmp (ap->names[i], bp->names[i]))
496 return 1;
497 if (bp->names[i])
498 return 1;
499 }
500 else if (ap->names || bp->names)
501 return 1;
502
503 return grecs_list_compare (ap->sockaddrs, bp->sockaddrs);
504 }
505
506 #define ACL_TAG_NONE 0
507 #define ACL_TAG_IGNORE 1
508 #define ACL_TAG_OPTIONAL 2
509 #define ACL_TAG_REQUIRED 3
510
511 int
_acl_common_section_parser(enum grecs_callback_command cmd,grecs_locus_t * locus,grecs_value_t * value,pies_acl_t * pacl,int flag)512 _acl_common_section_parser (enum grecs_callback_command cmd,
513 grecs_locus_t *locus,
514 grecs_value_t *value,
515 pies_acl_t *pacl,
516 int flag)
517 {
518 pies_acl_t acl;
519 const char *tag = NULL;
520 int has_value = 0;
521
522 switch (cmd)
523 {
524 case grecs_callback_section_begin:
525 if (value)
526 {
527 if (value->type != GRECS_TYPE_STRING)
528 {
529 grecs_error (locus, 0, _("ACL name must be a string"));
530 return 1;
531 }
532 has_value = value->v.string != NULL;
533 }
534 if (has_value)
535 {
536 switch (flag)
537 {
538 case ACL_TAG_NONE:
539 grecs_error (locus, 0, _("ACL name is not expected"));
540 return 1;
541
542 case ACL_TAG_IGNORE:
543 grecs_warning (locus, 0, _("ACL name is ignored"));
544 break;
545
546 case ACL_TAG_OPTIONAL:
547 case ACL_TAG_REQUIRED:
548 tag = value->v.string;
549 }
550 }
551 else if (flag == ACL_TAG_REQUIRED)
552 {
553 grecs_error (locus, 0, _("missing ACL name"));
554 return 1;
555 }
556 acl = pies_acl_create (tag, locus);
557 if (tag && (acl = pies_acl_install (acl)) == NULL)
558 return 1;
559 if (pacl)
560 {
561 pies_acl_free (*pacl);
562 *pacl = acl;
563 }
564 break;
565
566 case grecs_callback_section_end:
567 acl = *pacl;
568 if (acl->list)
569 acl->list->cmp = acl_entry_cmp;
570 break;
571
572 case grecs_callback_set_value:
573 if (grecs_assert_value_type (value, GRECS_TYPE_STRING, &value->locus))
574 return 0;
575 acl = pies_acl_lookup (value->v.string);
576 if (!acl)
577 {
578 grecs_error (&value->locus, 0, _("ACL not defined: %s"),
579 value->v.string);
580 return 0;
581 }
582 pies_acl_use (acl);
583 if (pacl)
584 {
585 pies_acl_free (*pacl);
586 *pacl = acl;
587 }
588 break;
589 }
590 return 0;
591 }
592
593 int
acl_section_parser(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)594 acl_section_parser (enum grecs_callback_command cmd,
595 grecs_node_t *node,
596 void *varptr, void *cb_data)
597 {
598 grecs_locus_t *locus = &node->locus;
599 grecs_value_t *value = node->v.value;
600 int rc = _acl_common_section_parser (cmd, locus, value, varptr,
601 ACL_TAG_NONE);
602 if (rc == 0)
603 *(void**)cb_data = *(pies_acl_t*)varptr;
604 return rc;
605 }
606
607 int
defacl_section_parser(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)608 defacl_section_parser (enum grecs_callback_command cmd,
609 grecs_node_t *node,
610 void *varptr, void *cb_data)
611 {
612 grecs_locus_t *locus = &node->locus;
613 grecs_value_t *value = node->v.value;
614 return _acl_common_section_parser (cmd, locus, value, cb_data,
615 ACL_TAG_REQUIRED);
616 }
617
618 static int
allow_cb(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)619 allow_cb (enum grecs_callback_command cmd,
620 grecs_node_t *node,
621 void *varptr, void *cb_data)
622 {
623 grecs_locus_t *locus = &node->locus;
624 grecs_value_t *value = node->v.value;
625 pies_acl_t acl = varptr;
626
627 if (cmd != grecs_callback_set_value)
628 {
629 grecs_error (locus, 0, _("unexpected block statement"));
630 return 1;
631 }
632 parse_acl_line (locus, 1, acl, value);
633 return 0;
634 }
635
636 static int
deny_cb(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)637 deny_cb (enum grecs_callback_command cmd,
638 grecs_node_t *node,
639 void *varptr, void *cb_data)
640 {
641 grecs_locus_t *locus = &node->locus;
642 grecs_value_t *value = node->v.value;
643 pies_acl_t acl = varptr;
644 if (cmd != grecs_callback_set_value)
645 {
646 grecs_error (locus, 0, _("unexpected block statement"));
647 return 1;
648 }
649 parse_acl_line (locus, 0, acl, value);
650 return 0;
651 }
652
653 struct grecs_keyword acl_keywords[] = {
654 /* TRANSLATORS: only words within angle brackets are translatable */
655 { "allow", N_("[all|authenticated|user <usr: list>|group <grp: list>] [acl <name: string>] [from <addr: list>]"),
656 N_("Allow access"),
657 grecs_type_string, GRECS_MULT, NULL, 0,
658 allow_cb },
659 /* TRANSLATORS: only words within angle brackets are translatable */
660 { "deny", N_("[all|authenticated|user <usr: list>|group <grp: list>] [acl <name: string>] [from <addr: list>]"),
661 N_("Deny access"),
662 grecs_type_string, GRECS_MULT, NULL, 0,
663 deny_cb },
664 { NULL }
665 };
666
667
668 /* ACL verification */
669
670 #define S_UN_NAME(sa, salen) \
671 ((salen < offsetof (struct sockaddr_un,sun_path)) ? "" : (sa)->sun_path)
672
673 static int
_check_sockaddr(struct pies_sockaddr * sptr,struct acl_input * input)674 _check_sockaddr (struct pies_sockaddr *sptr, struct acl_input *input)
675 {
676 if (sptr->sa.sa_family != input->addr->sa_family)
677 return 0;
678
679 switch (sptr->sa.sa_family)
680 {
681 case AF_INET:
682 {
683 struct sockaddr_in *sin_clt = (struct sockaddr_in *) input->addr;
684 struct sockaddr_in *sin_item = (struct sockaddr_in *) &sptr->sa;
685
686 if (sin_item->sin_addr.s_addr ==
687 (ntohl (sin_clt->sin_addr.s_addr) & sptr->netmask))
688 return 1;
689 break;
690 }
691
692 case AF_UNIX:
693 {
694 struct sockaddr_un *sun_clt = (struct sockaddr_un *) input->addr;
695 struct sockaddr_un *sun_item = (struct sockaddr_un *) &sptr->sa;
696
697 if (S_UN_NAME (sun_clt, input->addrlen)[0]
698 && S_UN_NAME (sun_item, sptr->salen)[0]
699 && strcmp (sun_clt->sun_path, sun_item->sun_path) == 0)
700 return 1;
701 }
702 }
703 return 0;
704 }
705
706 static int
_acl_check(struct acl_entry * ent,struct acl_input * input)707 _acl_check (struct acl_entry *ent, struct acl_input *input)
708 {
709 int result = 1;
710
711 if (ent->authenticated)
712 {
713 result = input->identity != NULL;
714 if (!result)
715 return result;
716 }
717
718 switch (ent->name_match)
719 {
720 case match_none:
721 break;
722
723 case match_user_name:
724 result = pies_identity_is_user (input->identity, ent->names);
725 if (!result)
726 return result;
727 break;
728
729 case match_group_name:
730 result = pies_identity_is_group_member (input->identity, ent->names);
731 if (!result)
732 return result;
733 }
734
735 result = pies_acl_check (ent->acl, input, 1);
736 if (!result)
737 return result;
738
739 if (ent->sockaddrs)
740 {
741 struct grecs_list_entry *ep;
742
743 result = 0;
744 for (ep = ent->sockaddrs->head; ep; ep = ep->next)
745 {
746 result = _check_sockaddr ((struct pies_sockaddr *)ep->data, input);
747 if (result)
748 break;
749 }
750 }
751
752 return result;
753 }
754
755 static int
_acl_check_cb(struct acl_entry * ent,struct acl_input * input,int * pres)756 _acl_check_cb (struct acl_entry *ent, struct acl_input *input, int *pres)
757 {
758 int result = _acl_check (ent, input);
759 debug (1, ("%s:%d: %s", ent->locus.beg.file, ent->locus.beg.line,
760 /* TRANSLATORS: `MATCHES' is the verb `match' in 2nd person.
761 E.g., in French: CONCORD AVEC */
762 result ? _("MATCHES") : _("does not match")));
763
764 if (result)
765 {
766 *pres = ent->allow;
767 return 1;
768 }
769 return 0;
770 }
771
772 int
pies_acl_check(pies_acl_t acl,struct acl_input * input,int result)773 pies_acl_check (pies_acl_t acl, struct acl_input *input, int result)
774 {
775 if (acl)
776 {
777 struct grecs_list_entry *ep;
778
779 for (ep = acl->list->head; ep; ep = ep->next)
780 if (_acl_check_cb ((struct acl_entry *)ep->data, input, &result))
781 break;
782 }
783 return result;
784 }
785
786
787 /* Hash table */
788
789 static struct grecs_symtab *acl_table;
790
791 /* Calculate the hash of a string. */
792 static unsigned
acl_hasher(void * data,unsigned long n_buckets)793 acl_hasher (void *data, unsigned long n_buckets)
794 {
795 const struct pies_acl *p = data;
796 return grecs_hash_string (p->name, n_buckets);
797 }
798
799 /* Compare two strings for equality. */
800 static int
acl_compare(void const * data1,void const * data2)801 acl_compare (void const *data1, void const *data2)
802 {
803 const struct pies_acl *p1 = data1;
804 const struct pies_acl *p2 = data2;
805 return strcasecmp (p1->name, p2->name);
806 }
807
808 static int
acl_copy(void * a,void * b)809 acl_copy (void *a, void *b)
810 {
811 memcpy (a, b, sizeof (struct pies_acl));
812 memset (b, 0, sizeof (struct pies_acl));
813 return 0;
814 }
815
816 static void
acl_free(void * p)817 acl_free (void *p)
818 {
819 pies_acl_free (p);
820 }
821
822 pies_acl_t
pies_acl_install(pies_acl_t acl)823 pies_acl_install (pies_acl_t acl)
824 {
825 pies_acl_t ret;
826 int install = 1;
827
828 if (!acl_table)
829 {
830 acl_table = grecs_symtab_create(sizeof (struct pies_acl),
831 acl_hasher,
832 acl_compare,
833 acl_copy,
834 NULL,
835 acl_free);
836 if (!acl_table)
837 grecs_alloc_die ();
838 }
839
840 ret = grecs_symtab_lookup_or_install (acl_table, acl, &install);
841
842 if (!ret)
843 {
844 logmsg (LOG_ERR, _("cannot install ACL: %s"), strerror (errno));
845 exit (1);
846 }
847
848 if (!install)
849 {
850 grecs_error (&acl->locus, 0,
851 _("redefinition of ACL %s"),
852 ret->name);
853 grecs_error (&ret->locus, 0,
854 _("location of the previous definition"));
855 ret = NULL;
856 }
857 pies_acl_free (acl);
858 return ret;
859 }
860
861 pies_acl_t
pies_acl_lookup(const char * name)862 pies_acl_lookup (const char *name)
863 {
864 struct pies_acl samp;
865 if (!acl_table)
866 return NULL;
867 samp.name = (char *) name;
868 return grecs_symtab_lookup_or_install (acl_table, &samp, NULL);
869 }
870
871 int
pies_acl_cmp(struct pies_acl * a,struct pies_acl * b)872 pies_acl_cmp (struct pies_acl *a, struct pies_acl *b)
873 {
874 if (!a)
875 return !!b;
876 else
877 return 1;
878 return grecs_list_compare (a->list, b->list);
879 }
880