fbd213cf9da8e0a38af1fe256768fefac8ca6218
[freeradius.git] / src / lib / vqp.c
1 /*
2  * vqp.c        Functions to send/receive VQP packets.
3  *
4  * Version:     $Id$
5  *
6  *   This library is free software; you can redistribute it and/or
7  *   modify it under the terms of the GNU Lesser General Public
8  *   License as published by the Free Software Foundation; either
9  *   version 2.1 of the License, or (at your option) any later version.
10  *
11  *   This library 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 GNU
14  *   Lesser General Public License for more details.
15  *
16  *   You should have received a copy of the GNU Lesser General Public
17  *   License along with this library; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2007 Alan DeKok <aland@deployingradius.com>
21  */
22
23 #include        <freeradius-devel/ident.h>
24 RCSID("$Id$");
25
26 #include        <freeradius-devel/libradius.h>
27 #include        <freeradius-devel/udpfromto.h>
28 #include        <freeradius-devel/vqp.h>
29
30 #ifdef WITH_VMPS
31
32 #define MAX_VMPS_LEN (MAX_STRING_LEN - 1)
33
34 /*
35  *  http://www.openbsd.org/cgi-bin/cvsweb/src/usr.sbin/tcpdump/print-vqp.c
36  *
37  *  Some of how it works:
38  *
39  *  http://www.hackingciscoexposed.com/pdf/chapter12.pdf
40  *
41  * VLAN Query Protocol (VQP)
42  *
43  *    0                   1                   2                   3
44  *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
45  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46  *   |    Version    |    Opcode     | Response Code |  Data Count   |
47  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48  *   |                         Transaction ID                        |
49  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50  *   |                            Type (1)                           |
51  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52  *   |             Length            |            Data               /
53  *   /                                                               /
54  *   /                                                               /
55  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
56  *   |                            Type (n)                           |
57  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
58  *   |             Length            |            Data               /
59  *   /                                                               /
60  *   /                                                               /
61  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
62  *
63  * VQP is layered over UDP.  The default destination port is 1589.
64  *
65  */
66 #define VQP_HDR_LEN (8)
67 #define VQP_VERSION (1)
68 #define VQP_MAX_ATTRIBUTES (12)
69
70
71 /*
72  *      Wrapper for sendto which handles sendfromto, IPv6, and all
73  *      possible combinations.
74  *
75  *      FIXME:  This is just a copy of rad_sendto().
76  *      Duplicate code is bad.
77  */
78 static int vqp_sendto(int sockfd, void *data, size_t data_len, int flags,
79                       fr_ipaddr_t *src_ipaddr, fr_ipaddr_t *dst_ipaddr,
80                       int dst_port)
81 {
82         struct sockaddr_storage dst;
83         socklen_t               sizeof_dst;
84
85 #ifdef WITH_UDPFROMTO
86         struct sockaddr_storage src;
87         socklen_t               sizeof_src;
88
89         if (!fr_ipaddr2sockaddr(src_ipaddr, 0, &src, &sizeof_src)) {
90                 return -1;   /* Unknown address family, Die Die Die! */ 
91         }
92 #else
93         src_ipaddr = src_ipaddr; /* -Wunused */
94 #endif
95
96         if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &dst, &sizeof_dst)) {
97                 return -1;   /* Unknown address family, Die Die Die! */
98         }
99
100 #ifdef WITH_UDPFROMTO
101         /*
102          *      Only IPv4 is supported for udpfromto.
103          *
104          *      And if they don't specify a source IP address, don't
105          *      use udpfromto.
106          */
107         if ((dst_ipaddr->af == AF_INET) &&
108             (src_ipaddr->af != AF_UNSPEC)) {
109                 return sendfromto(sockfd, data, data_len, flags,
110                                   (struct sockaddr *)&src, sizeof_src, 
111                                   (struct sockaddr *)&dst, sizeof_dst);
112         }
113 #else
114         src_ipaddr = src_ipaddr; /* -Wunused */
115 #endif
116
117         /*
118          *      No udpfromto, OR an IPv6 socket, fail gracefully.
119          */
120         return sendto(sockfd, data, data_len, flags, 
121                       (struct sockaddr *)&dst, sizeof_dst);
122 }
123
124 /*
125  *      Wrapper for recvfrom, which handles recvfromto, IPv6, and all
126  *      possible combinations.
127  *
128  *      FIXME:  This is copied from rad_recvfrom, with minor edits.
129  */
130 static ssize_t vqp_recvfrom(int sockfd, uint8_t **pbuf, int flags,
131                             fr_ipaddr_t *src_ipaddr, uint16_t *src_port,
132                             fr_ipaddr_t *dst_ipaddr, uint16_t *dst_port)
133 {
134         struct sockaddr_storage src;
135         struct sockaddr_storage dst;
136         socklen_t               sizeof_src = sizeof(src);
137         socklen_t               sizeof_dst = sizeof(dst);
138         ssize_t                 data_len;
139         uint8_t                 header[4];
140         void                    *buf;
141         size_t                  len;
142         int                     port;
143
144         memset(&src, 0, sizeof_src);
145         memset(&dst, 0, sizeof_dst);
146
147         /*
148          *      Get address family, etc. first, so we know if we
149          *      need to do udpfromto.
150          *
151          *      FIXME: udpfromto also does this, but it's not
152          *      a critical problem.
153          */
154         if (getsockname(sockfd, (struct sockaddr *)&dst,
155                         &sizeof_dst) < 0) return -1;
156
157         /*
158          *      Read the length of the packet, from the packet.
159          *      This lets us allocate the buffer to use for
160          *      reading the rest of the packet.
161          */
162         data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK,
163                             (struct sockaddr *)&src, &sizeof_src);
164         if (data_len < 0) return -1;
165
166         /*
167          *      Too little data is available, discard the packet.
168          */
169         if (data_len < 4) {
170                 rad_recv_discard(sockfd);
171                 
172                 return 0;
173
174         /*
175          *      Invalid version, packet type, or too many
176          *      attributes.  Die.
177          */
178         } else if ((header[0] != VQP_VERSION) ||
179                    (header[1] < 1) ||
180                    (header[1] > 4) ||
181                    (header[3] > VQP_MAX_ATTRIBUTES)) {
182                 rad_recv_discard(sockfd);
183                          
184                 return 0;
185
186         } else {                /* we got 4 bytes of data. */
187                 /*
188                  *      We don't care about the contents for now...
189                  */
190 #if 0
191                 /*
192                  *      How many attributes are in the packet.
193                  */
194                 len = header[3];
195
196                 if ((header[1] == 1) || (header[1] == 3)) {
197                         if (len != VQP_MAX_ATTRIBUTES) {
198                                 rad_recv_discard(sockfd);
199                                 
200                                 return 0;
201                         }
202                         /*
203                          *      Maximum length we support.
204                          */
205                         len = (12 * (4 + 4 + MAX_VMPS_LEN));
206
207                 } else {
208                         if (len != 2) {
209                                 rad_recv_discard(sockfd);
210                                 
211                                 return 0;
212                         }
213                         /*
214                          *      Maximum length we support.
215                          */
216                         len = (12 * (4 + 4 + MAX_VMPS_LEN));
217                 }
218 #endif
219         }
220
221         /*
222          *      For now, be generous.
223          */
224         len = (12 * (4 + 4 + MAX_VMPS_LEN));
225
226         buf = malloc(len);
227         if (!buf) return -1;
228
229         /*
230          *      Receive the packet.  The OS will discard any data in the
231          *      packet after "len" bytes.
232          */
233 #ifdef WITH_UDPFROMTO
234         if (dst.ss_family == AF_INET) {
235                 data_len = recvfromto(sockfd, buf, len, flags,
236                                       (struct sockaddr *)&src, &sizeof_src, 
237                                       (struct sockaddr *)&dst, &sizeof_dst);
238         } else
239 #endif
240                 /*
241                  *      No udpfromto, OR an IPv6 socket.  Fail gracefully.
242                  */
243                 data_len = recvfrom(sockfd, buf, len, flags, 
244                                     (struct sockaddr *)&src, &sizeof_src);
245         if (data_len < 0) {
246                 free(buf);
247                 return data_len;
248         }
249
250         if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, &port)) {
251                 free(buf);
252                 return -1;      /* Unknown address family, Die Die Die! */
253         }
254         *src_port = port;
255
256         fr_sockaddr2ipaddr(&dst, sizeof_dst, dst_ipaddr, &port);
257         *dst_port = port;
258
259         /*
260          *      Different address families should never happen.
261          */
262         if (src.ss_family != dst.ss_family) {
263                 free(buf);
264                 return -1;
265         }
266
267         /*
268          *      Tell the caller about the data
269          */
270         *pbuf = buf;
271
272         return data_len;
273 }
274
275 RADIUS_PACKET *vqp_recv(int sockfd)
276 {
277         uint8_t *ptr;
278         ssize_t length;
279         uint32_t id;
280         RADIUS_PACKET *packet;
281
282         /*
283          *      Allocate the new request data structure
284          */
285         if ((packet = malloc(sizeof(*packet))) == NULL) {
286                 fr_strerror_printf("out of memory");
287                 return NULL;
288         }
289         memset(packet, 0, sizeof(*packet));
290
291         length = vqp_recvfrom(sockfd, &packet->data, 0,
292                                         &packet->src_ipaddr, &packet->src_port,
293                                         &packet->dst_ipaddr, &packet->dst_port);
294
295         /*
296          *      Check for socket errors.
297          */
298         if (length < 0) {
299                 fr_strerror_printf("Error receiving packet: %s", strerror(errno));
300                 /* packet->data is NULL */
301                 free(packet);
302                 return NULL;
303         }
304         packet->data_len = length; /* unsigned vs signed */
305
306         /*
307          *      We can only receive packets formatted in a way we
308          *      expect.  However, we accept MORE attributes in a
309          *      packet than normal implementations may send.
310          */
311         if (packet->data_len < VQP_HDR_LEN) {
312                 fr_strerror_printf("VQP packet is too short");
313                 rad_free(&packet);
314                 return NULL;
315         }
316
317         ptr = packet->data;
318
319         if (0) {
320                 size_t i;
321                 for (i = 0; i < packet->data_len; i++) {
322                   if ((i & 0x0f) == 0) fprintf(stderr, "%02x: ", (int) i);
323                         fprintf(stderr, "%02x ", ptr[i]);
324                         if ((i & 0x0f) == 0x0f) fprintf(stderr, "\n");
325                 }
326           
327         }
328
329         if (ptr[3] > VQP_MAX_ATTRIBUTES) {
330                 fr_strerror_printf("Too many VQP attributes");
331                 rad_free(&packet);
332                 return NULL;
333         }
334
335         if (packet->data_len > VQP_HDR_LEN) {
336                 int attrlen;
337
338                 /*
339                  *      Skip the header.
340                  */
341                 ptr += VQP_HDR_LEN;
342                 length = packet->data_len - VQP_HDR_LEN;
343
344                 while (length > 0) {
345                         if (length < 7) {
346                                 fr_strerror_printf("Packet contains malformed attribute");
347                                 rad_free(&packet);
348                                 return NULL;
349                         }
350
351                         /*
352                          *      Attributes are 4 bytes
353                          *      0x00000c01 ... 0x00000c08
354                          */
355                         if ((ptr[0] != 0) || (ptr[1] != 0) ||
356                             (ptr[2] != 0x0c) || (ptr[3] < 1) || (ptr[3] > 8)) {
357                                 fr_strerror_printf("Packet contains invalid attribute");
358                                 rad_free(&packet);
359                                 return NULL;
360                         }
361
362                         /*
363                          *      Length is 2 bytes
364                          *
365                          *      We support lengths 1..253, for internal
366                          *      server reasons.  Also, there's no reason
367                          *      for bigger lengths to exist... admins
368                          *      won't be typing in a 32K vlan name.
369                          *
370                          *      Except for received ethernet frames...
371                          *      they get chopped to 253 internally.
372                          */
373                         if ((ptr[3] != 5) &&
374                             ((ptr[4] != 0) || (ptr[5] > MAX_VMPS_LEN))) {
375                                 fr_strerror_printf("Packet contains attribute with invalid length %02x %02x", ptr[4], ptr[5]);
376                                 rad_free(&packet);
377                                 return NULL;
378                         }
379                         attrlen = (ptr[4] << 8) | ptr[5];
380                         ptr += 6 + attrlen;
381                         length -= (6 + attrlen);
382                 }
383         }
384
385         packet->sockfd = sockfd;
386         packet->vps = NULL;
387
388         /*
389          *      This is more than a bit of a hack.
390          */
391         packet->code = PW_AUTHENTICATION_REQUEST;
392
393         memcpy(&id, packet->data + 4, 4);
394         packet->id = ntohl(id);
395
396         /*
397          *      FIXME: Create a fake "request authenticator", to
398          *      avoid duplicates?  Or is the VQP sequence number
399          *      adequate for this purpose?
400          */
401
402         return packet;
403 }
404
405 /*
406  *      We do NOT  mirror the old-style RADIUS code  that does encode,
407  *      sign && send in one function.  For VQP, the caller MUST perform
408  *      each task manually, and separately.
409  */
410 int vqp_send(RADIUS_PACKET *packet)
411 {
412         if (!packet || !packet->data || (packet->data_len < 8)) return -1;
413
414         /*
415          *      Don't print out the attributes, they were printed out
416          *      when it was encoded.
417          */
418
419         /*
420          *      And send it on it's way.
421          */
422         return vqp_sendto(packet->sockfd, packet->data, packet->data_len, 0,
423                           &packet->src_ipaddr, &packet->dst_ipaddr,
424                           packet->dst_port);
425 }
426
427
428 int vqp_decode(RADIUS_PACKET *packet)
429 {
430         uint8_t *ptr, *end;
431         int attribute, length;
432         VALUE_PAIR *vp, **tail;
433
434         if (!packet || !packet->data) return -1;
435
436         if (packet->data_len < VQP_HDR_LEN) return -1;
437
438         tail = &packet->vps;
439
440         vp = paircreate(PW_VQP_PACKET_TYPE, 0);
441         if (!vp) {
442                 fr_strerror_printf("No memory");
443                 return -1;
444         }
445         vp->vp_integer = packet->data[1];
446         debug_pair(vp);
447
448         *tail = vp;
449         tail = &(vp->next);
450
451         vp = paircreate(PW_VQP_ERROR_CODE, 0);
452         if (!vp) {
453                 fr_strerror_printf("No memory");
454                 return -1;
455         }
456         vp->vp_integer = packet->data[2];
457         debug_pair(vp);
458
459         *tail = vp;
460         tail = &(vp->next);
461
462         vp = paircreate(PW_VQP_SEQUENCE_NUMBER, 0);
463         if (!vp) {
464                 fr_strerror_printf("No memory");
465                 return -1;
466         }
467         vp->vp_integer = packet->id; /* already set by vqp_recv */
468         debug_pair(vp);
469
470         *tail = vp;
471         tail = &(vp->next);
472
473         ptr = packet->data + VQP_HDR_LEN;
474         end = packet->data + packet->data_len;
475
476         /*
477          *      Note that vqp_recv() MUST ensure that the packet is
478          *      formatted in a way we expect, and that vqp_recv() MUST
479          *      be called before vqp_decode().
480          */
481         while (ptr < end) {
482                 attribute = (ptr[2] << 8) | ptr[3];
483                 length = (ptr[4] << 8) | ptr[5];
484                 ptr += 6;
485
486                 /*
487                  *      Hack to get the dictionaries to work correctly.
488                  */
489                 attribute |= 0x2000;
490                 vp = paircreate(attribute, 0);
491                 if (!vp) {
492                         pairfree(&packet->vps);
493
494                         fr_strerror_printf("No memory");
495                         return -1;
496                 }
497
498                 switch (vp->da->type) {
499                 case PW_TYPE_IPADDR:
500                         if (length == 4) {
501                                 memcpy(&vp->vp_ipaddr, ptr, 4);
502                                 vp->length = 4;
503                                 break;
504                         }
505                         
506                         /*
507                          *      Value doesn't match the type we have for the
508                          *      valuepair so we must change it's da to an
509                          *      unknown attr.
510                          */
511                         vp->da = dict_attrunknown(vp->da->attr, vp->da->vendor,
512                                                   TRUE);
513                         /* FALL-THROUGH */
514
515                 default:
516                 case PW_TYPE_OCTETS:
517                 case PW_TYPE_STRING:
518                         vp->length = (length > MAX_VMPS_LEN) ? MAX_VMPS_LEN : length;
519                         memcpy(vp->vp_octets, ptr, vp->length);
520                         vp->vp_octets[vp->length] = '\0';
521                         break;
522                 }
523                 ptr += length;
524                 debug_pair(vp);
525
526                 *tail = vp;
527                 tail = &(vp->next);
528         }
529
530         /*
531          *      FIXME: Map attributes to Calling-Station-Id, etc...
532          */
533
534         return 0;
535 }
536
537 /*
538  *      These are the MUST HAVE contents for a VQP packet.
539  *
540  *      We don't allow the caller to give less than these, because
541  *      it won't work.  We don't encode more than these, because the
542  *      clients will ignore it.
543  *
544  *      FIXME: Be more generous?  Look for CISCO + VQP attributes?
545  */
546 static int contents[5][VQP_MAX_ATTRIBUTES] = {
547         { 0,      0,      0,      0,      0,      0 },
548         { 0x0c01, 0x0c02, 0x0c03, 0x0c04, 0x0c07, 0x0c05 }, /* Join request */
549         { 0x0c03, 0x0c08, 0,      0,      0,      0 },  /* Join Response */
550         { 0x0c01, 0x0c02, 0x0c03, 0x0c04, 0x0c07, 0x0c08 }, /* Reconfirm */
551         { 0x0c03, 0x0c08, 0,      0,      0,      0 }
552 };
553
554 int vqp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
555 {
556         int i, code, length;
557         VALUE_PAIR *vp;
558         uint8_t *ptr;
559         VALUE_PAIR      *vps[VQP_MAX_ATTRIBUTES];
560
561         if (!packet) {
562                 fr_strerror_printf("Failed encoding VQP");
563                 return -1;
564         }
565
566         if (packet->data) return 0;
567
568         vp = pairfind(packet->vps, PW_VQP_PACKET_TYPE, 0, TAG_ANY);
569         if (!vp) {
570                 fr_strerror_printf("Failed to find VQP-Packet-Type in response packet");
571                 return -1;
572         }
573
574         code = vp->vp_integer;
575         if ((code < 1) || (code > 4)) {
576                 fr_strerror_printf("Invalid value %d for VQP-Packet-Type", code);
577                 return -1;
578         }
579
580         length = VQP_HDR_LEN;
581         memset(vps, 0, sizeof(vps));
582
583         vp = pairfind(packet->vps, PW_VQP_ERROR_CODE, 0, TAG_ANY);
584
585         /*
586          *      FIXME: Map attributes from calling-station-Id, etc.
587          *
588          *      Maybe do this via rlm_vqp?  That's probably the
589          *      best place to add the code...
590          */
591
592         /*
593          *      No error: encode attributes.
594          */
595         if (!vp) for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) {
596                 if (!contents[code][i]) break;
597
598                 vps[i] = pairfind(packet->vps, contents[code][i] | 0x2000, 0, TAG_ANY);
599
600                 /*
601                  *      FIXME: Print the name...
602                  */
603                 if (!vps[i]) {
604                         fr_strerror_printf("Failed to find VQP attribute %02x",
605                                    contents[code][i]);
606                         return -1;
607                 }
608
609                 length += 6;
610                 length += vps[i]->length;
611         }
612
613         packet->data = malloc(length);
614         if (!packet->data) {
615                 fr_strerror_printf("No memory");
616                 return -1;
617         }
618         packet->data_len = length;
619
620         ptr = packet->data;
621
622         ptr[0] = VQP_VERSION;
623         ptr[1] = code;
624
625         if (!vp) {
626                 ptr[2] = 0;
627         } else {
628                 ptr[2] = vp->vp_integer & 0xff;
629                 return 0;
630         }
631
632         /*
633          *      The number of attributes is hard-coded.
634          */
635         if ((code == 1) || (code == 3)) {
636                 uint32_t sequence;
637
638                 ptr[3] = VQP_MAX_ATTRIBUTES;
639
640                 sequence = htonl(packet->id);
641                 memcpy(ptr + 4, &sequence, 4);
642         } else {
643                 if (!original) {
644                         fr_strerror_printf("Cannot send VQP response without request");
645                         return -1;
646                 }
647
648                 /*
649                  *      Packet Sequence Number
650                  */
651                 memcpy(ptr + 4, original->data + 4, 4);
652
653                 ptr[3] = 2;
654         }
655
656         ptr += 8;
657
658         /*
659          *      Encode the VP's.
660          */
661         for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) {
662                 if (!vps[i]) break;
663                 if (ptr >= (packet->data + packet->data_len)) break;
664
665                 vp = vps[i];
666
667                 debug_pair(vp);
668
669                 /*
670                  *      Type.  Note that we look at only the lower 8
671                  *      bits, as the upper 8 bits have been hacked.
672                  *      See also dictionary.vqp
673                  */
674                 ptr[0] = 0;
675                 ptr[1] = 0;
676                 ptr[2] = 0x0c;
677                 ptr[3] = vp->da->attr & 0xff;
678
679                 /* Length */
680                 ptr[4] = 0;
681                 ptr[5] = vp->length & 0xff;
682
683                 ptr += 6;
684
685                 /* Data */
686                 switch (vp->da->type) {
687                 case PW_TYPE_IPADDR:
688                         memcpy(ptr, &vp->vp_ipaddr, 4);
689                         break;
690
691                 default:
692                 case PW_TYPE_OCTETS:
693                 case PW_TYPE_STRING:
694                         memcpy(ptr, vp->vp_octets, vp->length);
695                         break;
696                 }
697                 ptr += vp->length;
698         }
699
700         return 0;
701 }
702 #endif