1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2007-2021 Free Software Foundation, Inc.
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 3 of the License, or (at your option) any later version.
8 
9    This library 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 GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General
15    Public License along with this library; If not, see
16    <http://www.gnu.org/licenses/>.  */
17 
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <sys/un.h>
24 #include <arpa/inet.h>
25 
26 #include <sys/types.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <mailutils/nls.h>
32 #include <mailutils/acl.h>
33 #include <mailutils/wordsplit.h>
34 #include <mailutils/list.h>
35 #include <mailutils/debug.h>
36 #include <mailutils/sys/debcat.h>
37 #include <mailutils/error.h>
38 #include <mailutils/errno.h>
39 #include <mailutils/kwd.h>
40 #include <mailutils/io.h>
41 #include <mailutils/util.h>
42 #include <mailutils/sockaddr.h>
43 #include <mailutils/cidr.h>
44 #include <mailutils/stream.h>
45 #include <mailutils/stdstream.h>
46 
47 #ifndef MU_INADDR_BYTES
48 #define MU_INADDR_BYTES 16
49 #endif
50 
51 struct _mu_acl_entry
52 {
53   mu_acl_action_t action;
54   void *arg;
55   struct mu_cidr cidr;
56 };
57 
58 struct _mu_acl
59 {
60   mu_list_t aclist;
61   char **envv;
62   size_t envc;
63   size_t envn;
64 };
65 
66 struct run_closure
67 {
68   unsigned idx;
69   struct mu_cidr addr;
70 
71   char **env;
72   char ipstr[40];
73   char *addrstr;
74   mu_acl_result_t *result;
75 };
76 
77 
78 static void
_destroy_acl_entry(void * item)79 _destroy_acl_entry (void *item)
80 {
81   struct _mu_acl_entry *p = item;
82   free (p);
83   /* FIXME: free arg? */
84 }
85 
86 int
mu_acl_entry_create(struct _mu_acl_entry ** pent,mu_acl_action_t action,void * data,struct mu_cidr * cidr)87 mu_acl_entry_create (struct _mu_acl_entry **pent,
88 		     mu_acl_action_t action, void *data,
89 		     struct mu_cidr *cidr)
90 {
91   struct _mu_acl_entry *p = malloc (sizeof (*p));
92   if (!p)
93     return EINVAL;
94 
95   p->action = action;
96   p->arg = data;
97   memcpy (&p->cidr, cidr, sizeof (p->cidr));
98 
99   *pent = p;
100   return 0;
101 }
102 
103 
104 int
mu_acl_create(mu_acl_t * pacl)105 mu_acl_create (mu_acl_t *pacl)
106 {
107   int rc;
108   mu_acl_t acl;
109 
110   acl = calloc (1, sizeof (*acl));
111   if (!acl)
112     return errno;
113   rc = mu_list_create (&acl->aclist);
114   if (rc)
115     free (acl);
116   else
117     *pacl = acl;
118   mu_list_set_destroy_item (acl->aclist, _destroy_acl_entry);
119 
120   return rc;
121 }
122 
123 int
mu_acl_count(mu_acl_t acl,size_t * pcount)124 mu_acl_count (mu_acl_t acl, size_t *pcount)
125 {
126   if (!acl)
127     return EINVAL;
128   return mu_list_count (acl->aclist, pcount);
129 }
130 
131 int
mu_acl_destroy(mu_acl_t * pacl)132 mu_acl_destroy (mu_acl_t *pacl)
133 {
134   size_t i;
135 
136   mu_acl_t acl;
137   if (!pacl || !*pacl)
138     return EINVAL;
139   acl = *pacl;
140   mu_list_destroy (&acl->aclist);
141   for (i = 0; i < acl->envc && acl->envv[i]; i++)
142     free (acl->envv[i]);
143   free (acl->envv);
144   free (acl);
145   *pacl = acl;
146   return 0;
147 }
148 
149 int
mu_acl_get_iterator(mu_acl_t acl,mu_iterator_t * pitr)150 mu_acl_get_iterator (mu_acl_t acl, mu_iterator_t *pitr)
151 {
152   if (!acl)
153     return EINVAL;
154   return mu_list_get_iterator (acl->aclist, pitr);
155 }
156 
157 int
mu_acl_append(mu_acl_t acl,mu_acl_action_t act,void * data,struct mu_cidr * cidr)158 mu_acl_append (mu_acl_t acl, mu_acl_action_t act,
159 	       void *data, struct mu_cidr *cidr)
160 {
161   int rc;
162   struct _mu_acl_entry *ent;
163 
164   if (!acl)
165     return EINVAL;
166   rc = mu_acl_entry_create (&ent, act, data, cidr);
167   if (rc)
168     {
169       mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR,
170                 ("Cannot allocate ACL entry: %s", mu_strerror (rc)));
171       return ENOMEM;
172     }
173 
174   rc = mu_list_append (acl->aclist, ent);
175   if (rc)
176     {
177       mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR,
178                 ("Cannot append ACL entry: %s", mu_strerror (rc)));
179       free (ent);
180     }
181   return rc;
182 }
183 
184 int
mu_acl_prepend(mu_acl_t acl,mu_acl_action_t act,void * data,struct mu_cidr * cidr)185 mu_acl_prepend (mu_acl_t acl, mu_acl_action_t act, void *data,
186 		struct mu_cidr *cidr)
187 {
188   int rc;
189   struct _mu_acl_entry *ent;
190 
191   if (!acl)
192     return EINVAL;
193   rc = mu_acl_entry_create (&ent, act, data, cidr);
194   if (rc)
195     {
196       mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR,
197                 ("Cannot allocate ACL entry: %s", mu_strerror (rc)));
198       return ENOMEM;
199     }
200   rc = mu_list_prepend (acl->aclist, ent);
201   if (rc)
202     {
203       mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR,
204                 ("Cannot prepend ACL entry: %s", mu_strerror (rc)));
205       free (ent);
206     }
207   return rc;
208 }
209 
210 int
mu_acl_insert(mu_acl_t acl,size_t pos,int before,mu_acl_action_t act,void * data,struct mu_cidr * cidr)211 mu_acl_insert (mu_acl_t acl, size_t pos, int before,
212 	       mu_acl_action_t act, void *data, struct mu_cidr *cidr)
213 {
214   int rc;
215   void *ptr;
216   struct _mu_acl_entry *ent;
217 
218   if (!acl)
219     return EINVAL;
220 
221   rc = mu_list_get (acl->aclist, pos, &ptr);
222   if (rc)
223     {
224       mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR,
225                 ("No such entry %lu", (unsigned long) pos));
226       return rc;
227     }
228   rc = mu_acl_entry_create (&ent, act, data, cidr);
229   if (!ent)
230     {
231       mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR,
232                 ("Cannot allocate ACL entry: %s", mu_strerror (rc)));
233       return ENOMEM;
234     }
235   rc = mu_list_insert (acl->aclist, ptr, ent, before);
236   if (rc)
237     {
238       mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR,
239                 ("Cannot insert ACL entry: %s", mu_strerror (rc)));
240       free (ent);
241     }
242   return rc;
243 }
244 
245 
246 static mu_kwd_t action_tab[] = {
247   { "accept", mu_acl_accept },
248   { "deny", mu_acl_deny },
249   { "log", mu_acl_log },
250   { "exec", mu_acl_exec },
251   { "ifexec", mu_acl_ifexec },
252   { NULL }
253 };
254 
255 int
mu_acl_action_to_string(mu_acl_action_t act,const char ** pstr)256 mu_acl_action_to_string (mu_acl_action_t act, const char **pstr)
257 {
258   return mu_kwd_xlat_tok (action_tab, act, pstr);
259 }
260 
261 int
mu_acl_string_to_action(const char * str,mu_acl_action_t * pres)262 mu_acl_string_to_action (const char *str, mu_acl_action_t *pres)
263 {
264   int x;
265   int rc = mu_kwd_xlat_name (action_tab, str, &x);
266   if (rc == 0)
267     *pres = x;
268   return rc;
269 }
270 
271 int
_acl_match(struct _mu_acl_entry * ent,struct run_closure * rp)272 _acl_match (struct _mu_acl_entry *ent, struct run_closure *rp)
273 {
274 #define RESMATCH(word)                                   \
275   if (mu_debug_level_p (MU_DEBCAT_ACL, MU_DEBUG_TRACE9))     \
276     mu_debug_log_end ("%s; ", word);
277 
278   if (mu_debug_level_p (MU_DEBCAT_ACL, MU_DEBUG_TRACE9))
279     {
280       char *s = NULL;
281       int rc;
282 
283       if (ent->cidr.len && (rc = mu_cidr_format (&ent->cidr, 0, &s)))
284         {
285           mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR,
286                     ("mu_cidr_format: %s", mu_strerror (rc)));
287           return 1;
288         }
289       if (!rp->addrstr)
290         mu_cidr_format (&rp->addr, MU_CIDR_FMT_ADDRONLY, &rp->addrstr);
291       mu_debug_log_begin ("Does %s match %s? ", s ? s : "any", rp->addrstr);
292       free (s);
293     }
294 
295   if (ent->cidr.len > 0 && mu_cidr_match (&ent->cidr, &rp->addr))
296     {
297       RESMATCH ("no");
298       return 1;
299     }
300   RESMATCH ("yes");
301   return 0;
302 }
303 
304 #define SEQ(s, n, l) \
305   (((l) == (sizeof(s) - 1)) && memcmp (s, n, l) == 0)
306 
307 static int
acl_getvar(char ** ret,const char * name,size_t nlen,void * data)308 acl_getvar (char **ret, const char *name, size_t nlen, void *data)
309 {
310   struct run_closure *rp = data;
311 
312   if (SEQ ("aclno", name, nlen))
313     {
314       if (mu_asprintf (ret, "%u", rp->idx))
315 	return MU_WRDSE_NOSPACE;
316       return MU_WRDSE_OK;
317     }
318   else if (SEQ ("address", name, nlen))
319     {
320       if (mu_cidr_format (&rp->addr, MU_CIDR_FMT_ADDRONLY, ret))
321 	return MU_WRDSE_NOSPACE;
322       return MU_WRDSE_OK;
323     }
324   else if (SEQ ("family", name, nlen))
325     {
326       char *s;
327 
328       switch (rp->addr.family)
329 	{
330 	case AF_INET:
331 	  s = "AF_INET";
332 	  break;
333 
334 #ifdef MAILUTILS_IPV6
335 	case AF_INET6:
336 	  s = "AF_INET6";
337 	  break;
338 #endif
339 
340 	case AF_UNIX:
341 	  s = "AF_UNIX";
342 	  break;
343 
344 	default:
345 	  return MU_WRDSE_UNDEF;
346 	}
347 
348       *ret = strdup (s);
349       if (!*ret)
350 	return MU_WRDSE_NOSPACE;
351       return MU_WRDSE_OK;
352     }
353 
354   return MU_WRDSE_UNDEF;
355 }
356 
357 static int
expand_arg(const char * cmdline,struct run_closure * rp,char ** s)358 expand_arg (const char *cmdline, struct run_closure *rp, char **s)
359 {
360   int rc;
361   struct mu_wordsplit ws;
362   int envflag = 0;
363 
364   mu_debug (MU_DEBCAT_ACL, MU_DEBUG_TRACE, ("Expanding \"%s\"", cmdline));
365   if (rp->env)
366     {
367       ws.ws_env = (const char **) rp->env;
368       envflag = MU_WRDSF_ENV;
369     }
370   ws.ws_getvar = acl_getvar;
371   ws.ws_closure = rp;
372   rc = mu_wordsplit (cmdline, &ws,
373 		     MU_WRDSF_NOSPLIT | MU_WRDSF_NOCMD |
374 		     envflag | MU_WRDSF_ENV_KV |
375 		     MU_WRDSF_GETVAR | MU_WRDSF_CLOSURE);
376 
377   if (rc == 0)
378     {
379       *s = strdup (ws.ws_wordv[0]);
380       mu_wordsplit_free (&ws);
381       if (!*s)
382 	{
383 	  mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR,
384 	            ("failed: not enough memory."));
385 	  return ENOMEM;
386 	}
387       mu_debug (MU_DEBCAT_ACL, MU_DEBUG_TRACE, ("Expansion: \"%s\". ", *s));
388     }
389   else
390     {
391       mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR,
392                 ("failed: %s", mu_wordsplit_strerror (&ws)));
393       rc = errno;
394     }
395   return rc;
396 }
397 
398 static int
spawn_prog(const char * cmdline,int * pstatus,struct run_closure * rp)399 spawn_prog (const char *cmdline, int *pstatus, struct run_closure *rp)
400 {
401   char *s;
402   pid_t pid;
403 
404   if (expand_arg (cmdline, rp, &s))
405     {
406       s = strdup (cmdline);
407       if (!s)
408         return ENOMEM;
409     }
410   pid = fork ();
411   if (pid == 0)
412     {
413       struct mu_wordsplit ws;
414 
415       if (mu_wordsplit (s, &ws, MU_WRDSF_DEFFLAGS))
416 	{
417 	  mu_error (_("cannot split line `%s': %s"), s,
418 		    mu_wordsplit_strerror (&ws));
419 	  _exit (127);
420 	}
421 
422       mu_close_fds (3);
423 
424       execvp (ws.ws_wordv[0], ws.ws_wordv);
425       _exit (127);
426     }
427 
428   free (s);
429 
430   if (pid == (pid_t)-1)
431     {
432       mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR,
433                 ("cannot fork: %s", mu_strerror (errno)));
434       return errno;
435     }
436 
437   if (pstatus)
438     {
439       int status;
440       waitpid (pid, &status, 0);
441       if (WIFEXITED (status))
442 	{
443 	  status = WEXITSTATUS (status);
444 	  mu_debug (MU_DEBCAT_ACL, MU_DEBUG_TRACE,
445 		    ("Program finished with code %d.", status));
446 	  *pstatus = status;
447 	}
448       else if (WIFSIGNALED (status))
449 	{
450           mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR,
451 		    ("Program terminated on signal %d.",
452 		     WTERMSIG (status)));
453 	  return MU_ERR_PROCESS_SIGNALED;
454 	}
455       else
456 	return MU_ERR_PROCESS_UNKNOWN_FAILURE;
457     }
458 
459   return 0;
460 }
461 
462 
463 int
_run_entry(void * item,void * data)464 _run_entry (void *item, void *data)
465 {
466   int status = 0;
467   struct _mu_acl_entry *ent = item;
468   struct run_closure *rp = data;
469 
470   rp->idx++;
471 
472   if (mu_debug_level_p (MU_DEBCAT_ACL, MU_DEBUG_TRACE9))
473     {
474       const char *s = "undefined";
475       mu_acl_action_to_string (ent->action, &s);
476       mu_debug_log_begin ("%d:%s: ", rp->idx, s);
477     }
478 
479   if (_acl_match (ent, rp) == 0)
480     {
481       switch (ent->action)
482 	{
483 	case mu_acl_accept:
484 	  *rp->result = mu_acl_result_accept;
485 	  status = MU_ERR_USER0;
486 	  break;
487 
488 	case mu_acl_deny:
489 	  *rp->result = mu_acl_result_deny;
490 	  status = MU_ERR_USER0;
491 	  break;
492 
493 	case mu_acl_log:
494 	  {
495 	    char *s;
496 	    if (ent->arg && expand_arg (ent->arg, rp, &s) == 0)
497 	      {
498 		mu_diag_output (MU_DIAG_INFO, "%s", s);
499 		free (s);
500 	      }
501 	    else
502 	      {
503 		if (!rp->addrstr)
504 		  mu_cidr_format (&rp->addr, MU_CIDR_FMT_ADDRONLY,
505 				  &rp->addrstr);
506 		mu_diag_output (MU_DIAG_INFO, "%s", rp->addrstr);
507 	      }
508 	  }
509 	  break;
510 
511 	case mu_acl_exec:
512 	  spawn_prog (ent->arg, NULL, rp);
513 	  break;
514 
515 	case mu_acl_ifexec:
516 	  {
517 	    int prog_status;
518 	    int rc = spawn_prog (ent->arg, &prog_status, rp);
519 	    if (rc == 0)
520 	      {
521 		switch (prog_status)
522 		  {
523 		  case 0:
524 		    *rp->result = mu_acl_result_accept;
525 		    status = MU_ERR_USER0;
526 		    break;
527 
528 		  case 1:
529 		    *rp->result = mu_acl_result_deny;
530 		    status = MU_ERR_USER0;
531 		  }
532 	      }
533 	  }
534 	  break;
535 	}
536     }
537 
538   if (mu_debug_level_p (MU_DEBCAT_ACL, MU_DEBUG_TRACE9))
539     mu_stream_flush (mu_strerr);
540 
541   return status;
542 }
543 
544 int
mu_acl_check_sockaddr(mu_acl_t acl,const struct sockaddr * sa,int salen,mu_acl_result_t * pres)545 mu_acl_check_sockaddr (mu_acl_t acl, const struct sockaddr *sa, int salen,
546 		       mu_acl_result_t *pres)
547 {
548   int rc;
549   struct run_closure r;
550 
551   if (!acl)
552     return EINVAL;
553 
554   memset (&r, 0, sizeof (r));
555   if (sa->sa_family == AF_UNIX)
556     {
557       *pres = mu_acl_result_accept;
558       return 0;
559     }
560   rc = mu_cidr_from_sockaddr (&r.addr, sa);
561   if (rc)
562     return rc;
563 
564   if (mu_debug_level_p (MU_DEBCAT_ACL, MU_DEBUG_TRACE9))
565     {
566       mu_cidr_format (&r.addr, MU_CIDR_FMT_ADDRONLY, &r.addrstr);
567       mu_debug_log_begin ("Checking sockaddr %s", r.addrstr);
568       mu_debug_log_nl ();
569     }
570 
571   r.idx = 0;
572   r.result = pres;
573   r.env = acl->envv;
574   *r.result = mu_acl_result_undefined;
575   rc = mu_list_foreach (acl->aclist, _run_entry, &r);
576   free (r.addrstr);
577   if (rc == MU_ERR_USER0)
578     rc = 0;
579   return rc;
580 }
581 
582 int
mu_acl_check_inaddr(mu_acl_t acl,const struct in_addr * inp,mu_acl_result_t * pres)583 mu_acl_check_inaddr (mu_acl_t acl, const struct in_addr *inp,
584 		     mu_acl_result_t *pres)
585 {
586   struct sockaddr_in cs;
587   int len = sizeof cs;
588 
589   cs.sin_family = AF_INET;
590   cs.sin_addr = *inp;
591   cs.sin_addr.s_addr = ntohl (cs.sin_addr.s_addr);
592   return mu_acl_check_sockaddr (acl, (struct sockaddr *) &cs, len, pres);
593 }
594 
595 int
mu_acl_check_ipv4(mu_acl_t acl,unsigned int addr,mu_acl_result_t * pres)596 mu_acl_check_ipv4 (mu_acl_t acl, unsigned int addr, mu_acl_result_t *pres)
597 {
598   struct in_addr in;
599 
600   in.s_addr = addr;
601   return mu_acl_check_inaddr (acl, &in, pres);
602 }
603 
604 int
mu_acl_check_fd(mu_acl_t acl,int fd,mu_acl_result_t * pres)605 mu_acl_check_fd (mu_acl_t acl, int fd, mu_acl_result_t *pres)
606 {
607   union
608   {
609     struct sockaddr sa;
610     struct sockaddr_in in;
611 #ifdef MAILUTILS_IPV6
612     struct sockaddr_in6 in6;
613 #endif
614   } addr;
615   socklen_t len = sizeof addr;
616 
617   if (getpeername (fd, &addr.sa, &len) < 0)
618     {
619       mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR,
620 		("Cannot obtain IP address of client: %s",
621 		 mu_strerror (errno)));
622       return MU_ERR_FAILURE;
623     }
624 
625   return mu_acl_check_sockaddr (acl, &addr.sa, len, pres);
626 }
627 
628 static int
_acl_getenv(mu_acl_t acl,const char * name,size_t * pres)629 _acl_getenv (mu_acl_t acl, const char *name, size_t *pres)
630 {
631   size_t i;
632 
633   if (!acl->envv)
634     return MU_ERR_NOENT;
635   for (i = 0; i < acl->envc; i++)
636     if (strcmp (acl->envv[i], name) == 0)
637       {
638 	*pres = i;
639 	return 0;
640       }
641   return MU_ERR_NOENT;
642 }
643 
644 const char *
mu_acl_getenv(mu_acl_t acl,const char * name)645 mu_acl_getenv (mu_acl_t acl, const char *name)
646 {
647   size_t i;
648 
649   if (_acl_getenv (acl, name, &i) == 0)
650     {
651       return acl->envv[i + 1];
652     }
653   return NULL;
654 }
655 
656 static int
_acl_env_store(mu_acl_t acl,int i,const char * val)657 _acl_env_store (mu_acl_t acl, int i, const char *val)
658 {
659   char *copy = strdup (val);
660   if (!copy)
661     return ENOMEM;
662   free (acl->envv[i]);
663   acl->envv[i] = copy;
664   return 0;
665 }
666 
667 int
mu_acl_setenv(mu_acl_t acl,const char * name,const char * val)668 mu_acl_setenv (mu_acl_t acl, const char *name, const char *val)
669 {
670   size_t i;
671 
672   if (_acl_getenv (acl, name, &i) == 0)
673     {
674       if (!val)
675 	{
676 	  free (acl->envv[i]);
677 	  free (acl->envv[i + 1]);
678 	  memmove (acl->envv + i, acl->envv + i + 3,
679 		   (acl->envn + 1 - (i + 3)) * sizeof (acl->envv[0]));
680 	  acl->envn -= 2;
681 	  return 0;
682 	}
683       return _acl_env_store (acl, i + 1, val);
684     }
685 
686   if (!acl->envv || acl->envn + 1 == acl->envc)
687     {
688       char **p;
689 
690       if (!val)
691 	return 0;
692 
693       if (acl->envv == NULL)
694 	p = calloc (3, sizeof (acl->envv[0]));
695       else
696 	{
697 	  p = realloc (acl->envv, (acl->envc + 3) * sizeof (acl->envv[0]));
698 	  if (!p)
699 	    return ENOMEM;
700 	  p[acl->envc] = NULL;
701 	}
702 
703       acl->envv = p;
704       acl->envc += 3;
705     }
706 
707   if (_acl_env_store (acl, acl->envn, name))
708     return ENOMEM;
709   if (_acl_env_store (acl, acl->envn + 1, val))
710     {
711       free (acl->envv[acl->envn]);
712       acl->envv[acl->envn] = NULL;
713       return ENOMEM;
714     }
715   acl->envn += 2;
716 
717   return 0;
718 }
719