1 #include "common.h"
2 #include "smtpd.h"
3 #include <ip.h>
4
5 enum {
6 NORELAY = 0,
7 DNSVERIFY,
8 SAVEBLOCK,
9 DOMNAME,
10 OURNETS,
11 OURDOMS,
12
13 IP = 0,
14 STRING,
15 };
16
17
18 typedef struct Keyword Keyword;
19
20 struct Keyword {
21 char *name;
22 int code;
23 };
24
25 static Keyword options[] = {
26 "norelay", NORELAY,
27 "verifysenderdom", DNSVERIFY,
28 "saveblockedmsg", SAVEBLOCK,
29 "defaultdomain", DOMNAME,
30 "ournets", OURNETS,
31 "ourdomains", OURDOMS,
32 0, NONE,
33 };
34
35 static Keyword actions[] = {
36 "allow", ACCEPT,
37 "block", BLOCKED,
38 "deny", DENIED,
39 "dial", DIALUP,
40 "delay", DELAY,
41 0, NONE,
42 };
43
44 static int hisaction;
45 static List ourdoms;
46 static List badguys;
47 static ulong v4peerip;
48
49 static char* getline(Biobuf*);
50 static int cidrcheck(char*);
51
52 static int
findkey(char * val,Keyword * p)53 findkey(char *val, Keyword *p)
54 {
55
56 for(; p->name; p++)
57 if(strcmp(val, p->name) == 0)
58 break;
59 return p->code;
60 }
61
62 char*
actstr(int a)63 actstr(int a)
64 {
65 static char buf[32];
66 Keyword *p;
67
68 for(p=actions; p->name; p++)
69 if(p->code == a)
70 return p->name;
71 if(a==NONE)
72 return "none";
73 sprint(buf, "%d", a);
74 return buf;
75 }
76
77 int
getaction(char * s,char * type)78 getaction(char *s, char *type)
79 {
80 char buf[1024];
81 Keyword *k;
82
83 if(s == nil || *s == 0)
84 return ACCEPT;
85
86 for(k = actions; k->name != 0; k++){
87 snprint(buf, sizeof buf, "/mail/ratify/%s/%s/%s", k->name, type, s);
88 if(access(buf,0) >= 0)
89 return k->code;
90 }
91 return ACCEPT;
92 }
93
94 int
istrusted(char * s)95 istrusted(char *s)
96 {
97 char buf[1024];
98
99 if(s == nil || *s == 0)
100 return 0;
101
102 snprint(buf, sizeof buf, "/mail/ratify/trusted/%s", s);
103 return access(buf,0) >= 0;
104 }
105
106 void
getconf(void)107 getconf(void)
108 {
109 Biobuf *bp;
110 char *cp, *p;
111 String *s;
112 char buf[512];
113 uchar addr[4];
114
115 v4parseip(addr, nci->rsys);
116 v4peerip = nhgetl(addr);
117
118 trusted = istrusted(nci->rsys);
119 hisaction = getaction(nci->rsys, "ip");
120 if(debug){
121 fprint(2, "istrusted(%s)=%d\n", nci->rsys, trusted);
122 fprint(2, "getaction(%s, ip)=%s\n", nci->rsys, actstr(hisaction));
123 }
124 snprint(buf, sizeof(buf), "%s/smtpd.conf", UPASLIB);
125 bp = sysopen(buf, "r", 0);
126 if(bp == 0)
127 return;
128
129 for(;;){
130 cp = getline(bp);
131 if(cp == 0)
132 break;
133 p = cp+strlen(cp)+1;
134 switch(findkey(cp, options)){
135 case NORELAY:
136 if(fflag == 0 && strcmp(p, "on") == 0)
137 fflag++;
138 break;
139 case DNSVERIFY:
140 if(rflag == 0 && strcmp(p, "on") == 0)
141 rflag++;
142 break;
143 case SAVEBLOCK:
144 if(sflag == 0 && strcmp(p, "on") == 0)
145 sflag++;
146 break;
147 case DOMNAME:
148 if(dom == 0)
149 dom = strdup(p);
150 break;
151 case OURNETS:
152 if (trusted == 0)
153 trusted = cidrcheck(p);
154 break;
155 case OURDOMS:
156 while(*p){
157 s = s_new();
158 s_append(s, p);
159 listadd(&ourdoms, s);
160 p += strlen(p)+1;
161 }
162 break;
163 default:
164 break;
165 }
166 }
167 sysclose(bp);
168 }
169
170 /*
171 * match a user name. the only meta-char is '*' which matches all
172 * characters. we only allow it as "*", which matches anything or
173 * an * at the end of the name (e.g., "username*") which matches
174 * trailing characters.
175 */
176 static int
usermatch(char * pathuser,char * specuser)177 usermatch(char *pathuser, char *specuser)
178 {
179 int n;
180
181 n = strlen(specuser)-1;
182 if(specuser[n] == '*'){
183 if(n == 0) /* match everything */
184 return 0;
185 return strncmp(pathuser, specuser, n);
186 }
187 return strcmp(pathuser, specuser);
188 }
189
190 static int
dommatch(char * pathdom,char * specdom)191 dommatch(char *pathdom, char *specdom)
192 {
193 int n;
194
195 if (*specdom == '*'){
196 if (specdom[1] == '.' && specdom[2]){
197 specdom += 2;
198 n = strlen(pathdom)-strlen(specdom);
199 if(n == 0 || (n > 0 && pathdom[n-1] == '.'))
200 return strcmp(pathdom+n, specdom);
201 return n;
202 }
203 }
204 return strcmp(pathdom, specdom);
205 }
206
207 /*
208 * figure out action for this sender
209 */
210 int
blocked(String * path)211 blocked(String *path)
212 {
213 String *lpath;
214 int action;
215
216 if(debug)
217 fprint(2, "blocked(%s)\n", s_to_c(path));
218
219 /* if the sender's IP address is blessed, ignore sender email address */
220 if(trusted){
221 if(debug)
222 fprint(2, "\ttrusted => trusted\n");
223 return TRUSTED;
224 }
225
226 /* if sender's IP address is blocked, ignore sender email address */
227 if(hisaction != ACCEPT){
228 if(debug)
229 fprint(2, "\thisaction=%s => %s\n", actstr(hisaction), actstr(hisaction));
230 return hisaction;
231 }
232
233 /* convert to lower case */
234 lpath = s_copy(s_to_c(path));
235 s_tolower(lpath);
236
237 /* classify */
238 action = getaction(s_to_c(lpath), "account");
239 if(debug)
240 fprint(2, "\tgetaction account %s => %s\n", s_to_c(lpath), actstr(action));
241 s_free(lpath);
242 return action;
243 }
244
245 /*
246 * get a canonicalized line: a string of null-terminated lower-case
247 * tokens with a two null bytes at the end.
248 */
249 static char*
getline(Biobuf * bp)250 getline(Biobuf *bp)
251 {
252 char c, *cp, *p, *q;
253 int n;
254
255 static char *buf;
256 static int bufsize;
257
258 for(;;){
259 cp = Brdline(bp, '\n');
260 if(cp == 0)
261 return 0;
262 n = Blinelen(bp);
263 cp[n-1] = 0;
264 if(buf == 0 || bufsize < n+1){
265 bufsize += 512;
266 if(bufsize < n+1)
267 bufsize = n+1;
268 buf = realloc(buf, bufsize);
269 if(buf == 0)
270 break;
271 }
272 q = buf;
273 for (p = cp; *p; p++){
274 c = *p;
275 if(c == '\\' && p[1]) /* we don't allow \<newline> */
276 c = *++p;
277 else
278 if(c == '#')
279 break;
280 else
281 if(c == ' ' || c == '\t' || c == ',')
282 if(q == buf || q[-1] == 0)
283 continue;
284 else
285 c = 0;
286 *q++ = tolower(c);
287 }
288 if(q != buf){
289 if(q[-1])
290 *q++ = 0;
291 *q = 0;
292 break;
293 }
294 }
295 return buf;
296 }
297
298 static int
isourdom(char * s)299 isourdom(char *s)
300 {
301 Link *l;
302
303 if(strchr(s, '.') == nil)
304 return 1;
305
306 for(l = ourdoms.first; l; l = l->next){
307 if(dommatch(s, s_to_c(l->p)) == 0)
308 return 1;
309 }
310 return 0;
311 }
312
313 int
forwarding(String * path)314 forwarding(String *path)
315 {
316 char *cp, *s;
317 String *lpath;
318
319 if(debug)
320 fprint(2, "forwarding(%s)\n", s_to_c(path));
321
322 /* first check if they want loopback */
323 lpath = s_copy(s_to_c(s_restart(path)));
324 if(nci->rsys && *nci->rsys){
325 cp = s_to_c(lpath);
326 if(strncmp(cp, "[]!", 3) == 0){
327 found:
328 s_append(path, "[");
329 s_append(path, nci->rsys);
330 s_append(path, "]!");
331 s_append(path, cp+3);
332 s_terminate(path);
333 s_free(lpath);
334 return 0;
335 }
336 cp = strchr(cp,'!'); /* skip our domain and check next */
337 if(cp++ && strncmp(cp, "[]!", 3) == 0)
338 goto found;
339 }
340
341 /* if mail is from a trusted IP addr, allow it to forward */
342 if(trusted) {
343 s_free(lpath);
344 return 0;
345 }
346
347 /* sender is untrusted; ensure receiver is in one of our domains */
348 for(cp = s_to_c(lpath); *cp; cp++) /* convert receiver lc */
349 *cp = tolower(*cp);
350
351 for(s = s_to_c(lpath); cp = strchr(s, '!'); s = cp+1){
352 *cp = 0;
353 if(!isourdom(s)){
354 s_free(lpath);
355 return 1;
356 }
357 }
358 s_free(lpath);
359 return 0;
360 }
361
362 int
masquerade(String * path,char * him)363 masquerade(String *path, char *him)
364 {
365 char *cp, *s;
366 String *lpath;
367 int rv = 0;
368
369 if(debug)
370 fprint(2, "masquerade(%s) ", s_to_c(path));
371
372 if(trusted || path == nil) {
373 if(debug)
374 fprint(2, "0\n");
375 return 0;
376 }
377
378 lpath = s_copy(s_to_c(path));
379
380 /* sender is untrusted; ensure receiver is in one of our domains */
381 for(cp = s_to_c(lpath); *cp; cp++) /* convert receiver lc */
382 *cp = tolower(*cp);
383 s = s_to_c(lpath);
384
385 /* scan first element of ! or last element of @ paths */
386 if((cp = strchr(s, '!')) != nil){
387 *cp = 0;
388 if(isourdom(s))
389 rv = 1;
390 } else if((cp = strrchr(s, '@')) != nil){
391 if(isourdom(cp+1))
392 rv = 1;
393 } else {
394 if(isourdom(him))
395 rv = 1;
396 }
397
398 s_free(lpath);
399 if (debug)
400 fprint(2, "%d\n", rv);
401 return rv;
402 }
403
404 /* this is a v4 only check */
405 static int
cidrcheck(char * cp)406 cidrcheck(char *cp)
407 {
408 char *p;
409 ulong a, m;
410 uchar addr[IPv4addrlen];
411 uchar mask[IPv4addrlen];
412
413 if(v4peerip == 0)
414 return 0;
415
416 /* parse a list of CIDR addresses comparing each to the peer IP addr */
417 while(cp && *cp){
418 v4parsecidr(addr, mask, cp);
419 a = nhgetl(addr);
420 m = nhgetl(mask);
421 /*
422 * if a mask isn't specified, we build a minimal mask
423 * instead of using the default mask for that net. in this
424 * case we never allow a class A mask (0xff000000).
425 */
426 if(strchr(cp, '/') == 0){
427 m = 0xff000000;
428 p = cp;
429 for(p = strchr(p, '.'); p && p[1]; p = strchr(p+1, '.'))
430 m = (m>>8)|0xff000000;
431
432 /* force at least a class B */
433 m |= 0xffff0000;
434 }
435 if((v4peerip&m) == a)
436 return 1;
437 cp += strlen(cp)+1;
438 }
439 return 0;
440 }
441
442 int
isbadguy(void)443 isbadguy(void)
444 {
445 Link *l;
446
447 /* check if this IP address is banned */
448 for(l = badguys.first; l; l = l->next)
449 if(cidrcheck(s_to_c(l->p)))
450 return 1;
451
452 return 0;
453 }
454
455 void
addbadguy(char * p)456 addbadguy(char *p)
457 {
458 listadd(&badguys, s_copy(p));
459 };
460
461 char*
dumpfile(char * sender)462 dumpfile(char *sender)
463 {
464 int i, fd;
465 ulong h;
466 static char buf[512];
467 char *cp;
468
469 if (sflag == 1){
470 cp = ctime(time(0));
471 cp[7] = 0;
472 if(cp[8] == ' ')
473 sprint(buf, "%s/queue.dump/%s%c", SPOOL, cp+4, cp[9]);
474 else
475 sprint(buf, "%s/queue.dump/%s%c%c", SPOOL, cp+4, cp[8], cp[9]);
476 cp = buf+strlen(buf);
477 if(access(buf, 0) < 0 && sysmkdir(buf, 0777) < 0)
478 return "/dev/null";
479 h = 0;
480 while(*sender)
481 h = h*257 + *sender++;
482 for(i = 0; i < 50; i++){
483 h += lrand();
484 sprint(cp, "/%lud", h);
485 if(access(buf, 0) >= 0)
486 continue;
487 fd = syscreate(buf, ORDWR, 0666);
488 if(fd >= 0){
489 if(debug)
490 fprint(2, "saving in %s\n", buf);
491 close(fd);
492 return buf;
493 }
494 }
495 }
496 return "/dev/null";
497 }
498
499 char *validator = "/mail/lib/validateaddress";
500
501 int
recipok(char * user)502 recipok(char *user)
503 {
504 char *cp, *p, c;
505 char buf[512];
506 int n;
507 Biobuf *bp;
508 int pid;
509 Waitmsg *w;
510
511 if(shellchars(user)){
512 syslog(0, "smtpd", "shellchars in user name");
513 return 0;
514 }
515
516 if(access(validator, AEXEC) == 0)
517 switch(pid = fork()) {
518 case -1:
519 break;
520 case 0:
521 execl(validator, "validateaddress", user, nil);
522 exits(0);
523 default:
524 while(w = wait()) {
525 if(w->pid != pid)
526 continue;
527 if(w->msg[0] != 0){
528 /*
529 syslog(0, "smtpd", "validateaddress %s: %s", user, w->msg);
530 */
531 return 0;
532 }
533 break;
534 }
535 }
536
537 snprint(buf, sizeof(buf), "%s/names.blocked", UPASLIB);
538 bp = sysopen(buf, "r", 0);
539 if(bp == 0)
540 return 1;
541 for(;;){
542 cp = Brdline(bp, '\n');
543 if(cp == 0)
544 break;
545 n = Blinelen(bp);
546 cp[n-1] = 0;
547
548 while(*cp == ' ' || *cp == '\t')
549 cp++;
550 for(p = cp; c = *p; p++){
551 if(c == '#')
552 break;
553 if(c == ' ' || c == '\t')
554 break;
555 }
556 if(p > cp){
557 *p = 0;
558 if(cistrcmp(user, cp) == 0){
559 syslog(0, "smtpd", "names.blocked blocks %s", user);
560 Bterm(bp);
561 return 0;
562 }
563 }
564 }
565 Bterm(bp);
566 return 1;
567 }
568
569 /*
570 * a user can opt out of spam filtering by creating
571 * a file in his mail directory named 'nospamfiltering'.
572 */
573 int
optoutofspamfilter(char * addr)574 optoutofspamfilter(char *addr)
575 {
576 char *p, *f;
577 int rv;
578
579 p = strchr(addr, '!');
580 if(p)
581 p++;
582 else
583 p = addr;
584
585
586 rv = 0;
587 f = smprint("/mail/box/%s/nospamfiltering", p);
588 if(f != nil){
589 rv = access(f, 0)==0;
590 free(f);
591 }
592
593 return rv;
594 }
595