X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Fsmtp%2Fserv_smtp.c;h=0987cb80b04c99a492c3aac062c4bda4f6def0cc;hb=80f14552e61c41057242205ad967d0a3fb6b98ff;hp=ad9137ff3961073ae65e030a35f12d7af89c5f4a;hpb=951fbe7c1ee0b3554af22d6ae0d1d51c1642ae0f;p=citadel.git diff --git a/citadel/modules/smtp/serv_smtp.c b/citadel/modules/smtp/serv_smtp.c index ad9137ff3..0987cb80b 100644 --- a/citadel/modules/smtp/serv_smtp.c +++ b/citadel/modules/smtp/serv_smtp.c @@ -20,15 +20,15 @@ * The VRFY and EXPN commands have been removed from this implementation * because nobody uses these commands anymore, except for spammers. * - * Copyright (c) 1998-2012 by the citadel.org team + * Copyright (c) 1998-2021 by the citadel.org team * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3. + * This program is open source software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #include "sysdep.h" @@ -42,18 +42,7 @@ #include #include #include - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - +#include #include #include #include @@ -69,6 +58,7 @@ #include "config.h" #include "control.h" #include "user_ops.h" +#include "room_ops.h" #include "database.h" #include "msgbase.h" #include "internet_addressing.h" @@ -80,6 +70,7 @@ #include "ctdl_module.h" #include "smtp_util.h" + enum { /* Command states for login authentication */ smtp_command, smtp_user, @@ -93,66 +84,32 @@ enum SMTP_FLAGS { LHLO }; -typedef void (*smtp_handler)(long offest, long Flags); - -typedef struct _smtp_handler_hook { - smtp_handler h; - int Flags; -} smtp_handler_hook; - -HashList *SMTPCmds = NULL; -#define MaxSMTPCmdLen 10 - -#define RegisterSmtpCMD(First, H, Flags) \ - registerSmtpCMD(HKEY(First), H, Flags) -void registerSmtpCMD(const char *First, long FLen, - smtp_handler H, - int Flags) -{ - smtp_handler_hook *h; - - if (FLen >= MaxSMTPCmdLen) - cit_panic_backtrace (0); - - h = (smtp_handler_hook*) malloc(sizeof(smtp_handler_hook)); - memset(h, 0, sizeof(smtp_handler_hook)); - - h->Flags = Flags; - h->h = H; - Put(SMTPCmds, First, FLen, h, NULL); -} - -void smtp_cleanup(void) -{ - DeleteHash(&SMTPCmds); -} /* * Here's where our SMTP session begins its happy day. */ -void smtp_greeting(int is_msa) -{ - citsmtp *sSMTP; +void smtp_greeting(int is_msa) { char message_to_spammer[1024]; strcpy(CC->cs_clientname, "SMTP session"); CC->internal_pgm = 1; CC->cs_flags |= CS_STEALTH; - CC->session_specific_data = malloc(sizeof(citsmtp)); - memset(SMTP, 0, sizeof(citsmtp)); - sSMTP = SMTP; - sSMTP->is_msa = is_msa; - sSMTP->Cmd = NewStrBufPlain(NULL, SIZ); - sSMTP->helo_node = NewStrBuf(); - sSMTP->from = NewStrBufPlain(NULL, SIZ); - sSMTP->recipients = NewStrBufPlain(NULL, SIZ); - sSMTP->OneRcpt = NewStrBufPlain(NULL, SIZ); + CC->session_specific_data = malloc(sizeof(struct citsmtp)); + memset(SMTP, 0, sizeof(struct citsmtp)); + SMTP->is_msa = is_msa; + SMTP->Cmd = NewStrBufPlain(NULL, SIZ); + SMTP->helo_node = NewStrBuf(); + SMTP->from = NewStrBufPlain(NULL, SIZ); + SMTP->recipients = NewStrBufPlain(NULL, SIZ); + SMTP->OneRcpt = NewStrBufPlain(NULL, SIZ); + SMTP->preferred_sender_email = NULL; + SMTP->preferred_sender_name = NULL; /* If this config option is set, reject connections from problem * addresses immediately instead of after they execute a RCPT */ - if ( (config.c_rbl_at_greeting) && (sSMTP->is_msa == 0) ) { - if (rbl_check(message_to_spammer)) { + if ( (CtdlGetConfigInt("c_rbl_at_greeting")) && (SMTP->is_msa == 0) ) { + if (rbl_check(CC->cs_addr, message_to_spammer)) { if (server_shutting_down) cprintf("421 %s\r\n", message_to_spammer); else @@ -175,7 +132,7 @@ void smtp_greeting(int is_msa) /* Note: the FQDN *must* appear as the first thing after the 220 code. * Some clients (including citmail.c) depend on it being there. */ - cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn); + cprintf("220 %s ESMTP Citadel server ready.\r\n", CtdlGetConfigStr("c_fqdn")); } @@ -221,21 +178,18 @@ void smtp_mta_greeting(void) { * We also have an unfiltered LMTP socket that bypasses spam filters. */ void lmtp_unfiltered_greeting(void) { - citsmtp *sSMTP; - smtp_greeting(0); - sSMTP = SMTP; - sSMTP->is_lmtp = 1; - sSMTP->is_unfiltered = 1; + SMTP->is_lmtp = 1; + SMTP->is_unfiltered = 1; } /* * Login greeting common to all auth methods */ -void smtp_auth_greeting(long offset, long Flags) { +void smtp_auth_greeting(void) { cprintf("235 Hello, %s\r\n", CC->user.fullname); - syslog(LOG_NOTICE, "SMTP authenticated %s\n", CC->user.fullname); + syslog(LOG_INFO, "serv_smtp: SMTP authenticated %s", CC->user.fullname); CC->internal_pgm = 0; CC->cs_flags &= ~CS_STEALTH; } @@ -246,25 +200,25 @@ void smtp_auth_greeting(long offset, long Flags) { * * which_command: 0=HELO, 1=EHLO, 2=LHLO */ -void smtp_hello(long offset, long which_command) -{ - citsmtp *sSMTP = SMTP; +void smtp_hello(int which_command) { - StrBufAppendBuf (sSMTP->helo_node, sSMTP->Cmd, offset); + if (StrLength(SMTP->Cmd) >= 6) { + StrBufAppendBuf(SMTP->helo_node, SMTP->Cmd, 5); + } - if ( (which_command != LHLO) && (sSMTP->is_lmtp) ) { + if ( (which_command != LHLO) && (SMTP->is_lmtp) ) { cprintf("500 Only LHLO is allowed when running LMTP\r\n"); return; } - if ( (which_command == LHLO) && (sSMTP->is_lmtp == 0) ) { + if ( (which_command == LHLO) && (SMTP->is_lmtp == 0) ) { cprintf("500 LHLO is only allowed when running LMTP\r\n"); return; } if (which_command == HELO) { cprintf("250 Hello %s (%s [%s])\r\n", - ChrPtr(sSMTP->helo_node), + ChrPtr(SMTP->helo_node), CC->cs_host, CC->cs_addr ); @@ -272,7 +226,7 @@ void smtp_hello(long offset, long which_command) else { if (which_command == EHLO) { cprintf("250-Hello %s (%s [%s])\r\n", - ChrPtr(sSMTP->helo_node), + ChrPtr(SMTP->helo_node), CC->cs_host, CC->cs_addr ); @@ -281,7 +235,7 @@ void smtp_hello(long offset, long which_command) cprintf("250-Greetings and joyous salutations.\r\n"); } cprintf("250-HELP\r\n"); - cprintf("250-SIZE %ld\r\n", config.c_maxmsglen); + cprintf("250-SIZE %ld\r\n", CtdlGetConfigLong("c_maxmsglen")); #ifdef HAVE_OPENSSL /* @@ -290,7 +244,7 @@ void smtp_hello(long offset, long which_command) * the SMTP-MSA port, not on the SMTP-MTA port, due to * questionable reliability of TLS in certain sending MTA's. */ - if ( (!CC->redirect_ssl) && (sSMTP->is_msa) ) { + if ( (!CC->redirect_ssl) && (SMTP->is_msa) ) { cprintf("250-STARTTLS\r\n"); } #endif /* HAVE_OPENSSL */ @@ -320,12 +274,12 @@ void smtp_webcit_preferences_hack_backend(long msgnum, void *userdata) { return; } - if ( (msg->cm_fields[eMsgSubject]) && (!strcasecmp(msg->cm_fields[eMsgSubject], "__ WebCit Preferences __")) ) { + if ( !CM_IsEmpty(msg, eMsgSubject) && (!strcasecmp(msg->cm_fields[eMsgSubject], "__ WebCit Preferences __"))) { /* This is it! Change ownership of the message text so it doesn't get freed. */ *webcit_conf = (char *)msg->cm_fields[eMesageText]; msg->cm_fields[eMesageText] = NULL; } - CtdlFreeMessage(msg); + CM_Free(msg); } @@ -352,18 +306,31 @@ void smtp_webcit_preferences_hack(void) { return; } - /* FIXME : now do something with this data */ + /* Parse the webcit configuration and attempt to do something useful with it */ + char *str = webcit_conf; + char *saveptr = str; + char *this_line = NULL; + while (this_line = strtok_r(str, "\n", &saveptr), this_line != NULL) { + str = NULL; + if (!strncasecmp(this_line, "defaultfrom|", 12)) { + SMTP->preferred_sender_email = NewStrBufPlain(&this_line[12], -1); + } + if (!strncasecmp(this_line, "defaultname|", 12)) { + SMTP->preferred_sender_name = NewStrBufPlain(&this_line[12], -1); + } + if ((!strncasecmp(this_line, "defaultname|", 12)) && (SMTP->preferred_sender_name == NULL)) { + SMTP->preferred_sender_name = NewStrBufPlain(&this_line[12], -1); + } + } free(webcit_conf); - abort(); } - /* * Implement HELP command. */ -void smtp_help(long offset, long Flags) { +void smtp_help(void) { cprintf("214 RTFM http://www.ietf.org/rfc/rfc2821.txt\r\n"); } @@ -371,81 +338,103 @@ void smtp_help(long offset, long Flags) { /* * */ -void smtp_get_user(long offset) -{ +void smtp_get_user(int offset) { char buf[SIZ]; - char username[SIZ]; - citsmtp *sSMTP = SMTP; - CtdlDecodeBase64(username, ChrPtr(sSMTP->Cmd) + offset, SIZ); - /* syslog(LOG_DEBUG, "Trying <%s>\n", username); */ - if (CtdlLoginExistingUser(NULL, username) == login_ok) { - CtdlEncodeBase64(buf, "Password:", 9, 0); + StrBuf *UserName = NewStrBufDup(SMTP->Cmd); + StrBufCutLeft(UserName, offset); + StrBufDecodeBase64(UserName); + + if (CtdlLoginExistingUser(ChrPtr(UserName)) == login_ok) { + size_t len = CtdlEncodeBase64(buf, "Password:", 9, 0); + + if (buf[len - 1] == '\n') { + buf[len - 1] = '\0'; + } cprintf("334 %s\r\n", buf); - sSMTP->command_state = smtp_password; + SMTP->command_state = smtp_password; } else { cprintf("500 No such user.\r\n"); - sSMTP->command_state = smtp_command; + SMTP->command_state = smtp_command; } + FreeStrBuf(&UserName); } /* * */ -void smtp_get_pass(long offset, long Flags) +void smtp_get_pass(void) { - citsmtp *sSMTP = SMTP; char password[SIZ]; - long len; - memset(password, 0, sizeof(password)); - len = CtdlDecodeBase64(password, ChrPtr(sSMTP->Cmd), SIZ); - /* syslog(LOG_DEBUG, "Trying <%s>\n", password); */ - if (CtdlTryPassword(password, len) == pass_ok) { - smtp_auth_greeting(offset, Flags); + memset(password, 0, sizeof(password)); + StrBufDecodeBase64(SMTP->Cmd); + syslog(LOG_DEBUG, "serv_smtp: trying <%s>", password); + if (CtdlTryPassword(SKEY(SMTP->Cmd)) == pass_ok) { + smtp_auth_greeting(); } else { cprintf("535 Authentication failed.\r\n"); } - sSMTP->command_state = smtp_command; + SMTP->command_state = smtp_command; } /* * Back end for PLAIN auth method (either inline or multistate) */ -void smtp_try_plain(long offset, long Flags) -{ - citsmtp *sSMTP = SMTP; - char decoded_authstring[1024]; - char ident[256]; - char user[256]; - char pass[256]; +void smtp_try_plain(void) { + const char*decoded_authstring; + char ident[256] = ""; + char user[256] = ""; + char pass[256] = ""; int result; - long len; - CtdlDecodeBase64(decoded_authstring, ChrPtr(sSMTP->Cmd), StrLength(sSMTP->Cmd)); - safestrncpy(ident, decoded_authstring, sizeof ident); - safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user); - len = safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass); - if (len == -1) - len = sizeof(pass) - 1; + long decoded_len; + long len = 0; + long plen = 0; + + memset(pass, 0, sizeof(pass)); + decoded_len = StrBufDecodeBase64(SMTP->Cmd); + + if (decoded_len > 0) { + decoded_authstring = ChrPtr(SMTP->Cmd); + + len = safestrncpy(ident, decoded_authstring, sizeof ident); + + decoded_len -= len - 1; + decoded_authstring += len + 1; + + if (decoded_len > 0) { + len = safestrncpy(user, decoded_authstring, sizeof user); + + decoded_authstring += len + 1; + decoded_len -= len - 1; + } + + if (decoded_len > 0) { + plen = safestrncpy(pass, decoded_authstring, sizeof pass); + + if (plen < 0) + plen = sizeof(pass) - 1; + } + } - sSMTP->command_state = smtp_command; + SMTP->command_state = smtp_command; if (!IsEmptyStr(ident)) { - result = CtdlLoginExistingUser(user, ident); + result = CtdlLoginExistingUser(ident); } else { - result = CtdlLoginExistingUser(NULL, user); + result = CtdlLoginExistingUser(user); } if (result == login_ok) { - if (CtdlTryPassword(pass, len) == pass_ok) { -//// smtp_webcit_preferences_hack(); - smtp_auth_greeting(offset, Flags); + if (CtdlTryPassword(pass, plen) == pass_ok) { + smtp_webcit_preferences_hack(); + smtp_auth_greeting(); return; } } @@ -456,9 +445,7 @@ void smtp_try_plain(long offset, long Flags) /* * Attempt to perform authenticated SMTP */ -void smtp_auth(long offset, long Flags) -{ - citsmtp *sSMTP = SMTP; +void smtp_auth(void) { char username_prompt[64]; char method[64]; char encoded_authstring[1024]; @@ -468,42 +455,48 @@ void smtp_auth(long offset, long Flags) return; } - extract_token(method, ChrPtr(sSMTP->Cmd) + offset, 0, ' ', sizeof method); + if (StrLength(SMTP->Cmd) < 6) { + cprintf("501 Syntax error\r\n"); + return; + } + + extract_token(method, ChrPtr(SMTP->Cmd) + 5, 0, ' ', sizeof method); if (!strncasecmp(method, "login", 5) ) { - if (StrLength(sSMTP->Cmd) - offset >= 7) { - smtp_get_user(6); + if (StrLength(SMTP->Cmd) >= 12) { + syslog(LOG_DEBUG, "serv_smtp: username <%s> supplied inline", ChrPtr(SMTP->Cmd)+11); + smtp_get_user(11); } else { - CtdlEncodeBase64(username_prompt, "Username:", 9, 0); + size_t len = CtdlEncodeBase64(username_prompt, "Username:", 9, 0); + if (username_prompt[len - 1] == '\n') { + username_prompt[len - 1] = '\0'; + } cprintf("334 %s\r\n", username_prompt); - sSMTP->command_state = smtp_user; + SMTP->command_state = smtp_user; } return; } if (!strncasecmp(method, "plain", 5) ) { long len; - if (num_tokens(ChrPtr(sSMTP->Cmd) + offset, ' ') < 2) { + if (num_tokens(ChrPtr(SMTP->Cmd) + 5, ' ') < 2) { cprintf("334 \r\n"); SMTP->command_state = smtp_plain; return; } len = extract_token(encoded_authstring, - ChrPtr(sSMTP->Cmd) + offset, + ChrPtr(SMTP->Cmd) + 5, 1, ' ', sizeof encoded_authstring); - StrBufPlain(sSMTP->Cmd, encoded_authstring, len); - smtp_try_plain(0, Flags); - return; - } - - if (strncasecmp(method, "login", 5) ) { - cprintf("504 Unknown authentication method.\r\n"); + StrBufPlain(SMTP->Cmd, encoded_authstring, len); + smtp_try_plain(); return; } + cprintf("504 Unknown authentication method.\r\n"); + return; } @@ -515,26 +508,24 @@ void smtp_auth(long offset, long Flags) * * Set do_response to nonzero to output the SMTP RSET response code. */ -void smtp_rset(long offset, long do_response) { - citsmtp *sSMTP = SMTP; - +void smtp_rset(int do_response) { /* * Our entire SMTP state is discarded when a RSET command is issued, * but we need to preserve this one little piece of information, so * we save it for later. */ - FlushStrBuf(sSMTP->Cmd); - FlushStrBuf(sSMTP->helo_node); - FlushStrBuf(sSMTP->from); - FlushStrBuf(sSMTP->recipients); - FlushStrBuf(sSMTP->OneRcpt); - - sSMTP->command_state = 0; - sSMTP->number_of_recipients = 0; - sSMTP->delivery_mode = 0; - sSMTP->message_originated_locally = 0; - sSMTP->is_msa = 0; + FlushStrBuf(SMTP->Cmd); + FlushStrBuf(SMTP->helo_node); + FlushStrBuf(SMTP->from); + FlushStrBuf(SMTP->recipients); + FlushStrBuf(SMTP->OneRcpt); + + SMTP->command_state = 0; + SMTP->number_of_recipients = 0; + SMTP->delivery_mode = 0; + SMTP->message_originated_locally = 0; + SMTP->is_msa = 0; /* * we must remember is_lmtp & is_unfiltered. */ @@ -555,45 +546,48 @@ void smtp_rset(long offset, long do_response) { } } + /* * Clear out the portions of the state buffer that need to be cleared out * after the DATA command finishes. */ -void smtp_data_clear(long offset, long flags) -{ - citsmtp *sSMTP = SMTP; - - FlushStrBuf(sSMTP->from); - FlushStrBuf(sSMTP->recipients); - FlushStrBuf(sSMTP->OneRcpt); - sSMTP->number_of_recipients = 0; - sSMTP->delivery_mode = 0; - sSMTP->message_originated_locally = 0; +void smtp_data_clear(void) { + FlushStrBuf(SMTP->from); + FlushStrBuf(SMTP->recipients); + FlushStrBuf(SMTP->OneRcpt); + SMTP->number_of_recipients = 0; + SMTP->delivery_mode = 0; + SMTP->message_originated_locally = 0; } + /* * Implements the "MAIL FROM:" command */ -void smtp_mail(long offset, long flags) { +void smtp_mail(void) { char user[SIZ]; char node[SIZ]; char name[SIZ]; - citsmtp *sSMTP = SMTP; - if (StrLength(sSMTP->from) > 0) { + if (StrLength(SMTP->from) > 0) { cprintf("503 Only one sender permitted\r\n"); return; } - if (strncasecmp(ChrPtr(sSMTP->Cmd) + offset, "From:", 5)) { + if (StrLength(SMTP->Cmd) < 6) { cprintf("501 Syntax error\r\n"); return; } - StrBufAppendBuf(sSMTP->from, sSMTP->Cmd, offset); - StrBufTrim(sSMTP->from); - if (strchr(ChrPtr(sSMTP->from), '<') != NULL) { - StrBufStripAllBut(sSMTP->from, '<', '>'); + if (strncasecmp(ChrPtr(SMTP->Cmd) + 5, "From:", 5)) { + cprintf("501 Syntax error\r\n"); + return; + } + + StrBufAppendBuf(SMTP->from, SMTP->Cmd, 5); + StrBufTrim(SMTP->from); + if (strchr(ChrPtr(SMTP->from), '<') != NULL) { + StrBufStripAllBut(SMTP->from, '<', '>'); } /* We used to reject empty sender names, until it was brought to our @@ -602,36 +596,36 @@ void smtp_mail(long offset, long flags) { * address so we don't have to contend with the empty string causing * other code to fail when it's expecting something there. */ - if (StrLength(sSMTP->from)) { - StrBufPlain(sSMTP->from, HKEY("someone@example.com")); + if (StrLength(SMTP->from) == 0) { + StrBufPlain(SMTP->from, HKEY("someone@example.com")); } /* If this SMTP connection is from a logged-in user, force the 'from' * to be the user's Internet e-mail address as Citadel knows it. */ if (CC->logged_in) { - StrBufPlain(sSMTP->from, CC->cs_inet_email, -1); - cprintf("250 Sender ok <%s>\r\n", ChrPtr(sSMTP->from)); - sSMTP->message_originated_locally = 1; + StrBufPlain(SMTP->from, CC->cs_inet_email, -1); + cprintf("250 Sender ok <%s>\r\n", ChrPtr(SMTP->from)); + SMTP->message_originated_locally = 1; return; } - else if (sSMTP->is_lmtp) { + else if (SMTP->is_lmtp) { /* Bypass forgery checking for LMTP */ } /* Otherwise, make sure outsiders aren't trying to forge mail from * this system (unless, of course, c_allow_spoofing is enabled) */ - else if (config.c_allow_spoofing == 0) { - process_rfc822_addr(ChrPtr(sSMTP->from), user, node, name); - syslog(LOG_DEBUG, "Claimed envelope sender is '%s' == '%s' @ '%s' ('%s')", - ChrPtr(sSMTP->from), user, node, name + else if (CtdlGetConfigInt("c_allow_spoofing") == 0) { + process_rfc822_addr(ChrPtr(SMTP->from), user, node, name); + syslog(LOG_DEBUG, "serv_smtp: claimed envelope sender is '%s' == '%s' @ '%s' ('%s')", + ChrPtr(SMTP->from), user, node, name ); if (CtdlHostAlias(node) != hostalias_nomatch) { cprintf("550 You must log in to send mail from %s\r\n", node); - FlushStrBuf(sSMTP->from); - syslog(LOG_DEBUG, "Rejecting unauthenticated mail from %s", node); + FlushStrBuf(SMTP->from); + syslog(LOG_DEBUG, "serv_smtp: rejecting unauthenticated mail from %s", node); return; } } @@ -640,47 +634,49 @@ void smtp_mail(long offset, long flags) { } - /* * Implements the "RCPT To:" command */ -void smtp_rcpt(long offset, long flags) -{ - struct CitContext *CCC = CC; +void smtp_rcpt(void) { char message_to_spammer[SIZ]; struct recptypes *valid = NULL; - citsmtp *sSMTP = SMTP; - if (StrLength(sSMTP->from) == 0) { + if (StrLength(SMTP->from) == 0) { cprintf("503 Need MAIL before RCPT\r\n"); return; } - if (strncasecmp(ChrPtr(sSMTP->Cmd) + offset, "To:", 3)) { + if (StrLength(SMTP->Cmd) < 6) { + cprintf("501 Syntax error\r\n"); + return; + } + + if (strncasecmp(ChrPtr(SMTP->Cmd) + 5, "To:", 3)) { cprintf("501 Syntax error\r\n"); return; } - if ( (sSMTP->is_msa) && (!CCC->logged_in) ) { + if ( (SMTP->is_msa) && (!CC->logged_in) ) { cprintf("550 You must log in to send mail on this port.\r\n"); - FlushStrBuf(sSMTP->from); + FlushStrBuf(SMTP->from); return; } - FlushStrBuf(sSMTP->OneRcpt); - StrBufAppendBuf(sSMTP->OneRcpt, sSMTP->Cmd, offset + 3); - StrBufTrim(sSMTP->OneRcpt); - StrBufStripAllBut(sSMTP->OneRcpt, '<', '>'); - if ( (StrLength(sSMTP->OneRcpt) + StrLength(sSMTP->recipients)) >= SIZ) { + FlushStrBuf(SMTP->OneRcpt); + StrBufAppendBuf(SMTP->OneRcpt, SMTP->Cmd, 8); + StrBufTrim(SMTP->OneRcpt); + StrBufStripAllBut(SMTP->OneRcpt, '<', '>'); + + if ( (StrLength(SMTP->OneRcpt) + StrLength(SMTP->recipients)) >= SIZ) { cprintf("452 Too many recipients\r\n"); return; } /* RBL check */ - if ( (!CCC->logged_in) /* Don't RBL authenticated users */ - && (!sSMTP->is_lmtp) ) { /* Don't RBL LMTP clients */ - if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */ - if (rbl_check(message_to_spammer)) { + if ( (!CC->logged_in) /* Don't RBL authenticated users */ + && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */ + if (CtdlGetConfigInt("c_rbl_at_greeting") == 0) { /* Don't RBL again if we already did it */ + if (rbl_check(CC->cs_addr, message_to_spammer)) { if (server_shutting_down) cprintf("421 %s\r\n", message_to_spammer); else @@ -692,9 +688,9 @@ void smtp_rcpt(long offset, long flags) } valid = validate_recipients( - ChrPtr(sSMTP->OneRcpt), + ChrPtr(SMTP->OneRcpt), smtp_get_Recipients(), - (sSMTP->is_lmtp)? POST_LMTP: (CCC->logged_in)? POST_LOGGED_IN: POST_EXTERNAL + (SMTP->is_lmtp)? POST_LMTP: (CC->logged_in)? POST_LOGGED_IN: POST_EXTERNAL ); if (valid->num_error != 0) { cprintf("550 %s\r\n", valid->errormsg); @@ -703,10 +699,10 @@ void smtp_rcpt(long offset, long flags) } if (valid->num_internet > 0) { - if (CCC->logged_in) { - if (CtdlCheckInternetMailPermission(&CCC->user)==0) { + if (CC->logged_in) { + if (CtdlCheckInternetMailPermission(&CC->user)==0) { cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", - ChrPtr(sSMTP->OneRcpt)); + ChrPtr(SMTP->OneRcpt)); free_recipients(valid); return; } @@ -714,34 +710,30 @@ void smtp_rcpt(long offset, long flags) } if (valid->num_internet > 0) { - if ( (sSMTP->message_originated_locally == 0) - && (sSMTP->is_lmtp == 0) ) { - cprintf("551 <%s> - relaying denied\r\n", ChrPtr(sSMTP->OneRcpt)); + if ( (SMTP->message_originated_locally == 0) + && (SMTP->is_lmtp == 0) ) { + cprintf("551 <%s> - relaying denied\r\n", ChrPtr(SMTP->OneRcpt)); free_recipients(valid); return; } } - cprintf("250 RCPT ok <%s>\r\n", ChrPtr(sSMTP->OneRcpt)); - if (StrLength(sSMTP->recipients) > 0) { - StrBufAppendBufPlain(sSMTP->recipients, HKEY(","), 0); + cprintf("250 RCPT ok <%s>\r\n", ChrPtr(SMTP->OneRcpt)); + if (StrLength(SMTP->recipients) > 0) { + StrBufAppendBufPlain(SMTP->recipients, HKEY(","), 0); } - StrBufAppendBuf(sSMTP->recipients, sSMTP->OneRcpt, 0); - sSMTP->number_of_recipients ++; + StrBufAppendBuf(SMTP->recipients, SMTP->OneRcpt, 0); + SMTP->number_of_recipients ++; if (valid != NULL) { free_recipients(valid); } } - - /* * Implements the DATA command */ -void smtp_data(long offset, long flags) -{ - struct CitContext *CCC = CC; +void smtp_data(void) { StrBuf *body; StrBuf *defbody; struct CtdlMessage *msg = NULL; @@ -750,14 +742,13 @@ void smtp_data(long offset, long flags) struct recptypes *valid; int scan_errors; int i; - citsmtp *sSMTP = SMTP; - if (StrLength(sSMTP->from) == 0) { + if (StrLength(SMTP->from) == 0) { cprintf("503 Need MAIL command first.\r\n"); return; } - if (sSMTP->number_of_recipients < 1) { + if (SMTP->number_of_recipients < 1) { cprintf("503 Need RCPT command first.\r\n"); return; } @@ -768,14 +759,14 @@ void smtp_data(long offset, long flags) defbody = NewStrBufPlain(NULL, SIZ); if (defbody != NULL) { - if (sSMTP->is_lmtp && (CCC->cs_UDSclientUID != -1)) { + if (SMTP->is_lmtp && (CC->cs_UDSclientUID != -1)) { StrBufPrintf( defbody, "Received: from %s (Citadel from userid %ld)\n" " by %s; %s\n", - ChrPtr(sSMTP->helo_node), - (long int) CCC->cs_UDSclientUID, - config.c_fqdn, + ChrPtr(SMTP->helo_node), + (long int) CC->cs_UDSclientUID, + CtdlGetConfigStr("c_fqdn"), nowstamp); } else { @@ -783,21 +774,21 @@ void smtp_data(long offset, long flags) defbody, "Received: from %s (%s [%s])\n" " by %s; %s\n", - ChrPtr(sSMTP->helo_node), - CCC->cs_host, - CCC->cs_addr, - config.c_fqdn, + ChrPtr(SMTP->helo_node), + CC->cs_host, + CC->cs_addr, + CtdlGetConfigStr("c_fqdn"), nowstamp); } } - body = CtdlReadMessageBodyBuf(HKEY("."), config.c_maxmsglen, defbody, 1, NULL); + body = CtdlReadMessageBodyBuf(HKEY("."), CtdlGetConfigLong("c_maxmsglen"), defbody, 1); FreeStrBuf(&defbody); if (body == NULL) { cprintf("550 Unable to save message: internal error.\r\n"); return; } - syslog(LOG_DEBUG, "Converting message...\n"); + syslog(LOG_DEBUG, "serv_smtp: converting message..."); msg = convert_internet_message_buf(&body); /* If the user is locally authenticated, FORCE the From: header to @@ -810,96 +801,91 @@ void smtp_data(long offset, long flags) * to something ugly like "0000058008.Sent Items>" when the message * is read with a Citadel client. */ - if ( (CCC->logged_in) && (config.c_rfc822_strict_from != CFG_SMTP_FROM_NOFILTER) ) { + if ( (CC->logged_in) && (CtdlGetConfigInt("c_rfc822_strict_from") != CFG_SMTP_FROM_NOFILTER) ) { int validemail = 0; - if (!IsEmptyStr(msg->cm_fields[erFc822Addr]) && - ((config.c_rfc822_strict_from == CFG_SMTP_FROM_CORRECT) || - (config.c_rfc822_strict_from == CFG_SMTP_FROM_REJECT) ) ) + if (!CM_IsEmpty(msg, erFc822Addr) && + ((CtdlGetConfigInt("c_rfc822_strict_from") == CFG_SMTP_FROM_CORRECT) || + (CtdlGetConfigInt("c_rfc822_strict_from") == CFG_SMTP_FROM_REJECT) ) ) { - if (!IsEmptyStr(CCC->cs_inet_email)) - validemail = strcmp(CCC->cs_inet_email, msg->cm_fields[erFc822Addr]) == 0; + if (!IsEmptyStr(CC->cs_inet_email)) + validemail = strcmp(CC->cs_inet_email, msg->cm_fields[erFc822Addr]) == 0; if ((!validemail) && - (!IsEmptyStr(CCC->cs_inet_other_emails))) + (!IsEmptyStr(CC->cs_inet_other_emails))) { int num_secondary_emails = 0; int i; - num_secondary_emails = num_tokens(CCC->cs_inet_other_emails, '|'); + num_secondary_emails = num_tokens(CC->cs_inet_other_emails, '|'); for (i=0; i < num_secondary_emails && !validemail; ++i) { char buf[256]; - extract_token(buf, CCC->cs_inet_other_emails,i,'|',sizeof CCC->cs_inet_other_emails); + extract_token(buf, CC->cs_inet_other_emails,i,'|',sizeof CC->cs_inet_other_emails); validemail = strcmp(buf, msg->cm_fields[erFc822Addr]) == 0; } } } - if (!validemail && (config.c_rfc822_strict_from == CFG_SMTP_FROM_REJECT)) { - syslog(LOG_ERR, "invalid sender '%s' - rejecting this message", msg->cm_fields[erFc822Addr]); + if (!validemail && (CtdlGetConfigInt("c_rfc822_strict_from") == CFG_SMTP_FROM_REJECT)) { + syslog(LOG_ERR, "serv_smtp: invalid sender '%s' - rejecting this message", msg->cm_fields[erFc822Addr]); cprintf("550 Invalid sender '%s' - rejecting this message.\r\n", msg->cm_fields[erFc822Addr]); return; } - if (msg->cm_fields[eAuthor] != NULL) free(msg->cm_fields[eAuthor]); - if (msg->cm_fields[eNodeName] != NULL) free(msg->cm_fields[eNodeName]); - if (msg->cm_fields[eHumanNode] != NULL) free(msg->cm_fields[eHumanNode]); - if (msg->cm_fields[eOriginalRoom] != NULL) free(msg->cm_fields[eOriginalRoom]); - msg->cm_fields[eAuthor] = strdup(CCC->user.fullname); - msg->cm_fields[eNodeName] = strdup(config.c_nodename); - msg->cm_fields[eHumanNode] = strdup(config.c_humannode); - msg->cm_fields[eOriginalRoom] = strdup(MAILROOM); + CM_SetField(msg, eOriginalRoom, HKEY(MAILROOM)); + if (SMTP->preferred_sender_name != NULL) + CM_SetField(msg, eAuthor, SKEY(SMTP->preferred_sender_name)); + else + CM_SetField(msg, eAuthor, CC->user.fullname, strlen(CC->user.fullname)); if (!validemail) { - if (msg->cm_fields[erFc822Addr] != NULL) free(msg->cm_fields[erFc822Addr]); - msg->cm_fields[erFc822Addr] = strdup(CCC->cs_inet_email); + if (SMTP->preferred_sender_email != NULL) { + CM_SetField(msg, erFc822Addr, SKEY(SMTP->preferred_sender_email)); + } + else { + CM_SetField(msg, erFc822Addr, CC->cs_inet_email, strlen(CC->cs_inet_email)); + } } } /* Set the "envelope from" address */ - if (msg->cm_fields[eMessagePath] != NULL) { - free(msg->cm_fields[eMessagePath]); - } - msg->cm_fields[eMessagePath] = strdup(ChrPtr(sSMTP->from)); + CM_SetField(msg, eMessagePath, SKEY(SMTP->from)); /* Set the "envelope to" address */ - if (msg->cm_fields[eenVelopeTo] != NULL) { - free(msg->cm_fields[eenVelopeTo]); - } - msg->cm_fields[eenVelopeTo] = strdup(ChrPtr(sSMTP->recipients)); + CM_SetField(msg, eenVelopeTo, SKEY(SMTP->recipients)); /* Submit the message into the Citadel system. */ valid = validate_recipients( - ChrPtr(sSMTP->recipients), + ChrPtr(SMTP->recipients), smtp_get_Recipients(), - (sSMTP->is_lmtp)? POST_LMTP: (CCC->logged_in)? POST_LOGGED_IN: POST_EXTERNAL + (SMTP->is_lmtp)? POST_LMTP: (CC->logged_in)? POST_LOGGED_IN: POST_EXTERNAL ); /* If there are modules that want to scan this message before final * submission (such as virus checkers or spam filters), call them now * and give them an opportunity to reject the message. */ - if (sSMTP->is_unfiltered) { + if (SMTP->is_unfiltered) { scan_errors = 0; } else { - scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN); + scan_errors = PerformMessageHooks(msg, valid, EVT_SMTPSCAN); } if (scan_errors > 0) { /* We don't want this message! */ - if (msg->cm_fields[eErrorMsg] == NULL) { - msg->cm_fields[eErrorMsg] = strdup("Message rejected by filter"); + if (CM_IsEmpty(msg, eErrorMsg)) { + CM_SetField(msg, eErrorMsg, HKEY("Message rejected by filter")); } - StrBufPrintf(sSMTP->OneRcpt, "550 %s\r\n", msg->cm_fields[eErrorMsg]); + StrBufPrintf(SMTP->OneRcpt, "550 %s\r\n", msg->cm_fields[eErrorMsg]); } else { /* Ok, we'll accept this message. */ - msgnum = CtdlSubmitMsg(msg, valid, "", 0); + msgnum = CtdlSubmitMsg(msg, valid, ""); if (msgnum > 0L) { - StrBufPrintf(sSMTP->OneRcpt, "250 Message accepted.\r\n"); + StrBufPrintf(SMTP->OneRcpt, "250 Message accepted.\r\n"); } else { - StrBufPrintf(sSMTP->OneRcpt, "550 Internal delivery error\r\n"); + StrBufPrintf(SMTP->OneRcpt, "550 Internal delivery error\r\n"); } } @@ -909,40 +895,39 @@ void smtp_data(long offset, long flags) * have different results, we can get away with just spitting out the * same message once for each recipient. */ - if (sSMTP->is_lmtp) { - for (i=0; inumber_of_recipients; ++i) { - cputbuf(sSMTP->OneRcpt); + if (SMTP->is_lmtp) { + for (i=0; inumber_of_recipients; ++i) { + cputbuf(SMTP->OneRcpt); } } else { - cputbuf(sSMTP->OneRcpt); + cputbuf(SMTP->OneRcpt); } /* Write something to the syslog(which may or may not be where the * rest of the Citadel logs are going; some sysadmins want LOG_MAIL). */ syslog((LOG_MAIL | LOG_INFO), - "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s", - msgnum, - ChrPtr(sSMTP->from), - sSMTP->number_of_recipients, - CCC->cs_host, - CCC->cs_addr, - ChrPtr(sSMTP->OneRcpt) + "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s", + msgnum, + ChrPtr(SMTP->from), + SMTP->number_of_recipients, + CC->cs_host, + CC->cs_addr, + ChrPtr(SMTP->OneRcpt) ); /* Clean up */ - CtdlFreeMessage(msg); + CM_Free(msg); free_recipients(valid); - smtp_data_clear(0, 0); /* clear out the buffers now */ + smtp_data_clear(); /* clear out the buffers now */ } /* - * implements the STARTTLS command + * Implements the STARTTLS command */ -void smtp_starttls(long offset, long flags) -{ +void smtp_starttls(void) { char ok_response[SIZ]; char nosup_response[SIZ]; char error_response[SIZ]; @@ -951,90 +936,132 @@ void smtp_starttls(long offset, long flags) sprintf(nosup_response, "554 TLS not supported here\r\n"); sprintf(error_response, "554 Internal error\r\n"); CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response); - smtp_rset(0, 0); + smtp_rset(0); +} + + +/* + * Implements the NOOP (NO OPeration) command + */ +void smtp_noop(void) { + cprintf("250 NOOP\r\n"); +} + + +/* + * Implements the QUIT command + */ +void smtp_quit(void) { + cprintf("221 Goodbye...\r\n"); + CC->kill_me = KILLME_CLIENT_LOGGED_OUT; } /* * Main command loop for SMTP server sessions. */ -void smtp_command_loop(void) -{ - struct CitContext *CCC = CC; - citsmtp *sSMTP = SMTP; - const char *pch, *pchs; - long i; - char CMD[MaxSMTPCmdLen + 1]; +void smtp_command_loop(void) { + static const ConstStr AuthPlainStr = {HKEY("AUTH PLAIN")}; - if (sSMTP == NULL) { - syslog(LOG_EMERG, "Session SMTP data is null. WTF? We will crash now.\n"); - return cit_panic_backtrace (0); + if (SMTP == NULL) { + syslog(LOG_ERR, "serv_smtp: Session SMTP data is null. WTF? We will crash now."); + abort(); } - time(&CCC->lastcmd); - if (CtdlClientGetLine(sSMTP->Cmd) < 1) { - syslog(LOG_CRIT, "SMTP: client disconnected: ending session.\n"); + time(&CC->lastcmd); + if (CtdlClientGetLine(SMTP->Cmd) < 1) { + syslog(LOG_INFO, "SMTP: client disconnected: ending session."); CC->kill_me = KILLME_CLIENT_DISCONNECTED; return; } - syslog(LOG_DEBUG, "SMTP server: %s\n", ChrPtr(sSMTP->Cmd)); - if (sSMTP->command_state == smtp_user) { - smtp_get_user(0); + if (SMTP->command_state == smtp_user) { + if (!strncmp(ChrPtr(SMTP->Cmd), AuthPlainStr.Key, AuthPlainStr.len)) { + smtp_try_plain(); + } + else { + smtp_get_user(5); + } + return; } - else if (sSMTP->command_state == smtp_password) { - smtp_get_pass(0, 0); + else if (SMTP->command_state == smtp_password) { + smtp_get_pass(); + return; } - else if (sSMTP->command_state == smtp_plain) { - smtp_try_plain(0, 0); + else if (SMTP->command_state == smtp_plain) { + smtp_try_plain(); + return; } - pchs = pch = ChrPtr(sSMTP->Cmd); - i = 0; - while ((*pch != '\0') && - (!isblank(*pch)) && - (pch - pchs <= MaxSMTPCmdLen)) - { - CMD[i] = toupper(*pch); - pch ++; - i++; + syslog(LOG_DEBUG, "serv_smtp: client sent command <%s>", ChrPtr(SMTP->Cmd)); + + if (!strncasecmp(ChrPtr(SMTP->Cmd), "NOOP", 4)) { + smtp_noop(); + return; } - CMD[i] = '\0'; - if ((*pch == '\0') || - (isblank(*pch))) - { - void *v; + if (!strncasecmp(ChrPtr(SMTP->Cmd), "QUIT", 4)) { + smtp_quit(); + return; + } - if (GetHash(SMTPCmds, CMD, i, &v) && - (v != NULL)) - { - smtp_handler_hook *h = (smtp_handler_hook*) v; + if (!strncasecmp(ChrPtr(SMTP->Cmd), "HELO", 4)) { + smtp_hello(HELO); + return; + } - if (isblank(pchs[i])) - i++; + if (!strncasecmp(ChrPtr(SMTP->Cmd), "EHLO", 4)) { + smtp_hello(EHLO); + return; + } - h->h(i, h->Flags); + if (!strncasecmp(ChrPtr(SMTP->Cmd), "LHLO", 4)) { + smtp_hello(LHLO); + return; + } - return; - } + if (!strncasecmp(ChrPtr(SMTP->Cmd), "RSET", 4)) { + smtp_rset(1); + return; } - cprintf("502 I'm afraid I can't do that.\r\n"); -} -void smtp_noop(long offest, long Flags) -{ - cprintf("250 NOOP\r\n"); -} + if (!strncasecmp(ChrPtr(SMTP->Cmd), "AUTH", 4)) { + smtp_auth(); + return; + } -void smtp_quit(long offest, long Flags) -{ - cprintf("221 Goodbye...\r\n"); - CC->kill_me = KILLME_CLIENT_LOGGED_OUT; + if (!strncasecmp(ChrPtr(SMTP->Cmd), "DATA", 4)) { + smtp_data(); + return; + } + + if (!strncasecmp(ChrPtr(SMTP->Cmd), "HELP", 4)) { + smtp_help(); + return; + } + + if (!strncasecmp(ChrPtr(SMTP->Cmd), "MAIL", 4)) { + smtp_mail(); + return; + } + + if (!strncasecmp(ChrPtr(SMTP->Cmd), "RCPT", 4)) { + smtp_rcpt(); + return; + } +#ifdef HAVE_OPENSSL + if (!strncasecmp(ChrPtr(SMTP->Cmd), "STARTTLS", 8)) { + smtp_starttls(); + return; + } +#endif + + cprintf("502 I'm afraid I can't do that.\r\n"); } + /*****************************************************************************/ /* MODULE INITIALIZATION STUFF */ /*****************************************************************************/ @@ -1044,20 +1071,20 @@ void smtp_quit(long offest, long Flags) */ void smtp_cleanup_function(void) { - citsmtp *sSMTP = SMTP; - /* Don't do this stuff if this is not an SMTP session! */ if (CC->h_command_function != smtp_command_loop) return; - syslog(LOG_DEBUG, "Performing SMTP cleanup hook\n"); + syslog(LOG_DEBUG, "Performing SMTP cleanup hook"); - FreeStrBuf(&sSMTP->Cmd); - FreeStrBuf(&sSMTP->helo_node); - FreeStrBuf(&sSMTP->from); - FreeStrBuf(&sSMTP->recipients); - FreeStrBuf(&sSMTP->OneRcpt); + FreeStrBuf(&SMTP->Cmd); + FreeStrBuf(&SMTP->helo_node); + FreeStrBuf(&SMTP->from); + FreeStrBuf(&SMTP->recipients); + FreeStrBuf(&SMTP->OneRcpt); + FreeStrBuf(&SMTP->preferred_sender_email); + FreeStrBuf(&SMTP->preferred_sender_name); - free(sSMTP); + free(SMTP); } const char *CitadelServiceSMTP_MTA="SMTP-MTA"; @@ -1066,29 +1093,11 @@ const char *CitadelServiceSMTP_MSA="SMTP-MSA"; const char *CitadelServiceSMTP_LMTP="LMTP"; const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF"; + CTDL_MODULE_INIT(smtp) { - if (!threading) - { - SMTPCmds = NewHash(1, NULL); - - RegisterSmtpCMD("AUTH", smtp_auth, 0); - RegisterSmtpCMD("DATA", smtp_data, 0); - RegisterSmtpCMD("HELO", smtp_hello, HELO); - RegisterSmtpCMD("EHLO", smtp_hello, EHLO); - RegisterSmtpCMD("LHLO", smtp_hello, LHLO); - RegisterSmtpCMD("HELP", smtp_help, 0); - RegisterSmtpCMD("MAIL", smtp_mail, 0); - RegisterSmtpCMD("NOOP", smtp_noop, 0); - RegisterSmtpCMD("QUIT", smtp_quit, 0); - RegisterSmtpCMD("RCPT", smtp_rcpt, 0); - RegisterSmtpCMD("RSET", smtp_rset, 1); -#ifdef HAVE_OPENSSL - RegisterSmtpCMD("STARTTLS", smtp_starttls, 0); -#endif - - - CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */ + if (!threading) { + CtdlRegisterServiceHook(CtdlGetConfigInt("c_smtp_port"), /* SMTP MTA */ NULL, smtp_mta_greeting, smtp_command_loop, @@ -1096,7 +1105,7 @@ CTDL_MODULE_INIT(smtp) CitadelServiceSMTP_MTA); #ifdef HAVE_OPENSSL - CtdlRegisterServiceHook(config.c_smtps_port, + CtdlRegisterServiceHook(CtdlGetConfigInt("c_smtps_port"), /* SMTPS MTA */ NULL, smtps_greeting, smtp_command_loop, @@ -1104,7 +1113,7 @@ CTDL_MODULE_INIT(smtp) CitadelServiceSMTPS_MTA); #endif - CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */ + CtdlRegisterServiceHook(CtdlGetConfigInt("c_msa_port"), /* SMTP MSA */ NULL, smtp_msa_greeting, smtp_command_loop, @@ -1125,7 +1134,6 @@ CTDL_MODULE_INIT(smtp) NULL, CitadelServiceSMTP_LMTP_UNF); - CtdlRegisterCleanupHook(smtp_cleanup); CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP, PRIO_STOP + 250); }