spam.c 10.0 KB

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