1 //--------------------------------------------------------------------------
2 // Copyright (C) 2015-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation.  You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 //--------------------------------------------------------------------------
18 
19 // pop.cc author Bhagyashree Bantwal < bbantwal@cisco.com>
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "pop.h"
26 
27 #include "detection/detection_engine.h"
28 #include "log/messages.h"
29 #include "profiler/profiler.h"
30 #include "protocols/packet.h"
31 #include "protocols/ssl.h"
32 #include "pub_sub/opportunistic_tls_event.h"
33 #include "search_engines/search_tool.h"
34 #include "stream/stream.h"
35 #include "utils/util_cstring.h"
36 
37 #include "pop_module.h"
38 #include "pop_paf.h"
39 
40 using namespace snort;
41 
42 THREAD_LOCAL ProfileStats popPerfStats;
43 THREAD_LOCAL PopStats popstats;
44 
45 POPToken pop_known_cmds[] =
46 {
47     { "APOP",          4, CMD_APOP },
48     { "AUTH",          4, CMD_AUTH },
49     { "CAPA",          4, CMD_CAPA },
50     { "DELE",          4, CMD_DELE },
51     { "LIST",          4, CMD_LIST },
52     { "NOOP",          4, CMD_NOOP },
53     { "PASS",          4, CMD_PASS },
54     { "QUIT",          4, CMD_QUIT },
55     { "RETR",          4, CMD_RETR },
56     { "RSET",          4, CMD_RSET },
57     { "STAT",          4, CMD_STAT },
58     { "STLS",          4, CMD_STLS },
59     { "TOP",           3, CMD_TOP },
60     { "UIDL",          4, CMD_UIDL },
61     { "USER",          4, CMD_USER },
62     { nullptr,            0, 0 }
63 };
64 
65 POPToken pop_resps[] =
66 {
67     { "+OK",   3,  RESP_OK },   /* SUCCESS */
68     { "-ERR",  4,  RESP_ERR },  /* FAILURE */
69     { nullptr,   0,  0 }
70 };
71 
72 SearchTool* pop_resp_search_mpse = nullptr;
73 SearchTool* pop_cmd_search_mpse = nullptr;
74 
75 POPSearch pop_resp_search[RESP_LAST];
76 POPSearch pop_cmd_search[CMD_LAST];
77 
78 static THREAD_LOCAL const POPSearch* pop_current_search = nullptr;
79 static THREAD_LOCAL POPSearchInfo pop_search_info;
80 
81 const PegInfo pop_peg_names[] =
82 {
83     { CountType::SUM, "packets", "total packets processed" },
84     { CountType::SUM, "total_bytes", "total number of bytes processed" },
85     { CountType::SUM, "sessions", "total pop sessions" },
86     { CountType::NOW, "concurrent_sessions", "total concurrent pop sessions" },
87     { CountType::MAX, "max_concurrent_sessions", "maximum concurrent pop sessions" },
88     { CountType::SUM, "start_tls", "total STARTTLS events generated" },
89     { CountType::SUM, "ssl_search_abandoned", "total SSL search abandoned" },
90     { CountType::SUM, "ssl_srch_abandoned_early", "total SSL search abandoned too soon" },
91     { CountType::SUM, "b64_attachments", "total base64 attachments decoded" },
92     { CountType::SUM, "b64_decoded_bytes", "total base64 decoded bytes" },
93     { CountType::SUM, "qp_attachments", "total quoted-printable attachments decoded" },
94     { CountType::SUM, "qp_decoded_bytes", "total quoted-printable decoded bytes" },
95     { CountType::SUM, "uu_attachments", "total uu attachments decoded" },
96     { CountType::SUM, "uu_decoded_bytes", "total uu decoded bytes" },
97     { CountType::SUM, "non_encoded_attachments", "total non-encoded attachments extracted" },
98     { CountType::SUM, "non_encoded_bytes", "total non-encoded extracted bytes" },
99 
100     { CountType::END, nullptr, nullptr }
101 };
102 
103 
104 static void snort_pop(POP_PROTO_CONF* GlobalConf, Packet* p);
105 static void POP_ResetState(Flow*);
106 
PopFlowData()107 PopFlowData::PopFlowData() : FlowData(inspector_id)
108 {
109     memset(&session, 0, sizeof(session));
110     popstats.concurrent_sessions++;
111     if(popstats.max_concurrent_sessions < popstats.concurrent_sessions)
112         popstats.max_concurrent_sessions = popstats.concurrent_sessions;
113 }
114 
~PopFlowData()115 PopFlowData::~PopFlowData()
116 {
117     if (session.mime_ssn)
118         delete(session.mime_ssn);
119 
120     assert(popstats.concurrent_sessions > 0);
121     popstats.concurrent_sessions--;
122 }
123 
124 unsigned PopFlowData::inspector_id = 0;
get_session_data(Flow * flow)125 static POPData* get_session_data(Flow* flow)
126 {
127     PopFlowData* fd = (PopFlowData*)flow->get_flow_data(PopFlowData::inspector_id);
128     return fd ? &fd->session : nullptr;
129 }
130 
SetNewPOPData(POP_PROTO_CONF * config,Packet * p)131 static POPData* SetNewPOPData(POP_PROTO_CONF* config, Packet* p)
132 {
133     POPData* pop_ssn;
134     PopFlowData* fd = new PopFlowData;
135 
136     p->flow->set_flow_data(fd);
137     pop_ssn = &fd->session;
138 
139     popstats.sessions++;
140     pop_ssn->mime_ssn = new PopMime(p, &(config->decode_conf), &(config->log_config));
141     pop_ssn->mime_ssn->set_mime_stats(&(popstats.mime_stats));
142 
143     if (p->packet_flags & SSNFLAG_MIDSTREAM)
144     {
145         pop_ssn->state = STATE_UNKNOWN;
146     }
147 
148     return pop_ssn;
149 }
150 
POP_SearchInit()151 static void POP_SearchInit()
152 {
153     const POPToken* tmp;
154     if ( pop_cmd_search_mpse )
155         return;
156     pop_cmd_search_mpse = new SearchTool;
157 
158     for (tmp = &pop_known_cmds[0]; tmp->name != nullptr; tmp++)
159     {
160         pop_cmd_search[tmp->search_id].name = tmp->name;
161         pop_cmd_search[tmp->search_id].name_len = tmp->name_len;
162         pop_cmd_search_mpse->add(tmp->name, tmp->name_len, tmp->search_id);
163     }
164     pop_cmd_search_mpse->prep();
165     pop_resp_search_mpse = new SearchTool;
166 
167     for (tmp = &pop_resps[0]; tmp->name != nullptr; tmp++)
168     {
169         pop_resp_search[tmp->search_id].name = tmp->name;
170         pop_resp_search[tmp->search_id].name_len = tmp->name_len;
171         pop_resp_search_mpse->add(tmp->name, tmp->name_len, tmp->search_id);
172     }
173     pop_resp_search_mpse->prep();
174 }
175 
POP_SearchFree()176 static void POP_SearchFree()
177 {
178     if (pop_cmd_search_mpse != nullptr)
179         delete pop_cmd_search_mpse;
180 
181     if (pop_resp_search_mpse != nullptr)
182         delete pop_resp_search_mpse;
183 }
184 
POP_ResetState(Flow * ssn)185 static void POP_ResetState(Flow* ssn)
186 {
187     POPData* pop_ssn = get_session_data(ssn);
188     pop_ssn->state = STATE_COMMAND;
189     pop_ssn->prev_response = 0;
190     pop_ssn->state_flags = 0;
191 }
192 
POP_GetEOL(const uint8_t * ptr,const uint8_t * end,const uint8_t ** eol,const uint8_t ** eolm)193 static void POP_GetEOL(const uint8_t* ptr, const uint8_t* end,
194     const uint8_t** eol, const uint8_t** eolm)
195 {
196     assert(ptr and end and eol and eolm);
197 
198     const uint8_t* tmp_eol;
199     const uint8_t* tmp_eolm;
200 
201     tmp_eol = (const uint8_t*)memchr(ptr, '\n', end - ptr);
202     if (tmp_eol == nullptr)
203     {
204         tmp_eol = end;
205         tmp_eolm = end;
206     }
207     else
208     {
209         /* end of line marker (eolm) should point to marker and
210          *          * end of line (eol) should point to end of marker */
211         if ((tmp_eol > ptr) && (*(tmp_eol - 1) == '\r'))
212         {
213             tmp_eolm = tmp_eol - 1;
214         }
215         else
216         {
217             tmp_eolm = tmp_eol;
218         }
219 
220         /* move past newline */
221         tmp_eol++;
222     }
223 
224     *eol = tmp_eol;
225     *eolm = tmp_eolm;
226 }
227 
InspectPacket(Packet * p)228 static inline int InspectPacket(Packet* p)
229 {
230     return p->has_paf_payload();
231 }
232 
POP_Setup(Packet * p,POPData * ssn)233 static int POP_Setup(Packet* p, POPData* ssn)
234 {
235     int pkt_dir;
236 
237     /* Get the direction of the packet. */
238     if ( p->is_from_server() )
239         pkt_dir = POP_PKT_FROM_SERVER;
240     else
241         pkt_dir = POP_PKT_FROM_CLIENT;
242 
243     if (!(ssn->session_flags & POP_FLAG_CHECK_SSL))
244         ssn->session_flags |= POP_FLAG_CHECK_SSL;
245     /* Check to see if there is a reassembly gap.  If so, we won't know
246      *      * what state we're in when we get the _next_ reassembled packet */
247     if ((pkt_dir != POP_PKT_FROM_SERVER) &&
248         (p->packet_flags & PKT_REBUILT_STREAM))
249     {
250         int missing_in_rebuilt =
251             Stream::missing_in_reassembled(p->flow, SSN_DIR_FROM_CLIENT);
252 
253         if (ssn->session_flags & POP_FLAG_NEXT_STATE_UNKNOWN)
254         {
255             ssn->state = STATE_UNKNOWN;
256             ssn->session_flags &= ~POP_FLAG_NEXT_STATE_UNKNOWN;
257         }
258 
259         if (missing_in_rebuilt == SSN_MISSING_BEFORE)
260         {
261             ssn->state = STATE_UNKNOWN;
262         }
263     }
264 
265     return pkt_dir;
266 }
267 
POP_SearchStrFound(void * id,void *,int index,void *,void *)268 static int POP_SearchStrFound(void* id, void* , int index, void* , void* )
269 {
270     int search_id = (int)(uintptr_t)id;
271 
272     pop_search_info.id = search_id;
273     pop_search_info.length = pop_current_search[search_id].name_len;
274     pop_search_info.index = index - pop_search_info.length;
275 
276     /* Returning non-zero stops search, which is okay since we only look for one at a time */
277     return 1;
278 }
279 
280 /*
281  * Handle COMMAND state
282  *
283  * @param   p       standard Packet structure
284  * @param   ptr     pointer into p->data buffer to start looking at data
285  * @param   end     points to end of p->data buffer
286  *
287  * @return          pointer into p->data where we stopped looking at data
288  *                  will be end of line or end of packet
289  */
POP_HandleCommand(Packet * p,POPData * pop_ssn,const uint8_t * ptr,const uint8_t * end)290 static const uint8_t* POP_HandleCommand(Packet* p, POPData* pop_ssn, const uint8_t* ptr, const
291     uint8_t* end)
292 {
293     const uint8_t* eol;   /* end of line */
294     const uint8_t* eolm;  /* end of line marker */
295     int cmd_found;
296 
297     /* get end of line and end of line marker */
298     POP_GetEOL(ptr, end, &eol, &eolm);
299 
300     // FIXIT-M If the end of line marker coincides with the end of data we
301     // can't be sure that we got a command and not a substring which we
302     // could tell through inspection of the next packet. Maybe a command
303     // pending state where the first char in the next packet is checked for
304     // a space and end of line marker
305 
306     /* do not confine since there could be space chars before command */
307     pop_current_search = &pop_cmd_search[0];
308     cmd_found = pop_cmd_search_mpse->find(
309         (const char*)ptr, eolm - ptr, POP_SearchStrFound);
310     /* see if we actually found a command and not a substring */
311     if (cmd_found > 0)
312     {
313         const uint8_t* tmp = ptr;
314         const uint8_t* cmd_start = ptr + pop_search_info.index;
315         const uint8_t* cmd_end = cmd_start + pop_search_info.length;
316 
317         /* move past spaces up until start of command */
318         while ((tmp < cmd_start) && isspace((int)*tmp))
319             tmp++;
320 
321         /* if not all spaces before command, we found a
322          * substring */
323         if (tmp != cmd_start)
324             cmd_found = 0;
325 
326         /* if we're before the end of line marker and the next
327          * character is not whitespace, we found a substring */
328         if ((cmd_end < eolm) && !isspace((int)*cmd_end))
329             cmd_found = 0;
330 
331         /* there is a chance that end of command coincides with the end of data
332          * in which case, it could be a substring, but for now, we will treat it as found */
333     }
334 
335     /* if command not found, alert and move on */
336     if (!cmd_found)
337     {
338         /* check for encrypted */
339         if (pop_ssn->state == STATE_UNKNOWN and
340             pop_ssn->session_flags & POP_FLAG_CHECK_SSL and
341             IsSSL(ptr, end - ptr, p->packet_flags))
342         {
343             pop_ssn->state = STATE_TLS_DATA;
344 
345             /* Ignore data */
346             return end;
347         }
348         else
349         {
350             if (pop_ssn->state == STATE_UNKNOWN)
351             {
352                 /* don't check for ssl again in this packet */
353                 if (pop_ssn->session_flags & POP_FLAG_CHECK_SSL)
354                     pop_ssn->session_flags &= ~POP_FLAG_CHECK_SSL;
355 
356                 pop_ssn->state = STATE_DATA;
357                 DetectionEngine::queue_event(GID_POP, POP_UNKNOWN_CMD);
358                 return ptr;
359             }
360             DetectionEngine::queue_event(GID_POP, POP_UNKNOWN_CMD);
361             return eol;
362         }
363     }
364     else if (pop_search_info.id == CMD_TOP)
365     {
366         pop_ssn->state = STATE_DATA;
367     }
368     else
369     {
370         if (pop_ssn->state == STATE_UNKNOWN)
371             pop_ssn->state = STATE_COMMAND;
372     }
373 
374     if (pop_search_info.id == CMD_STLS)
375     {
376         if (eol == end)
377             pop_ssn->state = STATE_TLS_CLIENT_PEND;
378     }
379 
380     return eol;
381 }
382 
383 /*
384  * Process client packet
385  *
386  * @param   packet  standard Packet structure
387  *
388  * @return  none
389  */
POP_ProcessClientPacket(Packet * p,POPData * pop_ssn)390 static void POP_ProcessClientPacket(Packet* p, POPData* pop_ssn)
391 {
392     const uint8_t* ptr = p->data;
393     const uint8_t* end = p->data + p->dsize;
394 
395     POP_HandleCommand(p, pop_ssn, ptr, end);
396 }
397 
398 /*
399  * Process server packet
400  *
401  * @param   packet  standard Packet structure
402  *
403  */
POP_ProcessServerPacket(Packet * p,POPData * pop_ssn)404 static void POP_ProcessServerPacket(Packet* p, POPData* pop_ssn)
405 {
406     int resp_found;
407     const uint8_t* ptr;
408     const uint8_t* end;
409     const uint8_t* eolm;
410     const uint8_t* eol;
411     int resp_line_len;
412     const char* tmp = nullptr;
413 
414     ptr = p->data;
415     end = p->data + p->dsize;
416 
417     while (ptr < end)
418     {
419         if (pop_ssn->state == STATE_DATA)
420         {
421             //ptr = POP_HandleData(p, ptr, end);
422             FilePosition position = get_file_position(p);
423             int len = end - ptr;
424             ptr = pop_ssn->mime_ssn->process_mime_data(p, ptr, len, false, position);
425             continue;
426         }
427         POP_GetEOL(ptr, end, &eol, &eolm);
428 
429         resp_line_len = eol - ptr;
430 
431         /* Check for response code */
432         pop_current_search = &pop_resp_search[0];
433         resp_found = pop_resp_search_mpse->find(
434             (const char*)ptr, resp_line_len, POP_SearchStrFound);
435 
436         if (resp_found > 0)
437         {
438             const uint8_t* cmd_start = ptr + pop_search_info.index;
439             switch (pop_search_info.id)
440             {
441             case RESP_OK:
442                 tmp = SnortStrcasestr((const char*)cmd_start, (eol - cmd_start), "octets");
443                 if (tmp != nullptr)
444                 {
445                     if (!(pop_ssn->session_flags & POP_FLAG_ABANDON_EVT)
446                         and !p->flow->flags.data_decrypted)
447                     {
448                         pop_ssn->session_flags |= POP_FLAG_ABANDON_EVT;
449                         DataBus::publish(SSL_SEARCH_ABANDONED, p);
450                         popstats.ssl_search_abandoned++;
451                     }
452 
453                     pop_ssn->state = STATE_DATA;
454                 }
455                 else if (pop_ssn->state == STATE_TLS_CLIENT_PEND)
456                 {
457                     if ((pop_ssn->session_flags & POP_FLAG_ABANDON_EVT)
458                         and !p->flow->flags.data_decrypted)
459                     {
460                         popstats.ssl_srch_abandoned_early++;
461                     }
462 
463                     OpportunisticTlsEvent event(p, p->flow->service);
464                     DataBus::publish(OPPORTUNISTIC_TLS_EVENT, event, p->flow);
465                     popstats.start_tls++;
466                     pop_ssn->state = STATE_DECRYPTION_REQ;
467                 }
468                 else
469                 {
470                     pop_ssn->prev_response = RESP_OK;
471                     pop_ssn->state = STATE_UNKNOWN;
472                 }
473                 break;
474 
475             default:
476                 break;
477             }
478         }
479         else
480         {
481             if ((pop_ssn->session_flags & POP_FLAG_CHECK_SSL) &&
482                 (IsSSL(ptr, end - ptr, p->packet_flags)))
483             {
484                 pop_ssn->state = STATE_TLS_DATA;
485                 return;
486             }
487             else if (pop_ssn->session_flags & POP_FLAG_CHECK_SSL)
488             {
489                 pop_ssn->session_flags &= ~POP_FLAG_CHECK_SSL;
490             }
491             if (pop_ssn->prev_response == RESP_OK)
492             {
493                 {
494                     pop_ssn->state = STATE_DATA;
495                     pop_ssn->prev_response = 0;
496                     continue;
497                 }
498             }
499             else if (*ptr == '+')
500             {
501                 DetectionEngine::queue_event(GID_POP, POP_UNKNOWN_RESP);
502             }
503         }
504 
505         ptr = eol;
506     }
507 }
508 
509 // Analyzes POP packets for anomalies/exploits.
510 
snort_pop(POP_PROTO_CONF * config,Packet * p)511 static void snort_pop(POP_PROTO_CONF* config, Packet* p)
512 {
513     /* Attempt to get a previously allocated POP block. */
514     POPData* pop_ssn = get_session_data(p->flow);
515 
516     if (pop_ssn == nullptr)
517     {
518         /* Check the stream session. If it does not currently
519          * have our POP data-block attached, create one.
520          */
521         pop_ssn = SetNewPOPData(config, p);
522 
523         if ( !pop_ssn )
524         {
525             /* Could not get/create the session data for this packet. */
526             return;
527         }
528     }
529 
530     popstats.total_bytes += p->dsize;
531     int pkt_dir = POP_Setup(p, pop_ssn);
532 
533     if (pkt_dir == POP_PKT_FROM_CLIENT)
534     {
535         /* This packet should be a tls client hello */
536         if ((pop_ssn->state == STATE_TLS_CLIENT_PEND) || (pop_ssn->state == STATE_DECRYPTION_REQ))
537         {
538             if (IsTlsClientHello(p->data, p->data + p->dsize))
539             {
540                 pop_ssn->state = STATE_TLS_SERVER_PEND;
541                 return;
542             }
543             else
544             {
545                 /* reset state - server may have rejected STARTTLS command */
546                 pop_ssn->state = STATE_UNKNOWN;
547             }
548         }
549         if ((pop_ssn->state == STATE_TLS_DATA)
550             || (pop_ssn->state == STATE_TLS_SERVER_PEND))
551         {
552             return;
553         }
554         POP_ProcessClientPacket(p, pop_ssn);
555     }
556     else
557     {
558         if (pop_ssn->state == STATE_TLS_SERVER_PEND)
559         {
560             if (IsTlsServerHello(p->data, p->data + p->dsize))
561             {
562                 pop_ssn->state = STATE_TLS_DATA;
563             }
564             else if ( !p->test_session_flags(SSNFLAG_MIDSTREAM)
565                 && !Stream::missed_packets(p->flow, SSN_DIR_BOTH))
566             {
567                 /* revert back to command state - assume server didn't accept STARTTLS */
568                 pop_ssn->state = STATE_UNKNOWN;
569             }
570             else
571                 return;
572         }
573 
574         if (pop_ssn->state == STATE_TLS_DATA)
575         {
576             return;
577         }
578         if ( !InspectPacket(p))
579         {
580             /* Packet will be rebuilt, so wait for it */
581             return;
582         }
583         else if (!(p->packet_flags & PKT_REBUILT_STREAM))
584         {
585             /* If this isn't a reassembled packet and didn't get
586              * inserted into reassembly buffer, there could be a
587              * problem.  If we miss syn or syn-ack that had window
588              * scaling this packet might not have gotten inserted
589              * into reassembly buffer because it fell outside of
590              * window, because we aren't scaling it */
591             pop_ssn->session_flags |= POP_FLAG_GOT_NON_REBUILT;
592             pop_ssn->state = STATE_UNKNOWN;
593         }
594         else if (pop_ssn->session_flags & POP_FLAG_GOT_NON_REBUILT)
595         {
596             /* This is a rebuilt packet.  If we got previous packets
597              * that were not rebuilt, state is going to be messed up
598              * so set state to unknown. It's likely this was the
599              * beginning of the conversation so reset state */
600             pop_ssn->state = STATE_UNKNOWN;
601             pop_ssn->session_flags &= ~POP_FLAG_GOT_NON_REBUILT;
602         }
603         /* Process as a server packet */
604         POP_ProcessServerPacket(p, pop_ssn);
605     }
606 }
607 
decode_alert()608 void PopMime::decode_alert()
609 {
610     switch ( decode_state->get_decode_type() )
611     {
612     case DECODE_B64:
613         DetectionEngine::queue_event(GID_POP, POP_B64_DECODING_FAILED);
614         break;
615     case DECODE_QP:
616         DetectionEngine::queue_event(GID_POP, POP_QP_DECODING_FAILED);
617         break;
618     case DECODE_UU:
619         DetectionEngine::queue_event(GID_POP, POP_UU_DECODING_FAILED);
620         break;
621 
622     default:
623         break;
624     }
625 }
626 
decompress_alert()627 void PopMime::decompress_alert()
628 {
629     DetectionEngine::queue_event(GID_POP, POP_FILE_DECOMP_FAILED);
630 }
631 
reset_state(Flow * ssn)632 void PopMime::reset_state(Flow* ssn)
633 {
634     POP_ResetState(ssn);
635 }
636 
637 
is_end_of_data(Flow * session)638 bool PopMime::is_end_of_data(Flow* session)
639 {
640     return pop_is_data_end(session);
641 }
642 
643 //-------------------------------------------------------------------------
644 // class stuff
645 //-------------------------------------------------------------------------
646 
647 class Pop : public Inspector
648 {
649 public:
650     Pop(POP_PROTO_CONF*);
651     ~Pop() override;
652 
653     bool configure(SnortConfig*) override;
654     void show(const SnortConfig*) const override;
655     void eval(Packet*) override;
656 
get_splitter(bool c2s)657     StreamSplitter* get_splitter(bool c2s) override
658     { return new PopSplitter(c2s); }
659 
can_carve_files() const660     bool can_carve_files() const override
661     { return true; }
662 
can_start_tls() const663     bool can_start_tls() const override
664     { return true; }
665 
666     bool get_buf(InspectionBuffer::Type, Packet*, InspectionBuffer&) override;
667     bool get_fp_buf(snort::InspectionBuffer::Type ibt, snort::Packet* p,
668         snort::InspectionBuffer& b) override;
669 
670 private:
671     POP_PROTO_CONF* config;
672 };
673 
Pop(POP_PROTO_CONF * pc)674 Pop::Pop(POP_PROTO_CONF* pc)
675 {
676     config = pc;
677 }
678 
~Pop()679 Pop::~Pop()
680 {
681     if ( config )
682         delete config;
683 }
684 
configure(SnortConfig *)685 bool Pop::configure(SnortConfig* )
686 {
687     config->decode_conf.sync_all_depths();
688 
689     if (config->decode_conf.get_file_depth() > -1)
690         config->log_config.log_filename = true;
691 
692     POP_SearchInit();
693     return true;
694 }
695 
show(const SnortConfig *) const696 void Pop::show(const SnortConfig*) const
697 {
698     if ( config )
699         config->decode_conf.show();
700 }
701 
eval(Packet * p)702 void Pop::eval(Packet* p)
703 {
704     Profile profile(popPerfStats);
705 
706     // precondition - what we registered for
707     assert(p->has_tcp_data());
708     assert(p->flow);
709 
710     ++popstats.packets;
711 
712     snort_pop(config, p);
713 }
714 
get_buf(InspectionBuffer::Type ibt,Packet * p,InspectionBuffer & b)715 bool Pop::get_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBuffer& b)
716 {
717     // Fast pattern buffers only supplied at specific times
718     switch (ibt)
719     {
720         case InspectionBuffer::IBT_VBA:
721         {
722             POPData* pop_ssn = get_session_data(p->flow);
723 
724             if (!pop_ssn)
725                 return false;
726 
727             const BufferData& vba_buf = pop_ssn->mime_ssn->get_vba_inspect_buf();
728 
729             if (vba_buf.data_ptr() && vba_buf.length())
730             {
731                 b.data = vba_buf.data_ptr();
732                 b.len = vba_buf.length();
733                 return true;
734             }
735             else
736                 return false;
737         }
738 
739         default:
740             break;
741     }
742     return false;
743 }
744 
get_fp_buf(InspectionBuffer::Type ibt,Packet * p,InspectionBuffer & b)745 bool Pop::get_fp_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBuffer& b)
746 {
747     return get_buf(ibt, p, b);
748 }
749 
750 //-------------------------------------------------------------------------
751 // api stuff
752 //-------------------------------------------------------------------------
753 
mod_ctor()754 static Module* mod_ctor()
755 { return new PopModule; }
756 
mod_dtor(Module * m)757 static void mod_dtor(Module* m)
758 { delete m; }
759 
pop_init()760 static void pop_init()
761 {
762     PopFlowData::init();
763 }
764 
pop_term()765 static void pop_term()
766 {
767     POP_SearchFree();
768 }
769 
pop_ctor(Module * m)770 static Inspector* pop_ctor(Module* m)
771 {
772     PopModule* mod = (PopModule*)m;
773     return new Pop(mod->get_data());
774 }
775 
pop_dtor(Inspector * p)776 static void pop_dtor(Inspector* p)
777 {
778     delete p;
779 }
780 
781 const InspectApi pop_api =
782 {
783     {
784         PT_INSPECTOR,
785         sizeof(InspectApi),
786         INSAPI_VERSION,
787         0,
788         API_RESERVED,
789         API_OPTIONS,
790         POP_NAME,
791         POP_HELP,
792         mod_ctor,
793         mod_dtor
794     },
795     IT_SERVICE,
796     PROTO_BIT__PDU,
797     nullptr, // buffers
798     "pop3",
799     pop_init,
800     pop_term, // pterm
801     nullptr, // tinit
802     nullptr, // tterm
803     pop_ctor,
804     pop_dtor,
805     nullptr, // ssn
806     nullptr  // reset
807 };
808 
809 #ifdef BUILDING_SO
810 SO_PUBLIC const BaseApi* snort_plugins[] =
811 {
812     &pop_api.base,
813     nullptr
814 };
815 #else
816 const BaseApi* sin_pop = &pop_api.base;
817 #endif
818 
819