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