citadel
About: Citadel is an advanced messaging and collaboration system for groupware and BBS applications (preferred OS: Linux).
  Fossies Dox: citadel.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

Loading...
Searching...
No Matches
serv_smtp.c
Go to the documentation of this file.
1// This module is an SMTP and ESMTP server for the Citadel system.
2// It is compliant with all of the following:
3//
4// RFC 821 - Simple Mail Transfer Protocol
5// RFC 876 - Survey of SMTP Implementations
6// RFC 1047 - Duplicate messages and SMTP
7// RFC 1652 - 8 bit MIME
8// RFC 1869 - Extended Simple Mail Transfer Protocol
9// RFC 1870 - SMTP Service Extension for Message Size Declaration
10// RFC 2033 - Local Mail Transfer Protocol
11// RFC 2197 - SMTP Service Extension for Command Pipelining
12// RFC 2476 - Message Submission
13// RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
14// RFC 2554 - SMTP Service Extension for Authentication
15// RFC 2821 - Simple Mail Transfer Protocol
16// RFC 2822 - Internet Message Format
17// RFC 2920 - SMTP Service Extension for Command Pipelining
18//
19// The VRFY and EXPN commands have been removed from this implementation
20// because nobody uses these commands anymore, except for spammers.
21//
22// Copyright (c) 1998-2022 by the citadel.org team
23//
24// This program is open source software; you can redistribute it and/or modify
25// it under the terms of the GNU General Public License version 3.
26//
27// This program is distributed in the hope that it will be useful,
28// but WITHOUT ANY WARRANTY; without even the implied warranty of
29// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30// GNU General Public License for more details.
31
32#include "../../sysdep.h"
33#include <stdlib.h>
34#include <unistd.h>
35#include <stdio.h>
36#include <termios.h>
37#include <fcntl.h>
38#include <signal.h>
39#include <pwd.h>
40#include <errno.h>
41#include <sys/types.h>
42#include <syslog.h>
43#include <time.h>
44#include <sys/wait.h>
45#include <ctype.h>
46#include <string.h>
47#include <limits.h>
48#include <sys/socket.h>
49#include <netinet/in.h>
50#include <arpa/inet.h>
51#include <assert.h>
52#include <libcitadel.h>
53#include "../../citadel.h"
54#include "../../server.h"
55#include "../../citserver.h"
56#include "../../support.h"
57#include "../../config.h"
58#include "../../control.h"
59#include "../../user_ops.h"
60#include "../../room_ops.h"
61#include "../../database.h"
62#include "../../msgbase.h"
63#include "../../internet_addressing.h"
64#include "../../genstamp.h"
65#include "../../domain.h"
66#include "../../clientsocket.h"
67#include "../../locate_host.h"
68#include "../../citadel_dirs.h"
69#include "../../ctdl_module.h"
70
71#include "smtp_util.h"
72
73enum { // Command states for login authentication
78};
79
83 LHLO
84};
85
86
87// Here's where our SMTP session begins its happy day.
88void smtp_greeting(int is_msa) {
89 char message_to_spammer[1024];
90
91 strcpy(CC->cs_clientname, "SMTP session");
92 CC->internal_pgm = 1;
93 CC->cs_flags |= CS_STEALTH;
94 CC->session_specific_data = malloc(sizeof(struct citsmtp));
95 memset(SMTP, 0, sizeof(struct citsmtp));
96 SMTP->is_msa = is_msa;
97 SMTP->Cmd = NewStrBufPlain(NULL, SIZ);
98 SMTP->helo_node = NewStrBuf();
99 SMTP->from = NewStrBufPlain(NULL, SIZ);
100 SMTP->recipients = NewStrBufPlain(NULL, SIZ);
101 SMTP->OneRcpt = NewStrBufPlain(NULL, SIZ);
102 SMTP->preferred_sender_email = NULL;
103 SMTP->preferred_sender_name = NULL;
104
105 // If this config option is set, reject connections from problem
106 // addresses immediately instead of after they execute a RCPT
107 if ( (CtdlGetConfigInt("c_rbl_at_greeting")) && (SMTP->is_msa == 0) ) {
108 if (rbl_check(CC->cs_addr, message_to_spammer)) {
110 cprintf("421 %s\r\n", message_to_spammer);
111 else
112 cprintf("550 %s\r\n", message_to_spammer);
113 CC->kill_me = KILLME_SPAMMER;
114 // no need to free_recipients(valid), it's not allocated yet
115 return;
116 }
117 }
118
119 // Otherwise we're either clean or we check later.
120
121 if (CC->nologin==1) {
122 cprintf("451 Too many connections are already open; please try again later.\r\n");
124 // no need to free_recipients(valid), it's not allocated yet
125 return;
126 }
127
128 // Note: the FQDN *must* appear as the first thing after the 220 code.
129 // Some clients (including citmail.c) depend on it being there.
130 cprintf("220 %s ESMTP Citadel server ready.\r\n", CtdlGetConfigStr("c_fqdn"));
131}
132
133
134// SMTPS is just like SMTP, except it goes crypto right away.
135void smtps_greeting(void) {
136 CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
137#ifdef HAVE_OPENSSL
138 if (!CC->redirect_ssl) CC->kill_me = KILLME_NO_CRYPTO; // kill session if no crypto
139#endif
140 smtp_greeting(0);
141}
142
143
144// SMTP MSA port requires authentication.
146 smtp_greeting(1);
147}
148
149
150// LMTP is like SMTP but with some extra bonus footage added.
151void lmtp_greeting(void) {
152
153 smtp_greeting(0);
154 SMTP->is_lmtp = 1;
155}
156
157
158// Generic SMTP MTA greeting
160 smtp_greeting(0);
161}
162
163
164// We also have an unfiltered LMTP socket that bypasses spam filters.
166 smtp_greeting(0);
167 SMTP->is_lmtp = 1;
168 SMTP->is_unfiltered = 1;
169}
170
171
172// Login greeting common to all auth methods
174 cprintf("235 Hello, %s\r\n", CC->user.fullname);
175 syslog(LOG_INFO, "serv_smtp: SMTP authenticated %s", CC->user.fullname);
176 CC->internal_pgm = 0;
177 CC->cs_flags &= ~CS_STEALTH;
178}
179
180
181// Implement HELO and EHLO commands.
182// which_command: 0=HELO, 1=EHLO, 2=LHLO
183void smtp_hello(int which_command) {
184
185 if (StrLength(SMTP->Cmd) >= 6) {
186 FlushStrBuf(SMTP->helo_node);
187 StrBufAppendBuf(SMTP->helo_node, SMTP->Cmd, 5);
188 }
189
190 if ( (which_command != LHLO) && (SMTP->is_lmtp) ) {
191 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
192 return;
193 }
194
195 if ( (which_command == LHLO) && (SMTP->is_lmtp == 0) ) {
196 cprintf("500 LHLO is only allowed when running LMTP\r\n");
197 return;
198 }
199
200 if (which_command == HELO) {
201 cprintf("250 Hello %s (%s [%s])\r\n",
202 ChrPtr(SMTP->helo_node),
203 CC->cs_host,
204 CC->cs_addr
205 );
206 }
207 else {
208 if (which_command == EHLO) {
209 cprintf("250-Hello %s (%s [%s])\r\n", ChrPtr(SMTP->helo_node), CC->cs_host, CC->cs_addr);
210 }
211 else {
212 cprintf("250-Greetings and joyous salutations.\r\n");
213 }
214 cprintf("250-HELP\r\n");
215 cprintf("250-SIZE %ld\r\n", CtdlGetConfigLong("c_maxmsglen"));
216
217#ifdef HAVE_OPENSSL
218 // Offer TLS, but only if TLS is not already active.
219 // Furthermore, only offer TLS when running on the SMTP-MSA port, not on the SMTP-MTA port,
220 // because if our server doesn't have a trusted certificate, some mailers will refuse to talk to it.
221 if ( (!CC->redirect_ssl) && (SMTP->is_msa) ) {
222 cprintf("250-STARTTLS\r\n");
223 }
224#endif
225
226 cprintf("250-AUTH LOGIN PLAIN\r\n"
227 "250-AUTH=LOGIN PLAIN\r\n"
228 "250 8BITMIME\r\n"
229 );
230 }
231}
232
233
234// Backend function for smtp_webcit_preferences_hack().
235// Look at a message and determine if it's the preferences file.
236void smtp_webcit_preferences_hack_backend(long msgnum, void *userdata) {
237 struct CtdlMessage *msg;
238 char **webcit_conf = (char **) userdata;
239
240 if (*webcit_conf) {
241 return; // already got it
242 }
243
244 msg = CtdlFetchMessage(msgnum, 1);
245 if (msg == NULL) {
246 return;
247 }
248
249 if ( !CM_IsEmpty(msg, eMsgSubject) && (!strcasecmp(msg->cm_fields[eMsgSubject], "__ WebCit Preferences __"))) {
250 // This is it! Change ownership of the message text so it doesn't get freed.
251 *webcit_conf = (char *)msg->cm_fields[eMesageText];
252 msg->cm_fields[eMesageText] = NULL;
253 }
254 CM_Free(msg);
255}
256
257
258// The configuration item for the user's preferred display name for outgoing email is, unfortunately,
259// stored in the account's WebCit configuration. We have to fetch it now.
261 char config_roomname[ROOMNAMELEN];
262 char *webcit_conf = NULL;
263
264 snprintf(config_roomname, sizeof config_roomname, "%010ld.%s", CC->user.usernum, USERCONFIGROOM);
265 if (CtdlGetRoom(&CC->room, config_roomname) != 0) {
266 return;
267 }
268
269 // Find the WebCit configuration message
270 CtdlForEachMessage(MSGS_ALL, 1, NULL, NULL, NULL, smtp_webcit_preferences_hack_backend, (void *)&webcit_conf);
271
272 if (!webcit_conf) {
273 return;
274 }
275
276 // Parse the webcit configuration and attempt to do something useful with it
277 char *str = webcit_conf;
278 char *saveptr = str;
279 char *this_line = NULL;
280 while (this_line = strtok_r(str, "\n", &saveptr), this_line != NULL) {
281 str = NULL;
282 if (!strncasecmp(this_line, "defaultfrom|", 12)) {
283 SMTP->preferred_sender_email = NewStrBufPlain(&this_line[12], -1);
284 }
285 if (!strncasecmp(this_line, "defaultname|", 12)) {
286 SMTP->preferred_sender_name = NewStrBufPlain(&this_line[12], -1);
287 }
288 if ((!strncasecmp(this_line, "defaultname|", 12)) && (SMTP->preferred_sender_name == NULL)) {
289 SMTP->preferred_sender_name = NewStrBufPlain(&this_line[12], -1);
290 }
291
292 }
293 free(webcit_conf);
294}
295
296
297// Implement HELP command.
298void smtp_help(void) {
299 cprintf("214 RTFM http://www.ietf.org/rfc/rfc2821.txt\r\n");
300}
301
302
303void smtp_get_user(int offset) {
304 char buf[SIZ];
305
306 StrBuf *UserName = NewStrBufDup(SMTP->Cmd);
307 StrBufCutLeft(UserName, offset);
308 StrBufDecodeBase64(UserName);
309
310 if (CtdlLoginExistingUser(ChrPtr(UserName)) == login_ok) {
311 size_t len = CtdlEncodeBase64(buf, "Password:", 9, BASE64_NO_LINEBREAKS);
312
313 if (buf[len - 1] == '\n') {
314 buf[len - 1] = '\0';
315 }
316 cprintf("334 %s\r\n", buf);
317 SMTP->command_state = smtp_password;
318 }
319 else {
320 cprintf("500 No such user.\r\n");
321 SMTP->command_state = smtp_command;
322 }
323 FreeStrBuf(&UserName);
324}
325
326
327void smtp_get_pass(void) {
328 char password[SIZ];
329
330 memset(password, 0, sizeof(password));
331 StrBufDecodeBase64(SMTP->Cmd);
332 syslog(LOG_DEBUG, "serv_smtp: trying <%s>", password);
333 if (CtdlTryPassword(SKEY(SMTP->Cmd)) == pass_ok) {
335 }
336 else {
337 cprintf("535 Authentication failed.\r\n");
338 }
339 SMTP->command_state = smtp_command;
340}
341
342
343// Back end for PLAIN auth method (either inline or multistate)
344void smtp_try_plain(void) {
345 const char*decoded_authstring;
346 char ident[256] = "";
347 char user[256] = "";
348 char pass[256] = "";
349 int result;
350
351 long decoded_len;
352 long len = 0;
353 long plen = 0;
354
355 memset(pass, 0, sizeof(pass));
356 decoded_len = StrBufDecodeBase64(SMTP->Cmd);
357
358 if (decoded_len > 0) {
359 decoded_authstring = ChrPtr(SMTP->Cmd);
360
361 len = safestrncpy(ident, decoded_authstring, sizeof ident);
362
363 decoded_len -= len - 1;
364 decoded_authstring += len + 1;
365
366 if (decoded_len > 0) {
367 len = safestrncpy(user, decoded_authstring, sizeof user);
368
369 decoded_authstring += len + 1;
370 decoded_len -= len - 1;
371 }
372
373 if (decoded_len > 0) {
374 plen = safestrncpy(pass, decoded_authstring, sizeof pass);
375
376 if (plen < 0)
377 plen = sizeof(pass) - 1;
378 }
379 }
380
381 SMTP->command_state = smtp_command;
382
383 if (!IsEmptyStr(ident)) {
384 result = CtdlLoginExistingUser(ident);
385 }
386 else {
387 result = CtdlLoginExistingUser(user);
388 }
389
390 if (result == login_ok) {
391 if (CtdlTryPassword(pass, plen) == pass_ok) {
394 return;
395 }
396 }
397 cprintf("504 Authentication failed.\r\n");
398}
399
400
401// Attempt to perform authenticated SMTP
402void smtp_auth(void) {
403 char username_prompt[64];
404 char method[64];
405 char encoded_authstring[1024];
406
407 if (CC->logged_in) {
408 cprintf("504 Already logged in.\r\n");
409 return;
410 }
411
412 if (StrLength(SMTP->Cmd) < 6) {
413 cprintf("501 Syntax error\r\n");
414 return;
415 }
416
417 extract_token(method, ChrPtr(SMTP->Cmd) + 5, 0, ' ', sizeof method);
418
419 if (!strncasecmp(method, "login", 5) ) {
420 if (StrLength(SMTP->Cmd) >= 12) {
421 syslog(LOG_DEBUG, "serv_smtp: username <%s> supplied inline", ChrPtr(SMTP->Cmd)+11);
422 smtp_get_user(11);
423 }
424 else {
425 size_t len = CtdlEncodeBase64(username_prompt, "Username:", 9, BASE64_NO_LINEBREAKS);
426 if (username_prompt[len - 1] == '\n') {
427 username_prompt[len - 1] = '\0';
428 }
429 cprintf("334 %s\r\n", username_prompt);
430 SMTP->command_state = smtp_user;
431 }
432 return;
433 }
434
435 if (!strncasecmp(method, "plain", 5) ) {
436 long len;
437 if (num_tokens(ChrPtr(SMTP->Cmd) + 5, ' ') < 2) {
438 cprintf("334 \r\n");
439 SMTP->command_state = smtp_plain;
440 return;
441 }
442
443 len = extract_token(encoded_authstring, ChrPtr(SMTP->Cmd) + 5, 1, ' ', sizeof encoded_authstring);
444 StrBufPlain(SMTP->Cmd, encoded_authstring, len);
446 return;
447 }
448
449 cprintf("504 Unknown authentication method.\r\n");
450 return;
451}
452
453
454// Implements the RSET (reset state) command.
455// Currently this just zeroes out the state buffer. If pointers to data
456// allocated with malloc() are ever placed in the state buffer, we have to
457// be sure to free() them first!
458//
459// Set do_response to nonzero to output the SMTP RSET response code.
460void smtp_rset(int do_response) {
461 FlushStrBuf(SMTP->Cmd);
462 FlushStrBuf(SMTP->helo_node);
463 FlushStrBuf(SMTP->from);
464 FlushStrBuf(SMTP->recipients);
465 FlushStrBuf(SMTP->OneRcpt);
466
467 SMTP->command_state = 0;
468 SMTP->number_of_recipients = 0;
469 SMTP->delivery_mode = 0;
470 SMTP->message_originated_locally = 0;
471 SMTP->is_msa = 0;
472 // is_lmtp and is_unfiltered should not be cleared.
473
474 if (do_response) {
475 cprintf("250 Zap!\r\n");
476 }
477}
478
479
480// Clear out the portions of the state buffer that need to be cleared out
481// after the DATA command finishes.
482void smtp_data_clear(void) {
483 FlushStrBuf(SMTP->from);
484 FlushStrBuf(SMTP->recipients);
485 FlushStrBuf(SMTP->OneRcpt);
486 SMTP->number_of_recipients = 0;
487 SMTP->delivery_mode = 0;
488 SMTP->message_originated_locally = 0;
489}
490
491
492// Implements the "MAIL FROM:" command
493void smtp_mail(void) {
494 char user[SIZ];
495 char node[SIZ];
496 char name[SIZ];
497
498 if (StrLength(SMTP->from) > 0) {
499 cprintf("503 Only one sender permitted\r\n");
500 return;
501 }
502
503 if (StrLength(SMTP->Cmd) < 6) {
504 cprintf("501 Syntax error\r\n");
505 return;
506 }
507
508 if (strncasecmp(ChrPtr(SMTP->Cmd) + 5, "From:", 5)) {
509 cprintf("501 Syntax error\r\n");
510 return;
511 }
512
513 StrBufAppendBuf(SMTP->from, SMTP->Cmd, 5);
514 StrBufTrim(SMTP->from);
515 if (strchr(ChrPtr(SMTP->from), '<') != NULL) {
516 StrBufStripAllBut(SMTP->from, '<', '>');
517 }
518
519 // We used to reject empty sender names, until it was brought to our
520 // attention that RFC1123 5.2.9 requires that this be allowed. So now
521 // we allow it, but replace the empty string with a fake
522 // address so we don't have to contend with the empty string causing
523 // other code to fail when it's expecting something there.
524 if (StrLength(SMTP->from) == 0) {
525 StrBufPlain(SMTP->from, HKEY("someone@example.com"));
526 }
527
528 // If this SMTP connection is from a logged-in user, force the 'from'
529 // to be the user's Internet e-mail address as Citadel knows it.
530 if (CC->logged_in) {
531 StrBufPlain(SMTP->from, CC->cs_inet_email, -1);
532 cprintf("250 Sender ok <%s>\r\n", ChrPtr(SMTP->from));
533 SMTP->message_originated_locally = 1;
534 return;
535 }
536
537 else if (SMTP->is_lmtp) {
538 // Bypass forgery checking for LMTP
539 }
540
541 // Otherwise, make sure outsiders aren't trying to forge mail from
542 // this system (unless, of course, c_allow_spoofing is enabled)
543 else if (CtdlGetConfigInt("c_allow_spoofing") == 0) {
544 process_rfc822_addr(ChrPtr(SMTP->from), user, node, name);
545 syslog(LOG_DEBUG, "serv_smtp: claimed envelope sender is '%s' == '%s' @ '%s' ('%s')",
546 ChrPtr(SMTP->from), user, node, name
547 );
548 if (CtdlHostAlias(node) != hostalias_nomatch) {
549 cprintf("550 You must log in to send mail from %s\r\n", node);
550 FlushStrBuf(SMTP->from);
551 syslog(LOG_DEBUG, "serv_smtp: rejecting unauthenticated mail from %s", node);
552 return;
553 }
554 }
555
556 cprintf("250 Sender ok\r\n");
557}
558
559
560// Implements the "RCPT To:" command
561void smtp_rcpt(void) {
562 char message_to_spammer[SIZ];
563 struct recptypes *valid = NULL;
564
565 if (StrLength(SMTP->from) == 0) {
566 cprintf("503 Need MAIL before RCPT\r\n");
567 return;
568 }
569
570 if (StrLength(SMTP->Cmd) < 6) {
571 cprintf("501 Syntax error\r\n");
572 return;
573 }
574
575 if (strncasecmp(ChrPtr(SMTP->Cmd) + 5, "To:", 3)) {
576 cprintf("501 Syntax error\r\n");
577 return;
578 }
579
580 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
581 cprintf("550 You must log in to send mail on this port.\r\n");
582 FlushStrBuf(SMTP->from);
583 return;
584 }
585
586 FlushStrBuf(SMTP->OneRcpt);
587 StrBufAppendBuf(SMTP->OneRcpt, SMTP->Cmd, 8);
588 StrBufTrim(SMTP->OneRcpt);
589 StrBufStripAllBut(SMTP->OneRcpt, '<', '>');
590
591 if ( (StrLength(SMTP->OneRcpt) + StrLength(SMTP->recipients)) >= SIZ) {
592 cprintf("452 Too many recipients\r\n");
593 return;
594 }
595
596 // RBL check
597 if (
598 (!CC->logged_in) // Don't RBL authenticated users
599 && (!SMTP->is_lmtp) // Don't RBL LMTP clients
600 && (CtdlGetConfigInt("c_rbl_at_greeting") == 0) // Don't RBL if we did it at connection time
601 && (rbl_check(CC->cs_addr, message_to_spammer))
602 ) {
603 cprintf("550 %s\r\n", message_to_spammer);
604 return; // no need to free_recipients(valid)
605 } // because it hasn't been allocated yet
606
607 // This is a *preliminary* call to validate_recipients() to evaluate one recipient.
608 valid = validate_recipients(
609 (char *)ChrPtr(SMTP->OneRcpt),
611 (SMTP->is_lmtp)? POST_LMTP: (CC->logged_in)? POST_LOGGED_IN: POST_EXTERNAL
612 );
613
614 // Any type of error thrown by validate_recipients() will make the SMTP transaction fail at this point.
615 if (valid->num_error != 0) {
616 cprintf("550 %s\r\n", valid->errormsg);
617 free_recipients(valid);
618 return;
619 }
620
621 if (
622 (valid->num_internet > 0) // If it's outbound Internet mail...
623 && (CC->logged_in) // ...and we're a logged-in user...
624 && (CtdlCheckInternetMailPermission(&CC->user)==0) // ...who does not have Internet mail rights...
625 ) {
626 cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", ChrPtr(SMTP->OneRcpt));
627 free_recipients(valid);
628 return;
629 }
630
631 if (
632 (valid->num_internet > 0) // If it's outbound Internet mail...
633 && (SMTP->message_originated_locally == 0) // ...and also inbound Internet mail...
634 && (SMTP->is_lmtp == 0) /// ...and didn't arrive via LMTP...
635 ) {
636 cprintf("551 <%s> - relaying denied\r\n", ChrPtr(SMTP->OneRcpt));
637 free_recipients(valid);
638 return;
639 }
640
641 if (
642 (valid->num_room > 0) // If it's mail to a room (mailing list)...
643 && (SMTP->message_originated_locally == 0) // ...and also inbound Internet mail...
644 && (is_email_subscribed_to_list((char *)ChrPtr(SMTP->from), valid->recp_room) == 0) // ...and not a subscriber
645 ) {
646 cprintf("551 <%s> - The message is not from a list member\r\n", ChrPtr(SMTP->OneRcpt));
647 free_recipients(valid);
648 return;
649 }
650
651 cprintf("250 RCPT ok <%s>\r\n", ChrPtr(SMTP->OneRcpt));
652 if (StrLength(SMTP->recipients) > 0) {
653 StrBufAppendBufPlain(SMTP->recipients, HKEY(","), 0);
654 }
655 StrBufAppendBuf(SMTP->recipients, SMTP->OneRcpt, 0);
656 SMTP->number_of_recipients ++;
657 if (valid != NULL) {
658 free_recipients(valid);
659 }
660}
661
662
663// Implements the DATA command
664void smtp_data(void) {
665 StrBuf *body;
666 StrBuf *defbody;
667 struct CtdlMessage *msg = NULL;
668 long msgnum = (-1L);
669 char nowstamp[SIZ];
670 struct recptypes *valid;
671 int scan_errors;
672 int i;
673
674 if (StrLength(SMTP->from) == 0) {
675 cprintf("503 Need MAIL command first.\r\n");
676 return;
677 }
678
679 if (SMTP->number_of_recipients < 1) {
680 cprintf("503 Need RCPT command first.\r\n");
681 return;
682 }
683
684 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
685
686 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
687 defbody = NewStrBufPlain(NULL, SIZ);
688
689 if (defbody != NULL) {
690 if (SMTP->is_lmtp && (CC->cs_UDSclientUID != -1)) {
691 StrBufPrintf(
692 defbody,
693 "Received: from %s (Citadel from userid %ld)\n"
694 " by %s; %s\n",
695 ChrPtr(SMTP->helo_node),
696 (long int) CC->cs_UDSclientUID,
697 CtdlGetConfigStr("c_fqdn"),
698 nowstamp);
699 }
700 else {
701 StrBufPrintf(
702 defbody,
703 "Received: from %s (%s [%s])\n"
704 " by %s; %s\n",
705 ChrPtr(SMTP->helo_node),
706 CC->cs_host,
707 CC->cs_addr,
708 CtdlGetConfigStr("c_fqdn"),
709 nowstamp);
710 }
711 }
712 body = CtdlReadMessageBodyBuf(HKEY("."), CtdlGetConfigLong("c_maxmsglen"), defbody, 1);
713 FreeStrBuf(&defbody);
714 if (body == NULL) {
715 cprintf("550 Unable to save message: internal error.\r\n");
716 return;
717 }
718
719 syslog(LOG_DEBUG, "serv_smtp: converting message...");
720 msg = convert_internet_message_buf(&body);
721
722 // If the user is locally authenticated, FORCE the From: header to
723 // show up as the real sender. Yes, this violates the RFC standard,
724 // but IT MAKES SENSE. If you prefer strict RFC adherence over
725 // common sense, you can disable this in the configuration.
726 //
727 // We also set the "message room name" ('O' field) to MAILROOM
728 // (which is Mail> on most systems) to prevent it from getting set
729 // to something ugly like "0000058008.Sent Items>" when the message
730 // is read with a Citadel client.
731
732 if ( (CC->logged_in) && (CtdlGetConfigInt("c_rfc822_strict_from") != CFG_SMTP_FROM_NOFILTER) ) {
733 int validemail = 0;
734
735 if (!CM_IsEmpty(msg, erFc822Addr) &&
736 ((CtdlGetConfigInt("c_rfc822_strict_from") == CFG_SMTP_FROM_CORRECT) ||
737 (CtdlGetConfigInt("c_rfc822_strict_from") == CFG_SMTP_FROM_REJECT) ) )
738 {
739 if (!IsEmptyStr(CC->cs_inet_email)) {
740 validemail = strcmp(CC->cs_inet_email, msg->cm_fields[erFc822Addr]) == 0;
741 }
742 if ((!validemail) && (!IsEmptyStr(CC->cs_inet_other_emails))) {
743 int num_secondary_emails = 0;
744 int i;
745 num_secondary_emails = num_tokens(CC->cs_inet_other_emails, '|');
746 for (i=0; i < num_secondary_emails && !validemail; ++i) {
747 char buf[256];
748 extract_token(buf, CC->cs_inet_other_emails,i,'|',sizeof CC->cs_inet_other_emails);
749 validemail = strcmp(buf, msg->cm_fields[erFc822Addr]) == 0;
750 }
751 }
752 }
753
754 if (!validemail && (CtdlGetConfigInt("c_rfc822_strict_from") == CFG_SMTP_FROM_REJECT)) {
755 syslog(LOG_ERR, "serv_smtp: invalid sender '%s' - rejecting this message", msg->cm_fields[erFc822Addr]);
756 cprintf("550 Invalid sender '%s' - rejecting this message.\r\n", msg->cm_fields[erFc822Addr]);
757 return;
758 }
759
761 if (SMTP->preferred_sender_name != NULL)
762 CM_SetField(msg, eAuthor, SKEY(SMTP->preferred_sender_name));
763 else
764 CM_SetField(msg, eAuthor, CC->user.fullname, strlen(CC->user.fullname));
765
766 if (!validemail) {
767 if (SMTP->preferred_sender_email != NULL) {
768 CM_SetField(msg, erFc822Addr, SKEY(SMTP->preferred_sender_email));
769 }
770 else {
771 CM_SetField(msg, erFc822Addr, CC->cs_inet_email, strlen(CC->cs_inet_email));
772 }
773 }
774 }
775
776 // Set the "envelope from" address
777 CM_SetField(msg, eMessagePath, SKEY(SMTP->from));
778
779 // Set the "envelope to" address
780 CM_SetField(msg, eenVelopeTo, SKEY(SMTP->recipients));
781
782 // Submit the message into the Citadel system.
783 valid = validate_recipients(
784 (char *)ChrPtr(SMTP->recipients),
786 (SMTP->is_lmtp)? POST_LMTP: (CC->logged_in)? POST_LOGGED_IN: POST_EXTERNAL
787 );
788
789 // If there are modules that want to scan this message before final
790 // submission (such as virus checkers or spam filters), call them now
791 // and give them an opportunity to reject the message.
792 if (SMTP->is_unfiltered) {
793 scan_errors = 0;
794 }
795 else {
796 scan_errors = PerformMessageHooks(msg, valid, EVT_SMTPSCAN);
797 }
798
799 if (scan_errors > 0) { // We don't want this message!
800
801 if (CM_IsEmpty(msg, eErrorMsg)) {
802 CM_SetField(msg, eErrorMsg, HKEY("Message rejected by filter"));
803 }
804
805 StrBufPrintf(SMTP->OneRcpt, "550 %s\r\n", msg->cm_fields[eErrorMsg]);
806 }
807
808 else { // Ok, we'll accept this message.
809 msgnum = CtdlSubmitMsg(msg, valid, "");
810 if (msgnum > 0L) {
811 StrBufPrintf(SMTP->OneRcpt, "250 Message accepted.\r\n");
812 }
813 else {
814 StrBufPrintf(SMTP->OneRcpt, "550 Internal delivery error\r\n");
815 }
816 }
817
818 // For SMTP and ESMTP, just print the result message. For LMTP, we
819 // have to print one result message for each recipient. Since there
820 // is nothing in Citadel which would cause different recipients to
821 // have different results, we can get away with just spitting out the
822 // same message once for each recipient.
823 if (SMTP->is_lmtp) {
824 for (i=0; i<SMTP->number_of_recipients; ++i) {
825 cputbuf(SMTP->OneRcpt);
826 }
827 }
828 else {
829 cputbuf(SMTP->OneRcpt);
830 }
831
832 // Write something to the syslog(which may or may not be where the
833 // rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
834 syslog((LOG_MAIL | LOG_INFO),
835 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
836 msgnum,
837 ChrPtr(SMTP->from),
838 SMTP->number_of_recipients,
839 CC->cs_host,
840 CC->cs_addr,
841 ChrPtr(SMTP->OneRcpt)
842 );
843
844 // Clean up
845 CM_Free(msg);
846 free_recipients(valid);
847 smtp_data_clear(); // clear out the buffers now
848}
849
850
851// Implements the STARTTLS command
852void smtp_starttls(void) {
853 char ok_response[SIZ];
854 char nosup_response[SIZ];
855 char error_response[SIZ];
856
857 sprintf(ok_response, "220 Begin TLS negotiation now\r\n");
858 sprintf(nosup_response, "554 TLS not supported here\r\n");
859 sprintf(error_response, "554 Internal error\r\n");
860 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
861 smtp_rset(0);
862}
863
864
865// Implements the NOOP (NO OPeration) command
866void smtp_noop(void) {
867 cprintf("250 NOOP\r\n");
868}
869
870
871// Implements the QUIT command
872void smtp_quit(void) {
873 cprintf("221 Goodbye...\r\n");
874 CC->kill_me = KILLME_CLIENT_LOGGED_OUT;
875}
876
877
878// Main command loop for SMTP server sessions.
880 static const ConstStr AuthPlainStr = {HKEY("AUTH PLAIN")};
881
882 assert(SMTP != NULL);
883
884 time(&CC->lastcmd);
885 if (CtdlClientGetLine(SMTP->Cmd) < 1) {
886 syslog(LOG_INFO, "SMTP: client disconnected: ending session.");
888 return;
889 }
890
891 if (SMTP->command_state == smtp_user) {
892 if (!strncmp(ChrPtr(SMTP->Cmd), AuthPlainStr.Key, AuthPlainStr.len)) {
894 }
895 else {
896 smtp_get_user(0);
897 }
898 return;
899 }
900
901 else if (SMTP->command_state == smtp_password) {
903 return;
904 }
905
906 else if (SMTP->command_state == smtp_plain) {
908 return;
909 }
910
911 syslog(LOG_DEBUG, "serv_smtp: client sent command <%s>", ChrPtr(SMTP->Cmd));
912
913 if (!strncasecmp(ChrPtr(SMTP->Cmd), "NOOP", 4)) {
914 smtp_noop();
915 return;
916 }
917
918 if (!strncasecmp(ChrPtr(SMTP->Cmd), "QUIT", 4)) {
919 smtp_quit();
920 return;
921 }
922
923 if (!strncasecmp(ChrPtr(SMTP->Cmd), "HELO", 4)) {
925 return;
926 }
927
928 if (!strncasecmp(ChrPtr(SMTP->Cmd), "EHLO", 4)) {
930 return;
931 }
932
933 if (!strncasecmp(ChrPtr(SMTP->Cmd), "LHLO", 4)) {
935 return;
936 }
937
938 if (!strncasecmp(ChrPtr(SMTP->Cmd), "RSET", 4)) {
939 smtp_rset(1);
940 return;
941 }
942
943 if (!strncasecmp(ChrPtr(SMTP->Cmd), "AUTH", 4)) {
944 smtp_auth();
945 return;
946 }
947
948 if (!strncasecmp(ChrPtr(SMTP->Cmd), "DATA", 4)) {
949 smtp_data();
950 return;
951 }
952
953 if (!strncasecmp(ChrPtr(SMTP->Cmd), "HELP", 4)) {
954 smtp_help();
955 return;
956 }
957
958 if (!strncasecmp(ChrPtr(SMTP->Cmd), "MAIL", 4)) {
959 smtp_mail();
960 return;
961 }
962
963 if (!strncasecmp(ChrPtr(SMTP->Cmd), "RCPT", 4)) {
964 smtp_rcpt();
965 return;
966 }
967#ifdef HAVE_OPENSSL
968 if (!strncasecmp(ChrPtr(SMTP->Cmd), "STARTTLS", 8)) {
970 return;
971 }
972#endif
973
974 cprintf("502 I'm afraid I can't do that.\r\n");
975}
976
977
978// *****************************************************************************
979// * MODULE INITIALIZATION STUFF *
980// *****************************************************************************
981
982// This cleanup function blows away the temporary memory used by
983// the SMTP server.
985 // Don't do this stuff if this is not an SMTP session!
986 if (CC->h_command_function != smtp_command_loop) return;
987
988 syslog(LOG_DEBUG, "Performing SMTP cleanup hook");
989
990 FreeStrBuf(&SMTP->Cmd);
991 FreeStrBuf(&SMTP->helo_node);
992 FreeStrBuf(&SMTP->from);
993 FreeStrBuf(&SMTP->recipients);
994 FreeStrBuf(&SMTP->OneRcpt);
995 FreeStrBuf(&SMTP->preferred_sender_email);
996 FreeStrBuf(&SMTP->preferred_sender_name);
997
998 free(SMTP);
999}
1000
1001const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1002const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1003const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1004const char *CitadelServiceSMTP_LMTP="LMTP";
1005const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1006
1007
1008// Initialization function, called from modules_init.c
1010 if (!threading) {
1011 CtdlRegisterServiceHook(CtdlGetConfigInt("c_smtp_port"), // SMTP MTA
1012 NULL,
1015 NULL,
1017
1018#ifdef HAVE_OPENSSL
1019 CtdlRegisterServiceHook(CtdlGetConfigInt("c_smtps_port"), // SMTPS MTA
1020 NULL,
1023 NULL,
1025#endif
1026
1027 CtdlRegisterServiceHook(CtdlGetConfigInt("c_msa_port"), // SMTP MSA
1028 NULL,
1031 NULL,
1033
1034 CtdlRegisterServiceHook(0, // local LMTP
1038 NULL,
1040
1041 CtdlRegisterServiceHook(0, // local LMTP
1045 NULL,
1047
1049 }
1050
1051 // return our module name for the log
1052 return "smtp";
1053}
#define ROOMNAMELEN
Definition: citadel.h:51
#define file_lmtp_unfiltered_socket
Definition: citadel_dirs.h:33
#define file_lmtp_socket
Definition: citadel_dirs.h:32
char * CtdlGetConfigStr(char *key)
Definition: config.c:363
int CtdlGetConfigInt(char *key)
Definition: config.c:390
long CtdlGetConfigLong(char *key)
Definition: config.c:398
#define CC
Definition: context.h:140
void CtdlModuleStartCryptoMsgs(char *ok_response, char *nosup_response, char *error_response)
int CtdlGetRoom(struct ctdlroom *qrbuf, const char *room_name)
Definition: room_ops.c:309
void CtdlRegisterServiceHook(int tcp_port, char *sockpath, void(*h_greeting_function)(void), void(*h_command_function)(void), void(*h_async_function)(void), const char *ServiceName)
void CtdlRegisterSessionHook(void(*fcn_ptr)(void), int EventType, int Priority)
int threading
Definition: modules_init.c:24
@ pass_ok
Definition: ctdl_module.h:293
int CtdlTryPassword(const char *password, long len)
Definition: user_ops.c:772
@ login_ok
Definition: ctdl_module.h:305
int CtdlLoginExistingUser(const char *username)
Definition: user_ops.c:461
#define PRIO_STOP
Definition: ctdl_module.h:90
long datestring(char *buf, size_t n, time_t xtime, int which_format)
Definition: genstamp.c:26
@ DATESTRING_RFC822
Definition: genstamp.h:5
struct CtdlMessage * convert_internet_message_buf(StrBuf **rfc822)
struct recptypes * validate_recipients(char *supplied_recipients, const char *RemoteIdentifier, int Flags)
void free_recipients(struct recptypes *valid)
int CtdlHostAlias(char *fqdn)
void process_rfc822_addr(const char *rfc822, char *user, char *node, char *name)
int is_email_subscribed_to_list(char *email, char *room_name)
@ hostalias_nomatch
int rbl_check(char *cs_addr, char *message_to_spammer)
Definition: locate_host.c:226
void CM_Free(struct CtdlMessage *msg)
Definition: msgbase.c:305
StrBuf * CtdlReadMessageBodyBuf(char *terminator, long tlen, size_t maxlen, StrBuf *exist, int crlf)
Definition: msgbase.c:2961
int CtdlForEachMessage(int mode, long ref, char *search_string, char *content_type, struct CtdlMessage *compare, ForEachMsgCallback CallBack, void *userdata)
Definition: msgbase.c:621
struct CtdlMessage * CtdlFetchMessage(long msgnum, int with_body)
Definition: msgbase.c:1128
int CM_IsEmpty(struct CtdlMessage *Msg, eMsgField which)
Definition: msgbase.c:132
long CtdlSubmitMsg(struct CtdlMessage *msg, struct recptypes *recps, const char *force)
Definition: msgbase.c:2588
void CM_SetField(struct CtdlMessage *Msg, eMsgField which, const char *buf, long length)
Definition: msgbase.c:137
@ MSGS_ALL
Definition: msgbase.h:6
void * malloc(unsigned)
void free(void *)
@ POST_LMTP
Definition: room_ops.h:16
@ POST_LOGGED_IN
Definition: room_ops.h:13
@ POST_EXTERNAL
Definition: room_ops.h:14
int PerformMessageHooks(struct CtdlMessage *msg, struct recptypes *recps, int EventType)
const char * CitadelServiceSMTP_MTA
Definition: serv_smtp.c:1001
void smtp_rset(int do_response)
Definition: serv_smtp.c:460
void smtp_quit(void)
Definition: serv_smtp.c:872
void smtp_help(void)
Definition: serv_smtp.c:298
void smtp_get_user(int offset)
Definition: serv_smtp.c:303
void lmtp_unfiltered_greeting(void)
Definition: serv_smtp.c:165
void smtp_try_plain(void)
Definition: serv_smtp.c:344
void smtp_auth(void)
Definition: serv_smtp.c:402
const char * CitadelServiceSMTPS_MTA
Definition: serv_smtp.c:1002
void smtp_greeting(int is_msa)
Definition: serv_smtp.c:88
const char * CitadelServiceSMTP_LMTP
Definition: serv_smtp.c:1004
void smtp_cleanup_function(void)
Definition: serv_smtp.c:984
void smtp_rcpt(void)
Definition: serv_smtp.c:561
void smtp_get_pass(void)
Definition: serv_smtp.c:327
void smtp_data_clear(void)
Definition: serv_smtp.c:482
void smtp_auth_greeting(void)
Definition: serv_smtp.c:173
void smtp_mail(void)
Definition: serv_smtp.c:493
SMTP_FLAGS
Definition: serv_smtp.c:80
@ HELO
Definition: serv_smtp.c:81
@ EHLO
Definition: serv_smtp.c:82
@ LHLO
Definition: serv_smtp.c:83
void lmtp_greeting(void)
Definition: serv_smtp.c:151
void smtp_starttls(void)
Definition: serv_smtp.c:852
@ smtp_password
Definition: serv_smtp.c:76
@ smtp_plain
Definition: serv_smtp.c:77
@ smtp_user
Definition: serv_smtp.c:75
@ smtp_command
Definition: serv_smtp.c:74
void smtp_webcit_preferences_hack(void)
Definition: serv_smtp.c:260
const char * CitadelServiceSMTP_LMTP_UNF
Definition: serv_smtp.c:1005
void smtp_hello(int which_command)
Definition: serv_smtp.c:183
void smtp_msa_greeting(void)
Definition: serv_smtp.c:145
void smtp_noop(void)
Definition: serv_smtp.c:866
void smtp_webcit_preferences_hack_backend(long msgnum, void *userdata)
Definition: serv_smtp.c:236
void smtp_data(void)
Definition: serv_smtp.c:664
void smtps_greeting(void)
Definition: serv_smtp.c:135
void smtp_command_loop(void)
Definition: serv_smtp.c:879
const char * CitadelServiceSMTP_MSA
Definition: serv_smtp.c:1003
void smtp_mta_greeting(void)
Definition: serv_smtp.c:159
char * ctdl_module_init_smtp(void)
Definition: serv_smtp.c:1009
#define EVT_STOP
Definition: server.h:212
@ eMessagePath
Definition: server.h:317
@ eenVelopeTo
Definition: server.h:321
@ eErrorMsg
Definition: server.h:324
@ eMesageText
Definition: server.h:315
@ erFc822Addr
Definition: server.h:310
@ eAuthor
Definition: server.h:307
@ eMsgSubject
Definition: server.h:320
@ eOriginalRoom
Definition: server.h:316
#define CS_STEALTH
Definition: server.h:109
@ KILLME_SPAMMER
Definition: server.h:104
@ KILLME_CLIENT_DISCONNECTED
Definition: server.h:89
@ KILLME_CLIENT_LOGGED_OUT
Definition: server.h:87
@ KILLME_MAX_SESSIONS_EXCEEDED
Definition: server.h:92
@ KILLME_NO_CRYPTO
Definition: server.h:99
#define EVT_SMTPSCAN
Definition: server.h:233
const char * smtp_get_Recipients(void)
Definition: smtp_util.c:53
#define SMTP
Definition: smtp_util.h:33
char * cm_fields[256]
Definition: server.h:37
char * recp_room
Definition: server.h:56
int num_internet
Definition: server.h:50
int num_room
Definition: server.h:51
int num_error
Definition: server.h:52
char * errormsg
Definition: server.h:53
#define USERCONFIGROOM
Definition: sysconfig.h:64
#define MAILROOM
Definition: sysconfig.h:61
#define SIZ
Definition: sysconfig.h:33
int CtdlClientGetLine(StrBuf *Target)
Definition: sysdep.c:499
void cprintf(const char *format,...)
Definition: sysdep.c:369
void cputbuf(const StrBuf *Buf)
Definition: sysdep.c:362
int server_shutting_down
Definition: threads.c:26
int CtdlCheckInternetMailPermission(struct ctdluser *who)
Definition: user_ops.c:312