1 /*
2 * mailsnarf.c
3 *
4 * Sniff mail on a network, saving messages in Berkeley mbox format.
5 *
6 * Copyright (c) 1999 Dug Song <dugsong@monkey.org>
7 *
8 * $Id: mailsnarf.c,v 1.38 2001/03/15 08:33:04 dugsong Exp $
9 */
10
11 #include "config.h"
12
13 #include <sys/types.h>
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <string.h>
19 #include <time.h>
20 #include <regex.h>
21 #include <err.h>
22 #include <libnet.h>
23 #include <nids.h>
24 #include <pcap.h>
25
26 #include "pcaputil.h"
27 #include "buf.h"
28 #include "version.h"
29
30 /* bogus SMTP state machine */
31 enum {
32 SMTP_NONE = 0,
33 SMTP_HELO,
34 SMTP_MAIL,
35 SMTP_RCPT,
36 SMTP_DATA
37 };
38
39 /* likewise, POP. */
40 enum {
41 POP_NONE = 0,
42 POP_RETR,
43 POP_DATA
44 };
45
46 struct smtp_info {
47 int state;
48 char *from;
49 };
50
51 struct pop_info {
52 int state;
53 };
54
55 int Opt_invert = 0;
56 regex_t *pregex = NULL;
57
58 static void
usage(void)59 usage(void)
60 {
61 fprintf(stderr, "Version: " VERSION "\n"
62 "Usage: mailsnarf [-i interface] [[-v] pattern [expression]]\n");
63 exit(1);
64 }
65
66 static int
regex_match(char * string)67 regex_match(char *string)
68 {
69 return (pregex == NULL ||
70 ((regexec(pregex, string, 0, NULL, 0) == 0) ^ Opt_invert));
71 }
72
73 static char *
grep_mail_address(char * buf)74 grep_mail_address(char *buf)
75 {
76 char *p, *q;
77
78 if ((p = strchr(buf, '<')) != NULL) {
79 p++;
80 if ((q = strchr(p, '>')) != NULL)
81 *q = '\0';
82 if (strlen(p) > 0)
83 return (strdup(p));
84 }
85 return (NULL);
86 }
87
88 static void
print_mbox_msg(char * from,char * msg)89 print_mbox_msg(char *from, char *msg)
90 {
91 char *p;
92 time_t t;
93
94 t = time(NULL);
95
96 if (from == NULL)
97 from = "mailsnarf";
98
99 printf("From %s %s", from, ctime(&t));
100
101 while ((p = strsep(&msg, "\n")) != NULL) {
102 if (strncmp(p, "From ", 5) == 0)
103 putchar('>');
104 for (; *p != '\r' && *p != '\0'; p++)
105 putchar(*p);
106 putchar('\n');
107 }
108 putchar('\n');
109 fflush(stdout);
110 }
111
112 static int
process_pop_client(struct pop_info * pop,char * data,int len)113 process_pop_client(struct pop_info *pop, char *data, int len)
114 {
115 struct buf *line, buf;
116 int i;
117
118 buf_init(&buf, data, len);
119
120 while ((i = buf_index(&buf, "\r\n", 2)) >= 0) {
121 line = buf_tok(&buf, NULL, i + 2);
122 line->base[line->end] = '\0';
123
124 if (strncasecmp(buf_ptr(line), "RETR ", 5) == 0) {
125 pop->state = POP_RETR;
126 }
127 else pop->state = POP_NONE;
128 }
129 return (len - buf_len(&buf));
130 }
131
132 static int
process_pop_server(struct pop_info * pop,char * data,int len)133 process_pop_server(struct pop_info *pop, char *data, int len)
134 {
135 struct buf *line, *body, buf;
136 int i;
137
138 buf_init(&buf, data, len);
139
140 if (pop->state == POP_NONE)
141 return (len);
142
143 if (pop->state == POP_RETR) {
144 if ((i = buf_index(&buf, "\r\n", 2)) < 0)
145 return (0);
146
147 line = buf_tok(&buf, NULL, i + 2);
148
149 if (buf_cmp(line, "+OK", 3) == 0) {
150 pop->state = POP_DATA;
151 }
152 else pop->state = POP_NONE;
153 }
154 if (pop->state == POP_DATA) {
155 if ((i = buf_index(&buf, "\r\n.\r\n", 5)) >= 0) {
156 body = buf_tok(&buf, NULL, i);
157 buf_skip(&buf, 5);
158 body->base[body->end] = '\0';
159
160 if (regex_match(buf_ptr(body)))
161 print_mbox_msg(NULL, buf_ptr(body));
162
163 pop->state = POP_NONE;
164 }
165 }
166 return (len - buf_len(&buf));
167 }
168
169 static int
process_smtp_client(struct smtp_info * smtp,char * data,int len)170 process_smtp_client(struct smtp_info *smtp, char *data, int len)
171 {
172 struct buf *line, *body, buf;
173 char *p;
174 int i;
175
176 buf_init(&buf, data, len);
177
178 if (smtp->state != SMTP_DATA) {
179 while ((i = buf_index(&buf, "\r\n", 2)) >= 0) {
180 line = buf_tok(&buf, NULL, i + 2);
181 line->base[line->end] = '\0';
182 p = buf_ptr(line);
183
184 if (strncasecmp(p, "RSET", 4) == 0) {
185 smtp->state = SMTP_HELO;
186 }
187 else if (smtp->state == SMTP_NONE &&
188 (strncasecmp(p, "HELO", 4) == 0 ||
189 strncasecmp(p, "EHLO", 4) == 0)) {
190 smtp->state = SMTP_HELO;
191 }
192 else if (smtp->state == SMTP_HELO &&
193 (strncasecmp(p, "MAIL ", 5) == 0 ||
194 strncasecmp(p, "SEND ", 5) == 0 ||
195 strncasecmp(p, "SAML ", 5) == 0)) {
196 smtp->from = grep_mail_address(p);
197 smtp->state = SMTP_MAIL;
198 }
199 else if (smtp->state == SMTP_MAIL &&
200 strncasecmp(p, "RCPT ", 5) == 0) {
201 smtp->state = SMTP_RCPT;
202 }
203 else if (smtp->state == SMTP_RCPT &&
204 strncasecmp(p, "DATA", 4) == 0) {
205 smtp->state = SMTP_DATA;
206 break;
207 }
208 }
209 }
210 if (smtp->state == SMTP_DATA) {
211 if ((i = buf_index(&buf, "\r\n.\r\n", 5)) >= 0) {
212 body = buf_tok(&buf, NULL, i);
213 buf_skip(&buf, 5);
214 body->base[body->end] = '\0';
215
216 if (regex_match(buf_ptr(body)))
217 print_mbox_msg(smtp->from, buf_ptr(body));
218
219 if (smtp->from) {
220 free(smtp->from);
221 smtp->from = NULL;
222 }
223 smtp->state = SMTP_HELO;
224 }
225 }
226 return (len - buf_len(&buf));
227 }
228
229 static void
sniff_pop_session(struct tcp_stream * ts,struct pop_info ** pop_save)230 sniff_pop_session(struct tcp_stream *ts, struct pop_info **pop_save)
231 {
232 struct pop_info *pop;
233 int i;
234
235 if (ts->addr.dest != 110 && ts->addr.source != 110 && /* POP3 */
236 ts->addr.dest != 109 && ts->addr.source != 109 && /* POP2 */
237 ts->addr.dest != 1109 && ts->addr.source != 1109) /* KPOP */
238 return;
239
240 switch (ts->nids_state) {
241
242 case NIDS_JUST_EST:
243 ts->server.collect = 1;
244 ts->client.collect = 1;
245
246 if ((pop = (struct pop_info *) malloc(sizeof(*pop))) == NULL)
247 nids_params.no_mem("sniff_pop_session");
248
249 pop->state = POP_NONE;
250 *pop_save = pop;
251 break;
252
253 case NIDS_DATA:
254 pop = *pop_save;
255
256 if (ts->server.count_new > 0) {
257 i = process_pop_client(pop, ts->server.data,
258 ts->server.count -
259 ts->server.offset);
260 nids_discard(ts, i);
261 }
262 else if (ts->client.count_new > 0) {
263 i = process_pop_server(pop, ts->client.data,
264 ts->client.count -
265 ts->client.offset);
266 nids_discard(ts, i);
267 }
268 break;
269
270 default:
271 pop = *pop_save;
272
273 if (ts->server.count > 0)
274 process_pop_client(pop, ts->server.data,
275 ts->server.count -
276 ts->server.offset);
277 else if (ts->client.count > 0)
278 process_pop_server(pop, ts->client.data,
279 ts->client.count -
280 ts->client.offset);
281 free(pop);
282 break;
283 }
284 }
285
286 /* XXX - Minimal SMTP FSM. We don't even consider server responses. */
287 static void
sniff_smtp_client(struct tcp_stream * ts,struct smtp_info ** smtp_save)288 sniff_smtp_client(struct tcp_stream *ts, struct smtp_info **smtp_save)
289 {
290 struct smtp_info *smtp;
291 int i;
292
293 if (ts->addr.dest != 25)
294 return;
295
296 switch (ts->nids_state) {
297
298 case NIDS_JUST_EST:
299 ts->server.collect = 1;
300
301 if ((smtp = (struct smtp_info *)malloc(sizeof(*smtp))) == NULL)
302 nids_params.no_mem("sniff_smtp_client");
303
304 smtp->state = SMTP_NONE;
305 smtp->from = NULL;
306 *smtp_save = smtp;
307 break;
308
309 case NIDS_DATA:
310 smtp = *smtp_save;
311
312 if (ts->server.count_new > 0) {
313 i = process_smtp_client(smtp, ts->server.data,
314 ts->server.count -
315 ts->server.offset);
316 nids_discard(ts, i);
317 }
318 break;
319
320 default:
321 smtp = *smtp_save;
322
323 if (ts->server.count > 0) {
324 process_smtp_client(smtp, ts->server.data,
325 ts->server.count -
326 ts->server.offset);
327 }
328 if (smtp->from)
329 free(smtp->from);
330 free(smtp);
331 break;
332 }
333 }
334
335 static void
null_syslog(int type,int errnum,struct ip * iph,void * data)336 null_syslog(int type, int errnum, struct ip *iph, void *data)
337 {
338 }
339
340 int
main(int argc,char * argv[])341 main(int argc, char *argv[])
342 {
343 extern char *optarg;
344 extern int optind;
345 int c;
346
347 while ((c = getopt(argc, argv, "i:vh?V")) != -1) {
348 switch (c) {
349 case 'i':
350 nids_params.device = optarg;
351 break;
352 case 'v':
353 Opt_invert = 1;
354 break;
355 default:
356 usage();
357 }
358 }
359 argc -= optind;
360 argv += optind;
361
362 if (argc > 0 && strlen(argv[0])) {
363 if ((pregex = (regex_t *) malloc(sizeof(*pregex))) == NULL)
364 err(1, "malloc");
365 if (regcomp(pregex, argv[0], REG_EXTENDED|REG_NOSUB) != 0)
366 errx(1, "invalid regular expression");
367 }
368 if (argc > 1)
369 nids_params.pcap_filter = copy_argv(argv + 1);
370
371 nids_params.scan_num_hosts = 0;
372 nids_params.syslog = null_syslog;
373
374 if (!nids_init())
375 errx(1, "%s", nids_errbuf);
376
377 nids_register_tcp(sniff_smtp_client);
378 nids_register_tcp(sniff_pop_session);
379
380 if (nids_params.pcap_filter != NULL) {
381 warnx("listening on %s [%s]", nids_params.device,
382 nids_params.pcap_filter);
383 }
384 else warnx("listening on %s", nids_params.device);
385
386 nids_run();
387
388 /* NOTREACHED */
389
390 exit(0);
391 }
392