ze-filter  (ze-filter-0.8.0-develop-180218)
ze-rcpt.c
Go to the documentation of this file.
1 
2 /*
3  *
4  * ze-filter - Mail Server Filter for sendmail
5  *
6  * Copyright (c) 2001-2018 - Jose-Marcio Martins da Cruz
7  *
8  * Auteur : Jose Marcio Martins da Cruz
9  * jose.marcio.mc@gmail.org
10  *
11  * Historique :
12  * Creation : janvier 2002
13  *
14  * This program is free software, but with restricted license :
15  *
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20  *
21  * More details about ze-filter license can be found at ze-filter
22  * web site : http://foss.jose-marcio.org
23  */
24 
25 #include <ze-sys.h>
26 
27 #include "ze-filter.h"
28 
29 #define DBG_LEVEL 12
30 
31 #define CHK_DOMAIN_OK 0 /* Accept all rcpts for this domain */
32 #define CHK_DOMAIN_YES 1 /* Check only full addresses and domain part */
33 #define CHK_DOMAIN_LOCAL 2 /* Check full addresses, domain and rcpt part */
34 #define CHK_DOMAIN_REJECT 3 /* Rejects all rcpts for this domain */
35 #define CHK_DOMAIN_TEMPFAIL 4 /* Rejects all rcpts for this domain */
36 #define CHK_DOMAIN_SPAMTRAP 5 /* This domain is a spam trap */
37 
38 
39 #define CHK_RCPT_OK 0 /* Recipient OK */
40 #define CHK_RCPT_REJECT 1 /* Access denied */
41 #define CHK_RCPT_SPAMTRAP 2 /* spam Trap */
42 #define CHK_RCPT_IGNORE 3
43 #define CHK_RCPT_TEMPFAIL 4 /* Access denied */
44 #define CHK_RCPT_NET_LOCAL 5 /* Accept only if coming from the local network */
45 #define CHK_RCPT_NET_DOMAIN 6 /* Accept only if coming from the domain network */
46 #define CHK_RCPT_NET_FRIEND 7 /* Accept only if coming from some friend network */
47 #define CHK_RCPT_NET_KNOWN 8
48 #define CHK_RCPT_USER_UNKNOWN 9
49 
50 #define CHK_RCPT_UNDEF -1
51 
52 
53 static int decode_domain_check(char *);
54 static int chk_rcpt_decode(char *);
55 
56 static int check_rcpt_net_access(int, char *, char *, int);
57 
58 
59 /* ****************************************************************************
60  * *
61  * *
62  **************************************************************************** */
63 
64 /*
65 ** Domain
66 ** - No record
67 ** - YES - Check IT
68 ** - NO - Don't check IT
69 ** - Local - Also check user part
70 ** - REJECT - Reject all messages for this domain
71 **
72 ** Rcpt
73 ** - OK
74 ** - REJECT
75 ** - KNOWN
76 */
77 
78 
79 /*
80 ** Checks :
81 **
82 **
83 **
84 */
85 #define RCPT_PREFIX "RcptAccess"
86 #define DOMAIN_PREFIX "CheckRcptDomain"
87 
88 int
89 check_rcpt(email, ip, name, netclass)
90  char *email;
91  char *ip;
92  char *name;
93  int netclass;
94 {
95  char buf[256];
96  bool found = FALSE;
97  int result = RCPT_OK;
98  char *kbuf = NULL;
99  char *kemail = NULL, *kbemail = NULL;
100  size_t size = 0;
101  char *rcpt, *domain;
102  char *p;
103 
104  int access = CHK_DOMAIN_OK;
105 
106  ASSERT(email != NULL && strlen(email) > 0);
107 
108  ZE_MessageInfo(DBG_LEVEL, "Checking %s:%s", RCPT_PREFIX, email);
109 
110  /*
111  * Create a copy with rcpt/domain parts
112  */
113  if ((kemail = strdup(email)) == NULL) {
114  ZE_LogSysError("strdup(%s) error :", email);
115  goto fin;
116  }
117 
118  /*
119  * remove detail part of address
120  */
121  {
122  char *p, *q;
123  bool ok = TRUE;
124 
125  for (p = email, q = kemail; *p != '\0'; p++) {
126  if (ok && *p == '+') {
127  ok = FALSE;
128  continue;
129  }
130  if (!ok && *p == '@')
131  ok = TRUE;
132  if (!ok)
133  continue;
134  *q++ = *p;
135  }
136  *q = '\0';
137  }
138 
139  ZE_MessageInfo(DBG_LEVEL, "Checking %s:%s", RCPT_PREFIX, kemail);
140 
141  /*
142  * Create a copy with rcpt/domain parts
143  */
144  if ((kbemail = strdup(kemail)) == NULL) {
145  ZE_LogSysError("strdup(%s) error :", kemail);
146  goto fin;
147  }
148 
149  rcpt = domain = NULL;
150  p = strchr(kbemail, '@');
151  if (p != NULL) {
152  *p++ = '\0';
153  domain = p;
154  rcpt = kbemail;
155  } else {
156  rcpt = NULL;
157  domain = kbemail;
158  }
159 
160  ZE_MessageInfo(DBG_LEVEL, "KEY = %s, RCPT = %s, DOMAIN = %s",
161  kemail, STRNULL(rcpt, "(null)"), STRNULL(domain, "(null)"));
162 
163  ASSERT(domain != NULL);
164 
165  /*
166  ** Shall check recipient access for this domain ?
167  ** May return
168  ** - Not found - ACCESS OK
169  ** - NO - ACCESS OK
170  ** - YES - Check it (full address only)
171  ** - LOCAL - Check it (full address and user part only)
172  ** - REJECT - ACCESS DENIED
173  */
174  found = FALSE;
175  memset(buf, 0, sizeof (buf));
176  if (db_rcpt_check_domain(DOMAIN_PREFIX, domain, buf, sizeof (buf), 0)) {
177  ZE_MessageInfo(DBG_LEVEL, " -> Got : %s:%-15s %s\n", "CheckDomainRcpt",
178  domain, buf);
179 
180  found = TRUE;
181  /*
182  * what more ???
183  */
184  }
185 #if 0
186  if (!found) {
187  /*
188  * XXX
189  */
190  if (db_rcpt_check_domain(DOMAIN_PREFIX, "default", buf, sizeof (buf), 0)) {
191  ZE_MessageInfo(DBG_LEVEL, " -> Got : %s:%-15s %s\n", "CheckDomainRcpt",
192  "default", "buf");
193 
194  found = TRUE;
195  /*
196  * what more ???
197  */
198  }
199  }
200 #endif
201  if (found) {
202  /*
203  * decode the value found for this domain
204  */
205  access = decode_domain_check(buf);
206  switch (access) {
207  case CHK_DOMAIN_OK:
208  result = RCPT_OK;
209  goto fin;
210  break;
211  case CHK_DOMAIN_REJECT:
212  result = RCPT_ACCESS_DENIED;
213  goto fin;
214  break;
215  case CHK_DOMAIN_TEMPFAIL:
216  result = RCPT_TEMPFAIL;
217  goto fin;
218  break;
219  case CHK_DOMAIN_YES:
220  break;
221  case CHK_DOMAIN_LOCAL:
222  break;
223  case CHK_DOMAIN_SPAMTRAP:
224  result = RCPT_SPAMTRAP;
225  goto fin;
226  break;
227  default:
228  goto fin;
229  break;
230  }
231  } else
232  goto fin;
233 
234  ZE_MessageInfo(DBG_LEVEL, "CheckRcptDomain access : %d", access);
235 
236  /*
237  ** Now let's check full recipient address
238  **
239  */
240 
241  found = false;
242  memset(buf, 0, sizeof (buf));
243  /*
244  * first of all, let's check full address
245  */
246  if (db_rcpt_check_email(RCPT_PREFIX, kemail, buf, sizeof (buf))) {
247  if (strlen(buf) > 0)
248  ZE_MessageInfo(DBG_LEVEL, " -> Got : %s:%-15s %s\n", RCPT_PREFIX, kemail,
249  buf);
250 
251  found = TRUE;
252 
253  goto docheck;
254  }
255 
256  /*
257  * a key build buffer
258  */
259  size = strlen(kemail) + 16;
260  if ((kbuf = malloc(size)) == NULL) {
261  ZE_LogSysError("malloc(%ld) error :", (long int ) size);
262  goto fin;
263  }
264 
265  /*
266  ** Let's check rcpt part
267  **
268  ** This shall be done only for local domains (class W)
269  */
270 
271  if (access == CHK_DOMAIN_LOCAL) {
272  ZE_MessageInfo(DBG_LEVEL, "Checking local part : %s",
273  STRNULL(rcpt, "(null)"));
274  /*
275  * let's check only rcpt part
276  */
277  if (rcpt != NULL && strlen(rcpt) > 0) {
278  snprintf(kbuf, size, "%s@", rcpt);
279  if (db_rcpt_check_email(RCPT_PREFIX, kbuf, buf, sizeof (buf))) {
280  ZE_MessageInfo(DBG_LEVEL, " -> Got : %s:%-15s %s\n", RCPT_PREFIX, kbuf,
281  buf);
282  if (strlen(buf) > 0) {
283  found = TRUE;
284  goto docheck;
285  }
286  }
287 
288  /*
289  * remove detail part, if there is one
290  */
291  if ((p = strchr(rcpt, '+')) != NULL) {
292  *p = '\0';
293  snprintf(kbuf, size, "%s@", rcpt);
294  if (db_rcpt_check_email(RCPT_PREFIX, kbuf, buf, sizeof (buf))) {
295  ZE_MessageInfo(DBG_LEVEL, " -> Got : %s:%-15s %s\n", RCPT_PREFIX,
296  kbuf, buf);
297  if (strlen(buf) > 0) {
298  found = TRUE;
299  goto docheck;
300  }
301  }
302  }
303  }
304  }
305 
306  /*
307  * let's now check default behaviour for this domain
308  */
309  if (domain != NULL) {
310  p = domain;
311  while (p != NULL && strlen(p) > 0) {
312  snprintf(kbuf, size, "%s", p);
313  if (db_rcpt_check_domain(RCPT_PREFIX, kbuf, buf, sizeof (buf), 0)) {
314  ZE_MessageInfo(DBG_LEVEL, " -> Got : %s:%-15s %s\n", RCPT_PREFIX, kbuf,
315  buf);
316  if (strlen(buf) > 0) {
317  found = TRUE;
318  goto docheck;
319  }
320  }
321  if ((p = strchr(p, '.')) != NULL)
322  p++;
323 
324  /*
325  * don't check upper level domains
326  */
327 #if 1
328  break;
329 #endif
330  }
331  }
332 
333  result = RCPT_USER_UNKNOWN;
334 
335  goto docheck;
336 
337 docheck:
338  /*
339  ** Restrict recipients :
340  ** - All domains
341  ** - Some domains
342  ** - Local domains
343  */
344 
345  /*
346  ** Check if
347  ** - rcpt exists
348  ** - rcpt accepts messages or is a service only rcpt
349  ** - rcpt mailbox access is restricted to some domains
350  */
351  /*
352  ** #define RCPT_OK 0
353  ** #define RCPT_ACCESS_DENIED 1
354  ** #define RCPT_ACCESS_LOCAL_ONLY 2
355  ** #define RCPT_USER_UNKNOWN 3
356  */
357 
358  if (!found) {
359  result = RCPT_USER_UNKNOWN;
360  goto fin;
361  }
362 
363  result = chk_rcpt_decode(buf);
364  switch (result) {
365  case CHK_RCPT_OK:
366  result = RCPT_OK;
367  break;
368  case CHK_RCPT_NET_LOCAL:
369  case CHK_RCPT_NET_DOMAIN:
370  case CHK_RCPT_NET_FRIEND:
371  case CHK_RCPT_NET_KNOWN:
372  result = check_rcpt_net_access(result, ip, name, netclass);
373  break;
374  case CHK_RCPT_REJECT:
375  result = RCPT_ACCESS_DENIED;
376  break;
377  case CHK_RCPT_TEMPFAIL:
378  result = RCPT_TEMPFAIL;
379  break;
380  case CHK_RCPT_SPAMTRAP:
381  result = RCPT_SPAMTRAP;
382  break;
383  case CHK_RCPT_IGNORE:
384  result = RCPT_IGNORE;
385  break;
387  result = RCPT_USER_UNKNOWN;
388  break;
389  default:
390  ZE_MessageNotice(9, "Error ...");
391  result = RCPT_USER_UNKNOWN;
392  break;
393  }
394 
395 fin:
396  FREE(kemail);
397  FREE(kbemail);
398  FREE(kbuf);
399 
400  return result;
401 }
402 
403 
404 
405 /* ****************************************************************************
406  * *
407  * *
408  **************************************************************************** */
409 static int
410 decode_domain_check(s)
411  char *s;
412 {
413  if (s == NULL)
414  return CHK_DOMAIN_OK;
415 
416  if (strcasecmp(s, "NO") == 0)
417  return CHK_DOMAIN_OK;
418 
419  if (strcasecmp(s, "YES") == 0)
420  return CHK_DOMAIN_YES;
421 
422  if (strcasecmp(s, "REJECT") == 0)
423  return CHK_DOMAIN_REJECT;
424 
425  if (strcasecmp(s, "TEMPFAIL") == 0)
426  return CHK_DOMAIN_TEMPFAIL;
427 
428  if (strcasecmp(s, "SPAMTRAP") == 0)
429  return CHK_DOMAIN_SPAMTRAP;
430 
431  if (strcasecmp(s, "LOCAL") == 0)
432  return CHK_DOMAIN_LOCAL;
433 
434  return CHK_DOMAIN_OK;
435 }
436 
437 
438 
439 /* ****************************************************************************
440  * *
441  * *
442  **************************************************************************** */
443 int
444 chk_rcpt_decode(code)
445  char *code;
446 {
447  if (code == NULL || strlen(code) == 0)
448  return CHK_RCPT_UNDEF;
449 
450  if (strcasecmp(code, "OK") == 0)
451  return RCPT_OK;
452 
453  if (strcasecmp(code, "REJECT") == 0)
454  return CHK_RCPT_REJECT;
455 
456  if (strcasecmp(code, "TEMPFAIL") == 0)
457  return CHK_RCPT_TEMPFAIL;
458 
459  if (strcasecmp(code, "SPAMTRAP") == 0)
460  return CHK_RCPT_SPAMTRAP;
461 
462  if (strcasecmp(code, "IGNORE") == 0)
463  return CHK_RCPT_IGNORE;
464 
465  if (strcasecmp(code, "LOCAL-NET") == 0)
466  return CHK_RCPT_NET_LOCAL;
467 
468  if (strcasecmp(code, "DOMAIN-NET") == 0)
469  return CHK_RCPT_NET_DOMAIN;
470 
471  if (strcasecmp(code, "FRIEND-NET") == 0)
472  return CHK_RCPT_NET_FRIEND;
473 
474  if (strcasecmp(code, "KNOWN-NET") == 0)
475  return CHK_RCPT_NET_KNOWN;
476 
477  if (strcasecmp(code, "USER-UNKNOWN") == 0)
478  return CHK_RCPT_USER_UNKNOWN;
479 
480  return CHK_RCPT_UNDEF;
481 }
482 
483 /* ****************************************************************************
484  * *
485  * *
486  **************************************************************************** */
487 #define CHECK_NET_CLASS(check, netclass) \
488  if (netclass != NET_UNKNOWN) { \
489  bool classok = FALSE; \
490  \
491  classok = IS_LOCAL(netclass) || IS_AUTH(netclass); \
492  if (classok && check == CHK_RCPT_NET_LOCAL) \
493  return RCPT_OK; \
494  \
495  classok = classok || IS_DOMAIN(netclass); \
496  if (classok && check == CHK_RCPT_NET_DOMAIN) \
497  return RCPT_OK; \
498  \
499  classok = classok || IS_FRIEND(netclass); \
500  if (classok && check == CHK_RCPT_NET_FRIEND) \
501  return RCPT_OK; \
502  \
503  classok = classok || IS_KNOWN(netclass); \
504  if (classok && check == CHK_RCPT_NET_KNOWN) \
505  return RCPT_OK; \
506  }
507 
508 static int
509 check_rcpt_net_access(check, ip, name, netclass)
510  int check;
511  char *ip;
512  char *name;
513  int netclass;
514 {
515  CHECK_NET_CLASS(check, netclass);
516 
517  netclass = GetClientNetClass(ip, name, NULL, NULL, 0);
518 
519  CHECK_NET_CLASS(check, netclass);
520 
521  return RCPT_BAD_NETWORK;
522 }
523 
524 /* ****************************************************************************
525  * *
526  * *
527  **************************************************************************** */
528 
529 char *
531  int code;
532 {
533  static name2id_T names[] = {
534  {"OK", RCPT_OK},
535  {"REJECT", RCPT_REJECT},
536  {"TEMPFAIL", RCPT_TEMPFAIL},
537  {"Access Denied", RCPT_ACCESS_DENIED},
538  {"Bad Network", RCPT_BAD_NETWORK},
539  {"User Unknown", RCPT_USER_UNKNOWN},
540  {"SpamTrap", RCPT_SPAMTRAP},
541  {NULL, -1}
542  };
543 
544  return get_name_by_id(names, code);
545 }
546 
547 /* ****************************************************************************
548  * *
549  * *
550  **************************************************************************** */
551 bool
553 {
554  return db_rcpt_open(TRUE);
555 }
556 
557 /* ****************************************************************************
558  * *
559  * *
560  **************************************************************************** */
561 bool
563 {
564  return db_rcpt_close();
565 }
566 
567 /* ****************************************************************************
568  * *
569  * *
570  **************************************************************************** */
571 bool
573 {
574  return db_rcpt_reopen();
575 }
#define RCPT_REJECT
Definition: ze-rcpt.h:39
#define CHK_RCPT_OK
Definition: ze-rcpt.c:39
#define CHK_RCPT_TEMPFAIL
Definition: ze-rcpt.c:43
#define CHK_DOMAIN_YES
Definition: ze-rcpt.c:32
#define ASSERT(a)
Definition: macros.h:27
#define CHK_RCPT_NET_DOMAIN
Definition: ze-rcpt.c:45
#define CHK_DOMAIN_TEMPFAIL
Definition: ze-rcpt.c:35
#define FREE(x)
Definition: macros.h:37
#define DOMAIN_PREFIX
Definition: ze-rcpt.c:86
bool rcpt_close()
Definition: ze-rcpt.c:562
#define STRNULL(x, r)
Definition: macros.h:81
#define CHK_RCPT_REJECT
Definition: ze-rcpt.c:40
bool ok
Definition: ze-connopen.c:59
bool rcpt_reopen()
Definition: ze-rcpt.c:572
int GetClientNetClass(char *ip, char *name, netclass_T *class, char *label, size_t sz)
Definition: ze-netclass.c:49
#define FALSE
Definition: macros.h:160
bool db_rcpt_close()
Definition: ze-dbrcpt.c:101
#define CHK_RCPT_NET_LOCAL
Definition: ze-rcpt.c:44
#define CHK_DOMAIN_SPAMTRAP
Definition: ze-rcpt.c:36
#define RCPT_PREFIX
Definition: ze-rcpt.c:85
#define strchr
Definition: ze-sys.h:218
#define CHK_RCPT_IGNORE
Definition: ze-rcpt.c:42
#define CHECK_NET_CLASS(check, netclass)
Definition: ze-rcpt.c:487
#define RCPT_SPAMTRAP
Definition: ze-rcpt.h:44
#define RCPT_USER_UNKNOWN
Definition: ze-rcpt.h:43
#define RCPT_TEMPFAIL
Definition: ze-rcpt.h:40
#define ZE_MessageNotice(level,...)
Definition: zeSyslog.h:91
#define RCPT_ACCESS_DENIED
Definition: ze-rcpt.h:41
char * get_name_by_id(name2id_T *, int)
Definition: ze-name2id.c:58
#define RCPT_OK
Definition: ze-rcpt.h:38
#define DBG_LEVEL
Definition: ze-rcpt.c:29
#define ZE_MessageInfo(level,...)
Definition: zeSyslog.h:90
#define CHK_RCPT_USER_UNKNOWN
Definition: ze-rcpt.c:48
#define TRUE
Definition: macros.h:157
#define RCPT_IGNORE
Definition: ze-rcpt.h:45
bool db_rcpt_check_domain(char *prefix, char *key, char *bufout, size_t szbuf, uint32_t flags)
Definition: ze-dbrcpt.c:298
#define ZE_LogSysError(...)
Definition: zeSyslog.h:129
bool rcpt_init()
Definition: ze-rcpt.c:552
bool db_rcpt_open(bool)
Definition: ze-dbrcpt.c:47
int check_rcpt(char *email, char *ip, char *name, int netclass)
Definition: ze-rcpt.c:89
#define CHK_DOMAIN_OK
Definition: ze-rcpt.c:31
bool db_rcpt_check_email(char *prefix, char *key, char *bufout, size_t szbuf)
Definition: ze-dbrcpt.c:139
bool db_rcpt_reopen()
Definition: ze-dbrcpt.c:82
#define RCPT_BAD_NETWORK
Definition: ze-rcpt.h:42
char * rcpt_code_string(int code)
Definition: ze-rcpt.c:530
#define CHK_RCPT_NET_KNOWN
Definition: ze-rcpt.c:47
#define CHK_RCPT_UNDEF
Definition: ze-rcpt.c:50
#define CHK_DOMAIN_REJECT
Definition: ze-rcpt.c:34
#define CHK_RCPT_SPAMTRAP
Definition: ze-rcpt.c:41
char domain[]
#define CHK_DOMAIN_LOCAL
Definition: ze-rcpt.c:33
#define CHK_RCPT_NET_FRIEND
Definition: ze-rcpt.c:46