00001 //--------------------------------------------------------------------------
00002 // Copyright (C) 2015-2017 Cisco and/or its affiliates. All rights reserved.
00003 //
00004 // This program is free software; you can redistribute it and/or modify it
00005 // under the terms of the GNU General Public License Version 2 as published
00006 // by the Free Software Foundation. You may not use, modify or distribute
00007 // this program under any other version of the GNU General Public License.
00008 //
00009 // This program is distributed in the hope that it will be useful, but
00010 // WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00012 // General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License along
00015 // with this program; if not, write to the Free Software Foundation, Inc.,
00016 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017 //--------------------------------------------------------------------------
00018
00019 #ifdef HAVE_CONFIG_H
00020 #include "config.h"
00021 #endif
00022
00023 #include "smtp.h"
00024
00025 #include "detection/detection_engine.h"
00026 #include "detection/detection_util.h"
00027 #include "log/messages.h"
00028 #include "log/unified2.h"
00029 #include "main/snort_debug.h"
00030 #include "profiler/profiler.h"
00031 #include "protocols/packet.h"
00032 #include "protocols/ssl.h"
00033 #include "stream/stream.h"
00034 #include "utils/safec.h"
00035 #include "utils/util.h"
00036 #include "utils/util_cstring.h"
00037
00038 #include "main/snort_debug.h"
00039
00040 #include "smtp_module.h"
00041 #include "smtp_normalize.h"
00042 #include "smtp_paf.h"
00043 #include "smtp_util.h"
00044 #include "smtp_xlink2state.h"
00045
00046 THREAD_LOCAL ProfileStats smtpPerfStats;
00047 THREAD_LOCAL SmtpStats smtpstats;
00048 THREAD_LOCAL bool smtp_normalizing;
00049
00050 /* Globals ****************************************************************/
00051
00052 const SMTPToken smtp_known_cmds[] =
00053 {
00054 { "ATRN", 4, CMD_ATRN, SMTP_CMD_TYPE_NORMAL },
00055 { "AUTH", 4, CMD_AUTH, SMTP_CMD_TYPE_AUTH },
00056 { "BDAT", 4, CMD_BDAT, SMTP_CMD_TYPE_BDATA },
00057 { "DATA", 4, CMD_DATA, SMTP_CMD_TYPE_DATA },
00058 { "DEBUG", 5, CMD_DEBUG, SMTP_CMD_TYPE_NORMAL },
00059 { "EHLO", 4, CMD_EHLO, SMTP_CMD_TYPE_NORMAL },
00060 { "EMAL", 4, CMD_EMAL, SMTP_CMD_TYPE_NORMAL },
00061 { "ESAM", 4, CMD_ESAM, SMTP_CMD_TYPE_NORMAL },
00062 { "ESND", 4, CMD_ESND, SMTP_CMD_TYPE_NORMAL },
00063 { "ESOM", 4, CMD_ESOM, SMTP_CMD_TYPE_NORMAL },
00064 { "ETRN", 4, CMD_ETRN, SMTP_CMD_TYPE_NORMAL },
00065 { "EVFY", 4, CMD_EVFY, SMTP_CMD_TYPE_NORMAL },
00066 { "EXPN", 4, CMD_EXPN, SMTP_CMD_TYPE_NORMAL },
00067 { "HELO", 4, CMD_HELO, SMTP_CMD_TYPE_NORMAL },
00068 { "HELP", 4, CMD_HELP, SMTP_CMD_TYPE_NORMAL },
00069 { "IDENT", 5, CMD_IDENT, SMTP_CMD_TYPE_NORMAL },
00070 { "MAIL", 4, CMD_MAIL, SMTP_CMD_TYPE_NORMAL },
00071 { "NOOP", 4, CMD_NOOP, SMTP_CMD_TYPE_NORMAL },
00072 { "ONEX", 4, CMD_ONEX, SMTP_CMD_TYPE_NORMAL },
00073 { "QUEU", 4, CMD_QUEU, SMTP_CMD_TYPE_NORMAL },
00074 { "QUIT", 4, CMD_QUIT, SMTP_CMD_TYPE_NORMAL },
00075 { "RCPT", 4, CMD_RCPT, SMTP_CMD_TYPE_NORMAL },
00076 { "RSET", 4, CMD_RSET, SMTP_CMD_TYPE_NORMAL },
00077 { "SAML", 4, CMD_SAML, SMTP_CMD_TYPE_NORMAL },
00078 { "SEND", 4, CMD_SEND, SMTP_CMD_TYPE_NORMAL },
00079 { "SIZE", 4, CMD_SIZE, SMTP_CMD_TYPE_NORMAL },
00080 { "STARTTLS", 8, CMD_STARTTLS, SMTP_CMD_TYPE_NORMAL },
00081 { "SOML", 4, CMD_SOML, SMTP_CMD_TYPE_NORMAL },
00082 { "TICK", 4, CMD_TICK, SMTP_CMD_TYPE_NORMAL },
00083 { "TIME", 4, CMD_TIME, SMTP_CMD_TYPE_NORMAL },
00084 { "TURN", 4, CMD_TURN, SMTP_CMD_TYPE_NORMAL },
00085 { "TURNME", 6, CMD_TURNME, SMTP_CMD_TYPE_NORMAL },
00086 { "VERB", 4, CMD_VERB, SMTP_CMD_TYPE_NORMAL },
00087 { "VRFY", 4, CMD_VRFY, SMTP_CMD_TYPE_NORMAL },
00088 { "X-EXPS", 6, CMD_X_EXPS, SMTP_CMD_TYPE_AUTH },
00089 { "XADR", 4, CMD_XADR, SMTP_CMD_TYPE_NORMAL },
00090 { "XAUTH", 5, CMD_XAUTH, SMTP_CMD_TYPE_AUTH },
00091 { "XCIR", 4, CMD_XCIR, SMTP_CMD_TYPE_NORMAL },
00092 { "XEXCH50", 7, CMD_XEXCH50, SMTP_CMD_TYPE_BDATA },
00093 { "XGEN", 4, CMD_XGEN, SMTP_CMD_TYPE_NORMAL },
00094 { "XLICENSE", 8, CMD_XLICENSE, SMTP_CMD_TYPE_NORMAL },
00095 { "X-LINK2STATE", 12, CMD_X_LINK2STATE, SMTP_CMD_TYPE_NORMAL },
00096 { "XQUE", 4, CMD_XQUE, SMTP_CMD_TYPE_NORMAL },
00097 { "XSTA", 4, CMD_XSTA, SMTP_CMD_TYPE_NORMAL },
00098 { "XTRN", 4, CMD_XTRN, SMTP_CMD_TYPE_NORMAL },
00099 { "XUSR", 4, CMD_XUSR, SMTP_CMD_TYPE_NORMAL },
00100 { "*", 1, CMD_ABORT, SMTP_CMD_TYPE_NORMAL },
00101 { nullptr, 0, 0, SMTP_CMD_TYPE_NORMAL }
00102 };
00103
00104 const SMTPToken smtp_resps[] =
00105 {
00106 { "220", 3, RESP_220, SMTP_CMD_TYPE_NORMAL }, /* Service ready - initial response and
00107 STARTTLS response */
00108 { "221", 3, RESP_221, SMTP_CMD_TYPE_NORMAL }, /* Goodbye - response to QUIT */
00109 { "235", 3, RESP_235, SMTP_CMD_TYPE_NORMAL }, /* Auth done response */
00110 { "250", 3, RESP_250, SMTP_CMD_TYPE_NORMAL }, /* Requested mail action okay, completed */
00111 { "334", 3, RESP_334, SMTP_CMD_TYPE_NORMAL }, /* Auth intermediate response */
00112 { "354", 3, RESP_354, SMTP_CMD_TYPE_NORMAL }, /* Start mail input - data response */
00113 { "421", 3, RESP_421, SMTP_CMD_TYPE_NORMAL }, /* Service not available - closes connection
00114 */
00115 { "450", 3, RESP_450, SMTP_CMD_TYPE_NORMAL }, /* Mailbox unavailable */
00116 { "451", 3, RESP_451, SMTP_CMD_TYPE_NORMAL }, /* Local error in processing */
00117 { "452", 3, RESP_452, SMTP_CMD_TYPE_NORMAL }, /* Insufficient system storage */
00118 { "500", 3, RESP_500, SMTP_CMD_TYPE_NORMAL }, /* Command unrecognized */
00119 { "501", 3, RESP_501, SMTP_CMD_TYPE_NORMAL }, /* Syntax error in parameters or arguments */
00120 { "502", 3, RESP_502, SMTP_CMD_TYPE_NORMAL }, /* Command not implemented */
00121 { "503", 3, RESP_503, SMTP_CMD_TYPE_NORMAL }, /* Bad sequence of commands */
00122 { "504", 3, RESP_504, SMTP_CMD_TYPE_NORMAL }, /* Command parameter not implemented */
00123 { "535", 3, RESP_535, SMTP_CMD_TYPE_NORMAL }, /* Authentication failed */
00124 { "550", 3, RESP_550, SMTP_CMD_TYPE_NORMAL }, /* Action not taken - mailbox unavailable */
00125 { "551", 3, RESP_551, SMTP_CMD_TYPE_NORMAL }, /* User not local; please try <forward-path>
00126 */
00127 { "552", 3, RESP_552, SMTP_CMD_TYPE_NORMAL }, /* Mail action aborted: exceeded storage
00128 allocation */
00129 { "553", 3, RESP_553, SMTP_CMD_TYPE_NORMAL }, /* Action not taken: mailbox name not allowed
00130 */
00131 { "554", 3, RESP_554, SMTP_CMD_TYPE_NORMAL }, /* Transaction failed */
00132 { nullptr, 0, 0, SMTP_CMD_TYPE_NORMAL }
00133 };
00134
00135 typedef struct _SMTPAuth
00136 {
00137 const char* name;
00138 int name_len;
00139 } SMTPAuth;
00140
00141 /* Cyrus SASL authentication mechanisms ANONYMOUS, PLAIN and LOGIN
00142 * does not have context
00143 */
00144 const SMTPAuth smtp_auth_no_ctx[] =
00145 {
00146 { "ANONYMOUS", 9 },
00147 { "PLAIN", 5 },
00148 { "LOGIN", 5 },
00149 { nullptr, 0 }
00150 };
00151
00152 SearchTool* smtp_resp_search_mpse = nullptr;
00153
00154 SMTPSearch smtp_resp_search[RESP_LAST];
00155
00156 static THREAD_LOCAL const SMTPSearch* smtp_current_search = nullptr;
00157 static THREAD_LOCAL SMTPSearchInfo smtp_search_info;
00158
00159 const PegInfo smtp_peg_names[] =
00160 {
00161 { CountType::SUM, "packets", "total packets processed" },
00162 { CountType::SUM, "sessions", "total smtp sessions" },
00163 { CountType::NOW, "concurrent_sessions", "total concurrent smtp sessions" },
00164 { CountType::MAX, "max_concurrent_sessions", "maximum concurrent smtp sessions" },
00165 { CountType::SUM, "b64_attachments", "total base64 attachments decoded" },
00166 { CountType::SUM, "b64_decoded_bytes", "total base64 decoded bytes" },
00167 { CountType::SUM, "qp_attachments", "total quoted-printable attachments decoded" },
00168 { CountType::SUM, "qp_decoded_bytes", "total quoted-printable decoded bytes" },
00169 { CountType::SUM, "uu_attachments", "total uu attachments decoded" },
00170 { CountType::SUM, "uu_decoded_bytes", "total uu decoded bytes" },
00171 { CountType::SUM, "non_encoded_attachments", "total non-encoded attachments extracted" },
00172 { CountType::SUM, "non_encoded_bytes", "total non-encoded extracted bytes" },
00173
00174 { CountType::END, nullptr, nullptr }
00175 };
00176
00177 static void snort_smtp(SMTP_PROTO_CONF* GlobalConf, Packet* p);
00178 static void SMTP_ResetState(Flow*);
00179
00180 SmtpFlowData::SmtpFlowData() : FlowData(inspector_id)
00181 {
00182 memset(&session, 0, sizeof(session));
00183 smtpstats.concurrent_sessions++;
00184 if(smtpstats.max_concurrent_sessions < smtpstats.concurrent_sessions)
00185 smtpstats.max_concurrent_sessions = smtpstats.concurrent_sessions;
00186 }
00187
00188 SmtpFlowData::~SmtpFlowData()
00189 {
00190 if ( session.mime_ssn )
00191 delete session.mime_ssn;
00192
00193 if ( session.auth_name )
00194 snort_free(session.auth_name);
00195
00196 assert(smtpstats.concurrent_sessions > 0);
00197 smtpstats.concurrent_sessions--;
00198 }
00199
00200 unsigned SmtpFlowData::inspector_id = 0;
00201 static SMTPData* get_session_data(Flow* flow)
00202 {
00203 SmtpFlowData* fd = (SmtpFlowData*)flow->get_flow_data(SmtpFlowData::inspector_id);
00204 return fd ? &fd->session : nullptr;
00205 }
00206
00207 static SMTPData* SetNewSMTPData(SMTP_PROTO_CONF* config, Packet* p)
00208 {
00209 SMTPData* smtp_ssn;
00210 SmtpFlowData* fd = new SmtpFlowData;
00211
00212 p->flow->set_flow_data(fd);
00213 smtp_ssn = &fd->session;
00214
00215 smtp_ssn->mime_ssn = new SmtpMime(&(config->decode_conf), &(config->log_config));
00216 smtp_ssn->mime_ssn->config = config;
00217 smtp_ssn->mime_ssn->set_mime_stats(&(smtpstats.mime_stats));
00218
00219 if(Stream::is_midstream(p->flow))
00220 {
00221 DebugMessage(DEBUG_SMTP, "Got midstream packet - "
00222 "setting state to unknown\n");
00223 smtp_ssn->state = STATE_UNKNOWN;
00224 }
00225
00226 return smtp_ssn;
00227 }
00228
00229 static void SMTP_InitCmds(SMTP_PROTO_CONF* config)
00230 {
00231 if (config == nullptr)
00232 return;
00233
00234 config->cmd_config = (SMTPCmdConfig*)snort_calloc(CMD_LAST, sizeof(*config->cmd_config));
00235 config->cmds = (SMTPToken*)snort_calloc((CMD_LAST + 1), sizeof(*config->cmds));
00236
00237 for (const SMTPToken* tmp = &smtp_known_cmds[0]; tmp->name != nullptr; tmp++)
00238 {
00239 SMTPToken* tok = config->cmds + tmp->search_id;
00240 tok->name_len = tmp->name_len;
00241 tok->search_id = tmp->search_id;
00242 tok->name = snort_strdup(tmp->name);
00243 tok->type = tmp->type;
00244 }
00245
00246 config->num_cmds = CMD_LAST;
00247 }
00248
00249 static void SMTP_TermCmds(SMTP_PROTO_CONF* config)
00250 {
00251 for ( int i = 0; i <= config->num_cmds; ++i )
00252 snort_free(const_cast<char*>(config->cmds[i].name));
00253
00254 snort_free(config->cmds);
00255 snort_free(config->cmd_config);
00256 }
00257
00258 static void SMTP_CommandSearchInit(SMTP_PROTO_CONF* config)
00259 {
00260 config->cmd_search_mpse = new SearchTool;
00261 config->cmd_search = (SMTPSearch*)snort_calloc(config->num_cmds, sizeof(*config->cmd_search));
00262
00263 for ( const SMTPToken* tmp = config->cmds; tmp->name != nullptr; tmp++ )
00264 {
00265 config->cmd_search[tmp->search_id].name = tmp->name;
00266 config->cmd_search[tmp->search_id].name_len = tmp->name_len;
00267 config->cmd_search_mpse->add(tmp->name, tmp->name_len, tmp->search_id);
00268 }
00269
00270 config->cmd_search_mpse->prep();
00271 }
00272
00273 static void SMTP_CommandSearchTerm(SMTP_PROTO_CONF* config)
00274 {
00275 snort_free(config->cmd_search);
00276 delete config->cmd_search_mpse;
00277 }
00278
00279 static void SMTP_ResponseSearchInit()
00280 {
00281 const SMTPToken* tmp;
00282
00283 if ( smtp_resp_search_mpse )
00284 return;
00285
00286 smtp_resp_search_mpse = new SearchTool;
00287
00288 for (tmp = &smtp_resps[0]; tmp->name != nullptr; tmp++)
00289 {
00290 smtp_resp_search[tmp->search_id].name = tmp->name;
00291 smtp_resp_search[tmp->search_id].name_len = tmp->name_len;
00292 smtp_resp_search_mpse->add(tmp->name, tmp->name_len, tmp->search_id);
00293 }
00294 smtp_resp_search_mpse->prep();
00295 }
00296
00297 static void SMTP_SearchFree()
00298 {
00299 if (smtp_resp_search_mpse != nullptr)
00300 delete smtp_resp_search_mpse;
00301 }
00302
00303 static int AddCmd(SMTP_PROTO_CONF* config, const char* name, SMTPCmdTypeEnum type)
00304 {
00305 SMTPToken* cmds;
00306 SMTPCmdConfig* cmd_config;
00307
00308 config->num_cmds++;
00309
00310 /* allocate enough memory for new command - alloc one extra for NULL entry */
00311 // FIXIT-L this constant reallocation is not necessary; use vector
00312 cmds = (SMTPToken*)snort_calloc((config->num_cmds + 1) * sizeof(*cmds));
00313 cmd_config = (SMTPCmdConfig*)snort_calloc((config->num_cmds + 1) * sizeof(*cmd_config));
00314
00315 /* copy existing commands into newly allocated memory */
00316 memcpy_s(cmds, (config->num_cmds) * sizeof(*cmds),
00317 config->cmds, (config->num_cmds) * sizeof(*cmds) - 1);
00318
00319 memcpy_s(cmd_config, config->num_cmds * sizeof(*cmd_config),
00320 config->cmd_config, config->num_cmds - 1);
00321
00322 /* add new command to cmds cmd_config doesn't need anything added - this
00323 * will probably be done by a calling function */
00324
00325 SMTPToken* tok = cmds + config->num_cmds - 1;
00326 tok->name = snort_strdup(name);
00327 tok->name_len = strlen(name);
00328 tok->search_id = config->num_cmds - 1;
00329
00330 if (type)
00331 tok->type = type;
00332
00333 /* free global memory structures */
00334 if ( config->cmds )
00335 snort_free(config->cmds);
00336
00337 if ( config->cmd_config )
00338 snort_free(config->cmd_config);
00339
00340 /* set globals to new memory */
00341 config->cmds = cmds;
00342 config->cmd_config = cmd_config;
00343
00344 return (config->num_cmds - 1);
00345 }
00346
00347 /* Return id associated with a given command string */
00348 static int GetCmdId(SMTP_PROTO_CONF* config, const char* name, SMTPCmdTypeEnum type)
00349 {
00350 SMTPToken* cmd;
00351
00352 for (cmd = config->cmds; cmd->name != nullptr; cmd++)
00353 {
00354 if (strcasecmp(cmd->name, name) == 0)
00355 {
00356 if (type && (type != cmd->type))
00357 cmd->type = type;
00358
00359 return cmd->search_id;
00360 }
00361 }
00362
00363 return AddCmd(config, name, type);
00364 }
00365
00366 static void SMTP_PrintConfig(SMTP_PROTO_CONF *config)
00367 {
00368 const SMTPToken* cmd;
00369 char buf[8192];
00370 int max_line_len_count = 0;
00371 int max_line_len = 0;
00372 int alert_count = 0;
00373
00374 if (config == nullptr)
00375 return;
00376
00377 memset(&buf[0], 0, sizeof(buf));
00378
00379 LogMessage("SMTP Config:\n");
00380 snprintf(buf, sizeof(buf) - 1, " Normalize: ");
00381
00382 if(config->normalize == NORMALIZE_ALL)
00383 sfsnprintfappend(buf, sizeof(buf) - 1, "all");
00384 else if(config->normalize == NORMALIZE_NONE)
00385 sfsnprintfappend(buf, sizeof(buf) - 1, "none");
00386 else if(config->normalize == NORMALIZE_CMDS)
00387 {
00388 for (cmd = config->cmds; cmd->name != nullptr; cmd++)
00389 {
00390 if (config->cmd_config[cmd->search_id].normalize)
00391 {
00392 sfsnprintfappend(buf, sizeof(buf) - 1, "%s ", cmd->name);
00393 }
00394 }
00395 }
00396
00397 LogMessage("%s\n", buf);
00398
00399 LogMessage(" Ignore Data: %s\n",
00400 config->decode_conf.is_ignore_data() ? "Yes" : "No");
00401 LogMessage(" Ignore TLS Data: %s\n",
00402 config->ignore_tls_data ? "Yes" : "No");
00403 snprintf(buf, sizeof(buf) - 1, " Max Command Line Length: ");
00404
00405 if (config->max_command_line_len == 0)
00406 sfsnprintfappend(buf, sizeof(buf) - 1, "Unlimited");
00407 else
00408 sfsnprintfappend(buf, sizeof(buf) - 1, "%d", config->max_command_line_len);
00409
00410 LogMessage("%s\n", buf);
00411
00412 {
00413 snprintf(buf, sizeof(buf) - 1, " Max Specific Command Line Length: ");
00414
00415 for (cmd = config->cmds; cmd->name != nullptr; cmd++)
00416 {
00417 max_line_len = config->cmd_config[cmd->search_id].max_line_len;
00418
00419 if (max_line_len != 0)
00420 {
00421 if (max_line_len_count % 5 == 0)
00422 {
00423 LogMessage("%s\n", buf);
00424 snprintf(buf, sizeof(buf) - 1, " %s:%d ", cmd->name, max_line_len);
00425 }
00426 else
00427 {
00428 sfsnprintfappend(buf, sizeof(buf) - 1, "%s:%d ", cmd->name, max_line_len);
00429 }
00430
00431 max_line_len_count++;
00432 }
00433 }
00434
00435 if (max_line_len_count == 0)
00436 LogMessage("%sNone\n", buf);
00437 else
00438 LogMessage("%s\n", buf);
00439 }
00440 snprintf(buf, sizeof(buf) - 1, " Max Header Line Length: ");
00441
00442 if (config->max_header_line_len == 0)
00443 LogMessage("%sUnlimited\n", buf);
00444 else
00445 LogMessage("%s%d\n", buf, config->max_header_line_len);
00446
00447 snprintf(buf, sizeof(buf) - 1, " Max Auth Command Line Length: ");
00448 LogMessage("%s%d\n", buf, config->max_auth_command_line_len);
00449
00450 snprintf(buf, sizeof(buf) - 1, " Max Response Line Length: ");
00451
00452 if (config->max_response_line_len == 0)
00453 LogMessage("%sUnlimited\n", buf);
00454 else
00455 LogMessage("%s%d\n", buf, config->max_response_line_len);
00456
00457 LogMessage(" X-Link2State Enabled: %s\n",
00458 (config->xlink2state == ALERT_XLINK2STATE) ? "Yes" : "No");
00459 if (config->xlink2state == DROP_XLINK2STATE)
00460 {
00461 LogMessage(" Drop on X-Link2State Alert: %s\n", "Yes" );
00462 }
00463 else
00464 {
00465 LogMessage(" Drop on X-Link2State Alert: %s\n", "No" );
00466 }
00467
00468 snprintf(buf, sizeof(buf) - 1, " Alert on commands: ");
00469
00470 for (cmd = config->cmds; cmd->name != nullptr; cmd++)
00471 {
00472 if (config->cmd_config[cmd->search_id].alert)
00473 {
00474 sfsnprintfappend(buf, sizeof(buf) - 1, "%s ", cmd->name);
00475 alert_count++;
00476 }
00477 }
00478
00479 if (alert_count == 0)
00480 {
00481 LogMessage("%sNone\n", buf);
00482 }
00483 else
00484 {
00485 LogMessage("%s\n", buf);
00486 }
00487
00488 config->decode_conf.print_decode_conf();
00489
00490 LogMessage(" Log Attachment filename: %s\n",
00491 config->log_config.log_filename ? "Enabled" : "Not Enabled");
00492
00493 LogMessage(" Log MAIL FROM Address: %s\n",
00494 config->log_config.log_mailfrom ? "Enabled" : "Not Enabled");
00495
00496 LogMessage(" Log RCPT TO Addresses: %s\n",
00497 config->log_config.log_rcptto ? "Enabled" : "Not Enabled");
00498
00499 LogMessage(" Log Email Headers: %s\n",
00500 config->log_config.log_email_hdrs ? "Enabled" : "Not Enabled");
00501 if (config->log_config.log_email_hdrs)
00502 {
00503 LogMessage(" Email Hdrs Log Depth: %u\n",
00504 config->log_config.email_hdrs_log_depth);
00505 }
00506 }
00507
00508 static void SMTP_ResetState(Flow* ssn)
00509 {
00510 SMTPData* smtp_ssn = get_session_data(ssn);
00511 smtp_ssn->state = STATE_COMMAND;
00512 smtp_ssn->state_flags = 0;
00513 }
00514
00515 static inline int InspectPacket(Packet* p)
00516 {
00517 return p->has_paf_payload();
00518 }
00519
00520 /*
00521 * Do first-packet setup
00522 *
00523 * @param p standard Packet structure
00524 *
00525 * @return none
00526 */
00527 static int SMTP_Setup(Packet* p, SMTPData* ssn)
00528 {
00529 int pkt_dir;
00530
00531 /* Get the direction of the packet. */
00532 if ( p->is_from_server() )
00533 pkt_dir = SMTP_PKT_FROM_SERVER;
00534 else
00535 pkt_dir = SMTP_PKT_FROM_CLIENT;
00536
00537 if (!(ssn->session_flags & SMTP_FLAG_CHECK_SSL))
00538 ssn->session_flags |= SMTP_FLAG_CHECK_SSL;
00539 /* Check to see if there is a reassembly gap. If so, we won't know
00540 * * * what state we're in when we get the _next_ reassembled packet */
00541
00542 /* Check to see if there is a reassembly gap. If so, we won't know
00543 * what state we're in when we get the _next_ reassembled packet */
00544 if ((pkt_dir != SMTP_PKT_FROM_SERVER) &&
00545 (p->packet_flags & PKT_REBUILT_STREAM))
00546 {
00547 int missing_in_rebuilt =
00548 Stream::missing_in_reassembled(p->flow, SSN_DIR_FROM_CLIENT);
00549
00550 if (ssn->session_flags & SMTP_FLAG_NEXT_STATE_UNKNOWN)
00551 {
00552 DebugMessage(DEBUG_SMTP, "Found gap in previous reassembly buffer - "
00553 "set state to unknown\n");
00554 ssn->state = STATE_UNKNOWN;
00555 ssn->session_flags &= ~SMTP_FLAG_NEXT_STATE_UNKNOWN;
00556 }
00557
00558 if (missing_in_rebuilt == SSN_MISSING_BEFORE)
00559 {
00560 DebugMessage(DEBUG_SMTP, "Found missing packets before "
00561 "in reassembly buffer - set state to unknown\n");
00562 ssn->state = STATE_UNKNOWN;
00563 }
00564 }
00565
00566 return pkt_dir;
00567 }
00568
00569 /*
00570 * Callback function for string search
00571 *
00572 * @param id id in array of search strings from smtp_config.cmds
00573 * @param index index in array of search strings from smtp_config.cmds
00574 * @param data buffer passed in to search function
00575 *
00576 * @return response
00577 * @retval 1 commands caller to stop searching
00578 */
00579 static int SMTP_SearchStrFound(void* id, void*, int index, void*, void*)
00580 {
00581 int search_id = (int)(uintptr_t)id;
00582
00583 smtp_search_info.id = search_id;
00584 smtp_search_info.length = smtp_current_search[search_id].name_len;
00585 smtp_search_info.index = index - smtp_search_info.length;
00586
00587 /* Returning non-zero stops search, which is okay since we only look for one at a time */
00588 return 1;
00589 }
00590
00591 static bool SMTP_IsAuthCtxIgnored(const uint8_t* start, int length)
00592 {
00593 const SMTPAuth* tmp;
00594 for (tmp = &smtp_auth_no_ctx[0]; tmp->name != nullptr; tmp++)
00595 {
00596 if ((tmp->name_len == length) && (!memcmp(start, tmp->name, length)))
00597 return true;
00598 }
00599
00600 return false;
00601 }
00602
00603 static bool SMTP_IsAuthChanged(SMTPData* smtp_ssn, const uint8_t* start_ptr, const
00604 uint8_t* end_ptr)
00605 {
00606 int length;
00607 bool auth_changed = false;
00608 const uint8_t* start = start_ptr;
00609 const uint8_t* end = end_ptr;
00610
00611 while ((start < end) && isspace(*start))
00612 start++;
00613 while ((start < end) && isspace(*(end-1)))
00614 end--;
00615
00616 if (start >= end)
00617 return auth_changed;
00618
00619 length = end - start;
00620
00621 if (length > MAX_AUTH_NAME_LEN)
00622 return auth_changed;
00623
00624 if (SMTP_IsAuthCtxIgnored(start, length))
00625 return auth_changed;
00626
00627 /* if authentication mechanism is set, compare it with current one*/
00628 if (smtp_ssn->auth_name)
00629 {
00630 if (smtp_ssn->auth_name->length != length)
00631 auth_changed = true;
00632 else if (memcmp(start, smtp_ssn->auth_name->name, length))
00633 auth_changed = true;
00634 }
00635 else
00636 smtp_ssn->auth_name = (SMTPAuthName*)snort_calloc(sizeof(*(smtp_ssn->auth_name)));
00637
00638 /* save the current authentication mechanism*/
00639 if (auth_changed || (!smtp_ssn->auth_name->length))
00640 {
00641 memcpy(smtp_ssn->auth_name->name, start, length);
00642 smtp_ssn->auth_name->length = length;
00643 }
00644
00645 return auth_changed;
00646 }
00647
00648 /*
00649 * Handle COMMAND state
00650 *
00651 * @param p standard Packet structure
00652 * @param ptr pointer into p->data buffer to start looking at data
00653 * @param end points to end of p->data buffer
00654 *
00655 * @return pointer into p->data where we stopped looking at data
00656 * will be end of line or end of packet
00657 */
00658 static const uint8_t* SMTP_HandleCommand(SMTP_PROTO_CONF* config, Packet* p, SMTPData* smtp_ssn,
00659 const uint8_t* ptr, const uint8_t* end)
00660 {
00661 const uint8_t* eol; /* end of line */
00662 const uint8_t* eolm; /* end of line marker */
00663 int cmd_line_len;
00664 int ret;
00665 int cmd_found;
00666 char alert_long_command_line = 0;
00667
00668 /* get end of line and end of line marker */
00669 SMTP_GetEOL(ptr, end, &eol, &eolm);
00670
00671 /* calculate length of command line */
00672 cmd_line_len = eol - ptr;
00673
00674 /* check for command line exceeding maximum
00675 * do this before checking for a command since this could overflow
00676 * some server's buffers without the presence of a known command */
00677 if ((config->max_command_line_len != 0) &&
00678 (cmd_line_len > config->max_command_line_len))
00679 {
00680 alert_long_command_line = 1;
00681 }
00682
00683 // FIXIT-M if the end of line marker coincides with the end of data we
00684 // can't be sure that we got a command and not a substring which we
00685 // could tell through inspection of the next packet. Maybe a command
00686 // pending state where the first char in the next packet is checked for
00687 // a space and end of line marker
00688
00689 /* do not confine since there could be space chars before command */
00690 smtp_current_search = &config->cmd_search[0];
00691 cmd_found = config->cmd_search_mpse->find(
00692 (const char*)ptr, eolm - ptr, SMTP_SearchStrFound);
00693 /* see if we actually found a command and not a substring */
00694 if (cmd_found > 0)
00695 {
00696 const uint8_t* tmp = ptr;
00697 const uint8_t* cmd_start = ptr + smtp_search_info.index;
00698 const uint8_t* cmd_end = cmd_start + smtp_search_info.length;
00699
00700 /* move past spaces up until start of command */
00701 while ((tmp < cmd_start) && isspace((int)*tmp))
00702 tmp++;
00703
00704 /* if not all spaces before command, we found a
00705 * substring */
00706 if (tmp != cmd_start)
00707 cmd_found = 0;
00708
00709 /* if we're before the end of line marker and the next
00710 * character is not whitespace, we found a substring */
00711 if ((cmd_end < eolm) && !isspace((int)*cmd_end))
00712 cmd_found = 0;
00713
00714 /* there is a chance that end of command coincides with the end of data
00715 * in which case, it could be a substring, but for now, we will treat it as found */
00716 }
00717
00718 /* if command not found, alert and move on */
00719 if (!cmd_found)
00720 {
00721 /* If we missed one or more packets we might not actually be in the command
00722 * state. Check to see if we're encrypted */
00723 if (smtp_ssn->state == STATE_UNKNOWN)
00724 {
00725 DebugMessage(DEBUG_SMTP, "Command not found, but state is "
00726 "unknown - checking for SSL\n");
00727
00728 /* check for encrypted */
00729
00730 if ((smtp_ssn->session_flags & SMTP_FLAG_CHECK_SSL) &&
00731 (IsSSL(ptr, end - ptr, p->packet_flags)))
00732 {
00733 DebugMessage(DEBUG_SMTP, "Packet is SSL encrypted\n");
00734
00735 smtp_ssn->state = STATE_TLS_DATA;
00736
00737 /* Ignore data */
00738 if (config->ignore_tls_data)
00739 {
00740 DebugMessage(DEBUG_SMTP, "Ignoring encrypted data\n");
00741 }
00742
00743 return end;
00744 }
00745 else
00746 {
00747 DebugMessage(DEBUG_SMTP, "Not SSL - try data state\n");
00748 /* don't check for ssl again in this packet */
00749 if (smtp_ssn->session_flags & SMTP_FLAG_CHECK_SSL)
00750 smtp_ssn->session_flags &= ~SMTP_FLAG_CHECK_SSL;
00751
00752 smtp_ssn->state = STATE_DATA;
00753 smtp_ssn->mime_ssn->set_data_state(STATE_DATA_INIT);
00754
00755 return ptr;
00756 }
00757 }
00758 else
00759 {
00760 DebugMessage(DEBUG_SMTP, "No known command found\n");
00761
00762 if (smtp_ssn->state != STATE_AUTH)
00763 {
00764 DetectionEngine::queue_event(GID_SMTP,SMTP_UNKNOWN_CMD);
00765
00766 if (alert_long_command_line)
00767 DetectionEngine::queue_event(GID_SMTP, SMTP_COMMAND_OVERFLOW);
00768 }
00769
00770 /* if normalizing, copy line to alt buffer */
00771 if (smtp_normalizing)
00772 {
00773 ret = SMTP_CopyToAltBuffer(p, ptr, eol - ptr);
00774 if (ret == -1)
00775 return nullptr;
00776 }
00777
00778 return eol;
00779 }
00780 }
00781
00782 /* At this point we have definitely found a legitimate command */
00783
00784 /* check if max command line length for a specific command is exceeded */
00785 if (config->cmd_config[smtp_search_info.id].max_line_len != 0)
00786 {
00787 if (cmd_line_len > config->cmd_config[smtp_search_info.id].max_line_len)
00788 {
00789 DetectionEngine::queue_event(GID_SMTP, SMTP_SPECIFIC_CMD_OVERFLOW);
00790 }
00791 }
00792 else if (alert_long_command_line)
00793 {
00794 DetectionEngine::queue_event(GID_SMTP, SMTP_COMMAND_OVERFLOW);
00795 }
00796
00797 if (config->cmd_config[smtp_search_info.id].alert)
00798 {
00799 /* Are we alerting on this command? */
00800 DetectionEngine::queue_event(GID_SMTP, SMTP_ILLEGAL_CMD);
00801 }
00802
00803 switch (smtp_search_info.id)
00804 {
00805 /* unless we do our own parsing of MAIL and RCTP commands we have to assume they
00806 * are ok unless we got a server error in which case we flush and if this is a
00807 * reassembled packet, the last command in this packet will be the command that
00808 * caused the error */
00809 case CMD_MAIL:
00810 smtp_ssn->state_flags |= SMTP_FLAG_GOT_MAIL_CMD;
00811 if ( config->log_config.log_mailfrom )
00812 {
00813 smtp_ssn->mime_ssn->get_log_state()->log_email_id(ptr, eolm - ptr, EMAIL_SENDER);
00814 }
00815
00816 break;
00817
00818 case CMD_RCPT:
00819 if ((smtp_ssn->state_flags & SMTP_FLAG_GOT_MAIL_CMD) ||
00820 smtp_ssn->state == STATE_UNKNOWN)
00821 {
00822 smtp_ssn->state_flags |= SMTP_FLAG_GOT_RCPT_CMD;
00823 }
00824
00825 if (config->log_config.log_rcptto)
00826 {
00827 smtp_ssn->mime_ssn->get_log_state()->log_email_id(ptr, eolm - ptr, EMAIL_RECIPIENT);
00828 }
00829
00830 break;
00831
00832 case CMD_RSET:
00833 case CMD_HELO:
00834 case CMD_EHLO:
00835 case CMD_QUIT:
00836 smtp_ssn->state_flags &= ~(SMTP_FLAG_GOT_MAIL_CMD | SMTP_FLAG_GOT_RCPT_CMD);
00837
00838 break;
00839
00840 case CMD_STARTTLS:
00841 /* if reassembled we flush after seeing a 220 so this should be the last
00842 * command in reassembled packet and if not reassembled it should be the
00843 * last line in the packet as you can't pipeline the tls hello */
00844 if (eol == end)
00845 smtp_ssn->state = STATE_TLS_CLIENT_PEND;
00846
00847 break;
00848
00849 case CMD_X_LINK2STATE:
00850 if (config->xlink2state)
00851 ParseXLink2State(config, p, smtp_ssn, ptr + smtp_search_info.index);
00852
00853 break;
00854
00855 case CMD_AUTH:
00856 smtp_ssn->state = STATE_AUTH;
00857 if (SMTP_IsAuthChanged(smtp_ssn, ptr + smtp_search_info.index + smtp_search_info.length,
00858 eolm)
00859 && (smtp_ssn->state_flags & SMTP_FLAG_ABORT))
00860 {
00861 DetectionEngine::queue_event(GID_SMTP, SMTP_AUTH_ABORT_AUTH);
00862 }
00863 smtp_ssn->state_flags &= ~(SMTP_FLAG_ABORT);
00864 break;
00865
00866 case CMD_ABORT:
00867 smtp_ssn->state_flags |= SMTP_FLAG_ABORT;
00868 break;
00869
00870 default:
00871 switch (smtp_known_cmds[smtp_search_info.id].type)
00872 {
00873 case SMTP_CMD_TYPE_DATA:
00874 if ((smtp_ssn->state_flags & SMTP_FLAG_GOT_RCPT_CMD) ||
00875 smtp_ssn->state == STATE_UNKNOWN)
00876 {
00877 DebugMessage(DEBUG_SMTP, "Set to data state.\n");
00878
00879 smtp_ssn->state = STATE_DATA;
00880 smtp_ssn->state_flags &= ~(SMTP_FLAG_GOT_MAIL_CMD | SMTP_FLAG_GOT_RCPT_CMD);
00881 }
00882 else
00883 {
00884 DebugMessage(DEBUG_SMTP, "Didn't get MAIL -> RCPT command sequence - "
00885 "stay in command state.\n");
00886 }
00887
00888 break;
00889
00890 case SMTP_CMD_TYPE_BDATA:
00891 if ((smtp_ssn->state_flags & (SMTP_FLAG_GOT_RCPT_CMD | SMTP_FLAG_BDAT))
00892 || (smtp_ssn->state == STATE_UNKNOWN))
00893 {
00894 const uint8_t* begin_chunk;
00895 const uint8_t* end_chunk;
00896 const uint8_t* tmp;
00897 int num_digits;
00898 int ten_power;
00899 uint32_t dat_chunk = 0;
00900
00901 begin_chunk = ptr + smtp_search_info.index + smtp_search_info.length;
00902 while ((begin_chunk < eolm) && isspace((int)*begin_chunk))
00903 begin_chunk++;
00904
00905 /* bad BDAT command - needs chunk argument */
00906 if (begin_chunk == eolm)
00907 break;
00908
00909 end_chunk = begin_chunk;
00910 while ((end_chunk < eolm) && isdigit((int)*end_chunk))
00911 end_chunk++;
00912
00913 /* didn't get all digits */
00914 if ((end_chunk < eolm) && !isspace((int)*end_chunk))
00915 break;
00916
00917 /* get chunk size */
00918 num_digits = end_chunk - begin_chunk;
00919
00920 /* more than 9 digits could potentially overflow a 32 bit integer
00921 * most servers won't accept this much in a chunk */
00922 if (num_digits > 9)
00923 break;
00924
00925 tmp = end_chunk;
00926 for (ten_power = 1, tmp--; tmp >= begin_chunk; ten_power *= 10, tmp--)
00927 dat_chunk += (*tmp - '0') * ten_power;
00928
00929 if (smtp_search_info.id == CMD_BDAT)
00930 {
00931 /* got a valid chunk size - check to see if this is the last chunk */
00932 const uint8_t* last = end_chunk;
00933 bool bdat_last = false;
00934
00935 while ((last < eolm) && isspace((int)*last))
00936 last++;
00937
00938 if (((eolm - last) >= 4)
00939 && (strncasecmp("LAST", (const char*)last, 4) == 0))
00940 {
00941 bdat_last = true;
00942 }
00943
00944 if (bdat_last || (dat_chunk == 0))
00945 smtp_ssn->state_flags &= ~(SMTP_FLAG_BDAT);
00946 else
00947 smtp_ssn->state_flags |= SMTP_FLAG_BDAT;
00948
00949 smtp_ssn->state = STATE_BDATA;
00950 smtp_ssn->state_flags &= ~(SMTP_FLAG_GOT_MAIL_CMD | SMTP_FLAG_GOT_RCPT_CMD);
00951 }
00952 else if (smtp_search_info.id == CMD_XEXCH50)
00953 {
00954 smtp_ssn->state = STATE_XEXCH50;
00955 }
00956 else
00957 {
00958 smtp_ssn->state = STATE_BDATA;
00959 smtp_ssn->state_flags &= ~(SMTP_FLAG_GOT_MAIL_CMD | SMTP_FLAG_GOT_RCPT_CMD);
00960 }
00961
00962 smtp_ssn->dat_chunk = dat_chunk;
00963 }
00964
00965 break;
00966
00967 case SMTP_CMD_TYPE_AUTH:
00968 smtp_ssn->state = STATE_AUTH;
00969 break;
00970
00971 default:
00972 break;
00973 }
00974 break;
00975 }
00976
00977 /* Since we found a command, if state is still unknown,
00978 * set to command state */
00979 if (smtp_ssn->state == STATE_UNKNOWN)
00980 smtp_ssn->state = STATE_COMMAND;
00981
00982 /* normalize command line */
00983 if (config->normalize == NORMALIZE_ALL ||
00984 config->cmd_config[smtp_search_info.id].normalize)
00985 {
00986 ret = SMTP_NormalizeCmd(p, ptr, eolm, eol);
00987 if (ret == -1)
00988 return nullptr;
00989 }
00990 else if (smtp_normalizing) /* Already normalizing */
00991 {
00992 ret = SMTP_CopyToAltBuffer(p, ptr, eol - ptr);
00993 if (ret == -1)
00994 return nullptr;
00995 }
00996
00997 return eol;
00998 }
00999
01000 /*
01001 * Process client packet
01002 *
01003 * @param packet standard Packet structure
01004 *
01005 * @return none
01006 */
01007 static void SMTP_ProcessClientPacket(SMTP_PROTO_CONF* config, Packet* p, SMTPData* smtp_ssn)
01008 {
01009 const uint8_t* ptr = p->data;
01010 const uint8_t* end = p->data + p->dsize;
01011
01012
01013 if (smtp_ssn->state == STATE_CONNECT)
01014 {
01015 smtp_ssn->state = STATE_COMMAND;
01016 }
01017
01018 while ((ptr != nullptr) && (ptr < end))
01019 {
01020 FilePosition position;
01021 int len = end - ptr;
01022
01023 switch (smtp_ssn->state)
01024 {
01025 case STATE_COMMAND:
01026 DebugMessage(DEBUG_SMTP, "COMMAND STATE ~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
01027 ptr = SMTP_HandleCommand(config, p, smtp_ssn, ptr, end);
01028 break;
01029 case STATE_DATA:
01030 case STATE_BDATA:
01031 DebugMessage(DEBUG_SMTP, "DATA STATE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
01032 position = get_file_position(p);
01033 ptr = smtp_ssn->mime_ssn->process_mime_data(p->flow, ptr, len, true, position);
01034 //ptr = SMTP_HandleData(p, ptr, end, &(smtp_ssn->mime_ssn));
01035 break;
01036 case STATE_XEXCH50:
01037 if (smtp_normalizing)
01038 SMTP_CopyToAltBuffer(p, ptr, end - ptr);
01039 if (smtp_is_data_end (p->flow))
01040 smtp_ssn->state = STATE_COMMAND;
01041 return;
01042 case STATE_AUTH:
01043 ptr = SMTP_HandleCommand(config, p, smtp_ssn, ptr, end);
01044 break;
01045 case STATE_UNKNOWN:
01046 DebugMessage(DEBUG_SMTP, "UNKNOWN STATE ~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
01047 /* If state is unknown try command state to see if we can
01048 * regain our bearings */
01049 ptr = SMTP_HandleCommand(config, p, smtp_ssn, ptr, end);
01050 break;
01051 default:
01052 DebugMessage(DEBUG_SMTP, "Bad SMTP state\n");
01053 return;
01054 }
01055 }
01056 }
01057
01058 /*
01059 * Process server packet
01060 *
01061 * @param packet standard Packet structure
01062 *
01063 * @return None
01064 */
01065 static void SMTP_ProcessServerPacket(SMTP_PROTO_CONF* config, Packet* p, SMTPData* smtp_ssn,
01066 int* next_state)
01067 {
01068 int resp_found;
01069 const uint8_t* ptr;
01070 const uint8_t* end;
01071 const uint8_t* eolm;
01072 const uint8_t* eol;
01073 int resp_line_len;
01074 #ifdef DEBUG_MSGS
01075 const uint8_t* dash;
01076 #endif
01077
01078 *next_state = 0;
01079
01080 ptr = p->data;
01081 end = p->data + p->dsize;
01082
01083 if (smtp_ssn->state == STATE_TLS_SERVER_PEND)
01084 {
01085 if (IsTlsServerHello(ptr, end))
01086 {
01087 smtp_ssn->state = STATE_TLS_DATA;
01088 }
01089 else if (!(p->flow->get_session_flags() & SSNFLAG_MIDSTREAM)
01090 && !Stream::missed_packets(p->flow, SSN_DIR_BOTH))
01091 {
01092 /* Check to see if the raw packet is in order */
01093 if (p->packet_flags & PKT_STREAM_ORDER_OK)
01094 {
01095 /* revert back to command state - assume server didn't accept STARTTLS */
01096 smtp_ssn->state = STATE_COMMAND;
01097 }
01098 else
01099 return;
01100 }
01101 }
01102
01103 if (smtp_ssn->state == STATE_TLS_DATA)
01104 {
01105 smtp_ssn->state = STATE_COMMAND;
01106 }
01107
01108 while (ptr < end)
01109 {
01110 SMTP_GetEOL(ptr, end, &eol, &eolm);
01111
01112 resp_line_len = eol - ptr;
01113
01114 /* Check for response code */
01115 smtp_current_search = &smtp_resp_search[0];
01116 resp_found = smtp_resp_search_mpse->find(
01117 (const char*)ptr, resp_line_len, SMTP_SearchStrFound);
01118
01119 if (resp_found > 0)
01120 {
01121 switch (smtp_search_info.id)
01122 {
01123 case RESP_220:
01124 /* This is either an initial server response or a STARTTLS response */
01125 if (smtp_ssn->state == STATE_CONNECT)
01126 smtp_ssn->state = STATE_COMMAND;
01127 break;
01128
01129 case RESP_250:
01130 case RESP_221:
01131 case RESP_334:
01132 case RESP_354:
01133 break;
01134
01135 case RESP_235:
01136 // Auth done
01137 *next_state = STATE_COMMAND;
01138 break;
01139
01140 default:
01141 if (smtp_ssn->state != STATE_COMMAND)
01142 {
01143 *next_state = STATE_COMMAND;
01144 }
01145 break;
01146 }
01147
01148 #ifdef DEBUG_MSGS
01149 dash = ptr + smtp_search_info.index + smtp_search_info.length;
01150
01151 /* only add response if not a dash after response code */
01152 if ((dash == eolm) || ((dash < eolm) && (*dash != '-')))
01153 {
01154 DebugFormat(DEBUG_SMTP, "Server sent %s response\n",
01155 smtp_resps[smtp_search_info.id].name);
01156 }
01157 #endif
01158 }
01159 else
01160 {
01161 DebugMessage(DEBUG_SMTP,
01162 "Server response not found - see if it's SSL data\n");
01163
01164 if ((smtp_ssn->session_flags & SMTP_FLAG_CHECK_SSL) &&
01165 (IsSSL(ptr, end - ptr, p->packet_flags)))
01166 {
01167 DebugMessage(DEBUG_SMTP, "Server response is an SSL packet\n");
01168
01169 smtp_ssn->state = STATE_TLS_DATA;
01170
01171 /* Ignore data */
01172 if (config->ignore_tls_data)
01173 {
01174 DebugMessage(DEBUG_SMTP, "Ignoring Server TLS encrypted data\n");
01175 }
01176
01177 return;
01178 }
01179 else if (smtp_ssn->session_flags & SMTP_FLAG_CHECK_SSL)
01180 {
01181 smtp_ssn->session_flags &= ~SMTP_FLAG_CHECK_SSL;
01182 }
01183 }
01184
01185 if ((config->max_response_line_len != 0) &&
01186 (resp_line_len > config->max_response_line_len))
01187 {
01188 DetectionEngine::queue_event(GID_SMTP, SMTP_RESPONSE_OVERFLOW);
01189 }
01190
01191 ptr = eol;
01192 }
01193 }
01194
01195 /*
01196 * Entry point to snort preprocessor for each packet
01197 *
01198 * @param packet standard Packet structure
01199 *
01200 * @return none
01201 */
01202 static void snort_smtp(SMTP_PROTO_CONF* config, Packet* p)
01203 {
01204 int pkt_dir;
01205
01206 /* Attempt to get a previously allocated SMTP block. */
01207
01208 SMTPData* smtp_ssn = get_session_data(p->flow);
01209
01210 if (smtp_ssn == nullptr)
01211 {
01212 /* Check the stream session. If it does not currently
01213 * * have our SMTP data-block attached, create one.
01214 * */
01215 smtp_ssn = SetNewSMTPData(config, p);
01216
01217 if ( !smtp_ssn )
01218 {
01219 /* Could not get/create the session data for this packet. */
01220 return;
01221 }
01222 }
01223
01224 pkt_dir = SMTP_Setup(p, smtp_ssn);
01225 SMTP_ResetAltBuffer(p);
01226
01227 /* reset normalization stuff */
01228 smtp_normalizing = false;
01229 SetDetectLimit(p, 0);
01230
01231 if (pkt_dir == SMTP_PKT_FROM_SERVER)
01232 {
01233 int next_state = 0;
01234
01235 DebugMessage(DEBUG_SMTP, "SMTP server packet\n");
01236
01237 /* Process as a server packet */
01238 SMTP_ProcessServerPacket(config, p, smtp_ssn, &next_state);
01239
01240 if (next_state)
01241 smtp_ssn->state = next_state;
01242 }
01243 else
01244 {
01245 #ifdef DEBUG_MSGS
01246 if (pkt_dir == SMTP_PKT_FROM_CLIENT)
01247 {
01248 DebugMessage(DEBUG_SMTP, "SMTP client packet\n");
01249 }
01250 else
01251 {
01252 DebugMessage(DEBUG_SMTP, "SMTP packet NOT from client or server! "
01253 "Processing as a client packet\n");
01254 }
01255 #endif
01256
01257 /* This packet should be a tls client hello */
01258 if (smtp_ssn->state == STATE_TLS_CLIENT_PEND)
01259 {
01260 if (IsTlsClientHello(p->data, p->data + p->dsize))
01261 {
01262 DebugMessage(DEBUG_SMTP,
01263 "TLS DATA STATE ~~~~~~~~~~~~~~~~~~~~~~~~~\n");
01264
01265 smtp_ssn->state = STATE_TLS_SERVER_PEND;
01266 }
01267 else if (p->packet_flags & PKT_STREAM_ORDER_OK)
01268 {
01269 /* reset state - server may have rejected STARTTLS command */
01270 smtp_ssn->state = STATE_COMMAND;
01271 }
01272 }
01273
01274 if ((smtp_ssn->state == STATE_TLS_DATA)
01275 || (smtp_ssn->state == STATE_TLS_SERVER_PEND))
01276 {
01277 /* if we're ignoring tls data, set a zero length alt buffer */
01278 if (config->ignore_tls_data)
01279 {
01280 Stream::stop_inspection(p->flow, p, SSN_DIR_BOTH, -1, 0);
01281 return;
01282 }
01283 }
01284 else
01285 {
01286 if ( !InspectPacket(p))
01287 {
01288 /* Packet will be rebuilt, so wait for it */
01289 DebugMessage(DEBUG_SMTP, "Client packet will be reassembled\n");
01290 return;
01291 }
01292 else if (!(p->packet_flags & PKT_REBUILT_STREAM))
01293 {
01294 /* If this isn't a reassembled packet and didn't get
01295 * inserted into reassembly buffer, there could be a
01296 * problem. If we miss syn or syn-ack that had window
01297 * scaling this packet might not have gotten inserted
01298 * into reassembly buffer because it fell outside of
01299 * window, because we aren't scaling it */
01300 smtp_ssn->session_flags |= SMTP_FLAG_GOT_NON_REBUILT;
01301 smtp_ssn->state = STATE_UNKNOWN;
01302 }
01303 else if ((smtp_ssn->session_flags & SMTP_FLAG_GOT_NON_REBUILT))
01304 {
01305 /* This is a rebuilt packet. If we got previous packets
01306 * that were not rebuilt, state is going to be messed up
01307 * so set state to unknown. It's likely this was the
01308 * beginning of the conversation so reset state */
01309 DebugMessage(DEBUG_SMTP, "Got non-rebuilt packets before "
01310 "this rebuilt packet\n");
01311
01312 smtp_ssn->state = STATE_UNKNOWN;
01313 smtp_ssn->session_flags &= ~SMTP_FLAG_GOT_NON_REBUILT;
01314 }
01315
01316 SMTP_ProcessClientPacket(config, p, smtp_ssn);
01317 }
01318 }
01319
01320 SMTP_LogFuncs(config, p, smtp_ssn->mime_ssn);
01321 }
01322
01323 /* Callback to return the MIME attachment filenames accumulated */
01324 static int SMTP_GetFilename(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t* type)
01325 {
01326 SMTPData* ssn = get_session_data(flow);
01327
01328 if (ssn == nullptr)
01329 return 0;
01330
01331 ssn->mime_ssn->get_log_state()->get_file_name(buf, len);
01332 *type = EVENT_INFO_SMTP_FILENAME;
01333 return 1;
01334 }
01335
01336 /* Callback to return the email addresses accumulated from the MAIL FROM command */
01337 static int SMTP_GetMailFrom(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t* type)
01338 {
01339 SMTPData* ssn = get_session_data(flow);
01340
01341 if (ssn == nullptr)
01342 return 0;
01343
01344 ssn->mime_ssn->get_log_state()->get_email_id(buf, len, EMAIL_SENDER);
01345 *type = EVENT_INFO_SMTP_MAILFROM;
01346 return 1;
01347 }
01348
01349 /* Callback to return the email addresses accumulated from the RCP TO command */
01350 static int SMTP_GetRcptTo(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t* type)
01351 {
01352 SMTPData* ssn = get_session_data(flow);
01353
01354 if (ssn == nullptr)
01355 return 0;
01356
01357 ssn->mime_ssn->get_log_state()->get_email_id(buf, len, EMAIL_RECIPIENT);
01358 *type = EVENT_INFO_SMTP_RCPTTO;
01359 return 1;
01360 }
01361
01362 /* Callback to return the email headers */
01363 static int SMTP_GetEmailHdrs(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t* type)
01364 {
01365 SMTPData* ssn = get_session_data(flow);
01366
01367 if (ssn == nullptr)
01368 return 0;
01369
01370 ssn->mime_ssn->get_log_state()->get_email_hdrs(buf, len);
01371 *type = EVENT_INFO_SMTP_EMAIL_HDRS;
01372 return 1;
01373 }
01374
01375 static void SMTP_RegXtraDataFuncs(SMTP_PROTO_CONF* config)
01376 {
01377 config->xtra_filename_id = Stream::reg_xtra_data_cb(SMTP_GetFilename);
01378 config->xtra_mfrom_id = Stream::reg_xtra_data_cb(SMTP_GetMailFrom);
01379 config->xtra_rcptto_id = Stream::reg_xtra_data_cb(SMTP_GetRcptTo);
01380 config->xtra_ehdrs_id = Stream::reg_xtra_data_cb(SMTP_GetEmailHdrs);
01381 }
01382
01383 int SmtpMime::handle_header_line(const uint8_t* ptr, const uint8_t* eol,
01384 int max_header_len)
01385 {
01386 int ret;
01387 int header_line_len;
01388 MimeSession* mime_ssn = (MimeSession*)this;
01389 /* get length of header line */
01390 header_line_len = eol - ptr;
01391
01392 if (max_header_len)
01393 DetectionEngine::queue_event(GID_SMTP, SMTP_HEADER_NAME_OVERFLOW);
01394
01395 if ((config->max_header_line_len != 0) &&
01396 (header_line_len > config->max_header_line_len))
01397 {
01398 DetectionEngine::queue_event(GID_SMTP, SMTP_DATA_HDR_OVERFLOW);
01399
01400 }
01401
01402 /* XXX Does VRT want data headers normalized?
01403 * currently the code does not normalize headers */
01404 if (smtp_normalizing)
01405 {
01406 ret = SMTP_CopyToAltBuffer(nullptr, ptr, eol - ptr);
01407 if (ret == -1)
01408 return (-1);
01409 }
01410
01411 if (config->log_config.log_email_hdrs)
01412 {
01413 if (mime_ssn->get_data_state() == STATE_DATA_HEADER)
01414 {
01415 mime_ssn->get_log_state()->log_email_hdrs(ptr, eol - ptr);
01416 }
01417 }
01418
01419 return 0;
01420 }
01421
01422 int SmtpMime::normalize_data(const uint8_t* ptr, const uint8_t* data_end)
01423 {
01424 /* if we're ignoring data and not already normalizing, copy everything
01425 * up to here into alt buffer so detection engine doesn't have
01426 * to look at the data; otherwise, if we're normalizing and not
01427 * ignoring data, copy all of the data into the alt buffer */
01428 /*if (config->decode_conf.ignore_data && !smtp_normalizing)
01429 {
01430 return SMTP_CopyToAltBuffer(nullptr, p->data, ptr - p->data);
01431 }
01432 else */
01433 if (!config->decode_conf.is_ignore_data() && smtp_normalizing)
01434 {
01435 return SMTP_CopyToAltBuffer(nullptr, ptr, data_end - ptr);
01436 }
01437
01438 return 0;
01439 }
01440
01441 void SmtpMime::decode_alert()
01442 {
01443 switch ( decode_state->get_decode_type() )
01444 {
01445 case DECODE_B64:
01446 DetectionEngine::queue_event(GID_SMTP, SMTP_B64_DECODING_FAILED);
01447 break;
01448 case DECODE_QP:
01449 DetectionEngine::queue_event(GID_SMTP, SMTP_QP_DECODING_FAILED);
01450 break;
01451 case DECODE_UU:
01452 DetectionEngine::queue_event(GID_SMTP, SMTP_UU_DECODING_FAILED);
01453 break;
01454
01455 default:
01456 break;
01457 }
01458 }
01459
01460 void SmtpMime::reset_state(Flow* ssn)
01461 {
01462 SMTP_ResetState(ssn);
01463 }
01464
01465
01466 bool SmtpMime::is_end_of_data(Flow* session)
01467 {
01468 return smtp_is_data_end(session);
01469 }
01470
01471 //-------------------------------------------------------------------------
01472 // class stuff
01473 //-------------------------------------------------------------------------
01474
01475 class Smtp : public Inspector
01476 {
01477 public:
01478 Smtp(SMTP_PROTO_CONF*);
01479 ~Smtp() override;
01480
01481 bool configure(SnortConfig*) override;
01482 void show(SnortConfig*) override;
01483 void eval(Packet*) override;
01484 bool get_buf(InspectionBuffer::Type, Packet*, InspectionBuffer&) override;
01485 void clear(Packet*) override;
01486
01487 StreamSplitter* get_splitter(bool c2s) override
01488 { return new SmtpSplitter(c2s, config->max_auth_command_line_len); }
01489
01490 void ProcessSmtpCmdsList(const SmtpCmd*);
01491
01492 private:
01493 SMTP_PROTO_CONF* config;
01494 };
01495
01496 Smtp::Smtp(SMTP_PROTO_CONF* pc)
01497 {
01498 config = pc;
01499
01500 SMTP_InitCmds(config);
01501 }
01502
01503 Smtp::~Smtp()
01504 {
01505 SMTP_CommandSearchTerm(config);
01506 SMTP_TermCmds(config);
01507
01508 delete config;
01509 }
01510
01511 bool Smtp::configure(SnortConfig*)
01512 {
01513 SMTP_RegXtraDataFuncs(config);
01514
01515 config->decode_conf.sync_all_depths();
01516
01517 if (config->decode_conf.get_file_depth() > -1)
01518 config->log_config.log_filename = 1;
01519
01520 SMTP_ResponseSearchInit();
01521 SMTP_CommandSearchInit(config);
01522 return true;
01523 }
01524
01525 void Smtp::show(SnortConfig*)
01526 {
01527 SMTP_PrintConfig(config);
01528 }
01529
01530 void Smtp::eval(Packet* p)
01531 {
01532 Profile profile(smtpPerfStats);
01533
01534 // precondition - what we registered for
01535 assert(p->has_tcp_data());
01536 assert(p->flow);
01537
01538 new_invoked_inspector(27, p, 0);
01539
01540 ++smtpstats.packets;
01541
01542 snort_smtp(config, p);
01543 }
01544
01545 bool Smtp::get_buf(
01546 InspectionBuffer::Type ibt, Packet* p, InspectionBuffer& b)
01547 {
01548 if ( ibt != InspectionBuffer::IBT_ALT )
01549 return false;
01550
01551 b.data = SMTP_GetAltBuffer(p, b.len);
01552
01553 return (b.data != nullptr);
01554 }
01555
01556 void Smtp::clear(Packet* p)
01557 {
01558 SMTP_ResetAltBuffer(p);
01559 }
01560
01561 void Smtp::ProcessSmtpCmdsList(const SmtpCmd* sc)
01562 {
01563 const char* cmd = sc->name.c_str();
01564 int id;
01565 SMTPCmdTypeEnum type;
01566
01567 if ( sc->flags & PCMD_AUTH )
01568 type = SMTP_CMD_TYPE_AUTH;
01569
01570 else if ( sc->flags & PCMD_BDATA )
01571 type = SMTP_CMD_TYPE_BDATA;
01572
01573 else if ( sc->flags & PCMD_DATA )
01574 type = SMTP_CMD_TYPE_DATA;
01575
01576 else
01577 type = SMTP_CMD_TYPE_NORMAL;
01578
01579 id = GetCmdId(config, cmd, type);
01580
01581 if ( sc->flags & PCMD_INVALID )
01582 config->cmd_config[id].alert = true;
01583
01584 else if ( sc->flags & PCMD_NORM )
01585 config->cmd_config[id].normalize = true;
01586
01587 else
01588 config->cmd_config[id].alert = false;
01589
01590 if ( sc->flags & PCMD_ALT )
01591 config->cmd_config[id].max_line_len = sc->number;
01592 }
01593
01594 //-------------------------------------------------------------------------
01595 // api stuff
01596 //-------------------------------------------------------------------------
01597
01598 static Module* mod_ctor()
01599 { return new SmtpModule; }
01600
01601 static void mod_dtor(Module* m)
01602 { delete m; }
01603
01604 static void smtp_init()
01605 {
01606 SmtpFlowData::init();
01607 }
01608
01609 static void smtp_term()
01610 {
01611 SMTP_SearchFree();
01612 }
01613
01614 static Inspector* smtp_ctor(Module* m)
01615 {
01616 SmtpModule* mod = (SmtpModule*)m;
01617 SMTP_PROTO_CONF* conf = mod->get_data();
01618 Smtp* smtp = new Smtp(conf);
01619
01620 unsigned i = 0;
01621 const SmtpCmd* cmd;
01622
01623 while ( (cmd = mod->get_cmd(i++)) )
01624 smtp->ProcessSmtpCmdsList(cmd);
01625
01626 return smtp;
01627 }
01628
01629 static void smtp_dtor(Inspector* p)
01630 {
01631 delete p;
01632 }
01633
01634 const InspectApi smtp_api =
01635 {
01636 {
01637 PT_INSPECTOR,
01638 sizeof(InspectApi),
01639 INSAPI_VERSION,
01640 0,
01641 API_RESERVED,
01642 API_OPTIONS,
01643 SMTP_NAME,
01644 SMTP_HELP,
01645 mod_ctor,
01646 mod_dtor
01647 },
01648 IT_SERVICE,
01649 (uint16_t)PktType::PDU,
01650 nullptr, // buffers
01651 "smtp",
01652 smtp_init,
01653 smtp_term,
01654 nullptr, // tinit
01655 nullptr, // tterm
01656 smtp_ctor,
01657 smtp_dtor,
01658 nullptr, // ssn
01659 nullptr // reset
01660 };
01661
01662 #undef BUILDING_SO // FIXIT-L can't be linked dynamically yet
01663
01664 #ifdef BUILDING_SO
01665 SO_PUBLIC const BaseApi* snort_plugins[] =
01666 {
01667 &smtp_api.base,
01668 nullptr
01669 };
01670 #else
01671 const BaseApi* sin_smtp = &smtp_api.base;
01672 #endif
01673
END OF CODE