Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature_unix/sys/src/cmd/upas/smtp/spam.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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*
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
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
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
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
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
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
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*
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
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
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
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
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
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
456
addbadguy(char *p)
457
{
458
	listadd(&badguys, s_copy(p));
459
};
460
 
461
char*
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
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
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
}