/* * 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; }
/* * Main program */ int main(int argc, char **argv) { CONF_SECTION *maincs, *cs; FILE *fp; struct radutmp rt; char othername[256]; char nasname[1024]; char session_id[sizeof(rt.session_id)+1]; int hideshell = 0; int showsid = 0; int rawoutput = 0; int radiusoutput = 0; /* Radius attributes */ char const *portind; int c; unsigned int portno; char buffer[2048]; char const *user = NULL; int user_cmp = 0; time_t now = 0; uint32_t nas_port = ~0; uint32_t nas_ip_address = INADDR_NONE; int zap = 0; raddb_dir = RADIUS_DIR; #ifndef NDEBUG if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) { fr_perror("radwho"); exit(EXIT_FAILURE); } #endif talloc_set_log_stderr(); while((c = getopt(argc, argv, "d:fF:nN:sSipP:crRu:U:Z")) != EOF) switch(c) { case 'd': raddb_dir = optarg; break; case 'F': radutmp_file = optarg; break; case 'h': usage(0); break; case 'S': hideshell = 1; break; case 'n': showname = 0; break; case 'N': if (inet_pton(AF_INET, optarg, &nas_ip_address) < 0) { usage(1); } break; case 's': showname = 1; break; case 'i': showsid = 1; break; case 'p': showptype = 1; break; case 'P': nas_port = atoi(optarg); break; case 'c': showcid = 1; showname = 1; break; case 'r': rawoutput = 1; break; case 'R': radiusoutput = 1; now = time(NULL); break; case 'u': user = optarg; user_cmp = 0; break; case 'U': user = optarg; user_cmp = 1; break; case 'Z': zap = 1; break; default: usage(1); break; } /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("radwho"); return 1; } /* * Be safe. */ if (zap && !radiusoutput) zap = 0; /* * zap EVERYONE, but only on this nas */ if (zap && !user && (~nas_port == 0)) { /* * We need to know which NAS to zap users in. */ if (nas_ip_address == INADDR_NONE) usage(1); printf("Acct-Status-Type = Accounting-Off\n"); printf("NAS-IP-Address = %s\n", hostname(buffer, sizeof(buffer), nas_ip_address)); printf("Acct-Delay-Time = 0\n"); exit(0); /* don't bother printing anything else */ } if (radutmp_file) goto have_radutmp; /* * Initialize main_config */ memset(&main_config, 0, sizeof(main_config)); /* Read radiusd.conf */ snprintf(buffer, sizeof(buffer), "%.200s/radiusd.conf", raddb_dir); maincs = cf_file_read(buffer); if (!maincs) { fprintf(stderr, "%s: Error reading or parsing radiusd.conf\n", argv[0]); exit(1); } cs = cf_section_sub_find(maincs, "modules"); if (!cs) { fprintf(stderr, "%s: No modules section found in radiusd.conf\n", argv[0]); exit(1); } /* Read the radutmp section of radiusd.conf */ cs = cf_section_sub_find_name2(cs, "radutmp", NULL); if (!cs) { fprintf(stderr, "%s: No configuration information in radutmp section of radiusd.conf\n", argv[0]); exit(1); } cf_section_parse(cs, NULL, module_config); /* Assign the correct path for the radutmp file */ radutmp_file = radutmpconfig.radutmp_fn; have_radutmp: if (showname < 0) showname = 1; /* * Show the users logged in on the terminal server(s). */ if ((fp = fopen(radutmp_file, "r")) == NULL) { fprintf(stderr, "%s: Error reading %s: %s\n", progname, radutmp_file, fr_syserror(errno)); return 0; } /* * Don't print the headers if raw or RADIUS */ if (!rawoutput && !radiusoutput) { fputs(showname ? hdr1 : hdr2, stdout); fputs(eol, stdout); } /* * Read the file, printing out active entries. */ while (fread(&rt, sizeof(rt), 1, fp) == 1) { char name[sizeof(rt.login) + 1]; if (rt.type != P_LOGIN) continue; /* hide logout sessions */ /* * We don't show shell users if we are * fingerd, as we have done that above. */ if (hideshell && !strchr("PCS", rt.proto)) continue; /* * Print out sessions only for the given user. */ if (user) { /* only for a particular user */ if (((user_cmp == 0) && (strncasecmp(rt.login, user, strlen(user)) != 0)) || ((user_cmp == 1) && (strncmp(rt.login, user, strlen(user)) != 0))) { continue; } } /* * Print out only for the given NAS port. */ if (~nas_port != 0) { if (rt.nas_port != nas_port) continue; } /* * Print out only for the given NAS IP address */ if (nas_ip_address != INADDR_NONE) { if (rt.nas_address != nas_ip_address) continue; } memcpy(session_id, rt.session_id, sizeof(rt.session_id)); session_id[sizeof(rt.session_id)] = 0; if (!rawoutput && rt.nas_port > (showname ? 999 : 99999)) { portind = ">"; portno = (showname ? 999 : 99999); } else { portind = "S"; portno = rt.nas_port; } /* * Print output as RADIUS attributes */ if (radiusoutput) { memcpy(nasname, rt.login, sizeof(rt.login)); nasname[sizeof(rt.login)] = '\0'; fr_print_string(nasname, 0, buffer, sizeof(buffer)); printf("User-Name = \"%s\"\n", buffer); fr_print_string(session_id, 0, buffer, sizeof(buffer)); printf("Acct-Session-Id = \"%s\"\n", buffer); if (zap) printf("Acct-Status-Type = Stop\n"); printf("NAS-IP-Address = %s\n", hostname(buffer, sizeof(buffer), rt.nas_address)); printf("NAS-Port = %u\n", rt.nas_port); switch (rt.proto) { case 'S': printf("Service-Type = Framed-User\n"); printf("Framed-Protocol = SLIP\n"); break; case 'P': printf("Service-Type = Framed-User\n"); printf("Framed-Protocol = PPP\n"); break; default: printf("Service-type = Login-User\n"); break; } if (rt.framed_address != INADDR_NONE) { printf("Framed-IP-Address = %s\n", hostname(buffer, sizeof(buffer), rt.framed_address)); } /* * Some sanity checks on the time */ if ((rt.time <= now) && (now - rt.time) <= (86400 * 365)) { printf("Acct-Session-Time = %" PRId64 "\n", (int64_t) (now - rt.time)); } if (rt.caller_id[0] != '\0') { memcpy(nasname, rt.caller_id, sizeof(rt.caller_id)); nasname[sizeof(rt.caller_id)] = '\0'; fr_print_string(nasname, 0, buffer, sizeof(buffer)); printf("Calling-Station-Id = \"%s\"\n", buffer); } printf("\n"); /* separate entries with a blank line */ continue; } /* * Show the fill name, or not. */ memcpy(name, rt.login, sizeof(rt.login)); name[sizeof(rt.login)] = '\0'; if (showname) { if (rawoutput == 0) { printf("%-10.10s %-17.17s %-5.5s %s%-3u %-9.9s %-15.15s %-.19s%s", name, showcid ? rt.caller_id : (showsid? session_id : fullname(rt.login)), proto(rt.proto, rt.porttype), portind, portno, dotime(rt.time), hostname(nasname, sizeof(nasname), rt.nas_address), hostname(othername, sizeof(othername), rt.framed_address), eol); } else { printf("%s,%s,%s,%s%u,%s,%s,%s%s", name, showcid ? rt.caller_id : (showsid? session_id : fullname(rt.login)), proto(rt.proto, rt.porttype), portind, portno, dotime(rt.time), hostname(nasname, sizeof(nasname), rt.nas_address), hostname(othername, sizeof(othername), rt.framed_address), eol); } } else { if (rawoutput == 0) { printf("%-10.10s %s%-5u %-6.6s %-13.13s %-15.15s %-.28s%s", name, portind, portno, proto(rt.proto, rt.porttype), dotime(rt.time), hostname(nasname, sizeof(nasname), rt.nas_address), hostname(othername, sizeof(othername), rt.framed_address), eol); } else { printf("%s,%s%u,%s,%s,%s,%s%s", name, portind, portno, proto(rt.proto, rt.porttype), dotime(rt.time), hostname(nasname, sizeof(nasname), rt.nas_address), hostname(othername, sizeof(othername), rt.framed_address), eol); } } } fclose(fp); return 0; }
int main(int argc, char **argv) { int c; char const *radius_dir = RADDBDIR; char const *dict_dir = DICTDIR; char filesecret[256]; FILE *fp; int do_summary = false; int persec = 0; int parallel = 1; rc_request_t *this; int force_af = AF_UNSPEC; /* * It's easier having two sets of flags to set the * verbosity of library calls and the verbosity of * radclient. */ fr_debug_lvl = 0; fr_log_fp = stdout; #ifndef NDEBUG if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) { fr_perror("radclient"); exit(EXIT_FAILURE); } #endif talloc_set_log_stderr(); filename_tree = rbtree_create(NULL, filename_cmp, NULL, 0); if (!filename_tree) { oom: ERROR("Out of memory"); exit(1); } while ((c = getopt(argc, argv, "46c:d:D:f:Fhi:n:p:qr:sS:t:vx" #ifdef WITH_TCP "P:" #endif )) != EOF) switch (c) { case '4': force_af = AF_INET; break; case '6': force_af = AF_INET6; break; case 'c': if (!isdigit((int) *optarg)) usage(); resend_count = atoi(optarg); break; case 'D': dict_dir = optarg; break; case 'd': radius_dir = optarg; break; case 'f': { char const *p; rc_file_pair_t *files; files = talloc(talloc_autofree_context(), rc_file_pair_t); if (!files) goto oom; p = strchr(optarg, ':'); if (p) { files->packets = talloc_strndup(files, optarg, p - optarg); if (!files->packets) goto oom; files->filters = p + 1; } else { files->packets = optarg; files->filters = NULL; } rbtree_insert(filename_tree, (void *) files); } break; case 'F': print_filename = true; break; case 'i': /* currently broken */ if (!isdigit((int) *optarg)) usage(); last_used_id = atoi(optarg); if ((last_used_id < 0) || (last_used_id > 255)) { usage(); } break; case 'n': persec = atoi(optarg); if (persec <= 0) usage(); break; /* * Note that sending MANY requests in * parallel can over-run the kernel * queues, and Linux will happily discard * packets. So even if the server responds, * the client may not see the reply. */ case 'p': parallel = atoi(optarg); if (parallel <= 0) usage(); break; #ifdef WITH_TCP case 'P': proto = optarg; if (strcmp(proto, "tcp") != 0) { if (strcmp(proto, "udp") == 0) { proto = NULL; } else { usage(); } } else { ipproto = IPPROTO_TCP; } break; #endif case 'q': do_output = false; fr_log_fp = NULL; /* no output from you, either! */ break; case 'r': if (!isdigit((int) *optarg)) usage(); retries = atoi(optarg); if ((retries == 0) || (retries > 1000)) usage(); break; case 's': do_summary = true; break; case 'S': { char *p; fp = fopen(optarg, "r"); if (!fp) { ERROR("Error opening %s: %s", optarg, fr_syserror(errno)); exit(1); } if (fgets(filesecret, sizeof(filesecret), fp) == NULL) { ERROR("Error reading %s: %s", optarg, fr_syserror(errno)); exit(1); } fclose(fp); /* truncate newline */ p = filesecret + strlen(filesecret) - 1; while ((p >= filesecret) && (*p < ' ')) { *p = '\0'; --p; } if (strlen(filesecret) < 2) { ERROR("Secret in %s is too short", optarg); exit(1); } secret = filesecret; } break; case 't': if (!isdigit((int) *optarg)) usage(); timeout = atof(optarg); break; case 'v': fr_debug_lvl = 1; DEBUG("%s", radclient_version); exit(0); case 'x': fr_debug_lvl++; break; case 'h': default: usage(); } argc -= (optind - 1); argv += (optind - 1); if ((argc < 3) || ((secret == NULL) && (argc < 4))) { ERROR("Insufficient arguments"); usage(); } /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("radclient"); return 1; } if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radclient"); return 1; } if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) { fr_perror("radclient"); return 1; } fr_strerror(); /* Clear the error buffer */ /* * Get the request type */ if (!isdigit((int) argv[2][0])) { packet_code = fr_str2int(request_types, argv[2], -2); if (packet_code == -2) { ERROR("Unrecognised request type \"%s\"", argv[2]); usage(); } } else { packet_code = atoi(argv[2]); } /* * Resolve hostname. */ if (strcmp(argv[1], "-") != 0) { if (fr_pton_port(&server_ipaddr, &server_port, argv[1], -1, force_af, true) < 0) { ERROR("%s", fr_strerror()); exit(1); } /* * Work backwards from the port to determine the packet type */ if (packet_code == PW_CODE_UNDEFINED) packet_code = radclient_get_code(server_port); } radclient_get_port(packet_code, &server_port); /* * Add the secret. */ if (argv[3]) secret = argv[3]; /* * If no '-f' is specified, we're reading from stdin. */ if (rbtree_num_elements(filename_tree) == 0) { rc_file_pair_t *files; files = talloc_zero(talloc_autofree_context(), rc_file_pair_t); files->packets = "-"; if (!radclient_init(files, files)) { exit(1); } } /* * Walk over the list of filenames, creating the requests. */ if (rbtree_walk(filename_tree, RBTREE_IN_ORDER, filename_walk, NULL) != 0) { ERROR("Failed parsing input files"); exit(1); } /* * No packets read. Die. */ if (!request_head) { ERROR("Nothing to send"); exit(1); } /* * Bind to the first specified IP address and port. * This means we ignore later ones. */ if (request_head->packet->src_ipaddr.af == AF_UNSPEC) { memset(&client_ipaddr, 0, sizeof(client_ipaddr)); client_ipaddr.af = server_ipaddr.af; } else { client_ipaddr = request_head->packet->src_ipaddr; } client_port = request_head->packet->src_port; #ifdef WITH_TCP if (proto) { sockfd = fr_socket_client_tcp(NULL, &server_ipaddr, server_port, false); } else #endif sockfd = fr_socket(&client_ipaddr, client_port); if (sockfd < 0) { ERROR("Error opening socket"); exit(1); } pl = fr_packet_list_create(1); if (!pl) { ERROR("Out of memory"); exit(1); } if (!fr_packet_list_socket_add(pl, sockfd, ipproto, &server_ipaddr, server_port, NULL)) { ERROR("Out of memory"); exit(1); } /* * Walk over the list of packets, sanity checking * everything. */ for (this = request_head; this != NULL; this = this->next) { this->packet->src_ipaddr = client_ipaddr; this->packet->src_port = client_port; if (radclient_sane(this) != 0) { exit(1); } } /* * Walk over the packets to send, until * we're all done. * * FIXME: This currently busy-loops until it receives * all of the packets. It should really have some sort of * send packet, get time to wait, select for time, etc. * loop. */ do { int n = parallel; rc_request_t *next; char const *filename = NULL; done = true; sleep_time = -1; /* * Walk over the packets, sending them. */ for (this = request_head; this != NULL; this = next) { next = this->next; /* * If there's a packet to receive, * receive it, but don't wait for a * packet. */ recv_one_packet(0); /* * This packet is done. Delete it. */ if (this->done) { talloc_free(this); continue; } /* * Packets from multiple '-f' are sent * in parallel. * * Packets from one file are sent in * series, unless '-p' is specified, in * which case N packets from each file * are sent in parallel. */ if (this->files->packets != filename) { filename = this->files->packets; n = parallel; } if (n > 0) { n--; /* * Send the current packet. */ if (send_one_packet(this) < 0) { talloc_free(this); break; } /* * Wait a little before sending * the next packet, if told to. */ if (persec) { struct timeval tv; /* * Don't sleep elsewhere. */ sleep_time = 0; if (persec == 1) { tv.tv_sec = 1; tv.tv_usec = 0; } else { tv.tv_sec = 0; tv.tv_usec = 1000000/persec; } /* * Sleep for milliseconds, * portably. * * If we get an error or * a signal, treat it like * a normal timeout. */ select(0, NULL, NULL, NULL, &tv); } /* * If we haven't sent this packet * often enough, we're not done, * and we shouldn't sleep. */ if (this->resend < resend_count) { done = false; sleep_time = 0; } } else { /* haven't sent this packet, we're not done */ assert(this->done == false); assert(this->reply == NULL); done = false; } } /* * Still have outstanding requests. */ if (fr_packet_list_num_elements(pl) > 0) { done = false; } else { sleep_time = 0; } /* * Nothing to do until we receive a request, so * sleep until then. Once we receive one packet, * we go back, and walk through the whole list again, * sending more packets (if necessary), and updating * the sleep time. */ if (!done && (sleep_time > 0)) { recv_one_packet(sleep_time); } } while (!done); rbtree_free(filename_tree); fr_packet_list_free(pl); while (request_head) TALLOC_FREE(request_head); dict_free(); if (do_summary) { DEBUG("Packet summary:\n" "\tAccepted : %" PRIu64 "\n" "\tRejected : %" PRIu64 "\n" "\tLost : %" PRIu64 "\n" "\tPassed filter : %" PRIu64 "\n" "\tFailed filter : %" PRIu64, stats.accepted, stats.rejected, stats.lost, stats.passed, stats.failed ); } if ((stats.lost > 0) || (stats.failed > 0)) { exit(1); } exit(0); }
int main(int argc, char **argv) { int argval; bool quiet = false; int sockfd = -1; char *line = NULL; ssize_t len; char const *file = NULL; char const *name = "radiusd"; char *p, buffer[65536]; char const *input_file = NULL; FILE *inputfp = stdin; char const *output_file = NULL; char const *server = NULL; char const *radius_dir = RADIUS_DIR; char const *dict_dir = DICTDIR; char *commands[MAX_COMMANDS]; int num_commands = -1; #ifndef NDEBUG if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) { fr_perror("radmin"); exit(EXIT_FAILURE); } #endif talloc_set_log_stderr(); outputfp = stdout; /* stdout is not a constant value... */ if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL) { progname = argv[0]; } else { progname++; } while ((argval = getopt(argc, argv, "d:D:hi:e:Ef:n:o:qs:S")) != EOF) { switch (argval) { case 'd': if (file) { fprintf(stderr, "%s: -d and -f cannot be used together.\n", progname); exit(1); } if (server) { fprintf(stderr, "%s: -d and -s cannot be used together.\n", progname); exit(1); } radius_dir = optarg; break; case 'D': dict_dir = optarg; break; case 'e': num_commands++; /* starts at -1 */ if (num_commands >= MAX_COMMANDS) { fprintf(stderr, "%s: Too many '-e'\n", progname); exit(1); } commands[num_commands] = optarg; break; case 'E': echo = true; break; case 'f': radius_dir = NULL; file = optarg; break; default: case 'h': usage(0); break; case 'i': if (strcmp(optarg, "-") != 0) { input_file = optarg; } quiet = true; break; case 'n': name = optarg; break; case 'o': if (strcmp(optarg, "-") != 0) { output_file = optarg; } quiet = true; break; case 'q': quiet = true; break; case 's': if (file) { fprintf(stderr, "%s: -s and -f cannot be used together.\n", progname); usage(1); } radius_dir = NULL; server = optarg; break; case 'S': secret = NULL; break; } } /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("radmin"); exit(1); } if (radius_dir) { int rcode; CONF_SECTION *cs, *subcs; file = NULL; /* MUST read it from the conffile now */ snprintf(buffer, sizeof(buffer), "%s/%s.conf", radius_dir, name); /* * Need to read in the dictionaries, else we may get * validation errors when we try and parse the config. */ if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radmin"); exit(64); } if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) { fr_perror("radmin"); exit(64); } cs = cf_file_read(buffer); if (!cs) { fprintf(stderr, "%s: Errors reading or parsing %s\n", progname, buffer); usage(1); } subcs = NULL; while ((subcs = cf_subsection_find_next(cs, subcs, "listen")) != NULL) { char const *value; CONF_PAIR *cp = cf_pair_find(subcs, "type"); if (!cp) continue; value = cf_pair_value(cp); if (!value) continue; if (strcmp(value, "control") != 0) continue; /* * Now find the socket name (sigh) */ rcode = cf_item_parse(subcs, "socket", FR_ITEM_POINTER(PW_TYPE_STRING, &file), NULL); if (rcode < 0) { fprintf(stderr, "%s: Failed parsing listen section\n", progname); exit(1); } if (!file) { fprintf(stderr, "%s: No path given for socket\n", progname); usage(1); } break; } if (!file) { fprintf(stderr, "%s: Could not find control socket in %s\n", progname, buffer); exit(1); } } if (input_file) { inputfp = fopen(input_file, "r"); if (!inputfp) { fprintf(stderr, "%s: Failed opening %s: %s\n", progname, input_file, fr_syserror(errno)); exit(1); } } if (output_file) { outputfp = fopen(output_file, "w"); if (!outputfp) { fprintf(stderr, "%s: Failed creating %s: %s\n", progname, output_file, fr_syserror(errno)); exit(1); } } if (!file && !server) { fprintf(stderr, "%s: Must use one of '-d' or '-f' or '-s'\n", progname); exit(1); } /* * Check if stdin is a TTY only if input is from stdin */ if (input_file && !quiet && !isatty(STDIN_FILENO)) quiet = true; #ifdef USE_READLINE if (!quiet) { #ifdef USE_READLINE_HISTORY using_history(); #endif rl_bind_key('\t', rl_insert); } #endif /* * Prevent SIGPIPEs from terminating the process */ signal(SIGPIPE, SIG_IGN); if (do_connect(&sockfd, file, server) < 0) exit(1); /* * Run one command. */ if (num_commands >= 0) { int i; for (i = 0; i <= num_commands; i++) { len = run_command(sockfd, commands[i], buffer, sizeof(buffer)); if (len < 0) exit(1); if (buffer[0]) { fputs(buffer, outputfp); fprintf(outputfp, "\n"); fflush(outputfp); } } exit(0); } if (!quiet) { printf("%s - FreeRADIUS Server administration tool.\n", radmin_version); printf("Copyright (C) 2008-2014 The FreeRADIUS server project and contributors.\n"); printf("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"); printf("PARTICULAR PURPOSE.\n"); printf("You may redistribute copies of FreeRADIUS under the terms of the\n"); printf("GNU General Public License v2.\n"); } /* * FIXME: Do login? */ while (1) { #ifndef USE_READLINE if (!quiet) { printf("radmin> "); fflush(stdout); } #else if (!quiet) { line = readline("radmin> "); if (!line) break; if (!*line) { free(line); continue; } #ifdef USE_READLINE_HISTORY add_history(line); #endif } else /* quiet, or no readline */ #endif { line = fgets(buffer, sizeof(buffer), inputfp); if (!line) break; p = strchr(buffer, '\n'); if (!p) { fprintf(stderr, "%s: Input line too long\n", progname); exit(1); } *p = '\0'; /* * Strip off leading spaces. */ for (p = line; *p != '\0'; p++) { if ((p[0] == ' ') || (p[0] == '\t')) { line = p + 1; continue; } if (p[0] == '#') { line = NULL; break; } break; } /* * Comments: keep going. */ if (!line) continue; /* * Strip off CR / LF */ for (p = line; *p != '\0'; p++) { if ((p[0] == '\r') || (p[0] == '\n')) { p[0] = '\0'; break; } } } if (strcmp(line, "reconnect") == 0) { if (do_connect(&sockfd, file, server) < 0) exit(1); line = NULL; continue; } if (memcmp(line, "secret ", 7) == 0) { if (!secret) { secret = line + 7; do_challenge(sockfd); } line = NULL; continue; } /* * Exit, done, etc. */ if ((strcmp(line, "exit") == 0) || (strcmp(line, "quit") == 0)) { break; } if (server && !secret) { fprintf(stderr, "ERROR: You must enter 'secret <SECRET>' before running any commands\n"); line = NULL; continue; } len = run_command(sockfd, line, buffer, sizeof(buffer)); if ((len < 0) && (do_connect(&sockfd, file, server) < 0)) { fprintf(stderr, "Reconnecting..."); exit(1); } else if (len == 0) break; else if (len == 1) continue; /* no output. */ fputs(buffer, outputfp); fflush(outputfp); fprintf(outputfp, "\n"); } fprintf(outputfp, "\n"); return 0; }
int main(int argc, char **argv) { int argval, quiet = 0; int done_license = 0; int sockfd; uint32_t magic, needed; char *line = NULL; ssize_t len, size; char const *file = NULL; char const *name = "radiusd"; char *p, buffer[65536]; char const *input_file = NULL; FILE *inputfp = stdin; char const *output_file = NULL; char const *server = NULL; char *commands[MAX_COMMANDS]; int num_commands = -1; #ifndef NDEBUG fr_fault_setup(getenv("PANIC_ACTION"), argv[0]); #endif talloc_set_log_stderr(); outputfp = stdout; /* stdout is not a constant value... */ if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL) progname = argv[0]; else progname++; while ((argval = getopt(argc, argv, "d:hi:e:Ef:n:o:qs:S")) != EOF) { switch(argval) { case 'd': if (file) { fprintf(stderr, "%s: -d and -f cannot be used together.\n", progname); exit(1); } if (server) { fprintf(stderr, "%s: -d and -s cannot be used together.\n", progname); exit(1); } radius_dir = optarg; break; case 'e': num_commands++; /* starts at -1 */ if (num_commands >= MAX_COMMANDS) { fprintf(stderr, "%s: Too many '-e'\n", progname); exit(1); } commands[num_commands] = optarg; break; case 'E': echo = true; break; case 'f': radius_dir = NULL; file = optarg; break; default: case 'h': usage(0); break; case 'i': if (strcmp(optarg, "-") != 0) { input_file = optarg; } quiet = 1; break; case 'n': name = optarg; break; case 'o': if (strcmp(optarg, "-") != 0) { output_file = optarg; } quiet = 1; break; case 'q': quiet = 1; break; case 's': if (file) { fprintf(stderr, "%s: -s and -f cannot be used together.\n", progname); usage(1); } radius_dir = NULL; server = optarg; break; case 'S': secret = NULL; break; } } /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("radmin"); exit(1); } if (radius_dir) { int rcode; CONF_SECTION *cs, *subcs; file = NULL; /* MUST read it from the conffile now */ snprintf(buffer, sizeof(buffer), "%s/%s.conf", radius_dir, name); cs = cf_file_read(buffer); if (!cs) { fprintf(stderr, "%s: Errors reading or parsing %s\n", progname, buffer); usage(1); } subcs = NULL; while ((subcs = cf_subsection_find_next(cs, subcs, "listen")) != NULL) { char const *value; CONF_PAIR *cp = cf_pair_find(subcs, "type"); if (!cp) continue; value = cf_pair_value(cp); if (!value) continue; if (strcmp(value, "control") != 0) continue; /* * Now find the socket name (sigh) */ rcode = cf_item_parse(subcs, "socket", PW_TYPE_STRING_PTR, &file, NULL); if (rcode < 0) { fprintf(stderr, "%s: Failed parsing listen section\n", progname); exit(1); } if (!file) { fprintf(stderr, "%s: No path given for socket\n", progname); usage(1); } break; } if (!file) { fprintf(stderr, "%s: Could not find control socket in %s\n", progname, buffer); exit(1); } } if (input_file) { inputfp = fopen(input_file, "r"); if (!inputfp) { fprintf(stderr, "%s: Failed opening %s: %s\n", progname, input_file, strerror(errno)); exit(1); } } if (output_file) { outputfp = fopen(output_file, "w"); if (!outputfp) { fprintf(stderr, "%s: Failed creating %s: %s\n", progname, output_file, strerror(errno)); exit(1); } } /* * Check if stdin is a TTY only if input is from stdin */ if (input_file && !quiet && !isatty(STDIN_FILENO)) quiet = 1; #ifdef USE_READLINE if (!quiet) { #ifdef USE_READLINE_HISTORY using_history(); #endif rl_bind_key('\t', rl_insert); } #endif reconnect: if (file) { /* * FIXME: Get destination from command line, if possible? */ sockfd = fr_domain_socket(file); if (sockfd < 0) { exit(1); } } else { sockfd = client_socket(server); } /* * Read initial magic && version information. */ for (size = 0; size < 8; size += len) { len = read(sockfd, buffer + size, 8 - size); if (len < 0) { fprintf(stderr, "%s: Error reading initial data from socket: %s\n", progname, strerror(errno)); exit(1); } } memcpy(&magic, buffer, 4); magic = ntohl(magic); if (magic != 0xf7eead15) { fprintf(stderr, "%s: Socket %s is not FreeRADIUS administration socket\n", progname, file); exit(1); } memcpy(&magic, buffer + 4, 4); magic = ntohl(magic); if (!server) { needed = 1; } else { needed = 2; } if (magic != needed) { fprintf(stderr, "%s: Socket version mismatch: Need %d, got %d\n", progname, needed, magic); exit(1); } if (server && secret) do_challenge(sockfd); /* * Run one command. */ if (num_commands >= 0) { int i; for (i = 0; i <= num_commands; i++) { size = run_command(sockfd, commands[i], buffer, sizeof(buffer)); if (size < 0) exit(1); if (buffer[0]) { fputs(buffer, outputfp); fprintf(outputfp, "\n"); fflush(outputfp); } } exit(0); } if (!done_license && !quiet) { printf("%s - FreeRADIUS Server administration tool.\n", radmin_version); printf("Copyright (C) 2008-2014 The FreeRADIUS server project and contributors.\n"); printf("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"); printf("PARTICULAR PURPOSE.\n"); printf("You may redistribute copies of FreeRADIUS under the terms of the\n"); printf("GNU General Public License v2.\n"); done_license = 1; } /* * FIXME: Do login? */ while (1) { #ifndef USE_READLINE if (!quiet) { printf("radmin> "); fflush(stdout); } #else if (!quiet) { line = readline("radmin> "); if (!line) break; if (!*line) { free(line); continue; } #ifdef USE_READLINE_HISTORY add_history(line); #endif } else /* quiet, or no readline */ #endif { line = fgets(buffer, sizeof(buffer), inputfp); if (!line) break; p = strchr(buffer, '\n'); if (!p) { fprintf(stderr, "%s: Input line too long\n", progname); exit(1); } *p = '\0'; /* * Strip off leading spaces. */ for (p = line; *p != '\0'; p++) { if ((p[0] == ' ') || (p[0] == '\t')) { line = p + 1; continue; } if (p[0] == '#') { line = NULL; break; } break; } /* * Comments: keep going. */ if (!line) continue; /* * Strip off CR / LF */ for (p = line; *p != '\0'; p++) { if ((p[0] == '\r') || (p[0] == '\n')) { p[0] = '\0'; break; } } } if (strcmp(line, "reconnect") == 0) { close(sockfd); line = NULL; goto reconnect; } if (memcmp(line, "secret ", 7) == 0) { if (!secret) { secret = line + 7; do_challenge(sockfd); } line = NULL; continue; } /* * Exit, done, etc. */ if ((strcmp(line, "exit") == 0) || (strcmp(line, "quit") == 0)) { break; } if (server && !secret) { fprintf(stderr, "ERROR: You must enter 'secret <SECRET>' before running any commands\n"); line = NULL; continue; } size = run_command(sockfd, line, buffer, sizeof(buffer)); if (size <= 0) break; /* error, or clean exit */ if (size == 1) continue; /* no output. */ fputs(buffer, outputfp); fflush(outputfp); fprintf(outputfp, "\n"); } fprintf(outputfp, "\n"); return 0; }
int main(int argc, char **argv) { int c; char filesecret[256]; FILE *fp; int force_af = AF_UNSPEC; radsnmp_conf_t *conf; int ret; int sockfd; TALLOC_CTX *autofree = talloc_autofree_context(); fr_log_fp = stderr; conf = talloc_zero(autofree, radsnmp_conf_t); conf->proto = IPPROTO_UDP; conf->dict_dir = DICTDIR; conf->raddb_dir = RADDBDIR; conf->secret = talloc_strdup(conf, "testing123"); conf->timeout.tv_sec = 3; conf->retries = 5; #ifndef NDEBUG if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) { fr_perror("radsnmp"); exit(EXIT_FAILURE); } #endif talloc_set_log_stderr(); while ((c = getopt(argc, argv, "46c:d:D:f:Fhi:l:n:p:P:qr:sS:t:vx")) != -1) switch (c) { case '4': force_af = AF_INET; break; case '6': force_af = AF_INET6; break; case 'D': conf->dict_dir = optarg; break; case 'd': conf->raddb_dir = optarg; break; case 'l': { int log_fd; if (strcmp(optarg, "stderr") == 0) { fr_log_fp = stderr; /* stdout goes to netsnmp */ break; } log_fd = open(optarg, O_WRONLY | O_APPEND | O_CREAT, 0640); if (log_fd < 0) { fprintf(stderr, "radsnmp: Failed to open log file %s: %s\n", optarg, fr_syserror(errno)); exit(EXIT_FAILURE); } fr_log_fp = fdopen(log_fd, "a"); } break; case 'P': conf->proto_str = optarg; if (strcmp(conf->proto_str, "tcp") != 0) { if (strcmp(conf->proto_str, "udp") != 0) usage(); } else { conf->proto = IPPROTO_TCP; } break; case 'r': if (!isdigit((int) *optarg)) usage(); conf->retries = atoi(optarg); if ((conf->retries == 0) || (conf->retries > 1000)) usage(); break; case 'S': { char *p; fp = fopen(optarg, "r"); if (!fp) { ERROR("Error opening %s: %s", optarg, fr_syserror(errno)); exit(EXIT_FAILURE); } if (fgets(filesecret, sizeof(filesecret), fp) == NULL) { ERROR("Error reading %s: %s", optarg, fr_syserror(errno)); exit(EXIT_FAILURE); } fclose(fp); /* truncate newline */ p = filesecret + strlen(filesecret) - 1; while ((p >= filesecret) && (*p < ' ')) { *p = '\0'; --p; } if (strlen(filesecret) < 2) { ERROR("Secret in %s is too short", optarg); exit(EXIT_FAILURE); } talloc_free(conf->secret); conf->secret = talloc_strdup(conf, filesecret); } break; case 't': if (fr_timeval_from_str(&conf->timeout, optarg) < 0) { ERROR("Failed parsing timeout value %s", fr_strerror()); exit(EXIT_FAILURE); } break; case 'v': DEBUG("%s", radsnmp_version); exit(0); case 'x': fr_debug_lvl++; break; case 'h': default: usage(); } argc -= (optind - 1); argv += (optind - 1); if ((argc < 2) || ((conf->secret == NULL) && (argc < 3))) { ERROR("Insufficient arguments"); usage(); } /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("radsnmp"); return EXIT_FAILURE; } if (fr_dict_autoload(radsnmp_dict) < 0) { fr_perror("radsnmp"); exit(EXIT_FAILURE); } if (fr_dict_attr_autoload(radsnmp_dict_attr) < 0) { fr_perror("radsnmp"); exit(EXIT_FAILURE); } if (fr_dict_read(dict_freeradius, conf->raddb_dir, FR_DICTIONARY_FILE) == -1) { fr_perror("radsnmp"); exit(EXIT_FAILURE); } fr_strerror(); /* Clear the error buffer */ if (fr_log_fp) setvbuf(fr_log_fp, NULL, _IONBF, 0); /* * Get the request type */ if (!isdigit((int) argv[2][0])) { int code; code = fr_str2int(fr_request_types, argv[2], -1); if (code < 0) { ERROR("Unrecognised request type \"%s\"", argv[2]); usage(); } conf->code = (unsigned int)code; } else { conf->code = atoi(argv[2]); } /* * Resolve hostname. */ if (fr_inet_pton_port(&conf->server_ipaddr, &conf->server_port, argv[1], -1, force_af, true, true) < 0) { ERROR("%s", fr_strerror()); exit(EXIT_FAILURE); } /* * Add the secret */ if (argv[3]) { talloc_free(conf->secret); conf->secret = talloc_strdup(conf, argv[3]); } conf->snmp_root = fr_dict_attr_child_by_num(attr_vendor_specific, VENDORPEC_FREERADIUS); if (!conf->snmp_root) { ERROR("Incomplete dictionary: Missing definition for Extended-Attribute-1(%i)." "Vendor-Specific(%i).FreeRADIUS(%i)", attr_extended_attribute_1->attr, attr_vendor_specific->attr, VENDORPEC_FREERADIUS); dict_error: talloc_free(conf); exit(EXIT_FAILURE); } conf->snmp_oid_root = fr_dict_attr_child_by_num(conf->snmp_root, 1); if (!conf->snmp_oid_root) { ERROR("Incomplete dictionary: Missing definition for 1.Extended-Attribute-1(%i)." "Vendor-Specific(%i).FreeRADIUS(%i).FreeRADIUS-Iso(%i)", attr_extended_attribute_1->attr, attr_vendor_specific->attr, VENDORPEC_FREERADIUS, 1); goto dict_error; } switch (conf->proto) { case IPPROTO_TCP: sockfd = fr_socket_client_tcp(NULL, &conf->server_ipaddr, conf->server_port, true); break; default: case IPPROTO_UDP: sockfd = fr_socket_client_udp(NULL, NULL, &conf->server_ipaddr, conf->server_port, true); break; } if (sockfd < 0) { ERROR("Failed connecting to server %s:%hu", "foo", conf->server_port); ret = 1; goto finish; } fr_set_signal(SIGPIPE, rs_signal_stop); fr_set_signal(SIGINT, rs_signal_stop); fr_set_signal(SIGTERM, rs_signal_stop); #ifdef SIGQUIT fr_set_signal(SIGQUIT, rs_signal_stop); #endif DEBUG("%s - Starting pass_persist read loop", radsnmp_version); ret = radsnmp_send_recv(conf, sockfd); DEBUG("Read loop done"); finish: if (fr_log_fp) fflush(fr_log_fp); /* * Everything should be parented from conf */ talloc_free(conf); /* * ...except the dictionaries */ fr_dict_autofree(radsnmp_dict); return ret; }
/* * The main guy. */ int main(int argc, char *argv[]) { int rcode = EXIT_SUCCESS; int status; int argval; bool spawn_flag = true; bool write_pid = false; bool display_version = false; int flag = 0; int from_child[2] = {-1, -1}; fr_state_t *state = NULL; /* * We probably don't want to free the talloc autofree context * directly, so we'll allocate a new context beneath it, and * free that before any leak reports. */ TALLOC_CTX *autofree = talloc_init("main"); #ifdef OSFC2 set_auth_parameters(argc, argv); #endif if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL) progname = argv[0]; else progname++; #ifdef WIN32 { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { fprintf(stderr, "%s: Unable to initialize socket library.\n", progname); exit(EXIT_FAILURE); } } #endif rad_debug_lvl = 0; set_radius_dir(autofree, 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"; main_config.daemonize = true; /* * Don't put output anywhere until we get told a little * more. */ default_log.dst = L_DST_NULL; default_log.fd = -1; main_config.log_file = NULL; /* Process the options. */ while ((argval = getopt(argc, argv, "Cd:D:fhi:l:mMn:p:PstvxX")) != EOF) { switch (argval) { case 'C': check_config = true; spawn_flag = false; main_config.daemonize = false; break; case 'd': set_radius_dir(autofree, optarg); break; case 'D': main_config.dictionary_dir = talloc_typed_strdup(autofree, optarg); break; case 'f': main_config.daemonize = false; break; case 'h': usage(0); break; case 'l': if (strcmp(optarg, "stdout") == 0) { goto do_stdout; } main_config.log_file = strdup(optarg); default_log.dst = L_DST_FILES; default_log.fd = open(main_config.log_file, O_WRONLY | O_APPEND | O_CREAT, 0640); if (default_log.fd < 0) { fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", main_config.log_file, fr_syserror(errno)); exit(EXIT_FAILURE); } fr_log_fp = fdopen(default_log.fd, "a"); break; case 'i': if (ip_hton(&main_config.myip, AF_UNSPEC, optarg, false) < 0) { fprintf(stderr, "radiusd: Invalid IP Address or hostname \"%s\"\n", optarg); exit(EXIT_FAILURE); } flag |= 1; break; case 'n': main_config.name = optarg; break; case 'm': main_config.debug_memory = true; break; case 'M': main_config.memory_report = true; main_config.debug_memory = true; break; case 'p': { unsigned long port; port = strtoul(optarg, 0, 10); if ((port == 0) || (port > UINT16_MAX)) { fprintf(stderr, "radiusd: Invalid port number \"%s\"\n", optarg); exit(EXIT_FAILURE); } main_config.port = (uint16_t) port; flag |= 2; } break; case 'P': /* Force the PID to be written, even in -f mode */ write_pid = true; break; case 's': /* Single process mode */ spawn_flag = false; main_config.daemonize = false; break; case 't': /* no child threads */ spawn_flag = false; break; case 'v': display_version = true; break; case 'X': spawn_flag = false; main_config.daemonize = false; rad_debug_lvl += 2; main_config.log_auth = true; main_config.log_auth_badpass = true; main_config.log_auth_goodpass = true; do_stdout: fr_log_fp = stdout; default_log.dst = L_DST_STDOUT; default_log.fd = STDOUT_FILENO; break; case 'x': rad_debug_lvl++; break; default: usage(1); break; } } /* * 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); } if (rad_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) exit(EXIT_FAILURE); /* * Mismatch between build time OpenSSL and linked SSL, better to die * here than segfault later. */ #ifdef HAVE_OPENSSL_CRYPTO_H if (ssl_check_consistency() < 0) exit(EXIT_FAILURE); #endif if (flag && (flag != 0x03)) { fprintf(stderr, "radiusd: The options -i and -p cannot be used individually.\n"); exit(EXIT_FAILURE); } /* * According to the talloc peeps, no two threads may modify any part of * a ctx tree with a common root without synchronisation. * * So we can't run with a null context and threads. */ if (main_config.memory_report) { if (spawn_flag) { fprintf(stderr, "radiusd: The server cannot produce memory reports (-M) in threaded mode\n"); exit(EXIT_FAILURE); } talloc_enable_null_tracking(); } else { talloc_disable_null_tracking(); } /* * Better here, so it doesn't matter whether we get passed -xv or -vx. */ if (display_version) { /* Don't print timestamps */ rad_debug_lvl += 2; fr_log_fp = stdout; default_log.dst = L_DST_STDOUT; default_log.fd = STDOUT_FILENO; INFO("%s: %s", progname, radiusd_version); version_print(); exit(EXIT_SUCCESS); } if (rad_debug_lvl) version_print(); /* * Under linux CAP_SYS_PTRACE is usually only available before setuid/setguid, * so we need to check whether we can attach before calling those functions * (in main_config_init()). */ fr_store_debug_state(); /* * Initialising OpenSSL once, here, is safer than having individual modules do it. */ #ifdef HAVE_OPENSSL_CRYPTO_H tls_global_init(); #endif /* * Read the configuration files, BEFORE doing anything else. */ if (main_config_init() < 0) exit(EXIT_FAILURE); /* * This is very useful in figuring out why the panic_action didn't fire. */ INFO("%s", fr_debug_state_to_msg(fr_debug_state)); /* * Check for vulnerabilities in the version of libssl were linked against. */ #if defined(HAVE_OPENSSL_CRYPTO_H) && defined(ENABLE_OPENSSL_VERSION_CHECK) if (tls_global_version_check(main_config.allow_vulnerable_openssl) < 0) exit(EXIT_FAILURE); #endif fr_talloc_fault_setup(); /* * Set the panic action (if required) */ { char const *panic_action = NULL; panic_action = getenv("PANIC_ACTION"); if (!panic_action) panic_action = main_config.panic_action; if (panic_action && (fr_fault_setup(panic_action, argv[0]) < 0)) { fr_perror("radiusd"); exit(EXIT_FAILURE); } } #ifndef __MINGW32__ /* * Disconnect from session */ if (main_config.daemonize) { pid_t pid; int devnull; /* * Really weird things happen if we leave stdin open and call things like * system() later. */ devnull = open("/dev/null", O_RDWR); if (devnull < 0) { ERROR("Failed opening /dev/null: %s", fr_syserror(errno)); exit(EXIT_FAILURE); } dup2(devnull, STDIN_FILENO); close(devnull); if (pipe(from_child) != 0) { ERROR("Couldn't open pipe for child status: %s", fr_syserror(errno)); exit(EXIT_FAILURE); } pid = fork(); if (pid < 0) { ERROR("Couldn't fork: %s", fr_syserror(errno)); exit(EXIT_FAILURE); } /* * The parent exits, so the child can run in the background. * * As the child can still encounter an error during initialisation * we do a blocking read on a pipe between it and the parent. * * Just before entering the event loop the child will send a success * or failure message to the parent, via the pipe. */ if (pid > 0) { uint8_t ret = 0; int stat_loc; /* So the pipe is correctly widowed if the child exits */ close(from_child[1]); /* * The child writes a 0x01 byte on success, and closes * the pipe on error. */ if ((read(from_child[0], &ret, 1) < 0)) { ret = 0; } /* For cleanliness... */ close(from_child[0]); /* Don't turn children into zombies */ if (!ret) { waitpid(pid, &stat_loc, WNOHANG); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } /* so the pipe is correctly widowed if the parent exits?! */ close(from_child[0]); # ifdef HAVE_SETSID setsid(); # endif } #endif /* * Ensure that we're using the CORRECT pid after forking, NOT the one * we started with. */ radius_pid = getpid(); /* * Initialize any event loops just enough so module instantiations can * add fd/event to them, but do not start them yet. * * This has to be done post-fork in case we're using kqueue, where the * queue isn't inherited by the child process. */ if (!radius_event_init(autofree)) exit(EXIT_FAILURE); /* * Load the modules */ if (modules_init(main_config.config) < 0) exit(EXIT_FAILURE); /* * Redirect stderr/stdout as appropriate. */ if (radlog_init(&default_log, main_config.daemonize) < 0) { ERROR("%s", fr_strerror()); exit(EXIT_FAILURE); } event_loop_started = true; /* * Start the event loop(s) and threads. */ radius_event_start(main_config.config, spawn_flag); /* * Now that we've set everything up, we can install the signal * handlers. Before this, if we get any signal, we don't know * what to do, so we might as well do the default, and die. */ #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif if ((fr_set_signal(SIGHUP, sig_hup) < 0) || (fr_set_signal(SIGTERM, sig_fatal) < 0)) { ERROR("%s", fr_strerror()); exit(EXIT_FAILURE); } /* * If we're debugging, then a CTRL-C will cause the server to die * immediately. Use SIGTERM to shut down the server cleanly in * that case. */ if (main_config.debug_memory || (rad_debug_lvl == 0)) { if ((fr_set_signal(SIGINT, sig_fatal) < 0) #ifdef SIGQUIT || (fr_set_signal(SIGQUIT, sig_fatal) < 0) #endif ) { ERROR("%s", fr_strerror()); exit(EXIT_FAILURE); } } /* * Everything seems to have loaded OK, exit gracefully. */ if (check_config) { DEBUG("Configuration appears to be OK"); /* for -C -m|-M */ if (main_config.debug_memory) goto cleanup; exit(EXIT_SUCCESS); } #ifdef WITH_STATS radius_stats_init(0); #endif /* * Write the PID always if we're running as a daemon. */ if (main_config.daemonize) write_pid = true; /* * Write the PID after we've forked, so that we write the correct one. */ if (write_pid) { FILE *fp; fp = fopen(main_config.pid_file, "w"); if (fp != NULL) { /* * @fixme What about following symlinks, * and having it over-write a normal file? */ fprintf(fp, "%d\n", (int) radius_pid); fclose(fp); } else { ERROR("Failed creating PID file %s: %s", main_config.pid_file, fr_syserror(errno)); exit(EXIT_FAILURE); } } exec_trigger(NULL, NULL, "server.start", false); /* * Inform the parent (who should still be waiting) that the rest of * initialisation went OK, and that it should exit with a 0 status. * If we don't get this far, then we just close the pipe on exit, and the * parent gets a read failure. */ if (main_config.daemonize) { if (write(from_child[1], "\001", 1) < 0) { WARN("Failed informing parent of successful start: %s", fr_syserror(errno)); } close(from_child[1]); } /* * Clear the libfreeradius error buffer. */ fr_strerror(); /* * Initialise the state rbtree (used to link multiple rounds of challenges). */ state = fr_state_init(NULL, 0); /* * Process requests until HUP or exit. */ while ((status = radius_event_process()) == 0x80) { #ifdef WITH_STATS radius_stats_init(1); #endif main_config_hup(); } if (status < 0) { ERROR("Exiting due to internal error: %s", fr_strerror()); rcode = EXIT_FAILURE; } else { INFO("Exiting normally"); } /* * Ignore the TERM signal: we're about to die. */ signal(SIGTERM, SIG_IGN); /* * Fire signal and stop triggers after ignoring SIGTERM, so handlers are * not killed with the rest of the process group, below. */ if (status == 2) exec_trigger(NULL, NULL, "server.signal.term", true); exec_trigger(NULL, NULL, "server.stop", false); /* * Send a TERM signal to all associated processes * (including us, which gets ignored.) */ #ifndef __MINGW32__ if (spawn_flag) kill(-radius_pid, SIGTERM); #endif /* * We're exiting, so we can delete the PID file. * (If it doesn't exist, we can ignore the error returned by unlink) */ if (main_config.daemonize) unlink(main_config.pid_file); radius_event_free(); cleanup: /* * Detach any modules. */ modules_free(); xlat_free(); /* modules may have xlat's */ fr_state_delete(state); /* * Free the configuration items. */ main_config_free(); #ifdef WIN32 WSACleanup(); #endif #ifdef HAVE_OPENSSL_CRYPTO_H tls_global_cleanup(); #endif /* * So we don't see autofreed memory in the talloc report */ talloc_free(autofree); if (main_config.memory_report) { INFO("Allocated memory at time of report:"); fr_log_talloc_report(NULL); } return rcode; }
/* * 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; bool xlat_only = false; fr_state_tree_t *state = NULL; fr_talloc_fault_setup(); /* * 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 rad_debug_lvl = 0; set_radius_dir(NULL, RADIUS_DIR); /* * Ensure that the configuration is initialized. */ memset(&main_config, 0, sizeof(main_config)); main_config.name = "unittest"; /* * 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: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 'O': if (strcmp(optarg, "xlat_only") == 0) { xlat_only = true; break; } fprintf(stderr, "Unknown option '%s'\n", optarg); exit(EXIT_FAILURE); case 'X': rad_debug_lvl += 2; main_config.log_auth = true; main_config.log_auth_badpass = true; main_config.log_auth_goodpass = true; break; case 'x': rad_debug_lvl++; break; default: usage(1); break; } } if (rad_debug_lvl) version_print(); fr_debug_lvl = rad_debug_lvl; /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("%s", main_config.name); exit(EXIT_FAILURE); } /* * Initialising OpenSSL once, here, is safer than having individual modules do it. */ #ifdef HAVE_OPENSSL_CRYPTO_H if (tls_global_init() < 0) { rcode = EXIT_FAILURE; goto finish; } #endif if (xlat_register(NULL, "poke", xlat_poke, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN) < 0) { rcode = EXIT_FAILURE; goto finish; } if (map_proc_register(NULL, "test-fail", mod_map_proc, NULL, NULL, 0) < 0) { rcode = EXIT_FAILURE; goto finish; } /* Read the configuration files, BEFORE doing anything else. */ if (main_config_init() < 0) { exit_failure: rcode = EXIT_FAILURE; goto finish; } /* * Setup dummy virtual server */ cf_section_add(main_config.config, cf_section_alloc(main_config.config, "server", "unit_test")); /* * Initialize Auth-Type, etc. in the virtual servers * before loading the modules. Some modules need those * to be defined. */ if (virtual_servers_bootstrap(main_config.config) < 0) goto exit_failure; /* * Bootstrap the modules. This links to them, and runs * their "bootstrap" routines. * * After this step, all dynamic attributes, xlats, etc. are defined. */ if (modules_bootstrap(main_config.config) < 0) exit(EXIT_FAILURE); /* * Load the modules */ if (modules_init(main_config.config) < 0) goto exit_failure; /* * And then load the virtual servers. */ if (virtual_servers_init(main_config.config) < 0) goto exit_failure; state = fr_state_tree_init(NULL, main_config.max_requests * 2, 10); /* * Set the panic action (if required) */ { char const *panic_action = NULL; panic_action = getenv("PANIC_ACTION"); if (!panic_action) panic_action = main_config.panic_action; if (panic_action && (fr_fault_setup(panic_action, argv[0]) < 0)) { fr_perror("%s", main_config.name); 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)); rcode = EXIT_FAILURE; goto finish; } } /* * For simplicity, read xlat's. */ if (xlat_only) { if (!do_xlats(input_file, fp)) rcode = EXIT_FAILURE; if (input_file) fclose(fp); 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 (fr_pair_list_afrom_file(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_pair_create(request->reply, &request->reply->vps, PW_RESPONSE_PACKET_TYPE, 0); vp->vp_integer = request->reply->code; { VALUE_PAIR const *failed[2]; if (filter_vps && !fr_pair_validate(failed, filter_vps, request->reply->vps)) { fr_pair_validate_debug(request, failed); fr_perror("Output file %s does not match attributes in filter %s (%s)", output_file ? output_file : input_file, filter_file, fr_strerror()); rcode = EXIT_FAILURE; goto finish; } } INFO("Exiting normally"); finish: talloc_free(request); talloc_free(state); /* * Free the configuration items. */ main_config_free(); /* * Detach any modules. */ modules_free(); xlat_unregister(NULL, "poke", xlat_poke); xlat_free(); /* modules may have xlat's */ if (memory_report) { INFO("Allocated memory at time of report:"); fr_log_talloc_report(NULL); } return rcode; }