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