source: trunk/src/sh_nmail.c@ 236

Last change on this file since 236 was 232, checked in by katerina, 15 years ago

Add some missing files

File size: 19.2 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2008 Rainer Wichmann */
3/* */
4/* This program is free software; you can redistribute it */
5/* and/or modify */
6/* it under the terms of the GNU General Public License as */
7/* published by */
8/* the Free Software Foundation; either version 2 of the License, or */
9/* (at your option) any later version. */
10/* */
11/* This program is distributed in the hope that it will be useful, */
12/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14/* GNU General Public License for more details. */
15/* */
16/* You should have received a copy of the GNU General Public License */
17/* along with this program; if not, write to the Free Software */
18/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20#include "config_xor.h"
21
22#if defined(HAVE_PTHREAD_MUTEX_RECURSIVE)
23#define _XOPEN_SOURCE 500
24#endif
25
26#include <string.h>
27#include <time.h>
28
29#if defined(SH_WITH_MAIL)
30
31#undef FIL__
32#define FIL__ _("sh_nmail.c")
33
34#include "samhain.h"
35#include "sh_pthread.h"
36#include "sh_mem.h"
37#include "sh_mail.h"
38#include "sh_tiger.h"
39#include "sh_string.h"
40#include "sh_utils.h"
41#include "sh_fifo.h"
42#include "sh_filter.h"
43#include "sh_mail_int.h"
44
45SH_MUTEX_INIT(mutex_listall, PTHREAD_MUTEX_INITIALIZER);
46
47/* Pointer to last address */
48
49static struct alias * last = NULL;
50
51/* List of mail recipients */
52
53static struct alias * recipient_list = NULL;
54
55static struct alias * compiled_recipient_list = NULL;
56static sh_filter_type compiled_mail_filter = SH_FILT_INIT;
57
58/* List of mail aliases */
59
60static struct alias * alias_list = NULL;
61
62/* List of all recipients */
63
64struct alias * all_recipients = NULL;
65
66int check_double (const char * str, struct alias * list)
67{
68 if (str && list)
69 {
70 struct alias * item = list;
71
72 while (item)
73 {
74 if (0 == strcmp(sh_string_str(item->recipient), str))
75 return -1;
76 item = item->next;
77 }
78 }
79 return 0;
80}
81
82struct alias * add_recipient_intern(const char * str,
83 struct alias * list)
84{
85 if (str)
86 {
87 struct alias * new = SH_ALLOC(sizeof(struct alias));
88 new->next = list;
89 new->mx_list = NULL;
90 new->mail_filter = NULL;
91 new->recipient_list = NULL;
92 new->severity = (-1);
93 new->send_mail = 0;
94 new->recipient = sh_string_new_from_lchar(str, strlen(str));
95 list = new;
96
97 SH_MUTEX_LOCK_UNSAFE(mutex_listall);
98 new->all_next = all_recipients;
99 all_recipients = new;
100 SH_MUTEX_UNLOCK_UNSAFE(mutex_listall);
101 }
102 return list;
103}
104
105int sh_nmail_close_recipient(const char * str)
106{
107 (void) str;
108
109 if (last)
110 {
111 last = NULL;
112 return 0;
113 }
114 return -1;
115}
116
117/* Add a single recipient
118 */
119int sh_nmail_add_recipient(const char * str)
120{
121 if (0 == check_double(str, recipient_list))
122 {
123 recipient_list = add_recipient_intern(str, recipient_list);
124 last = recipient_list;
125 return 0;
126 }
127 return -1;
128}
129
130/* Add a compiled-in address. These share the compiled_mail_filter
131 */
132int sh_nmail_add_compiled_recipient(const char * str)
133{
134 if (0 == check_double(str, compiled_recipient_list))
135 {
136 compiled_recipient_list =
137 add_recipient_intern(str, compiled_recipient_list);
138 if (compiled_recipient_list)
139 compiled_recipient_list->mail_filter = &compiled_mail_filter;
140 last = compiled_recipient_list;
141 return 0;
142 }
143 return -1;
144}
145
146/* Add an alias; format is name ":" comma-delimited_list_of_recipients
147 */
148int sh_nmail_add_alias(const char * str)
149{
150#define SH_ALIASES_RECP_NUM 256
151 size_t lengths[SH_ALIASES_RECP_NUM];
152 unsigned int nfields = SH_ALIASES_RECP_NUM;
153 char * new = sh_util_strdup(str);
154 char * p = strchr(new, ':');
155 char * q;
156
157 if (p && strlen(p) > 1)
158 {
159 unsigned int i;
160 char ** array;
161
162 *p = '\0'; q = p; ++p;
163 if (strlen(new) > 0)
164 {
165 --q; while ((q != new) && *q == ' ') { *q = '\0'; --q; }
166 }
167 else
168 {
169 goto err;
170 }
171
172 if (0 == check_double(new, alias_list))
173 {
174
175 array = split_array_list(p, &nfields, lengths);
176
177 if (array && nfields > 0)
178 {
179 struct alias * newalias = SH_ALLOC(sizeof(struct alias));
180 newalias->recipient_list = NULL;
181 newalias->mail_filter = NULL;
182 newalias->mx_list = NULL;
183 newalias->severity = (-1);
184 /* This is the alias */
185 newalias->recipient = sh_string_new_from_lchar(new, strlen(new));
186
187 for (i = 0; i < nfields; ++i)
188 {
189 if (lengths[i] > 0 &&
190 0 == check_double(array[i], newalias->recipient_list))
191 {
192 newalias->recipient_list =
193 add_recipient_intern(array[i],newalias->recipient_list);
194 }
195 }
196
197 SH_FREE(array);
198
199 if (newalias->recipient_list == NULL)
200 {
201 SH_FREE(newalias);
202 goto err;
203 }
204
205 newalias->next = alias_list;
206 alias_list = newalias;
207 last = alias_list;
208
209 SH_FREE(new);
210 return 0;
211 }
212 }
213 }
214 err:
215 SH_FREE(new);
216 return -1;
217}
218
219
220/* <<<<<<<<<<<<<<< Recipient List >>>>>>>>>>>>>>>>>>>>>> */
221
222static struct alias * find_list (const char * alias, int * single)
223{
224 struct alias * list = NULL;
225
226 *single = 0;
227
228 if (!alias)
229 {
230 list = all_recipients;
231 }
232 else
233 {
234 struct alias * test = alias_list;
235
236 while (test)
237 {
238 if (0 == strcmp(alias, sh_string_str(test->recipient)))
239 {
240 list = test->recipient_list;
241 break;
242 }
243 test = test->next;
244 }
245
246 if (!list)
247 {
248 test = recipient_list;
249 while (test)
250 {
251 if (0 == strcmp(alias, sh_string_str(test->recipient)))
252 {
253 list = test;
254 *single = 1;
255 break;
256 }
257 test = test->next;
258 }
259 }
260
261 if (!list)
262 {
263 test = compiled_recipient_list;
264 while (test)
265 {
266 if (0 == strcmp(alias, sh_string_str(test->recipient)))
267 {
268 list = test;
269 *single = 1;
270 break;
271 }
272 test = test->next;
273 }
274 }
275 }
276 return list;
277}
278
279/* Returns zero (no) or one (yes). Used to tag messages that are
280 * valid for a given recipient (or mailing list alias).
281 */
282int sh_nmail_valid_message_for_alias (int level,
283 const char * message,
284 const char * alias,
285 const void * rcv_info)
286{
287 struct alias * rcv = (struct alias *) rcv_info;
288
289 if (!alias || 0 == strcmp(alias, sh_string_str(rcv->recipient)))
290 {
291 if ((level & rcv->severity) == 0)
292 {
293 return 0;
294 }
295
296 if (rcv->mail_filter)
297 {
298 if (0 != sh_filter_filter(message, rcv->mail_filter))
299 {
300 return 0;
301 }
302 }
303 }
304
305 return 1;
306}
307
308/* Returns number of recipients */
309
310static
311int sh_nmail_compute_recipients (int level, const char * message,
312 const char * alias, int flagit)
313{
314 struct alias * list = NULL;
315 int single = 0;
316 int retval = 0;
317
318 if (flagit)
319 {
320 list = all_recipients;
321 while (list)
322 {
323 list->send_mail = 0;
324 list = list->all_next;
325 }
326 list = NULL;
327 }
328
329 if (message)
330 {
331 int flag = 0;
332
333 list = find_list (alias, &single);
334 if (list == all_recipients)
335 flag = 1;
336
337 while (list)
338 {
339 /* Check severity
340 */
341 if ((list->severity & level) == 0)
342 {
343 if (single) break;
344 if (flag)
345 list = list->all_next;
346 else
347 list = list->next;
348 continue;
349 }
350
351 /* Check filter
352 */
353 if (list->mail_filter &&
354 0 != sh_filter_filter(message, list->mail_filter))
355 {
356 if (single) break;
357 if (flag)
358 list = list->all_next;
359 else
360 list = list->next;
361 continue;
362 }
363
364 /* Mark the entry
365 */
366 if (flagit)
367 list->send_mail = 1;
368 if (flag)
369 list = list->all_next;
370 else
371 list = list->next;
372 ++retval;
373 }
374 }
375 return retval;
376}
377
378static
379int sh_nmail_flag_recipients (int level, const char * message,
380 const char * alias)
381{
382 int retval = 0;
383
384 if (message)
385 {
386 SH_MUTEX_LOCK_UNSAFE(mutex_listall);
387 retval = sh_nmail_compute_recipients (level, message, alias, 1);
388 SH_MUTEX_UNLOCK_UNSAFE(mutex_listall);
389 }
390 return retval;
391}
392
393static
394int sh_nmail_test_recipients (int level, const char * message,
395 const char * alias)
396{
397 int retval = 0;
398
399 if (message)
400 {
401 SH_MUTEX_LOCK_UNSAFE(mutex_listall);
402 retval = sh_nmail_compute_recipients (level, message, alias, 0);
403 SH_MUTEX_UNLOCK_UNSAFE(mutex_listall);
404 }
405 return retval;
406}
407
408/* <<<<<<<<<<<<<<<<<<< Mail the message >>>>>>>>>>>>>>>>>>>>>> */
409
410SH_MUTEX_RECURSIVE(mutex_nmail_msg);
411SH_MUTEX_STATIC(nmail_lock, PTHREAD_MUTEX_INITIALIZER);
412
413/*
414 * First test list of recipients, then call sh_mail_pushstack().
415 */
416int sh_nmail_pushstack (int level, const char * message,
417 const char * alias)
418{
419 int retval = 0;
420
421 if (0 != sh_nmail_test_recipients (level, message, alias))
422 {
423 retval = sh_mail_pushstack(level, message, alias);
424 }
425 return retval;
426}
427
428/*
429 * First mark list of recipients, then call sh_mail_msg().
430 */
431int sh_nmail_msg (int level, const char * message,
432 const char * alias)
433{
434 volatile int retval = 0;
435 static int count = 0;
436
437 /* Need to:
438 * -- wait if different thread, and
439 * -- fail if same thread. */
440 SH_MUTEX_RECURSIVE_INIT(mutex_nmail_msg);
441 SH_MUTEX_RECURSIVE_LOCK(mutex_nmail_msg);
442
443 /* Only same thread beyond this point. We fail
444 * if count > 0 already. */
445 if (0 == SH_MUTEX_TRYLOCK_UNSAFE(nmail_lock))
446 {
447 ++count;
448 if (count != 1)
449 {
450 --count;
451 SH_MUTEX_UNLOCK_UNSAFE(nmail_lock);
452 goto cleanup;
453 }
454 SH_MUTEX_UNLOCK_UNSAFE(nmail_lock);
455
456 if (0 != sh_nmail_flag_recipients (level, message, alias))
457 {
458 /* Need to keep info for sh_nmail_pushstack()
459 */
460 SH_MUTEX_LOCK(mutex_listall);
461 retval = sh_mail_msg(message);
462 SH_MUTEX_UNLOCK(mutex_listall);
463
464 if (retval != 0)
465 {
466 sh_mail_pushstack(level, message, alias);
467 }
468 }
469 SH_MUTEX_LOCK_UNSAFE(nmail_lock);
470 --count;
471 SH_MUTEX_UNLOCK_UNSAFE(nmail_lock);
472 }
473 cleanup:
474 ; /* label at end of compound statement */
475 SH_MUTEX_RECURSIVE_UNLOCK(mutex_nmail_msg);
476 return retval;
477}
478
479/*
480 * Loop over all recipients in stack.
481 * For each distinct one, mark all messages for sending.
482 * Then call sh_mail_msg().
483 */
484
485int sh_nmail_flush ()
486{
487 int retval = 0;
488 sh_string * msg = NULL;
489 sh_string * smsg = NULL;
490 struct alias * list;
491 struct alias * dlist;
492
493 SH_MUTEX_LOCK(mutex_listall);
494
495 /* Reset recipient list
496 */
497 list = all_recipients;
498 while (list)
499 {
500 list->send_mail = 0;
501 list = list->all_next;
502 }
503
504 /* Check (i) compiled recipients, (b) aliases, (c) single recipients.
505 * For each, tag all messages, then call sh_mail_msg with
506 * appropriate address list.
507 */
508
509 reset_list(fifo_mail);
510
511 /* Compiled recipients
512 */
513 list = compiled_recipient_list;
514
515 if (list)
516 {
517 msg = tag_list(fifo_mail, sh_string_str(list->recipient),
518 sh_nmail_valid_message_for_alias, list);
519 }
520
521 if (msg)
522 {
523 while (list)
524 {
525 list->send_mail = 1;
526 list = list->next;
527 }
528
529 list = compiled_recipient_list;
530
531 (void) sh_mail_msg(sh_string_str(msg));
532 sh_string_destroy(&msg);
533
534 list = compiled_recipient_list;
535 while (list)
536 {
537 list->send_mail = 0;
538 list = list->next;
539 }
540 }
541
542 /* Aliases
543 */
544
545 list = alias_list;
546
547 while (list) {
548
549 /* Work through the recipient list. As smsg stores last msg,
550 * we send a batch whenever msg != smsg, and continue from
551 * that point in the recipient list.
552 */
553 struct alias * lnew;
554
555 while (list)
556 {
557 msg = tag_list(fifo_mail, sh_string_str(list->recipient),
558 sh_nmail_valid_message_for_alias, list);
559
560 if (msg)
561 {
562 if (!smsg) /* init */
563 {
564 smsg = sh_string_copy(msg);
565 }
566 else
567 {
568 if (0 != strcmp(sh_string_str(smsg), sh_string_str(msg)))
569 {
570 /*
571 * Don't set list = list->next here, since we want
572 * to continue with this recipient in the next batch.
573 */
574 sh_string_destroy(&msg);
575 break;
576 }
577 }
578 lnew = list->recipient_list;
579 while (lnew)
580 {
581 lnew->send_mail = 1;
582 lnew= lnew->next;
583 }
584 sh_string_destroy(&msg);
585 }
586 list = list->next;
587 }
588
589 /* Continue here if smsg != msg */
590
591 if (smsg)
592 {
593 (void) sh_mail_msg(sh_string_str(smsg));
594 sh_string_destroy(&smsg);
595 }
596
597 /* Reset old list of recipients (up to current point in list)
598 * and then continue with list from current point on.
599 */
600 dlist = alias_list;
601 while (dlist)
602 {
603 lnew = dlist->recipient_list;
604 while (lnew)
605 {
606 lnew->send_mail = 0;
607 lnew = lnew->next;
608 }
609 dlist = dlist->next;
610 }
611 }
612
613
614 /* Single recipients
615 */
616 list = recipient_list;
617
618 while (list) {
619
620 /* Work through the recipient list. As smsg stores last msg,
621 * we send a batch whenever msg != smsg, and continue from
622 * that point in the recipient list.
623 */
624
625 while (list)
626 {
627 msg = tag_list(fifo_mail, sh_string_str(list->recipient),
628 sh_nmail_valid_message_for_alias, list);
629
630 if (msg)
631 {
632 if (!smsg) /* init */
633 {
634 smsg = sh_string_copy(msg);
635 }
636 else
637 {
638 if (0 != strcmp(sh_string_str(smsg), sh_string_str(msg)))
639 {
640 /*
641 * Don't set list = list->next here, since we want
642 * to continue with this recipient in the next batch.
643 */
644 sh_string_destroy(&msg);
645 break;
646 }
647 }
648 list->send_mail = 1;
649 sh_string_destroy(&msg);
650 }
651 list = list->next;
652 }
653
654 /* Continue here if smsg != msg */
655
656 if (smsg)
657 {
658 (void) sh_mail_msg(sh_string_str(smsg));
659 sh_string_destroy(&smsg);
660 }
661
662 /* Reset old list of recipients (up to current point in list)
663 * and then continue with list from current point on.
664 */
665 dlist = recipient_list;
666 while (dlist)
667 {
668 dlist->send_mail = 0;
669 dlist = dlist->next;
670 }
671 }
672
673 /* Remove all mails for which no recipient failed
674 */
675
676 sh.mailNum.alarm_last -= commit_list(fifo_mail);
677 SH_MUTEX_UNLOCK(mutex_listall);
678
679 return retval;
680}
681
682
683
684/* <<<<<<<<<<<<<<<<<<< Severity >>>>>>>>>>>>>>>>>>>>>> */
685
686/*
687 * -- set severity threshold for recipient or alias
688 */
689int sh_nmail_set_severity (const char * str)
690{
691 if (last == recipient_list || last == alias_list)
692 {
693 if (0 == sh_error_set_level(str, &(last->severity)))
694 {
695 /* All recipients in alias share the severity
696 */
697 if (last == alias_list)
698 {
699 struct alias * ptr = last->recipient_list;
700
701 while (ptr)
702 {
703 ptr->severity = last->severity;
704 ptr = ptr->next;
705 }
706 }
707 return 0;
708 }
709 }
710 return (-1);
711}
712
713/* <<<<<<<<<<<<<<<<<<< Filters >>>>>>>>>>>>>>>>>>>>>> */
714
715
716int sh_nmail_add_generic (const char * str, int flag)
717{
718 if (last)
719 {
720 if (NULL == last->mail_filter)
721 last->mail_filter = sh_filter_alloc();
722
723 /* All recipients in alias share the mail filter
724 */
725 if (last == alias_list)
726 {
727 struct alias * ptr = last->recipient_list;
728
729 while (ptr)
730 {
731 ptr->mail_filter = last->mail_filter;
732 ptr = ptr->next;
733 }
734 }
735
736 return (sh_filter_add (str, last->mail_filter, flag));
737 }
738 return (-1);
739}
740
741/*
742 * -- add keywords to the OR filter
743 */
744int sh_nmail_add_or (const char * str)
745{
746 return sh_nmail_add_generic(str, SH_FILT_OR);
747}
748
749/*
750 * -- add keywords to the AND filter
751 */
752int sh_nmail_add_and (const char * str)
753{
754 return sh_nmail_add_generic(str, SH_FILT_AND);
755}
756
757/*
758 * -- add keywords to the NOT filter
759 */
760int sh_nmail_add_not (const char * str)
761{
762 return sh_nmail_add_generic(str, SH_FILT_NOT);
763}
764
765
766/* <<<<<<<<<<<<<<<<<<< Mailkey per Alias >>>>>>>>>>>>>>>>>>>>>>>>> */
767
768#if defined(HAVE_MLOCK) && !defined(HAVE_BROKEN_MLOCK)
769#include <sys/mman.h>
770#endif
771
772#include "zAVLTree.h"
773
774zAVLTree * mailkeys = NULL;
775
776struct alias_mailkey {
777 char * alias;
778 unsigned int mailcount;
779 time_t id_audit;
780 char mailkey_old[KEY_LEN+1];
781 char mailkey_new[KEY_LEN+1];
782};
783
784static zAVLKey sh_nmail_getkey(void const *item)
785{
786 const struct alias_mailkey * t = (const struct alias_mailkey *) item;
787 return (zAVLKey) t->alias;
788}
789
790/* Return mailkey for alias. If there's no key yet, create it and
791 * store it in the AVL tree.
792 * This is called from sh_mail_msg,
793 * which is called from sh_nmail_msg,
794 * which is protected by a mutex.
795 */
796int sh_nmail_get_mailkey (const char * alias, char * buf, size_t bufsiz,
797 time_t * id_audit)
798{
799 char hashbuf[KEYBUF_SIZE];
800
801 start:
802
803 if (mailkeys)
804 {
805 struct alias_mailkey * t;
806
807 if (!alias)
808 t = (struct alias_mailkey *) zAVLSearch (mailkeys, _("(null)"));
809 else
810 t = (struct alias_mailkey *) zAVLSearch (mailkeys, alias);
811
812 if (t)
813 {
814 /* iterate the key
815 */
816 (void) sl_strlcpy(t->mailkey_new,
817 sh_tiger_hash (t->mailkey_old, TIGER_DATA, KEY_LEN,
818 hashbuf, sizeof(hashbuf)),
819 KEY_LEN+1);
820 (void) sl_strlcpy(buf, t->mailkey_new, bufsiz);
821 ++(t->mailcount);
822 }
823 else
824 {
825 t = SH_ALLOC(sizeof(struct alias_mailkey));
826
827 MLOCK(t, sizeof(struct alias_mailkey));
828
829 if (!alias)
830 t->alias = sh_util_strdup(_("(null)"));
831 else
832 t->alias = sh_util_strdup(alias);
833
834 t->mailcount = 0;
835 t->id_audit = time(NULL);
836
837 BREAKEXIT(sh_util_keyinit);
838 (void) sh_util_keyinit (t->mailkey_old, KEY_LEN+1);
839
840 /* iterate the key
841 */
842 (void) sl_strlcpy(t->mailkey_new,
843 sh_tiger_hash (t->mailkey_old, TIGER_DATA, KEY_LEN,
844 hashbuf, sizeof(hashbuf)),
845 KEY_LEN+1);
846 (void) sl_strlcpy(buf, t->mailkey_new, bufsiz);
847 (void) zAVLInsert(mailkeys, t);
848 }
849
850 /* X(n) -> X(n-1)
851 */
852 (void) sl_strlcpy (t->mailkey_old, t->mailkey_new, KEY_LEN+1);
853
854 *id_audit = t->id_audit;
855
856 return (t->mailcount);
857 }
858
859 mailkeys = zAVLAllocTree (sh_nmail_getkey);
860 goto start;
861}
862
863/* <<<<<<<<<<<<<<<<<<< Free for Reconfigure >>>>>>>>>>>>>>>>>>>>>> */
864
865
866static void free_recipient_list(struct alias * list)
867{
868 struct alias * new;
869
870 while (list)
871 {
872 new = list;
873 list = new->next;
874 if (new->mx_list)
875 free_mx(new->mx_list);
876 if (new->mail_filter)
877 {
878 sh_filter_free(new->mail_filter);
879 SH_FREE(new->mail_filter);
880 }
881 sh_string_destroy(&(new->recipient));
882 SH_FREE(new);
883 }
884}
885
886/* Free everything to prepare for reconfigure
887 */
888void sh_nmail_free()
889{
890 SH_MUTEX_LOCK_UNSAFE(mutex_listall);
891 all_recipients = NULL;
892 SH_MUTEX_UNLOCK_UNSAFE(mutex_listall);
893
894 free_recipient_list(recipient_list);
895 recipient_list = NULL;
896
897 sh_filter_free(&compiled_mail_filter);
898
899 while (alias_list)
900 {
901 struct alias * item = alias_list;
902
903 alias_list = item->next;
904
905 sh_string_destroy(&(item->recipient));
906 free_recipient_list(item->recipient_list);
907 if (item->mail_filter)
908 {
909 sh_filter_free(item->mail_filter);
910 SH_FREE(item->mail_filter);
911 }
912 SH_FREE(item);
913 }
914 alias_list = NULL;
915
916 last = compiled_recipient_list;
917 return;
918}
919
920/* defined(SH_WITH_MAIL) */
921#endif
Note: See TracBrowser for help on using the repository browser.