/************************************************************************* * * Function: sql_userparse * * Purpose: Read entries from the database and fill VALUE_PAIR structures * *************************************************************************/ int sql_userparse(TALLOC_CTX *ctx, VALUE_PAIR **head, rlm_sql_row_t row) { VALUE_PAIR *vp; char const *ptr, *value; char buf[MAX_STRING_LEN]; char do_xlat = 0; FR_TOKEN token, operator = T_EOL; /* * Verify the 'Attribute' field */ if (!row[2] || row[2][0] == '\0') { ERROR("rlm_sql: The 'Attribute' field is empty or NULL, skipping the entire row"); return -1; } /* * Verify the 'op' field */ if (row[4] != NULL && row[4][0] != '\0') { ptr = row[4]; operator = gettoken(&ptr, buf, sizeof(buf), false); if ((operator < T_OP_ADD) || (operator > T_OP_CMP_EQ)) { ERROR("rlm_sql: Invalid operator \"%s\" for attribute %s", row[4], row[2]); return -1; } } else { /* * Complain about empty or invalid 'op' field */ operator = T_OP_CMP_EQ; ERROR("rlm_sql: The 'op' field for attribute '%s = %s' is NULL, or non-existent.", row[2], row[3]); ERROR("rlm_sql: You MUST FIX THIS if you want the configuration to behave as you expect"); } /* * The 'Value' field may be empty or NULL */ value = row[3]; /* * If we have a new-style quoted string, where the * *entire* string is quoted, do xlat's. */ if (row[3] != NULL && ((row[3][0] == '\'') || (row[3][0] == '`') || (row[3][0] == '"')) && (row[3][0] == row[3][strlen(row[3])-1])) { token = gettoken(&value, buf, sizeof(buf), false); switch (token) { /* * Take the unquoted string. */ case T_SINGLE_QUOTED_STRING: case T_DOUBLE_QUOTED_STRING: value = buf; break; /* * Mark the pair to be allocated later. */ case T_BACK_QUOTED_STRING: value = NULL; do_xlat = 1; break; /* * Keep the original string. */ default: value = row[3]; break; } } /* * Create the pair */ vp = pairmake(ctx, NULL, row[2], NULL, operator); if (!vp) { ERROR("rlm_sql: Failed to create the pair: %s", fr_strerror()); return -1; } if (do_xlat) { if (pairmark_xlat(vp, value) < 0) { ERROR("rlm_sql: Error marking pair for xlat"); talloc_free(vp); return -1; } } else { if (pairparsevalue(vp, value, -1) < 0) { ERROR("rlm_sql: Error parsing value: %s", fr_strerror()); talloc_free(vp); return -1; } } /* * Add the pair into the packet */ pairadd(head, vp); return 0; }
/** 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); }
int main(int argc, char **argv) { static uint16_t server_port = 0; static int packet_code = 0; static fr_ipaddr_t server_ipaddr; static fr_ipaddr_t client_ipaddr; int c; char const *radius_dir = RADDBDIR; char const *dict_dir = DICTDIR; char const *filename = NULL; DICT_ATTR const *da; RADIUS_PACKET *request = NULL; #ifdef HAVE_LINUX_IF_PACKET_H bool raw_mode = false; #endif fr_debug_lvl = 0; while ((c = getopt(argc, argv, "d:D:f:hr:t:vx" #ifdef HAVE_LINUX_IF_PACKET_H "i:" #endif )) != EOF) switch(c) { case 'D': dict_dir = optarg; break; case 'd': radius_dir = optarg; break; case 'f': filename = optarg; break; #ifdef HAVE_LINUX_IF_PACKET_H case 'i': iface = optarg; break; #endif case 'r': if (!isdigit((int) *optarg)) usage(); retries = atoi(optarg); if ((retries == 0) || (retries > 1000)) usage(); break; case 't': if (!isdigit((int) *optarg)) usage(); timeout = atof(optarg); break; case 'v': printf("%s\n", dhcpclient_version); exit(0); case 'x': fr_debug_lvl++; fr_log_fp = stdout; break; case 'h': default: usage(); } argc -= (optind - 1); argv += (optind - 1); if (argc < 2) usage(); /* convert timeout to a struct timeval */ #define USEC 1000000 tv_timeout.tv_sec = timeout; tv_timeout.tv_usec = ((timeout - (float) tv_timeout.tv_sec) * USEC); if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radclient"); return 1; } if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) { fr_perror("radclient"); return 1; } fr_strerror(); /* Clear the error buffer */ /* * Ensure that dictionary.dhcp is loaded. */ da = dict_attrbyname("DHCP-Message-Type"); if (!da) { if (dict_read(dict_dir, "dictionary.dhcp") < 0) { fprintf(stderr, "Failed reading dictionary.dhcp: %s\n", fr_strerror()); return -1; } } /* * Resolve hostname. */ server_ipaddr.af = AF_INET; if (strcmp(argv[1], "-") != 0) { if (fr_pton_port(&server_ipaddr, &server_port, argv[1], -1, AF_INET, true) < 0) { fprintf(stderr, "dhcpclient: Failed parsing IP:port - %s", fr_strerror()); exit(1); } client_ipaddr.af = server_ipaddr.af; } /* * See what kind of request we want to send. */ if (argc >= 3) { if (!isdigit((int) argv[2][0])) { packet_code = fr_str2int(request_types, argv[2], -2); if (packet_code == -2) { fprintf(stderr, "Unknown packet type: %s\n", argv[2]); usage(); } } else { packet_code = atoi(argv[2]); } } if (!server_port) server_port = 67; #ifdef HAVE_LINUX_IF_PACKET_H /* * set "raw mode" if an interface is specified and if destination * IP address is the broadcast address. */ if (iface) { iface_ind = if_nametoindex(iface); if (iface_ind <= 0) { fprintf(stderr, "dhcpclient: unknown interface: %s\n", iface); fr_exit_now(1); } if (server_ipaddr.ipaddr.ip4addr.s_addr == 0xFFFFFFFF) { DEBUG("dhcpclient: Using interface: %s (index: %d) in raw packet mode\n", iface, iface_ind); raw_mode = true; } } if (raw_mode) { sockfd = fr_socket_packet(iface_ind, &ll); } else #endif { sockfd = fr_socket(&client_ipaddr, server_port + 1); } if (sockfd < 0) { fprintf(stderr, "dhcpclient: socket: %s\n", fr_strerror()); fr_exit_now(1); } /* * Set option 'receive timeout' on socket. * Note: in case of a timeout, the error will be "Resource temporarily unavailable". */ if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv_timeout,sizeof(struct timeval)) == -1) { fprintf(stderr, "dhcpclient: failed setting socket timeout: %s\n", fr_syserror(errno)); fr_exit_now(1); } request = request_init(filename); if (!request || !request->vps) { fprintf(stderr, "dhcpclient: Nothing to send.\n"); fr_exit_now(1); } /* * Set defaults if they weren't specified via pairs */ if (request->src_port == 0) request->src_port = server_port + 1; if (request->dst_port == 0) request->dst_port = server_port; if (request->src_ipaddr.af == AF_UNSPEC) request->src_ipaddr = client_ipaddr; if (request->dst_ipaddr.af == AF_UNSPEC) request->dst_ipaddr = server_ipaddr; if (!request->code) request->code = packet_code; /* * Sanity check. */ if (!request->code) { fprintf(stderr, "dhcpclient: Command was %s, and request did not contain DHCP-Message-Type nor Packet-Type.\n", (argc >= 3) ? "'auto'" : "unspecified"); exit(1); } if ((request->code == PW_DHCP_RELEASE) || (request->code == PW_DHCP_DECLINE)) { /* These kind of packets do not get a reply, so don't wait for one. */ reply_expected = false; } /* * Encode the packet */ if (fr_dhcp_encode(request) < 0) { fprintf(stderr, "dhcpclient: failed encoding: %s\n", fr_strerror()); fr_exit_now(1); } if (fr_debug_lvl) print_hex(request); #ifdef HAVE_LINUX_IF_PACKET_H if (raw_mode) { if (fr_dhcp_send_raw_packet(sockfd, &ll, request) < 0) { fprintf(stderr, "dhcpclient: failed sending (fr_dhcp_send_raw_packet): %s\n", fr_syserror(errno)); fr_exit_now(1); } if (reply_expected) { reply = fr_dhcp_recv_raw_loop(sockfd, &ll, request); if (!reply) { fprintf(stderr, "dhcpclient: Error receiving reply (fr_dhcp_recv_raw_loop)\n"); fr_exit_now(1); } } } else #endif { send_with_socket(request); } dict_free(); if (success) return 0; return 1; }
/* * The main guy. */ int main(int argc, char *argv[]) { int rcode; int argval; int spawn_flag = TRUE; int dont_fork = FALSE; int flag = 0; #ifdef HAVE_SIGACTION struct sigaction act; #endif #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); return 1; } } #endif debug_flag = 0; spawn_flag = TRUE; radius_dir = strdup(RADIUS_DIR); /* * Ensure that the configuration is initialized. */ memset(&mainconfig, 0, sizeof(mainconfig)); mainconfig.myip.af = AF_UNSPEC; mainconfig.port = -1; mainconfig.name = "radiusd"; #ifdef HAVE_SIGACTION memset(&act, 0, sizeof(act)); act.sa_flags = 0 ; sigemptyset( &act.sa_mask ) ; #endif /* * Don't put output anywhere until we get told a little * more. */ mainconfig.radlog_dest = RADLOG_NULL; mainconfig.radlog_fd = -1; mainconfig.log_file = NULL; /* Process the options. */ while ((argval = getopt(argc, argv, "Cd:fhi:l:mMn:p:stvxX")) != EOF) { switch(argval) { case 'C': check_config = TRUE; spawn_flag = FALSE; dont_fork = TRUE; break; case 'd': if (radius_dir) free(radius_dir); radius_dir = strdup(optarg); break; case 'f': dont_fork = TRUE; break; case 'h': usage(0); break; case 'l': if (strcmp(optarg, "stdout") == 0) { goto do_stdout; } mainconfig.log_file = strdup(optarg); mainconfig.radlog_dest = RADLOG_FILES; mainconfig.radlog_fd = open(mainconfig.log_file, O_WRONLY | O_APPEND | O_CREAT, 0640); if (mainconfig.radlog_fd < 0) { fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, strerror(errno)); exit(1); } fr_log_fp = fdopen(mainconfig.radlog_fd, "a"); break; case 'i': if (ip_hton(optarg, AF_UNSPEC, &mainconfig.myip) < 0) { fprintf(stderr, "radiusd: Invalid IP Address or hostname \"%s\"\n", optarg); exit(1); } flag |= 1; break; case 'n': mainconfig.name = optarg; break; case 'm': mainconfig.debug_memory = 1; break; case 'M': memory_report = 1; mainconfig.debug_memory = 1; break; case 'p': mainconfig.port = atoi(optarg); if ((mainconfig.port <= 0) || (mainconfig.port >= 65536)) { fprintf(stderr, "radiusd: Invalid port number %s\n", optarg); exit(1); } flag |= 2; break; case 's': /* Single process mode */ spawn_flag = FALSE; dont_fork = TRUE; break; case 't': /* no child threads */ spawn_flag = FALSE; break; case 'v': /* Don't print timestamps */ debug_flag += 2; fr_log_fp = stdout; mainconfig.radlog_dest = RADLOG_STDOUT; mainconfig.radlog_fd = STDOUT_FILENO; version(); exit(0); case 'X': spawn_flag = FALSE; dont_fork = TRUE; debug_flag += 2; mainconfig.log_auth = TRUE; mainconfig.log_auth_badpass = TRUE; mainconfig.log_auth_goodpass = TRUE; do_stdout: fr_log_fp = stdout; mainconfig.radlog_dest = RADLOG_STDOUT; mainconfig.radlog_fd = STDOUT_FILENO; break; case 'x': debug_flag++; break; default: usage(1); break; } } if (memory_report) { talloc_enable_null_tracking(); talloc_set_log_fn(log_talloc); } /* * Mismatch between build time OpenSSL and linked SSL, * better to die here than segfault later. */ #ifdef HAVE_OPENSSL_CRYPTO_H if (ssl_check_version() < 0) { exit(1); } #endif if (flag && (flag != 0x03)) { fprintf(stderr, "radiusd: The options -i and -p cannot be used individually.\n"); exit(1); } if (debug_flag) version(); /* Read the configuration files, BEFORE doing anything else. */ if (read_mainconfig(0) < 0) { exit(1); } #ifndef __MINGW32__ /* * Disconnect from session */ if (dont_fork == FALSE) { pid_t pid = fork(); if (pid < 0) { radlog(L_ERR, "Couldn't fork: %s", strerror(errno)); exit(1); } /* * The parent exits, so the child can run in the background. */ if (pid > 0) { exit(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(); /* * If we're running as a daemon, close the default file * descriptors, AFTER forking. */ if (!debug_flag) { int devnull; devnull = open("/dev/null", O_RDWR); if (devnull < 0) { radlog(L_ERR, "Failed opening /dev/null: %s\n", strerror(errno)); exit(1); } dup2(devnull, STDIN_FILENO); if (mainconfig.radlog_dest == RADLOG_STDOUT) { setlinebuf(stdout); mainconfig.radlog_fd = STDOUT_FILENO; } else { dup2(devnull, STDOUT_FILENO); } if (mainconfig.radlog_dest == RADLOG_STDERR) { setlinebuf(stderr); mainconfig.radlog_fd = STDERR_FILENO; } else { dup2(devnull, STDERR_FILENO); } close(devnull); } else { setlinebuf(stdout); /* unbuffered output */ } /* * Now we have logging check that the OpenSSL */ /* * Initialize the event pool, including threads. */ radius_event_init(mainconfig.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 #ifdef HAVE_SIGACTION act.sa_handler = sig_hup; sigaction(SIGHUP, &act, NULL); act.sa_handler = sig_fatal; sigaction(SIGTERM, &act, NULL); #else #ifdef SIGHUP signal(SIGHUP, sig_hup); #endif signal(SIGTERM, sig_fatal); #endif /* * 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 ((mainconfig.debug_memory == 1) || (debug_flag == 0)) { #ifdef HAVE_SIGACTION act.sa_handler = sig_fatal; sigaction(SIGINT, &act, NULL); sigaction(SIGQUIT, &act, NULL); #else signal(SIGINT, sig_fatal); #ifdef SIGQUIT signal(SIGQUIT, sig_fatal); #endif #endif } /* * Everything seems to have loaded OK, exit gracefully. */ if (check_config) { DEBUG("Configuration appears to be OK."); exit(0); } #ifdef WITH_STATS radius_stats_init(0); #endif /* * Only write the PID file if we're running as a daemon. * * And write it AFTER we've forked, so that we write the * correct PID. */ if (dont_fork == FALSE) { FILE *fp; fp = fopen(mainconfig.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 { radlog(L_ERR, "Failed creating PID file %s: %s\n", mainconfig.pid_file, strerror(errno)); exit(1); } } exec_trigger(NULL, NULL, "server.start", FALSE); /* * Process requests until HUP or exit. */ while ((rcode = radius_event_process()) == 0x80) { #ifdef WITH_STATS radius_stats_init(1); #endif hup_mainconfig(); } if (rcode < 0) { radlog(L_ERR, "Exiting due to internal error: %s", fr_strerror()); rcode = 2; } else { radlog(L_INFO, "Exiting normally."); } exec_trigger(NULL, NULL, "server.stop", FALSE); /* * Ignore the TERM signal: we're * about to die. */ signal(SIGTERM, SIG_IGN); /* * 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 (dont_fork == FALSE) { unlink(mainconfig.pid_file); } radius_event_free(); /* * Detach any modules. */ detach_modules(); xlat_free(); /* modules may have xlat's */ /* * Free the configuration items. */ free_mainconfig(); free(radius_dir); #ifdef WIN32 WSACleanup(); #endif if (memory_report) { log_talloc_report(NULL); } return (rcode - 1); }
static int rlm_sql_instantiate(CONF_SECTION *conf, void **instance) { rlm_sql_t *inst; const char *xlat_name; *instance = inst = talloc_zero(conf, rlm_sql_t); if (!inst) return -1; /* * Cache the SQL-User-Name DICT_ATTR, so we can be slightly * more efficient about creating SQL-User-Name attributes. */ inst->sql_user = dict_attrbyname("SQL-User-Name"); if (!inst->sql_user) { return -1; } /* * Export these methods, too. This avoids RTDL_GLOBAL. */ inst->sql_set_user = sql_set_user; inst->sql_get_socket = sql_get_socket; inst->sql_release_socket = sql_release_socket; inst->sql_escape_func = sql_escape_func; inst->sql_query = rlm_sql_query; inst->sql_select_query = rlm_sql_select_query; inst->sql_fetch_row = rlm_sql_fetch_row; inst->config = talloc_zero(inst, rlm_sql_config_t); inst->cs = conf; xlat_name = cf_section_name2(conf); if (xlat_name == NULL) { xlat_name = cf_section_name1(conf); } else { char *group_name; const DICT_ATTR *dattr; ATTR_FLAGS flags; /* * Allocate room for <instance>-SQL-Group */ group_name = talloc_asprintf(inst, "%s-SQL-Group", xlat_name); DEBUG("rlm_sql (%s): Creating new attribute %s", xlat_name, group_name); memset(&flags, 0, sizeof(flags)); if (dict_addattr(group_name, -1, 0, PW_TYPE_STRING, flags) < 0) { radlog(L_ERR, "rlm_sql (%s): Failed to create " "attribute %s: %s", xlat_name, group_name, fr_strerror()); return -1; } dattr = dict_attrbyname(group_name); if (!dattr) { radlog(L_ERR, "rlm_sql (%s): Failed to create " "attribute %s", xlat_name, group_name); return -1; } if (inst->config->groupmemb_query && inst->config->groupmemb_query[0]) { DEBUG("rlm_sql (%s): Registering sql_groupcmp for %s", xlat_name, group_name); paircompare_register(dattr->attr, PW_USER_NAME, sql_groupcmp, inst); } } rad_assert(xlat_name); /* * Register the SQL xlat function */ inst->config->xlat_name = talloc_strdup(inst->config, xlat_name); xlat_register(xlat_name, sql_xlat, inst); /* * If the configuration parameters can't be parsed, then fail. */ if ((cf_section_parse(conf, inst->config, module_config) < 0) || (parse_sub_section(conf, inst, &inst->config->accounting, RLM_COMPONENT_ACCT) < 0) || (parse_sub_section(conf, inst, &inst->config->postauth, RLM_COMPONENT_POST_AUTH) < 0)) { radlog(L_ERR, "rlm_sql (%s): Failed parsing configuration", inst->config->xlat_name); return -1; } /* * Sanity check for crazy people. */ if (strncmp(inst->config->sql_driver_name, "rlm_sql_", 8) != 0) { radlog(L_ERR, "rlm_sql (%s): \"%s\" is NOT an SQL driver!", inst->config->xlat_name, inst->config->sql_driver_name); return -1; } /* * Load the appropriate driver for our database */ inst->handle = lt_dlopenext(inst->config->sql_driver_name); if (inst->handle == NULL) { radlog(L_ERR, "Could not link driver %s: %s", inst->config->sql_driver_name, lt_dlerror()); radlog(L_ERR, "Make sure it (and all its dependent libraries!)" "are in the search path of your system's ld."); return -1; } inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver_name); if (!inst->module) { radlog(L_ERR, "Could not link symbol %s: %s", inst->config->sql_driver_name, lt_dlerror()); return -1; } if (inst->module->sql_instantiate) { CONF_SECTION *cs; const char *name; name = strrchr(inst->config->sql_driver_name, '_'); if (!name) { name = inst->config->sql_driver_name; } else { name++; } cs = cf_section_sub_find(conf, name); if (!cs) { cs = cf_section_alloc(conf, name, NULL); if (!cs) { return -1; } } /* * It's up to the driver to register a destructor */ if (inst->module->sql_instantiate(cs, inst->config) < 0) { return -1; } } radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked", inst->config->xlat_name, inst->config->sql_driver_name, inst->module->name); /* * Initialise the connection pool for this instance */ radlog(L_INFO, "rlm_sql (%s): Attempting to connect to database \"%s\"", inst->config->xlat_name, inst->config->sql_db); if (sql_socket_pool_init(inst) < 0) return -1; if (inst->config->groupmemb_query && inst->config->groupmemb_query[0]) { paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst); } if (inst->config->do_clients) { if (generate_sql_clients(inst) == -1){ radlog(L_ERR, "Failed to load clients from SQL."); return -1; } } return RLM_MODULE_OK; }
/* * Read config files. * * This function can ONLY be called from the main server process. */ int read_mainconfig(int reload) { const char *p = NULL; CONF_PAIR *cp; CONF_SECTION *cs; struct stat statbuf; cached_config_t *cc; char buffer[1024]; if (reload != 0) { radlog(L_ERR, "Reload is not implemented"); return -1; } if (stat(radius_dir, &statbuf) < 0) { radlog(L_ERR, "Errors reading %s: %s", radius_dir, strerror(errno)); return -1; } #ifdef S_IWOTH if ((statbuf.st_mode & S_IWOTH) != 0) { radlog(L_ERR, "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) { radlog(L_ERR, "Configuration directory %s is globally readable. Refusing to start due to insecure configuration.", radius_dir); return -1; } #endif radlog(L_INFO, "Starting - reading configuration files ..."); /* Read the configuration file */ snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf", radius_dir, mainconfig.name); if ((cs = cf_file_read(buffer)) == NULL) { radlog(L_ERR, "Errors reading %s", buffer); return -1; } /* * If there was no log destination set on the command line, * set it now. */ if (mainconfig.radlog_dest == RADLOG_NULL) { if (cf_section_parse(cs, NULL, serverdest_config) < 0) { fprintf(stderr, "radiusd: Error: Failed to parse log{} section.\n"); cf_section_free(&cs); return -1; } if (!radlog_dest) { fprintf(stderr, "radiusd: Error: No log destination specified.\n"); cf_section_free(&cs); return -1; } mainconfig.radlog_dest = fr_str2int(str2dest, radlog_dest, RADLOG_NUM_DEST); if (mainconfig.radlog_dest == RADLOG_NUM_DEST) { fprintf(stderr, "radiusd: Error: Unknown log_destination %s\n", radlog_dest); cf_section_free(&cs); return -1; } if (mainconfig.radlog_dest == RADLOG_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_section_free(&cs); return -1; } mainconfig.syslog_facility = fr_str2int(str2fac, syslog_facility, -1); if (mainconfig.syslog_facility < 0) { fprintf(stderr, "radiusd: Error: Unknown syslog_facility %s\n", syslog_facility); cf_section_free(&cs); return -1; } /* * Call openlog only once, when the * program starts. */ openlog(progname, LOG_PID, mainconfig.syslog_facility); } else if (mainconfig.radlog_dest == RADLOG_FILES) { if (!mainconfig.log_file) { fprintf(stderr, "radiusd: Error: Specified \"files\" as a log destination, but no log filename was given!\n"); cf_section_free(&cs); return -1; } } } #ifdef HAVE_SETUID /* * Switch users as early as possible. */ if (!switch_users(cs)) 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 ((mainconfig.radlog_dest == RADLOG_FILES) && (mainconfig.radlog_fd < 0)) { mainconfig.radlog_fd = open(mainconfig.log_file, O_WRONLY | O_APPEND | O_CREAT, 0640); if (mainconfig.radlog_fd < 0) { fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, strerror(errno)); cf_section_free(&cs); return -1; } } /* Initialize the dictionary */ cp = cf_pair_find(cs, "dictionary"); if (cp) p = cf_pair_value(cp); if (!p) p = radius_dir; DEBUG2("including dictionary file %s/%s", p, RADIUS_DICTIONARY); if (dict_init(p, RADIUS_DICTIONARY) != 0) { radlog(L_ERR, "Errors reading dictionary: %s", fr_strerror()); return -1; } /* * This allows us to figure out where, relative to * radiusd.conf, the other configuration files exist. */ cf_section_parse(cs, NULL, server_config); /* * 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. */ cf_section_free(&mainconfig.config); mainconfig.config = cs; DEBUG2("%s: #### Loading Realms and Home Servers ####", mainconfig.name); if (!realms_init(cs)) { return -1; } DEBUG2("%s: #### Loading Clients ####", mainconfig.name); if (!clients_parse_section(cs)) { return -1; } /* * Register the %{config:section.subsection} xlat function. */ xlat_register("config", xlat_config, NULL); xlat_register("client", xlat_client, NULL); /* * Starting the server, WITHOUT "-x" on the * command-line: use whatever is in the config * file. */ if (debug_flag == 0) { debug_flag = mainconfig.debug_level; } fr_debug_flag = debug_flag; /* * Go update our behaviour, based on the configuration * changes. */ /* * Sanity check the configuration for internal * consistency. */ if (mainconfig.reject_delay > mainconfig.cleanup_delay) { mainconfig.reject_delay = mainconfig.cleanup_delay; } if (mainconfig.reject_delay < 0) mainconfig.reject_delay = 0; /* Reload the modules. */ if (setup_modules(reload, mainconfig.config) < 0) { return -1; } if (chroot_dir) { if (chdir(radlog_dir) < 0) { radlog(L_ERR, "Failed to 'chdir %s' after chroot: %s", radlog_dir, strerror(errno)); return -1; } } cc = rad_malloc(sizeof(*cc)); memset(cc, 0, sizeof(*cc)); cc->cs = cs; rad_assert(cs_cache == NULL); cs_cache = cc; return 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; }
/** Instantiate the module * * Creates a new instance of the module reading parameters from a configuration section. * * @param conf to parse. * @param instance Where to write pointer to configuration data. * @return 0 on success < 0 on failure. */ static int mod_instantiate(CONF_SECTION *conf, void *instance) { ldap_instance_t *inst = instance; inst->cs = conf; inst->chase_referrals = 2; /* use OpenLDAP defaults */ inst->rebind = 2; inst->xlat_name = cf_section_name2(conf); if (!inst->xlat_name) { inst->xlat_name = cf_section_name1(conf); } /* * If the configuration parameters can't be parsed, then fail. */ if ((parse_sub_section(inst, conf, &inst->accounting, RLM_COMPONENT_ACCT) < 0) || (parse_sub_section(inst, conf, &inst->postauth, RLM_COMPONENT_POST_AUTH) < 0)) { LDAP_ERR("Failed parsing configuration"); goto error; } /* * Sanity checks for cacheable groups code. */ if (inst->cacheable_group_name && inst->groupobj_membership_filter) { if (!inst->groupobj_name_attr) { LDAP_ERR("Directive 'group.name_attribute' must be set if cacheable group names are enabled"); goto error; } } if (inst->cacheable_group_name || inst->cacheable_group_dn) { if (!inst->groupobj_base_dn) { LDAP_ERR("Directive 'group.base_dn' must be set if cacheable group names are enabled"); goto error; } } /* * Check for URLs. If they're used and the library doesn't support them, then complain. */ inst->is_url = 0; if (ldap_is_ldap_url(inst->server)) { #ifdef HAVE_LDAP_INITIALIZE inst->is_url = 1; inst->port = 0; #else LDAP_ERR("Directive 'server' is in URL form but ldap_initialize() is not available"); goto error; #endif } /* * Workaround for servers which support LDAPS but not START TLS */ if (inst->port == LDAPS_PORT || inst->tls_mode) { inst->tls_mode = LDAP_OPT_X_TLS_HARD; } else { inst->tls_mode = 0; } #if LDAP_SET_REBIND_PROC_ARGS != 3 /* * The 2-argument rebind doesn't take an instance variable. Our rebind function needs the instance * variable for the username, password, etc. */ if (inst->rebind == true) { LDAP_ERR("Cannot use 'rebind' directive as this version of libldap does not support the API " "that we need"); goto error; } #endif /* * Convert scope strings to enumerated constants */ inst->userobj_scope = fr_str2int(ldap_scope, inst->userobj_scope_str, -1); if (inst->userobj_scope < 0) { LDAP_ERR("Invalid 'user.scope' value \"%s\", expected 'sub', 'one', 'base' or 'children'", inst->userobj_scope_str); goto error; } inst->groupobj_scope = fr_str2int(ldap_scope, inst->groupobj_scope_str, -1); if (inst->groupobj_scope < 0) { LDAP_ERR("Invalid 'group.scope' value \"%s\", expected 'sub', 'one', 'base' or 'children'", inst->groupobj_scope_str); goto error; } inst->clientobj_scope = fr_str2int(ldap_scope, inst->clientobj_scope_str, -1); if (inst->clientobj_scope < 0) { LDAP_ERR("Invalid 'client.scope' value \"%s\", expected 'sub', 'one', 'base' or 'children'", inst->clientobj_scope_str); goto error; } if (inst->tls_require_cert_str) { #ifdef LDAP_OPT_X_TLS_NEVER /* * Convert cert strictness to enumerated constants */ inst->tls_require_cert = fr_str2int(ldap_tls_require_cert, inst->tls_require_cert_str, -1); if (inst->tls_require_cert < 0) { LDAP_ERR("Invalid 'tls.require_cert' value \"%s\", expected 'never', 'demand', 'allow', " "'try' or 'hard'", inst->tls_require_cert_str); goto error; } #else LDAP_ERR("Modifying 'tls.require_cert' is not supported by current version of libldap. " "Please upgrade libldap and rebuild this module"); goto error; #endif } /* * Build the attribute map */ if (rlm_ldap_map_verify(inst, &(inst->user_map)) < 0) { goto error; } /* * Group comparison checks. */ if (cf_section_name2(conf)) { ATTR_FLAGS flags; char buffer[256]; snprintf(buffer, sizeof(buffer), "%s-Ldap-Group", inst->xlat_name); memset(&flags, 0, sizeof(flags)); if (dict_addattr(buffer, -1, 0, PW_TYPE_STRING, flags) < 0) { LDAP_ERR("Error creating group attribute: %s", fr_strerror()); return -1; } inst->group_da = dict_attrbyname(buffer); if (!inst->group_da) { LDAP_ERR("Failed creating attribute %s", buffer); goto error; } paircompare_register(inst->group_da, dict_attrbyvalue(PW_USER_NAME, 0), false, rlm_ldap_groupcmp, inst); /* * Were the default instance */ } else { inst->group_da = dict_attrbyvalue(PW_LDAP_GROUP, 0); paircompare_register(dict_attrbyvalue(PW_LDAP_GROUP, 0), dict_attrbyvalue(PW_USER_NAME, 0), false, rlm_ldap_groupcmp, inst); } xlat_register(inst->xlat_name, ldap_xlat, rlm_ldap_escape_func, inst); /* * Setup the cache attribute */ if (inst->cache_attribute) { ATTR_FLAGS flags; memset(&flags, 0, sizeof(flags)); if (dict_addattr(inst->cache_attribute, -1, 0, PW_TYPE_STRING, flags) < 0) { LDAP_ERR("Error creating cache attribute: %s", fr_strerror()); return -1; } inst->cache_da = dict_attrbyname(inst->cache_attribute); } else { inst->cache_da = inst->group_da; /* Default to the group_da */ } /* * Initialize the socket pool. */ inst->pool = fr_connection_pool_init(inst->cs, inst, mod_conn_create, NULL, mod_conn_delete, NULL); if (!inst->pool) { return -1; } /* * Bulk load dynamic clients. */ if (inst->do_clients) { if (rlm_ldap_load_clients(inst) < 0) { LDAP_ERR("Error loading clients"); return -1; } } return 0; error: return -1; }
/* * Do chroot, if requested. * * Switch UID and GID to what is specified in the config file */ static int switch_users(CONF_SECTION *cs) { bool do_suid = false; bool do_sgid = false; /* * Get the current maximum for core files. Do this * before anything else so as to ensure it's properly * initialized. */ if (fr_set_dumpable_init() < 0) { fr_perror("%s", main_config.name); return 0; } /* * Don't do chroot/setuid/setgid if we're in debugging * as non-root. */ if (rad_debug_lvl && (getuid() != 0)) return 1; if (cf_section_parse(cs, NULL, bootstrap_config) < 0) { fprintf(stderr, "%s: Error: Failed to parse user/group information.\n", main_config.name); return 0; } #ifdef HAVE_GRP_H /* * Get the correct GID for the server. */ server_gid = getgid(); if (gid_name) { struct group *gr; gr = getgrnam(gid_name); if (!gr) { fprintf(stderr, "%s: Cannot get ID for group %s: %s\n", main_config.name, gid_name, fr_syserror(errno)); return 0; } if (server_gid != gr->gr_gid) { server_gid = gr->gr_gid; do_sgid = true; } } #endif /* * Get the correct UID for the server. */ server_uid = getuid(); if (uid_name) { struct passwd *user; if (rad_getpwnam(cs, &user, uid_name) < 0) { fprintf(stderr, "%s: Cannot get passwd entry for user %s: %s\n", main_config.name, uid_name, fr_strerror()); return 0; } /* * We're not the correct user. Go set that. */ if (server_uid != user->pw_uid) { server_uid = user->pw_uid; do_suid = true; #ifdef HAVE_INITGROUPS if (initgroups(uid_name, server_gid) < 0) { fprintf(stderr, "%s: Cannot initialize supplementary group list for user %s: %s\n", main_config.name, uid_name, fr_syserror(errno)); talloc_free(user); return 0; } #endif } talloc_free(user); } /* * Do chroot BEFORE changing UIDs. */ if (chroot_dir) { if (chroot(chroot_dir) < 0) { fprintf(stderr, "%s: Failed to perform chroot %s: %s", main_config.name, chroot_dir, fr_syserror(errno)); return 0; } /* * Note that we leave chdir alone. It may be * OUTSIDE of the root. This allows us to read * the configuration from "-d ./etc/raddb", with * the chroot as "./chroot/" for example. After * the server has been loaded, it does a "cd * ${logdir}" below, so that core files (if any) * go to a logging directory. * * This also allows the configuration of the * server to be outside of the chroot. If the * server is statically linked, then the only * things needed inside of the chroot are the * logging directories. */ } #ifdef HAVE_GRP_H /* * Set the GID. Don't bother checking it. */ if (do_sgid) { if (setgid(server_gid) < 0){ fprintf(stderr, "%s: Failed setting group to %s: %s", main_config.name, gid_name, fr_syserror(errno)); return 0; } } #endif /* * The directories for PID files and logs must exist. We * need to create them if we're told to write files to * those directories. * * Because this creation is new in 3.0.9, it's a soft * fail. * */ if (main_config.write_pid) { char *my_dir; my_dir = talloc_strdup(NULL, run_dir); if (rad_mkdir(my_dir, 0750, server_uid, server_gid) < 0) { DEBUG("Failed to create run_dir %s: %s", my_dir, strerror(errno)); } talloc_free(my_dir); } if (default_log.dst == L_DST_FILES) { char *my_dir; my_dir = talloc_strdup(NULL, radlog_dir); if (rad_mkdir(my_dir, 0750, server_uid, server_gid) < 0) { DEBUG("Failed to create logdir %s: %s", my_dir, strerror(errno)); } talloc_free(my_dir); } /* * If we don't already have a log file open, open one * now. We may not have been logging anything yet. The * server normally starts up fairly quietly. */ 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, "%s: Failed to open log file %s: %s\n", main_config.name, main_config.log_file, fr_syserror(errno)); return 0; } } /* * If we need to change UID, ensure that the log files * have the correct owner && group. * * We have to do this because some log files MAY already * have been written as root. We need to change them to * have the correct ownership before proceeding. */ if ((do_suid || do_sgid) && (default_log.dst == L_DST_FILES)) { if (fchown(default_log.fd, server_uid, server_gid) < 0) { fprintf(stderr, "%s: Cannot change ownership of log file %s: %s\n", main_config.name, main_config.log_file, fr_syserror(errno)); return 0; } } /* * Once we're done with all of the privileged work, * permanently change the UID. */ if (do_suid) { rad_suid_set_down_uid(server_uid); rad_suid_down(); } /* * This also clears the dumpable flag if core dumps * aren't allowed. */ if (fr_set_dumpable(allow_core_dumps) < 0) { ERROR("%s", fr_strerror()); } if (allow_core_dumps) { INFO("Core dumps are enabled"); } return 1; }
static void *mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout) { linelog_instance_t *inst = instance; linelog_conn_t *conn; int sockfd = -1; switch (inst->log_dst) { case LINELOG_DST_UNIX: DEBUG2("rlm_linelog (%s): Opening UNIX socket at \"%s\"", inst->name, inst->unix.path); sockfd = fr_socket_client_unix(inst->unix.path, true); if (sockfd < 0) { ERROR("rlm_linelog (%s): Failed opening UNIX socket: %s", inst->name, fr_strerror()); return NULL; } break; case LINELOG_DST_TCP: if (DEBUG_ENABLED2) { char buff[INET6_ADDRSTRLEN + 4]; /* IPv6 + /<d><d><d> */ fr_ntop(buff, sizeof(buff), &inst->tcp.dst_ipaddr); DEBUG2("rlm_linelog (%s): Opening TCP connection to %s:%u", inst->name, buff, inst->tcp.port); } sockfd = fr_socket_client_tcp(NULL, &inst->tcp.dst_ipaddr, inst->tcp.port, true); if (sockfd < 0) { ERROR("rlm_linelog (%s): Failed opening TCP socket: %s", inst->name, fr_strerror()); return NULL; } break; case LINELOG_DST_UDP: if (DEBUG_ENABLED2) { char buff[INET6_ADDRSTRLEN + 4]; /* IPv6 + /<d><d><d> */ fr_ntop(buff, sizeof(buff), &inst->udp.dst_ipaddr); DEBUG2("rlm_linelog (%s): Opening UDP connection to %s:%u", inst->name, buff, inst->udp.port); } sockfd = fr_socket_client_udp(NULL, &inst->udp.dst_ipaddr, inst->udp.port, true); if (sockfd < 0) { ERROR("rlm_linelog (%s): Failed opening UDP socket: %s", inst->name, fr_strerror()); return NULL; } break; /* * Are not connection oriented destinations */ case LINELOG_DST_INVALID: case LINELOG_DST_FILE: case LINELOG_DST_SYSLOG: rad_assert(0); return NULL; } if (errno == EINPROGRESS) { if (FR_TIMEVAL_TO_MS(timeout)) { DEBUG2("rlm_linelog (%s): Waiting for connection to complete...", inst->name); } else { DEBUG2("rlm_linelog (%s): Blocking until connection complete...", inst->name); } if (fr_socket_wait_for_connect(sockfd, timeout) < 0) { ERROR("rlm_linelog (%s): %s", inst->name, fr_strerror()); close(sockfd); return NULL; } } DEBUG2("rlm_linelog (%s): Connection successful", inst->name); /* * Set blocking operation as we have no timeout set */ if (!FR_TIMEVAL_TO_MS(timeout) && (fr_blocking(sockfd) < 0)) { ERROR("rlm_linelog (%s): Failed setting nonblock flag on fd", inst->name); close(sockfd); return NULL; } conn = talloc_zero(ctx, linelog_conn_t); conn->sockfd = sockfd; talloc_set_destructor(conn, _mod_conn_free); return conn; }
static rlm_rcode_t mod_do_linelog(void *instance, REQUEST *request) { int fd = -1; linelog_conn_t *conn; struct timeval *timeout = NULL; char buff[4096]; char *p = buff; linelog_instance_t *inst = instance; char const *value; vp_tmpl_t empty, *vpt = NULL, *vpt_p = NULL; rlm_rcode_t rcode = RLM_MODULE_OK; ssize_t slen; struct iovec vector_s[2]; struct iovec *vector = NULL, *vector_p; size_t vector_len; bool with_delim; buff[0] = '.'; /* force to be in current section */ buff[1] = '\0'; buff[2] = '\0'; /* * Expand log_ref to a config path, using the module * configuration section as the root. */ if (inst->log_ref) { CONF_ITEM *ci; CONF_PAIR *cp; char const *tmpl_str; if (tmpl_expand(NULL, buff + 1, sizeof(buff) - 1, request, inst->log_ref, linelog_escape_func, NULL) < 0) { return RLM_MODULE_FAIL; } if (buff[1] == '.') p++; /* * Don't go back up. */ if (buff[2] == '.') { REDEBUG("Invalid path \"%s\"", p); return RLM_MODULE_FAIL; } ci = cf_reference_item(NULL, inst->cs, p); if (!ci) { RDEBUG2("Path \"%s\" doesn't exist", p); goto default_msg; } if (!cf_item_is_pair(ci)) { REDEBUG("Path \"%s\" resolves to a section (should be a pair)", p); return RLM_MODULE_FAIL; } cp = cf_item_to_pair(ci); tmpl_str = cf_pair_value(cp); if (!tmpl_str || (tmpl_str[0] == '\0')) { RDEBUG2("Path \"%s\" resolves to an empty config pair", p); vpt_p = tmpl_init(&empty, TMPL_TYPE_LITERAL, "", 0); goto build_vector; } /* * Alloc a template from the value of the CONF_PAIR * using request as the context (which will hopefully avoid a malloc). */ slen = tmpl_afrom_str(request, &vpt, tmpl_str, talloc_array_length(tmpl_str) - 1, cf_pair_value_type(cp), REQUEST_CURRENT, PAIR_LIST_REQUEST, true); if (slen <= 0) { REMARKER(tmpl_str, -slen, fr_strerror()); return RLM_MODULE_FAIL; } vpt_p = vpt; } else { default_msg: /* * Use the default format string */ if (!inst->log_src) { RDEBUG2("No default message configured"); return RLM_MODULE_NOOP; } /* * Use the pre-parsed format template */ RDEBUG2("Using default message"); vpt_p = inst->log_src; } build_vector: with_delim = (inst->log_dst != LINELOG_DST_SYSLOG) && (inst->delimiter_len > 0); /* * Log all the things! */ switch (vpt_p->type) { case TMPL_TYPE_ATTR: case TMPL_TYPE_LIST: { #define VECTOR_INCREMENT 20 vp_cursor_t cursor; VALUE_PAIR *vp; int alloced = VECTOR_INCREMENT, i; MEM(vector = talloc_array(request, struct iovec, alloced)); for (vp = tmpl_cursor_init(NULL, &cursor, request, vpt_p), i = 0; vp; vp = tmpl_cursor_next(&cursor, vpt_p), i++) { /* need extra for line terminator */ if ((with_delim && ((i + 1) >= alloced)) || (i >= alloced)) { alloced += VECTOR_INCREMENT; MEM(vector = talloc_realloc(request, vector, struct iovec, alloced)); } switch (vp->da->type) { case PW_TYPE_OCTETS: case PW_TYPE_STRING: vector[i].iov_base = vp->data.ptr; vector[i].iov_len = vp->vp_length; break; default: p = vp_aprints_value(vector, vp, '\0'); vector[i].iov_base = p; vector[i].iov_len = talloc_array_length(p) - 1; break; } /* * Add the line delimiter string */ if (with_delim) { i++; memcpy(&vector[i].iov_base, &(inst->delimiter), sizeof(vector[i].iov_base)); vector[i].iov_len = inst->delimiter_len; } } vector_p = vector; vector_len = i; } break; /* * Log a single thing. */ default: slen = tmpl_expand(&value, buff, sizeof(buff), request, vpt_p, linelog_escape_func, NULL); if (slen < 0) { rcode = RLM_MODULE_FAIL; goto finish; } /* iov_base is not declared as const *sigh* */ memcpy(&vector_s[0].iov_base, &value, sizeof(vector_s[0].iov_base)); vector_s[0].iov_len = slen; if (!with_delim) { vector_len = 1; } else { memcpy(&vector_s[1].iov_base, &(inst->delimiter), sizeof(vector_s[1].iov_base)); vector_s[1].iov_len = inst->delimiter_len; vector_len = 2; } vector_p = &vector_s[0]; }
static int mod_instantiate(CONF_SECTION *conf, void *instance) { rlm_sql_t *inst = instance; /* * Hack... */ inst->config = &inst->myconfig; inst->cs = conf; inst->config->xlat_name = cf_section_name2(conf); if (!inst->config->xlat_name) { inst->config->xlat_name = cf_section_name1(conf); } else { char *group_name; DICT_ATTR const *da; ATTR_FLAGS flags; /* * Allocate room for <instance>-SQL-Group */ group_name = talloc_typed_asprintf(inst, "%s-SQL-Group", inst->config->xlat_name); DEBUG("rlm_sql (%s): Creating new attribute %s", inst->config->xlat_name, group_name); memset(&flags, 0, sizeof(flags)); if (dict_addattr(group_name, -1, 0, PW_TYPE_STRING, flags) < 0) { ERROR("rlm_sql (%s): Failed to create " "attribute %s: %s", inst->config->xlat_name, group_name, fr_strerror()); return -1; } da = dict_attrbyname(group_name); if (!da) { ERROR("rlm_sql (%s): Failed to create " "attribute %s", inst->config->xlat_name, group_name); return -1; } if (inst->config->groupmemb_query && inst->config->groupmemb_query[0]) { DEBUG("rlm_sql (%s): Registering sql_groupcmp for %s", inst->config->xlat_name, group_name); paircompare_register(da, dict_attrbyvalue(PW_USER_NAME, 0), false, sql_groupcmp, inst); } } rad_assert(inst->config->xlat_name); /* * This will always exist, as cf_section_parse_init() * will create it if it doesn't exist. However, the * "reference" config item won't exist in an auto-created * configuration. So if that doesn't exist, we ignore * the whole subsection. */ inst->config->accounting.cs = cf_section_sub_find(conf, "accounting"); inst->config->accounting.reference_cp = (cf_pair_find(inst->config->accounting.cs, "reference") != NULL); inst->config->postauth.cs = cf_section_sub_find(conf, "post-auth"); inst->config->postauth.reference_cp = (cf_pair_find(inst->config->postauth.cs, "reference") != NULL); /* * Cache the SQL-User-Name DICT_ATTR, so we can be slightly * more efficient about creating SQL-User-Name attributes. */ inst->sql_user = dict_attrbyname("SQL-User-Name"); if (!inst->sql_user) { return -1; } /* * Export these methods, too. This avoids RTDL_GLOBAL. */ inst->sql_set_user = sql_set_user; inst->sql_get_socket = sql_get_socket; inst->sql_release_socket = sql_release_socket; inst->sql_escape_func = sql_escape_func; inst->sql_query = rlm_sql_query; inst->sql_select_query = rlm_sql_select_query; inst->sql_fetch_row = rlm_sql_fetch_row; /* * Register the SQL xlat function */ xlat_register(inst->config->xlat_name, sql_xlat, sql_escape_func, inst); /* * Sanity check for crazy people. */ if (strncmp(inst->config->sql_driver_name, "rlm_sql_", 8) != 0) { ERROR("rlm_sql (%s): \"%s\" is NOT an SQL driver!", inst->config->xlat_name, inst->config->sql_driver_name); return -1; } /* * Load the appropriate driver for our database */ inst->handle = lt_dlopenext(inst->config->sql_driver_name); if (!inst->handle) { ERROR("Could not link driver %s: %s", inst->config->sql_driver_name, dlerror()); ERROR("Make sure it (and all its dependent libraries!)" "are in the search path of your system's ld"); return -1; } inst->module = (rlm_sql_module_t *) dlsym(inst->handle, inst->config->sql_driver_name); if (!inst->module) { ERROR("Could not link symbol %s: %s", inst->config->sql_driver_name, dlerror()); return -1; } if (inst->module->mod_instantiate) { CONF_SECTION *cs; char const *name; name = strrchr(inst->config->sql_driver_name, '_'); if (!name) { name = inst->config->sql_driver_name; } else { name++; } cs = cf_section_sub_find(conf, name); if (!cs) { cs = cf_section_alloc(conf, name, NULL); if (!cs) { return -1; } } /* * It's up to the driver to register a destructor */ if (inst->module->mod_instantiate(cs, inst->config) < 0) { return -1; } } inst->lf = fr_logfile_init(inst); if (!inst->lf) { cf_log_err_cs(conf, "Failed creating log file context"); return -1; } INFO("rlm_sql (%s): Driver %s (module %s) loaded and linked", inst->config->xlat_name, inst->config->sql_driver_name, inst->module->name); /* * Initialise the connection pool for this instance */ INFO("rlm_sql (%s): Attempting to connect to database \"%s\"", inst->config->xlat_name, inst->config->sql_db); if (sql_socket_pool_init(inst) < 0) return -1; if (inst->config->groupmemb_query && inst->config->groupmemb_query[0]) { paircompare_register(dict_attrbyvalue(PW_SQL_GROUP, 0), dict_attrbyvalue(PW_USER_NAME, 0), false, sql_groupcmp, inst); } if (inst->config->do_clients) { if (generate_sql_clients(inst) == -1){ ERROR("Failed to load clients from SQL"); return -1; } } return RLM_MODULE_OK; }
/* * 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; }
/* * Common code called by everything below. */ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const *filename, fr_hash_table_t *ht, VALUE_PAIR *request_pairs, VALUE_PAIR **reply_pairs) { char const *name, *match; VALUE_PAIR *check_tmp; VALUE_PAIR *reply_tmp; PAIR_LIST const *user_pl, *default_pl; int found = 0; PAIR_LIST my_pl; char buffer[256]; if (!inst->key) { VALUE_PAIR *namepair; namepair = request->username; name = namepair ? namepair->vp_strvalue : "NONE"; } else { int len; len = radius_xlat(buffer, sizeof(buffer), request, inst->key, NULL, NULL); if (len < 0) { return RLM_MODULE_FAIL; } name = len ? buffer : "NONE"; } if (!ht) return RLM_MODULE_NOOP; my_pl.name = name; user_pl = fr_hash_table_finddata(ht, &my_pl); my_pl.name = "DEFAULT"; default_pl = fr_hash_table_finddata(ht, &my_pl); /* * Find the entry for the user. */ while (user_pl || default_pl) { vp_cursor_t cursor; VALUE_PAIR *vp; PAIR_LIST const *pl; if (!default_pl && user_pl) { pl = user_pl; match = name; user_pl = user_pl->next; } else if (!user_pl && default_pl) { pl = default_pl; match = "DEFAULT"; default_pl = default_pl->next; } else if (user_pl->order < default_pl->order) { pl = user_pl; match = name; user_pl = user_pl->next; } else { pl = default_pl; match = "DEFAULT"; default_pl = default_pl->next; } check_tmp = paircopy(request, pl->check); for (vp = fr_cursor_init(&cursor, &check_tmp); vp; vp = fr_cursor_next(&cursor)) { if (radius_xlat_do(request, vp) < 0) { RWARN("Failed parsing expanded value for check item, skipping entry: %s", fr_strerror()); pairfree(&check_tmp); continue; } } if (paircompare(request, request_pairs, pl->check, reply_pairs) == 0) { RDEBUG2("%s: Matched entry %s at line %d", filename, match, pl->lineno); found = 1; /* ctx may be reply or proxy */ reply_tmp = paircopy(request, pl->reply); radius_xlat_move(request, reply_pairs, &reply_tmp); pairmove(request, &request->config_items, &check_tmp); /* Cleanup any unmoved valuepairs */ pairfree(&reply_tmp); pairfree(&check_tmp); /* * Fallthrough? */ if (!fallthrough(pl->reply)) break; } } /* * Remove server internal parameters. */ pairdelete(reply_pairs, PW_FALL_THROUGH, 0, TAG_ANY); /* * See if we succeeded. */ if (!found) return RLM_MODULE_NOOP; /* on to the next module */ return RLM_MODULE_OK; }
/** Callback for map_to_request * * Performs exactly the same job as map_to_vp, but pulls attribute values from LDAP entries * * @see map_to_vp */ int rlm_ldap_map_getvalue(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx) { rlm_ldap_result_t *self = uctx; VALUE_PAIR *head = NULL, *vp; vp_cursor_t cursor; int i; fr_cursor_init(&cursor, &head); switch (map->lhs->type) { /* * This is a mapping in the form of: * <list>: += <ldap attr> * * Where <ldap attr> is: * <list>:<attr> <op> <value> * * It is to allow for legacy installations which stored * RADIUS control and reply attributes in separate LDAP * attributes. */ case TMPL_TYPE_LIST: for (i = 0; i < self->count; i++) { vp_map_t *attr = NULL; RDEBUG3("Parsing valuepair string \"%s\"", self->values[i]->bv_val); if (map_afrom_attr_str(ctx, &attr, self->values[i]->bv_val, map->lhs->tmpl_request, map->lhs->tmpl_list, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { RWDEBUG("Failed parsing \"%s\" as valuepair (%s), skipping...", fr_strerror(), self->values[i]->bv_val); continue; } if (attr->lhs->tmpl_request != map->lhs->tmpl_request) { RWDEBUG("valuepair \"%s\" has conflicting request qualifier (%s vs %s), skipping...", self->values[i]->bv_val, fr_int2str(request_refs, attr->lhs->tmpl_request, "<INVALID>"), fr_int2str(request_refs, map->lhs->tmpl_request, "<INVALID>")); next_pair: talloc_free(attr); continue; } if ((attr->lhs->tmpl_list != map->lhs->tmpl_list)) { RWDEBUG("valuepair \"%s\" has conflicting list qualifier (%s vs %s), skipping...", self->values[i]->bv_val, fr_int2str(pair_lists, attr->lhs->tmpl_list, "<INVALID>"), fr_int2str(pair_lists, map->lhs->tmpl_list, "<INVALID>")); goto next_pair; } if (map_to_vp(request, &vp, request, attr, NULL) < 0) { RWDEBUG("Failed creating attribute for valuepair \"%s\", skipping...", self->values[i]->bv_val); goto next_pair; } fr_cursor_merge(&cursor, vp); talloc_free(attr); /* * Only process the first value, unless the operator is += */ if (map->op != T_OP_ADD) break; } break; /* * Iterate over all the retrieved values, * don't try and be clever about changing operators * just use whatever was set in the attribute map. */ case TMPL_TYPE_ATTR: for (i = 0; i < self->count; i++) { if (!self->values[i]->bv_len) continue; vp = pairalloc(ctx, map->lhs->tmpl_da); rad_assert(vp); if (pairparsevalue(vp, self->values[i]->bv_val, self->values[i]->bv_len) < 0) { char *escaped; escaped = fr_aprints(vp, self->values[i]->bv_val, self->values[i]->bv_len, '"'); RWDEBUG("Failed parsing value \"%s\" for attribute %s: %s", escaped, map->lhs->tmpl_da->name, fr_strerror()); talloc_free(vp); /* also frees escaped */ continue; } vp->op = map->op; fr_cursor_insert(&cursor, vp); /* * Only process the first value, unless the operator is += */ if (map->op != T_OP_ADD) break; } break; default: rad_assert(0); } *out = head; return 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, *subcs; 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 #if 0 && defined(S_IROTH) if (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 = DICTDIR; } /* * About sizeof(REQUEST) + sizeof(RADIUS_PACKET) * 2 + sizeof(VALUE_PAIR) * 400 * * Which should be enough for many configurations. */ main_config.talloc_pool_size = 8 * 1024; /* default */ /* * 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); cs = cf_section_alloc(NULL, "main", NULL); if (!cs) return -1; /* * Add a 'feature' subsection off the main config * We check if it's defined first, as the user may * have defined their own feature flags, or want * to manually override the ones set by modules * or the server. */ subcs = cf_section_sub_find(cs, "feature"); if (!subcs) { subcs = cf_section_alloc(cs, "feature", NULL); if (!subcs) return -1; cf_section_add(cs, subcs); } version_init_features(subcs); /* * Add a 'version' subsection off the main config * We check if it's defined first, this is for * backwards compatibility. */ subcs = cf_section_sub_find(cs, "version"); if (!subcs) { subcs = cf_section_alloc(cs, "version", NULL); if (!subcs) return -1; cf_section_add(cs, subcs); } version_init_numbers(subcs); /* Read the configuration file */ snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf", radius_dir, main_config.name); if (cf_file_read(cs, buffer) < 0) { ERROR("Errors reading or parsing %s", buffer); talloc_free(cs); return -1; } /* * If there was no log destination set on the command line, * set it now. */ if (default_log.dst == L_DST_NULL) { default_log.dst = L_DST_STDERR; default_log.fd = STDERR_FILENO; if (cf_section_parse(cs, NULL, startup_server_config) == -1) { fprintf(stderr, "%s: Error: Failed to parse log{} section.\n", main_config.name); cf_file_free(cs); return -1; } if (!radlog_dest) { fprintf(stderr, "%s: Error: No log destination specified.\n", main_config.name); cf_file_free(cs); return -1; } default_log.fd = -1; default_log.dst = fr_str2int(log_str2dst, radlog_dest, L_DST_NUM_DEST); if (default_log.dst == L_DST_NUM_DEST) { fprintf(stderr, "%s: Error: Unknown log_destination %s\n", main_config.name, 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, "%s: Error: Syslog chosen but no facility was specified\n", main_config.name); cf_file_free(cs); return -1; } main_config.syslog_facility = fr_str2int(syslog_facility_table, syslog_facility, -1); if (main_config.syslog_facility < 0) { fprintf(stderr, "%s: Error: Unknown syslog_facility %s\n", main_config.name, syslog_facility); cf_file_free(cs); return -1; } #ifdef HAVE_SYSLOG_H /* * Call openlog only once, when the * program starts. */ openlog(main_config.name, LOG_PID, main_config.syslog_facility); #endif } else if (default_log.dst == L_DST_FILES) { if (!main_config.log_file) { fprintf(stderr, "%s: Error: Specified \"files\" as a log destination, but no log filename was given!\n", main_config.name); cf_file_free(cs); return -1; } } } #ifdef HAVE_SETUID /* * Switch users as early as possible. */ if (!switch_users(cs)) fr_exit(1); #endif /* * 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; /* * Fix up log_auth, and log_accept and log_reject */ if (main_config.log_auth) { main_config.log_accept = main_config.log_reject = true; } /* * 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 (rad_debug_lvl == 0) { rad_debug_lvl = main_config.debug_level; } fr_debug_lvl = rad_debug_lvl; FR_INTEGER_COND_CHECK("max_request_time", main_config.max_request_time, (main_config.max_request_time != 0), 100); /* * reject_delay can be zero. OR 1 though 10. */ if ((main_config.reject_delay.tv_sec != 0) || (main_config.reject_delay.tv_usec != 0)) { FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, >=, 1, 0); }
/* * *presult is "did comparison match or not" */ static int radius_do_cmp(REQUEST *request, int *presult, FR_TOKEN lt, const char *pleft, FR_TOKEN token, FR_TOKEN rt, const char *pright, int cflags, int modreturn) { int result; uint32_t lint, rint; VALUE_PAIR *vp = NULL; #ifdef HAVE_REGEX_H char buffer[8192]; #else cflags = cflags; /* -Wunused */ #endif rt = rt; /* -Wunused */ if (lt == T_BARE_WORD) { /* * Maybe check the last return code. */ if (token == T_OP_CMP_TRUE) { int isreturn; /* * Looks like a return code, treat is as such. */ isreturn = fr_str2int(modreturn_table, pleft, -1); if (isreturn != -1) { *presult = (modreturn == isreturn); return TRUE; } } /* * Bare words on the left can be attribute names. */ if (radius_get_vp(request, pleft, &vp)) { VALUE_PAIR myvp; /* * VP exists, and that's all we're looking for. */ if (token == T_OP_CMP_TRUE) { *presult = (vp != NULL); return TRUE; } if (!vp) { DICT_ATTR *da; /* * The attribute on the LHS may * have been a dynamically * registered callback. i.e. it * doesn't exist as a VALUE_PAIR. * If so, try looking for it. */ da = dict_attrbyname(pleft); if (da && (da->vendor == 0) && radius_find_compare(da->attr)) { VALUE_PAIR *check = pairmake(pleft, pright, token); *presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0); RDEBUG3(" Callback returns %d", *presult); pairfree(&check); return TRUE; } RDEBUG2(" (Attribute %s was not found)", pleft); *presult = 0; return TRUE; } #ifdef HAVE_REGEX_H /* * Regex comparisons treat everything as * strings. */ if ((token == T_OP_REG_EQ) || (token == T_OP_REG_NE)) { vp_prints_value(buffer, sizeof(buffer), vp, 0); pleft = buffer; goto do_checks; } #endif memcpy(&myvp, vp, sizeof(myvp)); if (!pairparsevalue(&myvp, pright)) { RDEBUG2("Failed parsing \"%s\": %s", pright, fr_strerror()); return FALSE; } myvp.operator = token; *presult = paircmp(&myvp, vp); RDEBUG3(" paircmp -> %d", *presult); return TRUE; } /* else it's not a VP in a list */ } #ifdef HAVE_REGEX_H do_checks: #endif switch (token) { case T_OP_GE: case T_OP_GT: case T_OP_LE: case T_OP_LT: if (!all_digits(pright)) { RDEBUG2(" (Right field is not a number at: %s)", pright); return FALSE; } rint = strtoul(pright, NULL, 0); if (!all_digits(pleft)) { RDEBUG2(" (Left field is not a number at: %s)", pleft); return FALSE; } lint = strtoul(pleft, NULL, 0); break; default: lint = rint = 0; /* quiet the compiler */ break; } switch (token) { case T_OP_CMP_TRUE: /* * Check for truth or falsehood. */ if (all_digits(pleft)) { lint = strtoul(pleft, NULL, 0); result = (lint != 0); } else { result = (*pleft != '\0'); } break; case T_OP_CMP_EQ: result = (strcmp(pleft, pright) == 0); break; case T_OP_NE: result = (strcmp(pleft, pright) != 0); break; case T_OP_GE: result = (lint >= rint); break; case T_OP_GT: result = (lint > rint); break; case T_OP_LE: result = (lint <= rint); break; case T_OP_LT: result = (lint < rint); break; #ifdef HAVE_REGEX_H case T_OP_REG_EQ: { int i, compare; regex_t reg; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* * Include substring matches. */ compare = regcomp(®, pright, cflags); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); DEBUG("ERROR: Failed compiling regular expression: %s", errbuf); } return FALSE; } compare = regexec(®, pleft, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); /* * Add new %{0}, %{1}, etc. */ if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) { char *r; free(request_data_get(request, request, REQUEST_DATA_REGEX | i)); /* * No %{i}, skip it. * We MAY have %{2} without %{1}. */ if (rxmatch[i].rm_so == -1) continue; /* * Copy substring into allocated buffer */ r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1); memcpy(r, pleft + rxmatch[i].rm_so, rxmatch[i].rm_eo - rxmatch[i].rm_so); r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0'; request_data_add(request, request, REQUEST_DATA_REGEX | i, r, free); } result = (compare == 0); } break; case T_OP_REG_NE: { int compare; regex_t reg; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* * Include substring matches. */ compare = regcomp(®, pright, cflags); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); DEBUG("ERROR: Failed compiling regular expression: %s", errbuf); } return FALSE; } compare = regexec(®, pleft, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); result = (compare != 0); } break; #endif default: DEBUG("ERROR: Comparison operator %s is not supported", fr_token_name(token)); result = FALSE; break; } *presult = result; return TRUE; }
/* * Main program */ int main(int argc, char **argv) { CONF_SECTION *maincs, *cs; FILE *fp; struct radutmp rt; char othername[256]; char nasname[1024]; char session_id[sizeof(rt.session_id)+1]; int hideshell = 0; int showsid = 0; int rawoutput = 0; int radiusoutput = 0; /* Radius attributes */ char const *portind; int c; unsigned int portno; char buffer[2048]; char const *user = NULL; int user_cmp = 0; time_t now = 0; uint32_t nas_port = ~0; uint32_t nas_ip_address = INADDR_NONE; int zap = 0; raddb_dir = RADIUS_DIR; #ifndef NDEBUG if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) { fr_perror("radwho"); exit(EXIT_FAILURE); } #endif talloc_set_log_stderr(); while((c = getopt(argc, argv, "d:D:fF:nN:sSipP:crRu:U:Z")) != EOF) switch (c) { case 'd': raddb_dir = optarg; break; case 'D': dict_dir = optarg; break; case 'F': radutmp_file = optarg; break; case 'h': usage(0); /* never returns */ case 'S': hideshell = 1; break; case 'n': showname = 0; break; case 'N': if (inet_pton(AF_INET, optarg, &nas_ip_address) < 0) { usage(1); } break; case 's': showname = 1; break; case 'i': showsid = 1; break; case 'p': showptype = 1; break; case 'P': nas_port = atoi(optarg); break; case 'c': showcid = 1; showname = 1; break; case 'r': rawoutput = 1; break; case 'R': radiusoutput = 1; now = time(NULL); break; case 'u': user = optarg; user_cmp = 0; break; case 'U': user = optarg; user_cmp = 1; break; case 'Z': zap = 1; break; default: usage(1); /* never returns */ } /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("radwho"); return 1; } if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radwho"); return 1; } if (dict_read(raddb_dir, RADIUS_DICTIONARY) == -1) { fr_perror("radwho"); return 1; } fr_strerror(); /* Clear the error buffer */ /* * Be safe. */ if (zap && !radiusoutput) zap = 0; /* * zap EVERYONE, but only on this nas */ if (zap && !user && (~nas_port == 0)) { /* * We need to know which NAS to zap users in. */ if (nas_ip_address == INADDR_NONE) usage(1); printf("Acct-Status-Type = Accounting-Off\n"); printf("NAS-IP-Address = %s\n", hostname(buffer, sizeof(buffer), nas_ip_address)); printf("Acct-Delay-Time = 0\n"); exit(0); /* don't bother printing anything else */ } if (radutmp_file) goto have_radutmp; /* * Initialize main_config */ memset(&main_config, 0, sizeof(main_config)); /* Read radiusd.conf */ maincs = cf_section_alloc(NULL, "main", NULL); if (!maincs) exit(1); snprintf(buffer, sizeof(buffer), "%.200s/radiusd.conf", raddb_dir); if (cf_file_read(maincs, buffer) < 0) { fprintf(stderr, "%s: Error reading or parsing radiusd.conf\n", argv[0]); talloc_free(maincs); exit(1); } cs = cf_section_sub_find(maincs, "modules"); if (!cs) { fprintf(stderr, "%s: No modules section found in radiusd.conf\n", argv[0]); exit(1); } /* Read the radutmp section of radiusd.conf */ cs = cf_section_sub_find_name2(cs, "radutmp", NULL); if (!cs) { fprintf(stderr, "%s: No configuration information in radutmp section of radiusd.conf\n", argv[0]); exit(1); } cf_section_parse(cs, NULL, module_config); /* Assign the correct path for the radutmp file */ radutmp_file = radutmpconfig.radutmp_fn; have_radutmp: if (showname < 0) showname = 1; /* * Show the users logged in on the terminal server(s). */ if ((fp = fopen(radutmp_file, "r")) == NULL) { fprintf(stderr, "%s: Error reading %s: %s\n", progname, radutmp_file, fr_syserror(errno)); return 0; } /* * Don't print the headers if raw or RADIUS */ if (!rawoutput && !radiusoutput) { fputs(showname ? hdr1 : hdr2, stdout); fputs(eol, stdout); } /* * Read the file, printing out active entries. */ while (fread(&rt, sizeof(rt), 1, fp) == 1) { char name[sizeof(rt.login) + 1]; if (rt.type != P_LOGIN) continue; /* hide logout sessions */ /* * We don't show shell users if we are * fingerd, as we have done that above. */ if (hideshell && !strchr("PCS", rt.proto)) continue; /* * Print out sessions only for the given user. */ if (user) { /* only for a particular user */ if (((user_cmp == 0) && (strncasecmp(rt.login, user, strlen(user)) != 0)) || ((user_cmp == 1) && (strncmp(rt.login, user, strlen(user)) != 0))) { continue; } } /* * Print out only for the given NAS port. */ if (~nas_port != 0) { if (rt.nas_port != nas_port) continue; } /* * Print out only for the given NAS IP address */ if (nas_ip_address != INADDR_NONE) { if (rt.nas_address != nas_ip_address) continue; } memcpy(session_id, rt.session_id, sizeof(rt.session_id)); session_id[sizeof(rt.session_id)] = 0; if (!rawoutput && rt.nas_port > (showname ? 999 : 99999)) { portind = ">"; portno = (showname ? 999 : 99999); } else { portind = "S"; portno = rt.nas_port; } /* * Print output as RADIUS attributes */ if (radiusoutput) { memcpy(nasname, rt.login, sizeof(rt.login)); nasname[sizeof(rt.login)] = '\0'; fr_prints(buffer, sizeof(buffer), nasname, -1, '"'); printf("User-Name = \"%s\"\n", buffer); fr_prints(buffer, sizeof(buffer), session_id, -1, '"'); printf("Acct-Session-Id = \"%s\"\n", buffer); if (zap) printf("Acct-Status-Type = Stop\n"); printf("NAS-IP-Address = %s\n", hostname(buffer, sizeof(buffer), rt.nas_address)); printf("NAS-Port = %u\n", rt.nas_port); switch (rt.proto) { case 'S': printf("Service-Type = Framed-User\n"); printf("Framed-Protocol = SLIP\n"); break; case 'P': printf("Service-Type = Framed-User\n"); printf("Framed-Protocol = PPP\n"); break; default: printf("Service-type = Login-User\n"); break; } if (rt.framed_address != INADDR_NONE) { printf("Framed-IP-Address = %s\n", hostname(buffer, sizeof(buffer), rt.framed_address)); } /* * Some sanity checks on the time */ if ((rt.time <= now) && (now - rt.time) <= (86400 * 365)) { printf("Acct-Session-Time = %" PRId64 "\n", (int64_t) (now - rt.time)); } if (rt.caller_id[0] != '\0') { memcpy(nasname, rt.caller_id, sizeof(rt.caller_id)); nasname[sizeof(rt.caller_id)] = '\0'; fr_prints(buffer, sizeof(buffer), nasname, -1, '"'); printf("Calling-Station-Id = \"%s\"\n", buffer); } printf("\n"); /* separate entries with a blank line */ continue; } /* * Show the fill name, or not. */ memcpy(name, rt.login, sizeof(rt.login)); name[sizeof(rt.login)] = '\0'; if (showname) { if (rawoutput == 0) { printf("%-10.10s %-17.17s %-5.5s %s%-3u %-9.9s %-15.15s %-.19s%s", name, showcid ? rt.caller_id : (showsid? session_id : fullname(rt.login)), proto(rt.proto, rt.porttype), portind, portno, dotime(rt.time), hostname(nasname, sizeof(nasname), rt.nas_address), hostname(othername, sizeof(othername), rt.framed_address), eol); } else { printf("%s,%s,%s,%s%u,%s,%s,%s%s", name, showcid ? rt.caller_id : (showsid? session_id : fullname(rt.login)), proto(rt.proto, rt.porttype), portind, portno, dotime(rt.time), hostname(nasname, sizeof(nasname), rt.nas_address), hostname(othername, sizeof(othername), rt.framed_address), eol); } } else { if (rawoutput == 0) { printf("%-10.10s %s%-5u %-6.6s %-13.13s %-15.15s %-.28s%s", name, portind, portno, proto(rt.proto, rt.porttype), dotime(rt.time), hostname(nasname, sizeof(nasname), rt.nas_address), hostname(othername, sizeof(othername), rt.framed_address), eol); } else { printf("%s,%s%u,%s,%s,%s,%s%s", name, portind, portno, proto(rt.proto, rt.porttype), dotime(rt.time), hostname(nasname, sizeof(nasname), rt.nas_address), hostname(othername, sizeof(othername), rt.framed_address), eol); } } } fclose(fp); return 0; }
/* * Do chroot, if requested. * * Switch UID and GID to what is specified in the config file */ static int switch_users(CONF_SECTION *cs) { /* * Get the current maximum for core files. Do this * before anything else so as to ensure it's properly * initialized. */ if (fr_set_dumpable_init() < 0) { fr_perror("radiusd"); return 0; } /* * Don't do chroot/setuid/setgid if we're in debugging * as non-root. */ if (debug_flag && (getuid() != 0)) return 1; if (cf_section_parse(cs, NULL, bootstrap_config) < 0) { fprintf(stderr, "radiusd: Error: Failed to parse user/group information.\n"); return 0; } #ifdef HAVE_GRP_H /* Set GID. */ if (gid_name) { struct group *gr; gr = getgrnam(gid_name); if (gr == NULL) { fprintf(stderr, "%s: Cannot get ID for group %s: %s\n", progname, gid_name, fr_syserror(errno)); return 0; } server_gid = gr->gr_gid; } else { server_gid = getgid(); } #endif #ifdef HAVE_PWD_H /* Set UID. */ if (uid_name) { struct passwd *pw; pw = getpwnam(uid_name); if (pw == NULL) { fprintf(stderr, "%s: Cannot get passwd entry for user %s: %s\n", progname, uid_name, fr_syserror(errno)); return 0; } if (getuid() == pw->pw_uid) { uid_name = NULL; } else { server_uid = pw->pw_uid; #ifdef HAVE_INITGROUPS if (initgroups(uid_name, server_gid) < 0) { fprintf(stderr, "%s: Cannot initialize supplementary group list for user %s: %s\n", progname, uid_name, fr_syserror(errno)); return 0; } #endif } } else { server_uid = getuid(); } #endif if (chroot_dir) { if (chroot(chroot_dir) < 0) { fprintf(stderr, "%s: Failed to perform chroot %s: %s", progname, chroot_dir, fr_syserror(errno)); return 0; } /* * Note that we leave chdir alone. It may be * OUTSIDE of the root. This allows us to read * the configuration from "-d ./etc/raddb", with * the chroot as "./chroot/" for example. After * the server has been loaded, it does a "cd * ${logdir}" below, so that core files (if any) * go to a logging directory. * * This also allows the configuration of the * server to be outside of the chroot. If the * server is statically linked, then the only * things needed inside of the chroot are the * logging directories. */ } #ifdef HAVE_GRP_H /* Set GID. */ if (gid_name && (setgid(server_gid) < 0)) { fprintf(stderr, "%s: Failed setting group to %s: %s", progname, gid_name, fr_syserror(errno)); return 0; } #endif #ifdef HAVE_SETUID /* * Just before losing root permissions, ensure that the * log files have the correct owner && group. * * We have to do this because the log file MAY have been * specified on the command-line. */ if (uid_name || gid_name) { 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)); return 0; } if (chown(main_config.log_file, server_uid, server_gid) < 0) { fprintf(stderr, "%s: Cannot change ownership of log file %s: %s\n", progname, main_config.log_file, fr_syserror(errno)); return 0; } } } if (uid_name) { doing_setuid = true; fr_suid_down(); } #endif /* * This also clears the dumpable flag if core dumps * aren't allowed. */ if (fr_set_dumpable(allow_core_dumps) < 0) { ERROR("%s", fr_strerror()); } if (allow_core_dumps) { INFO("Core dumps are enabled"); } return 1; }
VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value) { char *p, *s=0; const char *cp, *cs; int x; unsigned long long y; size_t length; DICT_VALUE *dval; if (!value) return NULL; /* * Even for integers, dates and ip addresses we * keep the original string in vp->vp_strvalue. */ if (vp->type != PW_TYPE_TLV) { strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue)); vp->length = strlen(vp->vp_strvalue); } switch(vp->type) { case PW_TYPE_STRING: /* * Do escaping here */ p = vp->vp_strvalue; cp = value; length = 0; while (*cp && (length < (sizeof(vp->vp_strvalue) - 1))) { char c = *cp++; if (c == '\\') { switch (*cp) { case 'r': c = '\r'; cp++; break; case 'n': c = '\n'; cp++; break; case 't': c = '\t'; cp++; break; case '"': c = '"'; cp++; break; case '\'': c = '\''; cp++; break; case '\\': c = '\\'; cp++; break; case '`': c = '`'; cp++; break; case '\0': c = '\\'; /* no cp++ */ break; default: if ((cp[0] >= '0') && (cp[0] <= '9') && (cp[1] >= '0') && (cp[1] <= '9') && (cp[2] >= '0') && (cp[2] <= '9') && (sscanf(cp, "%3o", &x) == 1)) { c = x; cp += 3; } /* else just do '\\' */ } } *p++ = c; length++; } vp->vp_strvalue[length] = '\0'; vp->length = length; break; case PW_TYPE_IPADDR: /* * It's a comparison, not a real IP. */ if ((vp->operator == T_OP_REG_EQ) || (vp->operator == T_OP_REG_NE)) { break; } /* * FIXME: complain if hostname * cannot be resolved, or resolve later! */ s = NULL; if ((p = strrchr(value, '+')) != NULL && !p[1]) { cs = s = strdup(value); if (!s) return NULL; p = strrchr(s, '+'); *p = 0; vp->flags.addport = 1; } else { p = NULL; cs = value; } { fr_ipaddr_t ipaddr; if (ip_hton(cs, AF_INET, &ipaddr) < 0) { free(s); fr_strerror_printf("Failed to find IP address for %s", cs); return NULL; } vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr; } free(s); vp->length = 4; break; case PW_TYPE_BYTE: vp->length = 1; /* * Note that ALL integers are unsigned! */ vp->vp_integer = getint(value, &p); if (!*p) { if (vp->vp_integer > 255) { fr_strerror_printf("Byte value \"%s\" is larger than 255", value); return NULL; } break; } if (check_for_whitespace(p)) break; goto check_for_value; case PW_TYPE_SHORT: /* * Note that ALL integers are unsigned! */ vp->vp_integer = getint(value, &p); vp->length = 2; if (!*p) { if (vp->vp_integer > 65535) { fr_strerror_printf("Byte value \"%s\" is larger than 65535", value); return NULL; } break; } if (check_for_whitespace(p)) break; goto check_for_value; case PW_TYPE_INTEGER: /* * Note that ALL integers are unsigned! */ vp->vp_integer = getint(value, &p); vp->length = 4; if (!*p) break; if (check_for_whitespace(p)) break; check_for_value: /* * Look for the named value for the given * attribute. */ if ((dval = dict_valbyname(vp->attribute, vp->vendor, value)) == NULL) { fr_strerror_printf("Unknown value %s for attribute %s", value, vp->name); return NULL; } vp->vp_integer = dval->value; break; case PW_TYPE_INTEGER64: /* * Note that ALL integers are unsigned! */ p = vp->vp_strvalue; if (sscanf(p, "%llu", &y) != 1) { fr_strerror_printf("Invalid value %s for attribute %s", value, vp->name); return NULL; } vp->vp_integer64 = y; vp->length = 8; p += strspn(p, "0123456789"); if (check_for_whitespace(p)) break; break; case PW_TYPE_DATE: { /* * time_t may be 64 bits, whule vp_date * MUST be 32-bits. We need an * intermediary variable to handle * the conversions. */ time_t date; if (gettime(value, &date) < 0) { fr_strerror_printf("failed to parse time string " "\"%s\"", value); return NULL; } vp->vp_date = date; } vp->length = 4; break; case PW_TYPE_ABINARY: #ifdef ASCEND_BINARY if (strncasecmp(value, "0x", 2) == 0) { vp->type = PW_TYPE_OCTETS; goto do_octets; } if (ascend_parse_filter(vp) < 0 ) { char buffer[256]; snprintf(buffer, sizeof(buffer), "failed to parse Ascend binary attribute: %s", fr_strerror()); fr_strerror_printf("%s", buffer); return NULL; } break; /* * If Ascend binary is NOT defined, * then fall through to raw octets, so that * the user can at least make them by hand... */ do_octets: #endif /* raw octets: 0x01020304... */ case PW_TYPE_OCTETS: if (strncasecmp(value, "0x", 2) == 0) { size_t size; uint8_t *us; cp = value + 2; us = vp->vp_octets; vp->length = 0; /* * Invalid. */ size = strlen(cp); if ((size & 0x01) != 0) { fr_strerror_printf("Hex string is not an even length string."); return NULL; } vp->length = size >> 1; if (size > 2*sizeof(vp->vp_octets)) { vp->type |= PW_FLAG_LONG; us = vp->vp_tlv = malloc(vp->length); if (!us) { fr_strerror_printf("Out of memory."); return NULL; } } if (fr_hex2bin(cp, us, vp->length) != vp->length) { fr_strerror_printf("Invalid hex data"); return NULL; } } break; case PW_TYPE_IFID: if (ifid_aton(value, (void *) &vp->vp_ifid) == NULL) { fr_strerror_printf("failed to parse interface-id " "string \"%s\"", value); return NULL; } vp->length = 8; break; case PW_TYPE_IPV6ADDR: { fr_ipaddr_t ipaddr; if (ip_hton(value, AF_INET6, &ipaddr) < 0) { char buffer[1024]; strlcpy(buffer, fr_strerror(), sizeof(buffer)); fr_strerror_printf("failed to parse IPv6 address " "string \"%s\": %s", value, buffer); return NULL; } vp->vp_ipv6addr = ipaddr.ipaddr.ip6addr; vp->length = 16; /* length of IPv6 address */ } break; case PW_TYPE_IPV6PREFIX: p = strchr(value, '/'); if (!p || ((p - value) >= 256)) { fr_strerror_printf("invalid IPv6 prefix " "string \"%s\"", value); return NULL; } else { unsigned int prefix; char buffer[256], *eptr; memcpy(buffer, value, p - value); buffer[p - value] = '\0'; if (inet_pton(AF_INET6, buffer, vp->vp_octets + 2) <= 0) { fr_strerror_printf("failed to parse IPv6 address " "string \"%s\"", value); return NULL; } prefix = strtoul(p + 1, &eptr, 10); if ((prefix > 128) || *eptr) { fr_strerror_printf("failed to parse IPv6 address " "string \"%s\"", value); return NULL; } vp->vp_octets[1] = prefix; } vp->vp_octets[0] = '\0'; vp->length = 16 + 2; break; case PW_TYPE_ETHERNET: { const char *c1, *c2; length = 0; cp = value; while (*cp) { if (cp[1] == ':') { c1 = hextab; c2 = memchr(hextab, tolower((int) cp[0]), 16); cp += 2; } else if ((cp[1] != '\0') && ((cp[2] == ':') || (cp[2] == '\0'))) { c1 = memchr(hextab, tolower((int) cp[0]), 16); c2 = memchr(hextab, tolower((int) cp[1]), 16); cp += 2; if (*cp == ':') cp++; } else { c1 = c2 = NULL; } if (!c1 || !c2 || (length >= sizeof(vp->vp_ether))) { fr_strerror_printf("failed to parse Ethernet address \"%s\"", value); return NULL; } vp->vp_ether[length] = ((c1-hextab)<<4) + (c2-hextab); length++; } } vp->length = 6; break; case PW_TYPE_COMBO_IP: if (inet_pton(AF_INET6, value, vp->vp_strvalue) > 0) { vp->type = PW_TYPE_IPV6ADDR; vp->length = 16; /* length of IPv6 address */ vp->vp_strvalue[vp->length] = '\0'; } else { fr_ipaddr_t ipaddr; if (ip_hton(value, AF_INET, &ipaddr) < 0) { fr_strerror_printf("Failed to find IPv4 address for %s", value); return NULL; } vp->type = PW_TYPE_IPADDR; vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr; vp->length = 4; } break; case PW_TYPE_SIGNED: /* Damned code for 1 WiMAX attribute */ vp->vp_signed = (int32_t) strtol(value, &p, 10); vp->length = 4; break; case PW_TYPE_TLV: /* don't use this! */ if (strncasecmp(value, "0x", 2) != 0) { fr_strerror_printf("Invalid TLV specification"); return NULL; } length = strlen(value + 2) / 2; if (vp->length < length) { free(vp->vp_tlv); vp->vp_tlv = NULL; } vp->vp_tlv = malloc(length); if (!vp->vp_tlv) { fr_strerror_printf("No memory"); return NULL; } if (fr_hex2bin(value + 2, vp->vp_tlv, length) != length) { fr_strerror_printf("Invalid hex data in TLV"); return NULL; } vp->length = length; break; /* * Anything else. */ default: fr_strerror_printf("unknown attribute type %d", vp->type); return NULL; }
/* * Add MPPE attributes to the reply. */ static void mppe_add_reply(REQUEST *request, const char* name, const uint8_t * value, int len) { VALUE_PAIR *vp; vp = radius_pairmake(request, &request->reply->vps, name, "", T_OP_EQ); if (!vp) { RDEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, fr_strerror()); return; } memcpy(vp->vp_octets, value, len); vp->length = len; }
/** Copy packet to multiple servers * * Create a duplicate of the packet and send it to a list of realms * defined by the presence of the Replicate-To-Realm VP in the control * list of the current request. * * This is pretty hacky and is 100% fire and forget. If you're looking * to forward authentication requests to multiple realms and process * the responses, this function will not allow you to do that. * * @param[in] instance of this module. * @param[in] request The current request. * @param[in] list of attributes to copy to the duplicate packet. * @param[in] code to write into the code field of the duplicate packet. * @return RCODE fail on error, invalid if list does not exist, noop if no * replications succeeded, else ok. */ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, unsigned int code) { int rcode = RLM_MODULE_NOOP; VALUE_PAIR *vp, **vps, *last; home_server *home; REALM *realm; home_pool_t *pool; RADIUS_PACKET *packet = NULL; last = request->config_items; /* * Send as many packets as necessary to different * destinations. */ while (1) { vp = pairfind(last, PW_REPLICATE_TO_REALM, 0, TAG_ANY); if (!vp) break; last = vp->next; realm = realm_find2(vp->vp_strvalue); if (!realm) { RDEBUG2E("Cannot Replicate to unknown realm %s", realm); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: RDEBUG2E("Cannot replicate unknown packet code %d", request->packet->code); cleanup(packet); return RLM_MODULE_FAIL; case PW_AUTHENTICATION_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_COA_REQUEST: case PW_DISCONNECT_REQUEST: pool = realm->acct_pool; break; #endif } if (!pool) { RDEBUG2W("Cancelling replication to Realm %s, as the realm is local.", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { RDEBUG2E("Failed to find live home server for realm %s", realm->name); continue; } /* * For replication to multiple servers we re-use the packet * we built here. */ if (!packet) { packet = rad_alloc(NULL, 1); if (!packet) return RLM_MODULE_FAIL; packet->sockfd = -1; packet->code = code; packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { RDEBUGE("Failed opening socket: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } vps = radius_list(request, list); if (!vps) { RDEBUGW("List '%s' doesn't exist for " "this packet", fr_int2str(pair_lists, list, "?unknown?")); rcode = RLM_MODULE_INVALID; goto done; } /* * Don't assume the list actually contains any * attributes. */ if (*vps) { packet->vps = paircopy(packet, *vps); if (!packet->vps) { rcode = RLM_MODULE_FAIL; goto done; } } /* * For CHAP, create the CHAP-Challenge if * it doesn't exist. */ if ((code == PW_AUTHENTICATION_REQUEST) && (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) && (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) { vp = radius_paircreate(request, &packet->vps, PW_CHAP_CHALLENGE, 0); vp->length = AUTH_VECTOR_LEN; memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN); } } else { size_t i; for (i = 0; i < sizeof(packet->vector); i++) { packet->vector[i] = fr_rand() & 0xff; } packet->id++; free(packet->data); packet->data = NULL; packet->data_len = 0; } /* * (Re)-Write these. */ packet->dst_ipaddr = home->ipaddr; packet->dst_port = home->port; memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); packet->src_port = 0; /* * Encode, sign and then send the packet. */ RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "¿unknown?"),realm->name); if (rad_send(packet, NULL, home->secret) < 0) { RDEBUGE("Failed replicating packet: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } /* * We've sent it to at least one destination. */ rcode = RLM_MODULE_OK; } done: cleanup(packet); return rcode; }
static int generate_sql_clients(rlm_sql_t *inst) { rlm_sql_handle_t *handle; rlm_sql_row_t row; char querystr[MAX_QUERY_LEN]; RADCLIENT *c; char *prefix_ptr = NULL; unsigned int i = 0; int numf = 0; DEBUG("rlm_sql (%s): Processing generate_sql_clients", inst->config->xlat_name); /* NAS query isn't xlat'ed */ strlcpy(querystr, inst->config->nas_query, sizeof(querystr)); DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s", inst->config->xlat_name, querystr); handle = sql_get_socket(inst); if (handle == NULL) return -1; if (rlm_sql_select_query(&handle,inst,querystr)){ return -1; } while(rlm_sql_fetch_row(&handle, inst) == 0) { i++; row = handle->row; if (row == NULL) break; /* * The return data for each row MUST be in the following order: * * 0. Row ID (currently unused) * 1. Name (or IP address) * 2. Shortname * 3. Type * 4. Secret * 5. Virtual Server (optional) */ if (!row[0]){ radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i); continue; } if (!row[1]){ radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]); continue; } if (!row[2]){ radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]); continue; } if (!row[4]){ radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]); continue; } DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name, row[1],row[2],row[4]); c = talloc_zero(inst, RADCLIENT); #ifdef WITH_DYNAMIC_CLIENTS c->dynamic = 1; #endif /* * Look for prefixes */ c->prefix = -1; prefix_ptr = strchr(row[1], '/'); if (prefix_ptr) { c->prefix = atoi(prefix_ptr + 1); if ((c->prefix < 0) || (c->prefix > 128)) { radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.", inst->config->xlat_name, prefix_ptr + 1); talloc_free(c); continue; } /* Replace '/' with '\0' */ *prefix_ptr = '\0'; } /* * Always get the numeric representation of IP */ if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) { radlog(L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s", inst->config->xlat_name, row[1], fr_strerror()); talloc_free(c); continue; } else { char buffer[256]; ip_ntoh(&c->ipaddr, buffer, sizeof(buffer)); c->longname = talloc_strdup(c, buffer); } if (c->prefix < 0) switch (c->ipaddr.af) { case AF_INET: c->prefix = 32; break; case AF_INET6: c->prefix = 128; break; default: break; } /* * Other values (secret, shortname, nastype, virtual_server) */ c->secret = talloc_strdup(c, row[4]); c->shortname = talloc_strdup(c, row[2]); if(row[3] != NULL) c->nastype = strdup(row[3]); numf = (inst->module->sql_num_fields)(handle, inst->config); if ((numf > 5) && (row[5] != NULL) && *row[5]) c->server = strdup(row[5]); DEBUG("rlm_sql (%s): Adding client %s (%s, server=%s) to clients list", inst->config->xlat_name, c->longname,c->shortname, c->server ? c->server : "<none>"); if (!client_add(NULL, c)) { sql_release_socket(inst, handle); DEBUG("rlm_sql (%s): Failed to add client %s (%s) to clients list. Maybe there's a duplicate?", inst->config->xlat_name, c->longname,c->shortname); client_free(c); return -1; } } (inst->module->sql_finish_select_query)(handle, inst->config); sql_release_socket(inst, handle); return 0; }
/** Build value pairs from the passed JSON object and add to the request * * Parse the passed JSON object and create value pairs that will be injected into * the given request for authorization. * * Example JSON document structure: * @code{.json} * { * "docType": "raduser", * "userName": "******", * "config": { * "SHA-Password": { * "value": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", * "op": ":=" * } * }, * "reply": { * "Reply-Message": { * "value": "Hidey Ho!", * "op": "=" * } * } * } * @endcode * * @param json The JSON object representation of the user documnent. * @param section The pair section ("config" or "reply"). * @param request The request to which the generated pairs should be added. */ void *mod_json_object_to_value_pairs(json_object *json, const char *section, REQUEST *request) { json_object *jobj, *jval, *jop; /* json object pointers */ TALLOC_CTX *ctx; /* talloc context for pairmake */ VALUE_PAIR *vp, **ptr; /* value pair and value pair pointer for pairmake */ /* assign ctx and vps for pairmake based on section */ if (strcmp(section, "config") == 0) { ctx = request; ptr = &(request->config_items); } else if (strcmp(section, "reply") == 0) { ctx = request->reply; ptr = &(request->reply->vps); } else { /* log error - this shouldn't happen */ RERROR("invalid section passed for pairmake"); /* return */ return NULL; } /* get config payload */ if (json_object_object_get_ex(json, section, &jobj)) { /* make sure we have the correct type */ if (!json_object_is_type(jobj, json_type_object)) { /* log error */ RERROR("invalid json type for '%s' section - sections must be json objects", section); /* reuturn */ return NULL; } /* loop through object */ json_object_object_foreach(jobj, attribute, json_vp) { /* check for appropriate type in value and op */ if (!json_object_is_type(json_vp, json_type_object)) { /* log error */ RERROR("invalid json type for '%s' attribute - attributes must be json objects", attribute); /* return */ return NULL; } /* debugging */ RDEBUG("parsing '%s' attribute: %s => %s", section, attribute, json_object_to_json_string(json_vp)); /* create pair from json object */ if (json_object_object_get_ex(json_vp, "value", &jval) && json_object_object_get_ex(json_vp, "op", &jop)) { /* make correct pairs based on json object type */ switch (json_object_get_type(jval)) { case json_type_double: case json_type_int: case json_type_string: /* debugging */ RDEBUG("adding '%s' attribute to '%s' section", attribute, section); /* add pair */ vp = pairmake(ctx, ptr, attribute, json_object_get_string(jval), fr_str2int(fr_tokens, json_object_get_string(jop), 0)); /* check pair */ if (!vp) { RERROR("could not build value pair for '%s' attribute (%s)", attribute, fr_strerror()); /* return */ return NULL; } break; case json_type_object: case json_type_array: /* log error - we want to handle these eventually */ RERROR("skipping unhandled nested json object or array value pair object"); break; default: /* log error - this shouldn't ever happen */ RERROR("skipping unhandled json type in value pair object"); break; } } else { /* log error */ RERROR("failed to get 'value' or 'op' element for '%s' attribute", attribute); } } /* return NULL */ return NULL; } /* debugging */ RDEBUG("couldn't find '%s' section in json object - not adding value pairs for this section", section); /* return NULL */ return NULL; }
/* * Write accounting information to this modules database. */ static int replicate_packet(void *instance, REQUEST *request) { int rcode = RLM_MODULE_NOOP; VALUE_PAIR *vp, *last; home_server *home; REALM *realm; home_pool_t *pool; RADIUS_PACKET *packet = NULL; instance = instance; /* -Wunused */ last = request->config_items; /* * Send as many packets as necessary to different * destinations. */ while (1) { vp = pairfind(last, PW_REPLICATE_TO_REALM, 0); if (!vp) break; last = vp->next; realm = realm_find2(vp->vp_strvalue); if (!realm) { RDEBUG2("ERROR: Cannot Replicate to unknown realm %s", realm); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: RDEBUG2("ERROR: Cannot replicate unknown packet code %d", request->packet->code); cleanup(packet); return RLM_MODULE_FAIL; case PW_AUTHENTICATION_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_COA_REQUEST: case PW_DISCONNECT_REQUEST: pool = realm->acct_pool; break; #endif } if (!pool) { RDEBUG2(" WARNING: Cancelling replication to Realm %s, as the realm is local.", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { RDEBUG2("ERROR: Failed to find live home server for realm %s", realm->name); continue; } if (!packet) { packet = rad_alloc(1); if (!packet) return RLM_MODULE_FAIL; packet->sockfd = -1; packet->code = request->packet->code; packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { RDEBUG("ERROR: Failed opening socket: %s", fr_strerror()); cleanup(packet); return RLM_MODULE_FAIL; } packet->vps = paircopy(request->packet->vps); if (!packet->vps) { RDEBUG("ERROR: Out of memory!"); cleanup(packet); return RLM_MODULE_FAIL; } /* * For CHAP, create the CHAP-Challenge if * it doesn't exist. */ if ((request->packet->code == PW_AUTHENTICATION_REQUEST) && (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0) != NULL) && (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0) == NULL)) { vp = radius_paircreate(request, &packet->vps, PW_CHAP_CHALLENGE, 0, PW_TYPE_OCTETS); vp->length = AUTH_VECTOR_LEN; memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN); } } else { size_t i; for (i = 0; i < sizeof(packet->vector); i++) { packet->vector[i] = fr_rand() & 0xff; } packet->id++; free(packet->data); packet->data = NULL; packet->data_len = 0; } /* * (Re)-Write these. */ packet->dst_ipaddr = home->ipaddr; packet->dst_port = home->port; memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); packet->src_port = 0; /* * Encode, sign and then send the packet. */ RDEBUG("Replicating packet to Realm %s", realm->name); if (rad_send(packet, NULL, home->secret) < 0) { RDEBUG("ERROR: Failed replicating packet: %s", fr_strerror()); cleanup(packet); return RLM_MODULE_FAIL; } /* * We've sent it to at least one destination. */ rcode = RLM_MODULE_OK; } cleanup(packet); return rcode; }
int main(int argc, char **argv) { char *p; int c; char const *radius_dir = RADDBDIR; char const *filename = NULL; fr_debug_flag = 0; while ((c = getopt(argc, argv, "d:f:hr:t:vx")) != EOF) switch(c) { case 'd': radius_dir = optarg; break; case 'f': filename = optarg; break; case 'r': if (!isdigit((int) *optarg)) usage(); retries = atoi(optarg); if ((retries == 0) || (retries > 1000)) usage(); break; case 't': if (!isdigit((int) *optarg)) usage(); timeout = atof(optarg); break; case 'v': printf("%s\n", dhcpclient_version); exit(0); break; case 'x': fr_debug_flag++; fr_log_fp = stdout; break; case 'h': default: usage(); break; } argc -= (optind - 1); argv += (optind - 1); if (argc < 2) usage(); if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { fr_perror("dhcpclient"); return 1; } /* * Resolve hostname. */ server_ipaddr.af = AF_INET; 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, AF_INET, &server_ipaddr) < 0) { fprintf(stderr, "dhcpclient: Failed to find IP address for host %s: %s\n", hostname, fr_syserror(errno)); exit(1); } /* * Strip port from hostname if needed. */ if (portname) server_port = atoi(portname); } /* * See what kind of request we want to send. */ if (strcmp(argv[2], "discover") == 0) { if (server_port == 0) server_port = 67; packet_code = PW_DHCP_DISCOVER; } else if (strcmp(argv[2], "request") == 0) { if (server_port == 0) server_port = 67; packet_code = PW_DHCP_REQUEST; } else if (strcmp(argv[2], "offer") == 0) { if (server_port == 0) server_port = 67; packet_code = PW_DHCP_OFFER; } else if (isdigit((int) argv[2][0])) { if (server_port == 0) server_port = 67; packet_code = atoi(argv[2]); } else { fprintf(stderr, "Unknown packet type %s\n", argv[2]); usage(); } request_init(filename); /* * No data read. Die. */ if (!request || !request->vps) { fprintf(stderr, "dhcpclient: Nothing to send.\n"); exit(1); } request->code = packet_code; /* * Bind to the first specified IP address and port. * This means we ignore later ones. */ if (request->src_ipaddr.af == AF_UNSPEC) { memset(&client_ipaddr, 0, sizeof(client_ipaddr)); client_ipaddr.af = server_ipaddr.af; client_port = 0; } else { client_ipaddr = request->src_ipaddr; client_port = request->src_port; } sockfd = fr_socket(&client_ipaddr, client_port); if (sockfd < 0) { fprintf(stderr, "dhcpclient: socket: %s\n", fr_strerror()); exit(1); } request->sockfd = sockfd; if (request->src_ipaddr.af == AF_UNSPEC) { request->src_ipaddr = client_ipaddr; request->src_port = client_port; } if (request->dst_ipaddr.af == AF_UNSPEC) { request->dst_ipaddr = server_ipaddr; request->dst_port = server_port; } /* * Encode the packet */ if (fr_dhcp_encode(request) < 0) { fprintf(stderr, "dhcpclient: failed encoding: %s\n", fr_strerror()); exit(1); } if (fr_debug_flag) print_hex(request); if (fr_dhcp_send(request) < 0) { fprintf(stderr, "dhcpclient: failed sending: %s\n", fr_syserror(errno)); exit(1); } reply = fr_dhcp_recv(sockfd); if (!reply) { fprintf(stderr, "dhcpclient: Error receiving reply %s\n", fr_strerror()); exit(1); } if (fr_debug_flag) print_hex(reply); if (fr_dhcp_decode(reply) < 0) { fprintf(stderr, "dhcpclient: failed decoding\n"); return 1; } dict_free(); if (success) return 0; return 1; }
/* * Do detail, compatible with old accounting */ static rlm_rcode_t CC_HINT(nonnull) detail_do(void *instance, REQUEST *request, RADIUS_PACKET *packet, bool compat) { int outfd; char buffer[DIRLEN]; FILE *outfp; #ifdef HAVE_GRP_H gid_t gid; struct group *grp; char *endptr; #endif detail_instance_t *inst = instance; /* * Generate the path for the detail file. Use the same * format, but truncate at the last /. Then feed it * through radius_xlat() to expand the variables. */ if (radius_xlat(buffer, sizeof(buffer), request, inst->filename, NULL, NULL) < 0) { return RLM_MODULE_FAIL; } RDEBUG2("%s expands to %s", inst->filename, buffer); #ifdef WITH_ACCOUNTING #if defined(HAVE_FNMATCH_H) && defined(FNM_FILE_NAME) /* * If we read it from a detail file, and we're about to * write it back to the SAME detail file directory, then * suppress the write. This check prevents an infinite * loop. */ if ((request->listener->type == RAD_LISTEN_DETAIL) && (fnmatch(((listen_detail_t *)request->listener->data)->filename, buffer, FNM_FILE_NAME | FNM_PERIOD ) == 0)) { RWDEBUG2("Suppressing infinite loop"); return RLM_MODULE_NOOP; } #endif #endif outfd = fr_logfile_open(inst->lf, buffer, inst->perm); if (outfd < 0) { RERROR("Couldn't open file %s: %s", buffer, fr_strerror()); return RLM_MODULE_FAIL; } #ifdef HAVE_GRP_H if (inst->group != NULL) { gid = strtol(inst->group, &endptr, 10); if (*endptr != '\0') { grp = rad_getgrnam(inst->group); if (!grp) { RDEBUG2("Unable to find system group '%s'", inst->group); goto skip_group; } gid = grp->gr_gid; } if (chown(buffer, -1, gid) == -1) { RDEBUG2("Unable to change system group of '%s'", buffer); } } skip_group: #endif /* * Open the output fp for buffering. */ if ((outfp = fdopen(outfd, "a")) == NULL) { RERROR("Couldn't open file %s: %s", buffer, fr_syserror(errno)); fail: if (outfp) fclose(outfp); fr_logfile_unlock(inst->lf, outfd); return RLM_MODULE_FAIL; } if (detail_write(outfp, inst, request, packet, compat) < 0) goto fail; /* * Flush everything */ fclose(outfp); fr_logfile_unlock(inst->lf, outfd); /* do NOT close outfp */ /* * And everything is fine. */ return RLM_MODULE_OK; }
/** Convert attribute map into valuepairs * * Use the attribute map built earlier to convert LDAP values into valuepairs and insert them into whichever * list they need to go into. * * This is *NOT* atomic, but there's no condition for which we should error out... * * @param[in] inst rlm_ldap configuration. * @param[in] request Current request. * @param[in] handle associated with entry. * @param[in] expanded attributes (rhs of map). * @param[in] entry to retrieve attributes from. * @return * - Number of maps successfully applied. * - -1 on failure. */ int rlm_ldap_map_do(const rlm_ldap_t *inst, REQUEST *request, LDAP *handle, rlm_ldap_map_exp_t const *expanded, LDAPMessage *entry) { vp_map_t const *map; unsigned int total = 0; int applied = 0; /* How many maps have been applied to the current request */ rlm_ldap_result_t result; char const *name; RINDENT(); for (map = expanded->maps; map != NULL; map = map->next) { int ret; name = expanded->attrs[total++]; /* * Binary safe */ result.values = ldap_get_values_len(handle, entry, name); if (!result.values) { RDEBUG3("Attribute \"%s\" not found in LDAP object", name); goto next; } /* * Find out how many values there are for the * attribute and extract all of them. */ result.count = ldap_count_values_len(result.values); /* * If something bad happened, just skip, this is probably * a case of the dst being incorrect for the current * request context */ ret = map_to_request(request, map, rlm_ldap_map_getvalue, &result); if (ret == -1) return -1; /* Fail */ /* * How many maps we've processed */ applied++; next: ldap_value_free_len(result.values); } REXDENT(); /* * Retrieve any valuepair attributes from the result, these are generic values specifying * a radius list, operator and value. */ if (inst->valuepair_attr) { struct berval **values; int count, i; values = ldap_get_values_len(handle, entry, inst->valuepair_attr); count = ldap_count_values_len(values); RINDENT(); for (i = 0; i < count; i++) { vp_map_t *attr; char *value; value = rlm_ldap_berval_to_string(request, values[i]); RDEBUG3("Parsing attribute string '%s'", value); if (map_afrom_attr_str(request, &attr, value, REQUEST_CURRENT, PAIR_LIST_REPLY, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { RWDEBUG("Failed parsing '%s' value \"%s\" as valuepair (%s), skipping...", fr_strerror(), inst->valuepair_attr, value); talloc_free(value); continue; } if (map_to_request(request, attr, map_to_vp, NULL) < 0) { RWDEBUG("Failed adding \"%s\" to request, skipping...", value); } else { applied++; } talloc_free(attr); talloc_free(value); } REXDENT(); ldap_value_free_len(values); } return applied; }
/** Builds attribute representing OID string and adds 'index' attributes where required * * Will convert an OID string in the format @verbatim .1.2.3.4.5.0 @endverbatim * into a pair with a #fr_dict_attr_t of the dictionary attribute matching the OID * string, as evaluated from the specified parent. * * If an OID component does not match a child of a previous OID component, but a child * with attribute number 0 exists, and a child with attribute number 1 also exists, * the child with attribute number 0 will be used as an 'index' pair, and will be * created with the value of the non matching OID component. * * Parsing will then resume using the child with attribute number 1. * * This allows traversals of SNMP tables to be represented by the sequence of pairs * and allows the full range of entry indexes which would not be possible if we represented * table index numbers as TLV attributes. * * @param[in] ctx to allocate new pairs in. * @param[in] conf radsnmp config. * @param[in] cursor to add pairs to. * @param[in] oid string to evaluate. * @param[in] type SNMP value type. * @param[in] value to assign to OID attribute (SET operations only). * @return * - >0 on success (how much of the OID string we parsed). * - <=0 on failure (where format error occurred). */ static ssize_t radsnmp_pair_from_oid(TALLOC_CTX *ctx, radsnmp_conf_t *conf, fr_cursor_t *cursor, char const *oid, int type, char const *value) { ssize_t slen; char const *p = oid; unsigned int attr; fr_dict_attr_t const *index_attr, *da; fr_dict_attr_t const *parent = conf->snmp_root; VALUE_PAIR *vp; int ret; if (!oid) return 0; fr_cursor_tail(cursor); /* * Trim first. */ if (p[0] == '.') p++; /* * Support for indexes. If we can't find an attribute * matching a child at a given level in the OID tree, * look for attribute 0 (type integer) at that level. * We use this to represent the index instead. */ for (;;) { unsigned int num = 0; slen = fr_dict_attr_by_oid(conf->dict, &parent, &attr, p); if (slen > 0) break; p += -(slen); if (fr_dict_oid_component(&num, &p) < 0) break; /* Just advances the pointer */ assert(attr == num); p++; /* * Check for an index attribute */ index_attr = fr_dict_attr_child_by_num(parent, 0); if (!index_attr) { fr_strerror_printf("Unknown OID component: No index attribute at this level"); break; } if (index_attr->type != FR_TYPE_UINT32) { fr_strerror_printf("Index is not a \"integer\""); break; } /* * By convention SNMP entries are at .1 */ parent = fr_dict_attr_child_by_num(parent, 1); if (!parent) { fr_strerror_printf("Unknown OID component: No entry attribute at this level"); break; } /* * Entry must be a TLV type */ if (parent->type != FR_TYPE_TLV) { fr_strerror_printf("Entry is not \"tlv\""); break; } /* * We've skipped over the index attribute, and * the index number should be available in attr. */ MEM(vp = fr_pair_afrom_da(ctx, index_attr)); vp->vp_uint32 = attr; fr_cursor_append(cursor, vp); } /* * We errored out processing the OID. */ if (slen <= 0) { error: fr_cursor_free_list(cursor); return slen; } fr_strerror(); /* Clear pending errors */ /* * SNMP requests the leaf under the OID with .0. */ if (attr != 0) { da = fr_dict_attr_child_by_num(parent, attr); if (!da) { fr_strerror_printf("Unknown leaf attribute %i", attr); return -(slen); } } else { da = parent; } vp = fr_pair_afrom_da(ctx, da); if (!vp) { fr_strerror_printf("Failed allocating OID attribute"); return -(slen); } /* * VALUE_PAIRs with no value need a 1 byte value buffer. */ if (!value) { switch (da->type) { /* * We can blame the authors of RFC 6929 for * this hack. Apparently the presence or absence * of an attribute isn't considered a useful means * of conveying information, so empty TLVs are * disallowed. */ case FR_TYPE_TLV: fr_pair_to_unknown(vp); /* FALL-THROUGH */ case FR_TYPE_OCTETS: fr_pair_value_memcpy(vp, (uint8_t const *)"\0", 1, true); break; case FR_TYPE_STRING: fr_pair_value_bstrncpy(vp, "\0", 1); break; /* * Fine to leave other values zeroed out. */ default: break; } fr_cursor_append(cursor, vp); return slen; } if (da->type == FR_TYPE_TLV) { fr_strerror_printf("TLVs cannot hold values"); return -(slen); } ret = fr_pair_value_from_str(vp, value, strlen(value), '\0', true); if (ret < 0) { slen = -(slen); goto error; } vp = fr_pair_afrom_da(ctx, attr_freeradius_snmp_type); if (!vp) { slen = -(slen); goto error; } vp->vp_uint32 = type; fr_cursor_append(cursor, vp); return slen; }
/* * Recieve packets from a proxy socket. */ static int proxy_socket_recv(rad_listen_t *listener, RAD_REQUEST_FUNP *pfun, REQUEST **prequest) { REQUEST *request; RADIUS_PACKET *packet; char buffer[128]; RAD_REQUEST_FUNP fun = NULL; packet = rad_recv(listener->fd, 0); if (!packet) { radlog(L_ERR, "%s", fr_strerror()); return 0; } /* * FIXME: Client MIB updates? */ switch(packet->code) { case PW_AUTHENTICATION_ACK: case PW_ACCESS_CHALLENGE: case PW_AUTHENTICATION_REJECT: #ifdef WITH_ACCOUNTING case PW_ACCOUNTING_RESPONSE: #endif break; #ifdef WITH_COA case PW_DISCONNECT_ACK: case PW_DISCONNECT_NAK: case PW_COA_ACK: case PW_COA_NAK: break; #endif default: /* * FIXME: Update MIB for packet types? */ radlog(L_ERR, "Invalid packet code %d sent to a proxy port " "from home server %s port %d - ID %d : IGNORED", packet->code, ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)), packet->src_port, packet->id); rad_free(&packet); return 0; } request = received_proxy_response(packet); if (!request) { return 0; } rad_assert(request->process != NULL); #ifdef WITH_COA /* * Distinguish proxied CoA requests from ones we * originate. If we've proxied a DIFFERENT packet type * than the original, then it MUST be a CoA packet. In * that case, we process it as a CoA reply packet, rather * than re-running the original method. */ if (request->packet->code != request->proxy->code) { rad_assert((request->proxy->code == PW_COA_REQUEST) || (request->proxy->code == PW_DISCONNECT_REQUEST)); fun = rad_coa_reply; /* run NEW function */ } else #endif *pfun = request->process; /* re-run original function */ *prequest = request; return 1; }