Pull rad_verify() out of rad_decode(), and update the callers
[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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  *
20  *  Copyright 2006  The FreeRADIUS server project
21  *  Copyright 2006  Nicolas Baradakis <nicolas.baradakis@cegetel.net>
22  */
23
24 #include <freeradius-devel/autoconf.h>
25
26 #include <pcap.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <unistd.h>
33
34 #define _LIBRADIUS 1
35 #include <freeradius-devel/radpaths.h>
36 #include <freeradius-devel/conf.h>
37 #include <freeradius-devel/libradius.h>
38 #include <freeradius-devel/radsniff.h>
39
40 static const char *radius_secret = "testing123";
41 static VALUE_PAIR *filter_vps = NULL;
42
43 static const char *packet_codes[] = {
44   "",
45   "Access-Request",
46   "Access-Accept",
47   "Access-Reject",
48   "Accounting-Request",
49   "Accounting-Response",
50   "Accounting-Status",
51   "Password-Request",
52   "Password-Accept",
53   "Password-Reject",
54   "Accounting-Message",
55   "Access-Challenge",
56   "Status-Server",
57   "Status-Client",
58   "14",
59   "15",
60   "16",
61   "17",
62   "18",
63   "19",
64   "20",
65   "Resource-Free-Request",
66   "Resource-Free-Response",
67   "Resource-Query-Request",
68   "Resource-Query-Response",
69   "Alternate-Resource-Reclaim-Request",
70   "NAS-Reboot-Request",
71   "NAS-Reboot-Response",
72   "28",
73   "Next-Passcode",
74   "New-Pin",
75   "Terminate-Session",
76   "Password-Expired",
77   "Event-Request",
78   "Event-Response",
79   "35",
80   "36",
81   "37",
82   "38",
83   "39",
84   "Disconnect-Request",
85   "Disconnect-ACK",
86   "Disconnect-NAK",
87   "CoF-Request",
88   "CoF-ACK",
89   "CoF-NAK",
90   "46",
91   "47",
92   "48",
93   "49",
94   "IP-Address-Allocate",
95   "IP-Address-Release"
96 };
97
98 /*
99  *      Stolen from rad_recv() in ../lib/radius.c
100  */
101 static RADIUS_PACKET *init_packet(const uint8_t *data, size_t data_len)
102 {
103         RADIUS_PACKET           *packet;
104
105         /*
106          *      Allocate the new request data structure
107          */
108         if ((packet = malloc(sizeof(*packet))) == NULL) {
109                 librad_log("out of memory");
110                 return NULL;
111         }
112         memset(packet, 0, sizeof(*packet));
113
114         packet->data = data;
115         packet->data_len = data_len;
116
117         if (!rad_packet_ok(packet)) {
118                 rad_free(&packet);
119                 return NULL;
120         }
121
122         /*
123          *      Explicitely set the VP list to empty.
124          */
125         packet->vps = NULL;
126
127         return packet;
128 }
129
130 static int filter_packet(RADIUS_PACKET *packet)
131 {
132         VALUE_PAIR *check_item;
133         VALUE_PAIR *vp;
134         unsigned int pass, fail;
135         int compare;
136
137         pass = fail = 0;
138         for (vp = packet->vps; vp != NULL; vp = vp->next) {
139                 for (check_item = filter_vps;
140                      check_item != NULL;
141                      check_item = check_item->next)
142                         if ((check_item->attribute == vp->attribute)
143                          && (check_item->operator != T_OP_SET)) {
144                                 compare = paircmp(check_item, vp);
145                                 if (compare == 1)
146                                         pass++;
147                                 else
148                                         fail++;
149                         }
150         }
151         if (fail == 0 && pass != 0) {
152                 return 0;
153         }
154
155         return 1;
156 }
157
158 static void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
159 {
160         /* Just a counter of how many packets we've had */
161         static int count = 1;
162         /* Define pointers for packet's attributes */
163         const struct ethernet_header *ethernet;  /* The ethernet header */
164         const struct ip_header *ip;              /* The IP header */
165         const struct udp_header *udp;            /* The UDP header */
166         const char *payload;                     /* Packet payload */
167         /* And define the size of the structures we're using */
168         int size_ethernet = sizeof(struct ethernet_header);
169         int size_ip = sizeof(struct ip_header);
170         int size_udp = sizeof(struct udp_header);
171         /* For FreeRADIUS */
172         RADIUS_PACKET *request;
173
174         /* Define our packet's attributes */
175         ethernet = (const struct ethernet_header*)(packet);
176         ip = (const struct ip_header*)(packet + size_ethernet);
177         udp = (const struct udp_header*)(packet + size_ethernet + size_ip);
178         payload = (const u_char *)(packet + size_ethernet + size_ip + size_udp);
179
180         /* Read the RADIUS packet structure */
181         request = init_packet(payload, header->len - size_ethernet - size_ip - size_udp);
182         if (request == NULL) {
183                 librad_perror("check");
184                 return;
185         }
186         request->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
187         request->src_port = ntohs(udp->udp_sport);
188         request->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
189         request->dst_port = ntohs(udp->udp_dport);
190
191         /*
192          *      Decode the data without bothering to check the signatures.
193          */
194         if (rad_decode(request, NULL, radius_secret) != 0) {
195                 librad_perror("decode");
196                 return;
197         }
198         if (filter_vps && filter_packet(request)) {
199                 /* printf("Packet number %d doesn't match\n", count++); */
200                 return;
201         }
202
203         /* Print the RADIUS packet */
204         printf("Packet number %d has just been sniffed\n", count++);
205         printf("\tFrom:    %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
206         printf("\tTo:      %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
207         printf("\tType:    %s\n", packet_codes[request->code]);
208         if (request->vps != NULL) {
209                 vp_printlist(stdout, request->vps);
210                 pairfree(&request->vps);
211         }
212         free(request);
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-r filter\tRADIUS filter.\n");
226         fprintf(output, "\t-s secret\tRADIUS secret.\n");
227         exit(status);
228 }
229
230 int main(int argc, char *argv[])
231 {
232         char *dev;                      /* sniffing device */
233         char errbuf[PCAP_ERRBUF_SIZE];  /* error buffer */
234         pcap_t *descr;                  /* sniff handler */
235         struct bpf_program fp;          /* hold compiled program */
236         bpf_u_int32 maskp;              /* subnet mask */
237         bpf_u_int32 netp;               /* ip */
238         const char *pcap_filter = "udp port 1812 or 1813 or 1814";
239         char *radius_filter = NULL;
240         int packet_count = -1;          /* how many packets to sniff */
241         int opt;
242         LRAD_TOKEN parsecode;
243         char *radius_dir = RADIUS_DIR;
244
245         /* Default device */
246         dev = pcap_lookupdev(errbuf);
247
248         /* Get options */
249         while ((opt = getopt(argc, argv, "c:d:f:hi:r:s:")) != EOF) {
250                 switch (opt)
251                 {
252                 case 'c':
253                         packet_count = atoi(optarg);
254                         if (packet_count <= 0) {
255                                 fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg);
256                                 exit(1);
257                         }
258                         break;
259                 case 'd':
260                         radius_dir = optarg;
261                         break;
262                 case 'f':
263                         pcap_filter = optarg;
264                         break;
265                 case 'h':
266                         usage(0);
267                         break;
268                 case 'i':
269                         dev = optarg;
270                         break;
271                 case 'r':
272                         radius_filter = optarg;
273                         parsecode = userparse(radius_filter, &filter_vps);
274                         if (parsecode == T_OP_INVALID || filter_vps == NULL) {
275                                 fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\"\n", optarg);
276                                 exit(1);
277                         }
278                         break;
279                 case 's':
280                         radius_secret = optarg;
281                         break;
282                 default:
283                         usage(1);
284                 }
285         }
286
287         if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
288                 librad_perror("radsniff");
289                 return 1;
290         }
291         /* Set our device */
292         pcap_lookupnet(dev, &netp, &maskp, errbuf);
293
294         /* Print device to the user */
295         printf("Device: [%s]\n", dev);
296         if (packet_count > 0) {
297                 printf("Num of packets: [%d]\n", packet_count);
298         }
299         printf("PCAP filter: [%s]\n", pcap_filter);
300         if (filter_vps != NULL) {
301                 printf("RADIUS filter:\n");
302                 vp_printlist(stdout, filter_vps);
303         }
304         printf("RADIUS secret: [%s]\n", radius_secret);
305
306         /* Open the device so we can spy */
307         descr = pcap_open_live(dev, SNAPLEN, 1, 0, errbuf);
308         if (descr == NULL)
309         {
310                 printf("radsniff: pcap_open_live failed (%s)\n", errbuf);
311                 exit(1);
312         }
313
314         /* Apply the rules */
315         if( pcap_compile(descr, &fp, pcap_filter, 0, netp) == -1)
316         {
317                 printf("radsniff: pcap_compile failed\n");
318                 exit(1);
319         }
320         if (pcap_setfilter(descr, &fp) == -1)
321         {
322                 printf("radsniff: pcap_setfilter failed\n");
323                 exit(1);
324         }
325
326         /* Now we can set our callback function */
327         pcap_loop(descr, packet_count, got_packet, NULL);
328         pcap_close(descr);
329
330         printf("Done sniffing\n");
331         return 0;
332 }