Blob


1 #include "common.h"
2 #include "smtpd.h"
3 #include <ip.h>
5 enum {
6 NORELAY = 0,
7 DNSVERIFY,
8 SAVEBLOCK,
9 DOMNAME,
10 OURNETS,
11 OURDOMS,
13 IP = 0,
14 STRING,
15 };
18 typedef struct Keyword Keyword;
20 struct Keyword {
21 char *name;
22 int code;
23 };
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 };
35 static Keyword actions[] = {
36 "allow", ACCEPT,
37 "block", BLOCKED,
38 "deny", DENIED,
39 "dial", DIALUP,
40 "delay", DELAY,
41 0, NONE,
42 };
44 static int hisaction;
45 static List ourdoms;
46 static List badguys;
47 static ulong v4peerip;
49 static char* getline(Biobuf*);
50 static int cidrcheck(char*);
52 static int
53 findkey(char *val, Keyword *p)
54 {
56 for(; p->name; p++)
57 if(strcmp(val, p->name) == 0)
58 break;
59 return p->code;
60 }
62 char*
63 actstr(int a)
64 {
65 static char buf[32];
66 Keyword *p;
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 }
77 int
78 getaction(char *s, char *type)
79 {
80 char buf[1024];
81 Keyword *k;
83 if(s == nil || *s == 0)
84 return ACCEPT;
86 for(k = actions; k->name != 0; k++){
87 snprint(buf, sizeof buf, "%s/mail/ratify/%s/%s/%s",
88 get9root(), k->name, type, s);
89 if(access(buf,0) >= 0)
90 return k->code;
91 }
92 return ACCEPT;
93 }
95 int
96 istrusted(char *s)
97 {
98 char buf[1024];
100 if(s == nil || *s == 0)
101 return 0;
103 snprint(buf, sizeof buf, "%s/mail/ratify/trusted/%s", get9root(), s);
104 return access(buf,0) >= 0;
107 void
108 getconf(void)
110 Biobuf *bp;
111 char *cp, *p;
112 String *s;
113 char buf[512];
114 uchar addr[4];
116 v4parseip(addr, nci->rsys);
117 v4peerip = nhgetl(addr);
119 trusted = istrusted(nci->rsys);
120 hisaction = getaction(nci->rsys, "ip");
121 if(debug){
122 fprint(2, "istrusted(%s)=%d\n", nci->rsys, trusted);
123 fprint(2, "getaction(%s, ip)=%s\n", nci->rsys, actstr(hisaction));
125 snprint(buf, sizeof(buf), "%s/smtpd.conf", UPASLIB);
126 bp = sysopen(buf, "r", 0);
127 if(bp == 0)
128 return;
130 for(;;){
131 cp = getline(bp);
132 if(cp == 0)
133 break;
134 p = cp+strlen(cp)+1;
135 switch(findkey(cp, options)){
136 case NORELAY:
137 if(fflag == 0 && strcmp(p, "on") == 0)
138 fflag++;
139 break;
140 case DNSVERIFY:
141 if(rflag == 0 && strcmp(p, "on") == 0)
142 rflag++;
143 break;
144 case SAVEBLOCK:
145 if(sflag == 0 && strcmp(p, "on") == 0)
146 sflag++;
147 break;
148 case DOMNAME:
149 if(dom == 0)
150 dom = strdup(p);
151 break;
152 case OURNETS:
153 if (trusted == 0)
154 trusted = cidrcheck(p);
155 break;
156 case OURDOMS:
157 while(*p){
158 s = s_new();
159 s_append(s, p);
160 listadd(&ourdoms, s);
161 p += strlen(p)+1;
163 break;
164 default:
165 break;
168 sysclose(bp);
171 #if 0
172 /*
173 * match a user name. the only meta-char is '*' which matches all
174 * characters. we only allow it as "*", which matches anything or
175 * an * at the end of the name (e.g., "username*") which matches
176 * trailing characters.
177 */
178 static int
179 usermatch(char *pathuser, char *specuser)
181 int n;
183 n = strlen(specuser)-1;
184 if(specuser[n] == '*'){
185 if(n == 0) /* match everything */
186 return 0;
187 return strncmp(pathuser, specuser, n);
189 return strcmp(pathuser, specuser);
191 #endif
193 static int
194 dommatch(char *pathdom, char *specdom)
196 int n;
198 if (*specdom == '*'){
199 if (specdom[1] == '.' && specdom[2]){
200 specdom += 2;
201 n = strlen(pathdom)-strlen(specdom);
202 if(n == 0 || (n > 0 && pathdom[n-1] == '.'))
203 return strcmp(pathdom+n, specdom);
204 return n;
207 return strcmp(pathdom, specdom);
210 /*
211 * figure out action for this sender
212 */
213 int
214 blocked(String *path)
216 String *lpath;
217 int action;
219 if(debug)
220 fprint(2, "blocked(%s)\n", s_to_c(path));
222 /* if the sender's IP address is blessed, ignore sender email address */
223 if(trusted){
224 if(debug)
225 fprint(2, "\ttrusted => trusted\n");
226 return TRUSTED;
229 /* if sender's IP address is blocked, ignore sender email address */
230 if(hisaction != ACCEPT){
231 if(debug)
232 fprint(2, "\thisaction=%s => %s\n", actstr(hisaction), actstr(hisaction));
233 return hisaction;
236 /* convert to lower case */
237 lpath = s_copy(s_to_c(path));
238 s_tolower(lpath);
240 /* classify */
241 action = getaction(s_to_c(lpath), "account");
242 if(debug)
243 fprint(2, "\tgetaction account %s => %s\n", s_to_c(lpath), actstr(action));
244 s_free(lpath);
245 return action;
248 /*
249 * get a canonicalized line: a string of null-terminated lower-case
250 * tokens with a two null bytes at the end.
251 */
252 static char*
253 getline(Biobuf *bp)
255 char c, *cp, *p, *q;
256 int n;
258 static char *buf;
259 static int bufsize;
261 for(;;){
262 cp = Brdline(bp, '\n');
263 if(cp == 0)
264 return 0;
265 n = Blinelen(bp);
266 cp[n-1] = 0;
267 if(buf == 0 || bufsize < n+1){
268 bufsize += 512;
269 if(bufsize < n+1)
270 bufsize = n+1;
271 buf = realloc(buf, bufsize);
272 if(buf == 0)
273 break;
275 q = buf;
276 for (p = cp; *p; p++){
277 c = *p;
278 if(c == '\\' && p[1]) /* we don't allow \<newline> */
279 c = *++p;
280 else
281 if(c == '#')
282 break;
283 else
284 if(c == ' ' || c == '\t' || c == ',')
285 if(q == buf || q[-1] == 0)
286 continue;
287 else
288 c = 0;
289 *q++ = tolower(c);
291 if(q != buf){
292 if(q[-1])
293 *q++ = 0;
294 *q = 0;
295 break;
298 return buf;
301 static int
302 isourdom(char *s)
304 Link *l;
306 if(strchr(s, '.') == nil)
307 return 1;
309 for(l = ourdoms.first; l; l = l->next){
310 if(dommatch(s, s_to_c(l->p)) == 0)
311 return 1;
313 return 0;
316 int
317 forwarding(String *path)
319 char *cp, *s;
320 String *lpath;
322 if(debug)
323 fprint(2, "forwarding(%s)\n", s_to_c(path));
325 /* first check if they want loopback */
326 lpath = s_copy(s_to_c(s_restart(path)));
327 if(nci->rsys && *nci->rsys){
328 cp = s_to_c(lpath);
329 if(strncmp(cp, "[]!", 3) == 0){
330 found:
331 s_append(path, "[");
332 s_append(path, nci->rsys);
333 s_append(path, "]!");
334 s_append(path, cp+3);
335 s_terminate(path);
336 s_free(lpath);
337 return 0;
339 cp = strchr(cp,'!'); /* skip our domain and check next */
340 if(cp++ && strncmp(cp, "[]!", 3) == 0)
341 goto found;
344 /* if mail is from a trusted IP addr, allow it to forward */
345 if(trusted) {
346 s_free(lpath);
347 return 0;
350 /* sender is untrusted; ensure receiver is in one of our domains */
351 for(cp = s_to_c(lpath); *cp; cp++) /* convert receiver lc */
352 *cp = tolower(*cp);
354 for(s = s_to_c(lpath); cp = strchr(s, '!'); s = cp+1){
355 *cp = 0;
356 if(!isourdom(s)){
357 s_free(lpath);
358 return 1;
361 s_free(lpath);
362 return 0;
365 int
366 masquerade(String *path, char *him)
368 char *cp, *s;
369 String *lpath;
370 int rv = 0;
372 if(debug)
373 fprint(2, "masquerade(%s)\n", s_to_c(path));
375 if(trusted)
376 return 0;
377 if(path == nil)
378 return 0;
380 lpath = s_copy(s_to_c(path));
382 /* sender is untrusted; ensure receiver is in one of our domains */
383 for(cp = s_to_c(lpath); *cp; cp++) /* convert receiver lc */
384 *cp = tolower(*cp);
385 s = s_to_c(lpath);
387 /* scan first element of ! or last element of @ paths */
388 if((cp = strchr(s, '!')) != nil){
389 *cp = 0;
390 if(isourdom(s))
391 rv = 1;
392 } else if((cp = strrchr(s, '@')) != nil){
393 if(isourdom(cp+1))
394 rv = 1;
395 } else {
396 if(isourdom(him))
397 rv = 1;
400 s_free(lpath);
401 return rv;
404 /* this is a v4 only check */
405 static int
406 cidrcheck(char *cp)
408 char *p;
409 ulong a, m;
410 uchar addr[IPv4addrlen];
411 uchar mask[IPv4addrlen];
413 if(v4peerip == 0)
414 return 0;
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;
432 /* force at least a class B */
433 m |= 0xffff0000;
435 if((v4peerip&m) == a)
436 return 1;
437 cp += strlen(cp)+1;
439 return 0;
442 int
443 isbadguy(void)
445 Link *l;
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;
452 return 0;
455 void
456 addbadguy(char *p)
458 listadd(&badguys, s_copy(p));
459 };
461 char*
462 dumpfile(char *sender)
464 int i, fd;
465 ulong h;
466 static char buf[512];
467 char *cp;
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;
496 return "/dev/null";
499 char *validator = "#9/mail/lib/validateaddress";
501 int
502 recipok(char *user)
504 char *cp, *p, c;
505 char buf[512];
506 int n;
507 Biobuf *bp;
508 int pid;
509 Waitmsg *w;
510 static int beenhere;
512 if(!beenhere){
513 beenhere++;
514 validator = unsharp(validator);
516 if(shellchars(user)){
517 syslog(0, "smtpd", "shellchars in user name");
518 return 0;
521 if(access(validator, AEXEC) == 0)
522 switch(pid = fork()) {
523 case -1:
524 break;
525 case 0:
526 execl(validator, "validateaddress", user, nil);
527 exits(0);
528 default:
529 while(w = wait()) {
530 if(w->pid != pid)
531 continue;
532 if(w->msg[0] != 0){
533 syslog(0, "smtpd", "validateaddress %s: %s", user, w->msg);
534 return 0;
536 break;
540 snprint(buf, sizeof(buf), "%s/names.blocked", UPASLIB);
541 bp = sysopen(buf, "r", 0);
542 if(bp == 0)
543 return 1;
544 for(;;){
545 cp = Brdline(bp, '\n');
546 if(cp == 0)
547 break;
548 n = Blinelen(bp);
549 cp[n-1] = 0;
551 while(*cp == ' ' || *cp == '\t')
552 cp++;
553 for(p = cp; c = *p; p++){
554 if(c == '#')
555 break;
556 if(c == ' ' || c == '\t')
557 break;
559 if(p > cp){
560 *p = 0;
561 if(cistrcmp(user, cp) == 0){
562 syslog(0, "smtpd", "names.blocked blocks %s", user);
563 Bterm(bp);
564 return 0;
568 Bterm(bp);
569 return 1;
572 /*
573 * a user can opt out of spam filtering by creating
574 * a file in his mail directory named 'nospamfiltering'.
575 */
576 int
577 optoutofspamfilter(char *addr)
579 char *p, *f;
580 int rv;
582 p = strchr(addr, '!');
583 if(p)
584 p++;
585 else
586 p = addr;
589 rv = 0;
590 f = smprint("%s/mail/box/%s/nospamfiltering", get9root(), p);
591 if(f != nil){
592 rv = access(f, 0)==0;
593 free(f);
596 return rv;