newvector should be a bool
[freeradius.git] / src / main / unittest.c
1 /*
2  * unittest.c   Unit test wrapper for the RADIUS daemon.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program 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
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000-2013  The FreeRADIUS server project
21  * Copyright 2013  Alan DeKok <aland@ox.org>
22  */
23
24 RCSID("$Id$")
25
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/rad_assert.h>
29
30 #ifdef HAVE_GETOPT_H
31 #       include <getopt.h>
32 #endif
33
34 /*
35  *  Global variables.
36  */
37 char const *progname = NULL;
38 char const *radius_dir = NULL;
39 char const *radacct_dir = NULL;
40 char const *radlog_dir = NULL;
41 char const *radlib_dir = NULL;
42 log_debug_t debug_flag = 0;
43 bool memory_report = false;
44 bool check_config = false;
45 bool log_stripped_names = false;
46
47 bool filedone = false;
48
49 char const *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION_STRING
50 #ifdef RADIUSD_VERSION_COMMIT
51 " (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
52 #endif
53 ", for host " HOSTINFO ", built on " __DATE__ " at " __TIME__;
54
55 /*
56  *      Static functions.
57  */
58 static void usage(int);
59
60 void listen_free(UNUSED rad_listen_t **head)
61 {
62         /* do nothing */
63 }
64
65
66 static rad_listen_t *listen_alloc(void *ctx)
67 {
68         rad_listen_t *this;
69
70         this = talloc_zero(ctx, rad_listen_t);
71         if (!this) return NULL;
72
73         this->type = RAD_LISTEN_AUTH;
74         this->recv = NULL;
75         this->send = NULL;
76         this->print = NULL;
77         this->encode = NULL;
78         this->decode = NULL;
79
80         /*
81          *      We probably don't care about this.  We can always add
82          *      fields later.
83          */
84         this->data = talloc_zero(this, listen_socket_t);
85         if (!this->data) {
86                 talloc_free(this);
87                 return NULL;
88         }
89
90         return this;
91 }
92
93 static RADCLIENT *client_alloc(void *ctx)
94 {
95         RADCLIENT *client;
96
97         client = talloc_zero(ctx, RADCLIENT);
98         if (!client) return NULL;
99
100         return client;
101 }
102
103 static REQUEST *request_setup(FILE *fp)
104 {
105         VALUE_PAIR *vp;
106         REQUEST *request;
107         vp_cursor_t cursor;
108
109         /*
110          *      Create and initialize the new request.
111          */
112         request = request_alloc(NULL);
113
114         request->packet = rad_alloc(request, false);
115         if (!request->packet) {
116                 ERROR("No memory");
117                 talloc_free(request);
118                 return NULL;
119         }
120
121         request->reply = rad_alloc(request, false);
122         if (!request->reply) {
123                 ERROR("No memory");
124                 talloc_free(request);
125                 return NULL;
126         }
127
128         request->listener = listen_alloc(request);
129         request->client = client_alloc(request);
130
131         request->number = 0;
132
133         request->master_state = REQUEST_ACTIVE;
134         request->child_state = REQUEST_RUNNING;
135         request->handle = NULL;
136         request->server = talloc_typed_strdup(request, "default");
137
138         request->root = &main_config;
139
140         /*
141          *      Read packet from fp
142          */
143         if (readvp2(&request->packet->vps, request->packet, fp, &filedone) < 0) {
144                 fr_perror("unittest");
145                 talloc_free(request);
146                 return NULL;
147         }
148
149         /*
150          *      Set the defaults for IPs, etc.
151          */
152         request->packet->code = PW_CODE_ACCESS_REQUEST;
153
154         request->packet->src_ipaddr.af = AF_INET;
155         request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
156         request->packet->src_port = 18120;
157
158         request->packet->dst_ipaddr.af = AF_INET;
159         request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
160         request->packet->dst_port = 1812;
161
162         /*
163          *      Copied from radclient
164          *
165          *      Fix up Digest-Attributes issues
166          */
167         for (vp = fr_cursor_init(&cursor, &request->packet->vps);
168              vp;
169              vp = fr_cursor_next(&cursor)) {
170                 /*
171                  *      Double quoted strings get marked up as xlat expansions,
172                  *      but we don't support that here.
173                  */
174                 if (vp->type == VT_XLAT) {
175                         vp->vp_strvalue = vp->value.xlat;
176                         vp->value.xlat = NULL;
177                         vp->type = VT_DATA;
178                 }
179
180                 if (!vp->da->vendor) switch (vp->da->attr) {
181                         default:
182                                 break;
183
184                                 /*
185                                  *      Allow it to set the packet type in
186                                  *      the attributes read from the file.
187                                  */
188                         case PW_PACKET_TYPE:
189                                 request->packet->code = vp->vp_integer;
190                                 break;
191
192                         case PW_PACKET_DST_PORT:
193                                 request->packet->dst_port = (vp->vp_integer & 0xffff);
194                                 break;
195
196                         case PW_PACKET_DST_IP_ADDRESS:
197                                 request->packet->dst_ipaddr.af = AF_INET;
198                                 request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
199                                 break;
200
201                         case PW_PACKET_DST_IPV6_ADDRESS:
202                                 request->packet->dst_ipaddr.af = AF_INET6;
203                                 request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
204                                 break;
205
206                         case PW_PACKET_SRC_PORT:
207                                 request->packet->src_port = (vp->vp_integer & 0xffff);
208                                 break;
209
210                         case PW_PACKET_SRC_IP_ADDRESS:
211                                 request->packet->src_ipaddr.af = AF_INET;
212                                 request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
213                                 break;
214
215                         case PW_PACKET_SRC_IPV6_ADDRESS:
216                                 request->packet->src_ipaddr.af = AF_INET6;
217                                 request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
218                                 break;
219
220                         case PW_CHAP_PASSWORD: {
221                                 int i, already_hex = 0;
222
223                                 /*
224                                  *      If it's 17 octets, it *might* be already encoded.
225                                  *      Or, it might just be a 17-character password (maybe UTF-8)
226                                  *      Check it for non-printable characters.  The odds of ALL
227                                  *      of the characters being 32..255 is (1-7/8)^17, or (1/8)^17,
228                                  *      or 1/(2^51), which is pretty much zero.
229                                  */
230                                 if (vp->length == 17) {
231                                         for (i = 0; i < 17; i++) {
232                                                 if (vp->vp_octets[i] < 32) {
233                                                         already_hex = 1;
234                                                         break;
235                                                 }
236                                         }
237                                 }
238
239                                 /*
240                                  *      Allow the user to specify ASCII or hex CHAP-Password
241                                  */
242                                 if (!already_hex) {
243                                         uint8_t *p;
244                                         size_t len, len2;
245
246                                         len = len2 = vp->length;
247                                         if (len2 < 17) len2 = 17;
248
249                                         p = talloc_zero_array(vp, uint8_t, len2);
250
251                                         memcpy(p, vp->vp_strvalue, len);
252
253                                         rad_chap_encode(request->packet,
254                                                         p,
255                                                         fr_rand() & 0xff, vp);
256                                         vp->vp_octets = p;
257                                         vp->length = 17;
258                                 }
259                         }
260                                 break;
261
262                         case PW_DIGEST_REALM:
263                         case PW_DIGEST_NONCE:
264                         case PW_DIGEST_METHOD:
265                         case PW_DIGEST_URI:
266                         case PW_DIGEST_QOP:
267                         case PW_DIGEST_ALGORITHM:
268                         case PW_DIGEST_BODY_DIGEST:
269                         case PW_DIGEST_CNONCE:
270                         case PW_DIGEST_NONCE_COUNT:
271                         case PW_DIGEST_USER_NAME:
272                                 /* overlapping! */
273                         {
274                                 DICT_ATTR const *da;
275                                 uint8_t *p, *q;
276
277                                 p = talloc_array(vp, uint8_t, vp->length + 2);
278
279                                 memcpy(p + 2, vp->vp_octets, vp->length);
280                                 p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
281                                 vp->length += 2;
282                                 p[1] = vp->length;
283
284                                 da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
285                                 rad_assert(da != NULL);
286                                 vp->da = da;
287
288                                 /*
289                                  *      Re-do pairmemsteal ourselves,
290                                  *      because we play games with
291                                  *      vp->da, and pairmemsteal goes
292                                  *      to GREAT lengths to sanitize
293                                  *      and fix and change and
294                                  *      double-check the various
295                                  *      fields.
296                                  */
297                                 memcpy(&q, &vp->vp_octets, sizeof(q));
298                                 talloc_free(q);
299
300                                 vp->vp_octets = talloc_steal(vp, p);
301                                 vp->type = VT_DATA;
302
303                                 VERIFY_VP(vp);
304                         }
305
306                         break;
307                         }
308         } /* loop over the VP's we read in */
309
310         if (debug_flag) {
311                 for (vp = fr_cursor_init(&cursor, &request->packet->vps);
312                      vp;
313                      vp = fr_cursor_next(&cursor)) {
314                         /*
315                          *      Take this opportunity to verify all the VALUE_PAIRs are still valid.
316                          */
317                         if (!talloc_get_type(vp, VALUE_PAIR)) {
318                                 ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
319
320                                 fr_log_talloc_report(vp);
321                                 rad_assert(0);
322                         }
323
324                         vp_print(fr_log_fp, vp);
325                 }
326                 fflush(fr_log_fp);
327         }
328
329         /*
330          *      Build the reply template from the request.
331          */
332         request->reply->sockfd = request->packet->sockfd;
333         request->reply->dst_ipaddr = request->packet->src_ipaddr;
334         request->reply->src_ipaddr = request->packet->dst_ipaddr;
335         request->reply->dst_port = request->packet->src_port;
336         request->reply->src_port = request->packet->dst_port;
337         request->reply->id = request->packet->id;
338         request->reply->code = 0; /* UNKNOWN code */
339         memcpy(request->reply->vector, request->packet->vector,
340                sizeof(request->reply->vector));
341         request->reply->vps = NULL;
342         request->reply->data = NULL;
343         request->reply->data_len = 0;
344
345         /*
346          *      Debugging
347          */
348         request->log.lvl = debug_flag;
349         request->log.func = vradlog_request;
350
351         request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
352         request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
353
354         return request;
355 }
356
357
358 static void print_packet(FILE *fp, RADIUS_PACKET *packet)
359 {
360         VALUE_PAIR *vp;
361         vp_cursor_t cursor;
362
363         if (!packet) {
364                 fprintf(fp, "\n");
365                 return;
366         }
367
368         fprintf(fp, "%s\n", fr_packet_codes[packet->code]);
369
370         for (vp = fr_cursor_init(&cursor, &packet->vps);
371              vp;
372              vp = fr_cursor_next(&cursor)) {
373                 /*
374                  *      Take this opportunity to verify all the VALUE_PAIRs are still valid.
375                  */
376                 if (!talloc_get_type(vp, VALUE_PAIR)) {
377                         ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
378
379                         fr_log_talloc_report(vp);
380                         rad_assert(0);
381                 }
382
383                 vp_print(fp, vp);
384         }
385         fflush(fp);
386 }
387
388 /*
389  *      The main guy.
390  */
391 int main(int argc, char *argv[])
392 {
393         int rcode = EXIT_SUCCESS;
394         int argval;
395         const char *input_file = NULL;
396         const char *output_file = NULL;
397         const char *filter_file = NULL;
398         FILE *fp;
399         REQUEST *request = NULL;
400         VALUE_PAIR *vp;
401         VALUE_PAIR *filter_vps = NULL;
402
403         /*
404          *      If the server was built with debugging enabled always install
405          *      the basic fatal signal handlers.
406          */
407 #ifndef NDEBUG
408         if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
409                 fr_perror("unittest");
410                 exit(EXIT_FAILURE);
411         }
412 #endif
413
414         if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL)
415                 progname = argv[0];
416         else
417                 progname++;
418
419         debug_flag = 0;
420         set_radius_dir(NULL, RADIUS_DIR);
421
422         /*
423          *      Ensure that the configuration is initialized.
424          */
425         memset(&main_config, 0, sizeof(main_config));
426         main_config.myip.af = AF_UNSPEC;
427         main_config.port = 0;
428         main_config.name = "radiusd";
429
430         /*
431          *      The tests should have only IPs, not host names.
432          */
433         fr_hostname_lookups = false;
434
435         /*
436          *      We always log to stdout.
437          */
438         fr_log_fp = stdout;
439         default_log.dst = L_DST_STDOUT;
440         default_log.fd = STDOUT_FILENO;
441
442         /*  Process the options.  */
443         while ((argval = getopt(argc, argv, "d:D:f:hi:mMn:o:xX")) != EOF) {
444
445                 switch(argval) {
446                         case 'd':
447                                 set_radius_dir(NULL, optarg);
448                                 break;
449
450                         case 'D':
451                                 main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg);
452                                 break;
453
454                         case 'f':
455                                 filter_file = optarg;
456                                 break;
457
458                         case 'h':
459                                 usage(0);
460                                 break;
461
462                         case 'i':
463                                 input_file = optarg;
464                                 break;
465
466                         case 'm':
467                                 main_config.debug_memory = true;
468                                 break;
469
470                         case 'M':
471                                 memory_report = true;
472                                 main_config.debug_memory = true;
473                                 break;
474
475                         case 'n':
476                                 main_config.name = optarg;
477                                 break;
478
479                         case 'o':
480                                 output_file = optarg;
481                                 break;
482
483                         case 'X':
484                                 debug_flag += 2;
485                                 main_config.log_auth = true;
486                                 main_config.log_auth_badpass = true;
487                                 main_config.log_auth_goodpass = true;
488                                 break;
489
490                         case 'x':
491                                 debug_flag++;
492                                 break;
493
494                         default:
495                                 usage(1);
496                                 break;
497                 }
498         }
499
500         if (debug_flag) {
501                 version();
502         }
503         fr_debug_flag = debug_flag;
504
505         /*
506          *      Mismatch between the binary and the libraries it depends on
507          */
508         if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
509                 fr_perror("radiusd");
510                 exit(EXIT_FAILURE);
511         }
512
513         /*  Read the configuration files, BEFORE doing anything else.  */
514         if (main_config_init() < 0) {
515                 rcode = EXIT_FAILURE;
516                 goto finish;
517         }
518
519         /*
520          *  Load the modules
521          */
522         if (modules_init(main_config.config) < 0) {
523                 rcode = EXIT_FAILURE;
524                 goto finish;
525         }
526
527         /* Set the panic action (if required) */
528         if (main_config.panic_action &&
529 #ifndef NDEBUG
530             !getenv("PANIC_ACTION") &&
531 #endif
532             (fr_fault_setup(main_config.panic_action, argv[0]) < 0)) {
533                 rcode = EXIT_FAILURE;
534                 goto finish;
535         }
536
537         setlinebuf(stdout); /* unbuffered output */
538
539         if (!input_file || (strcmp(input_file, "-") == 0)) {
540                 fp = stdin;
541         } else {
542                 fp = fopen(input_file, "r");
543                 if (!fp) {
544                         fprintf(stderr, "Failed reading %s: %s\n",
545                                 input_file, fr_syserror(errno));
546                         goto finish;
547                 }
548         }
549
550         /*
551          *      Grab the VPs from stdin, or from the file.
552          */
553         request = request_setup(fp);
554         if (!request) {
555                 fprintf(stderr, "Failed reading input: %s\n", fr_strerror());
556                 rcode = EXIT_FAILURE;
557                 goto finish;
558         }
559
560         /*
561          *      No filter file, OR there's no more input, OR we're
562          *      reading from a file, and it's different from the
563          *      filter file.
564          */
565         if (!filter_file || filedone ||
566             ((input_file != NULL) && (strcmp(filter_file, input_file) != 0))) {
567                 if (output_file) {
568                         fclose(fp);
569                         fp = NULL;
570                 }
571                 filedone = false;
572         }
573
574         /*
575          *      There is a filter file.  If necessary, open it.  If we
576          *      already are reading it via "input_file", then we don't
577          *      need to re-open it.
578          */
579         if (filter_file) {
580                 if (!fp) {
581                         fp = fopen(filter_file, "r");
582                         if (!fp) {
583                                 fprintf(stderr, "Failed reading %s: %s\n", filter_file, strerror(errno));
584                                 rcode = EXIT_FAILURE;
585                                 goto finish;
586                         }
587                 }
588
589
590                 if (readvp2(&filter_vps, request, fp, &filedone) < 0) {
591                         fprintf(stderr, "Failed reading attributes from %s: %s\n",
592                                 filter_file, fr_strerror());
593                         rcode = EXIT_FAILURE;
594                         goto finish;
595                 }
596
597                 /*
598                  *      FIXME: loop over input packets.
599                  */
600                 fclose(fp);
601         }
602
603         rad_virtual_server(request);
604
605         if (!output_file || (strcmp(output_file, "-") == 0)) {
606                 fp = stdout;
607         } else {
608                 fp = fopen(output_file, "w");
609                 if (!fp) {
610                         fprintf(stderr, "Failed writing %s: %s\n",
611                                 output_file, fr_syserror(errno));
612                         exit(EXIT_FAILURE);
613                 }
614         }
615
616         print_packet(fp, request->reply);
617
618         if (output_file) fclose(fp);
619
620         /*
621          *      Update the list with the response type.
622          */
623         vp = radius_paircreate(request->reply, &request->reply->vps,
624                                PW_RESPONSE_PACKET_TYPE, 0);
625         vp->vp_integer = request->reply->code;
626
627         {
628                 VALUE_PAIR const *failed[2];
629
630                 if (filter_vps && !pairvalidate(failed, filter_vps, request->reply->vps)) {
631                         pairvalidate_debug(request, failed);
632                         fr_perror("Output file %s does not match attributes in filter %s",
633                                   output_file ? output_file : input_file, filter_file);
634                         rcode = EXIT_FAILURE;
635                         goto finish;
636                 }
637         }
638
639         INFO("Exiting normally");
640
641 finish:
642         talloc_free(request);
643
644         /*
645          *      Detach any modules.
646          */
647         modules_free();
648
649         xlat_free();            /* modules may have xlat's */
650
651         /*
652          *      Free the configuration items.
653          */
654         main_config_free();
655
656         if (memory_report) {
657                 INFO("Allocated memory at time of report:");
658                 fr_log_talloc_report(NULL);
659         }
660
661         return rcode;
662 }
663
664
665 /*
666  *  Display the syntax for starting this program.
667  */
668 static void NEVER_RETURNS usage(int status)
669 {
670         FILE *output = status?stderr:stdout;
671
672         fprintf(output, "Usage: %s [options]\n", progname);
673         fprintf(output, "Options:\n");
674         fprintf(output, "  -d raddb_dir  Configuration files are in \"raddb_dir/*\".\n");
675         fprintf(output, "  -D dict_dir   Dictionary files are in \"dict_dir/*\".\n");
676         fprintf(output, "  -f file       Filter reply against attributes in 'file'.\n");
677         fprintf(output, "  -h            Print this help message.\n");
678         fprintf(output, "  -m            On SIGINT or SIGQUIT exit cleanly instead of immediately.\n");
679         fprintf(output, "  -n name       Read raddb/name.conf instead of raddb/radiusd.conf.\n");
680         fprintf(output, "  -X            Turn on full debugging.\n");
681         fprintf(output, "  -x            Turn on additional debugging. (-xx gives more debugging).\n");
682         exit(status);
683 }