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