All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
unittest.c
Go to the documentation of this file.
1 /*
2  * unittest.c Unit test wrapper for the RADIUS daemon.
3  *
4  * Version: $Id: c1767c5a35bbb45e44c79447ba96e7801e3daae5 $
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: c1767c5a35bbb45e44c79447ba96e7801e3daae5 $")
25 
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/map_proc.h>
29 #include <freeradius-devel/state.h>
30 #include <freeradius-devel/rad_assert.h>
31 
32 #ifdef HAVE_GETOPT_H
33 # include <getopt.h>
34 #endif
35 
36 #include <ctype.h>
37 
38 /*
39  * Global variables.
40  */
41 char const *radacct_dir = NULL;
42 char const *radlog_dir = NULL;
43 bool log_stripped_names = false;
44 
45 static bool memory_report = false;
46 static bool filedone = false;
47 
48 char const *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION_STRING
49 #ifdef RADIUSD_VERSION_COMMIT
50 " (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
51 #endif
52 ", for host " HOSTINFO ", built on " __DATE__ " at " __TIME__;
53 
54 /*
55  * Static functions.
56  */
57 static void usage(int);
58 
59 int listen_bootstrap(UNUSED CONF_SECTION *server, UNUSED CONF_SECTION *cs, UNUSED char const *server_name)
60 {
61  return -1;
62 }
63 
65 {
66  /* do nothing */
67 }
68 
69 
70 static rad_listen_t *listen_alloc(void *ctx)
71 {
72  rad_listen_t *this;
73 
74  this = talloc_zero(ctx, rad_listen_t);
75  if (!this) return NULL;
76 
77  this->type = RAD_LISTEN_AUTH;
78  this->recv = NULL;
79  this->send = NULL;
80  this->print = NULL;
81  this->encode = NULL;
82  this->decode = NULL;
83 
84  /*
85  * We probably don't care about this. We can always add
86  * fields later.
87  */
88  this->data = talloc_zero(this, listen_socket_t);
89  if (!this->data) {
90  talloc_free(this);
91  return NULL;
92  }
93 
94  return this;
95 }
96 
97 static RADCLIENT *client_alloc(void *ctx)
98 {
99  RADCLIENT *client;
100 
101  client = talloc_zero(ctx, RADCLIENT);
102  if (!client) return NULL;
103 
104  return client;
105 }
106 
107 static REQUEST *request_setup(FILE *fp)
108 {
109  VALUE_PAIR *vp;
110  REQUEST *request;
111  vp_cursor_t cursor;
112  struct timeval now;
113 
114  /*
115  * Create and initialize the new request.
116  */
117  request = request_alloc(NULL);
118  gettimeofday(&now, NULL);
119  request->timestamp = now;
120 
121  request->packet = fr_radius_alloc(request, false);
122  if (!request->packet) {
123  ERROR("No memory");
124  talloc_free(request);
125  return NULL;
126  }
127  request->packet->timestamp = now;
128 
129  request->reply = fr_radius_alloc(request, false);
130  if (!request->reply) {
131  ERROR("No memory");
132  talloc_free(request);
133  return NULL;
134  }
135 
136  request->listener = listen_alloc(request);
137  request->client = client_alloc(request);
138 
139  request->number = 0;
140 
141  request->master_state = REQUEST_ACTIVE;
142  request->child_state = REQUEST_RUNNING;
143  request->handle = NULL;
144  request->server = talloc_typed_strdup(request, "default");
145 
146  request->root = &main_config;
147 
148  /*
149  * Read packet from fp
150  */
151  if (fr_pair_list_afrom_file(request->packet, &request->packet->vps, fp, &filedone) < 0) {
152  fr_perror("unittest");
153  talloc_free(request);
154  return NULL;
155  }
156 
157  /*
158  * Set the defaults for IPs, etc.
159  */
160  request->packet->code = PW_CODE_ACCESS_REQUEST;
161 
162  request->packet->src_ipaddr.af = AF_INET;
163  request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
164  request->packet->src_port = 18120;
165 
166  request->packet->dst_ipaddr.af = AF_INET;
167  request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
168  request->packet->dst_port = 1812;
169 
170  /*
171  * Copied from radclient
172  */
173 #if 1
174  /*
175  * Fix up Digest-Attributes issues
176  */
177  for (vp = fr_cursor_init(&cursor, &request->packet->vps);
178  vp;
179  vp = fr_cursor_next(&cursor)) {
180  /*
181  * Double quoted strings get marked up as xlat expansions,
182  * but we don't support that here.
183  */
184  if (vp->type == VT_XLAT) {
185  vp->vp_strvalue = vp->xlat;
186  vp->xlat = NULL;
187  vp->type = VT_DATA;
188  }
189 
190  if (!vp->da->vendor) switch (vp->da->attr) {
191  default:
192  break;
193 
194  /*
195  * Allow it to set the packet type in
196  * the attributes read from the file.
197  */
198  case PW_PACKET_TYPE:
199  request->packet->code = vp->vp_integer;
200  break;
201 
202  case PW_PACKET_DST_PORT:
203  request->packet->dst_port = (vp->vp_integer & 0xffff);
204  break;
205 
206  case PW_PACKET_DST_IP_ADDRESS:
207  request->packet->dst_ipaddr.af = AF_INET;
208  request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
209  request->packet->dst_ipaddr.prefix = 32;
210  break;
211 
212  case PW_PACKET_DST_IPV6_ADDRESS:
213  request->packet->dst_ipaddr.af = AF_INET6;
214  request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
215  request->packet->dst_ipaddr.prefix = 128;
216  break;
217 
218  case PW_PACKET_SRC_PORT:
219  request->packet->src_port = (vp->vp_integer & 0xffff);
220  break;
221 
222  case PW_PACKET_SRC_IP_ADDRESS:
223  request->packet->src_ipaddr.af = AF_INET;
224  request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
225  request->packet->src_ipaddr.prefix = 32;
226  break;
227 
228  case PW_PACKET_SRC_IPV6_ADDRESS:
229  request->packet->src_ipaddr.af = AF_INET6;
230  request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
231  request->packet->src_ipaddr.prefix = 128;
232  break;
233 
234  case PW_CHAP_PASSWORD: {
235  int i, already_hex = 0;
236 
237  /*
238  * If it's 17 octets, it *might* be already encoded.
239  * Or, it might just be a 17-character password (maybe UTF-8)
240  * Check it for non-printable characters. The odds of ALL
241  * of the characters being 32..255 is (1-7/8)^17, or (1/8)^17,
242  * or 1/(2^51), which is pretty much zero.
243  */
244  if (vp->vp_length == 17) {
245  for (i = 0; i < 17; i++) {
246  if (vp->vp_octets[i] < 32) {
247  already_hex = 1;
248  break;
249  }
250  }
251  }
252 
253  /*
254  * Allow the user to specify ASCII or hex CHAP-Password
255  */
256  if (!already_hex) {
257  uint8_t *p;
258  size_t len, len2;
259 
260  len = len2 = vp->vp_length;
261  if (len2 < 17) len2 = 17;
262 
263  p = talloc_zero_array(vp, uint8_t, len2);
264 
265  memcpy(p, vp->vp_strvalue, len);
266 
267  fr_radius_encode_chap_password(p, request->packet, fr_rand() & 0xff, vp);
268  vp->vp_octets = p;
269  vp->vp_length = 17;
270  }
271  }
272  break;
273 
274  case PW_DIGEST_REALM:
275  case PW_DIGEST_NONCE:
276  case PW_DIGEST_METHOD:
277  case PW_DIGEST_URI:
278  case PW_DIGEST_QOP:
279  case PW_DIGEST_ALGORITHM:
280  case PW_DIGEST_BODY_DIGEST:
281  case PW_DIGEST_CNONCE:
282  case PW_DIGEST_NONCE_COUNT:
283  case PW_DIGEST_USER_NAME:
284  /* overlapping! */
285  {
286  fr_dict_attr_t const *da;
287  uint8_t *p, *q;
288 
289  p = talloc_array(vp, uint8_t, vp->vp_length + 2);
290 
291  memcpy(p + 2, vp->vp_octets, vp->vp_length);
292  p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
293  vp->vp_length += 2;
294  p[1] = vp->vp_length;
295 
297  rad_assert(da != NULL);
298  vp->da = da;
299 
300  /*
301  * Re-do fr_pair_value_memsteal ourselves,
302  * because we play games with
303  * vp->da, and fr_pair_value_memsteal goes
304  * to GREAT lengths to sanitize
305  * and fix and change and
306  * double-check the various
307  * fields.
308  */
309  memcpy(&q, &vp->vp_octets, sizeof(q));
310  talloc_free(q);
311 
312  vp->vp_octets = talloc_steal(vp, p);
313  vp->type = VT_DATA;
314 
315  VERIFY_VP(vp);
316  }
317 
318  break;
319  }
320  } /* loop over the VP's we read in */
321 #endif
322 
323  if (rad_debug_lvl) {
324  for (vp = fr_cursor_init(&cursor, &request->packet->vps);
325  vp;
326  vp = fr_cursor_next(&cursor)) {
327  /*
328  * Take this opportunity to verify all the VALUE_PAIRs are still valid.
329  */
330  if (!talloc_get_type(vp, VALUE_PAIR)) {
331  ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
332 
334  rad_assert(0);
335  }
336 
338  }
339  fflush(fr_log_fp);
340  }
341 
342  /*
343  * FIXME: set IPs, etc.
344  */
345  request->packet->code = PW_CODE_ACCESS_REQUEST;
346 
347  request->packet->src_ipaddr.af = AF_INET;
348  request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
349  request->packet->src_port = 18120;
350 
351  request->packet->dst_ipaddr.af = AF_INET;
352  request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
353  request->packet->dst_port = 1812;
354 
355  /*
356  * Build the reply template from the request.
357  */
358  request->reply->sockfd = request->packet->sockfd;
359  request->reply->dst_ipaddr = request->packet->src_ipaddr;
360  request->reply->src_ipaddr = request->packet->dst_ipaddr;
361  request->reply->dst_port = request->packet->src_port;
362  request->reply->src_port = request->packet->dst_port;
363  request->reply->id = request->packet->id;
364  request->reply->code = 0; /* UNKNOWN code */
365  memcpy(request->reply->vector, request->packet->vector,
366  sizeof(request->reply->vector));
367  request->reply->vps = NULL;
368  request->reply->data = NULL;
369  request->reply->data_len = 0;
370 
371  /*
372  * Debugging
373  */
374  request->log.lvl = rad_debug_lvl;
375  request->log.func = vradlog_request;
376 
377  request->username = fr_pair_find_by_num(request->packet->vps, 0, PW_USER_NAME, TAG_ANY);
378  request->password = fr_pair_find_by_num(request->packet->vps, 0, PW_USER_PASSWORD, TAG_ANY);
379 
380  return request;
381 }
382 
383 
384 static void print_packet(FILE *fp, RADIUS_PACKET *packet)
385 {
386  VALUE_PAIR *vp;
387  vp_cursor_t cursor;
388 
389  if (!packet) {
390  fprintf(fp, "\n");
391  return;
392  }
393 
394  fprintf(fp, "%s\n", fr_packet_codes[packet->code]);
395 
396  for (vp = fr_cursor_init(&cursor, &packet->vps);
397  vp;
398  vp = fr_cursor_next(&cursor)) {
399  /*
400  * Take this opportunity to verify all the VALUE_PAIRs are still valid.
401  */
402  if (!talloc_get_type(vp, VALUE_PAIR)) {
403  ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
404 
406  rad_assert(0);
407  }
408 
409  fr_pair_fprint(fp, vp);
410  }
411  fflush(fp);
412 }
413 
414 
415 #include <freeradius-devel/modpriv.h>
416 
417 /*
418  * %{poke:sql.foo=bar}
419  */
420 static ssize_t xlat_poke(char **out, size_t outlen,
421  UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
422  REQUEST *request, char const *fmt)
423 {
424  int i;
425  void *data, *base;
426  char *p, *q;
427  module_instance_t *mi;
428  char *buffer;
429  CONF_SECTION *modules;
430  CONF_PAIR *cp;
431  CONF_PARSER const *variables;
432  size_t len;
433 
434  rad_assert(outlen > 1);
435  rad_assert(request != NULL);
436  rad_assert(fmt != NULL);
437  rad_assert(out != NULL);
438  rad_assert(*out);
439 
440  modules = cf_section_sub_find(request->root->config, "modules");
441  if (!modules) return 0;
442 
443  buffer = talloc_strdup(request, fmt);
444  if (!buffer) return 0;
445 
446  p = strchr(buffer, '.');
447  if (!p) return 0;
448 
449  *(p++) = '\0';
450 
451  mi = module_find(modules, buffer);
452  if (!mi) {
453  RDEBUG("Failed finding module '%s'", buffer);
454  fail:
455  talloc_free(buffer);
456  return 0;
457  }
458 
459  q = strchr(p, '=');
460  if (!q) {
461  RDEBUG("Failed finding '=' in string '%s'", fmt);
462  goto fail;
463  }
464 
465  *(q++) = '\0';
466 
467  if (strchr(p, '.') != NULL) {
468  RDEBUG("Can't do sub-sections right now");
469  goto fail;
470  }
471 
472  cp = cf_pair_find(mi->cs, p);
473  if (!cp) {
474  RDEBUG("No such item '%s'", p);
475  goto fail;
476  }
477 
478  /*
479  * Copy the old value to the output buffer, that way
480  * tests can restore it later, if they need to.
481  */
482  len = strlcpy(*out, cf_pair_value(cp), outlen);
483 
484  if (cf_pair_replace(mi->cs, cp, q) < 0) {
485  RDEBUG("Failed replacing pair");
486  goto fail;
487  }
488 
489  base = mi->insthandle;
490  variables = mi->entry->module->config;
491 
492  /*
493  * Handle the known configuration parameters.
494  */
495  for (i = 0; variables[i].name != NULL; i++) {
496  int ret;
497 
498  if (variables[i].type == PW_TYPE_SUBSECTION) continue;
499  /* else it's a CONF_PAIR */
500 
501  /*
502  * Not the pair we want. Skip it.
503  */
504  if (strcmp(variables[i].name, p) != 0) continue;
505 
506  if (variables[i].data) {
507  data = variables[i].data; /* prefer this. */
508  } else if (base) {
509  data = ((char *)base) + variables[i].offset;
510  } else {
511  DEBUG2("Internal sanity check 2 failed in cf_section_parse");
512  goto fail;
513  }
514 
515  /*
516  * Parse the pair we found, or a default value.
517  */
518  ret = cf_pair_parse(mi->cs, variables[i].name, variables[i].type,
519  data, variables[i].dflt, variables[i].quote);
520  if (ret < 0) {
521  DEBUG2("Failed inserting new value into module instance data");
522  goto fail;
523  }
524  break; /* we found it, don't do any more */
525  }
526 
527  talloc_free(buffer);
528 
529  return len;
530 }
531 
532 
533 /*
534  * Read a file compose of xlat's and expected results
535  */
536 static bool do_xlats(char const *filename, FILE *fp)
537 {
538  int lineno = 0;
539  ssize_t len;
540  char *p;
541  char input[8192];
542  char output[8192];
543  REQUEST *request;
544  struct timeval now;
545 
546  /*
547  * Create and initialize the new request.
548  */
549  request = request_alloc(NULL);
550  gettimeofday(&now, NULL);
551  request->timestamp = now;
552 
553  request->log.lvl = rad_debug_lvl;
554  request->log.func = vradlog_request;
555 
556  output[0] = '\0';
557 
558  while (fgets(input, sizeof(input), fp) != NULL) {
559  lineno++;
560 
561  /*
562  * Ignore blank lines and comments
563  */
564  p = input;
565  while (isspace((int) *p)) p++;
566 
567  if (*p < ' ') continue;
568  if (*p == '#') continue;
569 
570  p = strchr(p, '\n');
571  if (!p) {
572  if (!feof(fp)) {
573  fprintf(stderr, "Line %d too long in %s\n",
574  lineno, filename);
575  TALLOC_FREE(request);
576  return false;
577  }
578  } else {
579  *p = '\0';
580  }
581 
582  /*
583  * Look for "xlat"
584  */
585  if (strncmp(input, "xlat ", 5) == 0) {
586  ssize_t slen;
587  char const *error = NULL;
588  char *fmt = talloc_typed_strdup(NULL, input + 5);
589  xlat_exp_t *head;
590 
591  slen = xlat_tokenize(fmt, fmt, &head, &error);
592  if (slen <= 0) {
593  talloc_free(fmt);
594  snprintf(output, sizeof(output), "ERROR offset %d '%s'", (int) -slen, error);
595  continue;
596  }
597 
598  if (input[slen + 5] != '\0') {
599  talloc_free(fmt);
600  snprintf(output, sizeof(output), "ERROR offset %d 'Too much text' ::%s::", (int) slen, input + slen + 5);
601  continue;
602  }
603 
604  len = radius_xlat_struct(output, sizeof(output), request, head, NULL, NULL);
605  if (len < 0) {
606  snprintf(output, sizeof(output), "ERROR expanding xlat: %s", fr_strerror());
607  continue;
608  }
609 
610  TALLOC_FREE(fmt); /* also frees 'head' */
611  continue;
612  }
613 
614  /*
615  * Look for "data".
616  */
617  if (strncmp(input, "data ", 5) == 0) {
618  if (strcmp(input + 5, output) != 0) {
619  fprintf(stderr, "Mismatch at line %d of %s\n\tgot : %s\n\texpected : %s\n",
620  lineno, filename, output, input + 5);
621  TALLOC_FREE(request);
622  return false;
623  }
624  continue;
625  }
626 
627  fprintf(stderr, "Unknown keyword in %s[%d]\n", filename, lineno);
628  TALLOC_FREE(request);
629  return false;
630  }
631 
632  TALLOC_FREE(request);
633  return true;
634 }
635 
636 
637 static rlm_rcode_t mod_map_proc(UNUSED void *mod_inst, UNUSED void *proc_inst, UNUSED REQUEST *request,
638  UNUSED char const *src, UNUSED vp_map_t const *maps)
639 {
640  return RLM_MODULE_FAIL;
641 }
642 
643 /*
644  * The main guy.
645  */
646 int main(int argc, char *argv[])
647 {
648  int rcode = EXIT_SUCCESS;
649  int argval;
650  const char *input_file = NULL;
651  const char *output_file = NULL;
652  const char *filter_file = NULL;
653  FILE *fp;
654  REQUEST *request = NULL;
655  VALUE_PAIR *vp;
656  VALUE_PAIR *filter_vps = NULL;
657  bool xlat_only = false;
658  fr_state_tree_t *state = NULL;
659 
661 
662  /*
663  * If the server was built with debugging enabled always install
664  * the basic fatal signal handlers.
665  */
666 #ifndef NDEBUG
667  if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
668  fr_perror("unittest");
669  exit(EXIT_FAILURE);
670  }
671 #endif
672 
673  rad_debug_lvl = 0;
674  set_radius_dir(NULL, RADIUS_DIR);
675 
676  /*
677  * Ensure that the configuration is initialized.
678  */
679  memset(&main_config, 0, sizeof(main_config));
680  main_config.name = "unittest";
681 
682  /*
683  * The tests should have only IPs, not host names.
684  */
685  fr_hostname_lookups = false;
686 
687  /*
688  * We always log to stdout.
689  */
690  fr_log_fp = stdout;
692  default_log.fd = STDOUT_FILENO;
693 
694  /* Process the options. */
695  while ((argval = getopt(argc, argv, "d:D:f:hi:mMn:o:O:xX")) != EOF) {
696 
697  switch (argval) {
698  case 'd':
699  set_radius_dir(NULL, optarg);
700  break;
701 
702  case 'D':
704  break;
705 
706  case 'f':
707  filter_file = optarg;
708  break;
709 
710  case 'h':
711  usage(0);
712  break;
713 
714  case 'i':
715  input_file = optarg;
716  break;
717 
718  case 'm':
719  main_config.debug_memory = true;
720  break;
721 
722  case 'M':
723  memory_report = true;
724  main_config.debug_memory = true;
725  break;
726 
727  case 'n':
728  main_config.name = optarg;
729  break;
730 
731  case 'o':
732  output_file = optarg;
733  break;
734 
735  case 'O':
736  if (strcmp(optarg, "xlat_only") == 0) {
737  xlat_only = true;
738  break;
739  }
740 
741  fprintf(stderr, "Unknown option '%s'\n", optarg);
742  exit(EXIT_FAILURE);
743 
744  case 'X':
745  rad_debug_lvl += 2;
746  main_config.log_auth = true;
749  break;
750 
751  case 'x':
752  rad_debug_lvl++;
753  break;
754 
755  default:
756  usage(1);
757  break;
758  }
759  }
760 
763 
764  /*
765  * Mismatch between the binary and the libraries it depends on
766  */
768  fr_perror("%s", main_config.name);
769  exit(EXIT_FAILURE);
770  }
771 
772  /*
773  * Initialising OpenSSL once, here, is safer than having individual modules do it.
774  */
775 #ifdef HAVE_OPENSSL_CRYPTO_H
776  if (tls_global_init() < 0) {
777  rcode = EXIT_FAILURE;
778  goto finish;
779  }
780 #endif
781 
782  if (xlat_register(NULL, "poke", xlat_poke, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN) < 0) {
783  rcode = EXIT_FAILURE;
784  goto finish;
785  }
786 
787  if (map_proc_register(NULL, "test-fail", mod_map_proc, NULL, NULL, 0) < 0) {
788  rcode = EXIT_FAILURE;
789  goto finish;
790  }
791 
792 
793  /* Read the configuration files, BEFORE doing anything else. */
794  if (main_config_init() < 0) {
795  exit_failure:
796  rcode = EXIT_FAILURE;
797  goto finish;
798  }
799 
800  /*
801  * Setup dummy virtual server
802  */
804 
805  /*
806  * Initialize Auth-Type, etc. in the virtual servers
807  * before loading the modules. Some modules need those
808  * to be defined.
809  */
810  if (virtual_servers_bootstrap(main_config.config) < 0) goto exit_failure;
811 
812  /*
813  * Bootstrap the modules. This links to them, and runs
814  * their "bootstrap" routines.
815  *
816  * After this step, all dynamic attributes, xlats, etc. are defined.
817  */
818  if (modules_bootstrap(main_config.config) < 0) exit(EXIT_FAILURE);
819 
820  /*
821  * Load the modules
822  */
823  if (modules_init(main_config.config) < 0) goto exit_failure;
824 
825  /*
826  * And then load the virtual servers.
827  */
828  if (virtual_servers_init(main_config.config) < 0) goto exit_failure;
829 
830  state = fr_state_tree_init(NULL, main_config.max_requests * 2, 10);
831 
832  /*
833  * Set the panic action (if required)
834  */
835  {
836  char const *panic_action = NULL;
837 
838  panic_action = getenv("PANIC_ACTION");
839  if (!panic_action) panic_action = main_config.panic_action;
840 
841  if (panic_action && (fr_fault_setup(panic_action, argv[0]) < 0)) {
842  fr_perror("%s", main_config.name);
843  exit(EXIT_FAILURE);
844  }
845  }
846 
847  setlinebuf(stdout); /* unbuffered output */
848 
849  if (!input_file || (strcmp(input_file, "-") == 0)) {
850  fp = stdin;
851  } else {
852  fp = fopen(input_file, "r");
853  if (!fp) {
854  fprintf(stderr, "Failed reading %s: %s\n",
855  input_file, strerror(errno));
856  rcode = EXIT_FAILURE;
857  goto finish;
858  }
859  }
860 
861  /*
862  * For simplicity, read xlat's.
863  */
864  if (xlat_only) {
865  if (!do_xlats(input_file, fp)) rcode = EXIT_FAILURE;
866  if (input_file) fclose(fp);
867  goto finish;
868  }
869 
870  /*
871  * Grab the VPs from stdin, or from the file.
872  */
873  request = request_setup(fp);
874  if (!request) {
875  fprintf(stderr, "Failed reading input: %s\n", fr_strerror());
876  rcode = EXIT_FAILURE;
877  goto finish;
878  }
879 
880  /*
881  * No filter file, OR there's no more input, OR we're
882  * reading from a file, and it's different from the
883  * filter file.
884  */
885  if (!filter_file || filedone ||
886  ((input_file != NULL) && (strcmp(filter_file, input_file) != 0))) {
887  if (output_file) {
888  fclose(fp);
889  fp = NULL;
890  }
891  filedone = false;
892  }
893 
894  /*
895  * There is a filter file. If necessary, open it. If we
896  * already are reading it via "input_file", then we don't
897  * need to re-open it.
898  */
899  if (filter_file) {
900  if (!fp) {
901  fp = fopen(filter_file, "r");
902  if (!fp) {
903  fprintf(stderr, "Failed reading %s: %s\n", filter_file, strerror(errno));
904  rcode = EXIT_FAILURE;
905  goto finish;
906  }
907  }
908 
909 
910  if (fr_pair_list_afrom_file(request, &filter_vps, fp, &filedone) < 0) {
911  fprintf(stderr, "Failed reading attributes from %s: %s\n",
912  filter_file, fr_strerror());
913  rcode = EXIT_FAILURE;
914  goto finish;
915  }
916 
917  /*
918  * FIXME: loop over input packets.
919  */
920  fclose(fp);
921  }
922 
923  rad_virtual_server(request);
924 
925  if (!output_file || (strcmp(output_file, "-") == 0)) {
926  fp = stdout;
927  } else {
928  fp = fopen(output_file, "w");
929  if (!fp) {
930  fprintf(stderr, "Failed writing %s: %s\n",
931  output_file, strerror(errno));
932  exit(EXIT_FAILURE);
933  }
934  }
935 
936  print_packet(fp, request->reply);
937 
938  if (output_file) fclose(fp);
939 
940  /*
941  * Update the list with the response type.
942  */
943  vp = radius_pair_create(request->reply, &request->reply->vps,
944  PW_RESPONSE_PACKET_TYPE, 0);
945  vp->vp_integer = request->reply->code;
946 
947  {
948  VALUE_PAIR const *failed[2];
949 
950  if (filter_vps && !fr_pair_validate(failed, filter_vps, request->reply->vps)) {
951  fr_pair_validate_debug(request, failed);
952  fr_perror("Output file %s does not match attributes in filter %s (%s)",
953  output_file ? output_file : input_file, filter_file, fr_strerror());
954  rcode = EXIT_FAILURE;
955  goto finish;
956  }
957  }
958 
959  INFO("Exiting normally");
960 
961 finish:
962  talloc_free(request);
963  talloc_free(state);
964 
965  /*
966  * Free the configuration items.
967  */
969 
970  /*
971  * Detach any modules.
972  */
973  modules_free();
974 
975  xlat_unregister(NULL, "poke", xlat_poke);
976 
977  xlat_free(); /* modules may have xlat's */
978 
979  if (memory_report) {
980  INFO("Allocated memory at time of report:");
981  fr_log_talloc_report(NULL);
982  }
983 
984  return rcode;
985 }
986 
987 
988 /*
989  * Display the syntax for starting this program.
990  */
991 static void NEVER_RETURNS usage(int status)
992 {
993  FILE *output = status?stderr:stdout;
994 
995  fprintf(output, "Usage: %s [options]\n", main_config.name);
996  fprintf(output, "Options:\n");
997  fprintf(output, " -d raddb_dir Configuration files are in \"raddb_dir/*\".\n");
998  fprintf(output, " -D dict_dir Dictionary files are in \"dict_dir/*\".\n");
999  fprintf(output, " -f file Filter reply against attributes in 'file'.\n");
1000  fprintf(output, " -h Print this help message.\n");
1001  fprintf(output, " -i file File containing request attributes.\n");
1002  fprintf(output, " -m On SIGINT or SIGQUIT exit cleanly instead of immediately.\n");
1003  fprintf(output, " -n name Read raddb/name.conf instead of raddb/radiusd.conf.\n");
1004  fprintf(output, " -X Turn on full debugging.\n");
1005  fprintf(output, " -x Turn on additional debugging. (-xx gives more debugging).\n");
1006  exit(status);
1007 }
VALUE_PAIR has a single value.
Definition: pair.h:101
FR_TOKEN quote
Quoting around the default value. Only used for templates.
Definition: conffile.h:286
FILE * fr_log_fp
Definition: radius.c:81
static bool already_hex(VALUE_PAIR *vp)
Definition: radclient.c:255
int cf_pair_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *data, char const *dflt, FR_TOKEN dflt_quote)
Parses a CONF_PAIR into a C data type, with a default value.
Definition: conffile.c:1968
void * data
Pointer to a static variable to write the parsed value to.
Definition: conffile.h:278
int xlat_register(void *mod_inst, char const *name, xlat_func_t func, xlat_escape_t escape, xlat_instantiate_t instantiate, size_t inst_size, size_t buf_len)
Register an xlat function.
Definition: xlat.c:717
Main server configuration.
Definition: radiusd.h:108
static rlm_rcode_t mod_map_proc(UNUSED void *mod_inst, UNUSED void *proc_inst, UNUSED REQUEST *request, UNUSED char const *src, UNUSED vp_map_t const *maps)
Definition: unittest.c:637
char const * name
Name of the CONF_ITEM to parse.
Definition: conffile.h:268
module_entry_t * entry
Definition: modpriv.h:67
Dictionary attribute.
Definition: dict.h:77
int main_config_init(void)
Definition: mainconfig.c:731
char const * fr_packet_codes[FR_MAX_PACKET_CODE]
Definition: radius.c:101
void version_print(void)
Definition: version.c:512
CONF_PARSER const * config
Configuration information.
Definition: modules.h:139
int type
A PW_TYPE value, may be or'd with one or more PW_TYPE_* flags.
Definition: conffile.h:269
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition: radius.c:1621
module_t const * module
Definition: modpriv.h:54
void listen_free(UNUSED rad_listen_t **head)
Definition: unittest.c:64
char const * radacct_dir
Definition: unittest.c:41
#define INFO(fmt,...)
Definition: log.h:143
static char const * name
int virtual_servers_init(CONF_SECTION *config)
Definition: modules.c:1577
VALUE_PAIR * radius_pair_create(TALLOC_CTX *ctx, VALUE_PAIR **vps, unsigned int attribute, unsigned int vendor)
Create a VALUE_PAIR and add it to a list of VALUE_PAIR s.
Definition: pair.c:704
#define VERIFY_VP(_x)
Definition: pair.h:44
#define UNUSED
Definition: libradius.h:134
static bool do_xlats(char const *filename, FILE *fp)
Definition: unittest.c:536
void fr_pair_validate_debug(TALLOC_CTX *ctx, VALUE_PAIR const *failed[2])
Write an error to the library errorbuff detailing the mismatch.
Definition: pair.c:1068
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
Definition: libradius.h:162
module_instance_t * module_find(CONF_SECTION *modules, char const *askedname)
Find an existing module instance.
Definition: modules.c:623
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
Definition: cursor.c:60
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
char const * radlog_dir
Definition: unittest.c:42
int main_config_free(void)
Definition: mainconfig.c:1055
fr_state_tree_t * fr_state_tree_init(TALLOC_CTX *ctx, uint32_t max_sessions, uint32_t timeout)
Initialise a new state tree.
Definition: state.c:156
int map_proc_register(void *mod_inst, char const *name, map_proc_func_t evaluate, xlat_escape_t escape, map_proc_instantiate_t instantiate, size_t inst_size)
Register a map processor.
Definition: map_proc.c:132
size_t offset
Relative offset of field or structure to write the parsed value to.
Definition: conffile.h:272
valuepair value must be xlat expanded when it's added to VALUE_PAIR tree.
Definition: pair.h:102
fr_log_t default_log
Definition: log.c:216
int fr_log_talloc_report(TALLOC_CTX *ctx)
Generate a talloc memory report for a context and print to stderr/stdout.
Definition: debug.c:810
char const * name
Name of the daemon, usually 'radiusd'.
Definition: radiusd.h:109
#define XLAT_DEFAULT_BUF_LEN
Definition: xlat.h:89
CONF_SECTION * cs
Definition: modpriv.h:72
int virtual_servers_bootstrap(CONF_SECTION *config)
Definition: modules.c:1457
void fr_pair_fprint(FILE *, VALUE_PAIR const *vp)
Print one attribute and value to FP.
Definition: pair.c:2232
void set_radius_dir(TALLOC_CTX *ctx, char const *path)
Set the global radius config directory.
Definition: mainconfig.c:705
#define PW_TYPE_SUBSECTION
Definition: conffile.h:188
int fr_debug_lvl
Definition: misc.c:40
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
CONF_PAIR * cf_pair_find(CONF_SECTION const *, char const *name)
Definition: conffile.c:3478
char const * cf_pair_value(CONF_PAIR const *pair)
Definition: conffile.c:3506
int listen_bootstrap(UNUSED CONF_SECTION *server, UNUSED CONF_SECTION *cs, UNUSED char const *server_name)
Definition: unittest.c:59
void vradlog_request(log_type_t type, log_lvl_t lvl, REQUEST *request, char const *msg, va_list ap) CC_HINT(format(printf
int fr_fault_setup(char const *cmd, char const *program)
Registers signal handlers to execute panic_action on fatal signal.
Definition: debug.c:890
main_config_t * root
Pointer to the main config hack to try and deal with hup.
Definition: radiusd.h:267
#define rad_assert(expr)
Definition: rad_assert.h:38
#define PW_DIGEST_ATTRIBUTES
Definition: radius.h:165
RFC2865 - Access-Request.
Definition: radius.h:92
Definition: xlat.c:60
int modules_init(CONF_SECTION *)
Instantiate the modules.
Definition: modules.c:2064
int main(int argc, char *argv[])
Definition: unittest.c:646
int fr_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
Definition: version.c:38
int cf_pair_replace(CONF_SECTION *cs, CONF_PAIR *cp, char const *value)
Replace pair in a given section with a new pair, of the given value.
Definition: conffile.c:768
static void print_packet(FILE *fp, RADIUS_PACKET *packet)
Definition: unittest.c:384
bool debug_memory
Cleanup the server properly on exit, freeing up any memory we allocated.
Definition: radiusd.h:156
uint32_t max_requests
Definition: radiusd.h:136
#define DEBUG2(fmt,...)
Definition: log.h:176
#define STRINGIFY(x)
Definition: build.h:34
RADIUS_PACKET * fr_radius_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new RADIUS_PACKET.
Definition: radius.c:1651
static REQUEST * request_setup(FILE *fp)
Definition: unittest.c:107
void void fr_perror(char const *,...) CC_HINT(format(printf
unsigned int attr
Attribute number.
Definition: dict.h:79
int fr_radius_encode_chap_password(uint8_t *output, RADIUS_PACKET *packet, int id, VALUE_PAIR *password)
unsigned int code
Packet code (type).
Definition: libradius.h:155
const void * dflt
Default as it would appear in radiusd.conf.
Definition: conffile.h:281
static char panic_action[512]
The command to execute when panicking.
Definition: debug.c:92
unsigned int vendor
Vendor that defines this attribute.
Definition: dict.h:78
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
bool log_auth_goodpass
Log failed authentications.
Definition: radiusd.h:114
RADIUS_PACKET * reply
Outgoing response.
Definition: radiusd.h:225
ssize_t ssize_t radius_xlat_struct(char *out, size_t outlen, REQUEST *request, xlat_exp_t const *xlat, xlat_escape_t escape, void *ctx) CC_HINT(nonnull(1
bool fr_pair_validate(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list)
Uses fr_pair_cmp to verify all VALUE_PAIRs in list match the filter defined by check.
Definition: pair.c:1117
Configuration AVP similar to a VALUE_PAIR.
Definition: conffile.c:82
bool log_auth
Log authentication attempts.
Definition: radiusd.h:112
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
static ssize_t xlat_poke(char **out, size_t outlen, UNUSED void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt)
Definition: unittest.c:420
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
CONF_SECTION * cf_section_sub_find(CONF_SECTION const *, char const *name)
Find a sub-section in a section.
Definition: conffile.c:3708
void xlat_free(void)
De-register all xlat functions, used mainly for debugging.
Definition: xlat.c:1147
Describes a host allowed to send packets to the server.
Definition: clients.h:35
unsigned int state
Definition: proto_bfd.c:200
uint8_t data[]
Definition: eap_pwd.h:625
Module failed, don't reply.
Definition: radiusd.h:90
#define TAG_ANY
Definition: pair.h:191
value_type_t type
Type of pointer in value union.
Definition: pair.h:132
CONF_SECTION * config
Root of the server config.
Definition: radiusd.h:110
void * insthandle
Definition: modpriv.h:68
log_lvl_t rad_debug_lvl
Global debugging level.
Definition: log.c:49
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
Definition: cursor.c:263
int fr_pair_list_afrom_file(TALLOC_CTX *ctx, VALUE_PAIR **out, FILE *fp, bool *pfiledone)
Definition: pair.c:1333
bool fr_hostname_lookups
hostname -> IP lookups?
Definition: inet.c:29
char const * radiusd_version
Definition: unittest.c:48
ssize_t ssize_t ssize_t ssize_t ssize_t xlat_tokenize(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head, char const **error)
Definition: xlat.c:1784
CONF_SECTION * cf_section_alloc(CONF_SECTION *parent, char const *name1, char const *name2)
Allocate a CONF_SECTION.
Definition: conffile.c:626
char const * dictionary_dir
Where to load dictionaries from.
Definition: radiusd.h:142
int modules_free(void)
Definition: modules.c:413
int modules_bootstrap(CONF_SECTION *)
Definition: modules.c:1866
main_config_t main_config
Main server configuration.
Definition: mainconfig.c:43
static RADCLIENT * client_alloc(void *ctx)
Definition: unittest.c:97
static bool filedone
Definition: unittest.c:46
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
char const * panic_action
Command to execute if the server receives a fatal signal.
Definition: radiusd.h:150
void fr_talloc_fault_setup(void)
Register talloc fault handlers.
Definition: debug.c:872
bool log_auth_badpass
Log successful authentications.
Definition: radiusd.h:113
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
log_dst_t dst
Log destination.
Definition: log.h:72
fr_dict_attr_t const * fr_dict_attr_by_num(fr_dict_t *dict, unsigned int vendor, unsigned int attr)
Lookup a fr_dict_attr_t by its vendor and attribute numbers.
Definition: dict.c:3519
#define NEVER_RETURNS
Definition: libradius.h:133
Log to stdout.
Definition: log.h:58
#define RCSID(id)
Definition: build.h:135
int rad_virtual_server(REQUEST *)
Definition: auth.c:659
void cf_section_add(CONF_SECTION *parent, CONF_SECTION *cs)
Definition: conffile.c:754
char * talloc_typed_strdup(void const *t, char const *p)
Call talloc strdup, setting the type on the new chunk correctly.
Definition: missing.c:588
Value pair map.
Definition: map.h:46
int fd
File descriptor to write messages to.
Definition: log.h:71
#define RDEBUG(fmt,...)
Definition: log.h:243
static bool memory_report
Definition: unittest.c:45
#define ERROR(fmt,...)
Definition: log.h:145
#define RADIUS_DIR
Definition: conf.h:3
bool log_stripped_names
Definition: unittest.c:43
void xlat_unregister(void *mod_inst, char const *name, xlat_func_t func)
REQUEST * request_alloc(TALLOC_CTX *ctx)
Create a new REQUEST data structure.
Definition: request.c:85
static rad_listen_t * listen_alloc(void *ctx)
Definition: unittest.c:70
#define RADIUSD_MAGIC_NUMBER
Definition: libradius.h:51
static void usage(int)
Definition: unittest.c:991