Back to index

citadel  8.12
serv_smtp.c
Go to the documentation of this file.
00001 /*
00002  * This module is an SMTP and ESMTP server for the Citadel system.
00003  * It is compliant with all of the following:
00004  *
00005  * RFC  821 - Simple Mail Transfer Protocol
00006  * RFC  876 - Survey of SMTP Implementations
00007  * RFC 1047 - Duplicate messages and SMTP
00008  * RFC 1652 - 8 bit MIME
00009  * RFC 1869 - Extended Simple Mail Transfer Protocol
00010  * RFC 1870 - SMTP Service Extension for Message Size Declaration
00011  * RFC 2033 - Local Mail Transfer Protocol
00012  * RFC 2197 - SMTP Service Extension for Command Pipelining
00013  * RFC 2476 - Message Submission
00014  * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
00015  * RFC 2554 - SMTP Service Extension for Authentication
00016  * RFC 2821 - Simple Mail Transfer Protocol
00017  * RFC 2822 - Internet Message Format
00018  * RFC 2920 - SMTP Service Extension for Command Pipelining
00019  *  
00020  * The VRFY and EXPN commands have been removed from this implementation
00021  * because nobody uses these commands anymore, except for spammers.
00022  *
00023  * Copyright (c) 1998-2012 by the citadel.org team
00024  *
00025  *  This program is open source software; you can redistribute it and/or modify
00026  *  it under the terms of the GNU General Public License version 3.
00027  *  
00028  *  This program is distributed in the hope that it will be useful,
00029  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00030  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00031  *  GNU General Public License for more details.
00032  */
00033 
00034 #include "sysdep.h"
00035 #include <stdlib.h>
00036 #include <unistd.h>
00037 #include <stdio.h>
00038 #include <termios.h>
00039 #include <fcntl.h>
00040 #include <signal.h>
00041 #include <pwd.h>
00042 #include <errno.h>
00043 #include <sys/types.h>
00044 #include <syslog.h>
00045 
00046 #if TIME_WITH_SYS_TIME
00047 # include <sys/time.h>
00048 # include <time.h>
00049 #else
00050 # if HAVE_SYS_TIME_H
00051 #  include <sys/time.h>
00052 # else
00053 #  include <time.h>
00054 # endif
00055 #endif
00056 
00057 #include <sys/wait.h>
00058 #include <ctype.h>
00059 #include <string.h>
00060 #include <limits.h>
00061 #include <sys/socket.h>
00062 #include <netinet/in.h>
00063 #include <arpa/inet.h>
00064 #include <libcitadel.h>
00065 #include "citadel.h"
00066 #include "server.h"
00067 #include "citserver.h"
00068 #include "support.h"
00069 #include "config.h"
00070 #include "control.h"
00071 #include "user_ops.h"
00072 #include "database.h"
00073 #include "msgbase.h"
00074 #include "internet_addressing.h"
00075 #include "genstamp.h"
00076 #include "domain.h"
00077 #include "clientsocket.h"
00078 #include "locate_host.h"
00079 #include "citadel_dirs.h"
00080 
00081 
00082 
00083 #ifndef HAVE_SNPRINTF
00084 #include "snprintf.h"
00085 #endif
00086 
00087 
00088 #include "ctdl_module.h"
00089 
00090 #include "smtp_util.h"
00091 enum {                      /* Command states for login authentication */
00092        smtp_command,
00093        smtp_user,
00094        smtp_password,
00095        smtp_plain
00096 };
00097 
00098 
00099 /*
00100  * Here's where our SMTP session begins its happy day.
00101  */
00102 void smtp_greeting(int is_msa)
00103 {
00104        citsmtp *sSMTP;
00105        char message_to_spammer[1024];
00106 
00107        strcpy(CC->cs_clientname, "SMTP session");
00108        CC->internal_pgm = 1;
00109        CC->cs_flags |= CS_STEALTH;
00110        CC->session_specific_data = malloc(sizeof(citsmtp));
00111        memset(SMTP, 0, sizeof(citsmtp));
00112        sSMTP = SMTP;
00113        sSMTP->is_msa = is_msa;
00114 
00115        /* If this config option is set, reject connections from problem
00116         * addresses immediately instead of after they execute a RCPT
00117         */
00118        if ( (config.c_rbl_at_greeting) && (sSMTP->is_msa == 0) ) {
00119               if (rbl_check(message_to_spammer)) {
00120                      if (server_shutting_down)
00121                             cprintf("421 %s\r\n", message_to_spammer);
00122                      else
00123                             cprintf("550 %s\r\n", message_to_spammer);
00124                      CC->kill_me = KILLME_SPAMMER;
00125                      /* no need to free_recipients(valid), it's not allocated yet */
00126                      return;
00127               }
00128        }
00129 
00130        /* Otherwise we're either clean or we check later. */
00131 
00132        if (CC->nologin==1) {
00133               cprintf("451 Too many connections are already open; please try again later.\r\n");
00134               CC->kill_me = KILLME_MAX_SESSIONS_EXCEEDED;
00135               /* no need to free_recipients(valid), it's not allocated yet */
00136               return;
00137        }
00138 
00139        /* Note: the FQDN *must* appear as the first thing after the 220 code.
00140         * Some clients (including citmail.c) depend on it being there.
00141         */
00142        cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
00143 }
00144 
00145 
00146 /*
00147  * SMTPS is just like SMTP, except it goes crypto right away.
00148  */
00149 void smtps_greeting(void) {
00150        CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
00151 #ifdef HAVE_OPENSSL
00152        if (!CC->redirect_ssl) CC->kill_me = KILLME_NO_CRYPTO;         /* kill session if no crypto */
00153 #endif
00154        smtp_greeting(0);
00155 }
00156 
00157 
00158 /*
00159  * SMTP MSA port requires authentication.
00160  */
00161 void smtp_msa_greeting(void) {
00162        smtp_greeting(1);
00163 }
00164 
00165 
00166 /*
00167  * LMTP is like SMTP but with some extra bonus footage added.
00168  */
00169 void lmtp_greeting(void) {
00170 
00171        smtp_greeting(0);
00172        SMTP->is_lmtp = 1;
00173 }
00174 
00175 
00176 /* 
00177  * Generic SMTP MTA greeting
00178  */
00179 void smtp_mta_greeting(void) {
00180        smtp_greeting(0);
00181 }
00182 
00183 
00184 /*
00185  * We also have an unfiltered LMTP socket that bypasses spam filters.
00186  */
00187 void lmtp_unfiltered_greeting(void) {
00188        citsmtp *sSMTP;
00189 
00190        smtp_greeting(0);
00191        sSMTP = SMTP;
00192        sSMTP->is_lmtp = 1;
00193        sSMTP->is_unfiltered = 1;
00194 }
00195 
00196 
00197 /*
00198  * Login greeting common to all auth methods
00199  */
00200 void smtp_auth_greeting(void) {
00201               cprintf("235 Hello, %s\r\n", CC->user.fullname);
00202               syslog(LOG_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
00203               CC->internal_pgm = 0;
00204               CC->cs_flags &= ~CS_STEALTH;
00205 }
00206 
00207 
00208 /*
00209  * Implement HELO and EHLO commands.
00210  *
00211  * which_command:  0=HELO, 1=EHLO, 2=LHLO
00212  */
00213 void smtp_hello(char *argbuf, int which_command) {
00214        citsmtp *sSMTP = SMTP;
00215 
00216        safestrncpy(sSMTP->helo_node, argbuf, sizeof sSMTP->helo_node);
00217 
00218        if ( (which_command != 2) && (sSMTP->is_lmtp) ) {
00219               cprintf("500 Only LHLO is allowed when running LMTP\r\n");
00220               return;
00221        }
00222 
00223        if ( (which_command == 2) && (sSMTP->is_lmtp == 0) ) {
00224               cprintf("500 LHLO is only allowed when running LMTP\r\n");
00225               return;
00226        }
00227 
00228        if (which_command == 0) {
00229               cprintf("250 Hello %s (%s [%s])\r\n",
00230                      sSMTP->helo_node,
00231                      CC->cs_host,
00232                      CC->cs_addr
00233               );
00234        }
00235        else {
00236               if (which_command == 1) {
00237                      cprintf("250-Hello %s (%s [%s])\r\n",
00238                             sSMTP->helo_node,
00239                             CC->cs_host,
00240                             CC->cs_addr
00241                      );
00242               }
00243               else {
00244                      cprintf("250-Greetings and joyous salutations.\r\n");
00245               }
00246               cprintf("250-HELP\r\n");
00247               cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
00248 
00249 #ifdef HAVE_OPENSSL
00250               /*
00251                * Offer TLS, but only if TLS is not already active.
00252                * Furthermore, only offer TLS when running on
00253                * the SMTP-MSA port, not on the SMTP-MTA port, due to
00254                * questionable reliability of TLS in certain sending MTA's.
00255                */
00256               if ( (!CC->redirect_ssl) && (sSMTP->is_msa) ) {
00257                      cprintf("250-STARTTLS\r\n");
00258               }
00259 #endif /* HAVE_OPENSSL */
00260 
00261               cprintf("250-AUTH LOGIN PLAIN\r\n"
00262                      "250-AUTH=LOGIN PLAIN\r\n"
00263                      "250 8BITMIME\r\n"
00264               );
00265        }
00266 }
00267 
00268 
00269 
00270 /*
00271  * Implement HELP command.
00272  */
00273 void smtp_help(void) {
00274        cprintf("214 RTFM http://www.ietf.org/rfc/rfc2821.txt\r\n");
00275 }
00276 
00277 
00278 /*
00279  *
00280  */
00281 void smtp_get_user(char *argbuf) {
00282        char buf[SIZ];
00283        char username[SIZ];
00284        citsmtp *sSMTP = SMTP;
00285 
00286        CtdlDecodeBase64(username, argbuf, SIZ);
00287        /* syslog(LOG_DEBUG, "Trying <%s>\n", username); */
00288        if (CtdlLoginExistingUser(NULL, username) == login_ok) {
00289               CtdlEncodeBase64(buf, "Password:", 9, 0);
00290               cprintf("334 %s\r\n", buf);
00291               sSMTP->command_state = smtp_password;
00292        }
00293        else {
00294               cprintf("500 No such user.\r\n");
00295               sSMTP->command_state = smtp_command;
00296        }
00297 }
00298 
00299 
00300 /*
00301  *
00302  */
00303 void smtp_get_pass(char *argbuf) {
00304        char password[SIZ];
00305        long len;
00306 
00307        memset(password, 0, sizeof(password));    
00308        len = CtdlDecodeBase64(password, argbuf, SIZ);
00309        /* syslog(LOG_DEBUG, "Trying <%s>\n", password); */
00310        if (CtdlTryPassword(password, len) == pass_ok) {
00311               smtp_auth_greeting();
00312        }
00313        else {
00314               cprintf("535 Authentication failed.\r\n");
00315        }
00316        SMTP->command_state = smtp_command;
00317 }
00318 
00319 
00320 /*
00321  * Back end for PLAIN auth method (either inline or multistate)
00322  */
00323 void smtp_try_plain(char *encoded_authstring) {
00324        char decoded_authstring[1024];
00325        char ident[256];
00326        char user[256];
00327        char pass[256];
00328        int result;
00329        long len;
00330 
00331        CtdlDecodeBase64(decoded_authstring, encoded_authstring, strlen(encoded_authstring) );
00332        safestrncpy(ident, decoded_authstring, sizeof ident);
00333        safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
00334        len = safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
00335        if (len == -1)
00336               len = sizeof(pass) - 1;
00337 
00338        SMTP->command_state = smtp_command;
00339 
00340        if (!IsEmptyStr(ident)) {
00341               result = CtdlLoginExistingUser(user, ident);
00342        }
00343        else {
00344               result = CtdlLoginExistingUser(NULL, user);
00345        }
00346 
00347        if (result == login_ok) {
00348               if (CtdlTryPassword(pass, len) == pass_ok) {
00349                      smtp_auth_greeting();
00350                      return;
00351               }
00352        }
00353        cprintf("504 Authentication failed.\r\n");
00354 }
00355 
00356 
00357 /*
00358  * Attempt to perform authenticated SMTP
00359  */
00360 void smtp_auth(char *argbuf) {
00361        char username_prompt[64];
00362        char method[64];
00363        char encoded_authstring[1024];
00364 
00365        if (CC->logged_in) {
00366               cprintf("504 Already logged in.\r\n");
00367               return;
00368        }
00369 
00370        extract_token(method, argbuf, 0, ' ', sizeof method);
00371 
00372        if (!strncasecmp(method, "login", 5) ) {
00373               if (strlen(argbuf) >= 7) {
00374                      smtp_get_user(&argbuf[6]);
00375               }
00376               else {
00377                      CtdlEncodeBase64(username_prompt, "Username:", 9, 0);
00378                      cprintf("334 %s\r\n", username_prompt);
00379                      SMTP->command_state = smtp_user;
00380               }
00381               return;
00382        }
00383 
00384        if (!strncasecmp(method, "plain", 5) ) {
00385               if (num_tokens(argbuf, ' ') < 2) {
00386                      cprintf("334 \r\n");
00387                      SMTP->command_state = smtp_plain;
00388                      return;
00389               }
00390 
00391               extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
00392 
00393               smtp_try_plain(encoded_authstring);
00394               return;
00395        }
00396 
00397        if (strncasecmp(method, "login", 5) ) {
00398               cprintf("504 Unknown authentication method.\r\n");
00399               return;
00400        }
00401 
00402 }
00403 
00404 
00405 /*
00406  * Implements the RSET (reset state) command.
00407  * Currently this just zeroes out the state buffer.  If pointers to data
00408  * allocated with malloc() are ever placed in the state buffer, we have to
00409  * be sure to free() them first!
00410  *
00411  * Set do_response to nonzero to output the SMTP RSET response code.
00412  */
00413 void smtp_rset(int do_response) {
00414        int is_lmtp;
00415        int is_unfiltered;
00416        citsmtp *sSMTP = SMTP;
00417 
00418        /*
00419         * Our entire SMTP state is discarded when a RSET command is issued,
00420         * but we need to preserve this one little piece of information, so
00421         * we save it for later.
00422         */
00423        is_lmtp = sSMTP->is_lmtp;
00424        is_unfiltered = sSMTP->is_unfiltered;
00425 
00426        memset(sSMTP, 0, sizeof(citsmtp));
00427 
00428        /*
00429         * It is somewhat ambiguous whether we want to log out when a RSET
00430         * command is issued.  Here's the code to do it.  It is commented out
00431         * because some clients (such as Pine) issue RSET commands before
00432         * each message, but still expect to be logged in.
00433         *
00434         * if (CC->logged_in) {
00435         *     logout(CC);
00436         * }
00437         */
00438 
00439        /*
00440         * Reinstate this little piece of information we saved (see above).
00441         */
00442        sSMTP->is_lmtp = is_lmtp;
00443        sSMTP->is_unfiltered = is_unfiltered;
00444 
00445        if (do_response) {
00446               cprintf("250 Zap!\r\n");
00447        }
00448 }
00449 
00450 /*
00451  * Clear out the portions of the state buffer that need to be cleared out
00452  * after the DATA command finishes.
00453  */
00454 void smtp_data_clear(void) {
00455        citsmtp *sSMTP = SMTP;
00456 
00457        strcpy(sSMTP->from, "");
00458        strcpy(sSMTP->recipients, "");
00459        sSMTP->number_of_recipients = 0;
00460        sSMTP->delivery_mode = 0;
00461        sSMTP->message_originated_locally = 0;
00462 }
00463 
00464 /*
00465  * Implements the "MAIL FROM:" command
00466  */
00467 void smtp_mail(char *argbuf) {
00468        char user[SIZ];
00469        char node[SIZ];
00470        char name[SIZ];
00471        citsmtp *sSMTP = SMTP;
00472 
00473        if (!IsEmptyStr(sSMTP->from)) {
00474               cprintf("503 Only one sender permitted\r\n");
00475               return;
00476        }
00477 
00478        if (strncasecmp(argbuf, "From:", 5)) {
00479               cprintf("501 Syntax error\r\n");
00480               return;
00481        }
00482 
00483        strcpy(sSMTP->from, &argbuf[5]);
00484        striplt(sSMTP->from);
00485        if (haschar(sSMTP->from, '<') > 0) {
00486               stripallbut(sSMTP->from, '<', '>');
00487        }
00488 
00489        /* We used to reject empty sender names, until it was brought to our
00490         * attention that RFC1123 5.2.9 requires that this be allowed.  So now
00491         * we allow it, but replace the empty string with a fake
00492         * address so we don't have to contend with the empty string causing
00493         * other code to fail when it's expecting something there.
00494         */
00495        if (IsEmptyStr(sSMTP->from)) {
00496               strcpy(sSMTP->from, "someone@example.com");
00497        }
00498 
00499        /* If this SMTP connection is from a logged-in user, force the 'from'
00500         * to be the user's Internet e-mail address as Citadel knows it.
00501         */
00502        if (CC->logged_in) {
00503               safestrncpy(sSMTP->from, CC->cs_inet_email, sizeof sSMTP->from);
00504               cprintf("250 Sender ok <%s>\r\n", sSMTP->from);
00505               sSMTP->message_originated_locally = 1;
00506               return;
00507        }
00508 
00509        else if (sSMTP->is_lmtp) {
00510               /* Bypass forgery checking for LMTP */
00511        }
00512 
00513        /* Otherwise, make sure outsiders aren't trying to forge mail from
00514         * this system (unless, of course, c_allow_spoofing is enabled)
00515         */
00516        else if (config.c_allow_spoofing == 0) {
00517               process_rfc822_addr(sSMTP->from, user, node, name);
00518               if (CtdlHostAlias(node) != hostalias_nomatch) {
00519                      cprintf("550 You must log in to send mail from %s\r\n", node);
00520                      strcpy(sSMTP->from, "");
00521                      return;
00522               }
00523        }
00524 
00525        cprintf("250 Sender ok\r\n");
00526 }
00527 
00528 
00529 
00530 /*
00531  * Implements the "RCPT To:" command
00532  */
00533 void smtp_rcpt(char *argbuf) {
00534        char recp[1024];
00535        char message_to_spammer[SIZ];
00536        struct recptypes *valid = NULL;
00537        citsmtp *sSMTP = SMTP;
00538 
00539        if (IsEmptyStr(sSMTP->from)) {
00540               cprintf("503 Need MAIL before RCPT\r\n");
00541               return;
00542        }
00543 
00544        if (strncasecmp(argbuf, "To:", 3)) {
00545               cprintf("501 Syntax error\r\n");
00546               return;
00547        }
00548 
00549        if ( (sSMTP->is_msa) && (!CC->logged_in) ) {
00550               cprintf("550 You must log in to send mail on this port.\r\n");
00551               strcpy(sSMTP->from, "");
00552               return;
00553        }
00554 
00555        safestrncpy(recp, &argbuf[3], sizeof recp);
00556        striplt(recp);
00557        stripallbut(recp, '<', '>');
00558 
00559        if ( (strlen(recp) + strlen(sSMTP->recipients) + 1 ) >= SIZ) {
00560               cprintf("452 Too many recipients\r\n");
00561               return;
00562        }
00563 
00564        /* RBL check */
00565        if ( (!CC->logged_in)       /* Don't RBL authenticated users */
00566           && (!sSMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
00567               if (config.c_rbl_at_greeting == 0) {      /* Don't RBL again if we already did it */
00568                      if (rbl_check(message_to_spammer)) {
00569                             if (server_shutting_down)
00570                                    cprintf("421 %s\r\n", message_to_spammer);
00571                             else
00572                                    cprintf("550 %s\r\n", message_to_spammer);
00573                             /* no need to free_recipients(valid), it's not allocated yet */
00574                             return;
00575                      }
00576               }
00577        }
00578 
00579        valid = validate_recipients(
00580               recp, 
00581               smtp_get_Recipients(),
00582               (sSMTP->is_lmtp)? POST_LMTP: (CC->logged_in)? POST_LOGGED_IN: POST_EXTERNAL
00583        );
00584        if (valid->num_error != 0) {
00585               cprintf("550 %s\r\n", valid->errormsg);
00586               free_recipients(valid);
00587               return;
00588        }
00589 
00590        if (valid->num_internet > 0) {
00591               if (CC->logged_in) {
00592                         if (CtdlCheckInternetMailPermission(&CC->user)==0) {
00593                             cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", recp);
00594                                 free_recipients(valid);
00595                                 return;
00596                         }
00597                 }
00598        }
00599 
00600        if (valid->num_internet > 0) {
00601               if ( (sSMTP->message_originated_locally == 0)
00602                  && (sSMTP->is_lmtp == 0) ) {
00603                      cprintf("551 <%s> - relaying denied\r\n", recp);
00604                      free_recipients(valid);
00605                      return;
00606               }
00607        }
00608 
00609        cprintf("250 RCPT ok <%s>\r\n", recp);
00610        if (!IsEmptyStr(sSMTP->recipients)) {
00611               strcat(sSMTP->recipients, ",");
00612        }
00613        strcat(sSMTP->recipients, recp);
00614        sSMTP->number_of_recipients += 1;
00615        if (valid != NULL)  {
00616               free_recipients(valid);
00617        }
00618 }
00619 
00620 
00621 
00622 
00623 /*
00624  * Implements the DATA command
00625  */
00626 void smtp_data(void) {
00627        StrBuf *body;
00628        char *defbody; //TODO: remove me
00629        struct CtdlMessage *msg = NULL;
00630        long msgnum = (-1L);
00631        char nowstamp[SIZ];
00632        struct recptypes *valid;
00633        int scan_errors;
00634        int i;
00635        char result[SIZ];
00636        citsmtp *sSMTP = SMTP;
00637 
00638        if (IsEmptyStr(sSMTP->from)) {
00639               cprintf("503 Need MAIL command first.\r\n");
00640               return;
00641        }
00642 
00643        if (sSMTP->number_of_recipients < 1) {
00644               cprintf("503 Need RCPT command first.\r\n");
00645               return;
00646        }
00647 
00648        cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
00649        
00650        datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
00651        defbody = malloc(4096);
00652 
00653        if (defbody != NULL) {
00654               if (sSMTP->is_lmtp && (CC->cs_UDSclientUID != -1)) {
00655                      snprintf(defbody, 4096,
00656                             "Received: from %s (Citadel from userid %ld)\n"
00657                             "      by %s; %s\n",
00658                             sSMTP->helo_node,
00659                             (long int) CC->cs_UDSclientUID,
00660                             config.c_fqdn,
00661                             nowstamp);
00662               }
00663               else {
00664                      snprintf(defbody, 4096,
00665                              "Received: from %s (%s [%s])\n"
00666                              "     by %s; %s\n",
00667                              sSMTP->helo_node,
00668                              CC->cs_host,
00669                              CC->cs_addr,
00670                              config.c_fqdn,
00671                              nowstamp);
00672               }
00673        }
00674        body = CtdlReadMessageBodyBuf(HKEY("."), config.c_maxmsglen, defbody, 1, NULL);
00675        if (body == NULL) {
00676               cprintf("550 Unable to save message: internal error.\r\n");
00677               return;
00678        }
00679 
00680        syslog(LOG_DEBUG, "Converting message...\n");
00681        msg = convert_internet_message_buf(&body);
00682 
00683        /* If the user is locally authenticated, FORCE the From: header to
00684         * show up as the real sender.  Yes, this violates the RFC standard,
00685         * but IT MAKES SENSE.  If you prefer strict RFC adherence over
00686         * common sense, you can disable this in the configuration.
00687         *
00688         * We also set the "message room name" ('O' field) to MAILROOM
00689         * (which is Mail> on most systems) to prevent it from getting set
00690         * to something ugly like "0000058008.Sent Items>" when the message
00691         * is read with a Citadel client.
00692         */
00693        if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
00694               if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
00695               if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
00696               if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
00697               if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
00698               if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
00699               msg->cm_fields['A'] = strdup(CC->user.fullname);
00700               msg->cm_fields['N'] = strdup(config.c_nodename);
00701               msg->cm_fields['H'] = strdup(config.c_humannode);
00702               msg->cm_fields['F'] = strdup(CC->cs_inet_email);
00703               msg->cm_fields['O'] = strdup(MAILROOM);
00704        }
00705 
00706        /* Set the "envelope from" address */
00707        if (msg->cm_fields['P'] != NULL) {
00708               free(msg->cm_fields['P']);
00709        }
00710        msg->cm_fields['P'] = strdup(sSMTP->from);
00711 
00712        /* Set the "envelope to" address */
00713        if (msg->cm_fields['V'] != NULL) {
00714               free(msg->cm_fields['V']);
00715        }
00716        msg->cm_fields['V'] = strdup(sSMTP->recipients);
00717 
00718        /* Submit the message into the Citadel system. */
00719        valid = validate_recipients(
00720               sSMTP->recipients,
00721               smtp_get_Recipients(),
00722               (sSMTP->is_lmtp)? POST_LMTP: (CC->logged_in)? POST_LOGGED_IN: POST_EXTERNAL
00723        );
00724 
00725        /* If there are modules that want to scan this message before final
00726         * submission (such as virus checkers or spam filters), call them now
00727         * and give them an opportunity to reject the message.
00728         */
00729        if (sSMTP->is_unfiltered) {
00730               scan_errors = 0;
00731        }
00732        else {
00733               scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
00734        }
00735 
00736        if (scan_errors > 0) {      /* We don't want this message! */
00737 
00738               if (msg->cm_fields['0'] == NULL) {
00739                      msg->cm_fields['0'] = strdup("Message rejected by filter");
00740               }
00741 
00742               sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
00743        }
00744        
00745        else {               /* Ok, we'll accept this message. */
00746               msgnum = CtdlSubmitMsg(msg, valid, "", 0);
00747               if (msgnum > 0L) {
00748                      sprintf(result, "250 Message accepted.\r\n");
00749               }
00750               else {
00751                      sprintf(result, "550 Internal delivery error\r\n");
00752               }
00753        }
00754 
00755        /* For SMTP and ESMTP, just print the result message.  For LMTP, we
00756         * have to print one result message for each recipient.  Since there
00757         * is nothing in Citadel which would cause different recipients to
00758         * have different results, we can get away with just spitting out the
00759         * same message once for each recipient.
00760         */
00761        if (sSMTP->is_lmtp) {
00762               for (i=0; i<sSMTP->number_of_recipients; ++i) {
00763                      cprintf("%s", result);
00764               }
00765        }
00766        else {
00767               cprintf("%s", result);
00768        }
00769 
00770        /* Write something to the syslog(which may or may not be where the
00771         * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
00772         */
00773        syslog((LOG_MAIL | LOG_INFO),
00774               "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
00775               msgnum,
00776               sSMTP->from,
00777               sSMTP->number_of_recipients,
00778               CC->cs_host,
00779               CC->cs_addr,
00780               result
00781        );
00782 
00783        /* Clean up */
00784        CtdlFreeMessage(msg);
00785        free_recipients(valid);
00786        smtp_data_clear();   /* clear out the buffers now */
00787 }
00788 
00789 
00790 /*
00791  * implements the STARTTLS command
00792  */
00793 void smtp_starttls(void)
00794 {
00795        char ok_response[SIZ];
00796        char nosup_response[SIZ];
00797        char error_response[SIZ];
00798 
00799        sprintf(ok_response, "220 Begin TLS negotiation now\r\n");
00800        sprintf(nosup_response, "554 TLS not supported here\r\n");
00801        sprintf(error_response, "554 Internal error\r\n");
00802        CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
00803        smtp_rset(0);
00804 }
00805 
00806 
00807 /* 
00808  * Main command loop for SMTP server sessions.
00809  */
00810 void smtp_command_loop(void) {
00811        char cmdbuf[SIZ];
00812        citsmtp *sSMTP = SMTP;
00813 
00814        if (sSMTP == NULL) {
00815               syslog(LOG_EMERG, "Session SMTP data is null.  WTF?  We will crash now.\n");
00816               return cit_panic_backtrace (0);
00817        }
00818 
00819        time(&CC->lastcmd);
00820        memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
00821        if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
00822               syslog(LOG_CRIT, "SMTP: client disconnected: ending session.\n");
00823               CC->kill_me = KILLME_CLIENT_DISCONNECTED;
00824               return;
00825        }
00826        syslog(LOG_INFO, "SMTP server: %s\n", cmdbuf);
00827        while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
00828 
00829        if (sSMTP->command_state == smtp_user) {
00830               smtp_get_user(cmdbuf);
00831        }
00832 
00833        else if (sSMTP->command_state == smtp_password) {
00834               smtp_get_pass(cmdbuf);
00835        }
00836 
00837        else if (sSMTP->command_state == smtp_plain) {
00838               smtp_try_plain(cmdbuf);
00839        }
00840 
00841        else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
00842               smtp_auth(&cmdbuf[5]);
00843        }
00844 
00845        else if (!strncasecmp(cmdbuf, "DATA", 4)) {
00846               smtp_data();
00847        }
00848 
00849        else if (!strncasecmp(cmdbuf, "HELO", 4)) {
00850               smtp_hello(&cmdbuf[5], 0);
00851        }
00852 
00853        else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
00854               smtp_hello(&cmdbuf[5], 1);
00855        }
00856 
00857        else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
00858               smtp_hello(&cmdbuf[5], 2);
00859        }
00860 
00861        else if (!strncasecmp(cmdbuf, "HELP", 4)) {
00862               smtp_help();
00863        }
00864 
00865        else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
00866               smtp_mail(&cmdbuf[5]);
00867        }
00868 
00869        else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
00870               cprintf("250 NOOP\r\n");
00871        }
00872 
00873        else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
00874               cprintf("221 Goodbye...\r\n");
00875               CC->kill_me = KILLME_CLIENT_LOGGED_OUT;
00876               return;
00877        }
00878 
00879        else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
00880               smtp_rcpt(&cmdbuf[5]);
00881        }
00882 
00883        else if (!strncasecmp(cmdbuf, "RSET", 4)) {
00884               smtp_rset(1);
00885        }
00886 #ifdef HAVE_OPENSSL
00887        else if (!strcasecmp(cmdbuf, "STARTTLS")) {
00888               smtp_starttls();
00889        }
00890 #endif
00891        else {
00892               cprintf("502 I'm afraid I can't do that.\r\n");
00893        }
00894 
00895 
00896 }
00897 
00898 
00899 /*****************************************************************************/
00900 /*                      MODULE INITIALIZATION STUFF                          */
00901 /*****************************************************************************/
00902 /*
00903  * This cleanup function blows away the temporary memory used by
00904  * the SMTP server.
00905  */
00906 void smtp_cleanup_function(void) {
00907 
00908        /* Don't do this stuff if this is not an SMTP session! */
00909        if (CC->h_command_function != smtp_command_loop) return;
00910 
00911        syslog(LOG_DEBUG, "Performing SMTP cleanup hook\n");
00912        free(SMTP);
00913 }
00914 
00915 
00916 
00917 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
00918 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
00919 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
00920 const char *CitadelServiceSMTP_LMTP="LMTP";
00921 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
00922 
00923 CTDL_MODULE_INIT(smtp)
00924 {
00925        if (!threading)
00926        {
00927               CtdlRegisterServiceHook(config.c_smtp_port,      /* SMTP MTA */
00928                                    NULL,
00929                                    smtp_mta_greeting,
00930                                    smtp_command_loop,
00931                                    NULL, 
00932                                    CitadelServiceSMTP_MTA);
00933 
00934 #ifdef HAVE_OPENSSL
00935               CtdlRegisterServiceHook(config.c_smtps_port,
00936                                    NULL,
00937                                    smtps_greeting,
00938                                    smtp_command_loop,
00939                                    NULL,
00940                                    CitadelServiceSMTPS_MTA);
00941 #endif
00942 
00943               CtdlRegisterServiceHook(config.c_msa_port,       /* SMTP MSA */
00944                                    NULL,
00945                                    smtp_msa_greeting,
00946                                    smtp_command_loop,
00947                                    NULL,
00948                                    CitadelServiceSMTP_MSA);
00949 
00950               CtdlRegisterServiceHook(0,                /* local LMTP */
00951                                    file_lmtp_socket,
00952                                    lmtp_greeting,
00953                                    smtp_command_loop,
00954                                    NULL,
00955                                    CitadelServiceSMTP_LMTP);
00956 
00957               CtdlRegisterServiceHook(0,                /* local LMTP */
00958                                    file_lmtp_unfiltered_socket,
00959                                    lmtp_unfiltered_greeting,
00960                                    smtp_command_loop,
00961                                    NULL,
00962                                    CitadelServiceSMTP_LMTP_UNF);
00963 
00964               CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP, PRIO_STOP + 250);
00965        }
00966        
00967        /* return our module name for the log */
00968        return "smtp";
00969 }