"Fossies" - the Fresh Open Source Software Archive 
Member "snort-2.9.17/src/dynamic-preprocessors/pop/pop_paf.c" (16 Oct 2020, 14593 Bytes) of package /linux/misc/snort-2.9.17.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "pop_paf.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
2.9.16.1_vs_2.9.17.
1 /****************************************************************************
2 * Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved.
3 * Copyright (C) 2011-2013 Sourcefire, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License Version 2 as
7 * published by the Free Software Foundation. You may not use, modify or
8 * distribute this program under any other version of the GNU General
9 * Public License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 ****************************************************************************/
21
22
23 #include <sys/types.h>
24 #include "sf_types.h"
25 #include "pop_paf.h"
26 #include "spp_pop.h"
27 #include "sf_dynamic_preprocessor.h"
28 #include "stream_api.h"
29 #include "snort_pop.h"
30 #include "pop_config.h"
31 #include "file_api.h"
32 #include "pop_util.h"
33
34 static uint8_t pop_paf_id = 0;
35
36 // global variables defined in snort_pop.c
37 extern const POPToken pop_known_cmds[];
38
39 /* Structure used to record expected server termination sequence */
40 typedef enum _PopExpectedResp
41 {
42 POP_PAF_SINGLE_LINE_STATE, /* server response will end with \r\n */
43 POP_PAF_MULTI_LINE_STATE, /* server response will end with \r\n.\r\n */
44 POP_PAF_DATA_STATE, /* Indicated MIME will be contained in response */
45 POP_PAF_HAS_ARG /* Intermediate state when parsing LIST */
46 } PopExpectedResp;
47
48
49 typedef enum _PopParseCmdState
50 {
51 POP_CMD_SEARCH, /* Search for Command */
52 POP_CMD_FIN, /* Found space. Finished parsing Command */
53 POP_CMD_ARG /* Parsing command with multi-line response iff arg given */
54 } PopParseCmdState;
55
56
57 /* saves data when parsing client commands */
58 typedef struct _PopPafParseCmd
59 {
60 char *next_letter; /* a pointer to the current commands data */
61 PopExpectedResp exp_resp; /* the expected termination sequence for this command */
62 PopParseCmdState status; /* whether the current has already been found */
63 } PopPafParseCmd;
64
65
66 /* State tracker for POP PAF */
67 typedef struct _PopPafData
68 {
69 PopExpectedResp pop_state; /* The current POP PAF state. */
70 DataEndState end_state; /* Current termination sequence state */
71 PopPafParseCmd cmd_state; /* all of the command parsing data */
72 MimeDataPafInfo data_info; /* Mime Information */
73 bool cmd_continued; /* data continued from previous packet? */
74 bool end_of_data;
75 // uint32_t length; /* TODO --> use length in top and RETR */
76 } PopPafData;
77
78
79
80 /*
81 * read process_command() description below
82 */
83 static bool search_for_command(PopPafData *pfdata, const uint8_t ch)
84 {
85 char val = *(pfdata->cmd_state.next_letter);
86
87 // if end of command && data contains a space or newline
88 if (val == '\0' && (isblank(ch) || ch == '\r' || ch == '\n'))
89 {
90 if (pfdata->cmd_state.exp_resp == POP_PAF_HAS_ARG)
91 {
92 pfdata->cmd_state.status = POP_CMD_ARG;
93 }
94 else
95 {
96 pfdata->cmd_state.status = POP_CMD_FIN;
97 pfdata->pop_state = pfdata->cmd_state.exp_resp;
98 return true;
99 }
100 }
101 else if (toupper(ch) == toupper(val) )
102 {
103 pfdata->cmd_state.next_letter++;
104 }
105 else
106 {
107 pfdata->cmd_state.status = POP_CMD_FIN;
108 }
109
110 return false;
111 }
112
113
114 /*
115 * read process_command() description below
116 */
117 static bool init_command_search(PopPafData *pfdata, const uint8_t ch)
118 {
119 pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE;
120
121 switch(ch)
122 {
123 case 'c':
124 case 'C':
125 pfdata->cmd_state.exp_resp = POP_PAF_MULTI_LINE_STATE;
126 pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_CAPA].name[1]);
127 break;
128 case 'l':
129 case 'L':
130 pfdata->cmd_state.exp_resp = POP_PAF_HAS_ARG;
131 pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_LIST].name[1]);
132 break;
133 case 'r':
134 case 'R':
135 pfdata->cmd_state.exp_resp = POP_PAF_DATA_STATE;
136 pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_RETR].name[1]);
137 break;
138 case 't':
139 case 'T':
140 pfdata->cmd_state.exp_resp = POP_PAF_DATA_STATE;
141 pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_TOP].name[1]);
142 break;
143 case 'u':
144 case 'U':
145 pfdata->cmd_state.exp_resp = POP_PAF_HAS_ARG;
146 pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_UIDL].name[1]);
147 break;
148 default:
149 pfdata->cmd_state.status = POP_CMD_FIN;
150 }
151
152 return false;
153 }
154
155 /*
156 * Attempts to determine the current command based upon the given character
157 * If another character is required to determine the current command,
158 * sets the function pointer to the correct next state
159 *
160 * PARAMS:
161 * pop_cmd - a pointer to the struct containing all of the
162 * relevant parsing info
163 * ch - the first character from the clients command
164 * RETURNS
165 * true - if the expected response is NOT a single line
166 * false - otherwise
167 */
168 static inline bool process_command(PopPafData *pfdata, const uint8_t ch)
169 {
170 if (pfdata->cmd_state.next_letter)
171 return search_for_command(pfdata, ch);
172 else
173 return init_command_search(pfdata, ch);
174 }
175
176
177 static inline void reset_data_states(PopPafData *pfdata)
178 {
179 // reset MIME info
180 _dpd.fileAPI->reset_mime_paf_state(&(pfdata->data_info));
181
182 // reset general pop fields
183 pfdata->cmd_continued = false;
184 pfdata->end_state = PAF_DATA_END_UNKNOWN;
185 pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE;
186 }
187
188
189 /*
190 * Checks if the current data is a valid response.
191 * According to RFC 1939, every response begins with either
192 * +OK
193 * -ERR.
194 *
195 * RETURNS:
196 * true - if the character is a +
197 * false - if the character is anything else
198 */
199 static inline int valid_response(const uint8_t data)
200 {
201 return (data == '+');
202 }
203
204 /*
205 * Client PAF calls this command to set the server's state. This is the
206 * function which ensure's the server know the correct expected
207 * DATA
208 */
209 static inline void set_server_state(void *ssn, PopExpectedResp state)
210 {
211 PopPafData *server_data = *(_dpd.streamAPI->get_paf_user_data(ssn, 0, pop_paf_id));
212
213 // ERROR IF SERVER DATA DOES NOT EXIST!! SHOULD NOT BE POSSIBLE!!
214 if (server_data)
215 {
216 reset_data_states(server_data);
217 server_data->end_of_data = false;
218 server_data->pop_state = state;
219 }
220 }
221
222 /*
223 * A helper function to reset the client's command parsing
224 * information
225 */
226 static inline void reset_client_cmd_info(PopPafData *pfdata)
227 {
228 pfdata->cmd_state.next_letter = NULL;
229 pfdata->cmd_state.status = POP_CMD_SEARCH;
230 }
231
232 /*
233 * Statefully search for the termination sequence CRCL.CRLF ("\r\n.\r\n").
234 *
235 * PARAMS:
236 * mime_data : true if this is mime_data.
237 *
238 * RETURNS:
239 * 0 - if termination sequence not found
240 * 1 - if termination sequence found
241 */
242 static bool find_data_end_multi_line(PopPafData *pfdata, const uint8_t ch, bool mime_data)
243 {
244 // TODO: This will currently flush on MIME boundary, and one line later at end of PDU
245
246 if (_dpd.fileAPI->check_data_end(&(pfdata->end_state), ch))
247 {
248 DEBUG_WRAP(DebugMessage(DEBUG_POP, "End of Multi-line response found\n"););
249 pfdata->end_of_data = true;
250 pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE;
251 reset_data_states(pfdata);
252 return true;
253 }
254
255 // if this is a data command, search for MIME ending
256 if (mime_data)
257 {
258 if (_dpd.fileAPI->process_mime_paf_data(&(pfdata->data_info), ch))
259 {
260 DEBUG_WRAP(DebugMessage(DEBUG_POP, "Mime Boundary found. Flushing data!\n"););
261 pfdata->cmd_continued = true;
262 return true;
263 }
264 }
265
266
267 return false;
268 }
269
270 /*
271 * Statefully search for the termination sequence LF ("\n"). Will also
272 * set the correct response state.
273 *
274 * PARAMS:
275 *
276 * RETURNS:
277 * 0 - if terminatino sequence not found
278 * 1 - if termination sequence found
279 */
280 static inline bool find_data_end_single_line(PopPafData *pfdata, const uint8_t ch, bool client)
281 {
282 if (ch == '\n')
283 {
284 // reset the correct information
285 if (client)
286 reset_client_cmd_info(pfdata);
287 else
288 reset_data_states(pfdata);
289
290 DEBUG_WRAP(DebugMessage(DEBUG_POP, "End of single-line response "
291 "found. Flushing data!\n"););
292 return true;
293 }
294
295 return false;
296 }
297
298
299 static PAF_Status pop_paf_server(PopPafData *pfdata,
300 const uint8_t* data, uint32_t len, uint32_t* fp)
301 {
302 uint32_t i;
303 uint32_t boundary_start = 0;
304
305 // if a negative response was received, it will be a one line response.
306 if (!pfdata->cmd_continued && !valid_response(*data))
307 pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE;
308
309
310 for (i = 0; i < len; i++)
311 {
312 uint8_t ch = data[i];
313
314 // find the termination sequence based upon the current state
315 switch (pfdata->pop_state)
316 {
317 case POP_PAF_MULTI_LINE_STATE:
318 if( find_data_end_multi_line(pfdata, ch, false) )
319 {
320 *fp = i + 1;
321 return PAF_FLUSH;
322 }
323 break;
324
325 case POP_PAF_DATA_STATE:
326 // TODO --> statefully get length
327 if ( find_data_end_multi_line(pfdata, ch, true) )
328 {
329 *fp = i + 1;
330 return PAF_FLUSH;
331 }
332
333 if (pfdata->data_info.boundary_state == MIME_PAF_BOUNDARY_UNKNOWN)
334 boundary_start = i;
335
336 break;
337
338 case POP_PAF_SINGLE_LINE_STATE:
339 default:
340 if ( find_data_end_single_line(pfdata, ch, false) )
341 {
342 *fp = i + 1;
343 return PAF_FLUSH;
344 }
345 break;
346 }
347 }
348
349 pfdata->cmd_continued = true;
350
351
352 if ( scanning_boundary(&pfdata->data_info, boundary_start, fp) )
353 return PAF_LIMIT;
354
355 return PAF_SEARCH;
356 }
357
358
359 /*
360 * Determine the Client's command and set the response state.
361 * Flush data when "\r\n" is received
362 */
363 static PAF_Status pop_paf_client(void *ssn, PopPafData *pfdata,
364 const uint8_t* data, uint32_t len, uint32_t* fp)
365 {
366 uint32_t i;
367
368 // TODO ... ensure current command is smaller than max command length
369
370 for (i = 0; i < len; i++)
371 {
372 uint8_t ch = data[i];
373
374 switch (pfdata->cmd_state.status)
375 {
376 case POP_CMD_SEARCH:
377 if (process_command(pfdata, ch) )
378 {
379 set_server_state(ssn, pfdata->pop_state);
380 }
381
382 //break; DO NOT UNCOMMENT!! both cases should check for a LF.
383
384 case POP_CMD_FIN:
385 if (find_data_end_single_line(pfdata, ch, true) )
386 {
387 // reset command parsing data
388 *fp = i + 1;
389 return PAF_FLUSH;
390 }
391 break;
392
393 case POP_CMD_ARG:
394 if (find_data_end_single_line(pfdata, ch, true))
395 {
396 set_server_state(ssn, POP_PAF_MULTI_LINE_STATE);
397 *fp = i + 1;
398 return PAF_FLUSH;
399 }
400 else if (isdigit(ch))
401 {
402 pfdata->cmd_state.status = POP_CMD_FIN;
403 }
404 }
405 }
406
407 return PAF_SEARCH;
408 }
409
410 /* Function: pop_paf()
411
412 Purpose: POP PAF callback.
413 Inspects pop traffic. Checks client traffic for the current command
414 and sets correct server termination sequence. Client side data will
415 flush after receiving CRLF ("\r\n"). Server data flushes after
416 finding set termination sequence.
417
418 Arguments:
419 void * - stream5 session pointer
420 void ** - DNP3 state tracking structure
421 const uint8_t * - payload data to inspect
422 uint32_t - length of payload data
423 uint32_t - flags to check whether client or server
424 uint32_t * - pointer to set flush point
425 uint32_t * - pointer to set header flush point
426
427 Returns:
428 PAF_Status - PAF_FLUSH if flush point found, PAF_SEARCH otherwise
429 */
430
431 static PAF_Status pop_paf(void* ssn, void** ps, const uint8_t* data,
432 uint32_t len, uint64_t *flags, uint32_t* fp, uint32_t* fp_eoh)
433 {
434
435 PopPafData *pfdata = *(PopPafData **)ps;
436
437 if (pfdata == NULL)
438 {
439 if (_dpd.fileAPI->check_paf_abort(ssn))
440 return PAF_ABORT;
441
442 pfdata = _dpd.snortAlloc(1, sizeof(*pfdata), PP_POP, 0);
443
444 if (pfdata == NULL)
445 {
446 return PAF_ABORT;
447 }
448
449 reset_data_states(pfdata);
450 *ps = pfdata;
451 }
452
453
454 if (*flags & FLAG_FROM_SERVER)
455 {
456 DEBUG_WRAP(DebugMessage(DEBUG_POP, "PAF: From server.\n"););
457 return pop_paf_server(pfdata, data, len, fp);
458 }
459 else
460 {
461 DEBUG_WRAP(DebugMessage(DEBUG_POP, "PAF: From client.\n"););
462 return pop_paf_client(ssn, pfdata, data, len, fp);
463 }
464 }
465
466 bool is_data_end (void* ssn)
467 {
468 if ( ssn )
469 {
470 PopPafData** s = (PopPafData **)_dpd.streamAPI->get_paf_user_data(ssn, 0, pop_paf_id);
471
472 if ( s && (*s) )
473 return ((*s)->end_of_data);
474 }
475
476 return false;
477 }
478
479 void pop_paf_cleanup(void *pafData)
480 {
481 if (pafData) {
482 _dpd.snortFree(pafData, sizeof(PopPafData), PP_POP, 0);
483 }
484 }
485
486 #ifdef TARGET_BASED
487 void register_pop_paf_service (struct _SnortConfig *sc, int16_t app, tSfPolicyId policy)
488 {
489 if (_dpd.isPafEnabled())
490 {
491 pop_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, true, pop_paf, true);
492 pop_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, false,pop_paf, true);
493 _dpd.streamAPI->register_paf_free(pop_paf_id, pop_paf_cleanup);
494 }
495 }
496 #endif
497
498
499 void register_pop_paf_port(struct _SnortConfig *sc, unsigned int i, tSfPolicyId policy)
500 {
501 if (_dpd.isPafEnabled())
502 {
503 pop_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, true, pop_paf, true);
504 pop_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, false, pop_paf, true);
505 _dpd.streamAPI->register_paf_free(pop_paf_id, pop_paf_cleanup);
506 }
507 }