Blame


1 5cdb1798 2005-10-29 devnull #include "common.h"
2 5cdb1798 2005-10-29 devnull #include "smtpd.h"
3 5cdb1798 2005-10-29 devnull #include "smtp.h"
4 5cdb1798 2005-10-29 devnull #include <ctype.h>
5 5cdb1798 2005-10-29 devnull #include <ip.h>
6 5cdb1798 2005-10-29 devnull #include <ndb.h>
7 5cdb1798 2005-10-29 devnull
8 5cdb1798 2005-10-29 devnull typedef struct {
9 5cdb1798 2005-10-29 devnull int existed; /* these two are distinct to cope with errors */
10 5cdb1798 2005-10-29 devnull int created;
11 5cdb1798 2005-10-29 devnull int noperm;
12 5cdb1798 2005-10-29 devnull long mtime; /* mod time, iff it already existed */
13 5cdb1798 2005-10-29 devnull } Greysts;
14 5cdb1798 2005-10-29 devnull
15 5cdb1798 2005-10-29 devnull /*
16 5cdb1798 2005-10-29 devnull * There's a bit of a problem with yahoo; they apparently have a vast
17 5cdb1798 2005-10-29 devnull * pool of machines that all run the same queue(s), so a 451 retry can
18 5cdb1798 2005-10-29 devnull * come from a different IP address for many, many retries, and it can
19 5cdb1798 2005-10-29 devnull * take ~5 hours for the same IP to call us back. Various other goofballs,
20 5cdb1798 2005-10-29 devnull * notably the IEEE, try to send mail just before 9 AM, then refuse to try
21 5cdb1798 2005-10-29 devnull * again until after 5 PM. Doh!
22 5cdb1798 2005-10-29 devnull */
23 5cdb1798 2005-10-29 devnull enum {
24 5cdb1798 2005-10-29 devnull Nonspammax = 14*60*60, /* must call back within this time if real */
25 5cdb1798 2005-10-29 devnull };
26 b5f65921 2006-02-11 devnull static char *whitelist = "#9/mail/lib/whitelist";
27 5cdb1798 2005-10-29 devnull
28 5cdb1798 2005-10-29 devnull /*
29 5cdb1798 2005-10-29 devnull * matches ip addresses or subnets in whitelist against nci->rsys.
30 5cdb1798 2005-10-29 devnull * ignores comments and blank lines in /mail/lib/whitelist.
31 5cdb1798 2005-10-29 devnull */
32 5cdb1798 2005-10-29 devnull static int
33 5cdb1798 2005-10-29 devnull onwhitelist(void)
34 5cdb1798 2005-10-29 devnull {
35 5cdb1798 2005-10-29 devnull int lnlen;
36 5cdb1798 2005-10-29 devnull char *line, *parse;
37 5cdb1798 2005-10-29 devnull char input[128];
38 5cdb1798 2005-10-29 devnull uchar ip[IPaddrlen], ipmasked[IPaddrlen];
39 5cdb1798 2005-10-29 devnull uchar mask4[IPaddrlen], addr4[IPaddrlen];
40 5cdb1798 2005-10-29 devnull uchar mask[IPaddrlen], addr[IPaddrlen], addrmasked[IPaddrlen];
41 5cdb1798 2005-10-29 devnull Biobuf *wl;
42 5cdb1798 2005-10-29 devnull static int beenhere;
43 5cdb1798 2005-10-29 devnull
44 5cdb1798 2005-10-29 devnull if (!beenhere) {
45 5cdb1798 2005-10-29 devnull beenhere = 1;
46 5cdb1798 2005-10-29 devnull fmtinstall('I', eipfmt);
47 b5f65921 2006-02-11 devnull whitelist = unsharp(whitelist);
48 5cdb1798 2005-10-29 devnull }
49 5cdb1798 2005-10-29 devnull
50 5cdb1798 2005-10-29 devnull parseip(ip, nci->rsys);
51 5cdb1798 2005-10-29 devnull wl = Bopen(whitelist, OREAD);
52 5cdb1798 2005-10-29 devnull if (wl == nil)
53 5cdb1798 2005-10-29 devnull return 1;
54 5cdb1798 2005-10-29 devnull while ((line = Brdline(wl, '\n')) != nil) {
55 5cdb1798 2005-10-29 devnull if (line[0] == '#' || line[0] == '\n')
56 5cdb1798 2005-10-29 devnull continue;
57 5cdb1798 2005-10-29 devnull lnlen = Blinelen(wl);
58 5cdb1798 2005-10-29 devnull line[lnlen-1] = '\0'; /* clobber newline */
59 5cdb1798 2005-10-29 devnull
60 5cdb1798 2005-10-29 devnull /* default mask is /32 (v4) or /128 (v6) for bare IP */
61 5cdb1798 2005-10-29 devnull parse = line;
62 5cdb1798 2005-10-29 devnull if (strchr(line, '/') == nil) {
63 5cdb1798 2005-10-29 devnull strncpy(input, line, sizeof input - 5);
64 5cdb1798 2005-10-29 devnull if (strchr(line, '.') != nil)
65 5cdb1798 2005-10-29 devnull strcat(input, "/32");
66 5cdb1798 2005-10-29 devnull else
67 5cdb1798 2005-10-29 devnull strcat(input, "/128");
68 5cdb1798 2005-10-29 devnull parse = input;
69 5cdb1798 2005-10-29 devnull }
70 5cdb1798 2005-10-29 devnull /* sorry, dave; where's parsecidr for v4 or v6? */
71 5cdb1798 2005-10-29 devnull v4parsecidr(addr4, mask4, parse);
72 5cdb1798 2005-10-29 devnull v4tov6(addr, addr4);
73 5cdb1798 2005-10-29 devnull v4tov6(mask, mask4);
74 5cdb1798 2005-10-29 devnull
75 5cdb1798 2005-10-29 devnull maskip(addr, mask, addrmasked);
76 5cdb1798 2005-10-29 devnull maskip(ip, mask, ipmasked);
77 5cdb1798 2005-10-29 devnull if (memcmp(ipmasked, addrmasked, IPaddrlen) == 0)
78 5cdb1798 2005-10-29 devnull break;
79 5cdb1798 2005-10-29 devnull }
80 5cdb1798 2005-10-29 devnull Bterm(wl);
81 5cdb1798 2005-10-29 devnull return line != nil;
82 5cdb1798 2005-10-29 devnull }
83 5cdb1798 2005-10-29 devnull
84 5cdb1798 2005-10-29 devnull static int mkdirs(char *);
85 5cdb1798 2005-10-29 devnull
86 5cdb1798 2005-10-29 devnull /*
87 5cdb1798 2005-10-29 devnull * if any directories leading up to path don't exist, create them.
88 5cdb1798 2005-10-29 devnull * modifies but restores path.
89 5cdb1798 2005-10-29 devnull */
90 5cdb1798 2005-10-29 devnull static int
91 5cdb1798 2005-10-29 devnull mkpdirs(char *path)
92 5cdb1798 2005-10-29 devnull {
93 5cdb1798 2005-10-29 devnull int rv = 0;
94 5cdb1798 2005-10-29 devnull char *sl = strrchr(path, '/');
95 5cdb1798 2005-10-29 devnull
96 5cdb1798 2005-10-29 devnull if (sl != nil) {
97 5cdb1798 2005-10-29 devnull *sl = '\0';
98 5cdb1798 2005-10-29 devnull rv = mkdirs(path);
99 5cdb1798 2005-10-29 devnull *sl = '/';
100 5cdb1798 2005-10-29 devnull }
101 5cdb1798 2005-10-29 devnull return rv;
102 5cdb1798 2005-10-29 devnull }
103 5cdb1798 2005-10-29 devnull
104 5cdb1798 2005-10-29 devnull /*
105 5cdb1798 2005-10-29 devnull * if path or any directories leading up to it don't exist, create them.
106 5cdb1798 2005-10-29 devnull * modifies but restores path.
107 5cdb1798 2005-10-29 devnull */
108 5cdb1798 2005-10-29 devnull static int
109 5cdb1798 2005-10-29 devnull mkdirs(char *path)
110 5cdb1798 2005-10-29 devnull {
111 5cdb1798 2005-10-29 devnull int fd;
112 5cdb1798 2005-10-29 devnull
113 5cdb1798 2005-10-29 devnull if (access(path, AEXIST) >= 0)
114 5cdb1798 2005-10-29 devnull return 0;
115 5cdb1798 2005-10-29 devnull
116 5cdb1798 2005-10-29 devnull /* make presumed-missing intermediate directories */
117 5cdb1798 2005-10-29 devnull if (mkpdirs(path) < 0)
118 5cdb1798 2005-10-29 devnull return -1;
119 5cdb1798 2005-10-29 devnull
120 5cdb1798 2005-10-29 devnull /* make final directory */
121 5cdb1798 2005-10-29 devnull fd = create(path, OREAD, 0777|DMDIR);
122 5cdb1798 2005-10-29 devnull if (fd < 0)
123 5cdb1798 2005-10-29 devnull /*
124 5cdb1798 2005-10-29 devnull * we may have lost a race; if the directory now exists,
125 5cdb1798 2005-10-29 devnull * it's okay.
126 5cdb1798 2005-10-29 devnull */
127 5cdb1798 2005-10-29 devnull return access(path, AEXIST) < 0? -1: 0;
128 5cdb1798 2005-10-29 devnull close(fd);
129 5cdb1798 2005-10-29 devnull return 0;
130 5cdb1798 2005-10-29 devnull }
131 5cdb1798 2005-10-29 devnull
132 5cdb1798 2005-10-29 devnull static long
133 5cdb1798 2005-10-29 devnull getmtime(char *file)
134 5cdb1798 2005-10-29 devnull {
135 5cdb1798 2005-10-29 devnull long mtime = -1;
136 5cdb1798 2005-10-29 devnull Dir *ds = dirstat(file);
137 5cdb1798 2005-10-29 devnull
138 5cdb1798 2005-10-29 devnull if (ds != nil) {
139 5cdb1798 2005-10-29 devnull mtime = ds->mtime;
140 5cdb1798 2005-10-29 devnull free(ds);
141 5cdb1798 2005-10-29 devnull }
142 5cdb1798 2005-10-29 devnull return mtime;
143 5cdb1798 2005-10-29 devnull }
144 5cdb1798 2005-10-29 devnull
145 5cdb1798 2005-10-29 devnull static void
146 5cdb1798 2005-10-29 devnull tryaddgrey(char *file, Greysts *gsp)
147 5cdb1798 2005-10-29 devnull {
148 5cdb1798 2005-10-29 devnull int fd = create(file, OWRITE|OEXCL, 0444|DMEXCL);
149 5cdb1798 2005-10-29 devnull
150 5cdb1798 2005-10-29 devnull gsp->created = (fd >= 0);
151 5cdb1798 2005-10-29 devnull if (fd >= 0) {
152 5cdb1798 2005-10-29 devnull close(fd);
153 5cdb1798 2005-10-29 devnull gsp->existed = 0; /* just created; couldn't have existed */
154 5cdb1798 2005-10-29 devnull } else {
155 5cdb1798 2005-10-29 devnull /*
156 5cdb1798 2005-10-29 devnull * why couldn't we create file? it must have existed
157 5cdb1798 2005-10-29 devnull * (or we were denied perm on parent dir.).
158 5cdb1798 2005-10-29 devnull * if it existed, fill in gsp->mtime; otherwise
159 5cdb1798 2005-10-29 devnull * make presumed-missing intermediate directories.
160 5cdb1798 2005-10-29 devnull */
161 5cdb1798 2005-10-29 devnull gsp->existed = access(file, AEXIST) >= 0;
162 5cdb1798 2005-10-29 devnull if (gsp->existed)
163 5cdb1798 2005-10-29 devnull gsp->mtime = getmtime(file);
164 5cdb1798 2005-10-29 devnull else if (mkpdirs(file) < 0)
165 5cdb1798 2005-10-29 devnull gsp->noperm = 1;
166 5cdb1798 2005-10-29 devnull }
167 5cdb1798 2005-10-29 devnull }
168 5cdb1798 2005-10-29 devnull
169 5cdb1798 2005-10-29 devnull static void
170 5cdb1798 2005-10-29 devnull addgreylist(char *file, Greysts *gsp)
171 5cdb1798 2005-10-29 devnull {
172 5cdb1798 2005-10-29 devnull tryaddgrey(file, gsp);
173 5cdb1798 2005-10-29 devnull if (!gsp->created && !gsp->existed && !gsp->noperm)
174 5cdb1798 2005-10-29 devnull /* retry the greylist entry with parent dirs created */
175 5cdb1798 2005-10-29 devnull tryaddgrey(file, gsp);
176 5cdb1798 2005-10-29 devnull }
177 5cdb1798 2005-10-29 devnull
178 5cdb1798 2005-10-29 devnull static int
179 5cdb1798 2005-10-29 devnull recentcall(Greysts *gsp)
180 5cdb1798 2005-10-29 devnull {
181 5cdb1798 2005-10-29 devnull long delay = time(0) - gsp->mtime;
182 5cdb1798 2005-10-29 devnull
183 5cdb1798 2005-10-29 devnull if (!gsp->existed)
184 5cdb1798 2005-10-29 devnull return 0;
185 5cdb1798 2005-10-29 devnull /* reject immediate call-back; spammers are doing that now */
186 5cdb1798 2005-10-29 devnull return delay >= 30 && delay <= Nonspammax;
187 5cdb1798 2005-10-29 devnull }
188 5cdb1798 2005-10-29 devnull
189 5cdb1798 2005-10-29 devnull /*
190 5cdb1798 2005-10-29 devnull * policy: if (caller-IP, my-IP, rcpt) is not on the greylist,
191 5cdb1798 2005-10-29 devnull * reject this message as "451 temporary failure". if the caller is real,
192 5cdb1798 2005-10-29 devnull * he'll retry soon, otherwise he's a spammer.
193 5cdb1798 2005-10-29 devnull * at the first rejection, create a greylist entry for (my-ip, caller-ip,
194 5cdb1798 2005-10-29 devnull * rcpt, time), where time is the file's mtime. if they call back and there's
195 5cdb1798 2005-10-29 devnull * already a greylist entry, and it's within the allowed interval,
196 5cdb1798 2005-10-29 devnull * add their IP to the append-only whitelist.
197 5cdb1798 2005-10-29 devnull *
198 5cdb1798 2005-10-29 devnull * greylist files can be removed at will; at worst they'll cause a few
199 5cdb1798 2005-10-29 devnull * extra retries.
200 5cdb1798 2005-10-29 devnull */
201 5cdb1798 2005-10-29 devnull
202 5cdb1798 2005-10-29 devnull static int
203 5cdb1798 2005-10-29 devnull isrcptrecent(char *rcpt)
204 5cdb1798 2005-10-29 devnull {
205 5cdb1798 2005-10-29 devnull char *user;
206 5cdb1798 2005-10-29 devnull char file[256];
207 5cdb1798 2005-10-29 devnull Greysts gs;
208 5cdb1798 2005-10-29 devnull Greysts *gsp = &gs;
209 5cdb1798 2005-10-29 devnull
210 5cdb1798 2005-10-29 devnull if (rcpt[0] == '\0' || strchr(rcpt, '/') != nil ||
211 5cdb1798 2005-10-29 devnull strcmp(rcpt, ".") == 0 || strcmp(rcpt, "..") == 0)
212 5cdb1798 2005-10-29 devnull return 0;
213 5cdb1798 2005-10-29 devnull
214 5cdb1798 2005-10-29 devnull /* shorten names to fit pre-fossil or pre-9p2000 file servers */
215 5cdb1798 2005-10-29 devnull user = strrchr(rcpt, '!');
216 5cdb1798 2005-10-29 devnull if (user == nil)
217 5cdb1798 2005-10-29 devnull user = rcpt;
218 5cdb1798 2005-10-29 devnull else
219 5cdb1798 2005-10-29 devnull user++;
220 5cdb1798 2005-10-29 devnull
221 5cdb1798 2005-10-29 devnull /* check & try to update the grey list entry */
222 b5f65921 2006-02-11 devnull snprint(file, sizeof file, "%s/mail/grey/%s/%s/%s",
223 b5f65921 2006-02-11 devnull get9root(), nci->lsys, nci->rsys, user);
224 5cdb1798 2005-10-29 devnull memset(gsp, 0, sizeof *gsp);
225 5cdb1798 2005-10-29 devnull addgreylist(file, gsp);
226 5cdb1798 2005-10-29 devnull
227 5cdb1798 2005-10-29 devnull /* if on greylist already and prior call was recent, add to whitelist */
228 5cdb1798 2005-10-29 devnull if (gsp->existed && recentcall(gsp)) {
229 5cdb1798 2005-10-29 devnull syslog(0, "smtpd",
230 5cdb1798 2005-10-29 devnull "%s/%s was grey; adding IP to white", nci->rsys, rcpt);
231 5cdb1798 2005-10-29 devnull return 1;
232 5cdb1798 2005-10-29 devnull } else if (gsp->existed)
233 5cdb1798 2005-10-29 devnull syslog(0, "smtpd", "call for %s/%s was seconds ago or long ago",
234 5cdb1798 2005-10-29 devnull nci->rsys, rcpt);
235 5cdb1798 2005-10-29 devnull else
236 5cdb1798 2005-10-29 devnull syslog(0, "smtpd", "no call registered for %s/%s; registering",
237 5cdb1798 2005-10-29 devnull nci->rsys, rcpt);
238 5cdb1798 2005-10-29 devnull return 0;
239 5cdb1798 2005-10-29 devnull }
240 5cdb1798 2005-10-29 devnull
241 5cdb1798 2005-10-29 devnull void
242 5cdb1798 2005-10-29 devnull vfysenderhostok(void)
243 5cdb1798 2005-10-29 devnull {
244 5cdb1798 2005-10-29 devnull int recent = 0;
245 5cdb1798 2005-10-29 devnull Link *l;
246 5cdb1798 2005-10-29 devnull
247 5cdb1798 2005-10-29 devnull if (onwhitelist())
248 5cdb1798 2005-10-29 devnull return;
249 5cdb1798 2005-10-29 devnull
250 5cdb1798 2005-10-29 devnull for (l = rcvers.first; l; l = l->next)
251 5cdb1798 2005-10-29 devnull if (isrcptrecent(s_to_c(l->p)))
252 5cdb1798 2005-10-29 devnull recent = 1;
253 5cdb1798 2005-10-29 devnull
254 5cdb1798 2005-10-29 devnull /* if on greylist already and prior call was recent, add to whitelist */
255 5cdb1798 2005-10-29 devnull if (recent) {
256 5cdb1798 2005-10-29 devnull int fd = create(whitelist, OWRITE, 0666|DMAPPEND);
257 5cdb1798 2005-10-29 devnull
258 5cdb1798 2005-10-29 devnull if (fd >= 0) {
259 5cdb1798 2005-10-29 devnull seek(fd, 0, 2); /* paranoia */
260 5cc53af9 2006-02-12 devnull fprint(fd, "# unknown\n%s\n\n", nci->rsys);
261 5cdb1798 2005-10-29 devnull close(fd);
262 5cdb1798 2005-10-29 devnull }
263 5cdb1798 2005-10-29 devnull } else {
264 5cdb1798 2005-10-29 devnull syslog(0, "smtpd",
265 5cdb1798 2005-10-29 devnull "no recent call from %s for a rcpt; rejecting with temporary failure",
266 5cdb1798 2005-10-29 devnull nci->rsys);
267 5cdb1798 2005-10-29 devnull reply("451 please try again soon from the same IP.\r\n");
268 5cdb1798 2005-10-29 devnull exits("no recent call for a rcpt");
269 5cdb1798 2005-10-29 devnull }
270 5cdb1798 2005-10-29 devnull }