1 | /* |
---|
2 | * Copyright (c) 1998 Sendmail, Inc. All rights reserved. |
---|
3 | * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. |
---|
4 | * Copyright (c) 1988, 1993 |
---|
5 | * The Regents of the University of California. All rights reserved. |
---|
6 | * |
---|
7 | * By using this file, you agree to the terms and conditions set |
---|
8 | * forth in the LICENSE file which can be found at the top level of |
---|
9 | * the sendmail distribution. |
---|
10 | * |
---|
11 | */ |
---|
12 | |
---|
13 | # include "sendmail.h" |
---|
14 | |
---|
15 | #ifndef lint |
---|
16 | #if SMTP |
---|
17 | static char sccsid[] = "@(#)srvrsmtp.c 8.187 (Berkeley) 10/23/1998 (with SMTP)"; |
---|
18 | #else |
---|
19 | static char sccsid[] = "@(#)srvrsmtp.c 8.187 (Berkeley) 10/23/1998 (without SMTP)"; |
---|
20 | #endif |
---|
21 | #endif /* not lint */ |
---|
22 | |
---|
23 | # include <errno.h> |
---|
24 | |
---|
25 | # if SMTP |
---|
26 | |
---|
27 | /* |
---|
28 | ** SMTP -- run the SMTP protocol. |
---|
29 | ** |
---|
30 | ** Parameters: |
---|
31 | ** nullserver -- if non-NULL, rejection message for |
---|
32 | ** all SMTP commands. |
---|
33 | ** e -- the envelope. |
---|
34 | ** |
---|
35 | ** Returns: |
---|
36 | ** never. |
---|
37 | ** |
---|
38 | ** Side Effects: |
---|
39 | ** Reads commands from the input channel and processes |
---|
40 | ** them. |
---|
41 | */ |
---|
42 | |
---|
43 | struct cmd |
---|
44 | { |
---|
45 | char *cmdname; /* command name */ |
---|
46 | int cmdcode; /* internal code, see below */ |
---|
47 | }; |
---|
48 | |
---|
49 | /* values for cmdcode */ |
---|
50 | # define CMDERROR 0 /* bad command */ |
---|
51 | # define CMDMAIL 1 /* mail -- designate sender */ |
---|
52 | # define CMDRCPT 2 /* rcpt -- designate recipient */ |
---|
53 | # define CMDDATA 3 /* data -- send message text */ |
---|
54 | # define CMDRSET 4 /* rset -- reset state */ |
---|
55 | # define CMDVRFY 5 /* vrfy -- verify address */ |
---|
56 | # define CMDEXPN 6 /* expn -- expand address */ |
---|
57 | # define CMDNOOP 7 /* noop -- do nothing */ |
---|
58 | # define CMDQUIT 8 /* quit -- close connection and die */ |
---|
59 | # define CMDHELO 9 /* helo -- be polite */ |
---|
60 | # define CMDHELP 10 /* help -- give usage info */ |
---|
61 | # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ |
---|
62 | # define CMDETRN 12 /* etrn -- flush queue */ |
---|
63 | /* non-standard commands */ |
---|
64 | # define CMDONEX 16 /* onex -- sending one transaction only */ |
---|
65 | # define CMDVERB 17 /* verb -- go into verbose mode */ |
---|
66 | # define CMDXUSR 18 /* xusr -- initial (user) submission */ |
---|
67 | /* use this to catch and log "door handle" attempts on your system */ |
---|
68 | # define CMDLOGBOGUS 23 /* bogus command that should be logged */ |
---|
69 | /* debugging-only commands, only enabled if SMTPDEBUG is defined */ |
---|
70 | # define CMDDBGQSHOW 24 /* showq -- show send queue */ |
---|
71 | # define CMDDBGDEBUG 25 /* debug -- set debug mode */ |
---|
72 | |
---|
73 | static struct cmd CmdTab[] = |
---|
74 | { |
---|
75 | { "mail", CMDMAIL }, |
---|
76 | { "rcpt", CMDRCPT }, |
---|
77 | { "data", CMDDATA }, |
---|
78 | { "rset", CMDRSET }, |
---|
79 | { "vrfy", CMDVRFY }, |
---|
80 | { "expn", CMDEXPN }, |
---|
81 | { "help", CMDHELP }, |
---|
82 | { "noop", CMDNOOP }, |
---|
83 | { "quit", CMDQUIT }, |
---|
84 | { "helo", CMDHELO }, |
---|
85 | { "ehlo", CMDEHLO }, |
---|
86 | { "etrn", CMDETRN }, |
---|
87 | { "verb", CMDVERB }, |
---|
88 | { "onex", CMDONEX }, |
---|
89 | { "xusr", CMDXUSR }, |
---|
90 | /* remaining commands are here only to trap and log attempts to use them */ |
---|
91 | { "showq", CMDDBGQSHOW }, |
---|
92 | { "debug", CMDDBGDEBUG }, |
---|
93 | { "wiz", CMDLOGBOGUS }, |
---|
94 | |
---|
95 | { NULL, CMDERROR } |
---|
96 | }; |
---|
97 | |
---|
98 | bool OneXact = FALSE; /* one xaction only this run */ |
---|
99 | char *CurSmtpClient; /* who's at the other end of channel */ |
---|
100 | |
---|
101 | static char *skipword __P((char *volatile, char *)); |
---|
102 | |
---|
103 | |
---|
104 | #define MAXBADCOMMANDS 25 /* maximum number of bad commands */ |
---|
105 | #define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */ |
---|
106 | #define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */ |
---|
107 | #define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */ |
---|
108 | #define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */ |
---|
109 | |
---|
110 | void |
---|
111 | smtp(nullserver, e) |
---|
112 | char *nullserver; |
---|
113 | register ENVELOPE *volatile e; |
---|
114 | { |
---|
115 | register char *volatile p; |
---|
116 | register struct cmd *c; |
---|
117 | char *cmd; |
---|
118 | auto ADDRESS *vrfyqueue; |
---|
119 | ADDRESS *a; |
---|
120 | volatile bool gotmail; /* mail command received */ |
---|
121 | volatile bool gothello; /* helo command received */ |
---|
122 | bool vrfy; /* set if this is a vrfy command */ |
---|
123 | char *volatile protocol; /* sending protocol */ |
---|
124 | char *volatile sendinghost; /* sending hostname */ |
---|
125 | char *volatile peerhostname; /* name of SMTP peer or "localhost" */ |
---|
126 | auto char *delimptr; |
---|
127 | char *id; |
---|
128 | volatile int nrcpts = 0; /* number of RCPT commands */ |
---|
129 | bool doublequeue; |
---|
130 | volatile bool discard; |
---|
131 | volatile int badcommands = 0; /* count of bad commands */ |
---|
132 | volatile int nverifies = 0; /* count of VRFY/EXPN commands */ |
---|
133 | volatile int n_etrn = 0; /* count of ETRN commands */ |
---|
134 | volatile int n_noop = 0; /* count of NOOP/VERB/ONEX etc cmds */ |
---|
135 | volatile int n_helo = 0; /* count of HELO/EHLO commands */ |
---|
136 | bool ok; |
---|
137 | volatile int lognullconnection = TRUE; |
---|
138 | register char *q; |
---|
139 | QUEUE_CHAR *new; |
---|
140 | char inp[MAXLINE]; |
---|
141 | char cmdbuf[MAXLINE]; |
---|
142 | extern ENVELOPE BlankEnvelope; |
---|
143 | extern void help __P((char *)); |
---|
144 | extern void settime __P((ENVELOPE *)); |
---|
145 | extern bool enoughdiskspace __P((long)); |
---|
146 | extern int runinchild __P((char *, ENVELOPE *)); |
---|
147 | extern void checksmtpattack __P((volatile int *, int, char *, ENVELOPE *)); |
---|
148 | |
---|
149 | if (fileno(OutChannel) != fileno(stdout)) |
---|
150 | { |
---|
151 | /* arrange for debugging output to go to remote host */ |
---|
152 | (void) dup2(fileno(OutChannel), fileno(stdout)); |
---|
153 | } |
---|
154 | settime(e); |
---|
155 | peerhostname = RealHostName; |
---|
156 | if (peerhostname == NULL) |
---|
157 | peerhostname = "localhost"; |
---|
158 | CurHostName = peerhostname; |
---|
159 | CurSmtpClient = macvalue('_', e); |
---|
160 | if (CurSmtpClient == NULL) |
---|
161 | CurSmtpClient = CurHostName; |
---|
162 | |
---|
163 | /* check_relay may have set discard bit, save for later */ |
---|
164 | discard = bitset(EF_DISCARD, e->e_flags); |
---|
165 | |
---|
166 | sm_setproctitle(TRUE, "server %s startup", CurSmtpClient); |
---|
167 | #if DAEMON |
---|
168 | if (LogLevel > 11) |
---|
169 | { |
---|
170 | /* log connection information */ |
---|
171 | sm_syslog(LOG_INFO, NOQID, |
---|
172 | "SMTP connect from %.100s (%.100s)", |
---|
173 | CurSmtpClient, anynet_ntoa(&RealHostAddr)); |
---|
174 | } |
---|
175 | #endif |
---|
176 | |
---|
177 | /* output the first line, inserting "ESMTP" as second word */ |
---|
178 | expand(SmtpGreeting, inp, sizeof inp, e); |
---|
179 | p = strchr(inp, '\n'); |
---|
180 | if (p != NULL) |
---|
181 | *p++ = '\0'; |
---|
182 | id = strchr(inp, ' '); |
---|
183 | if (id == NULL) |
---|
184 | id = &inp[strlen(inp)]; |
---|
185 | cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s"; |
---|
186 | message(cmd, id - inp, inp, id); |
---|
187 | |
---|
188 | /* output remaining lines */ |
---|
189 | while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL) |
---|
190 | { |
---|
191 | *p++ = '\0'; |
---|
192 | if (isascii(*id) && isspace(*id)) |
---|
193 | id++; |
---|
194 | message("220-%s", id); |
---|
195 | } |
---|
196 | if (id != NULL) |
---|
197 | { |
---|
198 | if (isascii(*id) && isspace(*id)) |
---|
199 | id++; |
---|
200 | message("220 %s", id); |
---|
201 | } |
---|
202 | |
---|
203 | protocol = NULL; |
---|
204 | sendinghost = macvalue('s', e); |
---|
205 | gothello = FALSE; |
---|
206 | gotmail = FALSE; |
---|
207 | for (;;) |
---|
208 | { |
---|
209 | /* arrange for backout */ |
---|
210 | (void) setjmp(TopFrame); |
---|
211 | QuickAbort = FALSE; |
---|
212 | HoldErrs = FALSE; |
---|
213 | SuprErrs = FALSE; |
---|
214 | LogUsrErrs = FALSE; |
---|
215 | OnlyOneError = TRUE; |
---|
216 | e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); |
---|
217 | |
---|
218 | /* setup for the read */ |
---|
219 | e->e_to = NULL; |
---|
220 | Errors = 0; |
---|
221 | (void) fflush(stdout); |
---|
222 | |
---|
223 | /* read the input line */ |
---|
224 | SmtpPhase = "server cmd read"; |
---|
225 | sm_setproctitle(TRUE, "server %s cmd read", CurSmtpClient); |
---|
226 | p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, |
---|
227 | SmtpPhase); |
---|
228 | |
---|
229 | /* handle errors */ |
---|
230 | if (p == NULL) |
---|
231 | { |
---|
232 | /* end of file, just die */ |
---|
233 | disconnect(1, e); |
---|
234 | message("421 %s Lost input channel from %s", |
---|
235 | MyHostName, CurSmtpClient); |
---|
236 | if (LogLevel > (gotmail ? 1 : 19)) |
---|
237 | sm_syslog(LOG_NOTICE, e->e_id, |
---|
238 | "lost input channel from %.100s", |
---|
239 | CurSmtpClient); |
---|
240 | if (lognullconnection && LogLevel > 5) |
---|
241 | sm_syslog(LOG_INFO, NULL, |
---|
242 | "Null connection from %.100s", |
---|
243 | CurSmtpClient); |
---|
244 | |
---|
245 | /* |
---|
246 | ** If have not accepted mail (DATA), do not bounce |
---|
247 | ** bad addresses back to sender. |
---|
248 | */ |
---|
249 | if (bitset(EF_CLRQUEUE, e->e_flags)) |
---|
250 | e->e_sendqueue = NULL; |
---|
251 | |
---|
252 | if (InChild) |
---|
253 | ExitStat = EX_QUIT; |
---|
254 | finis(TRUE, ExitStat); |
---|
255 | } |
---|
256 | |
---|
257 | /* clean up end of line */ |
---|
258 | fixcrlf(inp, TRUE); |
---|
259 | |
---|
260 | /* echo command to transcript */ |
---|
261 | if (e->e_xfp != NULL) |
---|
262 | fprintf(e->e_xfp, "<<< %s\n", inp); |
---|
263 | |
---|
264 | if (LogLevel >= 15) |
---|
265 | sm_syslog(LOG_INFO, e->e_id, |
---|
266 | "<-- %s", |
---|
267 | inp); |
---|
268 | |
---|
269 | if (e->e_id == NULL) |
---|
270 | sm_setproctitle(TRUE, "%s: %.80s", CurSmtpClient, inp); |
---|
271 | else |
---|
272 | sm_setproctitle(TRUE, "%s %s: %.80s", e->e_id, CurSmtpClient, inp); |
---|
273 | |
---|
274 | /* break off command */ |
---|
275 | for (p = inp; isascii(*p) && isspace(*p); p++) |
---|
276 | continue; |
---|
277 | cmd = cmdbuf; |
---|
278 | while (*p != '\0' && |
---|
279 | !(isascii(*p) && isspace(*p)) && |
---|
280 | cmd < &cmdbuf[sizeof cmdbuf - 2]) |
---|
281 | *cmd++ = *p++; |
---|
282 | *cmd = '\0'; |
---|
283 | |
---|
284 | /* throw away leading whitespace */ |
---|
285 | while (isascii(*p) && isspace(*p)) |
---|
286 | p++; |
---|
287 | |
---|
288 | /* decode command */ |
---|
289 | for (c = CmdTab; c->cmdname != NULL; c++) |
---|
290 | { |
---|
291 | if (!strcasecmp(c->cmdname, cmdbuf)) |
---|
292 | break; |
---|
293 | } |
---|
294 | |
---|
295 | /* reset errors */ |
---|
296 | errno = 0; |
---|
297 | |
---|
298 | /* |
---|
299 | ** Process command. |
---|
300 | ** |
---|
301 | ** If we are running as a null server, return 550 |
---|
302 | ** to everything. |
---|
303 | */ |
---|
304 | |
---|
305 | if (nullserver != NULL) |
---|
306 | { |
---|
307 | switch (c->cmdcode) |
---|
308 | { |
---|
309 | case CMDQUIT: |
---|
310 | case CMDHELO: |
---|
311 | case CMDEHLO: |
---|
312 | case CMDNOOP: |
---|
313 | /* process normally */ |
---|
314 | break; |
---|
315 | |
---|
316 | default: |
---|
317 | if (++badcommands > MAXBADCOMMANDS) |
---|
318 | sleep(1); |
---|
319 | usrerr("550 %s", nullserver); |
---|
320 | continue; |
---|
321 | } |
---|
322 | } |
---|
323 | |
---|
324 | /* non-null server */ |
---|
325 | switch (c->cmdcode) |
---|
326 | { |
---|
327 | case CMDMAIL: |
---|
328 | case CMDEXPN: |
---|
329 | case CMDVRFY: |
---|
330 | case CMDETRN: |
---|
331 | lognullconnection = FALSE; |
---|
332 | } |
---|
333 | |
---|
334 | switch (c->cmdcode) |
---|
335 | { |
---|
336 | case CMDHELO: /* hello -- introduce yourself */ |
---|
337 | case CMDEHLO: /* extended hello */ |
---|
338 | if (c->cmdcode == CMDEHLO) |
---|
339 | { |
---|
340 | protocol = "ESMTP"; |
---|
341 | SmtpPhase = "server EHLO"; |
---|
342 | } |
---|
343 | else |
---|
344 | { |
---|
345 | protocol = "SMTP"; |
---|
346 | SmtpPhase = "server HELO"; |
---|
347 | } |
---|
348 | |
---|
349 | /* avoid denial-of-service */ |
---|
350 | checksmtpattack(&n_helo, MAXHELOCOMMANDS, "HELO/EHLO", e); |
---|
351 | |
---|
352 | /* check for duplicate HELO/EHLO per RFC 1651 4.2 */ |
---|
353 | if (gothello) |
---|
354 | { |
---|
355 | usrerr("503 %s Duplicate HELO/EHLO", |
---|
356 | MyHostName); |
---|
357 | break; |
---|
358 | } |
---|
359 | |
---|
360 | /* check for valid domain name (re 1123 5.2.5) */ |
---|
361 | if (*p == '\0' && !AllowBogusHELO) |
---|
362 | { |
---|
363 | usrerr("501 %s requires domain address", |
---|
364 | cmdbuf); |
---|
365 | break; |
---|
366 | } |
---|
367 | |
---|
368 | /* check for long domain name (hides Received: info) */ |
---|
369 | if (strlen(p) > MAXNAME) |
---|
370 | { |
---|
371 | usrerr("501 Invalid domain name"); |
---|
372 | break; |
---|
373 | } |
---|
374 | |
---|
375 | for (q = p; *q != '\0'; q++) |
---|
376 | { |
---|
377 | if (!isascii(*q)) |
---|
378 | break; |
---|
379 | if (isalnum(*q)) |
---|
380 | continue; |
---|
381 | if (isspace(*q)) |
---|
382 | { |
---|
383 | *q = '\0'; |
---|
384 | break; |
---|
385 | } |
---|
386 | if (strchr("[].-_#", *q) == NULL) |
---|
387 | break; |
---|
388 | } |
---|
389 | if (*q == '\0') |
---|
390 | { |
---|
391 | q = "pleased to meet you"; |
---|
392 | sendinghost = newstr(p); |
---|
393 | } |
---|
394 | else if (!AllowBogusHELO) |
---|
395 | { |
---|
396 | usrerr("501 Invalid domain name"); |
---|
397 | break; |
---|
398 | } |
---|
399 | else |
---|
400 | { |
---|
401 | q = "accepting invalid domain name"; |
---|
402 | } |
---|
403 | |
---|
404 | gothello = TRUE; |
---|
405 | |
---|
406 | /* print HELO response message */ |
---|
407 | if (c->cmdcode != CMDEHLO || nullserver != NULL) |
---|
408 | { |
---|
409 | message("250 %s Hello %s, %s", |
---|
410 | MyHostName, CurSmtpClient, q); |
---|
411 | break; |
---|
412 | } |
---|
413 | |
---|
414 | message("250-%s Hello %s, %s", |
---|
415 | MyHostName, CurSmtpClient, q); |
---|
416 | |
---|
417 | /* print EHLO features list */ |
---|
418 | if (!bitset(PRIV_NOEXPN, PrivacyFlags)) |
---|
419 | { |
---|
420 | message("250-EXPN"); |
---|
421 | if (!bitset(PRIV_NOVERB, PrivacyFlags)) |
---|
422 | message("250-VERB"); |
---|
423 | } |
---|
424 | #if MIME8TO7 |
---|
425 | message("250-8BITMIME"); |
---|
426 | #endif |
---|
427 | if (MaxMessageSize > 0) |
---|
428 | message("250-SIZE %ld", MaxMessageSize); |
---|
429 | else |
---|
430 | message("250-SIZE"); |
---|
431 | #if DSN |
---|
432 | if (SendMIMEErrors) |
---|
433 | message("250-DSN"); |
---|
434 | #endif |
---|
435 | message("250-ONEX"); |
---|
436 | if (!bitset(PRIV_NOETRN, PrivacyFlags)) |
---|
437 | message("250-ETRN"); |
---|
438 | message("250-XUSR"); |
---|
439 | message("250 HELP"); |
---|
440 | break; |
---|
441 | |
---|
442 | case CMDMAIL: /* mail -- designate sender */ |
---|
443 | SmtpPhase = "server MAIL"; |
---|
444 | |
---|
445 | /* check for validity of this command */ |
---|
446 | if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) |
---|
447 | { |
---|
448 | usrerr("503 Polite people say HELO first"); |
---|
449 | break; |
---|
450 | } |
---|
451 | if (gotmail) |
---|
452 | { |
---|
453 | usrerr("503 Sender already specified"); |
---|
454 | break; |
---|
455 | } |
---|
456 | if (InChild) |
---|
457 | { |
---|
458 | errno = 0; |
---|
459 | syserr("503 Nested MAIL command: MAIL %s", p); |
---|
460 | finis(TRUE, ExitStat); |
---|
461 | } |
---|
462 | |
---|
463 | /* make sure we know who the sending host is */ |
---|
464 | if (sendinghost == NULL) |
---|
465 | sendinghost = peerhostname; |
---|
466 | |
---|
467 | p = skipword(p, "from"); |
---|
468 | if (p == NULL) |
---|
469 | break; |
---|
470 | |
---|
471 | /* fork a subprocess to process this command */ |
---|
472 | if (runinchild("SMTP-MAIL", e) > 0) |
---|
473 | break; |
---|
474 | if (Errors > 0) |
---|
475 | goto undo_subproc_no_pm; |
---|
476 | if (!gothello) |
---|
477 | { |
---|
478 | auth_warning(e, |
---|
479 | "%s didn't use HELO protocol", |
---|
480 | CurSmtpClient); |
---|
481 | } |
---|
482 | #ifdef PICKY_HELO_CHECK |
---|
483 | if (strcasecmp(sendinghost, peerhostname) != 0 && |
---|
484 | (strcasecmp(peerhostname, "localhost") != 0 || |
---|
485 | strcasecmp(sendinghost, MyHostName) != 0)) |
---|
486 | { |
---|
487 | auth_warning(e, "Host %s claimed to be %s", |
---|
488 | CurSmtpClient, sendinghost); |
---|
489 | } |
---|
490 | #endif |
---|
491 | |
---|
492 | if (protocol == NULL) |
---|
493 | protocol = "SMTP"; |
---|
494 | define('r', protocol, e); |
---|
495 | define('s', sendinghost, e); |
---|
496 | initsys(e); |
---|
497 | if (Errors > 0) |
---|
498 | goto undo_subproc_no_pm; |
---|
499 | nrcpts = 0; |
---|
500 | e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE; |
---|
501 | sm_setproctitle(TRUE, "%s %s: %.80s", e->e_id, CurSmtpClient, inp); |
---|
502 | |
---|
503 | /* child -- go do the processing */ |
---|
504 | if (setjmp(TopFrame) > 0) |
---|
505 | { |
---|
506 | /* this failed -- undo work */ |
---|
507 | undo_subproc_no_pm: |
---|
508 | e->e_flags &= ~EF_PM_NOTIFY; |
---|
509 | undo_subproc: |
---|
510 | if (InChild) |
---|
511 | { |
---|
512 | QuickAbort = FALSE; |
---|
513 | SuprErrs = TRUE; |
---|
514 | e->e_flags &= ~EF_FATALERRS; |
---|
515 | finis(TRUE, ExitStat); |
---|
516 | } |
---|
517 | break; |
---|
518 | } |
---|
519 | QuickAbort = TRUE; |
---|
520 | |
---|
521 | /* must parse sender first */ |
---|
522 | delimptr = NULL; |
---|
523 | setsender(p, e, &delimptr, ' ', FALSE); |
---|
524 | if (delimptr != NULL && *delimptr != '\0') |
---|
525 | *delimptr++ = '\0'; |
---|
526 | if (Errors > 0) |
---|
527 | goto undo_subproc_no_pm; |
---|
528 | |
---|
529 | /* do config file checking of the sender */ |
---|
530 | if (rscheck("check_mail", p, NULL, e) != EX_OK || |
---|
531 | Errors > 0) |
---|
532 | goto undo_subproc_no_pm; |
---|
533 | |
---|
534 | /* check for possible spoofing */ |
---|
535 | if (RealUid != 0 && OpMode == MD_SMTP && |
---|
536 | !wordinclass(RealUserName, 't') && |
---|
537 | !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && |
---|
538 | strcmp(e->e_from.q_user, RealUserName) != 0) |
---|
539 | { |
---|
540 | auth_warning(e, "%s owned process doing -bs", |
---|
541 | RealUserName); |
---|
542 | } |
---|
543 | |
---|
544 | /* now parse ESMTP arguments */ |
---|
545 | e->e_msgsize = 0; |
---|
546 | p = delimptr; |
---|
547 | while (p != NULL && *p != '\0') |
---|
548 | { |
---|
549 | char *kp; |
---|
550 | char *vp = NULL; |
---|
551 | extern void mail_esmtp_args __P((char *, char *, ENVELOPE *)); |
---|
552 | |
---|
553 | /* locate the beginning of the keyword */ |
---|
554 | while (isascii(*p) && isspace(*p)) |
---|
555 | p++; |
---|
556 | if (*p == '\0') |
---|
557 | break; |
---|
558 | kp = p; |
---|
559 | |
---|
560 | /* skip to the value portion */ |
---|
561 | while ((isascii(*p) && isalnum(*p)) || *p == '-') |
---|
562 | p++; |
---|
563 | if (*p == '=') |
---|
564 | { |
---|
565 | *p++ = '\0'; |
---|
566 | vp = p; |
---|
567 | |
---|
568 | /* skip to the end of the value */ |
---|
569 | while (*p != '\0' && *p != ' ' && |
---|
570 | !(isascii(*p) && iscntrl(*p)) && |
---|
571 | *p != '=') |
---|
572 | p++; |
---|
573 | } |
---|
574 | |
---|
575 | if (*p != '\0') |
---|
576 | *p++ = '\0'; |
---|
577 | |
---|
578 | if (tTd(19, 1)) |
---|
579 | printf("MAIL: got arg %s=\"%s\"\n", kp, |
---|
580 | vp == NULL ? "<null>" : vp); |
---|
581 | |
---|
582 | mail_esmtp_args(kp, vp, e); |
---|
583 | if (Errors > 0) |
---|
584 | goto undo_subproc_no_pm; |
---|
585 | } |
---|
586 | if (Errors > 0) |
---|
587 | goto undo_subproc_no_pm; |
---|
588 | |
---|
589 | if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) |
---|
590 | { |
---|
591 | usrerr("552 Message size exceeds fixed maximum message size (%ld)", |
---|
592 | MaxMessageSize); |
---|
593 | goto undo_subproc_no_pm; |
---|
594 | } |
---|
595 | |
---|
596 | if (!enoughdiskspace(e->e_msgsize)) |
---|
597 | { |
---|
598 | usrerr("452 Insufficient disk space; try again later"); |
---|
599 | goto undo_subproc_no_pm; |
---|
600 | } |
---|
601 | if (Errors > 0) |
---|
602 | goto undo_subproc_no_pm; |
---|
603 | message("250 Sender ok"); |
---|
604 | gotmail = TRUE; |
---|
605 | break; |
---|
606 | |
---|
607 | case CMDRCPT: /* rcpt -- designate recipient */ |
---|
608 | if (!gotmail) |
---|
609 | { |
---|
610 | usrerr("503 Need MAIL before RCPT"); |
---|
611 | break; |
---|
612 | } |
---|
613 | SmtpPhase = "server RCPT"; |
---|
614 | if (setjmp(TopFrame) > 0) |
---|
615 | { |
---|
616 | e->e_flags &= ~EF_FATALERRS; |
---|
617 | break; |
---|
618 | } |
---|
619 | QuickAbort = TRUE; |
---|
620 | LogUsrErrs = TRUE; |
---|
621 | |
---|
622 | /* limit flooding of our machine */ |
---|
623 | if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg) |
---|
624 | { |
---|
625 | usrerr("452 Too many recipients"); |
---|
626 | break; |
---|
627 | } |
---|
628 | |
---|
629 | if (e->e_sendmode != SM_DELIVER) |
---|
630 | e->e_flags |= EF_VRFYONLY; |
---|
631 | |
---|
632 | p = skipword(p, "to"); |
---|
633 | if (p == NULL) |
---|
634 | break; |
---|
635 | a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); |
---|
636 | if (Errors > 0) |
---|
637 | break; |
---|
638 | if (a == NULL) |
---|
639 | { |
---|
640 | usrerr("501 Missing recipient"); |
---|
641 | break; |
---|
642 | } |
---|
643 | |
---|
644 | if (delimptr != NULL && *delimptr != '\0') |
---|
645 | *delimptr++ = '\0'; |
---|
646 | |
---|
647 | /* do config file checking of the recipient */ |
---|
648 | if (rscheck("check_rcpt", p, NULL, e) != EX_OK || |
---|
649 | Errors > 0) |
---|
650 | break; |
---|
651 | |
---|
652 | /* now parse ESMTP arguments */ |
---|
653 | p = delimptr; |
---|
654 | while (p != NULL && *p != '\0') |
---|
655 | { |
---|
656 | char *kp; |
---|
657 | char *vp = NULL; |
---|
658 | extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *)); |
---|
659 | |
---|
660 | /* locate the beginning of the keyword */ |
---|
661 | while (isascii(*p) && isspace(*p)) |
---|
662 | p++; |
---|
663 | if (*p == '\0') |
---|
664 | break; |
---|
665 | kp = p; |
---|
666 | |
---|
667 | /* skip to the value portion */ |
---|
668 | while ((isascii(*p) && isalnum(*p)) || *p == '-') |
---|
669 | p++; |
---|
670 | if (*p == '=') |
---|
671 | { |
---|
672 | *p++ = '\0'; |
---|
673 | vp = p; |
---|
674 | |
---|
675 | /* skip to the end of the value */ |
---|
676 | while (*p != '\0' && *p != ' ' && |
---|
677 | !(isascii(*p) && iscntrl(*p)) && |
---|
678 | *p != '=') |
---|
679 | p++; |
---|
680 | } |
---|
681 | |
---|
682 | if (*p != '\0') |
---|
683 | *p++ = '\0'; |
---|
684 | |
---|
685 | if (tTd(19, 1)) |
---|
686 | printf("RCPT: got arg %s=\"%s\"\n", kp, |
---|
687 | vp == NULL ? "<null>" : vp); |
---|
688 | |
---|
689 | rcpt_esmtp_args(a, kp, vp, e); |
---|
690 | if (Errors > 0) |
---|
691 | break; |
---|
692 | } |
---|
693 | if (Errors > 0) |
---|
694 | break; |
---|
695 | |
---|
696 | /* save in recipient list after ESMTP mods */ |
---|
697 | a = recipient(a, &e->e_sendqueue, 0, e); |
---|
698 | if (Errors > 0) |
---|
699 | break; |
---|
700 | |
---|
701 | /* no errors during parsing, but might be a duplicate */ |
---|
702 | e->e_to = a->q_paddr; |
---|
703 | if (!bitset(QBADADDR, a->q_flags)) |
---|
704 | { |
---|
705 | message("250 Recipient ok%s", |
---|
706 | bitset(QQUEUEUP, a->q_flags) ? |
---|
707 | " (will queue)" : ""); |
---|
708 | nrcpts++; |
---|
709 | } |
---|
710 | else |
---|
711 | { |
---|
712 | /* punt -- should keep message in ADDRESS.... */ |
---|
713 | usrerr("550 Addressee unknown"); |
---|
714 | } |
---|
715 | break; |
---|
716 | |
---|
717 | case CMDDATA: /* data -- text of mail */ |
---|
718 | SmtpPhase = "server DATA"; |
---|
719 | if (!gotmail) |
---|
720 | { |
---|
721 | usrerr("503 Need MAIL command"); |
---|
722 | break; |
---|
723 | } |
---|
724 | else if (nrcpts <= 0) |
---|
725 | { |
---|
726 | usrerr("503 Need RCPT (recipient)"); |
---|
727 | break; |
---|
728 | } |
---|
729 | |
---|
730 | /* put back discard bit */ |
---|
731 | if (discard) |
---|
732 | e->e_flags |= EF_DISCARD; |
---|
733 | |
---|
734 | /* check to see if we need to re-expand aliases */ |
---|
735 | /* also reset QBADADDR on already-diagnosted addrs */ |
---|
736 | doublequeue = FALSE; |
---|
737 | for (a = e->e_sendqueue; a != NULL; a = a->q_next) |
---|
738 | { |
---|
739 | if (bitset(QVERIFIED, a->q_flags) && |
---|
740 | !bitset(EF_DISCARD, e->e_flags)) |
---|
741 | { |
---|
742 | /* need to re-expand aliases */ |
---|
743 | doublequeue = TRUE; |
---|
744 | } |
---|
745 | if (bitset(QBADADDR, a->q_flags)) |
---|
746 | { |
---|
747 | /* make this "go away" */ |
---|
748 | a->q_flags |= QDONTSEND; |
---|
749 | a->q_flags &= ~QBADADDR; |
---|
750 | } |
---|
751 | } |
---|
752 | |
---|
753 | /* collect the text of the message */ |
---|
754 | SmtpPhase = "collect"; |
---|
755 | buffer_errors(); |
---|
756 | collect(InChannel, TRUE, NULL, e); |
---|
757 | if (Errors > 0) |
---|
758 | { |
---|
759 | flush_errors(TRUE); |
---|
760 | buffer_errors(); |
---|
761 | goto abortmessage; |
---|
762 | } |
---|
763 | |
---|
764 | /* make sure we actually do delivery */ |
---|
765 | e->e_flags &= ~EF_CLRQUEUE; |
---|
766 | |
---|
767 | /* from now on, we have to operate silently */ |
---|
768 | buffer_errors(); |
---|
769 | e->e_errormode = EM_MAIL; |
---|
770 | |
---|
771 | /* |
---|
772 | ** Arrange to send to everyone. |
---|
773 | ** If sending to multiple people, mail back |
---|
774 | ** errors rather than reporting directly. |
---|
775 | ** In any case, don't mail back errors for |
---|
776 | ** anything that has happened up to |
---|
777 | ** now (the other end will do this). |
---|
778 | ** Truncate our transcript -- the mail has gotten |
---|
779 | ** to us successfully, and if we have |
---|
780 | ** to mail this back, it will be easier |
---|
781 | ** on the reader. |
---|
782 | ** Then send to everyone. |
---|
783 | ** Finally give a reply code. If an error has |
---|
784 | ** already been given, don't mail a |
---|
785 | ** message back. |
---|
786 | ** We goose error returns by clearing error bit. |
---|
787 | */ |
---|
788 | |
---|
789 | SmtpPhase = "delivery"; |
---|
790 | e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); |
---|
791 | id = e->e_id; |
---|
792 | |
---|
793 | if (doublequeue) |
---|
794 | { |
---|
795 | /* make sure it is in the queue */ |
---|
796 | queueup(e, FALSE); |
---|
797 | } |
---|
798 | else |
---|
799 | { |
---|
800 | /* send to all recipients */ |
---|
801 | sendall(e, SM_DEFAULT); |
---|
802 | } |
---|
803 | e->e_to = NULL; |
---|
804 | |
---|
805 | /* issue success message */ |
---|
806 | message("250 %s Message accepted for delivery", id); |
---|
807 | |
---|
808 | /* if we just queued, poke it */ |
---|
809 | if (doublequeue && |
---|
810 | e->e_sendmode != SM_QUEUE && |
---|
811 | e->e_sendmode != SM_DEFER) |
---|
812 | { |
---|
813 | CurrentLA = getla(); |
---|
814 | |
---|
815 | if (!shouldqueue(e->e_msgpriority, e->e_ctime)) |
---|
816 | { |
---|
817 | unlockqueue(e); |
---|
818 | (void) dowork(id, TRUE, TRUE, e); |
---|
819 | } |
---|
820 | } |
---|
821 | |
---|
822 | abortmessage: |
---|
823 | /* if in a child, pop back to our parent */ |
---|
824 | if (InChild) |
---|
825 | finis(TRUE, ExitStat); |
---|
826 | |
---|
827 | /* clean up a bit */ |
---|
828 | gotmail = FALSE; |
---|
829 | dropenvelope(e, TRUE); |
---|
830 | CurEnv = e = newenvelope(e, CurEnv); |
---|
831 | e->e_flags = BlankEnvelope.e_flags; |
---|
832 | break; |
---|
833 | |
---|
834 | case CMDRSET: /* rset -- reset state */ |
---|
835 | if (tTd(94, 100)) |
---|
836 | message("451 Test failure"); |
---|
837 | else |
---|
838 | message("250 Reset state"); |
---|
839 | |
---|
840 | /* arrange to ignore any current send list */ |
---|
841 | e->e_sendqueue = NULL; |
---|
842 | e->e_flags |= EF_CLRQUEUE; |
---|
843 | if (InChild) |
---|
844 | finis(TRUE, ExitStat); |
---|
845 | |
---|
846 | /* clean up a bit */ |
---|
847 | gotmail = FALSE; |
---|
848 | SuprErrs = TRUE; |
---|
849 | dropenvelope(e, TRUE); |
---|
850 | CurEnv = e = newenvelope(e, CurEnv); |
---|
851 | break; |
---|
852 | |
---|
853 | case CMDVRFY: /* vrfy -- verify address */ |
---|
854 | case CMDEXPN: /* expn -- expand address */ |
---|
855 | checksmtpattack(&nverifies, MAXVRFYCOMMANDS, |
---|
856 | c->cmdcode == CMDVRFY ? "VRFY" : "EXPN", e); |
---|
857 | vrfy = c->cmdcode == CMDVRFY; |
---|
858 | if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, |
---|
859 | PrivacyFlags)) |
---|
860 | { |
---|
861 | if (vrfy) |
---|
862 | message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); |
---|
863 | else |
---|
864 | message("502 Sorry, we do not allow this operation"); |
---|
865 | if (LogLevel > 5) |
---|
866 | sm_syslog(LOG_INFO, e->e_id, |
---|
867 | "%.100s: %s [rejected]", |
---|
868 | CurSmtpClient, |
---|
869 | shortenstring(inp, MAXSHORTSTR)); |
---|
870 | break; |
---|
871 | } |
---|
872 | else if (!gothello && |
---|
873 | bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, |
---|
874 | PrivacyFlags)) |
---|
875 | { |
---|
876 | usrerr("503 I demand that you introduce yourself first"); |
---|
877 | break; |
---|
878 | } |
---|
879 | if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) |
---|
880 | break; |
---|
881 | if (Errors > 0) |
---|
882 | goto undo_subproc; |
---|
883 | if (LogLevel > 5) |
---|
884 | sm_syslog(LOG_INFO, e->e_id, |
---|
885 | "%.100s: %s", |
---|
886 | CurSmtpClient, |
---|
887 | shortenstring(inp, MAXSHORTSTR)); |
---|
888 | if (setjmp(TopFrame) > 0) |
---|
889 | goto undo_subproc; |
---|
890 | QuickAbort = TRUE; |
---|
891 | vrfyqueue = NULL; |
---|
892 | if (vrfy) |
---|
893 | e->e_flags |= EF_VRFYONLY; |
---|
894 | while (*p != '\0' && isascii(*p) && isspace(*p)) |
---|
895 | p++; |
---|
896 | if (*p == '\0') |
---|
897 | { |
---|
898 | usrerr("501 Argument required"); |
---|
899 | } |
---|
900 | else |
---|
901 | { |
---|
902 | (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); |
---|
903 | } |
---|
904 | if (Errors > 0) |
---|
905 | goto undo_subproc; |
---|
906 | if (vrfyqueue == NULL) |
---|
907 | { |
---|
908 | usrerr("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); |
---|
909 | } |
---|
910 | while (vrfyqueue != NULL) |
---|
911 | { |
---|
912 | extern void printvrfyaddr __P((ADDRESS *, bool, bool)); |
---|
913 | |
---|
914 | a = vrfyqueue; |
---|
915 | while ((a = a->q_next) != NULL && |
---|
916 | bitset(QDONTSEND|QBADADDR, a->q_flags)) |
---|
917 | continue; |
---|
918 | if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) |
---|
919 | printvrfyaddr(vrfyqueue, a == NULL, vrfy); |
---|
920 | vrfyqueue = vrfyqueue->q_next; |
---|
921 | } |
---|
922 | if (InChild) |
---|
923 | finis(TRUE, ExitStat); |
---|
924 | break; |
---|
925 | |
---|
926 | case CMDETRN: /* etrn -- force queue flush */ |
---|
927 | if (bitset(PRIV_NOETRN, PrivacyFlags)) |
---|
928 | { |
---|
929 | message("502 Sorry, we do not allow this operation"); |
---|
930 | if (LogLevel > 5) |
---|
931 | sm_syslog(LOG_INFO, e->e_id, |
---|
932 | "%.100s: %s [rejected]", |
---|
933 | CurSmtpClient, |
---|
934 | shortenstring(inp, MAXSHORTSTR)); |
---|
935 | break; |
---|
936 | } |
---|
937 | |
---|
938 | if (strlen(p) <= 0) |
---|
939 | { |
---|
940 | usrerr("500 Parameter required"); |
---|
941 | break; |
---|
942 | } |
---|
943 | |
---|
944 | /* crude way to avoid denial-of-service attacks */ |
---|
945 | checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN", e); |
---|
946 | |
---|
947 | if (LogLevel > 5) |
---|
948 | sm_syslog(LOG_INFO, e->e_id, |
---|
949 | "%.100s: ETRN %s", |
---|
950 | CurSmtpClient, |
---|
951 | shortenstring(p, MAXSHORTSTR)); |
---|
952 | |
---|
953 | id = p; |
---|
954 | if (*id == '@') |
---|
955 | id++; |
---|
956 | else |
---|
957 | *--id = '@'; |
---|
958 | |
---|
959 | if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL) |
---|
960 | { |
---|
961 | syserr("500 ETRN out of memory"); |
---|
962 | break; |
---|
963 | } |
---|
964 | new->queue_match = id; |
---|
965 | new->queue_next = NULL; |
---|
966 | QueueLimitRecipient = new; |
---|
967 | ok = runqueue(TRUE, TRUE); |
---|
968 | free(QueueLimitRecipient); |
---|
969 | QueueLimitRecipient = NULL; |
---|
970 | if (ok && Errors == 0) |
---|
971 | message("250 Queuing for node %s started", p); |
---|
972 | break; |
---|
973 | |
---|
974 | case CMDHELP: /* help -- give user info */ |
---|
975 | help(p); |
---|
976 | break; |
---|
977 | |
---|
978 | case CMDNOOP: /* noop -- do nothing */ |
---|
979 | checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "NOOP", e); |
---|
980 | message("250 OK"); |
---|
981 | break; |
---|
982 | |
---|
983 | case CMDQUIT: /* quit -- leave mail */ |
---|
984 | message("221 %s closing connection", MyHostName); |
---|
985 | |
---|
986 | doquit: |
---|
987 | /* arrange to ignore any current send list */ |
---|
988 | e->e_sendqueue = NULL; |
---|
989 | |
---|
990 | /* avoid future 050 messages */ |
---|
991 | disconnect(1, e); |
---|
992 | |
---|
993 | if (InChild) |
---|
994 | ExitStat = EX_QUIT; |
---|
995 | if (lognullconnection && LogLevel > 5) |
---|
996 | sm_syslog(LOG_INFO, NULL, |
---|
997 | "Null connection from %.100s", |
---|
998 | CurSmtpClient); |
---|
999 | finis(TRUE, ExitStat); |
---|
1000 | |
---|
1001 | case CMDVERB: /* set verbose mode */ |
---|
1002 | if (bitset(PRIV_NOEXPN, PrivacyFlags) || |
---|
1003 | bitset(PRIV_NOVERB, PrivacyFlags)) |
---|
1004 | { |
---|
1005 | /* this would give out the same info */ |
---|
1006 | message("502 Verbose unavailable"); |
---|
1007 | break; |
---|
1008 | } |
---|
1009 | checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "VERB", e); |
---|
1010 | Verbose = 1; |
---|
1011 | e->e_sendmode = SM_DELIVER; |
---|
1012 | message("250 Verbose mode"); |
---|
1013 | break; |
---|
1014 | |
---|
1015 | case CMDONEX: /* doing one transaction only */ |
---|
1016 | checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "ONEX", e); |
---|
1017 | OneXact = TRUE; |
---|
1018 | message("250 Only one transaction"); |
---|
1019 | break; |
---|
1020 | |
---|
1021 | case CMDXUSR: /* initial (user) submission */ |
---|
1022 | checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "XUSR", e); |
---|
1023 | UserSubmission = TRUE; |
---|
1024 | message("250 Initial submission"); |
---|
1025 | break; |
---|
1026 | |
---|
1027 | # if SMTPDEBUG |
---|
1028 | case CMDDBGQSHOW: /* show queues */ |
---|
1029 | printf("Send Queue="); |
---|
1030 | printaddr(e->e_sendqueue, TRUE); |
---|
1031 | break; |
---|
1032 | |
---|
1033 | case CMDDBGDEBUG: /* set debug mode */ |
---|
1034 | tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); |
---|
1035 | tTflag(p); |
---|
1036 | message("200 Debug set"); |
---|
1037 | break; |
---|
1038 | |
---|
1039 | # else /* not SMTPDEBUG */ |
---|
1040 | case CMDDBGQSHOW: /* show queues */ |
---|
1041 | case CMDDBGDEBUG: /* set debug mode */ |
---|
1042 | # endif /* SMTPDEBUG */ |
---|
1043 | case CMDLOGBOGUS: /* bogus command */ |
---|
1044 | if (LogLevel > 0) |
---|
1045 | sm_syslog(LOG_CRIT, e->e_id, |
---|
1046 | "\"%s\" command from %.100s (%.100s)", |
---|
1047 | c->cmdname, CurSmtpClient, |
---|
1048 | anynet_ntoa(&RealHostAddr)); |
---|
1049 | /* FALL THROUGH */ |
---|
1050 | |
---|
1051 | case CMDERROR: /* unknown command */ |
---|
1052 | if (++badcommands > MAXBADCOMMANDS) |
---|
1053 | { |
---|
1054 | message("421 %s Too many bad commands; closing connection", |
---|
1055 | MyHostName); |
---|
1056 | goto doquit; |
---|
1057 | } |
---|
1058 | |
---|
1059 | usrerr("500 Command unrecognized: \"%s\"", |
---|
1060 | shortenstring(inp, MAXSHORTSTR)); |
---|
1061 | break; |
---|
1062 | |
---|
1063 | default: |
---|
1064 | errno = 0; |
---|
1065 | syserr("500 smtp: unknown code %d", c->cmdcode); |
---|
1066 | break; |
---|
1067 | } |
---|
1068 | } |
---|
1069 | } |
---|
1070 | /* |
---|
1071 | ** CHECKSMTPATTACK -- check for denial-of-service attack by repetition |
---|
1072 | ** |
---|
1073 | ** Parameters: |
---|
1074 | ** pcounter -- pointer to a counter for this command. |
---|
1075 | ** maxcount -- maximum value for this counter before we |
---|
1076 | ** slow down. |
---|
1077 | ** cname -- command name for logging. |
---|
1078 | ** e -- the current envelope. |
---|
1079 | ** |
---|
1080 | ** Returns: |
---|
1081 | ** none. |
---|
1082 | ** |
---|
1083 | ** Side Effects: |
---|
1084 | ** Slows down if we seem to be under attack. |
---|
1085 | */ |
---|
1086 | |
---|
1087 | void |
---|
1088 | checksmtpattack(pcounter, maxcount, cname, e) |
---|
1089 | volatile int *pcounter; |
---|
1090 | int maxcount; |
---|
1091 | char *cname; |
---|
1092 | ENVELOPE *e; |
---|
1093 | { |
---|
1094 | if (++(*pcounter) >= maxcount) |
---|
1095 | { |
---|
1096 | if (*pcounter == maxcount && LogLevel > 5) |
---|
1097 | { |
---|
1098 | sm_syslog(LOG_INFO, e->e_id, |
---|
1099 | "%.100s: %.40s attack?", |
---|
1100 | CurSmtpClient, cname); |
---|
1101 | } |
---|
1102 | sleep(*pcounter / maxcount); |
---|
1103 | } |
---|
1104 | } |
---|
1105 | /* |
---|
1106 | ** SKIPWORD -- skip a fixed word. |
---|
1107 | ** |
---|
1108 | ** Parameters: |
---|
1109 | ** p -- place to start looking. |
---|
1110 | ** w -- word to skip. |
---|
1111 | ** |
---|
1112 | ** Returns: |
---|
1113 | ** p following w. |
---|
1114 | ** NULL on error. |
---|
1115 | ** |
---|
1116 | ** Side Effects: |
---|
1117 | ** clobbers the p data area. |
---|
1118 | */ |
---|
1119 | |
---|
1120 | static char * |
---|
1121 | skipword(p, w) |
---|
1122 | register char *volatile p; |
---|
1123 | char *w; |
---|
1124 | { |
---|
1125 | register char *q; |
---|
1126 | char *firstp = p; |
---|
1127 | |
---|
1128 | /* find beginning of word */ |
---|
1129 | while (isascii(*p) && isspace(*p)) |
---|
1130 | p++; |
---|
1131 | q = p; |
---|
1132 | |
---|
1133 | /* find end of word */ |
---|
1134 | while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) |
---|
1135 | p++; |
---|
1136 | while (isascii(*p) && isspace(*p)) |
---|
1137 | *p++ = '\0'; |
---|
1138 | if (*p != ':') |
---|
1139 | { |
---|
1140 | syntax: |
---|
1141 | usrerr("501 Syntax error in parameters scanning \"%s\"", |
---|
1142 | shortenstring(firstp, MAXSHORTSTR)); |
---|
1143 | return (NULL); |
---|
1144 | } |
---|
1145 | *p++ = '\0'; |
---|
1146 | while (isascii(*p) && isspace(*p)) |
---|
1147 | p++; |
---|
1148 | |
---|
1149 | if (*p == '\0') |
---|
1150 | goto syntax; |
---|
1151 | |
---|
1152 | /* see if the input word matches desired word */ |
---|
1153 | if (strcasecmp(q, w)) |
---|
1154 | goto syntax; |
---|
1155 | |
---|
1156 | return (p); |
---|
1157 | } |
---|
1158 | /* |
---|
1159 | ** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line |
---|
1160 | ** |
---|
1161 | ** Parameters: |
---|
1162 | ** kp -- the parameter key. |
---|
1163 | ** vp -- the value of that parameter. |
---|
1164 | ** e -- the envelope. |
---|
1165 | ** |
---|
1166 | ** Returns: |
---|
1167 | ** none. |
---|
1168 | */ |
---|
1169 | |
---|
1170 | void |
---|
1171 | mail_esmtp_args(kp, vp, e) |
---|
1172 | char *kp; |
---|
1173 | char *vp; |
---|
1174 | ENVELOPE *e; |
---|
1175 | { |
---|
1176 | if (strcasecmp(kp, "size") == 0) |
---|
1177 | { |
---|
1178 | if (vp == NULL) |
---|
1179 | { |
---|
1180 | usrerr("501 SIZE requires a value"); |
---|
1181 | /* NOTREACHED */ |
---|
1182 | } |
---|
1183 | # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) |
---|
1184 | e->e_msgsize = strtoul(vp, (char **) NULL, 10); |
---|
1185 | # else |
---|
1186 | e->e_msgsize = strtol(vp, (char **) NULL, 10); |
---|
1187 | # endif |
---|
1188 | } |
---|
1189 | else if (strcasecmp(kp, "body") == 0) |
---|
1190 | { |
---|
1191 | if (vp == NULL) |
---|
1192 | { |
---|
1193 | usrerr("501 BODY requires a value"); |
---|
1194 | /* NOTREACHED */ |
---|
1195 | } |
---|
1196 | else if (strcasecmp(vp, "8bitmime") == 0) |
---|
1197 | { |
---|
1198 | SevenBitInput = FALSE; |
---|
1199 | } |
---|
1200 | else if (strcasecmp(vp, "7bit") == 0) |
---|
1201 | { |
---|
1202 | SevenBitInput = TRUE; |
---|
1203 | } |
---|
1204 | else |
---|
1205 | { |
---|
1206 | usrerr("501 Unknown BODY type %s", |
---|
1207 | vp); |
---|
1208 | /* NOTREACHED */ |
---|
1209 | } |
---|
1210 | e->e_bodytype = newstr(vp); |
---|
1211 | } |
---|
1212 | else if (strcasecmp(kp, "envid") == 0) |
---|
1213 | { |
---|
1214 | if (vp == NULL) |
---|
1215 | { |
---|
1216 | usrerr("501 ENVID requires a value"); |
---|
1217 | /* NOTREACHED */ |
---|
1218 | } |
---|
1219 | if (!xtextok(vp)) |
---|
1220 | { |
---|
1221 | usrerr("501 Syntax error in ENVID parameter value"); |
---|
1222 | /* NOTREACHED */ |
---|
1223 | } |
---|
1224 | if (e->e_envid != NULL) |
---|
1225 | { |
---|
1226 | usrerr("501 Duplicate ENVID parameter"); |
---|
1227 | /* NOTREACHED */ |
---|
1228 | } |
---|
1229 | e->e_envid = newstr(vp); |
---|
1230 | } |
---|
1231 | else if (strcasecmp(kp, "ret") == 0) |
---|
1232 | { |
---|
1233 | if (vp == NULL) |
---|
1234 | { |
---|
1235 | usrerr("501 RET requires a value"); |
---|
1236 | /* NOTREACHED */ |
---|
1237 | } |
---|
1238 | if (bitset(EF_RET_PARAM, e->e_flags)) |
---|
1239 | { |
---|
1240 | usrerr("501 Duplicate RET parameter"); |
---|
1241 | /* NOTREACHED */ |
---|
1242 | } |
---|
1243 | e->e_flags |= EF_RET_PARAM; |
---|
1244 | if (strcasecmp(vp, "hdrs") == 0) |
---|
1245 | e->e_flags |= EF_NO_BODY_RETN; |
---|
1246 | else if (strcasecmp(vp, "full") != 0) |
---|
1247 | { |
---|
1248 | usrerr("501 Bad argument \"%s\" to RET", vp); |
---|
1249 | /* NOTREACHED */ |
---|
1250 | } |
---|
1251 | } |
---|
1252 | else |
---|
1253 | { |
---|
1254 | usrerr("501 %s parameter unrecognized", kp); |
---|
1255 | /* NOTREACHED */ |
---|
1256 | } |
---|
1257 | } |
---|
1258 | /* |
---|
1259 | ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line |
---|
1260 | ** |
---|
1261 | ** Parameters: |
---|
1262 | ** a -- the address corresponding to the To: parameter. |
---|
1263 | ** kp -- the parameter key. |
---|
1264 | ** vp -- the value of that parameter. |
---|
1265 | ** e -- the envelope. |
---|
1266 | ** |
---|
1267 | ** Returns: |
---|
1268 | ** none. |
---|
1269 | */ |
---|
1270 | |
---|
1271 | void |
---|
1272 | rcpt_esmtp_args(a, kp, vp, e) |
---|
1273 | ADDRESS *a; |
---|
1274 | char *kp; |
---|
1275 | char *vp; |
---|
1276 | ENVELOPE *e; |
---|
1277 | { |
---|
1278 | if (strcasecmp(kp, "notify") == 0) |
---|
1279 | { |
---|
1280 | char *p; |
---|
1281 | |
---|
1282 | if (vp == NULL) |
---|
1283 | { |
---|
1284 | usrerr("501 NOTIFY requires a value"); |
---|
1285 | /* NOTREACHED */ |
---|
1286 | } |
---|
1287 | a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); |
---|
1288 | a->q_flags |= QHASNOTIFY; |
---|
1289 | if (strcasecmp(vp, "never") == 0) |
---|
1290 | return; |
---|
1291 | for (p = vp; p != NULL; vp = p) |
---|
1292 | { |
---|
1293 | p = strchr(p, ','); |
---|
1294 | if (p != NULL) |
---|
1295 | *p++ = '\0'; |
---|
1296 | if (strcasecmp(vp, "success") == 0) |
---|
1297 | a->q_flags |= QPINGONSUCCESS; |
---|
1298 | else if (strcasecmp(vp, "failure") == 0) |
---|
1299 | a->q_flags |= QPINGONFAILURE; |
---|
1300 | else if (strcasecmp(vp, "delay") == 0) |
---|
1301 | a->q_flags |= QPINGONDELAY; |
---|
1302 | else |
---|
1303 | { |
---|
1304 | usrerr("501 Bad argument \"%s\" to NOTIFY", |
---|
1305 | vp); |
---|
1306 | /* NOTREACHED */ |
---|
1307 | } |
---|
1308 | } |
---|
1309 | } |
---|
1310 | else if (strcasecmp(kp, "orcpt") == 0) |
---|
1311 | { |
---|
1312 | if (vp == NULL) |
---|
1313 | { |
---|
1314 | usrerr("501 ORCPT requires a value"); |
---|
1315 | /* NOTREACHED */ |
---|
1316 | } |
---|
1317 | if (strchr(vp, ';') == NULL || !xtextok(vp)) |
---|
1318 | { |
---|
1319 | usrerr("501 Syntax error in ORCPT parameter value"); |
---|
1320 | /* NOTREACHED */ |
---|
1321 | } |
---|
1322 | if (a->q_orcpt != NULL) |
---|
1323 | { |
---|
1324 | usrerr("501 Duplicate ORCPT parameter"); |
---|
1325 | /* NOTREACHED */ |
---|
1326 | } |
---|
1327 | a->q_orcpt = newstr(vp); |
---|
1328 | } |
---|
1329 | else |
---|
1330 | { |
---|
1331 | usrerr("501 %s parameter unrecognized", kp); |
---|
1332 | /* NOTREACHED */ |
---|
1333 | } |
---|
1334 | } |
---|
1335 | /* |
---|
1336 | ** PRINTVRFYADDR -- print an entry in the verify queue |
---|
1337 | ** |
---|
1338 | ** Parameters: |
---|
1339 | ** a -- the address to print |
---|
1340 | ** last -- set if this is the last one. |
---|
1341 | ** vrfy -- set if this is a VRFY command. |
---|
1342 | ** |
---|
1343 | ** Returns: |
---|
1344 | ** none. |
---|
1345 | ** |
---|
1346 | ** Side Effects: |
---|
1347 | ** Prints the appropriate 250 codes. |
---|
1348 | */ |
---|
1349 | |
---|
1350 | void |
---|
1351 | printvrfyaddr(a, last, vrfy) |
---|
1352 | register ADDRESS *a; |
---|
1353 | bool last; |
---|
1354 | bool vrfy; |
---|
1355 | { |
---|
1356 | char fmtbuf[20]; |
---|
1357 | |
---|
1358 | if (vrfy && a->q_mailer != NULL && |
---|
1359 | !bitnset(M_VRFY250, a->q_mailer->m_flags)) |
---|
1360 | strcpy(fmtbuf, "252"); |
---|
1361 | else |
---|
1362 | strcpy(fmtbuf, "250"); |
---|
1363 | fmtbuf[3] = last ? ' ' : '-'; |
---|
1364 | |
---|
1365 | if (a->q_fullname == NULL) |
---|
1366 | { |
---|
1367 | if (strchr(a->q_user, '@') == NULL) |
---|
1368 | strcpy(&fmtbuf[4], "<%s@%s>"); |
---|
1369 | else |
---|
1370 | strcpy(&fmtbuf[4], "<%s>"); |
---|
1371 | message(fmtbuf, a->q_user, MyHostName); |
---|
1372 | } |
---|
1373 | else |
---|
1374 | { |
---|
1375 | if (strchr(a->q_user, '@') == NULL) |
---|
1376 | strcpy(&fmtbuf[4], "%s <%s@%s>"); |
---|
1377 | else |
---|
1378 | strcpy(&fmtbuf[4], "%s <%s>"); |
---|
1379 | message(fmtbuf, a->q_fullname, a->q_user, MyHostName); |
---|
1380 | } |
---|
1381 | } |
---|
1382 | /* |
---|
1383 | ** RUNINCHILD -- return twice -- once in the child, then in the parent again |
---|
1384 | ** |
---|
1385 | ** Parameters: |
---|
1386 | ** label -- a string used in error messages |
---|
1387 | ** |
---|
1388 | ** Returns: |
---|
1389 | ** zero in the child |
---|
1390 | ** one in the parent |
---|
1391 | ** |
---|
1392 | ** Side Effects: |
---|
1393 | ** none. |
---|
1394 | */ |
---|
1395 | |
---|
1396 | int |
---|
1397 | runinchild(label, e) |
---|
1398 | char *label; |
---|
1399 | register ENVELOPE *e; |
---|
1400 | { |
---|
1401 | pid_t childpid; |
---|
1402 | |
---|
1403 | if (!OneXact) |
---|
1404 | { |
---|
1405 | /* |
---|
1406 | ** Disable child process reaping, in case ETRN has preceeded |
---|
1407 | ** MAIL command, and then fork. |
---|
1408 | */ |
---|
1409 | |
---|
1410 | (void) blocksignal(SIGCHLD); |
---|
1411 | |
---|
1412 | childpid = dofork(); |
---|
1413 | if (childpid < 0) |
---|
1414 | { |
---|
1415 | syserr("451 %s: cannot fork", label); |
---|
1416 | (void) releasesignal(SIGCHLD); |
---|
1417 | return (1); |
---|
1418 | } |
---|
1419 | if (childpid > 0) |
---|
1420 | { |
---|
1421 | auto int st; |
---|
1422 | |
---|
1423 | /* parent -- wait for child to complete */ |
---|
1424 | sm_setproctitle(TRUE, "server %s child wait", CurSmtpClient); |
---|
1425 | st = waitfor(childpid); |
---|
1426 | if (st == -1) |
---|
1427 | syserr("451 %s: lost child", label); |
---|
1428 | else if (!WIFEXITED(st)) |
---|
1429 | syserr("451 %s: died on signal %d", |
---|
1430 | label, st & 0177); |
---|
1431 | |
---|
1432 | /* if we exited on a QUIT command, complete the process */ |
---|
1433 | if (WEXITSTATUS(st) == EX_QUIT) |
---|
1434 | { |
---|
1435 | disconnect(1, e); |
---|
1436 | finis(TRUE, ExitStat); |
---|
1437 | } |
---|
1438 | |
---|
1439 | /* restore the child signal */ |
---|
1440 | (void) releasesignal(SIGCHLD); |
---|
1441 | |
---|
1442 | return (1); |
---|
1443 | } |
---|
1444 | else |
---|
1445 | { |
---|
1446 | /* child */ |
---|
1447 | InChild = TRUE; |
---|
1448 | QuickAbort = FALSE; |
---|
1449 | clearenvelope(e, FALSE); |
---|
1450 | (void) setsignal(SIGCHLD, SIG_DFL); |
---|
1451 | (void) releasesignal(SIGCHLD); |
---|
1452 | } |
---|
1453 | } |
---|
1454 | return (0); |
---|
1455 | } |
---|
1456 | |
---|
1457 | # endif /* SMTP */ |
---|
1458 | /* |
---|
1459 | ** HELP -- implement the HELP command. |
---|
1460 | ** |
---|
1461 | ** Parameters: |
---|
1462 | ** topic -- the topic we want help for. |
---|
1463 | ** |
---|
1464 | ** Returns: |
---|
1465 | ** none. |
---|
1466 | ** |
---|
1467 | ** Side Effects: |
---|
1468 | ** outputs the help file to message output. |
---|
1469 | */ |
---|
1470 | |
---|
1471 | void |
---|
1472 | help(topic) |
---|
1473 | char *topic; |
---|
1474 | { |
---|
1475 | register FILE *hf; |
---|
1476 | int len; |
---|
1477 | bool noinfo; |
---|
1478 | int sff = SFF_OPENASROOT|SFF_REGONLY; |
---|
1479 | char buf[MAXLINE]; |
---|
1480 | extern char Version[]; |
---|
1481 | |
---|
1482 | if (DontLockReadFiles) |
---|
1483 | sff |= SFF_NOLOCK; |
---|
1484 | if (!bitset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail)) |
---|
1485 | sff |= SFF_SAFEDIRPATH; |
---|
1486 | |
---|
1487 | if (HelpFile == NULL || |
---|
1488 | (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL) |
---|
1489 | { |
---|
1490 | /* no help */ |
---|
1491 | errno = 0; |
---|
1492 | message("502 Sendmail %s -- HELP not implemented", Version); |
---|
1493 | return; |
---|
1494 | } |
---|
1495 | |
---|
1496 | if (topic == NULL || *topic == '\0') |
---|
1497 | { |
---|
1498 | topic = "smtp"; |
---|
1499 | message("214-This is Sendmail version %s", Version); |
---|
1500 | noinfo = FALSE; |
---|
1501 | } |
---|
1502 | else |
---|
1503 | { |
---|
1504 | makelower(topic); |
---|
1505 | noinfo = TRUE; |
---|
1506 | } |
---|
1507 | |
---|
1508 | len = strlen(topic); |
---|
1509 | |
---|
1510 | while (fgets(buf, sizeof buf, hf) != NULL) |
---|
1511 | { |
---|
1512 | if (strncmp(buf, topic, len) == 0) |
---|
1513 | { |
---|
1514 | register char *p; |
---|
1515 | |
---|
1516 | p = strchr(buf, '\t'); |
---|
1517 | if (p == NULL) |
---|
1518 | p = buf; |
---|
1519 | else |
---|
1520 | p++; |
---|
1521 | fixcrlf(p, TRUE); |
---|
1522 | message("214-%s", p); |
---|
1523 | noinfo = FALSE; |
---|
1524 | } |
---|
1525 | } |
---|
1526 | |
---|
1527 | if (noinfo) |
---|
1528 | message("504 HELP topic \"%.10s\" unknown", topic); |
---|
1529 | else |
---|
1530 | message("214 End of HELP info"); |
---|
1531 | (void) fclose(hf); |
---|
1532 | } |
---|