Commit | Line | Data |
---|---|---|
2117e02e MW |
1 | Please note that this file is not called ``Internet Mail For Dummies.'' |
2 | It _records_ my thoughts on various issues. It does not _explain_ them. | |
3 | Paragraphs are not organized except by section. The required background | |
4 | varies wildly from one paragraph to the next. | |
5 | ||
6 | In this file, ``sendmail'' means Allman's creation; ``sendmail-clone'' | |
7 | means the program in this package. | |
8 | ||
9 | ||
10 | 1. Security | |
11 | ||
12 | There are lots of interesting remote denial-of-service attacks on any | |
13 | mail system. A long-term solution is to insist on prepayment for | |
14 | unauthorized resource use. The tricky technical problem is to make the | |
15 | prepayment enforcement mechanism cheaper than the expected cost of the | |
16 | attacks. (For local denial-of-service attacks it's enough to be able to | |
17 | figure out which user is responsible.) | |
18 | ||
19 | qmail-send's log was originally designed for profiling. It subsequently | |
20 | sprouted some tracing features. However, there's no way to verify | |
21 | securely that a particular message came from a particular local user; | |
22 | how do you know the recipient is telling you the truth about the | |
23 | contents of the message? With QUEUE_EXTRA it'd be possible to record a | |
24 | one-way hash of each outgoing message, but a user who wants to send | |
25 | ``bad'' mail can avoid qmail entirely. | |
26 | ||
27 | I originally decided on security grounds not to put qmail advertisements | |
28 | into SMTP responses: advertisements often act as version identifiers. | |
29 | But this problem went away when I found a stable qmail URL. | |
30 | ||
31 | As qmail grows in popularity, the mere knowledge that rcpthosts is so | |
32 | easily available will deter people from setting up unauthorized MXs. | |
33 | (I've never seen an unauthorized MX, but I can imagine that it would be | |
34 | rather annoying.) Note that, unlike the bat book checkcompat() kludge, | |
35 | rcpthosts doesn't interfere with mailing lists. | |
36 | ||
37 | qmail-start doesn't bother with tty dissociation. On some old machines | |
38 | this means that random people can send tty signals to the qmail daemons. | |
39 | That's a security flaw in the job control subsystem, not in qmail. | |
40 | ||
41 | The resolver library isn't too bloated (before 4.9.4, at least), but it | |
42 | uses stdio, which _is_ bloated. Reading /etc/resolv.conf costs lots of | |
43 | memory in each qmail-remote process. So it's tempting to incorporate a | |
44 | smaller resolver library into qmail. (Bonus: I'd avoid system-specific | |
45 | problems with old resolvers.) The problem is that I'd then be writing a | |
46 | fundamentally insecure library. I'd no longer be able to blame the BIND | |
47 | authors and vendors for the fact that attackers can easily use DNS to | |
212b6f5d MW |
48 | steal mail. Solution: insist that the resolver run on the same host; the |
49 | kernel can guarantee the security of low-numbered 127.0.0.1 UDP ports. | |
2117e02e MW |
50 | |
51 | NFS is the primary enemy of security partitioning under UNIX. Here's the | |
52 | story. Sun knew from the start that NFS was completely insecure. It | |
53 | tried to hide that fact by disallowing root access over NFS. Intruders | |
54 | nevertheless broke into system after system, first obtaining bin access | |
55 | and then obtaining root access. Various people thus decided to compound | |
56 | Sun's error and build a wall between root and all other users: if all | |
57 | system files are owned by root, and if there are no security holes other | |
58 | than NFS, someone who breaks in via NFS won't be able to wipe out the | |
59 | operating system---he'll merely be able to wipe out all user files. This | |
60 | clueless policy means that, for example, all the qmail users have to be | |
61 | replaced by root. See what I mean by ``enemy''? ... Basic NFS comments: | |
62 | Aside from the cryptographic problem of having hosts communicate | |
63 | securely, it's obvious that there's an administrative problem of mapping | |
64 | client uids to server uids. If a host is secure and under your control, | |
65 | you shouldn't have to map anything. If a host is under someone else's | |
66 | control, you'll want to map his uids to one local account; it's his | |
67 | client's job to decide which of his users get to talk NFS in the first | |
68 | place. Sun's original map---root to nobody, everyone else left alone--- | |
69 | is, as far as I can tell, always wrong. | |
70 | ||
71 | ||
72 | 2. Injecting mail locally (qmail-inject, sendmail-clone) | |
73 | ||
212b6f5d MW |
74 | RFC 822 section 3.4.9 prohibits certain visual effects in headers, and |
75 | the 822bis draft prohibits even more. qmail-inject could enforce these | |
76 | absurd restrictions, but why waste the time? If you will suffer from | |
77 | someone sending you ``flash mail,'' go find a better mail reader. | |
2117e02e MW |
78 | |
79 | qmail-inject's ``Cc: recipient list not shown: ;'' successfully stops | |
80 | sendmail from adding Apparently-To. Unfortunately, old versions of | |
81 | sendmail will append a host name. This wasn't fixed until sendmail 8.7. | |
82 | How many years has it been since RFC 822 came out? | |
83 | ||
84 | sendmail discards duplicate addresses. This has probably resulted in | |
85 | more lost and stolen mail over the years than the entire Chicago branch | |
86 | of the United States Postal Service. The qmail system delivers messages | |
87 | exactly as it's told to do. Along the same lines: qmail-inject is both | |
88 | unable and unwilling to support anything like sendmail's (default) | |
89 | nometoo option. Of course, a list manager could support nometoo. | |
90 | ||
91 | There should be a mechanism in qmail-inject that does for envelope | |
92 | recipients what Return-Path does for the envelope sender. Then | |
93 | qmail-inject -n could print the recipients. | |
94 | ||
95 | Should qmail-inject bounce messages with no recipients? Should there be | |
96 | an option for this? If it stays as is (accept the message), qmail-inject | |
97 | could at least avoid invoking qmail-queue. | |
98 | ||
99 | It is possible to extract non-unique Message-IDs out of qmail-inject. | |
100 | Here's how: stop qmail-inject before it gets to the third line of | |
101 | main(), then wait until the pids wrap around, then restart qmail-inject | |
102 | and blast the message through, then start another qmail-inject with the | |
212b6f5d MW |
103 | same pid in the same second. I'm not sure how to fix this without |
104 | system-supplied sequence numbers. (Of course, the user could just type | |
105 | in his own non-unique Message-IDs.) | |
2117e02e MW |
106 | |
107 | The bat book says: ``Rules that hide hosts in a domain should be applied | |
108 | only to sender addresses.'' Recipient masquerading works fine with | |
109 | qmail. None of sendmail's pitfalls apply, basically because qmail has a | |
110 | straight paper path. | |
111 | ||
212b6f5d MW |
112 | I predicted that I would receive some pressure to make up for the |
113 | failings of MUA writers who don't understand the concept of reliability. | |
114 | (``Like, duh, you mean I'm supposed to check the sendmail exit code?'') | |
115 | I was right. | |
2117e02e MW |
116 | |
117 | ||
118 | 3. Receiving mail from the network (tcp-env, qmail-smtpd) | |
119 | ||
212b6f5d MW |
120 | qmail-smtpd doesn't allow privacy-invading commands like VRFY and EXPN. |
121 | If you really want to publish such information, use a mechanism that | |
122 | legitimate users actually know about, such as fingerd or httpd. | |
123 | ||
124 | RFC 1123 says that VRFY and EXPN are important to track down cross-host | |
125 | mailing list loops. With Delivered-To, mailing list loops do no damage, | |
126 | _and_ one of the list administrators gets a bounce message that shows | |
127 | exactly how the loop occurred. Solve the problem, not the symptom. | |
2117e02e MW |
128 | |
129 | Should dns.c make special allowances for 127.0.0.1/localhost? | |
130 | ||
131 | badmailfrom (like 8BITMIME) is a waste of code space. | |
132 | ||
212b6f5d MW |
133 | In theory a MAIL or RCPT argument can contain unquoted LFs. In practice |
134 | there are a huge number of clients that terminate commands with just LF, | |
135 | even if they use CR properly inside DATA. | |
136 | ||
2117e02e MW |
137 | |
138 | 4. Adding messages to the queue (qmail-queue) | |
139 | ||
140 | Should qmail-queue try to make sure enough disk space is free in | |
141 | advance? When qmail-queue is invoked by qmail-local or (with ESMTP) | |
212b6f5d MW |
142 | qmail-smtpd or qmail-qmtpd or qmail-qmqpd, it could be told a size in |
143 | advance. I wish UNIX had an atomic allocate-disk-space routine... | |
2117e02e MW |
144 | |
145 | The qmail.h interface (reflecting the qmail-queue interface, which in | |
146 | turn reflects the current queue file structure) is constitutionally | |
147 | incapable of handling an address that contains a 0 byte. I can't imagine | |
148 | that this will be a problem. | |
149 | ||
150 | Should qmail-queue not bother queueing a message with no recipients? | |
151 | ||
152 | ||
153 | 5. Handling queued mail (qmail-send, qmail-clean) | |
154 | ||
155 | The queue directory must be local. Mounting it over NFS is extremely | |
156 | dangerous---not that this stops people from running sendmail that way! | |
212b6f5d | 157 | Diskless hosts should use mini-qmail instead. |
2117e02e MW |
158 | |
159 | Queue reliability demands that single-byte writes be atomic. This is | |
160 | true for a fixed-block filesystem such as UFS, and for a logging | |
161 | filesystem such as LFS. | |
162 | ||
163 | qmail-send uses 8 bytes of memory per queued message. Double that for | |
164 | reallocation. (Fix: use a small forest of heaps; i.e., keep several | |
165 | prioqs.) Double again for buddy malloc()s. (Fix: be clever about the | |
166 | heap sizes.) 32 bytes is worrisome, but not devastating. Even on my | |
167 | disk-heavy memory-light machine, I'd run out of inodes long before | |
168 | running out of memory. | |
169 | ||
170 | Some mail systems organize the queue by host. This is pointless as a | |
171 | means of splitting up the queue directory. The real issue is what to do | |
172 | when you suddenly find out that a host is up. For local SLIP/PPP links | |
173 | you know in advance which hosts need this treatment, so you can handle | |
174 | them with virtualdomains and serialmail. | |
175 | ||
176 | For the old queue structure I implemented recipient list compression: | |
177 | if mail goes out to a giant mailing list, and most of the recipients are | |
178 | delivered, make a new, compressed, todo list. But this really isn't | |
179 | worth the effort: it saves only a tiny bit of CPU time. | |
180 | ||
181 | qmail-send doesn't have any notions of precedence, priority, fairness, | |
182 | importance, etc. It handles the queue in first-seen-first-served order. | |
183 | One could put a lot of work into doing something different, but that | |
184 | work would be a waste: given the triggering mechanism and qmail's | |
185 | deferral strategy, it is exceedingly rare for the queue to contain more | |
186 | than one deliverable message at any given moment. | |
187 | ||
188 | Exception: Even with all the concurrency tricks, qmail-send can end up | |
189 | spending a few minutes on a mailing list with thousands of remote | |
190 | entries. A user might send a new message to a remote address in the | |
212b6f5d MW |
191 | meantime. The simplest way to handle this would be to put big messages |
192 | on a separate channel. | |
2117e02e MW |
193 | |
194 | qmail-send will never start a pass for a job that it already has. This | |
195 | means that, if one delivery takes longer than the retry interval, the | |
196 | next pass will be delayed. I implemented the opposite strategy for the | |
197 | old queue structure. Some hassles: mark() had to understand how job | |
198 | input was buffered; every new delivery had to check whether the same | |
199 | mpos in the same message was already being done. | |
200 | ||
201 | Some things that qmail-send does synchronously: queueing a bounce | |
202 | message; doing a cleanup via qmail-clean; classifying and rewriting all | |
203 | the addresses in a new message. As usual, making these asynchronous | |
204 | would require some housekeeping, but could speed things up a bit. | |
212b6f5d MW |
205 | (I'm willing to assume POSIX waitpid() for asynchronous bounces; putting |
206 | an unbounded buffer into wait_pid() for the sake of NeXTSTEP 3 is not | |
207 | worthwhile.) | |
208 | ||
209 | Disk I/O is a bottleneck; UFS is reliable but it isn't fast. A good | |
210 | logging filesystem offers much better performance, but logging | |
211 | filesystems aren't widely available. Solution: Keep a journal, separate | |
212 | from the queue, adequate to rebuild the queue (with at worst some | |
213 | duplicate deliveries). Compress the journal. This would dramatically | |
214 | reduce total disk I/O. | |
215 | ||
216 | Bounce aggregation is a dubious feature. Bounce records aren't | |
217 | crashproof; there can be a huge delay between a failure and a bounce; | |
218 | the resulting bounce format is unnecessarily complicated. I'm tempted to | |
219 | scrap the bounce directory and send one bounce for each failing | |
220 | recipient, with appropriate modifications in the accompanying text. | |
221 | ||
222 | qmail-stop implementation: setuid to UID_SEND; kill -TERM -1. Or run | |
223 | qmail-start under an external service controller, such as supervise; | |
224 | that's why it runs in the foreground. | |
2117e02e MW |
225 | |
226 | The readdir() interface hides I/O errors. Lower-level interfaces would | |
227 | lead me into a thicket of portability problems. I'm really not sure what | |
228 | to do about this. Of course, a hard I/O error means that mail is toast, | |
229 | but a soft I/O error shouldn't cause any trouble. | |
230 | ||
231 | job_open() or pass_dochan() could be paranoid about the same id,channel | |
232 | already being open; but, since messdone() is so paranoid, the worst | |
233 | possible effect of a bug along these lines would be double delivery. | |
234 | ||
235 | Mathematical amusement: The optimal retry schedule is essentially, | |
236 | though not exactly, independent of the actual distribution of message | |
237 | delay times. What really matters is how much cost you assign to retries | |
238 | and to particular increases in latency. qmail's current quadratic retry | |
239 | schedule says that an hour-long delay in a day-old message is worth the | |
240 | same as a ten-minute delay in an hour-old message; this doesn't seem so | |
241 | unreasonable. | |
242 | ||
243 | Insider information: AOL retries their messages every five minutes for | |
244 | three days straight. Hmmm. | |
245 | ||
246 | ||
247 | 6. Sending mail through the network (qmail-rspawn, qmail-remote) | |
248 | ||
249 | Are there any hosts, anywhere, whose mailers are bogged down by huge | |
250 | messages to multiple recipients at a single host? For typical hosts, | |
251 | multiple RCPTs per SMTP aren't an ``efficiency feature''; they're a | |
252 | _slowness_ feature. Separate SMTP transactions have much lower latency. | |
253 | ||
212b6f5d MW |
254 | I've heard three complaints about bandwidth use from masochists sending |
255 | messages through a modem through a smarthost to thousands of users--- | |
256 | without sublists! They can get much better performance with QMQP. | |
2117e02e MW |
257 | |
258 | In the opposite direction: It's tempting to remove the @host part of the | |
259 | qmail-remote recip argument. Or at least avoid double-dns_cname. | |
260 | ||
261 | There are lots of reasons that qmail-rspawn should take a more active | |
262 | role in qmail-remote's activities. It should call separate programs to | |
212b6f5d MW |
263 | do (1) MX lookups, (2) SMTP connections, (3) QMTP connections. (But this |
264 | wouldn't be so important if the DNS library didn't burn so much memory.) | |
2117e02e MW |
265 | |
266 | I bounce ambiguous MXs. (An ``ambiguous MX'' is a best-preference MX | |
267 | record sending me mail for a host that I don't recognize as local.) | |
268 | Automatically treating ambiguous MXs as local is incompatible with my | |
269 | design decision to keep local delivery working when the network goes | |
270 | down. It puts more faith in DNS than DNS deserves. Much better: Have | |
271 | your MX records generated automatically from control/locals. | |
272 | ||
273 | If I successfully connect to an MX host but it temporarily refuses to | |
274 | accept the message, I give up and put the message back into the queue. | |
275 | But several documents seem to suggest that I should try further MX | |
276 | records. What are they thinking? My approach deals properly with downed | |
277 | hosts, hosts that are unreachable through a firewall, and load | |
278 | balancing; what else do people use multiple MX records for? | |
279 | ||
280 | Currently qmail-remote sends data in 1024-byte buffers. Perhaps it | |
281 | should try to take account of the MTU. | |
282 | ||
283 | Perhaps qmail-remote should allocate a fixed amount of DNS/connect() | |
284 | time across any number of MXs; this idea is due to Mark Delany. | |
285 | ||
286 | RFC 821 doesn't say what it means by ``text.'' qmail-remote assumes that | |
287 | the server's reply text doesn't contain bare LFs. | |
288 | ||
212b6f5d MW |
289 | RFC 821 and RFC 1123 prohibit host names in MAIL FROM and RCPT TO from |
290 | being aliases. qmail-remote, like sendmail, rewrites aliases in RCPT; | |
291 | people who don't list aliases in control/locals or sendmail's Cw are | |
292 | implicitly relying on this conversion. It is course quite silly for an | |
293 | internal DNS detail to have such an effect on mail delivery, but that's | |
294 | how the Internet works. On the other hand, the compatibility arguments | |
295 | do not apply to MAIL FROM. qmail-remote no longer bothers with CNAME | |
296 | lookups for the envelope sender host. | |
297 | ||
2117e02e MW |
298 | |
299 | 7. Delivering mail locally (qmail-lspawn, qmail-local) | |
300 | ||
301 | qmail-local doesn't support comsat. comsat is a pointless abomination. | |
302 | Use qbiff if you want that kind of notification. | |
303 | ||
304 | The getpwnam() interface hides I/O errors. Solution: qmail-pw2u. | |
305 | ||
306 | ||
307 | 8. sendmail V8's new features | |
308 | ||
309 | sendmail-8.8.0/doc/op/op.me includes a list of big improvements of | |
310 | sendmail 8.8.0 over sendmail 5.67. Here's how qmail stacks up against | |
311 | each of those improvements. (Of course, qmail has its own improvements, | |
312 | but that's not the point of this list.) | |
313 | ||
314 | Connection caching, MX piggybacking: Nope. (Profile. Don't speculate.) | |
315 | ||
316 | Response to RCPT command is fast: Yup. | |
317 | ||
318 | IP addresses show up in Received lines: Yup. | |
319 | ||
320 | Self domain literal is properly handled: Yup. | |
321 | ||
322 | Different timeouts for QUIT, RCPT, etc.: No, just a single timeout. | |
323 | ||
324 | Proper <> handling, route-address pruning: Yes, but not configurable. | |
325 | ||
326 | ESMTP support: Yup. (Server-side, including PIPELINING.) | |
327 | ||
328 | 8-bit clean: Yup. (Including server-side 8BITMIME support; same as | |
329 | sendmail with the 8 option.) | |
330 | ||
331 | Configurable user database: Yup. | |
332 | ||
333 | BIND support: Yup. | |
334 | ||
212b6f5d | 335 | Keyed files: Yes, in fastforward. |
2117e02e MW |
336 | |
337 | 931/1413/Ident/TAP: Yup. | |
338 | ||
339 | Correct 822 address list parsing: Yup. (Note that sendmail still has | |
340 | some major problems with quoting.) | |
341 | ||
342 | List-owner handling: Yup. | |
343 | ||
344 | Dynamic header allocation: Yup. | |
345 | ||
212b6f5d MW |
346 | Minimum number of disk blocks: Yes, via tunefs -m. (Or quotas; the right |
347 | setup has qmailq with a small quota, qmails with a larger quota, so that | |
348 | qmail-send always has room to work.) | |
2117e02e MW |
349 | |
350 | Checkpointing: Yes, but not configurable---qmail always checkpoints. | |
351 | ||
352 | Error message configuration: Nope. | |
353 | ||
354 | GECOS matching: Not directly, but easy to hook in. | |
355 | ||
356 | Hop limit configuration: No. (qmail's limit is 100 hops. qmail offers | |
357 | automatic loop protection much more advanced than hop counting.) | |
358 | ||
359 | MIME error messages: No. (qmail uses QSBMF error messages, which are | |
360 | much easier to parse.) | |
361 | ||
362 | Forward file path: Yes, via /etc/passwd. | |
363 | ||
364 | Incoming SMTP configuration: Yes, via inetd or tcpserver. | |
365 | ||
366 | Privacy options: Yes, but they're not options. | |
367 | ||
368 | Best-MX mangling: Nope. See section 6 for further discussion. | |
369 | ||
370 | 7-bit mangling: Nope. qmail always uses 8 bits. | |
371 | ||
372 | Support for up to 20 MX records: Yes, and more. qmail has no limits | |
373 | other than memory. | |
374 | ||
375 | Correct quoting of name-and-address headers: Yup. | |
376 | ||
377 | VRFY and EXPN now different: Nope. qmail always hides this information. | |
378 | ||
379 | Multi-word classes, deferred macro expansion, separate envelope/header | |
380 | $g processing, separate per-mailer envelope and header processing, new | |
381 | command line flags, new configuration lines, new mailer flags, new | |
382 | macros: These are sendmail-specific; they wouldn't even make sense for | |
383 | qmail. For example, _of course_ qmail handles envelopes and headers | |
384 | separately; they're almost entirely different objects! | |
385 | ||
386 | ||
387 | 9. Miscellany | |
388 | ||
389 | sendmail-clone and qsmhook are too bletcherous to be documented. (The | |
390 | official replacement for qsmhook is preline, together with the | |
391 | qmail-command environment variables.) | |
392 | ||
393 | I've considered making install atomic, but this is very difficult to do | |
394 | right, and pointless if it isn't done right. | |
395 | ||
396 | RN suggests automatically putting together a reasonable set of lines for | |
397 | /etc/passwd. I perceive this as getting into the adduser business, which | |
398 | is worrisome: I'll be lynched the first time I screw up somebody's | |
399 | passwd file. This should be left to OS-specific installation scripts. | |
400 | ||
401 | The BSD 4.2 inetd didn't allow a username. I think I can safely forget | |
402 | about this. (DS notes that the username works under Ultrix even though | |
403 | it's undocumented.) | |
404 | ||
405 | I should clean up the bput/put choices. | |
406 | ||
407 | Some of the stralloc_0()s indicate that certain lower-level routines | |
408 | should grok stralloc. | |
409 | ||
2117e02e MW |
410 | qmail assumes that all times are positive; that pid_t, time_t and ino_t |
411 | fit into unsigned long; that gid_t fits into int; that the character set | |
412 | is ASCII; and that all pointers are interchangeable. Do I care? | |
413 | ||
414 | The bat book justifies sendmail's insane line-splitting mechanism by | |
415 | pointing out that it might be useful for ``a 40-character braille | |
416 | print-driving program.'' C'mon, guys, is that your best excuse? | |
417 | ||
418 | qmail's mascot is a dolphin. |