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