/* * The main guy. */ int main(int argc, char *argv[]) { int rcode = EXIT_SUCCESS; int argval; const char *input_file = NULL; const char *output_file = NULL; const char *filter_file = NULL; FILE *fp; REQUEST *request = NULL; VALUE_PAIR *vp; VALUE_PAIR *filter_vps = NULL; /* * If the server was built with debugging enabled always install * the basic fatal signal handlers. */ #ifndef NDEBUG if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) { fr_perror("unittest"); exit(EXIT_FAILURE); } #endif if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL) progname = argv[0]; else progname++; debug_flag = 0; set_radius_dir(NULL, RADIUS_DIR); /* * Ensure that the configuration is initialized. */ memset(&main_config, 0, sizeof(main_config)); main_config.myip.af = AF_UNSPEC; main_config.port = 0; main_config.name = "radiusd"; /* * The tests should have only IPs, not host names. */ fr_hostname_lookups = false; /* * We always log to stdout. */ fr_log_fp = stdout; default_log.dst = L_DST_STDOUT; default_log.fd = STDOUT_FILENO; /* Process the options. */ while ((argval = getopt(argc, argv, "d:D:f:hi:mMn:o:xX")) != EOF) { switch (argval) { case 'd': set_radius_dir(NULL, optarg); break; case 'D': main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg); break; case 'f': filter_file = optarg; break; case 'h': usage(0); break; case 'i': input_file = optarg; break; case 'm': main_config.debug_memory = true; break; case 'M': memory_report = true; main_config.debug_memory = true; break; case 'n': main_config.name = optarg; break; case 'o': output_file = optarg; break; case 'X': debug_flag += 2; main_config.log_auth = true; main_config.log_auth_badpass = true; main_config.log_auth_goodpass = true; break; case 'x': debug_flag++; break; default: usage(1); break; } } if (debug_flag) { version(); } fr_debug_flag = debug_flag; /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("radiusd"); exit(EXIT_FAILURE); } /* Read the configuration files, BEFORE doing anything else. */ if (main_config_init() < 0) { rcode = EXIT_FAILURE; goto finish; } /* * Load the modules */ if (modules_init(main_config.config) < 0) { rcode = EXIT_FAILURE; goto finish; } fr_state_init(); /* Set the panic action (if required) */ if (main_config.panic_action && #ifndef NDEBUG !getenv("PANIC_ACTION") && #endif (fr_fault_setup(main_config.panic_action, argv[0]) < 0)) { rcode = EXIT_FAILURE; goto finish; } setlinebuf(stdout); /* unbuffered output */ if (!input_file || (strcmp(input_file, "-") == 0)) { fp = stdin; } else { fp = fopen(input_file, "r"); if (!fp) { fprintf(stderr, "Failed reading %s: %s\n", input_file, strerror(errno)); rcode = EXIT_FAILURE; goto finish; } } /* * Grab the VPs from stdin, or from the file. */ request = request_setup(fp); if (!request) { fprintf(stderr, "Failed reading input: %s\n", fr_strerror()); rcode = EXIT_FAILURE; goto finish; } /* * No filter file, OR there's no more input, OR we're * reading from a file, and it's different from the * filter file. */ if (!filter_file || filedone || ((input_file != NULL) && (strcmp(filter_file, input_file) != 0))) { if (output_file) { fclose(fp); fp = NULL; } filedone = false; } /* * There is a filter file. If necessary, open it. If we * already are reading it via "input_file", then we don't * need to re-open it. */ if (filter_file) { if (!fp) { fp = fopen(filter_file, "r"); if (!fp) { fprintf(stderr, "Failed reading %s: %s\n", filter_file, strerror(errno)); rcode = EXIT_FAILURE; goto finish; } } if (readvp2(request, &filter_vps, fp, &filedone) < 0) { fprintf(stderr, "Failed reading attributes from %s: %s\n", filter_file, fr_strerror()); rcode = EXIT_FAILURE; goto finish; } /* * FIXME: loop over input packets. */ fclose(fp); } rad_virtual_server(request); if (!output_file || (strcmp(output_file, "-") == 0)) { fp = stdout; } else { fp = fopen(output_file, "w"); if (!fp) { fprintf(stderr, "Failed writing %s: %s\n", output_file, strerror(errno)); exit(EXIT_FAILURE); } } print_packet(fp, request->reply); if (output_file) fclose(fp); /* * Update the list with the response type. */ vp = radius_paircreate(request->reply, &request->reply->vps, PW_RESPONSE_PACKET_TYPE, 0); vp->vp_integer = request->reply->code; { VALUE_PAIR const *failed[2]; if (filter_vps && !pairvalidate(failed, filter_vps, request->reply->vps)) { pairvalidate_debug(request, failed); fr_perror("Output file %s does not match attributes in filter %s", output_file ? output_file : input_file, filter_file); rcode = EXIT_FAILURE; goto finish; } } INFO("Exiting normally"); finish: talloc_free(request); /* * Detach any modules. */ modules_free(); xlat_free(); /* modules may have xlat's */ fr_state_delete(); /* * Free the configuration items. */ main_config_free(); if (memory_report) { INFO("Allocated memory at time of report:"); fr_log_talloc_report(NULL); } return rcode; }
/* * Receive one packet, maybe. */ static int recv_one_packet(int wait_time) { fd_set set; struct timeval tv; rc_request_t *request; RADIUS_PACKET *reply, **packet_p; volatile int max_fd; /* And wait for reply, timing out as necessary */ FD_ZERO(&set); max_fd = fr_packet_list_fd_set(pl, &set); if (max_fd < 0) exit(1); /* no sockets to listen on! */ if (wait_time <= 0) { tv.tv_sec = 0; } else { tv.tv_sec = wait_time; } tv.tv_usec = 0; /* * No packet was received. */ if (select(max_fd, &set, NULL, NULL, &tv) <= 0) { return 0; } /* * Look for the packet. */ reply = fr_packet_list_recv(pl, &set); if (!reply) { ERROR("Received bad packet"); #ifdef WITH_TCP /* * If the packet is bad, we close the socket. * I'm not sure how to do that now, so we just * die... */ if (proto) exit(1); #endif return -1; /* bad packet */ } /* * udpfromto issues. We may have bound to "*", * and we want to find the replies that are sent to * (say) 127.0.0.1. */ reply->dst_ipaddr = client_ipaddr; reply->dst_port = client_port; #ifdef WITH_TCP reply->src_ipaddr = server_ipaddr; reply->src_port = server_port; #endif packet_p = fr_packet_list_find_byreply(pl, reply); if (!packet_p) { ERROR("Received reply to request we did not send. (id=%d socket %d)", reply->id, reply->sockfd); rad_free(&reply); return -1; /* got reply to packet we didn't send */ } request = fr_packet2myptr(rc_request_t, packet, packet_p); /* * Fails the signature validation: not a real reply. * FIXME: Silently drop it and listen for another packet. */ if (rad_verify(reply, request->packet, secret) < 0) { REDEBUG("Reply verification failed"); stats.lost++; goto packet_done; /* shared secret is incorrect */ } if (print_filename) { RDEBUG("%s response code %d", request->files->packets, reply->code); } deallocate_id(request); request->reply = reply; reply = NULL; /* * If this fails, we're out of memory. */ if (rad_decode(request->reply, request->packet, secret) != 0) { REDEBUG("Reply decode failed"); stats.lost++; goto packet_done; } /* * Increment counters... */ switch (request->reply->code) { case PW_CODE_AUTHENTICATION_ACK: case PW_CODE_ACCOUNTING_RESPONSE: case PW_CODE_COA_ACK: case PW_CODE_DISCONNECT_ACK: stats.accepted++; break; case PW_CODE_ACCESS_CHALLENGE: break; default: stats.rejected++; } /* * If we had an expected response code, check to see if the * packet matched that. */ if (request->reply->code != request->filter_code) { if (is_radius_code(request->packet_code)) { REDEBUG("Expected %s got %s", fr_packet_codes[request->filter_code], fr_packet_codes[request->reply->code]); } else { REDEBUG("Expected %u got %i", request->filter_code, request->reply->code); } stats.failed++; /* * Check if the contents of the packet matched the filter */ } else if (!request->filter) { stats.passed++; } else { VALUE_PAIR const *failed[2]; pairsort(&request->reply->vps, attrtagcmp); if (pairvalidate(failed, request->filter, request->reply->vps)) { RDEBUG("Response passed filter"); stats.passed++; } else { pairvalidate_debug(request, failed); REDEBUG("Response failed filter"); stats.failed++; } } if (request->resend == resend_count) { request->done = true; } packet_done: rad_free(&request->reply); rad_free(&reply); /* may be NULL */ return 0; }
/* * The main guy. */ int main(int argc, char *argv[]) { int rcode = EXIT_SUCCESS; int argval; const char *input_file = NULL; const char *output_file = NULL; const char *filter_file = NULL; FILE *fp; REQUEST *request; VALUE_PAIR *filter_vps = NULL; if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL) progname = argv[0]; else progname++; debug_flag = 0; radius_dir = talloc_strdup(NULL, RADIUS_DIR); /* * Ensure that the configuration is initialized. */ memset(&mainconfig, 0, sizeof(mainconfig)); mainconfig.myip.af = AF_UNSPEC; mainconfig.port = -1; mainconfig.name = "radiusd"; /* * We always log to stdout. */ fr_log_fp = stdout; default_log.dest = L_DST_STDOUT; default_log.fd = STDOUT_FILENO; /* Process the options. */ while ((argval = getopt(argc, argv, "d:D:f:hi:mMn:o:xX")) != EOF) { switch(argval) { case 'd': if (radius_dir) { rad_const_free(radius_dir); } radius_dir = talloc_strdup(NULL, optarg); break; case 'D': mainconfig.dictionary_dir = talloc_strdup(NULL, optarg); break; case 'f': filter_file = optarg; break; case 'h': usage(0); break; case 'i': input_file = optarg; break; case 'm': mainconfig.debug_memory = 1; break; case 'M': memory_report = 1; mainconfig.debug_memory = 1; break; case 'n': mainconfig.name = optarg; break; case 'o': output_file = optarg; break; case 'X': debug_flag += 2; mainconfig.log_auth = true; mainconfig.log_auth_badpass = true; mainconfig.log_auth_goodpass = true; break; case 'x': debug_flag++; break; default: usage(1); break; } } if (memory_report) { talloc_enable_null_tracking(); #ifdef WITH_VERIFY_PTR talloc_set_abort_fn(die_horribly); #endif } talloc_set_log_fn(log_talloc); if (debug_flag) { version(); } fr_debug_flag = debug_flag; /* Read the configuration files, BEFORE doing anything else. */ if (read_mainconfig(0) < 0) { exit(EXIT_FAILURE); } setlinebuf(stdout); /* unbuffered output */ if (!input_file || (strcmp(input_file, "-") == 0)) { fp = stdin; } else { fp = fopen(input_file, "r"); if (!fp) { fprintf(stderr, "Failed reading %s: %s\n", input_file, strerror(errno)); exit(EXIT_FAILURE); } } /* * Grab the VPs from stdin, or from the file. */ request = request_setup(fp); if (!request) { fprintf(stderr, "Failed reading input: %s\n", fr_strerror()); exit(EXIT_FAILURE); } /* * No filter file, OR there's no more input, OR we're * reading from a file, and it's different from the * filter file. */ if (!filter_file || filedone || ((input_file != NULL) && (strcmp(filter_file, input_file) != 0))) { if (output_file) { fclose(fp); fp = NULL; } filedone = 0; } /* * There is a filter file. If necessary, open it. If we * already are reading it via "input_file", then we don't * need to re-open it. */ if (filter_file) { if (!fp) { fp = fopen(filter_file, "r"); if (!fp) { fprintf(stderr, "Failed reading %s: %s\n", filter_file, strerror(errno)); exit(EXIT_FAILURE); } } filter_vps = readvp2(request, fp, &filedone, "radiusd"); if (!filter_vps) { fprintf(stderr, "Failed reading attributes from %s: %s\n", filter_file, fr_strerror()); exit(EXIT_FAILURE); } /* * FIXME: loop over input packets. */ fclose(fp); } rad_virtual_server(request); if (!output_file || (strcmp(output_file, "-") == 0)) { fp = stdout; } else { fp = fopen(output_file, "w"); if (!fp) { fprintf(stderr, "Failed writing %s: %s\n", output_file, strerror(errno)); exit(EXIT_FAILURE); } } print_packet(fp, request->reply); if (output_file) fclose(fp); if (filter_vps && !pairvalidate(filter_vps, request->reply->vps)) { fprintf(stderr, "Output file %s does not match attributes in filter %s\n", output_file, filter_file); exit(EXIT_FAILURE); } talloc_free(request); INFO("Exiting normally."); /* * Detach any modules. */ detach_modules(); xlat_free(); /* modules may have xlat's */ /* * Free the configuration items. */ free_mainconfig(); rad_const_free(radius_dir); if (memory_report) { INFO("Allocated memory at time of report:"); log_talloc_report(NULL); } return rcode; }
/* * Receive one packet, maybe. */ static int recv_one_packet(int wait_time) { fd_set set; struct timeval tv; rc_request_t *request; RADIUS_PACKET *reply, **packet_p; volatile int max_fd; /* And wait for reply, timing out as necessary */ FD_ZERO(&set); max_fd = fr_packet_list_fd_set(pl, &set); if (max_fd < 0) exit(1); /* no sockets to listen on! */ tv.tv_sec = (wait_time <= 0) ? 0 : wait_time; tv.tv_usec = 0; /* * No packet was received. */ if (select(max_fd, &set, NULL, NULL, &tv) <= 0) return 0; /* * Look for the packet. */ reply = fr_packet_list_recv(pl, &set); if (!reply) { ERROR("Received bad packet"); #ifdef WITH_TCP /* * If the packet is bad, we close the socket. * I'm not sure how to do that now, so we just * die... */ if (proto) exit(1); #endif return -1; /* bad packet */ } /* * We don't use udpfromto. So if we bind to "*", we want * to find replies sent to 192.0.2.4. Therefore, we * force all replies to have the one address we know * about, no matter what real address they were sent to. * * This only works if were not using any of the * Packet-* attributes, or running with 'auto'. */ reply->dst_ipaddr = client_ipaddr; reply->dst_port = client_port; #ifdef WITH_TCP /* * TCP sockets don't use recvmsg(), and thus don't get * the source IP/port. However, since they're TCP, we * know what the source IP/port is, because that's where * we connected to. */ if (ipproto == IPPROTO_TCP) { reply->src_ipaddr = server_ipaddr; reply->src_port = server_port; } #endif packet_p = fr_packet_list_find_byreply(pl, reply); if (!packet_p) { ERROR("Received reply to request we did not send. (id=%d socket %d)", reply->id, reply->sockfd); rad_free(&reply); return -1; /* got reply to packet we didn't send */ } request = fr_packet2myptr(rc_request_t, packet, packet_p); /* * Fails the signature validation: not a real reply. * FIXME: Silently drop it and listen for another packet. */ if (rad_verify(reply, request->packet, secret) < 0) { REDEBUG("Reply verification failed"); stats.lost++; goto packet_done; /* shared secret is incorrect */ } if (print_filename) { RDEBUG("%s response code %d", request->files->packets, reply->code); } deallocate_id(request); request->reply = reply; reply = NULL; /* * If this fails, we're out of memory. */ if (rad_decode(request->reply, request->packet, secret) != 0) { REDEBUG("Reply decode failed"); stats.lost++; goto packet_done; } fr_packet_header_print(fr_log_fp, request->reply, true); if (fr_debug_flag > 0) vp_printlist(fr_log_fp, request->reply->vps); /* * Increment counters... */ switch (request->reply->code) { case PW_CODE_ACCESS_ACCEPT: case PW_CODE_ACCOUNTING_RESPONSE: case PW_CODE_COA_ACK: case PW_CODE_DISCONNECT_ACK: stats.accepted++; break; case PW_CODE_ACCESS_CHALLENGE: break; default: stats.rejected++; } /* * If we had an expected response code, check to see if the * packet matched that. */ if (request->reply->code != request->filter_code) { if (is_radius_code(request->reply->code)) { REDEBUG("%s: Expected %s got %s", request->name, fr_packet_codes[request->filter_code], fr_packet_codes[request->reply->code]); } else { REDEBUG("%s: Expected %u got %i", request->name, request->filter_code, request->reply->code); } stats.failed++; /* * Check if the contents of the packet matched the filter */ } else if (!request->filter) { stats.passed++; } else { VALUE_PAIR const *failed[2]; pairsort(&request->reply->vps, attrtagcmp); if (pairvalidate(failed, request->filter, request->reply->vps)) { RDEBUG("%s: Response passed filter", request->name); stats.passed++; } else { pairvalidate_debug(request, failed); REDEBUG("%s: Response for failed filter", request->name); stats.failed++; } } if (request->resend == resend_count) { request->done = true; } packet_done: rad_free(&request->reply); rad_free(&reply); /* may be NULL */ return 0; }