2 * radsniff.c Display the RADIUS traffic on the network.
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.
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.
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
20 * Copyright 2006 The FreeRADIUS server project
21 * Copyright 2006 Nicolas Baradakis <nicolas.baradakis@cegetel.net>
24 #include <freeradius-devel/autoconf.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
35 #include <freeradius-devel/radpaths.h>
36 #include <freeradius-devel/conf.h>
37 #include <freeradius-devel/libradius.h>
38 #include <freeradius-devel/radsniff.h>
40 static const char *radius_secret = "testing123";
41 static VALUE_PAIR *filter_vps = NULL;
43 static const char *packet_codes[] = {
49 "Accounting-Response",
65 "Resource-Free-Request",
66 "Resource-Free-Response",
67 "Resource-Query-Request",
68 "Resource-Query-Response",
69 "Alternate-Resource-Reclaim-Request",
71 "NAS-Reboot-Response",
94 "IP-Address-Allocate",
99 * Stolen from rad_recv() in ../lib/radius.c
101 static RADIUS_PACKET *init_packet(const uint8_t *data, size_t data_len)
103 RADIUS_PACKET *packet;
107 radius_packet_t *hdr;
108 char host_ipaddr[128];
113 * Allocate the new request data structure
115 if ((packet = malloc(sizeof(*packet))) == NULL) {
116 librad_log("out of memory");
119 memset(packet, 0, sizeof(*packet));
121 /* packet->data_len = rad_recvfrom(fd, &packet->data, 0, */
122 /* &packet->src_ipaddr, &packet->src_port, */
123 /* &packet->dst_ipaddr, &packet->dst_port); */
126 packet->data_len = data_len;
129 * Check for socket errors.
131 /* if (packet->data_len < 0) { */
132 /* librad_log("Error receiving packet: %s", strerror(errno)); */
133 /* /\* packet->data is NULL *\/ */
139 * Fill IP header fields. We need these for the error
140 * messages which may come later.
142 /* packet->sockfd = fd; */
145 * FIXME: Do even more filtering by only permitting
146 * certain IP's. The problem is that we don't know
147 * how to do this properly for all possible clients...
151 * Explicitely set the VP list to empty.
156 * Check for packets smaller than the packet header.
158 * RFC 2865, Section 3., subsection 'length' says:
160 * "The minimum length is 20 ..."
162 if (packet->data_len < AUTH_HDR_LEN) {
163 librad_log("WARNING: Malformed RADIUS packet from host %s: too short (received %d < minimum %d)",
164 inet_ntop(packet->src_ipaddr.af,
165 &packet->src_ipaddr.ipaddr,
166 host_ipaddr, sizeof(host_ipaddr)),
167 packet->data_len, AUTH_HDR_LEN);
173 * RFC 2865, Section 3., subsection 'length' says:
175 * " ... and maximum length is 4096."
177 if (packet->data_len > MAX_RADIUS_LEN) {
178 librad_log("WARNING: Malformed RADIUS packet from host %s: too long (received %d > maximum %d)",
179 inet_ntop(packet->src_ipaddr.af,
180 &packet->src_ipaddr.ipaddr,
181 host_ipaddr, sizeof(host_ipaddr)),
182 packet->data_len, MAX_RADIUS_LEN);
188 * Check for packets with mismatched size.
189 * i.e. We've received 128 bytes, and the packet header
190 * says it's 256 bytes long.
192 totallen = (packet->data[2] << 8) | packet->data[3];
193 hdr = (radius_packet_t *)packet->data;
196 * Code of 0 is not understood.
197 * Code of 16 or greate is not understood.
199 if ((hdr->code == 0) ||
201 librad_log("WARNING: Bad RADIUS packet from host %s: unknown packet code %d",
202 inet_ntop(packet->src_ipaddr.af,
203 &packet->src_ipaddr.ipaddr,
204 host_ipaddr, sizeof(host_ipaddr)),
211 * Repeat the length checks. This time, instead of
212 * looking at the data we received, look at the value
213 * of the 'length' field inside of the packet.
215 * Check for packets smaller than the packet header.
217 * RFC 2865, Section 3., subsection 'length' says:
219 * "The minimum length is 20 ..."
221 if (totallen < AUTH_HDR_LEN) {
222 librad_log("WARNING: Malformed RADIUS packet from host %s: too short (length %d < minimum %d)",
223 inet_ntop(packet->src_ipaddr.af,
224 &packet->src_ipaddr.ipaddr,
225 host_ipaddr, sizeof(host_ipaddr)),
226 totallen, AUTH_HDR_LEN);
232 * And again, for the value of the 'length' field.
234 * RFC 2865, Section 3., subsection 'length' says:
236 * " ... and maximum length is 4096."
238 if (totallen > MAX_RADIUS_LEN) {
239 librad_log("WARNING: Malformed RADIUS packet from host %s: too long (length %d > maximum %d)",
240 inet_ntop(packet->src_ipaddr.af,
241 &packet->src_ipaddr.ipaddr,
242 host_ipaddr, sizeof(host_ipaddr)),
243 totallen, MAX_RADIUS_LEN);
249 * RFC 2865, Section 3., subsection 'length' says:
251 * "If the packet is shorter than the Length field
252 * indicates, it MUST be silently discarded."
254 * i.e. No response to the NAS.
256 if (packet->data_len < totallen) {
257 librad_log("WARNING: Malformed RADIUS packet from host %s: received %d octets, packet length says %d",
258 inet_ntop(packet->src_ipaddr.af,
259 &packet->src_ipaddr.ipaddr,
260 host_ipaddr, sizeof(host_ipaddr)),
261 packet->data_len, totallen);
267 * RFC 2865, Section 3., subsection 'length' says:
269 * "Octets outside the range of the Length field MUST be
270 * treated as padding and ignored on reception."
272 if (packet->data_len > totallen) {
274 * We're shortening the packet below, but just
275 * to be paranoid, zero out the extra data.
277 memset(packet->data + totallen, 0, packet->data_len - totallen);
278 packet->data_len = totallen;
282 * Walk through the packet's attributes, ensuring that
283 * they add up EXACTLY to the size of the packet.
285 * If they don't, then the attributes either under-fill
286 * or over-fill the packet. Any parsing of the packet
287 * is impossible, and will result in unknown side effects.
289 * This would ONLY happen with buggy RADIUS implementations,
290 * or with an intentional attack. Either way, we do NOT want
291 * to be vulnerable to this problem.
294 count = totallen - AUTH_HDR_LEN;
300 * Attribute number zero is NOT defined.
303 librad_log("WARNING: Malformed RADIUS packet from host %s: Invalid attribute 0",
304 inet_ntop(packet->src_ipaddr.af,
305 &packet->src_ipaddr.ipaddr,
306 host_ipaddr, sizeof(host_ipaddr)));
312 * Attributes are at LEAST as long as the ID & length
313 * fields. Anything shorter is an invalid attribute.
316 librad_log("WARNING: Malformed RADIUS packet from host %s: attribute %d too short",
317 inet_ntop(packet->src_ipaddr.af,
318 &packet->src_ipaddr.ipaddr,
319 host_ipaddr, sizeof(host_ipaddr)),
326 * Sanity check the attributes for length.
329 default: /* don't do anything by default */
333 seen_eap |= PW_EAP_MESSAGE;
336 case PW_MESSAGE_AUTHENTICATOR:
337 if (attr[1] != 2 + AUTH_VECTOR_LEN) {
338 librad_log("WARNING: Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d",
339 inet_ntop(packet->src_ipaddr.af,
340 &packet->src_ipaddr.ipaddr,
341 host_ipaddr, sizeof(host_ipaddr)),
346 seen_eap |= PW_MESSAGE_AUTHENTICATOR;
351 * FIXME: Look up the base 255 attributes in the
352 * dictionary, and switch over their type. For
353 * integer/date/ip, the attribute length SHOULD
356 count -= attr[1]; /* grab the attribute length */
358 num_attributes++; /* seen one more attribute */
362 * If the attributes add up to a packet, it's allowed.
364 * If not, we complain, and throw the packet away.
367 librad_log("WARNING: Malformed RADIUS packet from host %s: packet attributes do NOT exactly fill the packet",
368 inet_ntop(packet->src_ipaddr.af,
369 &packet->src_ipaddr.ipaddr,
370 host_ipaddr, sizeof(host_ipaddr)));
376 * If we're configured to look for a maximum number of
377 * attributes, and we've seen more than that maximum,
378 * then throw the packet away, as a possible DoS.
380 if ((librad_max_attributes > 0) &&
381 (num_attributes > librad_max_attributes)) {
382 librad_log("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
383 inet_ntop(packet->src_ipaddr.af,
384 &packet->src_ipaddr.ipaddr,
385 host_ipaddr, sizeof(host_ipaddr)),
386 num_attributes, librad_max_attributes);
392 * http://www.freeradius.org/rfc/rfc2869.html#EAP-Message
394 * A packet with an EAP-Message attribute MUST also have
395 * a Message-Authenticator attribute.
397 * A Message-Authenticator all by itself is OK, though.
400 (seen_eap != PW_MESSAGE_AUTHENTICATOR) &&
401 (seen_eap != (PW_EAP_MESSAGE | PW_MESSAGE_AUTHENTICATOR))) {
402 librad_log("WARNING: Insecure packet from host %s: Received EAP-Message with no Message-Authenticator.",
403 inet_ntop(packet->src_ipaddr.af,
404 &packet->src_ipaddr.ipaddr,
405 host_ipaddr, sizeof(host_ipaddr)));
411 if ((hdr->code > 0) && (hdr->code < 52)) {
412 printf("rad_recv: %s packet from host %s port %d",
413 packet_codes[hdr->code],
414 inet_ntop(packet->src_ipaddr.af,
415 &packet->src_ipaddr.ipaddr,
416 host_ipaddr, sizeof(host_ipaddr)),
419 printf("rad_recv: Packet from host %s port %d code=%d",
420 inet_ntop(packet->src_ipaddr.af,
421 &packet->src_ipaddr.ipaddr,
422 host_ipaddr, sizeof(host_ipaddr)),
426 printf(", id=%d, length=%d\n", hdr->id, totallen);
430 * Fill RADIUS header fields
432 packet->code = hdr->code;
433 packet->id = hdr->id;
434 memcpy(packet->vector, hdr->vector, AUTH_VECTOR_LEN);
440 * Stolen from rad_decode() in ../lib/radius.c
442 static int decode_packet(RADIUS_PACKET *packet, const char *secret)
453 radius_packet_t *hdr;
454 int vsa_tlen, vsa_llen;
455 DICT_VENDOR *dv = NULL;
457 /* if (rad_verify(packet, original, secret) < 0) return -1; */
460 * Extract attribute-value pairs
462 hdr = (radius_packet_t *)packet->data;
464 packet_length = packet->data_len - AUTH_HDR_LEN;
467 * There may be VP's already in the packet. Don't
470 for (tail = &packet->vps; *tail != NULL; tail = &((*tail)->next)) {
476 vsa_tlen = vsa_llen = 1;
479 * We have to read at least two bytes.
481 * rad_recv() above ensures that this is OK.
483 while (packet_length > 0) {
488 * Normal attribute, handle it like normal.
490 if (vendorcode == 0) {
492 * No room to read attr/length,
493 * or bad attribute, or attribute is
494 * too short, or attribute is too long,
495 * stop processing the packet.
497 if ((packet_length < 2) ||
498 (ptr[0] == 0) || (ptr[1] < 2) ||
499 (ptr[1] > packet_length)) break;
507 if (attribute != PW_VENDOR_SPECIFIC) goto create_pair;
510 * No vendor code, or ONLY vendor code.
512 if (attrlen <= 4) goto create_pair;
518 * Handle Vendor-Specific
520 if (vendorlen == 0) {
526 * attrlen was checked above.
528 memcpy(&lvalue, ptr, 4);
529 myvendor = ntohl(lvalue);
532 * Zero isn't allowed.
534 if (myvendor == 0) goto create_pair;
537 * This is an implementation issue.
538 * We currently pack vendor into the upper
539 * 16 bits of a 32-bit attribute number,
540 * so we can't handle vendor numbers larger
543 if (myvendor > 65535) goto create_pair;
545 vsa_tlen = vsa_llen = 1;
546 dv = dict_vendorbyvalue(myvendor);
549 vsa_llen = dv->length;
553 * Sweep through the list of VSA's,
554 * seeing if they exactly fill the
555 * outer Vendor-Specific attribute.
557 * If not, create a raw Vendor-Specific.
560 sublen = attrlen - 4;
563 * See if we can parse it.
569 * Don't have a type, it's bad.
571 if (sublen < vsa_tlen) goto create_pair;
574 * Ensure that the attribute number
583 myattr = (subptr[0] << 8) | subptr[1];
587 if ((subptr[0] != 0) ||
588 (subptr[1] != 0)) goto create_pair;
590 myattr = (subptr[2] << 8) | subptr[3];
594 * Our dictionary is broken.
601 * Not enough room for one more
604 if (sublen < vsa_tlen + vsa_llen) goto create_pair;
607 attribute = (myvendor << 16) | myattr;
609 attrlen -= (4 + vsa_tlen);
610 packet_length -= 4 + vsa_tlen;
614 if (subptr[vsa_tlen] < (vsa_tlen + vsa_llen))
617 if (subptr[vsa_tlen] > sublen)
619 sublen -= subptr[vsa_tlen];
620 subptr += subptr[vsa_tlen];
624 if (subptr[vsa_tlen] != 0) goto create_pair;
625 if (subptr[vsa_tlen + 1] < (vsa_tlen + vsa_llen))
627 if (subptr[vsa_tlen + 1] > sublen)
629 sublen -= subptr[vsa_tlen + 1];
630 subptr += subptr[vsa_tlen + 1];
634 * Our dictionaries are
640 } while (sublen > 0);
642 vendorcode = myvendor;
643 vendorlen = attrlen - 4;
650 * attrlen is the length of this attribute.
651 * total_len is the length of the encompassing
660 attribute = (ptr[0] << 8) | ptr[1];
663 default: /* can't hit this. */
666 attribute |= (vendorcode << 16);
671 attrlen = ptr[0] - (vsa_tlen + vsa_llen);
675 attrlen = ptr[1] - (vsa_tlen + vsa_llen);
678 default: /* can't hit this. */
682 vendorlen -= vsa_tlen + vsa_llen + attrlen;
683 if (vendorlen == 0) vendorcode = 0;
684 packet_length -= (vsa_tlen + vsa_llen);
687 * Create the attribute, setting the default type
688 * to 'octects'. If the type in the dictionary
689 * is different, then the dictionary type will
690 * over-ride this one.
693 /* pair = rad_attr2vp(packet, original, secret, */
694 /* attribute, attrlen, ptr); */
695 pair = rad_attr2vp(packet, NULL, secret,
696 attribute, attrlen, ptr);
698 pairfree(&packet->vps);
699 librad_log("out of memory");
708 packet_length -= attrlen;
712 * Merge information from the outside world into our
715 lrad_rand_seed(packet->data, AUTH_HDR_LEN);
720 static int filter_packet(RADIUS_PACKET *packet)
722 VALUE_PAIR *check_item;
724 unsigned int pass, fail;
728 for (vp = packet->vps; vp != NULL; vp = vp->next) {
729 for (check_item = filter_vps;
731 check_item = check_item->next)
732 if ((check_item->attribute == vp->attribute)
733 && (check_item->operator != T_OP_SET)) {
734 compare = paircmp(check_item, vp);
741 if (fail == 0 && pass != 0) {
748 static void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
750 /* Just a counter of how many packets we've had */
751 static int count = 1;
752 /* Define pointers for packet's attributes */
753 const struct ethernet_header *ethernet; /* The ethernet header */
754 const struct ip_header *ip; /* The IP header */
755 const struct udp_header *udp; /* The UDP header */
756 const char *payload; /* Packet payload */
757 /* And define the size of the structures we're using */
758 int size_ethernet = sizeof(struct ethernet_header);
759 int size_ip = sizeof(struct ip_header);
760 int size_udp = sizeof(struct udp_header);
762 RADIUS_PACKET *request;
764 /* Define our packet's attributes */
765 ethernet = (struct ethernet_header*)(packet);
766 ip = (struct ip_header*)(packet + size_ethernet);
767 udp = (struct udp_header*)(packet + size_ethernet + size_ip);
768 payload = (u_char *)(packet + size_ethernet + size_ip + size_udp);
770 /* Read the RADIUS packet structure */
771 request = init_packet(payload, header->len - size_ethernet - size_ip - size_udp);
772 if (request == NULL) {
773 librad_perror("check");
776 request->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
777 request->src_port = ntohs(udp->udp_sport);
778 request->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
779 request->dst_port = ntohs(udp->udp_dport);
780 if (decode_packet(request, radius_secret) != 0) {
781 librad_perror("decode");
784 if (filter_vps && filter_packet(request)) {
785 /* printf("Packet number %d doesn't match\n", count++); */
789 /* Print the RADIUS packet */
790 printf("Packet number %d has just been sniffed\n", count++);
791 printf("\tFrom: %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
792 printf("\tTo: %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
793 printf("\tType: %s\n", packet_codes[request->code]);
794 if (request->vps != NULL) {
795 vp_printlist(stdout, request->vps);
796 pairfree(&request->vps);
801 static void NEVER_RETURNS usage(int status)
803 FILE *output = status ? stderr : stdout;
804 fprintf(output, "usage: radsniff [options]\n");
805 fprintf(output, "options:\n");
806 fprintf(output, "\t-c count\tNumber of packets to capture.\n");
807 fprintf(output, "\t-f filter\tPCAP filter. (default is udp port 1812 or 1813 or 1814)\n");
808 fprintf(output, "\t-h\t\tPrint this help message.\n");
809 fprintf(output, "\t-i interface\tInterface to capture.\n");
810 fprintf(output, "\t-r filter\tRADIUS filter.\n");
811 fprintf(output, "\t-s secret\tRADIUS secret.\n");
815 int main(int argc, char *argv[])
817 char *dev; /* sniffing device */
818 char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */
819 pcap_t *descr; /* sniff handler */
820 struct bpf_program fp; /* hold compiled program */
821 bpf_u_int32 maskp; /* subnet mask */
822 bpf_u_int32 netp; /* ip */
823 char *pcap_filter = "udp port 1812 or 1813 or 1814";
824 char *radius_filter = NULL;
825 int packet_count = -1; /* how many packets to sniff */
827 LRAD_TOKEN parsecode;
830 const char radius_dir[] = RADIUS_DIR;
831 if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
832 librad_perror("radsniff");
837 dev = pcap_lookupdev(errbuf);
840 while ((opt = getopt(argc, argv, "c:f:hi:r:s:")) != EOF) {
844 packet_count = atoi(optarg);
845 if (packet_count <= 0) {
846 fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg);
851 pcap_filter = optarg;
860 radius_filter = optarg;
861 parsecode = userparse(radius_filter, &filter_vps);
862 if (parsecode == T_OP_INVALID || filter_vps == NULL) {
863 fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\"\n", optarg);
868 radius_secret = optarg;
876 pcap_lookupnet(dev, &netp, &maskp, errbuf);
878 /* Print device to the user */
879 printf("Device: [%s]\n", dev);
880 if (packet_count > 0) {
881 printf("Num of packets: [%d]\n", packet_count);
883 printf("PCAP filter: [%s]\n", pcap_filter);
884 if (filter_vps != NULL) {
885 printf("RADIUS filter:\n");
886 vp_printlist(stdout, filter_vps);
888 printf("RADIUS secret: [%s]\n", radius_secret);
890 /* Open the device so we can spy */
891 descr = pcap_open_live(dev, SNAPLEN, 1, 0, errbuf);
894 printf("radsniff: pcap_open_live failed (%s)\n", errbuf);
898 /* Apply the rules */
899 if( pcap_compile(descr, &fp, pcap_filter, 0, netp) == -1)
901 printf("radsniff: pcap_compile failed\n");
904 if (pcap_setfilter(descr, &fp) == -1)
906 printf("radsniff: pcap_setfilter failed\n");
910 /* Now we can set our callback function */
911 pcap_loop(descr, packet_count, got_packet, NULL);
914 printf("Done sniffing\n");