Convert rad_alloc and rad_free to use talloc
[freeradius.git] / src / main / radsniff.c
1 /*
2  *  radsniff.c  Display the RADIUS traffic on the network.
3  *
4  *  Version:    $Id$
5  *
6  *  This program is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU General Public License
8  *  as published by the Free Software Foundation; either version 2
9  *  of the License, or (at your option) any later version.
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 St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  *  Copyright 2006  The FreeRADIUS server project
21  *  Copyright 2006  Nicolas Baradakis <nicolas.baradakis@cegetel.net>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #define _LIBRADIUS 1
28 #include <freeradius-devel/libradius.h>
29
30 #include <pcap.h>
31
32 #include <freeradius-devel/radpaths.h>
33 #include <freeradius-devel/conf.h>
34 #include <freeradius-devel/radsniff.h>
35
36 static const char *radius_secret = "testing123";
37 static VALUE_PAIR *filter_vps = NULL;
38
39 static int do_sort = 0;
40 static int to_stdout = 0;
41 static FILE *log_dst;
42
43 #ifndef PCAP_NETMASK_UNKNOWN
44 #  define PCAP_NETMASK_UNKNOWN 0
45 #endif
46
47 #undef DEBUG1
48 #define DEBUG1 if (fr_debug_flag > 2) fprintf
49 #undef DEBUG
50 #define DEBUG if (fr_debug_flag > 1) fprintf
51 #undef INFO
52 #define INFO if (fr_debug_flag > 0) fprintf
53
54 struct timeval start_pcap = {0, 0};
55 static rbtree_t *filter_tree = NULL;
56 static rbtree_t *request_tree = NULL;
57 static pcap_dumper_t *out = NULL;
58 static RADIUS_PACKET *nullpacket = NULL;
59
60 typedef int (*rbcmp)(const void *, const void *);
61
62 static const char *radsniff_version = "radsniff version " RADIUSD_VERSION_STRING
63 #ifdef RADIUSD_VERSION_COMMIT
64 " (git #" RADIUSD_VERSION_COMMIT ")"
65 #endif
66 ", built on " __DATE__ " at " __TIME__;
67
68 static int filter_packet(RADIUS_PACKET *packet)
69 {
70         VALUE_PAIR *check_item;
71         VALUE_PAIR *vp;
72         unsigned int pass, fail;
73         int compare;
74
75         pass = fail = 0;
76         for (vp = packet->vps; vp != NULL; vp = vp->next) {
77                 for (check_item = filter_vps;
78                      check_item != NULL;
79                      check_item = check_item->next)
80                         if ((check_item->da == vp->da)
81                          && (check_item->op != T_OP_SET)) {
82                                 compare = paircmp(check_item, vp);
83                                 if (compare == 1)
84                                         pass++;
85                                 else
86                                         fail++;
87                         }
88         }
89
90         if (fail == 0 && pass != 0) {
91                 /*
92                  *      Cache authentication requests, as the replies
93                  *      may not match the RADIUS filter.
94                  */
95                 if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
96                     (packet->code == PW_ACCOUNTING_REQUEST)) {
97                         rbtree_deletebydata(filter_tree, packet);
98                         
99                         if (!rbtree_insert(filter_tree, packet)) {
100                         oom:
101                                 fprintf(stderr, "radsniff: Out of memory\n");
102                                 exit(1);
103                         }
104                 }
105                 return 0;       /* matched */
106         }
107
108         /*
109          *      Don't create erroneous matches.
110          */
111         if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
112             (packet->code == PW_ACCOUNTING_REQUEST)) {
113                 rbtree_deletebydata(filter_tree, packet);
114                 return 1;
115         }
116         
117         /*
118          *      Else see if a previous Access-Request
119          *      matched.  If so, also print out the
120          *      matching accept, reject, or challenge.
121          */
122         if ((packet->code == PW_AUTHENTICATION_ACK) ||
123             (packet->code == PW_AUTHENTICATION_REJECT) ||
124             (packet->code == PW_ACCESS_CHALLENGE) ||
125             (packet->code == PW_ACCOUNTING_RESPONSE)) {
126                 RADIUS_PACKET *reply;
127
128                 /*
129                  *      This swaps the various fields.
130                  */
131                 reply = rad_alloc_reply(NULL, packet);
132                 if (!reply) goto oom;
133                 
134                 compare = 1;
135                 if (rbtree_finddata(filter_tree, reply)) {
136                         compare = 0;
137                 }
138                 
139                 rad_free(&reply);
140                 return compare;
141         }
142         
143         return 1;
144 }
145
146 /*
147  *      Bubble goodness
148  */
149 static void sort(RADIUS_PACKET *packet)
150 {
151         int i, j, size;
152         VALUE_PAIR *vp, *tmp;
153         VALUE_PAIR *array[1024]; /* way more than necessary */
154
155         size = 0;
156         for (vp = packet->vps; vp != NULL; vp = vp->next) {
157                 array[size++] = vp;
158         }
159
160         if (size == 0) return;
161
162         for (i = 0; i < size - 1; i++)  {
163                 for (j = 0; j < size - 1 - i; j++) {
164                         if (array[j + 1]->da->attr < array[j]->da->attr)  {
165                                 tmp = array[j];         
166                                 array[j] = array[j + 1];
167                                 array[j + 1] = tmp;
168                         }
169                 }
170         }
171
172         /*
173          *      And put them back again.
174          */
175         vp = packet->vps = array[0];
176         for (i = 1; i < size; i++) {
177                 vp->next = array[i];
178                 vp = array[i];
179         }
180         vp->next = NULL;
181 }
182
183 #define USEC 1000000
184 static void tv_sub(const struct timeval *end, const struct timeval *start,
185                    struct timeval *elapsed)
186 {
187         elapsed->tv_sec = end->tv_sec - start->tv_sec;
188         if (elapsed->tv_sec > 0) {
189                 elapsed->tv_sec--;
190                 elapsed->tv_usec = USEC;
191         } else {
192                 elapsed->tv_usec = 0;
193         }
194         elapsed->tv_usec += end->tv_usec;
195         elapsed->tv_usec -= start->tv_usec;
196         
197         if (elapsed->tv_usec >= USEC) {
198                 elapsed->tv_usec -= USEC;
199                 elapsed->tv_sec++;
200         }
201 }
202
203 static void got_packet(UNUSED uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *data)
204 {
205
206         static int count = 1;                   /* Packets seen */
207         
208         /*
209          *  Define pointers for packet's attributes
210          */
211         const struct ethernet_header *ethernet; /* The ethernet header */
212         const struct ip_header *ip;             /* The IP header */
213         const struct udp_header *udp;           /* The UDP header */
214         const uint8_t *payload;                 /* Packet payload */
215         
216         /*
217          *  And define the size of the structures we're using
218          */
219         int size_ethernet = sizeof(struct ethernet_header);
220         int size_ip = sizeof(struct ip_header);
221         int size_udp = sizeof(struct udp_header);
222         
223         /*
224          *  For FreeRADIUS
225          */
226         RADIUS_PACKET *packet, *original;
227         struct timeval elapsed;
228
229         /*
230          * Define our packet's attributes
231          */
232
233         if ((data[0] == 2) && (data[1] == 0) &&
234             (data[2] == 0) && (data[3] == 0)) {
235                 ip = (const struct ip_header*) (data + 4);
236
237         } else {
238                 ethernet = (const struct ethernet_header*)(data);
239                 ip = (const struct ip_header*)(data + size_ethernet);
240         }
241         
242         udp = (const struct udp_header*)(((const uint8_t *) ip) + size_ip);
243         payload = (const uint8_t *)(((const uint8_t *) udp) + size_udp);
244
245         packet = rad_alloc(NULL, 0);
246         if (!packet) {
247                 fprintf(stderr, "Out of memory\n");
248                 return;
249         }
250
251         packet->src_ipaddr.af = AF_INET;
252         packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
253         packet->src_port = ntohs(udp->udp_sport);
254         packet->dst_ipaddr.af = AF_INET;
255         packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
256         packet->dst_port = ntohs(udp->udp_dport);
257
258         memcpy(&packet->data, &payload, sizeof(packet->data));
259         packet->data_len = header->len - (payload - data);
260
261         if (!rad_packet_ok(packet, 0)) {
262                 DEBUG(log_dst, "Packet: %s\n", fr_strerror());
263                 
264                 DEBUG(log_dst, "  From     %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
265                 DEBUG(log_dst, "  To:      %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
266                 DEBUG(log_dst, "  Type:    %s\n", fr_packet_codes[packet->code]);
267
268                 rad_free(&packet);
269                 return;
270         }
271         
272         switch (packet->code) {
273         case PW_COA_REQUEST:
274                 /* we need a 16 x 0 byte vector for decrypting encrypted VSAs */
275                 original = nullpacket;
276                 break;
277         case PW_AUTHENTICATION_ACK:
278                 /* look for a matching request and use it for decoding */
279                 original = rbtree_finddata(request_tree, packet);
280                 break;
281         case PW_AUTHENTICATION_REQUEST:
282                 /* save the request for later matching */
283                 original = rad_alloc_reply(NULL, packet);
284                 if (original) { /* just ignore allocation failures */
285                         rbtree_deletebydata(request_tree, original);
286                         rbtree_insert(request_tree, original);
287                 }
288                 /* fallthrough */
289         default:
290                 /* don't attempt to decode any encrypted attributes */
291                 original = NULL;
292         }
293
294         /*
295          *  Decode the data without bothering to check the signatures.
296          */
297         if (rad_decode(packet, original, radius_secret) != 0) {
298                 rad_free(&packet);
299                 fr_perror("decode");
300                 return;
301         }
302
303         /*
304          *  We've seen a successfull reply to this, so delete it now
305          */
306         if (original)
307                 rbtree_deletebydata(request_tree, original);
308
309         if (filter_vps && filter_packet(packet)) {
310                 rad_free(&packet);
311                 DEBUG(log_dst, "Packet number %d doesn't match\n", count++);
312                 return;
313         }
314
315         if (out) {
316                 pcap_dump((void *) out, header, data);
317                 goto check_filter;
318         }
319
320         INFO(log_dst, "%s Id %d\t", fr_packet_codes[packet->code], packet->id);
321
322         /*
323          *  Print the RADIUS packet
324          */
325         INFO(log_dst, "%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
326         INFO(log_dst, "%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
327         
328         DEBUG1(log_dst, "\t(%d packets)", count++);
329         
330         if (!start_pcap.tv_sec) {
331                 start_pcap = header->ts;
332         }
333
334         tv_sub(&header->ts, &start_pcap, &elapsed);
335
336         INFO(log_dst, "\t+%u.%03u", (unsigned int) elapsed.tv_sec,
337                (unsigned int) elapsed.tv_usec / 1000);
338                
339         if (fr_debug_flag > 1) {
340                 DEBUG(log_dst, "\n");
341                 if (packet->vps) {
342                         if (do_sort) sort(packet);
343         
344                         vp_printlist(log_dst, packet->vps);
345                         pairfree(&packet->vps);
346                 }
347         }
348         
349         INFO(log_dst, "\n");
350         
351         if (!to_stdout && (fr_debug_flag > 4)) {
352                 rad_print_hex(packet);
353         }
354         
355         fflush(log_dst);
356
357  check_filter:
358         /*
359          *  If we're doing filtering, Access-Requests are cached in the
360          *  filter tree.
361          */
362         if (!filter_vps ||
363             ((packet->code != PW_AUTHENTICATION_REQUEST) &&
364              (packet->code != PW_ACCOUNTING_REQUEST))) {
365                 rad_free(&packet);
366         }
367 }
368
369 static void NEVER_RETURNS usage(int status)
370 {
371         FILE *output = status ? stderr : stdout;
372         fprintf(output, "Usage: radsniff [options]\n");
373         fprintf(output, "options:\n");
374         fprintf(output, "  -c <count>      Number of packets to capture.\n");
375         fprintf(output, "  -d <directory>  Set dictionary directory.\n");
376         fprintf(output, "  -F              Filter PCAP file from stdin to stdout.\n");
377         fprintf(output, "  -f <filter>     PCAP filter (default is 'udp port <port> or <port + 1> or 3799')\n");
378         fprintf(output, "  -h              This help message.\n");
379         fprintf(output, "  -i <interface>  Capture packets from interface (defaults to any if supported).\n");
380         fprintf(output, "  -I <file>       Read packets from file (overrides input of -F).\n");
381         fprintf(output, "  -p <port>       Filter packets by port (default is 1812).\n");
382         fprintf(output, "  -q              Print less debugging information.\n");
383         fprintf(output, "  -r <filter>     RADIUS attribute filter.\n");
384         fprintf(output, "  -s <secret>     RADIUS secret.\n");
385         fprintf(output, "  -S              Sort attributes in the packet (useful for diffing responses).\n");
386         fprintf(output, "  -v              Show program version information.\n");
387         fprintf(output, "  -w <file>       Write output packets to file (overrides output of -F).\n");
388         fprintf(output, "  -x              Print more debugging information (defaults to -xx).\n");
389         exit(status);
390 }
391
392 int main(int argc, char *argv[])
393 {
394         const char *from_dev = NULL;                    /* Capture from device */
395         const char *from_file = NULL;                   /* Read from pcap file */
396         int from_stdin = 0;                             /* Read from stdin */
397         
398         pcap_t *in;                                     /* PCAP input handle */
399         
400         int limit = -1;                                 /* How many packets to sniff */
401         
402         char errbuf[PCAP_ERRBUF_SIZE];                  /* Error buffer */
403
404         char *to_file = NULL;                           /* PCAP output file */
405         
406         char *pcap_filter = NULL;                       /* PCAP filter string */
407         char *radius_filter = NULL;
408         int port = 1812;
409         
410         struct bpf_program fp;                          /* Holds compiled filter */
411         bpf_u_int32 ip_mask = PCAP_NETMASK_UNKNOWN;     /* Device Subnet mask */
412         bpf_u_int32 ip_addr = 0;                        /* Device IP */
413         
414         char buffer[1024];
415
416         int opt;
417         FR_TOKEN parsecode;
418         const char *radius_dir = RADIUS_DIR;
419         
420         fr_debug_flag = 2;
421         log_dst = stdout;
422
423         /*
424          *  Get options
425          */
426         while ((opt = getopt(argc, argv, "c:d:Ff:hi:I:p:qr:s:Svw:xX")) != EOF) {
427                 switch (opt)
428                 {
429                 case 'c':
430                         limit = atoi(optarg);
431                         if (limit <= 0) {
432                                 fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg);
433                                 exit(1);
434                         }
435                         break;
436                 case 'd':
437                         radius_dir = optarg;
438                         break;
439                 case 'F':
440                         from_stdin = 1;
441                         to_stdout = 1;
442                         break;
443                 case 'f':
444                         pcap_filter = optarg;
445                         break;
446                 case 'h':
447                         usage(0);
448                         break;
449                 case 'i':
450                         from_dev = optarg;
451                         break;
452                 case 'I':
453                         from_file = optarg;
454                         break;
455                 case 'p':
456                         port = atoi(optarg);
457                         break;
458                 case 'q':
459                         if (fr_debug_flag > 0) {
460                                 fr_debug_flag--;
461                         }
462                         break;
463                 case 'r':
464                         radius_filter = optarg;
465                         break;
466                 case 's':
467                         radius_secret = optarg;
468                         break;
469                 case 'S':
470                         do_sort = 1;
471                         break;
472                 case 'v':
473                         INFO(log_dst, "%s %s\n", radsniff_version, pcap_lib_version());
474                         exit(0);
475                         break;
476                 case 'w':
477                         to_file = optarg;
478                         break;
479                 case 'x':
480                 case 'X':
481                         fr_debug_flag++;
482                         break;
483                 default:
484                         usage(64);
485                 }
486         }
487         
488         /* What's the point in specifying -F ?! */
489         if (from_stdin && from_file && to_file) {
490                 usage(64);
491         }
492         
493         /* Can't read from both... */
494         if (from_file && from_dev) {
495                 usage(64);
496         }
497         
498         /* Reading from file overrides stdin */
499         if (from_stdin && (from_file || from_dev)) {
500                 from_stdin = 0;
501         }
502         
503         /* Writing to file overrides stdout */
504         if (to_file && to_stdout) {
505                 to_stdout = 0;
506         }
507         
508         /*
509          *  If were writing pcap data stdout we *really* don't want to send
510          *  logging there as well.
511          */
512         log_dst = to_stdout ? stderr : stdout;
513
514 #if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN)
515         if (from_stdin || to_stdout) {
516                 fprintf(stderr, "radsniff: PCAP streams not supported.\n");
517                 exit(64);
518         }
519 #endif
520
521         if (!pcap_filter) {
522                 pcap_filter = buffer;
523                 snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d",
524                          port, port + 1, 3799);
525         }
526         
527         /*
528          *  There are times when we don't need the dictionaries.
529          */
530         if (!to_stdout) {
531                 if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
532                         fr_perror("radsniff");
533                         exit(64);
534                 }
535         }
536
537         if (radius_filter) {
538                 parsecode = userparse(radius_filter, &filter_vps);
539                 if (parsecode == T_OP_INVALID) {
540                         fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\" (%s)\n", radius_filter, fr_strerror());
541                         exit(64);
542                 }
543                 
544                 if (!filter_vps) {
545                         fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter);
546                         exit(64);
547                 }
548
549                 filter_tree = rbtree_create((rbcmp) fr_packet_cmp, free, 0);
550                 if (!filter_tree) {
551                         fprintf(stderr, "radsniff: Failed creating filter tree\n");
552                         exit(1);
553                 }
554         }
555
556         /*
557          *  Setup the request tree
558          */
559         request_tree = rbtree_create((rbcmp) fr_packet_cmp, free, 0);
560         if (!request_tree) {
561                 fprintf(stderr, "radsniff: Failed creating request tree\n");
562                 exit(1);
563         }
564
565         /*
566          *  Allocate a null packet for decrypting attributes in CoA requests
567          */
568         nullpacket = rad_alloc(NULL, 0);
569         if (!nullpacket) {
570                 fprintf(stderr, "radsniff: Out of memory\n");
571                 exit(1);
572         }
573
574         /*
575          *  Get the default capture device
576          */
577         if (!from_stdin && !from_file && !from_dev) {
578                 from_dev = pcap_lookupdev(errbuf);
579                 if (!from_dev) {
580                         fprintf(stderr, "radsniff: Failed discovering default interface (%s)\n", errbuf);
581                         exit(1);
582                 }
583
584                 INFO(log_dst, "Capturing from interface \"%s\"\n", from_dev);
585         }
586         
587         /*
588          *  Print captures values which will be used
589          */
590         if (fr_debug_flag > 2) {
591                                 DEBUG1(log_dst, "Sniffing with options:\n");
592                 if (from_dev)   DEBUG1(log_dst, "  Device                   : [%s]\n", from_dev);
593                 if (limit > 0)  DEBUG1(log_dst, "  Capture limit (packets)  : [%d]\n", limit);
594                                 DEBUG1(log_dst, "  PCAP filter              : [%s]\n", pcap_filter);
595                                 DEBUG1(log_dst, "  RADIUS secret            : [%s]\n", radius_secret);
596                 if (filter_vps){DEBUG1(log_dst, "  RADIUS filter            :\n");
597                         vp_printlist(log_dst, filter_vps);
598                 }
599         }
600
601         /*
602          *  Figure out whether were doing a reading from a file, doing a live
603          *  capture or reading from stdin.
604          */
605         if (from_file) {
606                 in = pcap_open_offline(from_file, errbuf);
607 #ifdef HAVE_PCAP_FOPEN_OFFLINE
608         } else if (from_stdin) {
609                 in = pcap_fopen_offline(stdin, errbuf);
610 #endif
611         } else if (from_dev) {
612                 pcap_lookupnet(from_dev, &ip_addr, &ip_mask, errbuf);
613                 in = pcap_open_live(from_dev, 65536, 1, 1, errbuf);
614         } else {
615                 fprintf(stderr, "radsniff: No capture devices available\n");
616         }
617         
618         if (!in) {
619                 fprintf(stderr, "radsniff: Failed opening input (%s)\n", errbuf);
620                 exit(1);
621         }
622
623         if (to_file) {
624                 out = pcap_dump_open(in, to_file);
625                 if (!out) {
626                         fprintf(stderr, "radsniff: Failed opening output file (%s)\n", pcap_geterr(in));
627                         exit(1);
628                 }
629 #ifdef HAVE_PCAP_DUMP_FOPEN
630         } else if (to_stdout) {
631                 out = pcap_dump_fopen(in, stdout);
632                 if (!out) {
633                         fprintf(stderr, "radsniff: Failed opening stdout (%s)\n", pcap_geterr(in));
634                         exit(1);
635                 }
636 #endif
637         }
638
639         /*
640          *  Apply the rules
641          */
642         if (pcap_compile(in, &fp, pcap_filter, 0, ip_mask) < 0) {
643                 fprintf(stderr, "radsniff: Failed compiling PCAP filter (%s)\n", pcap_geterr(in));
644                 exit(1);
645         }
646         
647         if (pcap_setfilter(in, &fp) < 0) {
648                 fprintf(stderr, "radsniff: Failed applying PCAP filter (%s)\n", pcap_geterr(in));
649                 exit(1);
650         }
651
652         /*
653          *  Enter the main capture loop...
654          */
655         pcap_loop(in, limit, got_packet, NULL);
656         
657         /*
658          *  ...were done capturing.
659          */
660         pcap_close(in);
661         if (out) {
662                 pcap_dump_close(out);
663         }
664         
665         if (filter_tree) {
666                 rbtree_free(filter_tree);
667         }
668         
669         INFO(log_dst, "Done sniffing\n");
670         
671         return 0;
672 }