Add rs_packet_add_avp() and use it.
[radsecproxy.git] / lib / radius / radpkt.c
1 /*
2 Copyright (c) 2011, Network RADIUS SARL
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9     * Redistributions in binary form must reproduce the above copyright
10       notice, this list of conditions and the following disclaimer in the
11       documentation and/or other materials provided with the distribution.
12     * Neither the name of the <organization> nor the
13       names of its contributors may be used to endorse or promote products
14       derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 /** \file packet.c
29  *  \brief Encoding and decoding packets
30  */
31
32 #include        "client.h"
33
34 #if RS_MAX_PACKET_LEN < 64
35 #error RS_MAX_PACKET_LEN is too small.  It should be at least 64.
36 #endif
37
38 #if RS_MAX_PACKET_LEN > 16384
39 #error RS_MAX_PACKET_LEN is too large.  It should be smaller than 16K.
40 #endif
41
42 const char *nr_packet_codes[RS_MAX_PACKET_CODE + 1] = {
43   NULL,
44   "Access-Request",
45   "Access-Accept",
46   "Access-Reject",
47   "Accounting-Request",
48   "Accounting-Response",
49   NULL, NULL, NULL, NULL, NULL,
50   "Access-Challenge",
51   "Status-Server",              /* 12 */
52   NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 19 */
53   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 20..29 */
54   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 30..39 */
55   "Disconnect-Request",
56   "Disconnect-ACK",
57   "Disconnect-NAK",
58   "CoA-Request",
59   "CoA-ACK",
60   "CoA-NAK"
61 };
62
63
64 static uint64_t allowed_responses[RS_MAX_PACKET_CODE + 1] = {
65         0,
66         (1 << PW_ACCESS_ACCEPT) | (1 << PW_ACCESS_REJECT) | (1 << PW_ACCESS_CHALLENGE),
67         0, 0,
68         1 << PW_ACCOUNTING_RESPONSE,
69         0,
70         0, 0, 0, 0, 0,
71         0,
72         (1 << PW_ACCESS_ACCEPT) | (1 << PW_ACCESS_REJECT) | (1 << PW_ACCESS_CHALLENGE) | (1 << PW_ACCOUNTING_RESPONSE),
73         0, 0, 0, 0, 0, 0, 0,
74         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20..29 */
75         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 30..39 */
76         (((uint64_t) 1) << PW_DISCONNECT_ACK) | (((uint64_t) 1) << PW_DISCONNECT_NAK),
77         0,
78         0,
79         (((uint64_t) 1) << PW_COA_ACK) | (((uint64_t) 1) << PW_COA_NAK),
80         0,
81         0
82 };
83
84
85 int nr_packet_ok_raw(const uint8_t *data, size_t sizeof_data)
86 {
87         size_t packet_len;
88         const uint8_t *attr, *end;
89
90         if (!data || (sizeof_data < 20)) {
91                 nr_debug_error("Invalid argument");
92                 return -RSE_INVAL;
93         }
94
95         packet_len = (data[2] << 8) | data[3];
96         if (packet_len < 20) {
97                 nr_debug_error("Packet length is too small");
98                 return -RSE_PACKET_TOO_SMALL;
99         }
100
101         if (packet_len > sizeof_data) {
102                 nr_debug_error("Packet length overflows received data");
103                 return -RSE_PACKET_TOO_LARGE;
104         }
105
106         /*
107          *      If we receive 100 bytes, and the header says it's 20 bytes,
108          *      then it's 20 bytes.
109          */
110         end = data + packet_len;
111
112         for (attr = data + 20; attr < end; attr += attr[1]) {
113                 if ((attr + 2) > end) {
114                         nr_debug_error("Attribute overflows packet");
115                         return -RSE_ATTR_OVERFLOW;
116                 }
117
118                 if (attr[1] < 2) {
119                         nr_debug_error("Attribute length is too small");
120                         return -RSE_ATTR_TOO_SMALL;
121                 }
122
123                 if ((attr + attr[1]) > end) {
124                         nr_debug_error("Attribute length is too large");
125                         return -RSE_ATTR_TOO_LARGE;
126                 }
127         }
128
129         return 0;
130 }
131
132 int nr_packet_ok(RADIUS_PACKET *packet)
133 {
134         int rcode;
135
136         if (!packet) return -RSE_INVAL;
137
138         if ((packet->flags & RS_PACKET_OK) != 0) return 0;
139
140         rcode = nr_packet_ok_raw(packet->data, packet->length);
141         if (rcode < 0) return rcode;
142
143         packet->flags |= RS_PACKET_OK;
144         return 0;
145 }
146
147
148 /*
149  *      Comparison function that is time-independent.  Using "memcmp"
150  *      would satisfy the "comparison" part.  However, it would also
151  *      leak information about *which* bytes are wrong.  Attackers
152  *      could use that leak to create a "correct" RADIUS packet which
153  *      will be accepted by the client and/or server.
154  */
155 static int digest_cmp(const uint8_t *a, const uint8_t *b, size_t length)
156 {
157         int result = 0;
158         size_t i;
159
160         for (i = 0; i < length; i++) {
161                 result |= (a[i] ^ b[i]);
162         }
163
164         return result;
165 }
166
167
168 #ifdef PW_MESSAGE_AUTHENTICATOR
169 static int msg_auth_ok(const RADIUS_PACKET *original,
170                        uint8_t *ma,
171                        uint8_t *data, size_t length)
172 {
173         uint8_t packet_vector[sizeof(original->vector)];
174         uint8_t msg_auth_vector[sizeof(original->vector)];
175         uint8_t calc_auth_vector[sizeof(original->vector)];
176         
177         if (ma[1] != 18) {
178                 nr_debug_error("Message-Authenticator has invalid length");
179                 return -RSE_MSG_AUTH_LEN;
180         }
181
182         memcpy(packet_vector, data + 4, sizeof(packet_vector));
183         memcpy(msg_auth_vector, ma + 2, sizeof(msg_auth_vector));
184         memset(ma + 2, 0, sizeof(msg_auth_vector));
185
186         switch (data[0]) {
187         default:
188                 break;
189                 
190         case PW_ACCOUNTING_REQUEST:
191         case PW_ACCOUNTING_RESPONSE:
192         case PW_DISCONNECT_REQUEST:
193         case PW_DISCONNECT_ACK:
194         case PW_DISCONNECT_NAK:
195         case PW_COA_REQUEST:
196         case PW_COA_ACK:
197         case PW_COA_NAK:
198                 memset(data + 4, 0, sizeof(packet_vector));
199                 break;
200                 
201         case PW_ACCESS_ACCEPT:
202         case PW_ACCESS_REJECT:
203         case PW_ACCESS_CHALLENGE:
204                 if (!original) {
205                         nr_debug_error("Cannot validate response without request");
206                         return -RSE_REQUEST_REQUIRED;
207                 }
208                 memcpy(data + 4, original->vector, sizeof(original->vector));
209                 break;
210         }
211         
212         nr_hmac_md5(data, length,
213                     (const uint8_t *) original->secret, original->sizeof_secret,
214                     calc_auth_vector);
215
216         memcpy(ma + 2, msg_auth_vector, sizeof(msg_auth_vector));
217         memcpy(data + 4, packet_vector, sizeof(packet_vector));
218
219         if (digest_cmp(calc_auth_vector, msg_auth_vector,
220                        sizeof(calc_auth_vector)) != 0) {
221                 nr_debug_error("Invalid Message-Authenticator");
222                 return -RSE_MSG_AUTH_WRONG;
223         }
224
225         return 1;
226 }
227 #endif
228
229 /*
230  *      The caller ensures that the packet codes are as expected.
231  */
232 static int packet_auth_ok(const RADIUS_PACKET *original,
233                           uint8_t *data, size_t length)
234 {
235         uint8_t packet_vector[sizeof(original->vector)];
236         uint8_t calc_digest[sizeof(original->vector)];
237         RS_MD5_CTX ctx;
238
239         if ((data[0] == PW_ACCESS_REQUEST) ||
240             (data[0] == PW_STATUS_SERVER)) return 1;
241
242         memcpy(packet_vector, data + 4, sizeof(packet_vector));
243
244         if (!original) {
245                 memset(data + 4, 0, sizeof(packet_vector));
246         } else {
247                 memcpy(data + 4, original->vector, sizeof(original->vector));
248         }
249
250         RS_MD5Init(&ctx);
251         RS_MD5Update(&ctx, data, length);
252         RS_MD5Update(&ctx, (const unsigned char *)original->secret, original->sizeof_secret);
253         RS_MD5Final(calc_digest, &ctx);
254
255         memcpy(data + 4, packet_vector, sizeof(packet_vector));
256
257         if (digest_cmp(calc_digest, packet_vector,
258                        sizeof(packet_vector)) != 0) {
259                 nr_debug_error("Invalid authentication vector");
260                 return -RSE_AUTH_VECTOR_WRONG;
261         }
262
263         return 0;
264 }
265
266
267 int nr_packet_verify(RADIUS_PACKET *packet, const RADIUS_PACKET *original)
268 {
269         int rcode;
270         uint8_t *attr;
271 #ifdef PW_MESSAGE_AUTHENTICATOR
272         const uint8_t *end;
273 #endif
274
275         if (!packet || !packet->data || !packet->secret) {
276                 nr_debug_error("Invalid argument");
277                 return -RSE_INVAL;
278         }
279
280         if ((packet->flags & RS_PACKET_VERIFIED) != 0) return 0;
281
282         /*
283          *      Packet isn't well formed.  Ignore it.
284          */
285         rcode = nr_packet_ok(packet);
286         if (rcode < 0) return rcode;
287
288         /*
289          *      Get rid of improper packets as early as possible.
290          */
291         if (original) {
292                 uint64_t mask;
293
294                 if (original->code > RS_MAX_PACKET_CODE) {
295                         nr_debug_error("Invalid original code %u",
296                                            original->code);
297                         return -RSE_INVALID_REQUEST_CODE;
298                 }
299
300                 if (packet->data[1] != original->id) {
301                         nr_debug_error("Ignoring response with wrong ID %u",
302                                            packet->data[1]);
303                         return -RSE_INVALID_RESPONSE_CODE;
304                 }
305
306                 mask = 1;
307                 mask <<= packet->data[0];
308
309                 if ((allowed_responses[original->code] & mask) == 0) {
310                         nr_debug_error("Ignoring response with wrong code %u",
311                                            packet->data[0]);
312                         return -RSE_INVALID_RESPONSE_CODE;
313                 }
314
315                 if ((memcmp(&packet->src, &original->dst, sizeof(packet->src)) != 0) &&
316                     (evutil_sockaddr_cmp((struct sockaddr *)&packet->src, (struct sockaddr *)&original->dst, 1) != 0)) {
317                         nr_debug_error("Ignoring response from wrong IP/port");
318                         return -RSE_INVALID_RESPONSE_SRC;
319                 }
320
321         } else if (allowed_responses[packet->data[0]] != 0) {
322                 nr_debug_error("Ignoring response without original");
323                 return -RSE_INVALID_RESPONSE_CODE;
324         }
325
326 #ifdef PW_MESSAGE_AUTHENTICATOR
327         end = packet->data + packet->length;
328
329         /*
330          *      Note that the packet MUST be well-formed here.
331          */
332         for (attr = packet->data + 20; attr < end; attr += attr[1]) {
333                 if (attr[0] == PW_MESSAGE_AUTHENTICATOR) {
334                         rcode = msg_auth_ok(original, attr,
335                                             packet->data, packet->length);
336                         if (rcode < 0) return rcode;
337                 }
338         }
339 #endif
340
341         /*
342          *      Verify the packet authenticator.
343          */
344         rcode = packet_auth_ok(original, packet->data, packet->length);
345         if (rcode < 0) return rcode;
346
347         packet->flags |= RS_PACKET_VERIFIED;
348
349         return 0;
350 }
351
352
353 int nr_packet_decode(RADIUS_PACKET *packet, const RADIUS_PACKET *original)
354 {
355         int             rcode, num_attributes;
356         uint8_t         *data, *attr;
357         const uint8_t   *end;
358         VALUE_PAIR      **tail, *vp;
359
360         if (!packet) return -RSE_INVAL;
361
362         if ((packet->flags & RS_PACKET_DECODED) != 0) return 0;
363       
364         rcode = nr_packet_ok(packet);
365         if (rcode < 0) return rcode;
366
367         data = packet->data;
368         end = data + packet->length;
369         tail = &packet->vps;
370         num_attributes = 0;
371
372         /*
373          *      Loop over the packet, converting attrs to VPs.
374          */
375         for (attr = data + 20; attr < end; attr += attr[1]) {
376                 rcode = nr_attr2vp(packet, original,
377                                     attr, end - attr, &vp);
378                 if (rcode < 0) {
379                         nr_vp_free(&packet->vps);
380                         return -rcode;
381                 }
382
383                 *tail = vp;
384                 while (vp) {
385                         num_attributes++;
386                         tail = &(vp->next);
387                         vp = vp->next;
388                 }
389
390                 if (num_attributes > RS_MAX_ATTRIBUTES) {
391                         nr_debug_error("Too many attributes");
392                         nr_vp_free(&packet->vps);
393                         return -RSE_TOO_MANY_ATTRS;
394                 }
395         }
396
397         packet->code = data[0];
398         packet->id = data[1];
399         memcpy(packet->vector, data + 4, sizeof(packet->vector));
400
401         packet->flags |= RS_PACKET_DECODED;
402
403         return 0;
404 }
405
406
407 int nr_packet_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original)
408 {
409 #ifdef PW_MESSAGE_AUTHENTICATOR
410         size_t ma = 0;
411         const uint8_t *attr, *end;
412 #endif
413
414         if ((packet->flags & RS_PACKET_SIGNED) != 0) return 0;
415
416         if ((packet->flags & RS_PACKET_ENCODED) == 0) {
417                 int rcode;
418
419                 rcode = nr_packet_encode(packet, original);
420                 if (rcode < 0) return rcode;
421         }
422
423         if ((packet->code == PW_ACCESS_ACCEPT) ||
424             (packet->code == PW_ACCESS_CHALLENGE) ||
425             (packet->code == PW_ACCESS_REJECT)) {
426 #ifdef PW_MESSAGE_AUTHENTICATOR
427                 if (!original) {
428                         nr_debug_error("Original packet is required to create the  Message-Authenticator");
429                         return -RSE_REQUEST_REQUIRED;
430                 }
431 #endif
432                 
433                 memcpy(packet->data + 4, original->vector,
434                        sizeof(original->vector));
435         } else {
436                 memcpy(packet->data + 4, packet->vector,
437                        sizeof(packet->vector));
438         }
439
440 #ifdef PW_MESSAGE_AUTHENTICATOR
441         end = packet->data + packet->length;
442
443         for (attr = packet->data + 20; attr < end; attr += attr[1]) {
444                 if (attr[0] == PW_MESSAGE_AUTHENTICATOR) {
445                         ma = (attr - packet->data);
446                         break;
447                 }
448         }
449
450         /*
451          *      Force all Access-Request packets to have a
452          *      Message-Authenticator.
453          */
454         if (!ma && ((packet->length + 18) <= packet->sizeof_data) &&
455             ((packet->code == PW_ACCESS_REQUEST) ||
456              (packet->code == PW_STATUS_SERVER))) {
457                 ma = packet->length;
458
459                 packet->data[ma]= PW_MESSAGE_AUTHENTICATOR;
460                 packet->data[ma + 1] = 18;
461                 memset(&packet->data[ma + 2], 0, 16);
462                 packet->length += 18;
463         }
464
465         /*
466          *      Reset the length.
467          */
468         packet->data[2] = (packet->length >> 8) & 0xff;
469         packet->data[3] = packet->length & 0xff;
470
471         /*
472          *      Sign the Message-Authenticator && packet.
473          */
474         if (ma) {
475                 nr_hmac_md5(packet->data, packet->length,
476                             (const uint8_t *) packet->secret, packet->sizeof_secret,
477                             packet->data + ma + 2);
478         }
479 #endif
480
481         /*
482          *      Calculate the signature.
483          */
484         if (!((packet->code == PW_ACCESS_REQUEST) ||
485               (packet->code == PW_STATUS_SERVER))) {
486                 RS_MD5_CTX      ctx;
487
488                 RS_MD5Init(&ctx);
489                 RS_MD5Update(&ctx, packet->data, packet->length);
490                 RS_MD5Update(&ctx, (const unsigned char *)packet->secret, packet->sizeof_secret);
491                 RS_MD5Final(packet->vector, &ctx);
492         }
493
494         memcpy(packet->data + 4, packet->vector, sizeof(packet->vector));
495
496         packet->attempts = 0;
497         packet->flags |= RS_PACKET_SIGNED;
498
499         return 0;
500 }
501
502
503 static int can_encode_packet(RADIUS_PACKET *packet,
504                              const RADIUS_PACKET *original)
505 {
506         if ((packet->code == 0) ||
507             (packet->code > RS_MAX_PACKET_CODE) ||
508             (original && (original->code > RS_MAX_PACKET_CODE))) {
509                 nr_debug_error("Cannot send unknown packet code");
510                 return -RSE_INVALID_REQUEST_CODE;
511         }
512
513         if (!nr_packet_codes[packet->code]) {
514                 nr_debug_error("Cannot handle packet code %u",
515                                    packet->code);
516                 return -RSE_INVALID_REQUEST_CODE;
517         }
518
519 #ifdef NR_NO_MALLOC
520         if (!packet->data) {
521                 nr_debug_error("No place to put packet");
522                 return -RSE_NO_PACKET_DATA;
523         }
524 #endif
525
526         if (packet->sizeof_data < 20) {
527                 nr_debug_error("The buffer is too small to encode the packet");
528                 return -RSE_PACKET_TOO_SMALL;
529         }
530
531         /*
532          *      Enforce request / response correlation.
533          */
534         if (original) {
535                 uint64_t mask;
536
537                 mask = 1;
538                 mask <<= packet->code;
539
540                 if ((allowed_responses[original->code] & mask) == 0) {
541                         nr_debug_error("Cannot encode response %u to packet %u",
542                                            packet->code, original->code);
543                         return -RSE_INVALID_RESPONSE_CODE;
544                 }
545                 packet->id = original->id;
546
547         } else if (allowed_responses[packet->code] == 0) {
548                 nr_debug_error("Cannot encode response %u without original",
549                                    packet->code);
550                 return -RSE_REQUEST_REQUIRED;
551         }
552
553         return 0;
554 }
555
556 static void encode_header(RADIUS_PACKET *packet)
557 {
558         if ((packet->flags & RS_PACKET_HEADER) != 0) return;
559
560         memset(packet->data, 0, 20);
561         packet->data[0] = packet->code;
562         packet->data[1] = packet->id;
563         packet->data[2] = 0;
564         packet->data[3] = 20;
565         packet->length = 20;
566
567         /*
568          *      Calculate a random authentication vector.
569          */
570         if ((packet->code == PW_ACCESS_REQUEST) ||
571             (packet->code == PW_STATUS_SERVER)) {
572                 nr_rand_bytes(packet->vector, sizeof(packet->vector));
573         } else {
574                 memset(packet->vector, 0, sizeof(packet->vector));
575         }
576
577         memcpy(packet->data + 4, packet->vector, sizeof(packet->vector));
578
579         packet->flags |= RS_PACKET_HEADER;
580 }
581
582 int nr_packet_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original)
583 {
584 #ifdef PW_MESSAGE_AUTHENTICATOR
585         size_t ma = 0;
586 #endif
587         int rcode;
588         ssize_t len;
589         const VALUE_PAIR *vp;
590         uint8_t *data, *end;
591
592         if ((packet->flags & RS_PACKET_ENCODED) != 0) return 0;
593
594         rcode = can_encode_packet(packet, original);
595         if (rcode < 0) return rcode;
596
597         data = packet->data;
598         end = data + packet->sizeof_data;
599
600         encode_header(packet);
601         data += 20;
602
603         /*
604          *      Encode each VALUE_PAIR
605          */
606         vp = packet->vps;
607         while (vp) {
608 #ifdef PW_MESSAGE_AUTHENTICATOR
609                 if (vp->da->attr == PW_MESSAGE_AUTHENTICATOR) {
610                         ma = (data - packet->data);
611                 }
612 #endif
613                 len = nr_vp2attr(packet, original, &vp,
614                                   data, end - data);
615                 if (len < 0) return len;
616
617                 if (len == 0) break; /* insufficient room to encode it */
618
619                 data += data[1];
620         }
621
622 #ifdef PW_MESSAGE_AUTHENTICATOR
623         /*
624          *      Always send a Message-Authenticator.
625          *
626          *      We do *not* recommend removing this code.
627          */
628         if (((packet->code == PW_ACCESS_REQUEST) ||
629              (packet->code == PW_STATUS_SERVER)) &&
630             !ma &&
631             ((data + 18) <= end)) {
632                 ma = (data - packet->data);
633                 data[0] = PW_MESSAGE_AUTHENTICATOR;
634                 data[1] = 18;
635                 memset(data + 2, 0, 16);
636                 data += data[1];
637         }
638 #endif
639
640         packet->length = data - packet->data;
641
642         packet->data[2] = (packet->length >> 8) & 0xff;
643         packet->data[3] = packet->length & 0xff;
644
645         packet->flags |= RS_PACKET_ENCODED;
646
647         return packet->length;
648 }
649
650
651 /*
652  *      Ensure that the nr_data2attr_t structure is filled in
653  *      appropriately.  This includes filling in a fake DICT_ATTR
654  *      structure, if necessary.
655  */
656 static int do_callback(void *ctx, nr_packet_walk_func_t callback,
657                        int attr, int vendor,
658                        const uint8_t *data, size_t sizeof_data)
659                        
660 {
661         int rcode;
662         const DICT_ATTR *da;
663         DICT_ATTR myda;
664         char buffer[64];
665
666         da = nr_dict_attr_byvalue(attr, vendor);
667
668         /*
669          *      The attribute is supposed to have a particular length,
670          *      but does not.  It is therefore malformed.
671          */
672         if (da && (da->flags.length != 0) &&
673             da->flags.length != sizeof_data) {
674                 da = NULL;
675         }
676
677         if (!da) {
678                 rcode = nr_dict_attr_2struct(&myda, attr, vendor,
679                                              buffer, sizeof(buffer));
680                 
681                 if (rcode < 0) return rcode;
682                 da = &myda;
683         }
684         
685         rcode = callback(ctx, da, data, sizeof_data);
686         if (rcode < 0) return rcode;
687
688         return 0;
689 }
690
691
692 int nr_packet_walk(RADIUS_PACKET *packet, void *ctx,
693                    nr_packet_walk_func_t callback)
694 {
695         int rcode;
696         uint8_t *attr;
697         const uint8_t *end;
698
699         if (!packet || !callback) return -RSE_INVAL;
700
701         rcode = nr_packet_ok(packet);
702         if (rcode < 0) return rcode;
703
704         end = packet->data + packet->length;
705
706         for (attr = packet->data + 20; attr < end; attr += attr[1]) {
707                 int length, value;
708                 int dv_type, dv_length;
709                 uint32_t vendorpec;
710                 const uint8_t *vsa;
711                 const DICT_VENDOR *dv = NULL;
712
713                 vendorpec = 0;
714                 value = attr[0];
715
716                 if (value != PW_VENDOR_SPECIFIC) {
717                 raw:
718                         rcode = do_callback(ctx, callback,
719                                             attr[0], 0,
720                                             attr + 2, attr[1] - 2);
721                         if (rcode < 0) return rcode;
722                         continue;
723                 }
724
725                 if (attr[1] < 6) goto raw;
726                 memcpy(&vendorpec, attr + 2, 4);
727                 vendorpec = ntohl(vendorpec);
728
729                 if (dv && (dv->vendor != vendorpec)) dv = NULL;
730
731                 if (!dv) dv = nr_dict_vendor_byvalue(vendorpec);
732
733                 if (dv) {
734                         dv_type = dv->type;
735                         dv_length = dv->length;
736                 } else {
737                         dv_type = 1;
738                         dv_length = 1;
739                 }
740
741                 /*
742                  *      Malformed: it's a raw attribute.
743                  */
744                 if (nr_tlv_ok(attr + 6, attr[1] - 6, dv_type, dv_length) < 0) {
745                         goto raw;
746                 }
747
748                 for (vsa = attr + 6; vsa < attr + attr[1]; vsa += length) {
749                         switch (dv_type) {
750                         case 4:
751                                 value = (vsa[2] << 8) | vsa[3];
752                                 break;
753
754                         case 2:
755                                 value = (vsa[0] << 8) | vsa[1];
756                                 break;
757
758                         case 1:
759                                 value = vsa[0];
760                                 break;
761
762                         default:
763                                 return -RSE_INTERNAL;
764                         }
765
766                         switch (dv_length) {
767                         case 0:
768                                 length = attr[1] - 6 - dv_type;
769                                 break;
770
771                         case 2:
772                         case 1:
773                                 length = vsa[dv_type + dv_length - 1];
774                                 break;
775
776                         default:
777                                 return -RSE_INTERNAL;
778                         }
779
780                         rcode = do_callback(ctx, callback,
781                                             value, vendorpec,
782                                             vsa + dv_type + dv_length,
783                                             length - dv_type - dv_length);
784                         if (rcode < 0) return rcode;
785                 }
786         }
787
788         return 0;
789 }
790
791 int nr_packet_init(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
792                    const char *secret, int code,
793                    void *data, size_t sizeof_data)
794 {
795         int rcode;
796
797         if ((code < 0) || (code > RS_MAX_PACKET_CODE)) {
798                 return -RSE_INVALID_REQUEST_CODE;
799         }
800
801         if (!data || (sizeof_data < 20)) return -RSE_INVAL;
802
803         memset(packet, 0, sizeof(*packet));
804         packet->secret = secret;
805         packet->sizeof_secret = secret ? strlen(secret) : 0;
806         packet->code = code;
807         packet->id = 0;
808         packet->data = data;
809         packet->sizeof_data = sizeof_data;
810
811         rcode = can_encode_packet(packet, original);
812         if (rcode < 0) return rcode;
813
814         encode_header(packet);
815
816         return 0;
817 }
818
819
820 static int pack_eap(RADIUS_PACKET *packet,
821                     const void *data, size_t data_len)
822 {
823         uint8_t *attr, *end;
824         const uint8_t *eap;
825         size_t left;
826                 
827         eap = data;
828         left = data_len;
829         attr = packet->data + packet->length;
830         end = attr + packet->sizeof_data;
831         
832         while (left > 253) {
833                 if ((attr + 255) > end) return -RSE_ATTR_OVERFLOW;
834                 
835                 attr[0] = PW_EAP_MESSAGE;
836                 attr[1] = 255;
837                 memcpy(attr + 2, eap, 253);
838                 attr += attr[1];
839                 eap += 253;
840                 left -= 253;
841         }
842         
843         if ((attr + (2 + left)) > end) return -RSE_ATTR_OVERFLOW;
844         
845         attr[0] = PW_EAP_MESSAGE;
846         attr[1] = 2 + left;
847         memcpy(attr + 2, eap, left);
848         attr += attr[1];
849         packet->length = attr - packet->data;
850
851         return 0;
852 }
853
854 ssize_t nr_packet_attr_append(RADIUS_PACKET *packet,
855                               const RADIUS_PACKET *original,
856                               const DICT_ATTR *da,
857                               const void *data, size_t data_len)
858 {
859         ssize_t rcode;
860         uint8_t *attr, *end;
861         VALUE_PAIR my_vp;
862         const VALUE_PAIR *vp;
863
864         if (!packet || !da || !data) {
865                 return -RSE_INVAL;
866         }
867
868         if (data_len == 0) {
869                 if (da->type != RS_TYPE_STRING) return -RSE_ATTR_TOO_SMALL;
870
871                 data_len = strlen(data);
872         }
873
874         /* We're going to mark the whole packet as encoded so we
875            better not have any unencoded value-pairs attached. */
876         if (packet->vps)
877                 return -RSE_INVAL;
878         packet->flags |= RS_PACKET_ENCODED;
879
880         attr = packet->data + packet->length;
881         end = attr + packet->sizeof_data;
882
883         if ((attr + 2 + data_len) > end) {
884                 return -RSE_ATTR_OVERFLOW;
885         }
886
887         if ((da->flags.length != 0) &&
888             (data_len != da->flags.length)) {
889                 return -RSE_ATTR_VALUE_MALFORMED;
890         }
891
892 #ifdef PW_EAP_MESSAGE
893         /*
894          *      automatically split EAP-Message into multiple
895          *      attributes.
896          */
897         if (!da->vendor && (da->attr == PW_EAP_MESSAGE) && (data_len > 253)) {
898                 return pack_eap(packet, data, data_len);
899         }
900 #endif
901
902         if (data_len > 253) return -RSE_ATTR_TOO_LARGE;
903
904         vp = nr_vp_init(&my_vp, da);
905         rcode = nr_vp_set_data(&my_vp, data, data_len);
906         if (rcode < 0) return rcode;
907
908         /*
909          *      Note that this function packs VSAs each into their own
910          *      Vendor-Specific attribute.  If this isn't what you
911          *      want, use the version of the library with full support
912          *      for TLVs, WiMAX, and extended attributes.
913          */
914         rcode = nr_vp2attr(packet, original, &vp, attr, end - attr);
915         if (rcode <= 0) return rcode;
916
917         packet->length += rcode;
918
919         return rcode;
920 }