/* * Read valuepairs from the fp up to End-Of-File. * * Hmm... this function is only used by radclient.. */ VALUE_PAIR *readvp2(FILE *fp, int *pfiledone, const char *errprefix) { char buf[8192]; FR_TOKEN last_token = T_EOL; VALUE_PAIR *vp; VALUE_PAIR *list; int error = 0; list = NULL; while (!error && fgets(buf, sizeof(buf), fp) != NULL) { /* * If we get a '\n' by itself, we assume that's * the end of that VP */ if ((buf[0] == '\n') && (list)) { return list; } if ((buf[0] == '\n') && (!list)) { continue; } /* * Comments get ignored */ if (buf[0] == '#') continue; /* * Read all of the attributes on the current line. */ vp = NULL; last_token = userparse(buf, &vp); if (!vp) { if (last_token != T_EOL) { fr_perror("%s", errprefix); error = 1; break; } break; } pairadd(&list, vp); buf[0] = '\0'; } if (error) pairfree(&list); *pfiledone = 1; return error ? NULL: list; }
/** Convert a valuepair string to VALUE_PAIR and insert it into a list * * Takes a valuepair string with list and request qualifiers, converts it into a VALUE_PAIR * and inserts it into the appropriate list. * * @param request Current request. * @param raw string to parse. * @param request_def to use if attribute isn't qualified. * @param list_def to use if attribute isn't qualified. * @return 0 on success, -1 on error. */ int radius_str2vp(REQUEST *request, char const *raw, request_refs_t request_def, pair_lists_t list_def) { char const *p; size_t len; request_refs_t req; pair_lists_t list; VALUE_PAIR *vp = NULL; VALUE_PAIR **vps; p = raw; req = radius_request_name(&p, request_def); len = p - raw; if (req == REQUEST_UNKNOWN) { REDEBUG("Invalid request qualifier \"%.*s\"", (int) len, raw); return -1; } raw += len; list = radius_list_name(&p, list_def); if (list == PAIR_LIST_UNKNOWN) { len = p - raw; REDEBUG("Invalid list qualifier \"%.*s\"", (int) len, raw); return -1; } raw += len; if (radius_request(&request, req) < 0) { return -1; } vps = radius_list(request, list); if (!vps) { return -1; } if (userparse(request, raw, &vp) == T_OP_INVALID) { return -1; } pairmove(request, vps, &vp); return 0; }
/* * Read the users, huntgroups or hints file. * Return a PAIR_LIST. */ int pairlist_read(TALLOC_CTX *ctx, char const *file, PAIR_LIST **list, int complain) { FILE *fp; int mode = FIND_MODE_NAME; char entry[256]; char buffer[8192]; char const *ptr; VALUE_PAIR *check_tmp; VALUE_PAIR *reply_tmp; PAIR_LIST *pl = NULL, *t; PAIR_LIST **last = &pl; int lineno = 0; int old_lineno = 0; FR_TOKEN parsecode; char newfile[8192]; DEBUG2("reading pairlist file %s", file); /* * Open the file. The error message should be a little * more useful... */ if ((fp = fopen(file, "r")) == NULL) { if (!complain) return -1; ERROR("Couldn't open %s for reading: %s", file, fr_syserror(errno)); return -1; } parsecode = T_EOL; /* * Read the entire file into memory for speed. */ while(fgets(buffer, sizeof(buffer), fp) != NULL) { lineno++; if (!feof(fp) && (strchr(buffer, '\n') == NULL)) { fclose(fp); ERROR("%s[%d]: line too long", file, lineno); pairlist_free(&pl); return -1; } if (buffer[0] == '#' || buffer[0] == '\n') continue; /* * If the line contains nothing but whitespace, * ignore it. */ ptr = buffer; while (isspace((int) *ptr)) ptr++; if (*ptr == '\0') continue; parse_again: if(mode == FIND_MODE_NAME) { /* * Find the entry starting with the users name */ if (isspace((int) buffer[0])) { if (parsecode != T_EOL) { ERROR("%s[%d]: Unexpected trailing comma for entry %s", file, lineno, entry); fclose(fp); return -1; } continue; } ptr = buffer; getword(&ptr, entry, sizeof(entry)); /* * Include another file if we see * $INCLUDE filename */ if (strcasecmp(entry, "$INCLUDE") == 0) { while(isspace((int) *ptr)) ptr++; /* * If it's an absolute pathname, * then use it verbatim. * * If not, then make the $include * files *relative* to the current * file. */ if (FR_DIR_IS_RELATIVE(ptr)) { char *p; strlcpy(newfile, file, sizeof(newfile)); p = strrchr(newfile, FR_DIR_SEP); if (!p) { p = newfile + strlen(newfile); *p = FR_DIR_SEP; } getword(&ptr, p + 1, sizeof(newfile) - 1 - (p - newfile)); } else { getword(&ptr, newfile, sizeof(newfile)); } t = NULL; if (pairlist_read(ctx, newfile, &t, 0) != 0) { pairlist_free(&pl); ERROR("%s[%d]: Could not open included file %s: %s", file, lineno, newfile, fr_syserror(errno)); fclose(fp); return -1; } *last = t; /* * t may be NULL, it may have one * entry, or it may be a linked list * of entries. Go to the end of the * list. */ while (*last) last = &((*last)->next); continue; } /* * Parse the check values */ check_tmp = NULL; reply_tmp = NULL; old_lineno = lineno; parsecode = userparse(ctx, ptr, &check_tmp); if (parsecode == T_OP_INVALID) { pairlist_free(&pl); ERROR("%s[%d]: Parse error (check) for entry %s: %s", file, lineno, entry, fr_strerror()); fclose(fp); return -1; } else if (parsecode == T_COMMA) { ERROR("%s[%d]: Unexpected trailing comma in check item list for entry %s", file, lineno, entry); fclose(fp); return -1; } mode = FIND_MODE_REPLY; parsecode = T_COMMA; } else { if(*buffer == ' ' || *buffer == '\t') { if (parsecode != T_COMMA) { ERROR("%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s", file, lineno, entry); fclose(fp); return -1; } /* * Parse the reply values */ parsecode = userparse(ctx, buffer, &reply_tmp); /* valid tokens are 1 or greater */ if (parsecode < 1) { pairlist_free(&pl); ERROR("%s[%d]: Parse error (reply) for entry %s: %s", file, lineno, entry, fr_strerror()); fclose(fp); return -1; } } else { /* * Done with this entry... */ MEM(t = talloc_zero(ctx, PAIR_LIST)); t->check = check_tmp; t->reply = reply_tmp; t->lineno = old_lineno; check_tmp = NULL; reply_tmp = NULL; t->name = talloc_typed_strdup(t, entry); *last = t; last = &(t->next); mode = FIND_MODE_NAME; if (buffer[0] != 0) goto parse_again; } } } /* * Make sure that we also read the last line of the file! */ if (mode == FIND_MODE_REPLY) { buffer[0] = 0; goto parse_again; } fclose(fp); *list = pl; return 0; }
static int sm_parse_file(FILE*fp,const char* fname) { FR_TOKEN tok; VALUE_PAIR *vp = NULL; sm_parse_state_t parse_state = SMP_USER; unsigned long lino = 0; char *p; char buff[MAX_BUFF_SIZE]; char username[256]; while( parse_state != SMP_INVALID && fgets(buff, sizeof(buff), fp) != NULL ) { lino ++; st_lines++; if ( strchr(buff, '\n') == NULL) { fprintf(stderr,"%s: %s[%lu]:Warning: line too long or not closed by \\n character. Skiped\n",progname,fname,lino); st_warns++; st_skiped++; /* _LINE_ skiped */ continue; } DOUT2("Parseline: %s",buff); for ( p = buff; isspace((int) *p); p++); if ( *p == '#' || *p == 0 ) continue; /* userparse hack */ if ( *p == ';' ) *p = '\n'; p = buff; /* try to decide is this line new user or new pattern */ if ( parse_state == SMP_PATTERN_OR_USER ) { if ( isspace((int) buff[0]) ) parse_state = SMP_PATTERN; else { parse_state = SMP_USER; storecontent(username); st_users++; } } if ( parse_state == SMP_USER ) { tok = getuname(&p,username,sizeof(username)); /* check: is it include. not implemented */ if ( tok ) { fprintf(stderr ,"%s: %s[%lu]: error while expecting user name\n",progname,fname,lino); parse_state = SMP_INVALID; st_errors++; } else { parse_state = SMP_PATTERN; DOUT1("Found user: %s\n",username); } } if ( parse_state == SMP_PATTERN || parse_state == SMP_ACTION ) { /* check for empty line */ while( *p && isspace((int) *p) ) p++; if ( *p && ( *p != ';' ) ) tok = userparse(p,&vp); else tok = T_EOL; /* ';' - signs empty line */ switch(tok) { case T_EOL: /* add to content */ addlinetocontent(vp); pairfree(&vp); if ( parse_state == SMP_PATTERN ) parse_state = SMP_ACTION; else parse_state = SMP_PATTERN_OR_USER; case T_COMMA: break; /* parse next line */ default: /* error: we do not expect anything else */ fprintf(stderr ,"%s: %s[%lu]: syntax error\n",progname,fname,lino); fr_perror("Error"); parse_state = SMP_INVALID; st_errors++; } } } if ( feof(fp) ) switch (parse_state ) { case SMP_USER: /* file is empty, last line is comment */ break; case SMP_PATTERN: /* only username ?*/ fprintf(stderr ,"%s: %s[%lu]: EOF while pattern line are expecting\n",progname,fname,lino); st_errors++; parse_state = SMP_INVALID; break; case SMP_ACTION: /* looking for reply line */ fprintf(stderr ,"%s: %s[%lu]: EOF while reply line are expecting\n",progname,fname,lino); st_errors++; parse_state = SMP_INVALID; break; case SMP_PATTERN_OR_USER: storecontent(username); st_users++; break; default:break; } else if ( parse_state != SMP_INVALID ) { /* file read error */ fprintf(stderr ,"%s: error file reading from file\n",progname); } pairfree(&vp); return (parse_state == SMP_INVALID)?-1:0; }
int main(int argc, char *argv[]) { const char *from_dev = NULL; /* Capture from device */ const char *from_file = NULL; /* Read from pcap file */ int from_stdin = 0; /* Read from stdin */ pcap_t *in; /* PCAP input handle */ int limit = -1; /* How many packets to sniff */ char errbuf[PCAP_ERRBUF_SIZE]; /* Error buffer */ char *to_file = NULL; /* PCAP output file */ char *pcap_filter = NULL; /* PCAP filter string */ char *radius_filter = NULL; int port = 1812; struct bpf_program fp; /* Holds compiled filter */ bpf_u_int32 ip_mask = PCAP_NETMASK_UNKNOWN; /* Device Subnet mask */ bpf_u_int32 ip_addr = 0; /* Device IP */ char buffer[1024]; int opt; FR_TOKEN parsecode; const char *radius_dir = RADIUS_DIR; fr_debug_flag = 2; log_dst = stdout; /* * Get options */ while ((opt = getopt(argc, argv, "c:d:Ff:hi:I:p:qr:s:Svw:xX")) != EOF) { switch (opt) { case 'c': limit = atoi(optarg); if (limit <= 0) { fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg); exit(1); } break; case 'd': radius_dir = optarg; break; case 'F': from_stdin = 1; to_stdout = 1; break; case 'f': pcap_filter = optarg; break; case 'h': usage(0); break; case 'i': from_dev = optarg; break; case 'I': from_file = optarg; break; case 'p': port = atoi(optarg); break; case 'q': if (fr_debug_flag > 0) { fr_debug_flag--; } break; case 'r': radius_filter = optarg; break; case 's': radius_secret = optarg; break; case 'S': do_sort = 1; break; case 'v': INFO(log_dst, "%s %s\n", radsniff_version, pcap_lib_version()); exit(0); break; case 'w': to_file = optarg; break; case 'x': case 'X': fr_debug_flag++; break; default: usage(64); } } /* What's the point in specifying -F ?! */ if (from_stdin && from_file && to_file) { usage(64); } /* Can't read from both... */ if (from_file && from_dev) { usage(64); } /* Reading from file overrides stdin */ if (from_stdin && (from_file || from_dev)) { from_stdin = 0; } /* Writing to file overrides stdout */ if (to_file && to_stdout) { to_stdout = 0; } /* * If were writing pcap data stdout we *really* don't want to send * logging there as well. */ log_dst = to_stdout ? stderr : stdout; #if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN) if (from_stdin || to_stdout) { fprintf(stderr, "radsniff: PCAP streams not supported.\n"); exit(64); } #endif if (!pcap_filter) { pcap_filter = buffer; snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d", port, port + 1, 3799); } /* * There are times when we don't need the dictionaries. */ if (!to_stdout) { if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radsniff"); exit(64); } } if (radius_filter) { parsecode = userparse(radius_filter, &filter_vps); if (parsecode == T_OP_INVALID) { fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\" (%s)\n", radius_filter, fr_strerror()); exit(64); } if (!filter_vps) { fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter); exit(64); } filter_tree = rbtree_create((rbcmp) fr_packet_cmp, free, 0); if (!filter_tree) { fprintf(stderr, "radsniff: Failed creating filter tree\n"); exit(1); } } /* * Setup the request tree */ request_tree = rbtree_create((rbcmp) fr_packet_cmp, free, 0); if (!request_tree) { fprintf(stderr, "radsniff: Failed creating request tree\n"); exit(1); } /* * Allocate a null packet for decrypting attributes in CoA requests */ nullpacket = rad_alloc(NULL, 0); if (!nullpacket) { fprintf(stderr, "radsniff: Out of memory\n"); exit(1); } /* * Get the default capture device */ if (!from_stdin && !from_file && !from_dev) { from_dev = pcap_lookupdev(errbuf); if (!from_dev) { fprintf(stderr, "radsniff: Failed discovering default interface (%s)\n", errbuf); exit(1); } INFO(log_dst, "Capturing from interface \"%s\"\n", from_dev); } /* * Print captures values which will be used */ if (fr_debug_flag > 2) { DEBUG1(log_dst, "Sniffing with options:\n"); if (from_dev) DEBUG1(log_dst, " Device : [%s]\n", from_dev); if (limit > 0) DEBUG1(log_dst, " Capture limit (packets) : [%d]\n", limit); DEBUG1(log_dst, " PCAP filter : [%s]\n", pcap_filter); DEBUG1(log_dst, " RADIUS secret : [%s]\n", radius_secret); if (filter_vps){DEBUG1(log_dst, " RADIUS filter :\n"); vp_printlist(log_dst, filter_vps); } } /* * Figure out whether were doing a reading from a file, doing a live * capture or reading from stdin. */ if (from_file) { in = pcap_open_offline(from_file, errbuf); #ifdef HAVE_PCAP_FOPEN_OFFLINE } else if (from_stdin) { in = pcap_fopen_offline(stdin, errbuf); #endif } else if (from_dev) { pcap_lookupnet(from_dev, &ip_addr, &ip_mask, errbuf); in = pcap_open_live(from_dev, 65536, 1, 1, errbuf); } else { fprintf(stderr, "radsniff: No capture devices available\n"); } if (!in) { fprintf(stderr, "radsniff: Failed opening input (%s)\n", errbuf); exit(1); } if (to_file) { out = pcap_dump_open(in, to_file); if (!out) { fprintf(stderr, "radsniff: Failed opening output file (%s)\n", pcap_geterr(in)); exit(1); } #ifdef HAVE_PCAP_DUMP_FOPEN } else if (to_stdout) { out = pcap_dump_fopen(in, stdout); if (!out) { fprintf(stderr, "radsniff: Failed opening stdout (%s)\n", pcap_geterr(in)); exit(1); } #endif } /* * Apply the rules */ if (pcap_compile(in, &fp, pcap_filter, 0, ip_mask) < 0) { fprintf(stderr, "radsniff: Failed compiling PCAP filter (%s)\n", pcap_geterr(in)); exit(1); } if (pcap_setfilter(in, &fp) < 0) { fprintf(stderr, "radsniff: Failed applying PCAP filter (%s)\n", pcap_geterr(in)); exit(1); } /* * Enter the main capture loop... */ pcap_loop(in, limit, got_packet, NULL); /* * ...were done capturing. */ pcap_close(in); if (out) { pcap_dump_close(out); } if (filter_tree) { rbtree_free(filter_tree); } INFO(log_dst, "Done sniffing\n"); return 0; }
int main(int argc, char *argv[]) { rs_t *conf; fr_pcap_t *in = NULL, *in_p; fr_pcap_t **in_head = ∈ fr_pcap_t *out = NULL; int ret = 1; /* Exit status */ int limit = -1; /* How many packets to sniff */ char errbuf[PCAP_ERRBUF_SIZE]; /* Error buffer */ int port = 1812; char buffer[1024]; int opt; FR_TOKEN parsecode; char const *radius_dir = RADIUS_DIR; rs_stats_t stats; fr_debug_flag = 2; log_dst = stdout; talloc_set_log_stderr(); conf = talloc_zero(NULL, rs_t); if (!fr_assert(conf)) { exit (1); } /* * We don't really want probes taking down machines */ #ifdef HAVE_TALLOC_SET_MEMLIMIT talloc_set_memlimit(conf, 52428800); /* 50 MB */ #endif /* * Get options */ while ((opt = getopt(argc, argv, "c:d:DFf:hi:I:p:qr:s:Svw:xXW:P:O:")) != EOF) { switch (opt) { case 'c': limit = atoi(optarg); if (limit <= 0) { fprintf(stderr, "radsniff: Invalid number of packets \"%s\"", optarg); exit(1); } break; case 'd': radius_dir = optarg; break; case 'D': { pcap_if_t *all_devices = NULL; pcap_if_t *dev_p; if (pcap_findalldevs(&all_devices, errbuf) < 0) { ERROR("Error getting available capture devices: %s", errbuf); goto finish; } int i = 1; for (dev_p = all_devices; dev_p; dev_p = dev_p->next) { INFO("%i.%s", i++, dev_p->name); } ret = 0; goto finish; } case 'F': conf->from_stdin = true; conf->to_stdout = true; break; case 'f': conf->pcap_filter = optarg; break; case 'h': usage(0); break; case 'i': *in_head = fr_pcap_init(conf, optarg, PCAP_INTERFACE_IN); if (!*in_head) { goto finish; } in_head = &(*in_head)->next; conf->from_dev = true; break; case 'I': *in_head = fr_pcap_init(conf, optarg, PCAP_FILE_IN); if (!*in_head) { goto finish; } in_head = &(*in_head)->next; conf->from_file = true; break; case 'p': port = atoi(optarg); break; case 'q': if (fr_debug_flag > 0) { fr_debug_flag--; } break; case 'r': conf->radius_filter = optarg; break; case 's': conf->radius_secret = optarg; break; case 'S': conf->do_sort = true; break; case 'v': #ifdef HAVE_COLLECTDC_H INFO("%s, %s, collectdclient version %s", radsniff_version, pcap_lib_version(), lcc_version_string()); #else INFO("%s %s", radsniff_version, pcap_lib_version()); #endif exit(0); break; case 'w': out = fr_pcap_init(conf, optarg, PCAP_FILE_OUT); conf->to_file = true; break; case 'x': case 'X': fr_debug_flag++; break; case 'W': conf->stats.interval = atoi(optarg); if (conf->stats.interval <= 0) { ERROR("Stats interval must be > 0"); usage(64); } break; case 'T': conf->stats.timeout = atoi(optarg); if (conf->stats.timeout <= 0) { ERROR("Timeout value must be > 0"); usage(64); } break; #ifdef HAVE_COLLECTDC_H case 'P': conf->stats.prefix = optarg; break; case 'O': conf->stats.collectd = optarg; conf->stats.out = RS_STATS_OUT_COLLECTD; break; #endif default: usage(64); } } /* What's the point in specifying -F ?! */ if (conf->from_stdin && conf->from_file && conf->to_file) { usage(64); } /* Can't read from both... */ if (conf->from_file && conf->from_dev) { usage(64); } /* Reading from file overrides stdin */ if (conf->from_stdin && (conf->from_file || conf->from_dev)) { conf->from_stdin = false; } /* Writing to file overrides stdout */ if (conf->to_file && conf->to_stdout) { conf->to_stdout = false; } if (conf->to_stdout) { out = fr_pcap_init(conf, "stdout", PCAP_STDIO_OUT); if (!out) { goto finish; } } if (conf->from_stdin) { *in_head = fr_pcap_init(conf, "stdin", PCAP_STDIO_IN); if (!*in_head) { goto finish; } in_head = &(*in_head)->next; } if (!conf->radius_secret) { conf->radius_secret = RS_DEFAULT_SECRET; } if (conf->stats.interval && !conf->stats.out) { conf->stats.out = RS_STATS_OUT_STDIO; } if (conf->stats.timeout == 0) { conf->stats.timeout = RS_DEFAULT_TIMEOUT; } /* * If were writing pcap data stdout we *really* don't want to send * logging there as well. */ log_dst = conf->to_stdout ? stderr : stdout; #if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN) if (conf->from_stdin || conf->to_stdout) { ERROR("PCAP streams not supported"); goto finish; } #endif if (!conf->pcap_filter) { snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d", port, port + 1, 3799); conf->pcap_filter = buffer; } if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radsniff"); ret = 64; goto finish; } fr_strerror(); /* Clear out any non-fatal errors */ if (conf->radius_filter) { parsecode = userparse(NULL, conf->radius_filter, &filter_vps); if (parsecode == T_OP_INVALID) { ERROR("Invalid RADIUS filter \"%s\" (%s)", conf->radius_filter, fr_strerror()); ret = 64; goto finish; } if (!filter_vps) { ERROR("Empty RADIUS filter \"%s\"", conf->radius_filter); ret = 64; goto finish; } filter_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0); if (!filter_tree) { ERROR("Failed creating filter tree"); ret = 64; goto finish; } } /* * Setup the request tree */ request_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0); if (!request_tree) { ERROR("Failed creating request tree"); goto finish; } /* * Allocate a null packet for decrypting attributes in CoA requests */ nullpacket = rad_alloc(conf, 0); if (!nullpacket) { ERROR("Out of memory"); goto finish; } /* * Get the default capture device */ if (!conf->from_stdin && !conf->from_file && !conf->from_dev) { pcap_if_t *all_devices; /* List of all devices libpcap can listen on */ pcap_if_t *dev_p; if (pcap_findalldevs(&all_devices, errbuf) < 0) { ERROR("Error getting available capture devices: %s", errbuf); goto finish; } if (!all_devices) { ERROR("No capture files specified and no live interfaces available"); ret = 64; goto finish; } for (dev_p = all_devices; dev_p; dev_p = dev_p->next) { /* Don't use the any devices, it's horribly broken */ if (!strcmp(dev_p->name, "any")) continue; *in_head = fr_pcap_init(conf, dev_p->name, PCAP_INTERFACE_IN); in_head = &(*in_head)->next; } conf->from_auto = true; conf->from_dev = true; INFO("Defaulting to capture on all interfaces"); } /* * Print captures values which will be used */ if (fr_debug_flag > 2) { DEBUG1("Sniffing with options:"); if (conf->from_dev) { char *buff = fr_pcap_device_names(conf, in, ' '); DEBUG1(" Device(s) : [%s]", buff); talloc_free(buff); } if (conf->to_file || conf->to_stdout) { DEBUG1(" Writing to : [%s]", out->name); } if (limit > 0) { DEBUG1(" Capture limit (packets) : [%d]", limit); } DEBUG1(" PCAP filter : [%s]", conf->pcap_filter); DEBUG1(" RADIUS secret : [%s]", conf->radius_secret); if (filter_vps){ DEBUG1(" RADIUS filter :"); vp_printlist(log_dst, filter_vps); } } /* * Open our interface to collectd */ #ifdef HAVE_COLLECTDC_H if (conf->stats.out == RS_STATS_OUT_COLLECTD) { size_t i; rs_stats_tmpl_t *tmpl, **next; if (rs_stats_collectd_open(conf) < 0) { exit(1); } next = &conf->stats.tmpl; for (i = 0; i < (sizeof(rs_useful_codes) / sizeof(*rs_useful_codes)); i++) { tmpl = rs_stats_collectd_init_latency(conf, next, conf, "radius_pkt_ex", &stats.exchange[rs_useful_codes[i]], rs_useful_codes[i]); if (!tmpl) { goto tmpl_error; } next = &(tmpl->next); tmpl = rs_stats_collectd_init_counter(conf, next, conf, "radius_pkt", &stats.gauge.type[rs_useful_codes[i]], rs_useful_codes[i]); if (!tmpl) { tmpl_error: ERROR("Error allocating memory for stats template"); goto finish; } next = &(tmpl->next); } } #endif /* * This actually opens the capture interfaces/files (we just allocated the memory earlier) */ { fr_pcap_t *prev = NULL; for (in_p = in; in_p; in_p = in_p->next) { if (fr_pcap_open(in_p) < 0) { if (!conf->from_auto) { ERROR("Failed opening pcap handle for %s", in_p->name); goto finish; } DEBUG("Failed opening pcap handle: %s", fr_strerror()); /* Unlink it from the list */ if (prev) { prev->next = in_p->next; talloc_free(in_p); in_p = prev; } else { in = in_p->next; talloc_free(in_p); in_p = in; } goto next; } if (conf->pcap_filter) { if (fr_pcap_apply_filter(in_p, conf->pcap_filter) < 0) { ERROR("Failed applying filter"); goto finish; } } next: prev = in_p; } } /* * Open our output interface (if we have one); */ if (out) { if (fr_pcap_open(out) < 0) { ERROR("Failed opening pcap output"); goto finish; } } /* * Setup and enter the main event loop. Who needs libev when you can roll your own... */ { struct timeval now; fr_event_list_t *events; rs_update_t update; char *buff; memset(&stats, 0, sizeof(stats)); memset(&update, 0, sizeof(update)); events = fr_event_list_create(conf, _rs_event_status); if (!events) { ERROR(); goto finish; } for (in_p = in; in_p; in_p = in_p->next) { rs_event_t *event; event = talloc_zero(events, rs_event_t); event->conf = conf; event->in = in_p; event->out = out; event->stats = &stats; if (!fr_event_fd_insert(events, 0, in_p->fd, rs_got_packet, event)) { ERROR("Failed inserting file descriptor"); goto finish; } } buff = fr_pcap_device_names(conf, in, ' '); INFO("Sniffing on (%s)", buff); talloc_free(buff); gettimeofday(&now, NULL); start_pcap = now; /* * Insert our stats processor */ if (conf->stats.interval) { update.list = events; update.conf = conf; update.stats = &stats; update.in = in; now.tv_sec += conf->stats.interval; now.tv_usec = 0; fr_event_insert(events, rs_stats_process, (void *) &update, &now, NULL); } ret = fr_event_loop(events); /* Enter the main event loop */ } INFO("Done sniffing"); finish: if (filter_tree) { rbtree_free(filter_tree); } INFO("Exiting..."); /* * Free all the things! This also closes all the sockets and file descriptors */ talloc_free(conf); return ret; }
/** Execute a program. * * @param[in] request Current request. * @param[in] cmd Command to execute. This is parsed into argv[] parts, then each individual argv part * is xlat'ed. * @param[in] exec_wait set to 1 if you want to read from or write to child. * @param[in] shell_escape values before passing them as arguments. * @param[in] user_msg buffer to append plaintext (non valuepair) output. * @param[in] msg_len length of user_msg buffer. * @param[in] input_pairs list of value pairs - these will be available in the environment of the child. * @param[out] output_pairs list of value pairs - child stdout will be parsed and added into this list * of value pairs. * @return 0 if exec_wait==0, exit code if exec_wait!=0, -1 on error. */ int radius_exec_program(REQUEST *request, char const *cmd, bool exec_wait, bool shell_escape, char *user_msg, size_t msg_len, VALUE_PAIR *input_pairs, VALUE_PAIR **output_pairs) { pid_t pid; int from_child; #ifndef __MINGW32__ VALUE_PAIR *vp; char *p; pid_t child_pid; int comma = 0; int status; int n, done; char answer[4096]; #endif RDEBUG2("Executing: \"%s\"", cmd); if (user_msg) *user_msg = '\0'; pid = radius_start_program(cmd, request, exec_wait, NULL, &from_child, input_pairs, shell_escape); if (pid < 0) { return -1; } if (!exec_wait) { return 0; } #ifndef __MINGW32__ done = radius_readfrom_program(request, from_child, pid, 10, answer, sizeof(answer)); if (done < 0) { /* * failure - radius_readfrom_program will * have called close(from_child) for us */ DEBUG("Failed to read from child output"); return -1; } answer[done] = '\0'; /* * Make sure that the writer can't block while writing to * a pipe that no one is reading from anymore. */ close(from_child); /* * Parse the output, if any. */ if (done) { n = T_OP_INVALID; if (output_pairs) { /* * For backwards compatibility, first check * for plain text (user_msg). */ vp = NULL; n = userparse(request, answer, &vp); if (vp) { pairfree(&vp); } } if (n == T_OP_INVALID) { if (user_msg) { strlcpy(user_msg, answer, msg_len); } } else { /* * HACK: Replace '\n' with ',' so that * userparse() can parse the buffer in * one go (the proper way would be to * fix userparse(), but oh well). */ for (p = answer; *p; p++) { if (*p == '\n') { *p = comma ? ' ' : ','; p++; comma = 0; } if (*p == ',') comma++; } /* * Replace any trailing comma by a NUL. */ if (answer[strlen(answer) - 1] == ',') { answer[strlen(answer) - 1] = '\0'; } if (userparse(request, answer, &vp) == T_OP_INVALID) { REDEBUG("Unparsable reply from '%s'", cmd); return -1; } else { /* * Tell the caller about the value * pairs. */ *output_pairs = vp; } } /* else the answer was a set of VP's, not a text message */ } /* else we didn't read anything from the child */ /* * Call rad_waitpid (should map to waitpid on non-threaded * or single-server systems). */ child_pid = rad_waitpid(pid, &status); if (child_pid == 0) { REDEBUG("Timeout waiting for child"); return -2; } if (child_pid == pid) { if (WIFEXITED(status)) { status = WEXITSTATUS(status); RDEBUG("Program returned code (%d): %s", status, answer); return status; } } REDEBUG("Abnormal child exit: %s", strerror(errno)); #endif /* __MINGW32__ */ return -1; }
/* * Find the named user in this modules database. Create the set * of attribute-value pairs to check and reply with for this user * from the database. The authentication code only needs to check * the password, the rest is done here. */ static int caching_authorize(void *instance, REQUEST *request) { rlm_caching_t *data = (rlm_caching_t *) instance; char key[MAX_STRING_LEN]; datum key_datum; datum data_datum; rlm_caching_data cache_data; VALUE_PAIR *reply_item; VALUE_PAIR *item; char *tmp; int len = 0; int delete_cache = 0; float hit_ratio = 0.0; /* quiet the compiler */ instance = instance; request = request; if (pairfind(request->packet->vps, PW_CACHE_NO_CACHING, 0) != NULL){ DEBUG("rlm_caching: Cache-No-Caching is set. Returning NOOP"); return RLM_MODULE_NOOP; } if (pairfind(request->packet->vps, PW_CACHE_DELETE_CACHE, 0) != NULL){ DEBUG("rlm_caching: Found Cache-Delete-Cache. Will delete record if found"); delete_cache = 1; } if (!radius_xlat(key,sizeof(key), data->key, request, NULL)){ radlog(L_ERR, "rlm_caching: xlat on key '%s' failed.",data->key); return RLM_MODULE_FAIL; } key_datum.dptr = key; key_datum.dsize = strlen(key); DEBUG("rlm_caching: Searching the database for key '%s'",key); pthread_mutex_lock(&data->mutex); data_datum = gdbm_fetch(data->gdbm, key_datum); pthread_mutex_unlock(&data->mutex); data->cache_queries++; if (data_datum.dptr != NULL){ DEBUG("rlm_caching: Key Found."); data->cache_hits++; hit_ratio = (float)data->cache_hits / data->cache_queries; hit_ratio *= 100.0; memcpy(&cache_data, data_datum.dptr, sizeof(rlm_caching_data)); free(data_datum.dptr); if (delete_cache == 0 && cache_data.creation + data->cache_ttl <= time(NULL)){ DEBUG("rlm_caching: Cache entry has expired"); DEBUG("rlm_caching: Cache Queries: %7d, Cache Hits: %7d, Hit Ratio: %.2f%%", data->cache_queries,data->cache_hits,hit_ratio); show_hit_ratio; delete_cache = 1; } if (delete_cache){ DEBUG("rlm_caching: Deleting record"); pthread_mutex_lock(&data->mutex); gdbm_delete(data->gdbm, key_datum); pthread_mutex_unlock(&data->mutex); return RLM_MODULE_NOOP; } tmp = cache_data.data; if (tmp){ pairfree(&request->reply->vps); while(tmp && len < cache_data.len){ reply_item = NULL; if (userparse(tmp, &reply_item) > 0 && reply_item != NULL) pairadd(&request->reply->vps, reply_item); len += (strlen(tmp) + 1); DEBUG("rlm_caching: VP='%s',VALUE='%s',lenth='%d',cache record length='%d'", reply_item->name,reply_item->vp_strvalue,reply_item->length,len); tmp = cache_data.data + len; } } else{ DEBUG("rlm_caching: No reply items found. Returning NOOP"); return RLM_MODULE_NOOP; } if (cache_data.auth_type){ DEBUG("rlm_caching: Adding Auth-Type '%s'",cache_data.auth_type); if ((item = pairfind(request->config_items, PW_AUTH_TYPE, 0)) == NULL){ item = pairmake("Auth-Type", cache_data.auth_type, T_OP_SET); pairadd(&request->config_items, item); } else{ strcmp(item->vp_strvalue, cache_data.auth_type); item->length = strlen(cache_data.auth_type); } } if (data->post_auth){ DEBUG("rlm_caching: Adding Post-Auth-Type '%s'",data->post_auth); if ((item = pairfind(request->config_items, PW_POST_AUTH_TYPE, 0)) == NULL){ item = pairmake("Post-Auth-Type", data->post_auth, T_OP_SET); pairadd(&request->config_items, item); } else{ strcmp(item->vp_strvalue, data->post_auth); item->length = strlen(data->post_auth); } } item = pairmake("Cache-No-Caching", "YES", T_OP_EQ); pairadd(&request->packet->vps, item); DEBUG("rlm_caching: Cache Queries: %7d, Cache Hits: %7d, Hit Ratio: %.2f%%", data->cache_queries,data->cache_hits,hit_ratio); show_hit_ratio; return RLM_MODULE_OK; } else{ DEBUG("rlm_caching: Could not find the requested key in the database."); DEBUG("rlm_caching: Cache Queries: %7d, Cache Hits: %7d, Hit Ratio: %.2f%%", data->cache_queries,data->cache_hits,hit_ratio); show_hit_ratio; } return RLM_MODULE_NOOP; }
/** Execute a program. * * @param[in] request Current request (may be NULL). * @param[in] cmd Command to execute. This is parsed into argv[] parts, then each individual argv part * is xlat'ed. * @param[in] exec_wait set to 1 if you want to read from or write to child. * @param[in] shell_escape values before passing them as arguments. * @param[in] user_msg buffer to append plaintext (non valuepair) output. * @param[in] msg_len length of user_msg buffer. * @param[in] timeout amount of time to wait, in seconds. * @param[in] input_pairs list of value pairs - these will be available in the environment of the child. * @param[out] output_pairs list of value pairs - child stdout will be parsed and added into this list * of value pairs. * @return 0 if exec_wait==0, exit code if exec_wait!=0, -1 on error. */ int radius_exec_program(REQUEST *request, char const *cmd, bool exec_wait, bool shell_escape, char *user_msg, size_t msg_len, int timeout, VALUE_PAIR *input_pairs, VALUE_PAIR **output_pairs) { pid_t pid; int from_child; #ifndef __MINGW32__ char *p; pid_t child_pid; int comma = 0; int status, ret = 0; ssize_t len; char answer[4096]; #endif DEBUG2("Executing: %s:", cmd); if (user_msg) *user_msg = '\0'; pid = radius_start_program(cmd, request, exec_wait, NULL, &from_child, input_pairs, shell_escape); if (pid < 0) { return -1; } if (!exec_wait) { return 0; } #ifndef __MINGW32__ len = radius_readfrom_program(request, from_child, pid, timeout, answer, sizeof(answer)); if (len < 0) { /* * Failure - radius_readfrom_program will * have called close(from_child) for us */ DEBUG("Failed to read from child output"); return -1; } answer[len] = '\0'; /* * Make sure that the writer can't block while writing to * a pipe that no one is reading from anymore. */ close(from_child); if (len == 0) { goto wait; } /* * Parse the output, if any. */ if (output_pairs) { /* * HACK: Replace '\n' with ',' so that * userparse() can parse the buffer in * one go (the proper way would be to * fix userparse(), but oh well). */ for (p = answer; *p; p++) { if (*p == '\n') { *p = comma ? ' ' : ','; p++; comma = 0; } if (*p == ',') { comma++; } } /* * Replace any trailing comma by a NUL. */ if (answer[len - 1] == ',') { answer[--len] = '\0'; } if (userparse(request, answer, output_pairs) == T_OP_INVALID) { ERROR("Failed parsing output from: %s: %s", cmd, fr_strerror()); strlcpy(user_msg, answer, len); ret = -1; } /* * We've not been told to extract output pairs, * just copy the programs output to the user_msg * buffer. */ } else if (user_msg) { strlcpy(user_msg, answer, msg_len); } /* * Call rad_waitpid (should map to waitpid on non-threaded * or single-server systems). */ wait: child_pid = rad_waitpid(pid, &status); if (child_pid == 0) { ERROR("Timeout waiting for child"); return -2; } if (child_pid == pid) { if (WIFEXITED(status)) { status = WEXITSTATUS(status); if ((status != 0) || (ret < 0)) { ERROR("Program returned code (%d) and output '%s'", status, answer); } else { ERROR("Program returned code (%d) and output '%s'", status, answer); } return ret < 0 ? ret : status; } } ERROR("Abnormal child exit: %s", fr_syserror(errno)); #endif /* __MINGW32__ */ return -1; }
int main(int argc, char *argv[]) { char *dev; /* sniffing device */ char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */ pcap_t *descr; /* sniff handler */ struct bpf_program fp; /* hold compiled program */ bpf_u_int32 maskp; /* subnet mask */ bpf_u_int32 netp; /* ip */ char buffer[1024]; char *pcap_filter = NULL; char *radius_filter = NULL; int packet_count = -1; /* how many packets to sniff */ int opt; FR_TOKEN parsecode; const char *radius_dir = RADIUS_DIR; int port = 1812; /* Default device */ dev = pcap_lookupdev(errbuf); /* Get options */ while ((opt = getopt(argc, argv, "c:d:f:hi:p:r:s:X")) != EOF) { switch (opt) { case 'c': packet_count = atoi(optarg); if (packet_count <= 0) { fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg); exit(1); } break; case 'd': radius_dir = optarg; break; case 'f': pcap_filter = optarg; break; case 'h': usage(0); break; case 'i': dev = optarg; break; case 'p': port = atoi(optarg); break; case 'r': radius_filter = optarg; break; case 's': radius_secret = optarg; break; case 'X': debug_flag = 1; break; default: usage(1); } } if (!pcap_filter) { pcap_filter = buffer; snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d", port, port + 1, port + 2); } if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radsniff"); return 1; } if (radius_filter) { parsecode = userparse(radius_filter, &filter_vps); if (parsecode == T_OP_INVALID) { fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\": %s\n", radius_filter, fr_strerror()); exit(1); } if (!filter_vps) { fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter); exit(1); } } /* Set our device */ pcap_lookupnet(dev, &netp, &maskp, errbuf); /* Print device to the user */ printf("Device: [%s]\n", dev); if (packet_count > 0) { printf("Num of packets: [%d]\n", packet_count); } printf("PCAP filter: [%s]\n", pcap_filter); if (filter_vps != NULL) { printf("RADIUS filter:\n"); vp_printlist(stdout, filter_vps); } printf("RADIUS secret: [%s]\n", radius_secret); /* Open the device so we can spy */ descr = pcap_open_live(dev, SNAPLEN, 1, 0, errbuf); if (descr == NULL) { printf("radsniff: pcap_open_live failed (%s)\n", errbuf); exit(1); } /* Apply the rules */ if( pcap_compile(descr, &fp, pcap_filter, 0, netp) == -1) { printf("radsniff: pcap_compile failed\n"); exit(1); } if (pcap_setfilter(descr, &fp) == -1) { printf("radsniff: pcap_setfilter failed\n"); exit(1); } /* Now we can set our callback function */ pcap_loop(descr, packet_count, got_packet, NULL); pcap_close(descr); printf("Done sniffing\n"); fflush(stdout); return 0; }
/* * Execute a program on successful authentication. * Return 0 if exec_wait == 0. * Return the exit code of the called program if exec_wait != 0. * Return -1 on fork/other errors in the parent process. */ int radius_exec_program(const char *cmd, REQUEST *request, int exec_wait, char *user_msg, int msg_len, VALUE_PAIR *input_pairs, VALUE_PAIR **output_pairs, int shell_escape) { VALUE_PAIR *vp; char mycmd[1024]; const char *from; char *p, *to; int pd[2]; pid_t pid, child_pid; int argc = -1; int comma = 0; int status; int i; int n, left, done; char *argv[MAX_ARGV]; char answer[4096]; char argv_buf[4096]; #define MAX_ENVP 1024 char *envp[MAX_ENVP]; struct timeval start; #ifdef O_NONBLOCK int nonblock = TRUE; #endif if (user_msg) *user_msg = '\0'; if (output_pairs) *output_pairs = NULL; if (strlen(cmd) > (sizeof(mycmd) - 1)) { radlog(L_ERR|L_CONS, "Command line is too long"); return -1; } /* * Check for bad escapes. */ if (cmd[strlen(cmd) - 1] == '\\') { radlog(L_ERR|L_CONS, "Command line has final backslash, without a following character"); return -1; } strlcpy(mycmd, cmd, sizeof(mycmd)); /* * Split the string into argv's BEFORE doing radius_xlat... */ from = cmd; to = mycmd; argc = 0; while (*from) { int length; /* * Skip spaces. */ if ((*from == ' ') || (*from == '\t')) { from++; continue; } argv[argc] = to; argc++; if (argc >= (MAX_ARGV - 1)) break; /* * Copy the argv over to our buffer. */ while (*from && (*from != ' ') && (*from != '\t')) { if (to >= mycmd + sizeof(mycmd) - 1) { return -1; /* ran out of space */ } switch (*from) { case '"': case '\'': length = rad_copy_string(to, from); if (length < 0) { radlog(L_ERR|L_CONS, "Invalid string passed as argument for external program"); return -1; } from += length; to += length; break; case '%': if (from[1] == '{') { *(to++) = *(from++); length = rad_copy_variable(to, from); if (length < 0) { radlog(L_ERR|L_CONS, "Invalid variable expansion passed as argument for external program"); return -1; } from += length; to += length; } else { /* FIXME: catch %%{ ? */ *(to++) = *(from++); } break; case '\\': if (from[1] == ' ') from++; /* FALL-THROUGH */ default: *(to++) = *(from++); } } /* end of string, or found a space */ *(to++) = '\0'; /* terminate the string */ } /* * We have to have SOMETHING, at least. */ if (argc <= 0) { radlog(L_ERR, "Exec-Program: empty command line."); return -1; } /* * Expand each string, as appropriate. */ to = argv_buf; left = sizeof(argv_buf); for (i = 0; i < argc; i++) { int sublen; /* * Don't touch argv's which won't be translated. */ if (strchr(argv[i], '%') == NULL) continue; if (!request) continue; sublen = radius_xlat(to, left - 1, argv[i], request, NULL); if (sublen <= 0) { /* * Fail to be backwards compatible. * * It's yucky, but it won't break anything, * and it won't cause security problems. */ sublen = 0; } argv[i] = to; to += sublen; *(to++) = '\0'; left -= sublen; left--; if (left <= 0) { radlog(L_ERR, "Exec-Program: Ran out of space while expanding arguments."); return -1; } } argv[argc] = NULL; #ifndef __MINGW32__ /* * Open a pipe for child/parent communication, if necessary. */ if (exec_wait) { if (pipe(pd) != 0) { radlog(L_ERR|L_CONS, "Couldn't open pipe: %s", strerror(errno)); return -1; } } else { /* * We're not waiting, so we don't look for a * message, or VP's. */ user_msg = NULL; output_pairs = NULL; } envp[0] = NULL; if (input_pairs) { int envlen; char buffer[1024]; /* * Set up the environment variables in the * parent, so we don't call libc functions that * hold mutexes. They might be locked when we fork, * and will remain locked in the child. */ envlen = 0; for (vp = input_pairs; vp != NULL; vp = vp->next) { /* * Hmm... maybe we shouldn't pass the * user's password in an environment * variable... */ snprintf(buffer, sizeof(buffer), "%s=", vp->name); if (shell_escape) { for (p = buffer; *p != '='; p++) { if (*p == '-') { *p = '_'; } else if (isalpha((int) *p)) { *p = toupper(*p); } } } n = strlen(buffer); vp_prints_value(buffer+n, sizeof(buffer) - n, vp, shell_escape); envp[envlen++] = strdup(buffer); /* * Don't add too many attributes. */ if (envlen == (MAX_ENVP - 1)) break; } envp[envlen] = NULL; } if (exec_wait) { pid = rad_fork(); /* remember PID */ } else { pid = fork(); /* don't wait */ } if (pid == 0) { int devnull; /* * Child process. * * We try to be fail-safe here. So if ANYTHING * goes wrong, we exit with status 1. */ /* * Open STDIN to /dev/null */ devnull = open("/dev/null", O_RDWR); if (devnull < 0) { radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n", strerror(errno)); exit(1); } dup2(devnull, STDIN_FILENO); /* * Only massage the pipe handles if the parent * has created them. */ if (exec_wait) { /* * pd[0] is the FD the child will read from, * which we don't want. */ if (close(pd[0]) != 0) { radlog(L_ERR|L_CONS, "Can't close pipe: %s", strerror(errno)); exit(1); } /* * pd[1] is the FD that the child will write to, * so we make it STDOUT. */ if (dup2(pd[1], STDOUT_FILENO) != 1) { radlog(L_ERR|L_CONS, "Can't dup stdout: %s", strerror(errno)); exit(1); } } else { /* no pipe, STDOUT should be /dev/null */ dup2(devnull, STDOUT_FILENO); } /* * If we're not debugging, then we can't do * anything with the error messages, so we throw * them away. * * If we are debugging, then we want the error * messages to go to the STDERR of the server. */ if (debug_flag == 0) { dup2(devnull, STDERR_FILENO); } close(devnull); /* * The server may have MANY FD's open. We don't * want to leave dangling FD's for the child process * to play funky games with, so we close them. */ closefrom(3); execve(argv[0], argv, envp); radlog(L_ERR, "Exec-Program: FAILED to execute %s: %s", argv[0], strerror(errno)); exit(1); } /* * Free child environment variables */ for (i = 0; envp[i] != NULL; i++) { free(envp[i]); } /* * Parent process. */ if (pid < 0) { radlog(L_ERR|L_CONS, "Couldn't fork %s: %s", argv[0], strerror(errno)); if (exec_wait) { close(pd[0]); close(pd[1]); } return -1; } /* * We're not waiting, exit, and ignore any child's status. */ if (!exec_wait) { return 0; } /* * Close the FD to which the child writes it's data. * * If we can't close it, then we close pd[0], and return an * error. */ if (close(pd[1]) != 0) { radlog(L_ERR|L_CONS, "Can't close pipe: %s", strerror(errno)); close(pd[0]); return -1; } #ifdef O_NONBLOCK /* * Try to set it non-blocking. */ do { int flags; if ((flags = fcntl(pd[0], F_GETFL, NULL)) < 0) { nonblock = FALSE; break; } flags |= O_NONBLOCK; if( fcntl(pd[0], F_SETFL, flags) < 0) { nonblock = FALSE; break; } } while (0); #endif /* * Read from the pipe until we doesn't get any more or * until the message is full. */ done = 0; left = sizeof(answer) - 1; gettimeofday(&start, NULL); while (1) { int rcode; fd_set fds; struct timeval when, elapsed, wake; FD_ZERO(&fds); FD_SET(pd[0], &fds); gettimeofday(&when, NULL); tv_sub(&when, &start, &elapsed); if (elapsed.tv_sec >= 10) goto too_long; when.tv_sec = 10; when.tv_usec = 0; tv_sub(&when, &elapsed, &wake); rcode = select(pd[0] + 1, &fds, NULL, NULL, &wake); if (rcode == 0) { too_long: radlog(L_ERR, "Child PID %u (%s) is taking too much time: forcing failure and killing child.", pid, argv[0]); kill(pid, SIGTERM); close(pd[0]); /* should give SIGPIPE to child, too */ /* * Clean up the child entry. */ rad_waitpid(pid, &status); return 1; } if (rcode < 0) { if (errno == EINTR) continue; break; } #ifdef O_NONBLOCK /* * Read as many bytes as possible. The kernel * will return the number of bytes available. */ if (nonblock) { status = read(pd[0], answer + done, left); } else #endif /* * There's at least 1 byte ready: read it. */ status = read(pd[0], answer + done, 1); /* * Nothing more to read: stop. */ if (status == 0) { break; } /* * Error: See if we have to continue. */ if (status < 0) { /* * We were interrupted: continue reading. */ if (errno == EINTR) { continue; } /* * There was another error. Most likely * The child process has finished, and * exited. */ break; } done += status; left -= status; if (left <= 0) break; } answer[done] = 0; /* * Make sure that the writer can't block while writing to * a pipe that no one is reading from anymore. */ close(pd[0]); DEBUG2("Exec-Program output: %s", answer); /* * Parse the output, if any. */ if (done) { n = T_OP_INVALID; if (output_pairs) { /* * For backwards compatibility, first check * for plain text (user_msg). */ vp = NULL; n = userparse(answer, &vp); if (vp) { pairfree(&vp); } } if (n == T_OP_INVALID) { DEBUG("Exec-Program-Wait: plaintext: %s", answer); if (user_msg) { strlcpy(user_msg, answer, msg_len); } } else { /* * HACK: Replace '\n' with ',' so that * userparse() can parse the buffer in * one go (the proper way would be to * fix userparse(), but oh well). */ for (p = answer; *p; p++) { if (*p == '\n') { *p = comma ? ' ' : ','; p++; comma = 0; } if (*p == ',') comma++; } /* * Replace any trailing comma by a NUL. */ if (answer[strlen(answer) - 1] == ',') { answer[strlen(answer) - 1] = '\0'; } radlog(L_DBG,"Exec-Program-Wait: value-pairs: %s", answer); if (userparse(answer, &vp) == T_OP_INVALID) { radlog(L_ERR, "Exec-Program-Wait: %s: unparsable reply", cmd); } else { /* * Tell the caller about the value * pairs. */ *output_pairs = vp; } } /* else the answer was a set of VP's, not a text message */ } /* else we didn't read anything from the child */ /* * Call rad_waitpid (should map to waitpid on non-threaded * or single-server systems). */ child_pid = rad_waitpid(pid, &status); if (child_pid == 0) { radlog(L_DBG, "Exec-Program: Timeout waiting for child"); return 2; } if (child_pid == pid) { if (WIFEXITED(status)) { status = WEXITSTATUS(status); radlog(L_DBG, "Exec-Program: returned: %d", status); return status; } } radlog(L_ERR|L_CONS, "Exec-Program: Abnormal child exit: %s", strerror(errno)); return 1; #else msg_len = msg_len; /* -Wunused */ if (exec_wait) { radlog(L_ERR, "Exec-Program-Wait is not supported"); return -1; } /* * We're not waiting, so we don't look for a * message, or VP's. */ user_msg = NULL; output_pairs = NULL; { /* * The _spawn and _exec families of functions are * found in Windows compiler libraries for * portability from UNIX. There is a variety of * functions, including the ability to pass * either a list or array of parameters, to * search in the PATH or otherwise, and whether * or not to pass an environment (a set of * environment variables). Using _spawn, you can * also specify whether you want the new process * to close your program (_P_OVERLAY), to wait * until the new process is finished (_P_WAIT) or * for the two to run concurrently (_P_NOWAIT). * _spawn and _exec are useful for instances in * which you have simple requirements for running * the program, don't want the overhead of the * Windows header file, or are interested * primarily in portability. */ /* * FIXME: check return code... what is it? */ _spawnve(_P_NOWAIT, argv[0], argv, envp); } return 0; #endif }