Move .gitignore to lib/ in preparation for moving lib to the root.
[libradsec.git] / radius / tests / radattr.c
1 /*
2  * Copyright (C) 2011 Network RADIUS SARL <info@networkradius.com>
3  *
4  * This software may not be redistributed in any form without the prior
5  * written consent of Network RADIUS.
6  *
7  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
8  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
9  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
10  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
11  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
14  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
15  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
16  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
17  * SUCH DAMAGE.
18  */
19
20 #include <networkradius-devel/client.h>
21
22 #include <ctype.h>
23
24 #include <assert.h>
25
26 static int packet_code = PW_ACCESS_REQUEST;
27 static int packet_id = 1;
28 static uint8_t packet_vector[16] = { 0, 0, 0, 0, 0, 0, 0, 0,
29                                      0, 0, 0, 0, 0, 0, 0, 0 };
30 static char secret[256] = "testing123";
31
32 static int encode_tlv(char *buffer, uint8_t *output, size_t outlen);
33
34 static const char *hextab = "0123456789abcdef";
35
36 static int encode_data_string(char *buffer,
37                               uint8_t *output, size_t outlen)
38 {
39         int length = 0;
40         char *p;
41         
42         p = buffer + 1;
43
44         while (*p && (outlen > 0)) {
45                 if (*p == '"') {
46                         return length;
47                 }
48
49                 if (*p != '\\') {
50                         *(output++) = *(p++);
51                         outlen--;
52                         length++;
53                         continue;
54                 }
55
56                 switch (p[1]) {
57                 default:
58                         *(output++) = p[1];
59                         break;
60
61                 case 'n':
62                         *(output++) = '\n';
63                         break;
64
65                 case 'r':
66                         *(output++) = '\r';
67                         break;
68
69                 case 't':
70                         *(output++) = '\t';
71                         break;
72                 }
73
74                 outlen--;
75                 length++;
76         }
77
78         fprintf(stderr, "String is not terminated\n");
79         return 0;
80 }
81
82 static int encode_data_tlv(char *buffer, char **endptr,
83                            uint8_t *output, size_t outlen)
84 {
85         int depth = 0;
86         int length;
87         char *p;
88
89         for (p = buffer; *p != '\0'; p++) {
90                 if (*p == '{') depth++;
91                 if (*p == '}') {
92                         depth--;
93                         if (depth == 0) break;
94                 }
95         }
96
97         if (*p != '}') {
98                 fprintf(stderr, "No trailing '}' in string starting "
99                         "with \"%s\"\n",
100                         buffer);
101                 return 0;
102         }
103
104         *endptr = p + 1;
105         *p = '\0';
106         
107         p = buffer + 1;
108         while (isspace((int) *p)) p++;
109         
110         length = encode_tlv(p, output, outlen);
111         if (length == 0) return 0;
112         
113         return length;
114 }
115
116 static int encode_hex(char *p, uint8_t *output, size_t outlen)
117 {
118         int length = 0;
119         while (*p) {
120                 char *c1, *c2;
121
122                 while (isspace((int) *p)) p++;
123
124                 if (!*p) break;
125
126                 if(!(c1 = memchr(hextab, tolower((int) p[0]), 16)) ||
127                    !(c2 = memchr(hextab, tolower((int)  p[1]), 16))) {
128                         fprintf(stderr, "Invalid data starting at "
129                                 "\"%s\"\n", p);
130                         return 0;
131                 }
132
133                 *output = ((c1 - hextab) << 4) + (c2 - hextab);
134                 output++;
135                 length++;
136                 p += 2;
137
138                 outlen--;
139                 if (outlen == 0) {
140                         fprintf(stderr, "Too much data\n");
141                         return 0;
142                 }
143         }
144
145         return length;
146 }
147
148
149 static int encode_data(char *p, uint8_t *output, size_t outlen)
150 {
151         int length;
152
153         if (!isspace((int) *p)) {
154                 fprintf(stderr, "Invalid character following attribute "
155                         "definition\n");
156                 return 0;
157         }
158
159         while (isspace((int) *p)) p++;
160
161         if (*p == '{') {
162                 int sublen;
163                 char *q;
164
165                 length = 0;
166
167                 do {
168                         while (isspace((int) *p)) p++;
169                         if (!*p) {
170                                 if (length == 0) {
171                                         fprintf(stderr, "No data\n");
172                                         return 0;
173                                 }
174
175                                 break;
176                         }
177
178                         sublen = encode_data_tlv(p, &q, output, outlen);
179                         if (sublen == 0) return 0;
180
181                         length += sublen;
182                         output += sublen;
183                         outlen -= sublen;
184                         p = q;
185                 } while (*q);
186
187                 return length;
188         }
189
190         if (*p == '"') {
191                 length = encode_data_string(p, output, outlen);
192                 return length;
193         }
194
195         length = encode_hex(p, output, outlen);
196
197         if (length == 0) {
198                 fprintf(stderr, "Empty string\n");
199                 return 0;
200         }
201
202         return length;
203 }
204
205 static int decode_attr(char *buffer, char **endptr)
206 {
207         long attr;
208
209         attr = strtol(buffer, endptr, 10);
210         if (*endptr == buffer) {
211                 fprintf(stderr, "No valid number found in string "
212                         "starting with \"%s\"\n", buffer);
213                 return 0;
214         }
215
216         if (!**endptr) {
217                 fprintf(stderr, "Nothing follows attribute number\n");
218                 return 0;
219         }
220
221         if ((attr <= 0) || (attr > 256)) {
222                 fprintf(stderr, "Attribute number is out of valid "
223                         "range\n");
224                 return 0;
225         }
226
227         return (int) attr;
228 }
229
230 static int decode_vendor(char *buffer, char **endptr)
231 {
232         long vendor;
233
234         if (*buffer != '.') {
235                 fprintf(stderr, "Invalid separator before vendor id\n");
236                 return 0;
237         }
238
239         vendor = strtol(buffer + 1, endptr, 10);
240         if (*endptr == (buffer + 1)) {
241                 fprintf(stderr, "No valid vendor number found\n");
242                 return 0;
243         }
244
245         if (!**endptr) {
246                 fprintf(stderr, "Nothing follows vendor number\n");
247                 return 0;
248         }
249
250         if ((vendor <= 0) || (vendor > (1 << 24))) {
251                 fprintf(stderr, "Vendor number is out of valid range\n");
252                 return 0;
253         }
254
255         if (**endptr != '.') {
256                 fprintf(stderr, "Invalid data following vendor number\n");
257                 return 0;
258         }
259         (*endptr)++;
260
261         return (int) vendor;
262 }
263
264 static int encode_tlv(char *buffer, uint8_t *output, size_t outlen)
265 {
266         int attr;
267         int length;
268         char *p;
269
270         attr = decode_attr(buffer, &p);
271         if (attr == 0) return 0;
272
273         output[0] = attr;
274         output[1] = 2;
275
276         if (*p == '.') {
277                 p++;
278                 length = encode_tlv(p, output + 2, outlen - 2);
279
280         } else {
281                 length = encode_data(p, output + 2, outlen - 2);
282         }
283
284         if (length == 0) return 0;
285         if (length > (255 - 2)) {
286                 fprintf(stderr, "TLV data is too long\n");
287                 return 0;
288         }
289
290         output[1] += length;
291
292         return length + 2;
293 }
294
295 static int encode_vsa(char *buffer, uint8_t *output, size_t outlen)
296 {
297         int vendor;
298         int length;
299         char *p;
300
301         vendor = decode_vendor(buffer, &p);
302         if (vendor == 0) return 0;
303
304         output[0] = 0;
305         output[1] = (vendor >> 16) & 0xff;
306         output[2] = (vendor >> 8) & 0xff;
307         output[3] = vendor & 0xff;
308
309         length = encode_tlv(p, output + 4, outlen - 4);
310         if (length == 0) return 0;
311         if (length > (255 - 6)) {
312                 fprintf(stderr, "VSA data is too long\n");
313                 return 0;
314         }
315
316
317         return length + 4;
318 }
319
320 static int encode_evs(char *buffer, uint8_t *output, size_t outlen)
321 {
322         int vendor;
323         int attr;
324         int length;
325         char *p;
326
327         vendor = decode_vendor(buffer, &p);
328         if (vendor == 0) return 0;
329
330         attr = decode_attr(p, &p);
331         if (attr == 0) return 0;
332
333         output[0] = 0;
334         output[1] = (vendor >> 16) & 0xff;
335         output[2] = (vendor >> 8) & 0xff;
336         output[3] = vendor & 0xff;
337         output[4] = attr;
338
339         length = encode_data(p, output + 5, outlen - 5);
340         if (length == 0) return 0;
341
342         return length + 5;
343 }
344
345 static int encode_extended(char *buffer,
346                            uint8_t *output, size_t outlen)
347 {
348         int attr;
349         int length;
350         char *p;
351         
352         attr = decode_attr(buffer, &p);
353         if (attr == 0) return 0;
354
355         output[0] = attr;
356
357         if (attr == 26) {
358                 length = encode_evs(p, output + 1, outlen - 1);
359         } else {
360                 length = encode_data(p, output + 1, outlen - 1);
361         }
362         if (length == 0) return 0;
363         if (length > (255 - 3)) {
364                 fprintf(stderr, "Extended Attr data is too long\n");
365                 return 0;
366         }
367
368         return length + 1;
369 }
370
371 static int encode_extended_flags(char *buffer,
372                                  uint8_t *output, size_t outlen)
373 {
374         int attr;
375         int length, total;
376         char *p;
377         
378         attr = decode_attr(buffer, &p);
379         if (attr == 0) return 0;
380
381         /* output[0] is the extended attribute */
382         output[1] = 4;
383         output[2] = attr;
384         output[3] = 0;
385
386         if (attr == 26) {
387                 length = encode_evs(p, output + 4, outlen - 4);
388                 if (length == 0) return 0;
389
390                 output[1] += 5;
391                 length -= 5;
392         } else {
393                 length = encode_data(p, output + 4, outlen - 4);
394         }
395         if (length == 0) return 0;
396
397         total = 0;
398         while (1) {
399                 int sublen = 255 - output[1];
400
401                 if (length <= sublen) {
402                         output[1] += length;
403                         total += output[1];
404                         break;
405                 }
406
407                 length -= sublen;
408
409                 memmove(output + 255 + 4, output + 255, length);
410                 memcpy(output + 255, output, 4);
411
412                 output[1] = 255;
413                 output[3] |= 0x80;
414
415                 output += 255;
416                 output[1] = 4;
417                 total += 255;
418         }
419
420         return total;
421 }
422
423 static int encode_rfc(char *buffer, uint8_t *output, size_t outlen)
424 {
425         int attr;
426         int length, sublen;
427         char *p;
428
429         attr = decode_attr(buffer, &p);
430         if (attr == 0) return 0;
431
432         length = 2;
433         output[0] = attr;
434         output[1] = 2;
435
436         if (attr == 26) {
437                 sublen = encode_vsa(p, output + 2, outlen - 2);
438
439         } else if ((attr < 241) || (attr > 246)) {
440                 sublen = encode_data(p, output + 2, outlen - 2);
441
442         } else {
443                 if (*p != '.') {
444                         fprintf(stderr, "Invalid data following "
445                                 "attribute number\n");
446                         return 0;
447                 }
448
449                 if (attr < 245) {
450                         sublen = encode_extended(p + 1,
451                                                  output + 2, outlen - 2);
452                 } else {
453
454                         /*
455                          *      Not like the others!
456                          */
457                         return encode_extended_flags(p + 1, output, outlen);
458                 }
459         }
460         if (sublen == 0) return 0;
461         if (sublen > (255 -2)) {
462                 fprintf(stderr, "RFC Data is too long\n");
463                 return 0;
464         }
465
466         output[1] += sublen;
467         return length + sublen;
468 }
469
470 static int walk_callback(void *ctx, const DICT_ATTR *da,
471                          const uint8_t *data, size_t sizeof_data)
472 {
473         char **p = ctx;
474
475         sprintf(*p, "v%u a%u l%ld,",
476                 da->vendor, da->attr, sizeof_data);
477
478         *p += strlen(*p);
479 }
480
481 static void process_file(const char *filename)
482 {
483         int lineno, rcode;
484         size_t i, outlen;
485         ssize_t len, data_len;
486         FILE *fp;
487         RADIUS_PACKET packet;
488         char input[8192], buffer[8192];
489         char output[8192];
490         uint8_t *attr, data[2048];
491
492         if (strcmp(filename, "-") == 0) {
493                 fp = stdin;
494                 filename = "<stdin>";
495
496         } else {
497                 fp = fopen(filename, "r");
498                 if (!fp) {
499                         fprintf(stderr, "Error opening %s: %s\n",
500                                 filename, strerror(errno));
501                         exit(1);
502                 }
503         }
504
505         lineno = 0;
506         *output = '\0';
507         data_len = 0;
508
509         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
510                 char *p = strchr(buffer, '\n');
511                 VALUE_PAIR *vp, *head = NULL;
512                 VALUE_PAIR **tail = &head;
513
514                 lineno++;
515
516                 if (!p) {
517                         if (!feof(fp)) {
518                                 fprintf(stderr, "Line %d too long in %s\n",
519                                         lineno, filename);
520                                 exit(1);
521                         }
522                 } else {
523                         *p = '\0';
524                 }
525
526                 p = strchr(buffer, '#');
527                 if (p) *p = '\0';
528
529                 p = buffer;
530                 while (isspace((int) *p)) p++;
531                 if (!*p) continue;
532
533                 strcpy(input, p);
534
535                 if (strncmp(p, "raw ", 4) == 0) {
536                         outlen = encode_rfc(p + 4, data, sizeof(data));
537                         if (outlen == 0) {
538                                 fprintf(stderr, "Parse error in line %d of %s\n",
539                                         lineno, filename);
540                                 exit(1);
541                         }
542
543                 print_hex:
544                         if (outlen == 0) {
545                                 output[0] = 0;
546                                 continue;
547                         }
548
549                         data_len = outlen;
550                         for (i = 0; i < outlen; i++) {
551                                 snprintf(output + 3*i, sizeof(output),
552                                          "%02x ", data[i]);
553                         }
554                         outlen = strlen(output);
555                         output[outlen - 1] = '\0';
556                         continue;
557                 }
558
559                 if (strncmp(p, "data ", 5) == 0) {
560                         if (strcmp(p + 5, output) != 0) {
561                                 fprintf(stderr, "Mismatch in line %d of %s, expected: %s\n",
562                                         lineno, filename, output);
563                                 exit(1);
564                         }
565                         continue;
566                 }
567
568                 head = NULL;
569                 if (strncmp(p, "encode ", 7) == 0) {
570                         if (strcmp(p + 7, "-") == 0) {
571                                 p = output;
572                         } else {
573                                 p += 7;
574                         }
575
576                         rcode = nr_vp_sscanf(p, &head);
577                         if (rcode < 0) {
578                                 strcpy(output, nr_strerror(rcode));
579                                 continue;
580                         }
581
582                         attr = data;
583                         vp = head;
584                         while (vp != NULL) {
585                                 len = nr_vp2attr(NULL, NULL, &vp,
586                                                  attr, sizeof(data) - (attr - data));
587                                 if (len < 0) {
588                                         fprintf(stderr, "Failed encoding %s: %s\n",
589                                                 vp->da->name, nr_strerror(len));
590                                         exit(1);
591                                 }
592
593                                 attr += len;
594                                 if (len == 0) break;
595                         }
596                         
597                         nr_vp_free(&head);
598                         outlen = len;
599                         goto print_hex;
600                 }
601
602                 if (strncmp(p, "decode ", 7) == 0) {
603                         ssize_t my_len;
604
605                         if (strcmp(p + 7, "-") == 0) {
606                                 attr = data;
607                                 len = data_len;
608                         } else {
609                                 attr = data;
610                                 len = encode_hex(p + 7, data, sizeof(data));
611                                 if (len == 0) {
612                                         fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, filename);
613                                         exit(1);
614                                 }
615                         }
616
617                         while (len > 0) {
618                                 vp = NULL;
619                                 my_len = nr_attr2vp(NULL, NULL,
620                                                      attr, len, &vp);
621                                 if (my_len < 0) {
622                                         nr_vp_free(&head);
623                                         break;
624                                 }
625
626                                 if (my_len > len) {
627                                         fprintf(stderr, "Internal sanity check failed at %d\n", __LINE__);
628                                         exit(1);
629                                 }
630
631                                 *tail = vp;
632                                 while (vp) {
633                                         tail = &(vp->next);
634                                         vp = vp->next;
635                                 }                               
636
637                                 attr += my_len;
638                                 len -= my_len;                          
639                         }
640
641                         /*
642                          *      Output may be an error, and we ignore
643                          *      it if so.
644                          */
645                         if (head) {
646                                 p = output;
647                                 for (vp = head; vp != NULL; vp = vp->next) {
648                                         nr_vp_snprintf(p, sizeof(output) - (p - output), vp);
649                                         p += strlen(p);
650                                         
651                                         if (vp->next) {strcpy(p, ", ");
652                                                 p += 2;
653                                         }
654                                 }
655                                 
656                                 nr_vp_free(&head);
657                         } else if (my_len < 0) {
658                                 strcpy(output, nr_strerror(my_len));
659
660                         } else { /* zero-length attribute */
661                                 *output = '\0';
662                         }
663                         continue;
664                 }
665
666                 if (strncmp(p, "walk ", 5) == 0) {
667                         len = encode_hex(p + 5, data + 20, sizeof(data) - 20);
668
669                         if (len == 0) {
670                                 fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, filename);
671                                 exit(1);
672                         }
673
674                         memset(data, 0, 20);
675                         packet.data = data;
676                         packet.length = len + 20;
677                         packet.data[2] = ((len + 20) >> 8) & 0xff;
678                         packet.data[3] = (len + 20) & 0xff;
679
680                         *output = '\0';
681                         p = output;
682
683                         rcode = nr_packet_walk(&packet, &p, walk_callback);
684                         if (rcode < 0) {
685                                 snprintf(output, sizeof(output), "%d", rcode);
686                                 continue;
687                         }
688
689                         if (*output) output[strlen(output) - 1] = '\0';
690                         continue;
691                 }
692
693                 if (strncmp(p, "$INCLUDE ", 9) == 0) {
694                         p += 9;
695                         while (isspace((int) *p)) p++;
696
697                         process_file(p);
698                         continue;
699                 }
700
701                 if (strncmp(p, "secret ", 7) == 0) {
702                         strlcpy(secret, p + 7, sizeof(secret));
703                         strlcpy(output, secret, sizeof(output));
704                         continue;
705                 }
706
707                 if (strncmp(p, "code ", 5) == 0) {
708                         packet_code = atoi(p + 5);
709                         snprintf(output, sizeof(output), "%u", packet_code);
710                         continue;
711                 }
712
713                 if (strncmp(p, "sign ", 5) == 0) {
714                         len = encode_hex(p + 5, data + 20, sizeof(data) - 20);
715                         if (len == 0) {
716                                 fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, filename);
717                                 exit(1);
718                         }
719
720                         memset(&packet, 0, sizeof(packet));
721                         packet.secret = secret;
722                         packet.sizeof_secret = strlen(secret);
723                         packet.code = packet_code;
724                         packet.id = packet_id;
725                         memcpy(packet.vector, packet_vector, 16);
726                         packet.data = data;
727                         packet.length = len + 20;
728
729                         /*
730                          *      Hack encode the packet.
731                          */
732                         packet.data[0] = packet_code;
733                         packet.data[1] = packet_id;
734                         packet.data[2] = ((len + 20) >> 8) & 0xff;
735                         packet.data[3] = (len + 20) & 0xff;
736                         memcpy(packet.data + 4, packet_vector, 16);
737
738                         rcode = nr_packet_sign(&packet, NULL);
739                         if (rcode < 0) {
740                                 snprintf(output, sizeof(output), "%d", rcode);
741                                 continue;
742                         }
743
744                         memcpy(data, packet.vector, sizeof(packet.vector));
745                         outlen = sizeof(packet.vector);
746                         goto print_hex;
747                 }
748
749                 fprintf(stderr, "Unknown input at line %d of %s\n",
750                         lineno, filename);
751                 exit(1);
752         }
753
754         if (fp != stdin) fclose(fp);
755 }
756
757 int main(int argc, char *argv[])
758 {
759         int c;
760
761         if (argc < 2) {
762                 process_file("-");
763                 
764         } else {
765                 process_file(argv[1]);
766         }
767
768         return 0;
769 }