/* * Xlat for %{attr_by_oid:<oid>} */ static ssize_t xlat_dict_attr_by_oid(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen, UNUSED void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt) { unsigned int attr = 0; fr_dict_attr_t const *parent = fr_dict_root(request->dict); fr_dict_attr_t const *da; ssize_t ret; ret = fr_dict_attr_by_oid(NULL, &parent, &attr, fmt); if (ret <= 0) { REMARKER(fmt, -(ret), fr_strerror()); return ret; } da = fr_dict_attr_child_by_num(parent, attr); *out = talloc_typed_strdup(ctx, da->name); return talloc_array_length(*out) - 1; }
/* * Do any per-module initialization that is separate to each * configured instance of the module. e.g. set up connections * to external databases, read configuration files, set up * dictionary entries, etc. * * If configuration information is given in the config section * that must be referenced in later calls, store a handle to it * in *instance otherwise put a null pointer there. */ static int mod_bootstrap(void *instance, CONF_SECTION *conf) { rlm_csv_t *inst = instance; int i; char const *p; char *q; char *header; FILE *fp; int lineno; char buffer[8192]; inst->name = cf_section_name2(conf); if (!inst->name) inst->name = cf_section_name1(conf); if (inst->delimiter[1]) { cf_log_err(conf, "'delimiter' must be one character long"); return -1; } for (p = inst->header; p != NULL; p = strchr(p + 1, *inst->delimiter)) { inst->num_fields++; } if (inst->num_fields < 2) { cf_log_err(conf, "Must have at least a key field and data field"); return -1; } inst->field_names = talloc_array(inst, const char *, inst->num_fields); if (!inst->field_names) { oom: cf_log_err(conf, "Out of memory"); return -1; } inst->field_offsets = talloc_array(inst, int, inst->num_fields); if (!inst->field_offsets) goto oom; for (i = 0; i < inst->num_fields; i++) { inst->field_offsets[i] = -1; /* unused */ } /* * Get a writable copy of the header */ header = talloc_typed_strdup(inst, inst->header); if (!header) goto oom; /* * Mark up the field names. Note that they can be empty, * in which case they don't map to anything. */ inst->key_field = -1; /* * FIXME: remove whitespace from field names, if we care. */ for (p = header, i = 0; p != NULL; p = q, i++) { q = strchr(p, *inst->delimiter); /* * Fields 0..N-1 */ if (q) { *q = '\0'; if (q > (p + 1)) { if (strcmp(p, inst->key) == 0) { inst->key_field = i; } else { inst->field_offsets[i] = inst->used_fields++; } } q++; } else { /* field N */ if (*p) { if (strcmp(p, inst->key) == 0) { inst->key_field = i; } else { inst->field_offsets[i] = inst->used_fields++; } } } /* * Save the field names, even when they're not used. */ inst->field_names[i] = p; } if (inst->key_field < 0) { cf_log_err(conf, "Key field '%s' does not appear in header", inst->key); return -1; } inst->tree = rbtree_talloc_create(inst, csv_entry_cmp, rlm_csv_entry_t, NULL, 0); if (!inst->tree) goto oom; /* * Read the file line by line. */ fp = fopen(inst->filename, "r"); if (!fp) { cf_log_err(conf, "Error opening filename %s: %s", inst->filename, fr_syserror(errno)); return -1; } lineno = 1; while (fgets(buffer, sizeof(buffer), fp)) { rlm_csv_entry_t *e; e = file2csv(conf, inst, lineno, buffer); if (!e) { fclose(fp); return -1; } lineno++; } fclose(fp); /* * And register the map function. */ map_proc_register(inst, inst->name, mod_map_proc, csv_map_verify, 0); return 0; }
/* * The main guy. */ int main(int argc, char *argv[]) { int rcode = EXIT_SUCCESS; int argval; const char *input_file = NULL; const char *output_file = NULL; const char *filter_file = NULL; FILE *fp; REQUEST *request = 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; }
/* * 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; }
int main(int argc, char **argv) { RADIUS_PACKET *req; char *p; int c; uint16_t port = 0; char *filename = NULL; FILE *fp; int id; int force_af = AF_UNSPEC; static fr_log_t radclient_log = { .colourise = true, .fd = STDOUT_FILENO, .dst = L_DST_STDOUT, .file = NULL, .debug_file = NULL, }; radlog_init(&radclient_log, false); /* * 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"); id = ((int)getpid() & 0xff); fr_debug_flag = 0; set_radius_dir(autofree, RADIUS_DIR); while ((c = getopt(argc, argv, "46c:d:D:f:hi:qst:r:S:xXv")) != EOF) { switch(c) { case '4': force_af = AF_INET; break; case '6': force_af = AF_INET6; break; case 'd': set_radius_dir(autofree, optarg); break; case 'D': main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg); break; case 'f': filename = optarg; break; case 'q': do_output = 0; break; case 'x': debug_flag++; fr_debug_flag++; break; case 'X': #if 0 sha1_data_problems = 1; /* for debugging only */ #endif break; case 'r': if (!isdigit((int) *optarg)) usage(); retries = atoi(optarg); break; case 'i': if (!isdigit((int) *optarg)) usage(); id = atoi(optarg); if ((id < 0) || (id > 255)) { usage(); } break; case 's': do_summary = 1; break; case 't': if (!isdigit((int) *optarg)) usage(); timeout = atof(optarg); break; case 'v': printf("radeapclient: $Id: 69643e042c5a7c9053977756a5623e3c07598f2e $ built on " __DATE__ " at " __TIME__ "\n"); exit(0); break; case 'S': fp = fopen(optarg, "r"); if (!fp) { fprintf(stderr, "radclient: Error opening %s: %s\n", optarg, fr_syserror(errno)); exit(1); } if (fgets(filesecret, sizeof(filesecret), fp) == NULL) { fprintf(stderr, "radclient: Error reading %s: %s\n", 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) { fprintf(stderr, "radclient: Secret in %s is too short\n", optarg); exit(1); } secret = filesecret; break; case 'h': default: usage(); break; } } argc -= (optind - 1); argv += (optind - 1); if ((argc < 3) || ((!secret) && (argc < 4))) { usage(); } if (!main_config.dictionary_dir) { main_config.dictionary_dir = DICTDIR; } /* * Read the distribution dictionaries first, then * the ones in raddb. */ DEBUG2("including dictionary file %s/%s", main_config.dictionary_dir, RADIUS_DICTIONARY); if (dict_init(main_config.dictionary_dir, RADIUS_DICTIONARY) != 0) { ERROR("Errors reading dictionary: %s", fr_strerror()); exit(1); } /* * It's OK if this one doesn't exist. */ int rcode = dict_read(radius_dir, RADIUS_DICTIONARY); if (rcode == -1) { ERROR("Errors reading %s/%s: %s", radius_dir, RADIUS_DICTIONARY, fr_strerror()); exit(1); } /* * We print this after reading it. That way if * it doesn't exist, it's OK, and we don't print * anything. */ if (rcode == 0) { DEBUG2("including dictionary file %s/%s", radius_dir, RADIUS_DICTIONARY); } req = rad_alloc(NULL, 1); if (!req) { fr_perror("radclient"); exit(1); } #if 0 { FILE *randinit; if((randinit = fopen("/dev/urandom", "r")) == NULL) { perror("/dev/urandom"); } else { fread(randctx.randrsl, 256, 1, randinit); fclose(randinit); } } fr_randinit(&randctx, 1); #endif req->id = id; /* * Resolve hostname. */ if (force_af == AF_UNSPEC) force_af = AF_INET; req->dst_ipaddr.af = force_af; if (strcmp(argv[1], "-") != 0) { char const *hostname = argv[1]; char const *portname = argv[1]; char buffer[256]; if (*argv[1] == '[') { /* IPv6 URL encoded */ p = strchr(argv[1], ']'); if ((size_t) (p - argv[1]) >= sizeof(buffer)) { usage(); } memcpy(buffer, argv[1] + 1, p - argv[1] - 1); buffer[p - argv[1] - 1] = '\0'; hostname = buffer; portname = p + 1; } p = strchr(portname, ':'); if (p && (strchr(p + 1, ':') == NULL)) { *p = '\0'; portname = p + 1; } else { portname = NULL; } if (ip_hton(&req->dst_ipaddr, force_af, hostname, false) < 0) { fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", hostname, fr_syserror(errno)); exit(1); } /* * Strip port from hostname if needed. */ if (portname) port = atoi(portname); } /* * See what kind of request we want to send. */ if (strcmp(argv[2], "auth") == 0) { if (port == 0) port = getport("radius"); if (port == 0) port = PW_AUTH_UDP_PORT; req->code = PW_CODE_AUTHENTICATION_REQUEST; } else if (strcmp(argv[2], "acct") == 0) { if (port == 0) port = getport("radacct"); if (port == 0) port = PW_ACCT_UDP_PORT; req->code = PW_CODE_ACCOUNTING_REQUEST; do_summary = 0; } else if (strcmp(argv[2], "status") == 0) { if (port == 0) port = getport("radius"); if (port == 0) port = PW_AUTH_UDP_PORT; req->code = PW_CODE_STATUS_SERVER; } else if (strcmp(argv[2], "disconnect") == 0) { if (port == 0) port = PW_POD_UDP_PORT; req->code = PW_CODE_DISCONNECT_REQUEST; } else if (isdigit((int) argv[2][0])) { if (port == 0) port = getport("radius"); if (port == 0) port = PW_AUTH_UDP_PORT; req->code = atoi(argv[2]); } else { usage(); } req->dst_port = port; /* * Add the secret. */ if (argv[3]) secret = argv[3]; /* * Read valuepairs. * Maybe read them, from stdin, if there's no * filename, or if the filename is '-'. */ if (filename && (strcmp(filename, "-") != 0)) { fp = fopen(filename, "r"); if (!fp) { fprintf(stderr, "radclient: Error opening %s: %s\n", filename, fr_syserror(errno)); exit(1); } } else { fp = stdin; } /* * Send request. */ if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("radclient: socket: "); exit(1); } while(!filedone) { if (req->vps) pairfree(&req->vps); if (readvp2(&req->vps, NULL, fp, &filedone) < 0) { fr_perror("radeapclient"); break; } sendrecv_eap(req); } if(do_summary) { printf("\n\t Total approved auths: %d\n", totalapp); printf("\t Total denied auths: %d\n", totaldeny); } talloc_free(autofree); return 0; } /* * given a radius request with some attributes in the EAP range, build * them all into a single EAP-Message body. * * Note that this function will build multiple EAP-Message bodies * if there are multiple eligible EAP-types. This is incorrect, as the * recipient will in fact concatenate them. * * XXX - we could break the loop once we process one type. Maybe this * just deserves an assert? * */ static void map_eap_methods(RADIUS_PACKET *req) { VALUE_PAIR *vp, *vpnext; int id, eapcode; int eap_method; eap_packet_t *pt_ep = talloc_zero(req, eap_packet_t); vp = pairfind(req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY); if(!vp) { id = ((int)getpid() & 0xff); } else { id = vp->vp_integer; } vp = pairfind(req->vps, ATTRIBUTE_EAP_CODE, 0, TAG_ANY); if(!vp) { eapcode = PW_EAP_REQUEST; } else { eapcode = vp->vp_integer; } for(vp = req->vps; vp != NULL; vp = vpnext) { /* save it in case it changes! */ vpnext = vp->next; if(vp->da->attr >= ATTRIBUTE_EAP_BASE && vp->da->attr < ATTRIBUTE_EAP_BASE+256) { break; } } if(!vp) { return; } eap_method = vp->da->attr - ATTRIBUTE_EAP_BASE; switch(eap_method) { case PW_EAP_IDENTITY: case PW_EAP_NOTIFICATION: case PW_EAP_NAK: case PW_EAP_MD5: case PW_EAP_OTP: case PW_EAP_GTC: case PW_EAP_TLS: case PW_EAP_LEAP: case PW_EAP_TTLS: case PW_EAP_PEAP: default: /* * no known special handling, it is just encoded as an * EAP-message with the given type. */ /* nuke any existing EAP-Messages */ pairdelete(&req->vps, PW_EAP_MESSAGE, 0, TAG_ANY); pt_ep->code = eapcode; pt_ep->id = id; pt_ep->type.num = eap_method; pt_ep->type.length = vp->length; pt_ep->type.data = talloc_memdup(vp, vp->vp_octets, vp->length); talloc_set_type(pt_ep->type.data, uint8_t); eap_basic_compose(req, pt_ep); } }
/** Allocate a new client from a config section * * @param ctx to allocate new clients in. * @param cs to process as a client. * @param in_server Whether the client should belong to a specific virtual server. * @param with_coa If true and coa_server or coa_pool aren't specified automatically, * create a coa home_server section and add it to the client CONF_SECTION. * @return new RADCLIENT struct. */ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bool with_coa) { RADCLIENT *c; char const *name2; name2 = cf_section_name2(cs); if (!name2) { cf_log_err_cs(cs, "Missing client name"); return NULL; } /* * The size is fine.. Let's create the buffer */ c = talloc_zero(ctx, RADCLIENT); c->cs = cs; memset(&cl_ipaddr, 0, sizeof(cl_ipaddr)); if (cf_section_parse(cs, c, client_config) < 0) { cf_log_err_cs(cs, "Error parsing client section"); error: client_free(c); #ifdef WITH_TCP hs_proto = NULL; cl_srcipaddr = NULL; #endif return NULL; } /* * Global clients can set servers to use, per-server clients cannot. */ if (in_server && c->server) { cf_log_err_cs(cs, "Clients inside of an server section cannot point to a server"); goto error; } /* * Newer style client definitions with either ipaddr or ipaddr6 * config items. */ if (cf_pair_find(cs, "ipaddr") || cf_pair_find(cs, "ipv4addr") || cf_pair_find(cs, "ipv6addr")) { char buffer[128]; /* * Sets ipv4/ipv6 address and prefix. */ c->ipaddr = cl_ipaddr; /* * Set the long name to be the result of a reverse lookup on the IP address. */ ip_ntoh(&c->ipaddr, buffer, sizeof(buffer)); c->longname = talloc_typed_strdup(c, buffer); /* * Set the short name to the name2. */ if (!c->shortname) c->shortname = talloc_typed_strdup(c, name2); /* * No "ipaddr" or "ipv6addr", use old-style "client <ipaddr> {" syntax. */ } else { cf_log_err_cs(cs, "No 'ipaddr' or 'ipv4addr' or 'ipv6addr' configuration " "directive found in client %s", name2); goto error; } c->proto = IPPROTO_UDP; if (hs_proto) { if (strcmp(hs_proto, "udp") == 0) { hs_proto = NULL; #ifdef WITH_TCP } else if (strcmp(hs_proto, "tcp") == 0) { hs_proto = NULL; c->proto = IPPROTO_TCP; # ifdef WITH_TLS } else if (strcmp(hs_proto, "tls") == 0) { hs_proto = NULL; c->proto = IPPROTO_TCP; c->tls_required = true; } else if (strcmp(hs_proto, "radsec") == 0) { hs_proto = NULL; c->proto = IPPROTO_TCP; c->tls_required = true; # endif } else if (strcmp(hs_proto, "*") == 0) { hs_proto = NULL; c->proto = IPPROTO_IP; /* fake for dual */ #endif } else { cf_log_err_cs(cs, "Unknown proto \"%s\".", hs_proto); goto error; } } /* * If a src_ipaddr is specified, when we send the return packet * we will use this address instead of the src from the * request. */ if (cl_srcipaddr) { #ifdef WITH_UDPFROMTO switch (c->ipaddr.af) { case AF_INET: if (fr_pton4(&c->src_ipaddr, cl_srcipaddr, -1, true, false) < 0) { cf_log_err_cs(cs, "Failed parsing src_ipaddr: %s", fr_strerror()); goto error; } break; case AF_INET6: if (fr_pton6(&c->src_ipaddr, cl_srcipaddr, -1, true, false) < 0) { cf_log_err_cs(cs, "Failed parsing src_ipaddr: %s", fr_strerror()); goto error; } break; default: rad_assert(0); } #else WARN("Server not built with udpfromto, ignoring client src_ipaddr"); #endif cl_srcipaddr = NULL; } /* * A response_window of zero is OK, and means that it's * ignored by the rest of the server timers. */ if (timerisset(&c->response_window)) { FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, >=, 0, 1000); FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, <=, 60, 0); FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, <=, main_config.max_request_time, 0); }
/* * Read config files. * * This function can ONLY be called from the main server process. */ int main_config_init(void) { char const *p = NULL; CONF_SECTION *cs; struct stat statbuf; cached_config_t *cc; char buffer[1024]; if (stat(radius_dir, &statbuf) < 0) { ERROR("Errors reading %s: %s", radius_dir, fr_syserror(errno)); return -1; } #ifdef S_IWOTH if ((statbuf.st_mode & S_IWOTH) != 0) { ERROR("Configuration directory %s is globally writable. Refusing to start due to insecure configuration.", radius_dir); return -1; } #endif #ifdef S_IROTH if (0 && (statbuf.st_mode & S_IROTH) != 0) { ERROR("Configuration directory %s is globally readable. Refusing to start due to insecure configuration.", radius_dir); return -1; } #endif INFO("Starting - reading configuration files ..."); /* * We need to load the dictionaries before reading the * configuration files. This is because of the * pre-compilation in conffile.c. That should probably * be fixed to be done as a second stage. */ if (!main_config.dictionary_dir) { main_config.dictionary_dir = talloc_typed_strdup(NULL, DICTDIR); } /* * Read the distribution dictionaries first, then * the ones in raddb. */ DEBUG2("including dictionary file %s/%s", main_config.dictionary_dir, RADIUS_DICTIONARY); if (dict_init(main_config.dictionary_dir, RADIUS_DICTIONARY) != 0) { ERROR("Errors reading dictionary: %s", fr_strerror()); return -1; } #define DICT_READ_OPTIONAL(_d, _n) \ do {\ switch (dict_read(_d, _n)) {\ case -1:\ ERROR("Errors reading %s/%s: %s", _d, _n, fr_strerror());\ return -1;\ case 0:\ DEBUG2("including dictionary file %s/%s", _d,_n);\ break;\ default:\ break;\ }\ } while (0) /* * Try to load protocol-specific dictionaries. It's OK * if they don't exist. */ #ifdef WITH_DHCP DICT_READ_OPTIONAL(main_config.dictionary_dir, "dictionary.dhcp"); #endif #ifdef WITH_VMPS DICT_READ_OPTIONAL(main_config.dictionary_dir, "dictionary.vqp"); #endif /* * It's OK if this one doesn't exist. */ DICT_READ_OPTIONAL(radius_dir, RADIUS_DICTIONARY); /* Read the configuration file */ snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf", radius_dir, main_config.name); if ((cs = cf_file_read(buffer)) == NULL) { ERROR("Errors reading or parsing %s", buffer); return -1; } /* * If there was no log destination set on the command line, * set it now. */ if (default_log.dst == L_DST_NULL) { if (cf_section_parse(cs, NULL, serverdest_config) < 0) { fprintf(stderr, "radiusd: Error: Failed to parse log{} section.\n"); cf_file_free(cs); return -1; } if (!radlog_dest) { fprintf(stderr, "radiusd: Error: No log destination specified.\n"); cf_file_free(cs); return -1; } default_log.dst = fr_str2int(log_str2dst, radlog_dest, L_DST_NUM_DEST); if (default_log.dst == L_DST_NUM_DEST) { fprintf(stderr, "radiusd: Error: Unknown log_destination %s\n", radlog_dest); cf_file_free(cs); return -1; } if (default_log.dst == L_DST_SYSLOG) { /* * Make sure syslog_facility isn't NULL * before using it */ if (!syslog_facility) { fprintf(stderr, "radiusd: Error: Syslog chosen but no facility was specified\n"); cf_file_free(cs); return -1; } main_config.syslog_facility = fr_str2int(syslog_str2fac, syslog_facility, -1); if (main_config.syslog_facility < 0) { fprintf(stderr, "radiusd: Error: Unknown syslog_facility %s\n", syslog_facility); cf_file_free(cs); return -1; } #ifdef HAVE_SYSLOG_H /* * Call openlog only once, when the * program starts. */ openlog(progname, LOG_PID, main_config.syslog_facility); #endif } else if (default_log.dst == L_DST_FILES) { if (!main_config.log_file) { fprintf(stderr, "radiusd: Error: Specified \"files\" as a log destination, but no log filename was given!\n"); cf_file_free(cs); return -1; } } } #ifdef HAVE_SETUID /* * Switch users as early as possible. */ if (!switch_users(cs)) fr_exit(1); #endif /* * Open the log file AFTER switching uid / gid. If we * did switch uid/gid, then the code in switch_users() * took care of setting the file permissions correctly. */ if ((default_log.dst == L_DST_FILES) && (default_log.fd < 0)) { 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)); cf_file_free(cs); return -1; } } /* * This allows us to figure out where, relative to * radiusd.conf, the other configuration files exist. */ if (cf_section_parse(cs, NULL, server_config) < 0) { return -1; } /* * We ignore colourization of output until after the * configuration files have been parsed. */ p = getenv("TERM"); if (do_colourise && p && isatty(default_log.fd) && strstr(p, "xterm")) { default_log.colourise = true; } else { default_log.colourise = false; } /* * Starting the server, WITHOUT "-x" on the * command-line: use whatever is in the config * file. */ if (debug_flag == 0) { debug_flag = main_config.debug_level; } fr_debug_flag = debug_flag; FR_INTEGER_COND_CHECK("max_request_time", main_config.max_request_time, (main_config.max_request_time != 0), 100); FR_INTEGER_BOUND_CHECK("reject_delay", main_config.reject_delay, <=, 10); FR_INTEGER_BOUND_CHECK("cleanup_delay", main_config.cleanup_delay, <=, 10); /* * Set default initial request processing delay to 1/3 of a second. * Will be updated by the lowest response window across all home servers, * if it is less than this. */ main_config.init_delay.tv_sec = 0; main_config.init_delay.tv_usec = 1000000 / 3; /* * Free the old configuration items, and replace them * with the new ones. * * Note that where possible, we do atomic switch-overs, * to ensure that the pointers are always valid. */ rad_assert(main_config.config == NULL); root_config = main_config.config = cs; DEBUG2("%s: #### Loading Realms and Home Servers ####", main_config.name); if (!realms_init(cs)) { return -1; } DEBUG2("%s: #### Loading Clients ####", main_config.name); if (!clients_parse_section(cs, false)) { return -1; } /* * Register the %{config:section.subsection} xlat function. */ xlat_register("config", xlat_config, NULL, NULL); xlat_register("client", xlat_client, NULL, NULL); xlat_register("getclient", xlat_getclient, NULL, NULL); /* * Go update our behaviour, based on the configuration * changes. */ /* * Sanity check the configuration for internal * consistency. */ FR_INTEGER_BOUND_CHECK("reject_delay", main_config.reject_delay, <=, main_config.cleanup_delay); if (chroot_dir) { if (chdir(radlog_dir) < 0) { ERROR("Failed to 'chdir %s' after chroot: %s", radlog_dir, fr_syserror(errno)); return -1; } } cc = talloc_zero(NULL, cached_config_t); if (!cc) return -1; cc->cs = talloc_steal(cc ,cs); rad_assert(cs_cache == NULL); cs_cache = cc; /* Clear any unprocessed configuration errors */ (void) fr_strerror(); 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 = NULL; VALUE_PAIR *reply_tmp = NULL; PAIR_LIST *pl = NULL, *t; PAIR_LIST **last = &pl; int lineno = 0; int entry_lineno = 0; FR_TOKEN parsecode; #ifdef HAVE_REGEX_H VALUE_PAIR *vp; vp_cursor_t cursor; #endif 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; } /* * 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 the line contains nothing but whitespace, * ignore it. */ ptr = buffer; while (isspace((int) *ptr)) ptr++; if (*ptr == '#' || *ptr == '\n' || !*ptr) continue; parse_again: if (mode == FIND_MODE_NAME) { /* * The user's name MUST be the first text on the line. */ if (isspace((int) buffer[0])) { ERROR("%s[%d]: Entry does not begin with a user name", file, lineno); fclose(fp); return -1; } /* * Get the name. */ ptr = buffer; getword(&ptr, entry, sizeof(entry), false); entry_lineno = lineno; /* * 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), false); } else { getword(&ptr, newfile, sizeof(newfile), false); } 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; } /* $INCLUDE ... */ /* * Parse the check values */ rad_assert(check_tmp == NULL); rad_assert(reply_tmp == NULL); parsecode = fr_pair_list_afrom_str(ctx, ptr, &check_tmp); if (parsecode == T_INVALID) { pairlist_free(&pl); ERROR("%s[%d]: Parse error (check) for entry %s: %s", file, lineno, entry, fr_strerror()); fclose(fp); return -1; } if (parsecode != T_EOL) { pairlist_free(&pl); talloc_free(check_tmp); ERROR("%s[%d]: Invalid text after check attributes for entry %s", file, lineno, entry); fclose(fp); return -1; } #ifdef HAVE_REGEX_H /* * Do some more sanity checks. */ for (vp = fr_cursor_init(&cursor, &check_tmp); vp; vp = fr_cursor_next(&cursor)) { if (((vp->op == T_OP_REG_EQ) || (vp->op == T_OP_REG_NE)) && (vp->da->type != PW_TYPE_STRING)) { pairlist_free(&pl); talloc_free(check_tmp); ERROR("%s[%d]: Cannot use regular expressions for non-string attributes in entry %s", file, lineno, entry); fclose(fp); return -1; } } #endif /* * The reply MUST be on a new line. */ mode = FIND_MODE_WANT_REPLY; continue; } /* * We COULD have a reply, OR we could have a new entry. */ if (mode == FIND_MODE_WANT_REPLY) { if (!isspace((int) buffer[0])) goto create_entry; mode = FIND_MODE_HAVE_REPLY; } /* * mode == FIND_MODE_HAVE_REPLY */ /* * The previous line ended with a comma, and then * we have the start of a new entry! */ if (!isspace((int) buffer[0])) { trailing_comma: pairlist_free(&pl); talloc_free(check_tmp); talloc_free(reply_tmp); ERROR("%s[%d]: Invalid comma after the reply attributes. Please delete it.", file, lineno); fclose(fp); return -1; } /* * Parse the reply values. If there's a trailing * comma, keep parsing the reply values. */ parsecode = fr_pair_list_afrom_str(ctx, buffer, &reply_tmp); if (parsecode == T_COMMA) { continue; } /* * We expect an EOL. Anything else is an error. */ if (parsecode != T_EOL) { pairlist_free(&pl); talloc_free(check_tmp); talloc_free(reply_tmp); ERROR("%s[%d]: Parse error (reply) for entry %s: %s", file, lineno, entry, fr_strerror()); fclose(fp); return -1; } create_entry: /* * Done with this entry... */ MEM(t = talloc_zero(ctx, PAIR_LIST)); if (check_tmp) fr_pair_steal(t, check_tmp); if (reply_tmp) fr_pair_steal(t, reply_tmp); t->check = check_tmp; t->reply = reply_tmp; t->lineno = entry_lineno; check_tmp = NULL; reply_tmp = NULL; t->name = talloc_typed_strdup(t, entry); *last = t; last = &(t->next); /* * Look for a name. If we came here because * there were no reply attributes, then re-parse * the current line, instead of reading another one. */ mode = FIND_MODE_NAME; if (feof(fp)) break; if (!isspace((int) buffer[0])) goto parse_again; } /* * We're at EOF. If we're supposed to read more, that's * an error. */ if (mode == FIND_MODE_HAVE_REPLY) goto trailing_comma; /* * We had an entry, but no reply attributes. That's OK. */ if (mode == FIND_MODE_WANT_REPLY) goto create_entry; /* * Else we were looking for an entry. We didn't get one * because we were at EOF, so that's OK. */ fclose(fp); *list = pl; return 0; }
/** Set the filename of a #CONF_ITEM * * @param[in] ci to set filename on. * @param[in] filename to set. */ void _cf_filename_set(CONF_ITEM *ci, char const *filename) { talloc_const_free(ci->filename); ci->filename = talloc_typed_strdup(ci, filename); }
/** Allocate a #CONF_SECTION * * @param[in] ctx to allocate * @param[in] parent #CONF_SECTION to hang this #CONF_SECTION off of. * If parent is not NULL, the new section will be added as a child. * @param[in] name1 Primary name. * @param[in] name2 Secondary name. * @param[in] filename Caller file name for debugging. May be overridden later. * @param[in] lineno Caller line number for debugging. May be overridden later. * @return * - NULL on error. * - A new #CONF_SECTION parented by parent. */ CONF_SECTION *_cf_section_alloc(TALLOC_CTX *ctx, CONF_SECTION *parent, char const *name1, char const *name2, char const *filename, int lineno) { CONF_SECTION *cs; char buffer[1024]; if (!name1) return NULL; if (name2 && parent) { char const *p; p = strchr(name2, '$'); if (p && (p[1] != '{')) p = NULL; if (p) { name2 = cf_expand_variables(parent->item.filename, &parent->item.lineno, parent, buffer, sizeof(buffer), name2, NULL); if (!name2) { ERROR("Failed expanding section name"); return NULL; } } } cs = talloc_zero(ctx, CONF_SECTION); if (!cs) return NULL; cs->item.type = CONF_ITEM_SECTION; cs->item.parent = cf_section_to_item(parent); fr_cursor_init(&cs->item.cursor, &cs->item.child); MEM(cs->name1 = talloc_typed_strdup(cs, name1)); if (name2) { MEM(cs->name2 = talloc_typed_strdup(cs, name2)); cs->name2_quote = T_BARE_WORD; } talloc_set_destructor(cs, _cf_section_free); if (filename) cf_filename_set(cs, filename); if (lineno) cf_lineno_set(cs, lineno); if (parent) { CONF_DATA const *cd; CONF_PARSER *rule; cs->depth = parent->depth + 1; cf_item_add(parent, &(cs->item)); cd = cf_data_find(CF_TO_ITEM(parent), CONF_PARSER, cf_section_name1(parent)); if (cd) { rule = cf_data_value(cd); /* * The parent has an ON_READ rule. Check * if that rule has a child which matches * this section. */ if ((FR_BASE_TYPE(rule->type) == FR_TYPE_SUBSECTION) && ((rule->type & FR_TYPE_ON_READ) != 0) && rule->subcs) { CONF_PARSER const *rule_p; for (rule_p = rule->subcs; rule_p->name; rule_p++) { if ((FR_BASE_TYPE(rule_p->type) == FR_TYPE_SUBSECTION) && ((rule_p->type & FR_TYPE_ON_READ) != 0) && (strcmp(rule_p->name, name1) == 0)) { (void) _cf_section_rule_push(cs, rule_p, cd->item.filename, cd->item.lineno); (void) rule_p->func(ctx, NULL, NULL, cf_section_to_item(cs), rule_p); return cs; } } } } /* * Call any ON_READ callback. And push this rule * to the child section, so that the child can * pick it up. */ cd = cf_data_find(CF_TO_ITEM(parent), CONF_PARSER, name1); if (cd) { rule = cf_data_value(cd); if (rule->func && (FR_BASE_TYPE(rule->type) == FR_TYPE_SUBSECTION) && ((rule->type & FR_TYPE_ON_READ) != 0)) { (void) cf_section_rules_push(cs, rule); (void) rule->func(ctx, NULL, NULL, cf_section_to_item(cs), rule); } } } return cs; }
/** Convert module specific attribute id to value_pair_tmpl_t. * * @param[in] ctx for talloc * @param[in] name string to convert. * @param[in] type Type of quoting around value. * @param[in] request_def The default request to insert unqualified * attributes into. * @param[in] list_def The default list to insert unqualified attributes into. * @return pointer to new VPT. */ value_pair_tmpl_t *radius_str2tmpl(TALLOC_CTX *ctx, char const *name, FR_TOKEN type, request_refs_t request_def, pair_lists_t list_def) { int rcode; char const *p; value_pair_tmpl_t *vpt; char buffer[1024]; vpt = talloc_zero(ctx, value_pair_tmpl_t); vpt->name = talloc_typed_strdup(vpt, name); switch (type) { case T_BARE_WORD: /* * If we can parse it as an attribute, it's an attribute. * Otherwise, treat it as a literal. */ rcode = radius_parse_attr(vpt, vpt->name, request_def, list_def); if (rcode == -2) { talloc_free(vpt); return NULL; } if (rcode == 0) { break; } /* FALL-THROUGH */ case T_SINGLE_QUOTED_STRING: vpt->type = VPT_TYPE_LITERAL; break; case T_DOUBLE_QUOTED_STRING: p = name; while (*p) { if (*p == '\\') { if (!p[1]) break; p += 2; continue; } if (*p == '%') break; p++; } /* * If the double quoted string needs to be * expanded at run time, make it an xlat * expansion. Otherwise, convert it to be a * literal. */ if (*p) { vpt->type = VPT_TYPE_XLAT; } else { vpt->type = VPT_TYPE_LITERAL; } break; case T_BACK_QUOTED_STRING: vpt->type = VPT_TYPE_EXEC; break; case T_OP_REG_EQ: /* hack */ vpt->type = VPT_TYPE_REGEX; break; default: rad_assert(0); return NULL; } radius_tmpl2str(buffer, sizeof(buffer), vpt); return vpt; }
RADCLIENT *client_from_request(RADCLIENT_LIST *clients, REQUEST *request) { int i, *pi; char **p; RADCLIENT *c; char buffer[128]; if (!clients || !request) return NULL; c = talloc_zero(clients, RADCLIENT); c->cs = request->client->cs; c->ipaddr.af = AF_UNSPEC; c->src_ipaddr.af = AF_UNSPEC; for (i = 0; dynamic_config[i].name != NULL; i++) { DICT_ATTR const *da; VALUE_PAIR *vp; da = dict_attrbyname(dynamic_config[i].name); if (!da) { DEBUG("- Cannot add client %s: attribute \"%s\"is not in the dictionary", ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer)), dynamic_config[i].name); error: client_free(c); return NULL; } vp = pairfind(request->config_items, da->attr, da->vendor, TAG_ANY); if (!vp) { /* * Not required. Skip it. */ if (!dynamic_config[i].dflt) continue; DEBUG("- Cannot add client %s: Required attribute \"%s\" is missing.", ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer)), dynamic_config[i].name); goto error; } switch (dynamic_config[i].type) { case PW_TYPE_IPV4_ADDR: if (da->attr == PW_FREERADIUS_CLIENT_IP_ADDRESS) { c->ipaddr.af = AF_INET; c->ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; c->ipaddr.prefix = 32; } else if (da->attr == PW_FREERADIUS_CLIENT_SRC_IP_ADDRESS) { #ifdef WITH_UDPFROMTO c->src_ipaddr.af = AF_INET; c->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; #else WARN("Server not built with udpfromto, ignoring FreeRADIUS-Client-Src-IP-Address"); #endif } break; case PW_TYPE_IPV6_ADDR: if (da->attr == PW_FREERADIUS_CLIENT_IPV6_ADDRESS) { c->ipaddr.af = AF_INET6; c->ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; c->ipaddr.prefix = 128; } else if (da->attr == PW_FREERADIUS_CLIENT_SRC_IPV6_ADDRESS) { #ifdef WITH_UDPFROMTO c->src_ipaddr.af = AF_INET6; c->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; #else WARN("Server not built with udpfromto, ignoring FreeRADIUS-Client-Src-IPv6-Address"); #endif } break; case PW_TYPE_IPV4_PREFIX: if (da->attr == PW_FREERADIUS_CLIENT_IP_PREFIX) { c->ipaddr.af = AF_INET; memcpy(&c->ipaddr.ipaddr.ip4addr.s_addr, &(vp->vp_ipv4prefix[2]), sizeof(c->ipaddr.ipaddr.ip4addr.s_addr)); fr_ipaddr_mask(&c->ipaddr, (vp->vp_ipv4prefix[1] & 0x3f)); } break; case PW_TYPE_IPV6_PREFIX: if (da->attr == PW_FREERADIUS_CLIENT_IPV6_PREFIX) { c->ipaddr.af = AF_INET6; memcpy(&c->ipaddr.ipaddr.ip6addr, &(vp->vp_ipv6prefix[2]), sizeof(c->ipaddr.ipaddr.ip6addr)); fr_ipaddr_mask(&c->ipaddr, vp->vp_ipv6prefix[1]); } break; case PW_TYPE_STRING: p = (char **) ((char *) c + dynamic_config[i].offset); if (*p) talloc_free(*p); if (vp->vp_strvalue[0]) { *p = talloc_typed_strdup(c->cs, vp->vp_strvalue); } else { *p = NULL; } break; case PW_TYPE_BOOLEAN: pi = (int *) ((bool *) ((char *) c + dynamic_config[i].offset)); *pi = vp->vp_integer; break; default: goto error; } } if (c->ipaddr.af == AF_UNSPEC) { DEBUG("- Cannot add client %s: No IP address was specified.", ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer))); goto error; } { fr_ipaddr_t addr; /* * Need to apply the same mask as we set for the client * else clients created with FreeRADIUS-Client-IPv6-Prefix * or FreeRADIUS-Client-IPv4-Prefix will fail this check. */ addr = request->packet->src_ipaddr; fr_ipaddr_mask(&addr, c->ipaddr.prefix); if (fr_ipaddr_cmp(&addr, &c->ipaddr) != 0) { char buf2[128]; DEBUG("- Cannot add client %s: IP address %s do not match", ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer)), ip_ntoh(&c->ipaddr, buf2, sizeof(buf2))); goto error; } } if (!c->secret || !*c->secret) { DEBUG("- Cannot add client %s: No secret was specified.", ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer))); goto error; } if (!client_validate(clients, request->client, c)) { return NULL; } if ((c->src_ipaddr.af != AF_UNSPEC) && (c->src_ipaddr.af != c->ipaddr.af)) { DEBUG("- Cannot add client %s: Client IP and src address are different IP version.", ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer))); goto error; } return c; }
static RADCLIENT *client_parse(CONF_SECTION *cs, int in_server) { RADCLIENT *c; char const *name2; name2 = cf_section_name2(cs); if (!name2) { cf_log_err_cs(cs, "Missing client name"); return NULL; } /* * The size is fine.. Let's create the buffer */ c = talloc_zero(cs, RADCLIENT); c->cs = cs; memset(&cl_ipaddr, 0, sizeof(cl_ipaddr)); if (cf_section_parse(cs, c, client_config) < 0) { cf_log_err_cs(cs, "Error parsing client section"); error: client_free(c); #ifdef WITH_TCP hs_proto = NULL; cl_srcipaddr = NULL; #endif return NULL; } /* * Global clients can set servers to use per-server clients cannot. */ if (in_server && c->server) { cf_log_err_cs(cs, "Clients inside of an server section cannot point to a server"); goto error; } /* * Newer style client definitions with either ipaddr or ipaddr6 * config items. */ if (cf_pair_find(cs, "ipaddr") || cf_pair_find(cs, "ipv4addr") || cf_pair_find(cs, "ipv6addr")) { char buffer[128]; /* * Sets ipv4/ipv6 address and prefix. */ c->ipaddr = cl_ipaddr; /* * Set the long name to be the result of a reverse lookup on the IP address. */ ip_ntoh(&c->ipaddr, buffer, sizeof(buffer)); c->longname = talloc_typed_strdup(c, buffer); /* * Set the short name to the name2. */ if (!c->shortname) c->shortname = talloc_typed_strdup(c, name2); /* * No "ipaddr" or "ipv6addr", use old-style "client <ipaddr> {" syntax. */ } else { ERROR("No 'ipaddr' or 'ipv4addr' or 'ipv6addr' configuration directive found in client %s", name2); goto error; } c->proto = IPPROTO_UDP; if (hs_proto) { if (strcmp(hs_proto, "udp") == 0) { hs_proto = NULL; #ifdef WITH_TCP } else if (strcmp(hs_proto, "tcp") == 0) { hs_proto = NULL; c->proto = IPPROTO_TCP; # ifdef WITH_TLS } else if (strcmp(hs_proto, "tls") == 0) { hs_proto = NULL; c->proto = IPPROTO_TCP; c->tls_required = true; } else if (strcmp(hs_proto, "radsec") == 0) { hs_proto = NULL; c->proto = IPPROTO_TCP; c->tls_required = true; # endif } else if (strcmp(hs_proto, "*") == 0) { hs_proto = NULL; c->proto = IPPROTO_IP; /* fake for dual */ #endif } else { cf_log_err_cs(cs, "Unknown proto \"%s\".", hs_proto); goto error; } } /* * If a src_ipaddr is specified, when we send the return packet * we will use this address instead of the src from the * request. */ if (cl_srcipaddr) { #ifdef WITH_UDPFROMTO switch (c->ipaddr.af) { case AF_INET: if (fr_pton4(&c->src_ipaddr, cl_srcipaddr, 0, true, false) < 0) { cf_log_err_cs(cs, "Failed parsing src_ipaddr: %s", fr_strerror()); goto error; } break; case AF_INET6: if (fr_pton6(&c->src_ipaddr, cl_srcipaddr, 0, true, false) < 0) { cf_log_err_cs(cs, "Failed parsing src_ipaddr: %s", fr_strerror()); goto error; } break; default: rad_assert(0); } #else WARN("Server not built with udpfromto, ignoring client src_ipaddr"); #endif cl_srcipaddr = NULL; } FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, >=, 0, 1000); FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, <=, 60, 0); FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, <=, main_config.max_request_time, 0); #ifdef WITH_DYNAMIC_CLIENTS if (c->client_server) { c->secret = talloc_typed_strdup(c, "testing123"); if (((c->ipaddr.af == AF_INET) && (c->ipaddr.prefix == 32)) || ((c->ipaddr.af == AF_INET6) && (c->ipaddr.prefix == 128))) { cf_log_err_cs(cs, "Dynamic clients MUST be a network, not a single IP address"); goto error; } return c; } #endif if (!c->secret || (c->secret[0] == '\0')) { #ifdef WITH_DHCP char const *value = NULL; CONF_PAIR *cp = cf_pair_find(cs, "dhcp"); if (cp) value = cf_pair_value(cp); /* * Secrets aren't needed for DHCP. */ if (value && (strcmp(value, "yes") == 0)) return c; #endif #ifdef WITH_TLS /* * If the client is TLS only, the secret can be * omitted. When omitted, it's hard-coded to * "radsec". See RFC 6614. */ if (c->tls_required) { c->secret = talloc_typed_strdup(cs, "radsec"); } else #endif { cf_log_err_cs(cs, "secret must be at least 1 character long"); goto error; } } #ifdef WITH_COA /* * Point the client to the home server pool, OR to the * home server. This gets around the problem of figuring * out which port to use. */ if (c->coa_name) { c->coa_pool = home_pool_byname(c->coa_name, HOME_TYPE_COA); if (!c->coa_pool) { c->coa_server = home_server_byname(c->coa_name, HOME_TYPE_COA); } if (!c->coa_pool && !c->coa_server) { cf_log_err_cs(cs, "No such home_server or home_server_pool \"%s\"", c->coa_name); goto error; } } #endif #ifdef WITH_TCP if ((c->proto == IPPROTO_TCP) || (c->proto == IPPROTO_IP)) { if ((c->limit.idle_timeout > 0) && (c->limit.idle_timeout < 5)) c->limit.idle_timeout = 5; if ((c->limit.lifetime > 0) && (c->limit.lifetime < 5)) c->limit.lifetime = 5; if ((c->limit.lifetime > 0) && (c->limit.idle_timeout > c->limit.lifetime)) c->limit.idle_timeout = 0; } #endif return c; }
/* * Internal function to cut down on duplicated code. * * Returns -1 on failure, 0 on no failure. returnrealm * is NULL on don't proxy, realm otherwise. */ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm) { char *namebuf; char *username; char const *realmname = NULL; char *ptr; VALUE_PAIR *vp; REALM *realm; struct realm_config_t *inst = instance; /* initiate returnrealm */ *returnrealm = NULL; /* * If the request has a proxy entry, then it's a proxy * reply, and we're walking through the module list again. * * In that case, don't bother trying to proxy the request * again. * * Also, if there's no User-Name attribute, we can't * proxy it, either. */ if ((!request->username) #ifdef WITH_PROXY || (request->proxy != NULL) #endif ) { RDEBUG2("Proxy reply, or no User-Name. Ignoring"); return RLM_MODULE_NOOP; } /* * Check for 'Realm' attribute. If it exists, then we've proxied * it already ( via another rlm_realm instance ) and should return. */ if (pairfind(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL) { RDEBUG2("Request already has destination realm set. Ignoring"); return RLM_MODULE_NOOP; } /* * We will be modifing this later, so we want our own copy * of it. */ namebuf = talloc_typed_strdup(request, request->username->vp_strvalue); username = namebuf; switch (inst->format) { case REALM_FORMAT_SUFFIX: /* DEBUG2(" rlm_realm: Checking for suffix after \"%c\"", inst->delim[0]); */ ptr = strrchr(username, inst->delim[0]); if (ptr) { *ptr = '\0'; realmname = ptr + 1; } break; case REALM_FORMAT_PREFIX: /* DEBUG2(" rlm_realm: Checking for prefix before \"%c\"", inst->delim[0]); */ ptr = strchr(username, inst->delim[0]); if (ptr) { *ptr = '\0'; ptr++; realmname = username; username = ptr; } break; default: realmname = NULL; break; } /* * Print out excruciatingly descriptive debugging messages * for the people who find it too difficult to think about * what's going on. */ if (realmname) { RDEBUG2("Looking up realm \"%s\" for User-Name = \"%s\"", realmname, request->username->vp_strvalue); } else { if (inst->ignore_null) { RDEBUG2("No '%c' in User-Name = \"%s\", skipping NULL due to config.", inst->delim[0], request->username->vp_strvalue); talloc_free(namebuf); return RLM_MODULE_NOOP; } RDEBUG2("No '%c' in User-Name = \"%s\", looking up realm NULL", inst->delim[0], request->username->vp_strvalue); } /* * Allow DEFAULT realms unless told not to. */ realm = realm_find(realmname); if (!realm) { RDEBUG2("No such realm \"%s\"", (!realmname) ? "NULL" : realmname); talloc_free(namebuf); return RLM_MODULE_NOOP; } if (inst->ignore_default && (strcmp(realm->name, "DEFAULT")) == 0) { RDEBUG2("Found DEFAULT, but skipping due to config"); talloc_free(namebuf); return RLM_MODULE_NOOP; } RDEBUG2("Found realm \"%s\"", realm->name); /* * If we've been told to strip the realm off, then do so. */ if (realm->striprealm) { /* * Create the Stripped-User-Name attribute, if it * doesn't exist. * */ if (request->username->da->attr != PW_STRIPPED_USER_NAME) { vp = radius_paircreate(request->packet, &request->packet->vps, PW_STRIPPED_USER_NAME, 0); RDEBUG2("Adding Stripped-User-Name = \"%s\"", username); } else { vp = request->username; RDEBUG2("Setting Stripped-User-Name = \"%s\"", username); } pairstrcpy(vp, username); request->username = vp; } /* * Add the realm name to the request. * If the realm is a regex, the use the realm as entered * by the user. Otherwise, use the configured realm name, * as realm name comparison is case insensitive. We want * to use the configured name, rather than what the user * entered. */ if (realm->name[0] != '~') realmname = realm->name; pairmake_packet("Realm", realmname, T_OP_EQ); RDEBUG2("Adding Realm = \"%s\"", realmname); talloc_free(namebuf); username = NULL; /* * Figure out what to do with the request. */ switch (request->packet->code) { default: RDEBUG2("Unknown packet code %d\n", request->packet->code); return RLM_MODULE_NOOP; /* * Perhaps accounting proxying was turned off. */ case PW_CODE_ACCOUNTING_REQUEST: if (!realm->acct_pool) { RDEBUG2("Accounting realm is LOCAL"); return RLM_MODULE_OK; } break; /* * Perhaps authentication proxying was turned off. */ case PW_CODE_ACCESS_REQUEST: if (!realm->auth_pool) { RDEBUG2("Authentication realm is LOCAL"); return RLM_MODULE_OK; } break; } #ifdef WITH_PROXY RDEBUG2("Proxying request from user %s to realm %s", request->username->vp_strvalue, realm->name); /* * Skip additional checks if it's not an accounting * request. */ if (request->packet->code != PW_CODE_ACCOUNTING_REQUEST) { *returnrealm = realm; return RLM_MODULE_UPDATED; } /* * FIXME: Each server should have a unique server key, * and put it in the accounting packet. Every server * should know about the keys, and NOT proxy requests to * a server with key X if the packet already contains key * X. */ /* * If this request has arrived from another freeradius server * that has already proxied the request, we don't need to do * it again. */ vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0, TAG_ANY); if (vp && (request->packet->src_ipaddr.af == AF_INET)) { int i; fr_ipaddr_t my_ipaddr; my_ipaddr.af = AF_INET; my_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; /* * Loop over the home accounting servers for this * realm. If one of them has the same IP as the * FreeRADIUS-Proxied-To attribute, then the * packet has already been sent there. Don't * send it there again. */ for (i = 0; i < realm->acct_pool->num_home_servers; i++) { if (fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr, &my_ipaddr) == 0) { RDEBUG2("Suppressing proxy due to FreeRADIUS-Proxied-To"); return RLM_MODULE_OK; } } /* * See detail_recv() in src/main/listen.c for the * additional checks. */ #ifdef WITH_DETAIL } else if ((request->listener->type == RAD_LISTEN_DETAIL) && !fr_inaddr_any(&request->packet->src_ipaddr)) { int i; /* * Loop over the home accounting servers for this * realm. If one of them has the same IP as the * FreeRADIUS-Proxied-To attribute, then the * packet has already been sent there. Don't * send it there again. */ for (i = 0; i < realm->acct_pool->num_home_servers; i++) { if ((fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr, &request->packet->src_ipaddr) == 0) && (realm->acct_pool->servers[i]->port == request->packet->src_port)) { RDEBUG2("Suppressing proxy because packet was already sent to a server in that realm"); return RLM_MODULE_OK; } } #endif /* WITH_DETAIL */ } #endif /* WITH_PROXY */ /* * We got this far, which means we have a realm, set returnrealm */ *returnrealm = realm; return RLM_MODULE_UPDATED; }
static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head, char const **error) { ssize_t slen; char *p; xlat_exp_t *node; rad_assert(fmt[0] == '%'); rad_assert(fmt[1] == '{'); rad_assert(fmt[2] == '%'); rad_assert(fmt[3] == '{'); XLAT_DEBUG("ALTERNATE <-- %s", fmt); node = talloc_zero(ctx, xlat_exp_t); node->type = XLAT_ALTERNATE; p = fmt + 2; slen = xlat_tokenize_expansion(node, p, &node->child, error); if (slen <= 0) { talloc_free(node); return slen - (p - fmt); } p += slen; if (p[0] != ':') { talloc_free(node); *error = "Expected ':' after first expansion"; return -(p - fmt); } p++; if (p[0] != '-') { talloc_free(node); *error = "Expected '-' after ':'"; return -(p - fmt); } p++; /* * Allow the RHS to be empty as a special case. */ if (*p == '}') { /* * Hack up an empty string. */ node->alternate = talloc_zero(node, xlat_exp_t); node->alternate->type = XLAT_LITERAL; node->alternate->fmt = talloc_typed_strdup(node->alternate, ""); *(p++) = '\0'; } else { slen = xlat_tokenize_literal(node, p, &node->alternate, true, error); if (slen <= 0) { talloc_free(node); return slen - (p - fmt); } if (!node->alternate) { talloc_free(node); *error = "Empty expansion is invalid"; return -(p - fmt); } p += slen; } node->async_safe = (node->child->async_safe && node->alternate->async_safe); *head = node; return p - fmt; }
/** Check if a given user is already logged in. * * Process accounting data to determine if a user is already logged in. Sets request->simul_count * to the current session count for this user. * * Check twice. If on the first pass the user exceeds his maximum number of logins, do a second * pass and validate all logins by querying the terminal server. * * @param instance The module instance. * @param request The checksimul request object. * @return Returns operation status (@p rlm_rcode_t). */ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) { rlm_couchbase_t *inst = instance; /* our module instance */ rlm_rcode_t rcode = RLM_MODULE_OK; /* return code */ rlm_couchbase_handle_t *handle = NULL; /* connection pool handle */ char vpath[256], vkey[MAX_KEY_SIZE]; /* view path and query key */ char docid[MAX_KEY_SIZE]; /* document id returned from view */ char error[512]; /* view error return */ int idx = 0; /* row array index counter */ char element[MAX_KEY_SIZE]; /* mapped radius attribute to element name */ lcb_error_t cb_error = LCB_SUCCESS; /* couchbase error holder */ json_object *json, *jval; /* json object holders */ json_object *jrows = NULL; /* json object to hold view rows */ VALUE_PAIR *vp; /* value pair */ uint32_t client_ip_addr = 0; /* current client ip address */ char const *client_cs_id = NULL; /* current client calling station id */ char *user_name = NULL; /* user name from accounting document */ char *session_id = NULL; /* session id from accounting document */ char *cs_id = NULL; /* calling station id from accounting document */ uint32_t nas_addr = 0; /* nas address from accounting document */ uint32_t nas_port = 0; /* nas port from accounting document */ uint32_t framed_ip_addr = 0; /* framed ip address from accounting document */ char framed_proto = 0; /* framed proto from accounting document */ int session_time = 0; /* session time from accounting document */ /* do nothing if this is not enabled */ if (inst->check_simul != true) { RDEBUG3("mod_checksimul returning noop - not enabled"); return RLM_MODULE_NOOP; } /* ensure valid username in request */ if ((!request->username) || (request->username->vp_length == '\0')) { RDEBUG3("mod_checksimul - invalid username"); return RLM_MODULE_INVALID; } /* attempt to build view key */ if (radius_xlat(vkey, sizeof(vkey), request, inst->simul_vkey, NULL, NULL) < 0) { /* log error */ RERROR("could not find simultaneous use view key attribute (%s) in packet", inst->simul_vkey); /* return */ return RLM_MODULE_FAIL; } /* get handle */ handle = fr_connection_get(inst->pool); /* check handle */ if (!handle) return RLM_MODULE_FAIL; /* set couchbase instance */ lcb_t cb_inst = handle->handle; /* set cookie */ cookie_t *cookie = handle->cookie; /* build view path */ snprintf(vpath, sizeof(vpath), "%s?key=\"%s\"&stale=update_after", inst->simul_view, vkey); /* query view for document */ cb_error = couchbase_query_view(cb_inst, cookie, vpath, NULL); /* check error and object */ if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) { /* log error */ RERROR("failed to execute view request or parse return"); /* set return */ rcode = RLM_MODULE_FAIL; /* return */ goto free_and_return; } /* debugging */ RDEBUG3("cookie->jobj == %s", json_object_to_json_string(cookie->jobj)); /* check for error in json object */ if (json_object_object_get_ex(cookie->jobj, "error", &json)) { /* build initial error buffer */ strlcpy(error, json_object_get_string(json), sizeof(error)); /* get error reason */ if (json_object_object_get_ex(cookie->jobj, "reason", &json)) { /* append divider */ strlcat(error, " - ", sizeof(error)); /* append reason */ strlcat(error, json_object_get_string(json), sizeof(error)); } /* log error */ RERROR("view request failed with error: %s", error); /* set return */ rcode = RLM_MODULE_FAIL; /* return */ goto free_and_return; } /* check for document id in return */ if (!json_object_object_get_ex(cookie->jobj, "rows", &json)) { /* log error */ RERROR("failed to fetch rows from view payload"); /* set return */ rcode = RLM_MODULE_FAIL; /* return */ goto free_and_return; } /* get and hold rows */ jrows = json_object_get(json); /* free cookie object */ if (cookie->jobj) { json_object_put(cookie->jobj); cookie->jobj = NULL; } /* check for valid row value */ if (!jrows || !json_object_is_type(jrows, json_type_array)) { /* log error */ RERROR("no valid rows returned from view: %s", vpath); /* set return */ rcode = RLM_MODULE_FAIL; /* return */ goto free_and_return; } /* debugging */ RDEBUG3("jrows == %s", json_object_to_json_string(jrows)); /* set the count */ request->simul_count = json_object_array_length(jrows); /* debugging */ RDEBUG("found %d open sessions for %s", request->simul_count, request->username->vp_strvalue); /* check count */ if (request->simul_count < request->simul_max) { rcode = RLM_MODULE_OK; goto free_and_return; } /* * Current session count exceeds configured maximum. * Continue on to verify the sessions if configured otherwise stop here. */ if (inst->verify_simul != true) { rcode = RLM_MODULE_OK; goto free_and_return; } /* debugging */ RDEBUG("verifying session count"); /* reset the count */ request->simul_count = 0; /* get client ip address for MPP detection below */ if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) { client_ip_addr = vp->vp_ipaddr; } /* get calling station id for MPP detection below */ if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) { client_cs_id = vp->vp_strvalue; } /* loop across all row elements */ for (idx = 0; idx < json_object_array_length(jrows); idx++) { /* clear docid */ memset(docid, 0, sizeof(docid)); /* fetch current index */ json = json_object_array_get_idx(jrows, idx); /* get document id */ if (json_object_object_get_ex(json, "id", &jval)) { /* copy and check length */ if (strlcpy(docid, json_object_get_string(jval), sizeof(docid)) >= sizeof(docid)) { RERROR("document id from row longer than MAX_KEY_SIZE (%d)", MAX_KEY_SIZE); continue; } } /* check for valid doc id */ if (docid[0] == 0) { RWARN("failed to fetch document id from row - skipping"); continue; } /* fetch document */ cb_error = couchbase_get_key(cb_inst, cookie, docid); /* check error and object */ if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) { /* log error */ RERROR("failed to execute get request or parse return"); /* set return */ rcode = RLM_MODULE_FAIL; /* return */ goto free_and_return; } /* debugging */ RDEBUG3("cookie->jobj == %s", json_object_to_json_string(cookie->jobj)); /* get element name for User-Name attribute */ if (mod_attribute_to_element("User-Name", inst->map, &element) == 0) { /* get and check username element */ if (!json_object_object_get_ex(cookie->jobj, element, &jval)){ RDEBUG("cannot zap stale entry without username"); rcode = RLM_MODULE_FAIL; goto free_and_return; } /* copy json string value to user_name */ user_name = talloc_typed_strdup(request, json_object_get_string(jval)); } else { RDEBUG("failed to find map entry for User-Name attribute"); rcode = RLM_MODULE_FAIL; goto free_and_return; } /* get element name for Acct-Session-Id attribute */ if (mod_attribute_to_element("Acct-Session-Id", inst->map, &element) == 0) { /* get and check session id element */ if (!json_object_object_get_ex(cookie->jobj, element, &jval)){ RDEBUG("cannot zap stale entry without session id"); rcode = RLM_MODULE_FAIL; goto free_and_return; } /* copy json string value to session_id */ session_id = talloc_typed_strdup(request, json_object_get_string(jval)); } else { RDEBUG("failed to find map entry for Acct-Session-Id attribute"); rcode = RLM_MODULE_FAIL; goto free_and_return; } /* get element name for NAS-IP-Address attribute */ if (mod_attribute_to_element("NAS-IP-Address", inst->map, &element) == 0) { /* attempt to get and nas address element */ if (json_object_object_get_ex(cookie->jobj, element, &jval)){ nas_addr = inet_addr(json_object_get_string(jval)); } } /* get element name for NAS-Port attribute */ if (mod_attribute_to_element("NAS-Port", inst->map, &element) == 0) { /* attempt to get nas port element */ if (json_object_object_get_ex(cookie->jobj, element, &jval)) { nas_port = (uint32_t) json_object_get_int(jval); } } /* check terminal server */ int check = rad_check_ts(nas_addr, nas_port, user_name, session_id); /* take action based on check return */ if (check == 0) { /* stale record - zap it if enabled */ if (inst->delete_stale_sessions) { /* get element name for Framed-IP-Address attribute */ if (mod_attribute_to_element("Framed-IP-Address", inst->map, &element) == 0) { /* attempt to get framed ip address element */ if (json_object_object_get_ex(cookie->jobj, element, &jval)) { framed_ip_addr = inet_addr(json_object_get_string(jval)); } } /* get element name for Framed-Port attribute */ if (mod_attribute_to_element("Framed-Port", inst->map, &element) == 0) { /* attempt to get framed port element */ if (json_object_object_get_ex(cookie->jobj, element, &jval)) { if (strcmp(json_object_get_string(jval), "PPP") == 0) { framed_proto = 'P'; } else if (strcmp(json_object_get_string(jval), "SLIP") == 0) { framed_proto = 'S'; } } } /* get element name for Acct-Session-Time attribute */ if (mod_attribute_to_element("Acct-Session-Time", inst->map, &element) == 0) { /* attempt to get session time element */ if (json_object_object_get_ex(cookie->jobj, element, &jval)) { session_time = json_object_get_int(jval); } } /* zap session */ session_zap(request, nas_addr, nas_port, user_name, session_id, framed_ip_addr, framed_proto, session_time); } } else if (check == 1) { /* user is still logged in - increase count */ ++request->simul_count; /* get element name for Framed-IP-Address attribute */ if (mod_attribute_to_element("Framed-IP-Address", inst->map, &element) == 0) { /* attempt to get framed ip address element */ if (json_object_object_get_ex(cookie->jobj, element, &jval)) { framed_ip_addr = inet_addr(json_object_get_string(jval)); } else { /* ensure 0 if not found */ framed_ip_addr = 0; } } /* get element name for Calling-Station-Id attribute */ if (mod_attribute_to_element("Calling-Station-Id", inst->map, &element) == 0) { /* attempt to get framed ip address element */ if (json_object_object_get_ex(cookie->jobj, element, &jval)) { /* copy json string value to cs_id */ cs_id = talloc_typed_strdup(request, json_object_get_string(jval)); } else { /* ensure null if not found */ cs_id = NULL; } } /* Does it look like a MPP attempt? */ if (client_ip_addr && framed_ip_addr && framed_ip_addr == client_ip_addr) { request->simul_mpp = 2; } else if (client_cs_id && cs_id && !strncmp(cs_id, client_cs_id, 16)) { request->simul_mpp = 2; } } else { /* check failed - return error */ REDEBUG("failed to check the terminal server for user '%s'", user_name); rcode = RLM_MODULE_FAIL; goto free_and_return; } /* free and reset document user name talloc */ if (user_name) { talloc_free(user_name); user_name = NULL; } /* free and reset document calling station id talloc */ if (cs_id) { talloc_free(cs_id); cs_id = NULL; } /* free and reset document session id talloc */ if (session_id) { talloc_free(session_id); session_id = NULL; } /* free and reset json object before fetching next row */ if (cookie->jobj) { json_object_put(cookie->jobj); cookie->jobj = NULL; } } /* debugging */ RDEBUG("retained %d open sessions for %s after verification", request->simul_count, request->username->vp_strvalue); free_and_return: /* free document user name talloc */ if (user_name) { talloc_free(user_name); } /* free document calling station id talloc */ if (cs_id) { talloc_free(cs_id); } /* free document session id talloc */ if (session_id) { talloc_free(session_id); } /* free rows */ if (jrows) { json_object_put(jrows); } /* free and reset json object */ if (cookie->jobj) { json_object_put(cookie->jobj); cookie->jobj = NULL; } /* release handle */ if (handle) { fr_connection_release(inst->pool, handle); } /* * The Auth module apparently looks at request->simul_count, * not the return value of this module when deciding to deny * a call for too many sessions. */ return rcode; }
/* * filterBinary: * * This routine will call routines to parse entries from an ASCII format * to a binary format recognized by the Ascend boxes. * * pair: Pointer to value_pair to place return. * * valstr: The string to parse * * return: -1 for error or 0. */ int ascend_parse_filter(VALUE_PAIR *vp, char const *value) { int token, type; int rcode; int argc; char *argv[32]; ascend_filter_t filter; char *p; rcode = -1; /* * Rather than printing specific error messages, we create * a general one here, which won't be used if the function * returns OK. */ fr_strerror_printf("Text is not in proper format"); /* * Tokenize the input string in the VP. * * Once the filter is *completelty* parsed, then we will * over-write it with the final binary filter. */ p = talloc_typed_strdup(vp, value); argc = str2argv(p, argv, 32); if (argc < 3) { talloc_free(p); return -1; } /* * Decide which filter type it is: ip, ipx, or generic */ type = fr_str2int(filterType, argv[0], -1); memset(&filter, 0, sizeof(filter)); /* * Validate the filter type. */ switch (type) { case RAD_FILTER_GENERIC: case RAD_FILTER_IP: case RAD_FILTER_IPX: filter.type = type; break; default: fr_strerror_printf("Unknown Ascend filter type \"%s\"", argv[0]); talloc_free(p); return -1; break; } /* * Parse direction */ token = fr_str2int(filterKeywords, argv[1], -1); switch (token) { case FILTER_IN: filter.direction = 1; break; case FILTER_OUT: filter.direction = 0; break; default: fr_strerror_printf("Unknown Ascend filter direction \"%s\"", argv[1]); talloc_free(p); return -1; } /* * Parse action */ token = fr_str2int(filterKeywords, argv[2], -1); switch (token) { case FILTER_FORWARD: filter.forward = 1; break; case FILTER_DROP: filter.forward = 0; break; default: fr_strerror_printf("Unknown Ascend filter action \"%s\"", argv[2]); talloc_free(p); return -1; break; } switch (type) { case RAD_FILTER_GENERIC: rcode = ascend_parse_generic(argc - 3, &argv[3], &filter.u.generic); break; case RAD_FILTER_IP: rcode = ascend_parse_ip(argc - 3, &argv[3], &filter.u.ip); break; case RAD_FILTER_IPX: rcode = ascend_parse_ipx(argc - 3, &argv[3], &filter.u.ipx); break; } /* * Touch the VP only if everything was OK. */ if (rcode == 0) { vp->length = sizeof(filter); memcpy(vp->vp_filter, &filter, sizeof(filter)); } talloc_free(p); return rcode; #if 0 /* * if 'more' is set then this new entry must exist, be a * FILTER_GENERIC_TYPE, direction and disposition must match for * the previous 'more' to be valid. If any should fail then TURN OFF * previous 'more' */ if( prevRadvp ) { filt = ( RadFilter * )prevRadvp->vp_strvalue; if(( tok != FILTER_GENERIC_TYPE ) || (rc == -1 ) || ( prevRadvp->attribute != vp->attribute ) || ( filt->indirection != radFil.indirection ) || ( filt->forward != radFil.forward ) ) { gen = &filt->u.generic; gen->more = false; fr_strerror_printf("filterBinary: 'more' for previous entry doesn't match: %s.\n", valstr); } } prevRadvp = NULL; if( rc != -1 && tok == FILTER_GENERIC_TYPE ) { if( radFil.u.generic.more ) { prevRadvp = vp; } } if( rc != -1 ) { vpmemcpy(vp, &radFil, vp->length ); } return(rc); #endif }
/** Create and insert a cache entry * * @return * - #RLM_MODULE_OK on success. * - #RLM_MODULE_UPDATED if we merged the cache entry. * - #RLM_MODULE_FAIL on failure. */ static rlm_rcode_t cache_insert(rlm_cache_t const *inst, REQUEST *request, rlm_cache_handle_t **handle, uint8_t const *key, size_t key_len, int ttl) { vp_map_t const *map; vp_map_t **last, *c_map; VALUE_PAIR *vp; bool merge = false; rlm_cache_entry_t *c; size_t len; TALLOC_CTX *pool; if ((inst->config.max_entries > 0) && inst->driver->count && (inst->driver->count(&inst->config, inst->driver_inst->data, request, handle) > inst->config.max_entries)) { RWDEBUG("Cache is full: %d entries", inst->config.max_entries); return RLM_MODULE_FAIL; } c = cache_alloc(inst, request); if (!c) return RLM_MODULE_FAIL; c->key = talloc_memdup(c, key, key_len); c->key_len = key_len; c->created = c->expires = request->packet->timestamp.tv_sec; c->expires += ttl; last = &c->maps; RDEBUG2("Creating new cache entry"); /* * Alloc a pool so we don't have excessive allocs when * gathering VALUE_PAIRs to cache. */ pool = talloc_pool(NULL, 2048); for (map = inst->maps; map != NULL; map = map->next) { VALUE_PAIR *to_cache = NULL; fr_cursor_t cursor; rad_assert(map->lhs && map->rhs); /* * Calling map_to_vp gives us exactly the same result, * as if this were an update section. */ if (map_to_vp(pool, &to_cache, request, map, NULL) < 0) { RDEBUG2("Skipping %s", map->rhs->name); continue; } for (vp = fr_cursor_init(&cursor, &to_cache); vp; vp = fr_cursor_next(&cursor)) { /* * Prevent people from accidentally caching * cache control attributes. */ if (map->rhs->type == TMPL_TYPE_LIST) switch (vp->da->attr) { case FR_CACHE_TTL: case FR_CACHE_STATUS_ONLY: case FR_CACHE_MERGE_NEW: case FR_CACHE_ENTRY_HITS: RDEBUG2("Skipping %s", vp->da->name); continue; default: break; } RINDENT(); if (RDEBUG_ENABLED2) map_debug_log(request, map, vp); REXDENT(); MEM(c_map = talloc_zero(c, vp_map_t)); c_map->op = map->op; /* * Now we turn the VALUE_PAIRs into maps. */ switch (map->lhs->type) { /* * Attributes are easy, reuse the LHS, and create a new * RHS with the fr_value_box_t from the VALUE_PAIR. */ case TMPL_TYPE_ATTR: c_map->lhs = map->lhs; /* lhs shouldn't be touched, so this is ok */ do_rhs: MEM(c_map->rhs = tmpl_init(talloc(c_map, vp_tmpl_t), TMPL_TYPE_DATA, map->rhs->name, map->rhs->len, T_BARE_WORD)); if (fr_value_box_copy(c_map->rhs, &c_map->rhs->tmpl_value, &vp->data) < 0) { REDEBUG("Failed copying attribute value"); error: talloc_free(pool); talloc_free(c); return RLM_MODULE_FAIL; } c_map->rhs->tmpl_value_type = vp->vp_type; if (vp->vp_type == FR_TYPE_STRING) { c_map->rhs->quote = is_printable(vp->vp_strvalue, vp->vp_length) ? T_SINGLE_QUOTED_STRING : T_DOUBLE_QUOTED_STRING; } break; /* * Lists are weird... We need to fudge a new LHS template, * which is a combination of the LHS list and the attribute. */ case TMPL_TYPE_LIST: { char attr[256]; MEM(c_map->lhs = tmpl_init(talloc(c_map, vp_tmpl_t), TMPL_TYPE_ATTR, map->lhs->name, map->lhs->len, T_BARE_WORD)); c_map->lhs->tmpl_da = vp->da; if (vp->da->flags.is_unknown) { /* for tmpl_verify() */ c_map->lhs->tmpl_unknown = fr_dict_unknown_acopy(c_map->lhs, vp->da); c_map->lhs->tmpl_da = c_map->lhs->tmpl_unknown; } c_map->lhs->tmpl_tag = vp->tag; c_map->lhs->tmpl_list = map->lhs->tmpl_list; c_map->lhs->tmpl_num = map->lhs->tmpl_num; c_map->lhs->tmpl_request = map->lhs->tmpl_request; /* * We need to rebuild the attribute name, to be the * one we copied from the source list. */ len = tmpl_snprint(attr, sizeof(attr), c_map->lhs); if (is_truncated(len, sizeof(attr))) { REDEBUG("Serialized attribute too long. Must be < " STRINGIFY(sizeof(attr)) " bytes, got %zu bytes", len); goto error; } c_map->lhs->len = len; c_map->lhs->name = talloc_typed_strdup(c_map->lhs, attr); } goto do_rhs; default: rad_assert(0); } *last = c_map; last = &(*last)->next; } talloc_free_children(pool); /* reset pool state */ } talloc_free(pool); /* * Check to see if we need to merge the entry into the request */ vp = fr_pair_find_by_da(request->control, attr_cache_merge_new, TAG_ANY); if (vp && vp->vp_bool) merge = true; if (merge) cache_merge(inst, request, c); for (;;) { cache_status_t ret; ret = inst->driver->insert(&inst->config, inst->driver_inst->data, request, *handle, c); switch (ret) { case CACHE_RECONNECT: if (cache_reconnect(handle, inst, request) == 0) continue; return RLM_MODULE_FAIL; case CACHE_OK: RDEBUG2("Committed entry, TTL %d seconds", ttl); cache_free(inst, &c); return merge ? RLM_MODULE_UPDATED : RLM_MODULE_OK; default: talloc_free(c); /* Failed insertion - use talloc_free not the driver free */ return RLM_MODULE_FAIL; } } }
static SECURID_AUTH_RC securidAuth(void *instance, REQUEST *request, char const *username, char const *passcode, char *replyMsgBuffer, size_t replyMsgBufferSize) { rlm_securid_t *inst = (rlm_securid_t *) instance; int acm_ret; SD_PIN pin_params; char new_pin[10]; char format[30]; SECURID_SESSION *securid_session = NULL; int rc = -1; SD_CHAR *securid_user, *securid_pass; if (!username) { ERROR("SecurID username is NULL"); return RC_SECURID_AUTH_FAILURE; } if (!passcode) { ERROR("SecurID passcode is NULL for %s user", username); return RC_SECURID_AUTH_FAILURE; } memcpy(&securid_user, &username, sizeof(securid_user)); memcpy(&securid_pass, &passcode, sizeof(securid_pass)); *replyMsgBuffer = '\0'; securid_session = securid_sessionlist_find(inst, request); if (!securid_session) { /* securid session not found */ SDI_HANDLE sdiHandle = SDI_HANDLE_NONE; acm_ret = SD_Init(&sdiHandle); if (acm_ret != ACM_OK) { RERROR("Cannot communicate with the ACE/Server"); return -1; } acm_ret = SD_Lock(sdiHandle, securid_user); if (acm_ret != ACM_OK) { REDEBUG("SecurID: Access denied. Name [%s] lock failed", username); return -2; } acm_ret = SD_Check(sdiHandle, securid_pass, securid_user); switch (acm_ret) { case ACM_OK: /* we are in now */ RDEBUG2("SecurID authentication successful for %s", username); SD_Close(sdiHandle); return RC_SECURID_AUTH_SUCCESS; case ACM_ACCESS_DENIED: /* not this time */ RDEBUG2("SecurID Access denied for %s", username); SD_Close(sdiHandle); return RC_SECURID_AUTH_ACCESS_DENIED_FAILURE; case ACM_INVALID_SERVER: RERROR("SecurID: Invalid ACE server"); return RC_SECURID_AUTH_INVALID_SERVER_FAILURE; case ACM_NEW_PIN_REQUIRED: RDEBUG2("SecurID new pin required for %s", username); /* create a new session */ securid_session = securid_session_alloc(); securid_session->sdiHandle = sdiHandle; /* save ACE handle for future use */ securid_session->securidSessionState = NEW_PIN_REQUIRED_STATE; securid_session->identity = talloc_typed_strdup(securid_session, username); /* Get PIN requirements */ acm_ret = AceGetPinParams(sdiHandle, &pin_params); /* If a system-generated PIN is required */ if (pin_params.Selectable == CANNOT_CHOOSE_PIN) { /* Prompt user to accept a system generated PIN */ snprintf(replyMsgBuffer, replyMsgBufferSize, "\r\nAre you prepared to accept a new system-generated PIN [y/n]?"); securid_session->securidSessionState = NEW_PIN_SYSTEM_ACCEPT_STATE; } else if (pin_params.Selectable == USER_SELECTABLE) { //may be returned by AM 6.x servers. snprintf(replyMsgBuffer, replyMsgBufferSize, "\r\nPress 'y' to generate a new PIN\r\nOR\r\n'n'to enter a new PIN yourself [y/n]"); securid_session->securidSessionState = NEW_PIN_USER_SELECT_STATE; } else { if (pin_params.Alphanumeric) { strcpy(format, "alphanumeric characters"); } else { strcpy(format, "digits"); } snprintf(replyMsgBuffer, replyMsgBufferSize, " \r\n Enter your new PIN of %d to %d %s, \r\n or\r\n <Ctrl-D> to cancel the New PIN procedure:", pin_params.Min, pin_params.Max, format); } /* insert new session in the session list */ securid_sessionlist_add(inst, request, securid_session); return RC_SECURID_AUTH_CHALLENGE; case ACM_NEXT_CODE_REQUIRED: RDEBUG2("Next securid token code required for %s", username); /* create a new session */ securid_session = securid_session_alloc(); securid_session->sdiHandle = sdiHandle; securid_session->securidSessionState = NEXT_CODE_REQUIRED_STATE; securid_session->identity = talloc_typed_strdup(securid_session, username); /* insert new session in the session list */ securid_sessionlist_add(inst, request, securid_session); strlcpy(replyMsgBuffer, "\r\nPlease Enter the Next Code from Your Token:", replyMsgBufferSize); return RC_SECURID_AUTH_CHALLENGE; default: ERROR("SecurID: Unexpected error from ACE/Agent API acm_ret=%d", acm_ret); securid_session_free(inst, request, securid_session); return RC_SECURID_AUTH_FAILURE; } } else { /* existing session found */ RDEBUG2("Continuing previous session found for user [%s]", username); /* continue previous session */ switch (securid_session->securidSessionState) { case NEXT_CODE_REQUIRED_STATE: DEBUG2("Securid NEXT_CODE_REQUIRED_STATE: User [%s]", username); /* next token code mode */ acm_ret = SD_Next(securid_session->sdiHandle, securid_pass); if (acm_ret == ACM_OK) { INFO("Next SecurID token accepted for [%s].", securid_session->identity); rc = RC_SECURID_AUTH_SUCCESS; } else { INFO("SecurID: Next token rejected for [%s].", securid_session->identity); rc = RC_SECURID_AUTH_FAILURE; } /* deallocate session */ securid_session_free(inst, request, securid_session); return rc; case NEW_PIN_REQUIRED_STATE: RDEBUG2("SecurID NEW_PIN_REQUIRED_STATE for %s", username); /* save the previous pin */ if (securid_session->pin) TALLOC_FREE(securid_session->pin); securid_session->pin = talloc_typed_strdup(securid_session, passcode); strlcpy(replyMsgBuffer, "\r\n Please re-enter new PIN:", replyMsgBufferSize); /* set next state */ securid_session->securidSessionState = NEW_PIN_USER_CONFIRM_STATE; /* insert the updated session in the session list */ securid_sessionlist_add(inst, request, securid_session); return RC_SECURID_AUTH_CHALLENGE; case NEW_PIN_USER_CONFIRM_STATE: RDEBUG2("SecurID NEW_PIN_USER_CONFIRM_STATE: User [%s]", username); /* compare previous pin and current pin */ if (!securid_session->pin || strcmp(securid_session->pin, passcode)) { RDEBUG2("Pin confirmation failed. Pins do not match [%s] and [%s]", SAFE_STR(securid_session->pin), securid_pass); /* pins do not match */ /* challenge the user again */ AceGetPinParams(securid_session->sdiHandle, &pin_params); if (pin_params.Alphanumeric) { strcpy(format, "alphanumeric characters"); } else { strcpy(format, "digits"); } snprintf(replyMsgBuffer, replyMsgBufferSize, " \r\n Pins do not match--Please try again.\r\n Enter your new PIN of %d to %d %s, \r\n or\r\n <Ctrl-D> to cancel the New PIN procedure:", pin_params.Min, pin_params.Max, format); securid_session->securidSessionState = NEW_PIN_REQUIRED_STATE; /* insert the updated session in the session list */ securid_sessionlist_add(inst, request, securid_session); rc = RC_SECURID_AUTH_CHALLENGE; } else { /* pins match */ RDEBUG2("Pin confirmation succeeded. Pins match"); acm_ret = SD_Pin(securid_session->sdiHandle, securid_pass); if (acm_ret == ACM_NEW_PIN_ACCEPTED) { RDEBUG2("New SecurID pin accepted for %s.", securid_session->identity); securid_session->securidSessionState = NEW_PIN_AUTH_VALIDATE_STATE; /* insert the updated session in the session list */ securid_sessionlist_add(inst, request, securid_session); rc = RC_SECURID_AUTH_CHALLENGE; strlcpy(replyMsgBuffer, " \r\n\r\nWait for the code on your card to change, then enter new PIN and TokenCode\r\n\r\nEnter PASSCODE:", replyMsgBufferSize); } else { RDEBUG2("SecurID: New SecurID pin rejected for %s.", securid_session->identity); SD_Pin(securid_session->sdiHandle, &empty_pin[0]); /* cancel PIN */ rc = RC_SECURID_AUTH_FAILURE; /* deallocate session */ securid_session_free(inst, request, securid_session); } } return rc; case NEW_PIN_AUTH_VALIDATE_STATE: acm_ret = SD_Check(securid_session->sdiHandle, securid_pass, securid_user); if (acm_ret == ACM_OK) { RDEBUG2("New SecurID passcode accepted for %s", securid_session->identity); rc = RC_SECURID_AUTH_SUCCESS; } else { INFO("SecurID: New passcode rejected for [%s]", securid_session->identity); rc = RC_SECURID_AUTH_FAILURE; } /* deallocate session */ securid_session_free(inst, request, securid_session); return rc; case NEW_PIN_SYSTEM_ACCEPT_STATE: if (!strcmp(passcode, "y")) { AceGetSystemPin(securid_session->sdiHandle, new_pin); /* Save the PIN for the next session * continuation */ if (securid_session->pin) TALLOC_FREE(securid_session->pin); securid_session->pin = talloc_typed_strdup(securid_session, new_pin); snprintf(replyMsgBuffer, replyMsgBufferSize, "\r\nYour new PIN is: %s\r\nDo you accept this [y/n]?", new_pin); securid_session->securidSessionState = NEW_PIN_SYSTEM_CONFIRM_STATE; /* insert the updated session in the * session list */ securid_sessionlist_add(inst, request, securid_session); rc = RC_SECURID_AUTH_CHALLENGE; } else { SD_Pin(securid_session->sdiHandle, &empty_pin[0]); //Cancel new PIN /* deallocate session */ securid_session_free(inst, request, securid_session); rc = RC_SECURID_AUTH_FAILURE; } return rc; case NEW_PIN_SYSTEM_CONFIRM_STATE: acm_ret = SD_Pin(securid_session->sdiHandle, (SD_CHAR*)securid_session->pin); if (acm_ret == ACM_NEW_PIN_ACCEPTED) { strlcpy(replyMsgBuffer, " \r\n\r\nPin Accepted. Wait for the code on your card to change, then enter new PIN and TokenCode\r\n\r\nEnter PASSCODE:", replyMsgBufferSize); securid_session->securidSessionState = NEW_PIN_AUTH_VALIDATE_STATE; /* insert the updated session in the session list */ securid_sessionlist_add(inst, request, securid_session); rc = RC_SECURID_AUTH_CHALLENGE; } else { SD_Pin(securid_session->sdiHandle, &empty_pin[0]); //Cancel new PIN strlcpy(replyMsgBuffer, " \r\n\r\nPin Rejected. Wait for the code on your card to change, then try again.\r\n\r\nEnter PASSCODE:", replyMsgBufferSize); /* deallocate session */ securid_session_free(inst, request, securid_session); rc = RC_SECURID_AUTH_FAILURE; } return rc; /* USER_SELECTABLE state should be implemented to preserve compatibility with AM 6.x servers, which can return this state */ case NEW_PIN_USER_SELECT_STATE: if (!strcmp(passcode, "y")) { /* User has opted for a system-generated PIN */ AceGetSystemPin(securid_session->sdiHandle, new_pin); snprintf(replyMsgBuffer, replyMsgBufferSize, "\r\nYour new PIN is: %s\r\nDo you accept this [y/n]?", new_pin); securid_session->securidSessionState = NEW_PIN_SYSTEM_CONFIRM_STATE; /* insert the updated session in the session list */ securid_sessionlist_add(inst, request, securid_session); rc = RC_SECURID_AUTH_CHALLENGE; } else { /* User has opted for a user-defined PIN */ AceGetPinParams(securid_session->sdiHandle, &pin_params); if (pin_params.Alphanumeric) { strcpy(format, "alphanumeric characters"); } else { strcpy(format, "digits"); } snprintf(replyMsgBuffer, replyMsgBufferSize, " \r\n Enter your new PIN of %d to %d %s, \r\n or\r\n <Ctrl-D> to cancel the New PIN procedure:", pin_params.Min, pin_params.Max, format); securid_session->securidSessionState = NEW_PIN_REQUIRED_STATE; /* insert the updated session in the session list */ securid_sessionlist_add(inst, request, securid_session); rc = RC_SECURID_AUTH_CHALLENGE; } return rc; default: ERROR("Invalid session state %d for user \"%s\"", securid_session->securidSessionState, username); break; } } return 0; }
/** Expand the RHS of a template * * @note Length of expanded string can be found with talloc_array_length(*out) - 1 * * @param out where to write a pointer to the newly allocated buffer. * @param request Current request. * @param vpt to evaluate. * @return -1 on error, else 0. */ static int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR *vp; *out = NULL; rad_assert(vpt->type != VPT_TYPE_LIST); switch (vpt->type) { case VPT_TYPE_LITERAL: EVAL_DEBUG("TMPL LITERAL"); *out = talloc_typed_strdup(request, vpt->name); break; case VPT_TYPE_EXEC: EVAL_DEBUG("TMPL EXEC"); *out = talloc_array(request, char, 1024); if (radius_exec_program(request, vpt->name, true, false, *out, 1024, EXEC_TIMEOUT, NULL, NULL) != 0) { TALLOC_FREE(*out); return -1; } break; case VPT_TYPE_REGEX: EVAL_DEBUG("TMPL REGEX"); /* Error in expansion, this is distinct from zero length expansion */ if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) { rad_assert(!*out); return -1; } break; case VPT_TYPE_XLAT: EVAL_DEBUG("TMPL XLAT"); /* Error in expansion, this is distinct from zero length expansion */ if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) { rad_assert(!*out); return -1; } break; case VPT_TYPE_XLAT_STRUCT: EVAL_DEBUG("TMPL XLAT_STRUCT"); /* Error in expansion, this is distinct from zero length expansion */ if (radius_axlat_struct(out, request, vpt->vpt_xlat, NULL, NULL) < 0) { rad_assert(!*out); return -1; } RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */ RDEBUG2(" --> %s", *out); break; case VPT_TYPE_ATTR: EVAL_DEBUG("TMPL ATTR"); if (radius_vpt_get_vp(&vp, request, vpt) < 0) { return -2; } *out = vp_aprint(request, vp); if (!*out) { return -1; } break; case VPT_TYPE_DATA: case VPT_TYPE_REGEX_STRUCT: rad_assert(0 == 1); /* FALL-THROUGH */ default: break; } EVAL_DEBUG("Expand tmpl --> %s", *out); return 0; }
/* * Read a file compose of xlat's and expected results */ static bool do_xlats(char const *filename, FILE *fp) { int lineno = 0; ssize_t len; char *p; char input[8192]; char output[8192]; REQUEST *request; struct timeval now; /* * Create and initialize the new request. */ request = request_alloc(NULL); gettimeofday(&now, NULL); request->timestamp = now; request->log.lvl = rad_debug_lvl; request->log.func = vradlog_request; output[0] = '\0'; while (fgets(input, sizeof(input), fp) != NULL) { lineno++; /* * Ignore blank lines and comments */ p = input; while (isspace((int) *p)) p++; if (*p < ' ') continue; if (*p == '#') continue; p = strchr(p, '\n'); if (!p) { if (!feof(fp)) { fprintf(stderr, "Line %d too long in %s\n", lineno, filename); TALLOC_FREE(request); return false; } } else { *p = '\0'; } /* * Look for "xlat" */ if (strncmp(input, "xlat ", 5) == 0) { ssize_t slen; char const *error = NULL; char *fmt = talloc_typed_strdup(NULL, input + 5); xlat_exp_t *head; slen = xlat_tokenize(fmt, fmt, &head, &error); if (slen <= 0) { talloc_free(fmt); snprintf(output, sizeof(output), "ERROR offset %d '%s'", (int) -slen, error); continue; } if (input[slen + 5] != '\0') { talloc_free(fmt); snprintf(output, sizeof(output), "ERROR offset %d 'Too much text' ::%s::", (int) slen, input + slen + 5); continue; } len = radius_xlat_struct(output, sizeof(output), request, head, NULL, NULL); if (len < 0) { snprintf(output, sizeof(output), "ERROR expanding xlat: %s", fr_strerror()); continue; } TALLOC_FREE(fmt); /* also frees 'head' */ continue; } /* * Look for "data". */ if (strncmp(input, "data ", 5) == 0) { if (strcmp(input + 5, output) != 0) { fprintf(stderr, "Mismatch at line %d of %s\n\tgot : %s\n\texpected : %s\n", lineno, filename, output, input + 5); TALLOC_FREE(request); return false; } continue; } fprintf(stderr, "Unknown keyword in %s[%d]\n", filename, lineno); TALLOC_FREE(request); return false; } TALLOC_FREE(request); return true; }
int main(int argc, char **argv) { RADIUS_PACKET *req; char *p; int c; int port = 0; char *filename = NULL; FILE *fp; int id; int force_af = AF_UNSPEC; /* * 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"); id = ((int)getpid() & 0xff); fr_debug_flag = 0; radlog_dest = L_DST_STDERR; set_radius_dir(autofree, RADIUS_DIR); while ((c = getopt(argc, argv, "46c:d:D:f:hi:qst:r:S:xXv")) != EOF) { switch(c) { case '4': force_af = AF_INET; break; case '6': force_af = AF_INET6; break; case 'd': set_radius_dir(autofree, optarg); break; case 'D': mainconfig.dictionary_dir = talloc_typed_strdup(NULL, optarg); break; case 'f': filename = optarg; break; case 'q': do_output = 0; break; case 'x': debug_flag++; fr_debug_flag++; break; case 'X': #if 0 sha1_data_problems = 1; /* for debugging only */ #endif break; case 'r': if (!isdigit((int) *optarg)) usage(); retries = atoi(optarg); break; case 'i': if (!isdigit((int) *optarg)) usage(); id = atoi(optarg); if ((id < 0) || (id > 255)) { usage(); } break; case 's': do_summary = 1; break; case 't': if (!isdigit((int) *optarg)) usage(); timeout = atof(optarg); break; case 'v': printf("radeapclient: $Id: 20c518ef414933fae3cdf564852c12e483f7c8c9 $ built on " __DATE__ " at " __TIME__ "\n"); exit(0); break; case 'S': fp = fopen(optarg, "r"); if (!fp) { fprintf(stderr, "radclient: Error opening %s: %s\n", optarg, fr_syserror(errno)); exit(1); } if (fgets(filesecret, sizeof(filesecret), fp) == NULL) { fprintf(stderr, "radclient: Error reading %s: %s\n", 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) { fprintf(stderr, "radclient: Secret in %s is too short\n", optarg); exit(1); } secret = filesecret; break; case 'h': default: usage(); break; } } argc -= (optind - 1); argv += (optind - 1); if ((argc < 3) || ((!secret) && (argc < 4))) { usage(); } if (!mainconfig.dictionary_dir) { mainconfig.dictionary_dir = DICTDIR; } /* * Read the distribution dictionaries first, then * the ones in raddb. */ DEBUG2("including dictionary file %s/%s", mainconfig.dictionary_dir, RADIUS_DICTIONARY); if (dict_init(mainconfig.dictionary_dir, RADIUS_DICTIONARY) != 0) { ERROR("Errors reading dictionary: %s", fr_strerror()); exit(1); } /* * It's OK if this one doesn't exist. */ int rcode = dict_read(radius_dir, RADIUS_DICTIONARY); if (rcode == -1) { ERROR("Errors reading %s/%s: %s", radius_dir, RADIUS_DICTIONARY, fr_strerror()); exit(1); } /* * We print this after reading it. That way if * it doesn't exist, it's OK, and we don't print * anything. */ if (rcode == 0) { DEBUG2("including dictionary file %s/%s", radius_dir, RADIUS_DICTIONARY); } req = rad_alloc(NULL, 1); if (!req) { fr_perror("radclient"); exit(1); } #if 0 { FILE *randinit; if((randinit = fopen("/dev/urandom", "r")) == NULL) { perror("/dev/urandom"); } else { fread(randctx.randrsl, 256, 1, randinit); fclose(randinit); } } fr_randinit(&randctx, 1); #endif req->id = id; /* * Resolve hostname. */ if (force_af == AF_UNSPEC) force_af = AF_INET; req->dst_ipaddr.af = force_af; if (strcmp(argv[1], "-") != 0) { char const *hostname = argv[1]; char const *portname = argv[1]; char buffer[256]; if (*argv[1] == '[') { /* IPv6 URL encoded */ p = strchr(argv[1], ']'); if ((size_t) (p - argv[1]) >= sizeof(buffer)) { usage(); } memcpy(buffer, argv[1] + 1, p - argv[1] - 1); buffer[p - argv[1] - 1] = '\0'; hostname = buffer; portname = p + 1; } p = strchr(portname, ':'); if (p && (strchr(p + 1, ':') == NULL)) { *p = '\0'; portname = p + 1; } else { portname = NULL; } if (ip_hton(hostname, force_af, &req->dst_ipaddr) < 0) { fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", hostname, fr_syserror(errno)); exit(1); } /* * Strip port from hostname if needed. */ if (portname) port = atoi(portname); } /* * See what kind of request we want to send. */ if (strcmp(argv[2], "auth") == 0) { if (port == 0) port = getport("radius"); if (port == 0) port = PW_AUTH_UDP_PORT; req->code = PW_CODE_AUTHENTICATION_REQUEST; } else if (strcmp(argv[2], "acct") == 0) { if (port == 0) port = getport("radacct"); if (port == 0) port = PW_ACCT_UDP_PORT; req->code = PW_CODE_ACCOUNTING_REQUEST; do_summary = 0; } else if (strcmp(argv[2], "status") == 0) { if (port == 0) port = getport("radius"); if (port == 0) port = PW_AUTH_UDP_PORT; req->code = PW_CODE_STATUS_SERVER; } else if (strcmp(argv[2], "disconnect") == 0) { if (port == 0) port = PW_POD_UDP_PORT; req->code = PW_CODE_DISCONNECT_REQUEST; } else if (isdigit((int) argv[2][0])) { if (port == 0) port = getport("radius"); if (port == 0) port = PW_AUTH_UDP_PORT; req->code = atoi(argv[2]); } else { usage(); } req->dst_port = port; /* * Add the secret. */ if (argv[3]) secret = argv[3]; /* * Read valuepairs. * Maybe read them, from stdin, if there's no * filename, or if the filename is '-'. */ if (filename && (strcmp(filename, "-") != 0)) { fp = fopen(filename, "r"); if (!fp) { fprintf(stderr, "radclient: Error opening %s: %s\n", filename, fr_syserror(errno)); exit(1); } } else { fp = stdin; } /* * Send request. */ if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("radclient: socket: "); exit(1); } while(!filedone) { if(req->vps) pairfree(&req->vps); if ((req->vps = readvp2(NULL, fp, &filedone, "radeapclient:")) == NULL) { break; } sendrecv_eap(req); } if(do_summary) { printf("\n\t Total approved auths: %d\n", totalapp); printf("\t Total denied auths: %d\n", totaldeny); } talloc_free(autofree); return 0; }
/* * The main guy. */ int main(int argc, char *argv[]) { int rcode = EXIT_SUCCESS; int argval; const char *input_file = NULL; const char *output_file = NULL; const char *filter_file = NULL; FILE *fp; REQUEST *request = 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; }
/** Expand values in an attribute map where needed * */ int rlm_ldap_map_xlat(REQUEST *request, value_pair_map_t const *maps, rlm_ldap_map_xlat_t *expanded) { value_pair_map_t const *map; unsigned int total = 0; VALUE_PAIR *found, **from = NULL; REQUEST *context; for (map = maps; map != NULL; map = map->next) { switch (map->src->type) { case VPT_TYPE_XLAT: { ssize_t len; char *exp = NULL; len = radius_xlat(exp, 0, request, map->src->name, NULL, NULL); if (len < 0) { RDEBUG("Expansion of LDAP attribute \"%s\" failed", map->src->name); goto error; } expanded->attrs[total++] = exp; break; } case VPT_TYPE_ATTR: context = request; if (radius_request(&context, map->src->vpt_request) == 0) { from = radius_list(context, map->src->vpt_list); } if (!from) continue; found = pairfind(*from, map->src->vpt_da->attr, map->src->vpt_da->vendor, TAG_ANY); if (!found) continue; expanded->attrs[total++] = talloc_typed_strdup(request, found->vp_strvalue); break; case VPT_TYPE_EXEC: { char answer[1024]; VALUE_PAIR **input_pairs = NULL; int result; input_pairs = radius_list(request, PAIR_LIST_REQUEST); result = radius_exec_program(request, map->src->name, true, true, answer, sizeof(answer), EXEC_TIMEOUT, input_pairs ? *input_pairs : NULL, NULL); if (result != 0) { return -1; } expanded->attrs[total++] = talloc_typed_strdup(request, answer); } break; case VPT_TYPE_LITERAL: expanded->attrs[total++] = map->src->name; break; default: rad_assert(0); error: expanded->attrs[total] = NULL; rlm_ldap_map_xlat_free(expanded); return -1; } } rad_assert(total < LDAP_MAX_ATTRMAP); expanded->attrs[total] = NULL; expanded->count = total; expanded->maps = maps; return 0; }
/* * 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; }
static REQUEST *request_setup(FILE *fp) { VALUE_PAIR *vp; REQUEST *request; vp_cursor_t cursor; /* * Create and initialize the new request. */ request = request_alloc(NULL); request->packet = rad_alloc(request, false); if (!request->packet) { ERROR("No memory"); talloc_free(request); return NULL; } request->reply = rad_alloc(request, false); if (!request->reply) { ERROR("No memory"); talloc_free(request); return NULL; } request->listener = listen_alloc(request); request->client = client_alloc(request); request->number = 0; request->master_state = REQUEST_ACTIVE; request->child_state = REQUEST_RUNNING; request->handle = NULL; request->server = talloc_typed_strdup(request, "default"); request->root = &main_config; /* * Read packet from fp */ if (readvp2(request->packet, &request->packet->vps, fp, &filedone) < 0) { fr_perror("unittest"); talloc_free(request); return NULL; } /* * Set the defaults for IPs, etc. */ request->packet->code = PW_CODE_ACCESS_REQUEST; request->packet->src_ipaddr.af = AF_INET; request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK); request->packet->src_port = 18120; request->packet->dst_ipaddr.af = AF_INET; request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK); request->packet->dst_port = 1812; /* * Copied from radclient */ #if 1 /* * Fix up Digest-Attributes issues */ for (vp = fr_cursor_init(&cursor, &request->packet->vps); vp; vp = fr_cursor_next(&cursor)) { /* * Double quoted strings get marked up as xlat expansions, * but we don't support that here. */ if (vp->type == VT_XLAT) { vp->vp_strvalue = vp->value.xlat; vp->value.xlat = NULL; vp->type = VT_DATA; } if (!vp->da->vendor) switch (vp->da->attr) { default: break; /* * Allow it to set the packet type in * the attributes read from the file. */ case PW_PACKET_TYPE: request->packet->code = vp->vp_integer; break; case PW_PACKET_DST_PORT: request->packet->dst_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_DST_IP_ADDRESS: request->packet->dst_ipaddr.af = AF_INET; request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; break; case PW_PACKET_DST_IPV6_ADDRESS: request->packet->dst_ipaddr.af = AF_INET6; request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; break; case PW_PACKET_SRC_PORT: request->packet->src_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_SRC_IP_ADDRESS: request->packet->src_ipaddr.af = AF_INET; request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; break; case PW_PACKET_SRC_IPV6_ADDRESS: request->packet->src_ipaddr.af = AF_INET6; request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; break; case PW_CHAP_PASSWORD: { int i, already_hex = 0; /* * If it's 17 octets, it *might* be already encoded. * Or, it might just be a 17-character password (maybe UTF-8) * Check it for non-printable characters. The odds of ALL * of the characters being 32..255 is (1-7/8)^17, or (1/8)^17, * or 1/(2^51), which is pretty much zero. */ if (vp->length == 17) { for (i = 0; i < 17; i++) { if (vp->vp_octets[i] < 32) { already_hex = 1; break; } } } /* * Allow the user to specify ASCII or hex CHAP-Password */ if (!already_hex) { uint8_t *p; size_t len, len2; len = len2 = vp->length; if (len2 < 17) len2 = 17; p = talloc_zero_array(vp, uint8_t, len2); memcpy(p, vp->vp_strvalue, len); rad_chap_encode(request->packet, p, fr_rand() & 0xff, vp); vp->vp_octets = p; vp->length = 17; } } break; case PW_DIGEST_REALM: case PW_DIGEST_NONCE: case PW_DIGEST_METHOD: case PW_DIGEST_URI: case PW_DIGEST_QOP: case PW_DIGEST_ALGORITHM: case PW_DIGEST_BODY_DIGEST: case PW_DIGEST_CNONCE: case PW_DIGEST_NONCE_COUNT: case PW_DIGEST_USER_NAME: /* overlapping! */ { DICT_ATTR const *da; uint8_t *p, *q; p = talloc_array(vp, uint8_t, vp->length + 2); memcpy(p + 2, vp->vp_octets, vp->length); p[0] = vp->da->attr - PW_DIGEST_REALM + 1; vp->length += 2; p[1] = vp->length; da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0); rad_assert(da != NULL); vp->da = da; /* * Re-do pairmemsteal ourselves, * because we play games with * vp->da, and pairmemsteal goes * to GREAT lengths to sanitize * and fix and change and * double-check the various * fields. */ memcpy(&q, &vp->vp_octets, sizeof(q)); talloc_free(q); vp->vp_octets = talloc_steal(vp, p); vp->type = VT_DATA; VERIFY_VP(vp); } break; } } /* loop over the VP's we read in */ #endif if (debug_flag) { for (vp = fr_cursor_init(&cursor, &request->packet->vps); vp; vp = fr_cursor_next(&cursor)) { /* * Take this opportunity to verify all the VALUE_PAIRs are still valid. */ if (!talloc_get_type(vp, VALUE_PAIR)) { ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp)); fr_log_talloc_report(vp); rad_assert(0); } vp_print(fr_log_fp, vp); } fflush(fr_log_fp); } /* * FIXME: set IPs, etc. */ request->packet->code = PW_CODE_ACCESS_REQUEST; request->packet->src_ipaddr.af = AF_INET; request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK); request->packet->src_port = 18120; request->packet->dst_ipaddr.af = AF_INET; request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK); request->packet->dst_port = 1812; /* * Build the reply template from the request. */ request->reply->sockfd = request->packet->sockfd; request->reply->dst_ipaddr = request->packet->src_ipaddr; request->reply->src_ipaddr = request->packet->dst_ipaddr; request->reply->dst_port = request->packet->src_port; request->reply->src_port = request->packet->dst_port; request->reply->id = request->packet->id; request->reply->code = 0; /* UNKNOWN code */ memcpy(request->reply->vector, request->packet->vector, sizeof(request->reply->vector)); request->reply->vps = NULL; request->reply->data = NULL; request->reply->data_len = 0; /* * Debugging */ request->log.lvl = debug_flag; request->log.func = vradlog_request; request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY); request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY); return request; }
static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head, char const **error) { ssize_t slen; char *p, *q; char *start; xlat_exp_t *node; #ifdef HAVE_REGEX long num; #endif rad_assert(fmt[0] == '%'); rad_assert(fmt[1] == '{'); /* * %{%{...}:-bar} */ if ((fmt[2] == '%') && (fmt[3] == '{')) return xlat_tokenize_alternation(ctx, fmt, head, error); XLAT_DEBUG("EXPANSION <-- %s", fmt); node = talloc_zero(ctx, xlat_exp_t); node->fmt = start = talloc_typed_strdup(node, fmt + 2); node->len = 0; #ifdef HAVE_REGEX /* * Handle regex's specially. */ p = start; num = strtol(p, &q, 10); if (p != q && (*q == '}')) { XLAT_DEBUG("REGEX <-- %s", fmt); *q = '\0'; if ((num > REQUEST_MAX_REGEX) || (num < 0)) { talloc_free(node); *error = "Invalid regex reference. Must be in range 0-" STRINGIFY(REQUEST_MAX_REGEX); return -2; /* error */ } node->regex_index = num; node->type = XLAT_REGEX; *head = node; node->len = (q - start); MEM(start = talloc_realloc_bstr(start, node->len)); q++; /* Skip closing brace */ return 2 + (q - start); } #endif /* HAVE_REGEX */ /* * %{Attr-Name} * %{Attr-Name[#]} * %{Tunnel-Password:1} * %{Tunnel-Password:1[#]} * %{request:Attr-Name} * %{request:Tunnel-Password:1} * %{request:Tunnel-Password:1[#]} * %{mod:foo} */ /* * This is for efficiency, so we don't search for an xlat, * when what's being referenced is obviously an attribute. */ p = start; for (q = p; *q != '\0'; q++) { if (*q == ':') break; if (isspace((int) *q)) break; if (*q == '[') continue; if (*q == '}') break; } /* * Check for empty expressions %{} */ if ((*q == '}') && (q == p)) { talloc_free(node); *error = "Empty expression is invalid"; return (-(p - start)) - 2; /* error */ } /* * Might be a module name reference. * * If it's not, it's an attribute or parse error. */ if (*q == ':') { *q = '\0'; node->xlat = xlat_func_find(node->fmt); if (node->xlat) { /* * %{mod:foo} */ node->type = XLAT_FUNC; p = q + 1; XLAT_DEBUG("MOD <-- %s ... %s", node->fmt, p); slen = xlat_tokenize_literal(node, p, &node->child, true, error); if (slen < 0) { talloc_free(node); return (slen - (p - start)) - 2; /* error */ } p += slen; node->async_safe = (node->xlat->async_safe && node->child->async_safe); *head = node; rad_assert(node->next == NULL); node->len = p - start; MEM(start = talloc_realloc_bstr(start, node->len)); return 2 + node->len; } *q = ':'; /* Avoids a talloc_strdup */ } /* * The first token ends with: * - '[' - Which is an attribute index, so it must be an attribute. * - '}' - The end of the expansion, which means it was a bareword. */ slen = tmpl_afrom_attr_substr(node, &node->attr, p, &(vp_tmpl_rules_t){ .allow_undefined = true, .allow_unknown = true });
/** Create and insert a cache entry. * * @return RLM_MODULE_OK on success, RLM_MODULE_UPDATED if we merged the cache entry and RLM_MODULE_FAIL on failure. */ static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle, char const *key, int ttl) { VALUE_PAIR *vp, *to_cache; vp_cursor_t src_list, cached_request, cached_reply, cached_control; value_pair_map_t const *map; bool merge = false; rlm_cache_entry_t *c; if ((inst->max_entries > 0) && inst->module->count && (inst->module->count(inst, request, handle) > inst->max_entries)) { RWDEBUG("Cache is full: %d entries", inst->max_entries); return RLM_MODULE_FAIL; } c = cache_alloc(inst, request); if (!c) return RLM_MODULE_FAIL; c->key = talloc_typed_strdup(c, key); c->created = c->expires = request->timestamp; c->expires += ttl; RDEBUG("Creating new cache entry"); fr_cursor_init(&cached_request, &c->packet); fr_cursor_init(&cached_reply, &c->reply); fr_cursor_init(&cached_control, &c->control); for (map = inst->maps; map != NULL; map = map->next) { rad_assert(map->lhs && map->rhs); if (map_to_vp(&to_cache, request, map, NULL) < 0) { RDEBUG("Skipping %s", map->rhs->name); continue; } /* * Reparent the VPs map_to_vp may return multiple. */ for (vp = fr_cursor_init(&src_list, &to_cache); vp; vp = fr_cursor_next(&src_list)) { VERIFY_VP(vp); /* * Prevent people from accidentally caching * cache control attributes. */ if (map->rhs->type == TMPL_TYPE_LIST) switch (vp->da->attr) { case PW_CACHE_TTL: case PW_CACHE_STATUS_ONLY: case PW_CACHE_READ_ONLY: case PW_CACHE_MERGE: case PW_CACHE_ENTRY_HITS: RDEBUG2("Skipping %s", vp->da->name); continue; default: break; } RINDENT(); if (RDEBUG_ENABLED2) map_debug_log(request, map, vp); REXDENT(); (void) talloc_steal(c, vp); vp->op = map->op; switch (map->lhs->tmpl_list) { case PAIR_LIST_REQUEST: fr_cursor_insert(&cached_request, vp); break; case PAIR_LIST_REPLY: fr_cursor_insert(&cached_reply, vp); break; case PAIR_LIST_CONTROL: fr_cursor_insert(&cached_control, vp); break; default: rad_assert(0); /* should have been caught by validation */ } } } /* * Check to see if we need to merge the entry into the request */ vp = pairfind(request->config_items, PW_CACHE_MERGE, 0, TAG_ANY); if (vp && (vp->vp_integer > 0)) merge = true; if (merge) cache_merge(inst, request, c); for (;;) { cache_status_t ret; ret = inst->module->insert(inst, request, handle, c); switch (ret) { case CACHE_RECONNECT: if (cache_reconnect(inst, request, handle) == 0) continue; return RLM_MODULE_FAIL; case CACHE_OK: RDEBUG("Commited entry, TTL %d seconds", ttl); cache_free(inst, &c); return merge ? RLM_MODULE_UPDATED : RLM_MODULE_OK; default: talloc_free(c); /* Failed insertion - use talloc_free not the driver free */ return RLM_MODULE_FAIL; } } }
/** Create and insert a cache entry. * * @return * - #RLM_MODULE_OK on success. * - #RLM_MODULE_UPDATED if we merged the cache entry. * - #RLM_MODULE_FAIL on failure. */ static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle, char const *key, int ttl) { vp_map_t const *map; vp_map_t **last, *c_map; VALUE_PAIR *vp; bool merge = false; rlm_cache_entry_t *c; TALLOC_CTX *pool; if ((inst->max_entries > 0) && inst->module->count && (inst->module->count(inst, request, handle) > inst->max_entries)) { RWDEBUG("Cache is full: %d entries", inst->max_entries); return RLM_MODULE_FAIL; } c = cache_alloc(inst, request); if (!c) return RLM_MODULE_FAIL; c->key = talloc_typed_strdup(c, key); c->created = c->expires = request->timestamp; c->expires += ttl; last = &c->maps; RDEBUG("Creating new cache entry"); /* * Alloc a pool so we don't have excessive mallocs when * gathering VALUE_PAIRs to cache. */ pool = talloc_pool(NULL, 1024); for (map = inst->maps; map != NULL; map = map->next) { VALUE_PAIR *to_cache = NULL; vp_cursor_t cursor; rad_assert(map->lhs && map->rhs); /* * Calling map_to_vp gives us exactly the same result, * as if this were an update section. */ if (map_to_vp(pool, &to_cache, request, map, NULL) < 0) { RDEBUG("Skipping %s", map->rhs->name); continue; } for (vp = fr_cursor_init(&cursor, &to_cache); vp; vp = fr_cursor_next(&cursor)) { /* * Prevent people from accidentally caching * cache control attributes. */ if (map->rhs->type == TMPL_TYPE_LIST) switch (vp->da->attr) { case PW_CACHE_TTL: case PW_CACHE_STATUS_ONLY: case PW_CACHE_READ_ONLY: case PW_CACHE_MERGE: case PW_CACHE_ENTRY_HITS: RDEBUG2("Skipping %s", vp->da->name); continue; default: break; } RINDENT(); if (RDEBUG_ENABLED2) map_debug_log(request, map, vp); REXDENT(); MEM(c_map = talloc_zero(c, vp_map_t)); c_map->op = map->op; /* * Now we turn the VALUE_PAIRs into maps. */ switch (map->lhs->type) { /* * Attributes are easy, reuse the LHS, and create a new * RHS with the value_data_t from the VALUE_PAIR. */ case TMPL_TYPE_ATTR: c_map->lhs = map->lhs; /* lhs shouldn't be touched, so this is ok */ do_rhs: MEM(c_map->rhs = tmpl_init(talloc(c_map, vp_tmpl_t), TMPL_TYPE_DATA, map->rhs->name, map->rhs->len)); if (value_data_copy(c_map->rhs, &c_map->rhs->tmpl_data_value, vp->da->type, &vp->data) < 0) { REDEBUG("Failed copying attribute value"); talloc_free(pool); talloc_free(c); return RLM_MODULE_FAIL; } c_map->rhs->tmpl_data_type = vp->da->type; break; /* * Lists are weird... We need to fudge a new LHS template, * which is a combination of the LHS list and the attribute. */ case TMPL_TYPE_LIST: { char attr[256]; MEM(c_map->lhs = tmpl_init(talloc(c_map, vp_tmpl_t), TMPL_TYPE_ATTR, map->lhs->name, map->lhs->len)); c_map->lhs->tmpl_da = vp->da; c_map->lhs->tmpl_tag = vp->tag; c_map->lhs->tmpl_list = map->lhs->tmpl_list; c_map->lhs->tmpl_num = map->lhs->tmpl_num; c_map->lhs->tmpl_request = map->lhs->tmpl_request; /* * We need to rebuild the attribute name, to be the * one we copied from the source list. */ c_map->lhs->len = tmpl_prints(attr, sizeof(attr), c_map->lhs, NULL); c_map->lhs->name = talloc_strdup(map->lhs, attr); } goto do_rhs; default: rad_assert(0); } *last = c_map; last = &(*last)->next; } talloc_free_children(pool); /* reset pool state */ } talloc_free(pool); /* * Check to see if we need to merge the entry into the request */ vp = pairfind(request->config, PW_CACHE_MERGE, 0, TAG_ANY); if (vp && (vp->vp_integer > 0)) merge = true; if (merge) cache_merge(inst, request, c); for (;;) { cache_status_t ret; ret = inst->module->insert(inst, request, handle, c); switch (ret) { case CACHE_RECONNECT: if (cache_reconnect(inst, request, handle) == 0) continue; return RLM_MODULE_FAIL; case CACHE_OK: RDEBUG("Commited entry, TTL %d seconds", ttl); cache_free(inst, &c); return merge ? RLM_MODULE_UPDATED : RLM_MODULE_OK; default: talloc_free(c); /* Failed insertion - use talloc_free not the driver free */ return RLM_MODULE_FAIL; } } }