022d957c0d39ac486d4d6f8e512629acf6f39ae5
[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 #undef DEBUG
39 #define DEBUG if (fr_debug_flag) printf
40
41 static rbtree_t *filter_tree = NULL;
42 typedef int (*rbcmp)(const void *, const void *);
43
44 static int filter_packet(RADIUS_PACKET *packet)
45 {
46         VALUE_PAIR *check_item;
47         VALUE_PAIR *vp;
48         unsigned int pass, fail;
49         int compare;
50
51         pass = fail = 0;
52         for (vp = packet->vps; vp != NULL; vp = vp->next) {
53                 for (check_item = filter_vps;
54                      check_item != NULL;
55                      check_item = check_item->next)
56                         if ((check_item->attribute == vp->attribute)
57                          && (check_item->operator != T_OP_SET)) {
58                                 compare = paircmp(check_item, vp);
59                                 if (compare == 1)
60                                         pass++;
61                                 else
62                                         fail++;
63                         }
64         }
65
66
67         if (fail == 0 && pass != 0) {
68                 /*
69                  *      Cache authentication requests, as the replies
70                  *      may not match the RADIUS filter.
71                  */
72                 if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
73                     (packet->code == PW_ACCOUNTING_REQUEST)) {
74                         rbtree_deletebydata(filter_tree, packet);
75                         
76                         if (!rbtree_insert(filter_tree, packet)) {
77                         oom:
78                                 fprintf(stderr, "radsniff: Out of memory\n");
79                                 exit(1);
80                         }
81                 }
82                 return 0;       /* matched */
83         }
84
85         /*
86          *      Don't create erroneous matches.
87          */
88         if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
89             (packet->code == PW_ACCOUNTING_REQUEST)) {
90                 rbtree_deletebydata(filter_tree, packet);
91                 return 1;
92         }
93         
94         /*
95          *      Else see if a previous Access-Request
96          *      matched.  If so, also print out the
97          *      matching accept, reject, or challenge.
98          */
99         if ((packet->code == PW_AUTHENTICATION_ACK) ||
100             (packet->code == PW_AUTHENTICATION_REJECT) ||
101             (packet->code == PW_ACCESS_CHALLENGE) ||
102             (packet->code == PW_ACCOUNTING_RESPONSE)) {
103                 RADIUS_PACKET *reply;
104
105                 /*
106                  *      This swaps the various fields.
107                  */
108                 reply = rad_alloc_reply(packet);
109                 if (!reply) goto oom;
110                 
111                 compare = 1;
112                 if (rbtree_finddata(filter_tree, reply)) {
113                         compare = 0;
114                 }
115                 
116                 rad_free(&reply);
117                 return compare;
118         }
119         
120         return 1;
121 }
122
123 static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *data)
124 {
125         /* Just a counter of how many packets we've had */
126         static int count = 1;
127         /* Define pointers for packet's attributes */
128         const struct ethernet_header *ethernet;  /* The ethernet header */
129         const struct ip_header *ip;              /* The IP header */
130         const struct udp_header *udp;            /* The UDP header */
131         const uint8_t *payload;                     /* Packet payload */
132         /* And define the size of the structures we're using */
133         int size_ethernet = sizeof(struct ethernet_header);
134         int size_ip = sizeof(struct ip_header);
135         int size_udp = sizeof(struct udp_header);
136         /* For FreeRADIUS */
137         RADIUS_PACKET *packet;
138
139         args = args;            /* -Wunused */
140
141         /* Define our packet's attributes */
142         ethernet = (const struct ethernet_header*)(data);
143         ip = (const struct ip_header*)(data + size_ethernet);
144         udp = (const struct udp_header*)(data + size_ethernet + size_ip);
145         payload = (const uint8_t *)(data + size_ethernet + size_ip + size_udp);
146
147         packet = malloc(sizeof(*packet));
148         if (!packet) {
149                 fprintf(stderr, "Out of memory\n");
150                 return;
151         }
152
153         memset(packet, 0, sizeof(*packet));
154         packet->src_ipaddr.af = AF_INET;
155         packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
156         packet->src_port = ntohs(udp->udp_sport);
157         packet->dst_ipaddr.af = AF_INET;
158         packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
159         packet->dst_port = ntohs(udp->udp_dport);
160
161         packet->data = payload;
162         packet->data_len = header->len - size_ethernet - size_ip - size_udp;
163
164         if (!rad_packet_ok(packet, 0)) {
165                 fr_perror("Packet");
166                 
167                 fprintf(stderr, "\tFrom:    %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
168                 fprintf(stderr, "\tTo:      %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
169                 fprintf(stderr, "\tType:    %s\n", fr_packet_codes[packet->code]);
170
171                 free(packet);
172                 return;
173         }
174
175         /*
176          *      Decode the data without bothering to check the signatures.
177          */
178         if (rad_decode(packet, NULL, radius_secret) != 0) {
179                 free(packet);
180                 fr_perror("decode");
181                 return;
182         }
183
184         if (filter_vps && filter_packet(packet)) {
185                 free(packet);
186                 DEBUG("Packet number %d doesn't match\n", count++);
187                 return;
188         }
189         printf("%s Id %d\t", fr_packet_codes[packet->code], packet->id);
190
191         /* Print the RADIUS packet */
192         printf("%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
193         printf("%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
194         if (fr_debug_flag) printf("\t(%d packets)", count++);
195         printf("\t%08x", header->ts.tv_sec);
196         printf("\n");
197         if (packet->vps != NULL) {
198                 vp_printlist(stdout, packet->vps);
199                 pairfree(&packet->vps);
200         }
201         printf("\n");
202         fflush(stdout);
203
204         /*
205          *      If we're doing filtering, Access-Requests are cached
206          *      in the filter tree.
207          */
208         if (!filter_vps ||
209             ((packet->code != PW_AUTHENTICATION_REQUEST) &&
210              (packet->code != PW_ACCOUNTING_REQUEST))) {
211                 free(packet);
212         }
213 }
214
215 static void NEVER_RETURNS usage(int status)
216 {
217         FILE *output = status ? stderr : stdout;
218         fprintf(output, "usage: radsniff [options]\n");
219         fprintf(output, "options:\n");
220         fprintf(output, "\t-c count\tNumber of packets to capture.\n");
221         fprintf(output, "\t-d directory\tDirectory where the dictionaries are found\n");
222         fprintf(output, "\t-f filter\tPCAP filter. (default is udp port 1812 or 1813 or 1814)\n");
223         fprintf(output, "\t-h\t\tPrint this help message.\n");
224         fprintf(output, "\t-i interface\tInterface to capture.\n");
225         fprintf(output, "\t-I filename\tRead packets from filename.\n");
226         fprintf(output, "\t-p port\tList for packets on port.\n");
227         fprintf(output, "\t-r filter\tRADIUS attribute filter.\n");
228         fprintf(output, "\t-s secret\tRADIUS secret.\n");
229         fprintf(output, "\t-X\t\tPrint out debugging information.\n");
230         exit(status);
231 }
232
233 int main(int argc, char *argv[])
234 {
235         char *dev;                      /* sniffing device */
236         char errbuf[PCAP_ERRBUF_SIZE];  /* error buffer */
237         pcap_t *descr;                  /* sniff handler */
238         struct bpf_program fp;          /* hold compiled program */
239         bpf_u_int32 maskp;              /* subnet mask */
240         bpf_u_int32 netp;               /* ip */
241         char buffer[1024];
242         char *pcap_filter = NULL;
243         char *radius_filter = NULL;
244         char *filename = NULL;
245         int packet_count = -1;          /* how many packets to sniff */
246         int opt;
247         FR_TOKEN parsecode;
248         const char *radius_dir = RADIUS_DIR;
249         int port = 1812;
250
251         /* Default device */
252         dev = pcap_lookupdev(errbuf);
253
254         /* Get options */
255         while ((opt = getopt(argc, argv, "c:d:f:hi:I:p:r:s:X")) != EOF) {
256                 switch (opt)
257                 {
258                 case 'c':
259                         packet_count = atoi(optarg);
260                         if (packet_count <= 0) {
261                                 fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg);
262                                 exit(1);
263                         }
264                         break;
265                 case 'd':
266                         radius_dir = optarg;
267                         break;
268                 case 'f':
269                         pcap_filter = optarg;
270                         break;
271                 case 'h':
272                         usage(0);
273                         break;
274                 case 'i':
275                         dev = optarg;
276                         break;
277                 case 'I':
278                         filename = optarg;
279                         break;
280                 case 'p':
281                         port = atoi(optarg);
282                         break;
283                 case 'r':
284                         radius_filter = optarg;
285                         break;
286                 case 's':
287                         radius_secret = optarg;
288                         break;
289                 case 'X':
290                         fr_debug_flag++;
291                         break;
292                 default:
293                         usage(1);
294                 }
295         }
296
297         if (!pcap_filter) {
298                 pcap_filter = buffer;
299                 snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d",
300                          port, port + 1, port + 2);
301         }
302
303         if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
304                 fr_perror("radsniff");
305                 return 1;
306         }
307
308         if (radius_filter) {
309                 parsecode = userparse(radius_filter, &filter_vps);
310                 if (parsecode == T_OP_INVALID) {
311                         fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\": %s\n", radius_filter, fr_strerror());
312                         exit(1);
313                 }
314                 if (!filter_vps) {
315                         fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter);
316                         exit(1);
317                 }
318
319                 filter_tree = rbtree_create((rbcmp) fr_packet_cmp,
320                                             free, 0);
321                 if (!filter_tree) {
322                         fprintf(stderr, "radsniff: Failed creating filter tree\n");
323                         exit(1);
324                 }
325         }
326
327         /* Set our device */
328         pcap_lookupnet(dev, &netp, &maskp, errbuf);
329
330         /* Print device to the user */
331         if (fr_debug_flag) {
332                 if (dev) printf("Device: [%s]\n", dev);
333                 if (packet_count > 0) {
334                         printf("Num of packets: [%d]\n",
335                                packet_count);
336                 }
337                 printf("PCAP filter: [%s]\n", pcap_filter);
338                 if (filter_vps) {
339                         printf("RADIUS filter:\n");
340                         vp_printlist(stdout, filter_vps);
341                 }
342                 printf("RADIUS secret: [%s]\n", radius_secret);
343         }
344
345         /* Open the device so we can spy */
346         if (filename) {
347                 descr = pcap_open_offline(filename, errbuf);
348         } else {
349                 descr = pcap_open_live(dev, SNAPLEN, 1, 0, errbuf);
350         }
351         if (descr == NULL)
352         {
353                 printf("radsniff: pcap_open_live failed (%s)\n", errbuf);
354                 exit(1);
355         }
356
357         /* Apply the rules */
358         if( pcap_compile(descr, &fp, pcap_filter, 0, netp) == -1)
359         {
360                 printf("radsniff: pcap_compile failed\n");
361                 exit(1);
362         }
363         if (pcap_setfilter(descr, &fp) == -1)
364         {
365                 printf("radsniff: pcap_setfilter failed\n");
366                 exit(1);
367         }
368
369         /* Now we can set our callback function */
370         pcap_loop(descr, packet_count, got_packet, NULL);
371         pcap_close(descr);
372
373         if (filter_tree) rbtree_free(filter_tree);
374
375         DEBUG("Done sniffing\n");
376         return 0;
377 }