/* * The "free" functions are here, for no particular reason. */ void rlm_policy_free_item(policy_item_t *item) { while (item) { policy_item_t *next = item->next; switch (item->type) { default: case POLICY_TYPE_BAD: break; case POLICY_TYPE_ASSIGNMENT: { policy_assignment_t *this; this = (policy_assignment_t *) item; if (this->lhs) free(this->lhs); if (this->rhs) free(this->rhs); } break; case POLICY_TYPE_CONDITIONAL: { policy_condition_t *this; this = (policy_condition_t *) item; if (this->lhs) free(this->lhs); if (this->rhs) free(this->rhs); if (this->child) { rlm_policy_free_item(this->child); this->child = NULL; } } break; case POLICY_TYPE_IF: { policy_if_t *this; this = (policy_if_t *) item; if (this->condition) { rlm_policy_free_item(this->condition); this->condition = NULL; } if (this->if_true) { rlm_policy_free_item(this->if_true); this->if_true = NULL; } if (this->if_false) { rlm_policy_free_item(this->if_false); this->if_false = NULL; } } break; case POLICY_TYPE_ATTRIBUTE_LIST: { policy_attributes_t *this; this = (policy_attributes_t *) item; rlm_policy_free_item(this->attributes); } break; case POLICY_TYPE_NAMED_POLICY: { policy_named_t *this; this = (policy_named_t *) item; rad_assert(this->name != NULL); free(this->name); rlm_policy_free_item(this->policy); } break; case POLICY_TYPE_CALL: { policy_call_t *this; this = (policy_call_t *) item; if (this->name) free(this->name); } break; case POLICY_TYPE_RETURN: break; /* do nothing */ case POLICY_TYPE_MODULE: { policy_module_t *this; this = (policy_module_t *) item; if (this->cs) cf_section_free(&this->cs); if (this->mc) modcallable_free(&this->mc); } break; } /* switch over type */ item->next = NULL; /* for debugging & sanity checks */ item->type = POLICY_TYPE_BAD; free(item); item = next; } }
int dual_tls_recv(rad_listen_t *listener) { RADIUS_PACKET *packet; REQUEST *request; RAD_REQUEST_FUNP fun = NULL; listen_socket_t *sock = listener->data; RADCLIENT *client = sock->client; if (!tls_socket_recv(listener)) { return 0; } rad_assert(sock->request != NULL); rad_assert(sock->request->packet != NULL); rad_assert(sock->packet != NULL); rad_assert(sock->ssn != NULL); rad_assert(client != NULL); request = sock->request; packet = sock->packet; /* * Some sanity checks, based on the packet code. */ switch(packet->code) { case PW_CODE_AUTHENTICATION_REQUEST: if (listener->type != RAD_LISTEN_AUTH) goto bad_packet; FR_STATS_INC(auth, total_requests); fun = rad_authenticate; break; case PW_CODE_ACCOUNTING_REQUEST: if (listener->type != RAD_LISTEN_ACCT) goto bad_packet; FR_STATS_INC(acct, total_requests); fun = rad_accounting; break; case PW_CODE_STATUS_SERVER: if (!mainconfig.status_server) { FR_STATS_INC(auth, total_unknown_types); WDEBUG("Ignoring Status-Server request due to security configuration"); rad_free(&sock->packet); request->packet = NULL; return 0; } fun = rad_status_server; break; default: bad_packet: FR_STATS_INC(auth, total_unknown_types); DEBUG("Invalid packet code %d sent from client %s port %d : IGNORED", packet->code, client->shortname, packet->src_port); rad_free(&sock->packet); request->packet = NULL; return 0; } /* switch over packet types */ if (!request_receive(listener, packet, client, fun)) { FR_STATS_INC(auth, total_packets_dropped); rad_free(&sock->packet); request->packet = NULL; return 0; } sock->packet = NULL; /* we have no need for more partial reads */ request->packet = NULL; return 1; }
/* * 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: fun = rad_coa_reply; /* run NEW function */ 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 if (!fun) #endif fun = request->process; /* re-run original function */ *pfun = fun; *prequest = request; return 1; }
/* * The "detail.work" file exists, and is open in the 'fd'. */ static int work_exists(proto_detail_file_thread_t *thread, int fd) { proto_detail_file_t const *inst = thread->inst; bool opened = false; proto_detail_work_thread_t *work; fr_listen_t *li = NULL; struct stat st; fr_event_vnode_func_t funcs = { .delete = mod_vnode_delete }; DEBUG3("proto_detail (%s): Trying to lock %s", thread->name, inst->filename_work); /* * "detail.work" exists, try to lock it. */ if (rad_lockfd_nonblock(fd, 0) < 0) { struct timeval when, now; DEBUG3("proto_detail (%s): Failed locking %s: %s", thread->name, inst->filename_work, fr_syserror(errno)); close(fd); when.tv_usec = thread->lock_interval % USEC; when.tv_sec = thread->lock_interval / USEC; /* * Ensure that we don't do massive busy-polling. */ thread->lock_interval += thread->lock_interval / 2; if (thread->lock_interval > (30 * USEC)) thread->lock_interval = 30 * USEC; DEBUG3("proto_detail (%s): Waiting %d.%06ds for lock on file %s", thread->name, (int) when.tv_sec, (int) when.tv_usec, inst->filename_work); gettimeofday(&now, NULL); fr_timeval_add(&when, &when, &now); if (fr_event_timer_insert(thread, thread->el, &thread->ev, &when, work_retry_timer, thread) < 0) { ERROR("Failed inserting retry timer for %s", inst->filename_work); } return 0; } DEBUG3("proto_detail (%s): Obtained lock and starting to process file %s", thread->name, inst->filename_work); /* * Ignore empty files. */ if (fstat(fd, &st) < 0) { ERROR("Failed opening %s: %s", inst->filename_work, fr_syserror(errno)); unlink(inst->filename_work); close(fd); return 1; } if (!st.st_size) { DEBUG3("proto_detail (%s): %s file is empty, ignoring it.", thread->name, inst->filename_work); unlink(inst->filename_work); close(fd); return 1; } /* * This listener is allocated in a thread-specific * context, so it doesn't need a destructor, */ MEM(li = talloc_zero(NULL, fr_listen_t)); /* * Create a new listener, and insert it into the * scheduler. Shamelessly copied from proto_detail.c * mod_open(), with changes. * * This listener is parented from the worker. So that * when the worker goes away, so does the listener. */ li->app_io = inst->parent->work_io; li->app = inst->parent->self; li->app_instance = inst->parent; li->server_cs = inst->parent->server_cs; /* * The worker may be in a different thread, so avoid * talloc threading issues by using a NULL TALLOC_CTX. */ MEM(li->thread_instance = work = talloc_zero(li, proto_detail_work_thread_t)); li->app_io_instance = inst->parent->work_io_instance; work->inst = li->app_io_instance; work->file_parent = thread; work->ev = NULL; li->fd = work->fd = dup(fd); if (work->fd < 0) { DEBUG("proto_detail (%s): Failed opening %s: %s", thread->name, inst->filename_work, fr_syserror(errno)); close(fd); talloc_free(li); return -1; } /* * Don't do anything until the file has been deleted. * * @todo - ensure that proto_detail_work is done the file... * maybe by creating a new instance? */ if (fr_event_filter_insert(thread, thread->el, fd, FR_EVENT_FILTER_VNODE, &funcs, NULL, thread) < 0) { PERROR("Failed adding work socket to event loop"); close(fd); talloc_free(li); return -1; } /* * Remember this for later. */ thread->vnode_fd = fd; /* * For us, this is the worker listener. * For the worker, this is it's own parent */ thread->listen = li; work->filename_work = talloc_strdup(work, inst->filename_work); /* * Set configurable parameters for message ring buffer. */ li->default_message_size = inst->parent->max_packet_size; li->num_messages = inst->parent->num_messages; pthread_mutex_lock(&thread->worker_mutex); thread->num_workers++; pthread_mutex_unlock(&thread->worker_mutex); /* * Open the detail.work file. */ if (li->app_io->open(li) < 0) { ERROR("Failed opening %s", li->app_io->name); goto error; } opened = true; rad_assert(li->app_io->get_name); li->name = li->app_io->get_name(li); if (!fr_schedule_listen_add(inst->parent->sc, li)) { error: if (fr_event_fd_delete(thread->el, thread->vnode_fd, FR_EVENT_FILTER_VNODE) < 0) { PERROR("Failed removing DELETE callback when opening work file"); } close(thread->vnode_fd); thread->vnode_fd = -1; if (opened) { (void) li->app_io->close(li); thread->listen = NULL; li = NULL; } talloc_free(li); return -1; } /* * Tell the worker to clean itself up. */ work->listen = li; return 0; }
static int mod_bootstrap(void *instance, CONF_SECTION *cs) { proto_detail_file_t *inst = talloc_get_type_abort(instance, proto_detail_file_t); dl_instance_t const *dl_inst; char *p; #ifdef __linux__ /* * The kqueue API takes an FD, but inotify requires a filename. * libkqueue uses /proc/PID/fd/# to look up the FD -> filename mapping. * * However, if you start the server as "root", and then swap to "radiusd", * /proc/PID will be owned by "root" for security reasons. The only way * to make /proc/PID owned by "radiusd" is to set the DUMPABLE flag. * * Instead of making the poor sysadmin figure this out, * we check for this situation, and give them a * descriptive message telling them what to do. */ if (!main_config->allow_core_dumps && main_config->uid_is_set && main_config->server_uid != 0) { cf_log_err(cs, "Cannot start detail file reader due to Linux limitations."); cf_log_err(cs, "Please set 'allow_core_dumps = true' in the main configuration file."); return -1; } #endif /* * Find the dl_instance_t holding our instance data * so we can find out what the parent of our instance * was. */ dl_inst = dl_instance_by_data(instance); rad_assert(dl_inst); #ifndef __linux__ /* * Linux inotify works. So we allow poll_interval==0 */ FR_INTEGER_BOUND_CHECK("poll_interval", inst->poll_interval, >=, 1); #endif FR_INTEGER_BOUND_CHECK("poll_interval", inst->poll_interval, <=, 3600); inst->parent = talloc_get_type_abort(dl_inst->parent->data, proto_detail_t); inst->cs = cs; inst->directory = p = talloc_strdup(inst, inst->filename); p = strrchr(p, '/'); if (!p) { cf_log_err(cs, "Filename must contain '/'"); return -1; } *p = '\0'; if (!inst->filename_work) { inst->filename_work = talloc_typed_asprintf(inst, "%s/detail.work", inst->directory); } /* * We need this for the lock. */ inst->mode = O_RDWR; return 0; }
static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * const node, RADIUS_ESCAPE_STRING escape, void *escape_ctx, int lvl) { ssize_t rcode; char *str = NULL, *child; REQUEST *ref; XLAT_DEBUG("%.*sxlat aprint %d", lvl, xlat_spaces, node->type); switch (node->type) { /* * Don't escape this. */ case XLAT_LITERAL: XLAT_DEBUG("xlat_aprint LITERAL"); return talloc_strdup(ctx, node->fmt); /* * Do a one-character expansion. */ case XLAT_PERCENT: { char const *p; char *nl; size_t freespace = 256; struct tm ts; time_t when; XLAT_DEBUG("xlat_aprint PERCENT"); str = talloc_array(ctx, char, freespace); /* @todo do better allocation */ p = node->fmt; when = request->timestamp; if (request->packet) { when = request->packet->timestamp.tv_sec; } switch (*p) { case '%': str[0] = '%'; str[1] = '\0'; break; case 'd': /* request day */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%d", &ts); break; case 'l': /* request timestamp */ snprintf(str, freespace, "%lu", (unsigned long) when); break; case 'm': /* request month */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%m", &ts); break; case 't': /* request timestamp */ CTIME_R(&when, str, freespace); nl = strchr(str, '\n'); if (nl) *nl = '\0'; break; case 'D': /* request date */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%Y%m%d", &ts); break; case 'G': /* request minute */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%M", &ts); break; case 'H': /* request hour */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%H", &ts); break; case 'I': /* Request ID */ if (request->packet) { snprintf(str, freespace, "%i", request->packet->id); } break; case 'S': /* request timestamp in SQL format*/ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%Y-%m-%d %H:%M:%S", &ts); break; case 'T': /* request timestamp */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%Y-%m-%d-%H.%M.%S.000000", &ts); break; case 'Y': /* request year */ if (!localtime_r(&when, &ts)) { error: REDEBUG("Failed converting packet timestamp to localtime: %s", fr_syserror(errno)); talloc_free(str); return NULL; } strftime(str, freespace, "%Y", &ts); break; default: rad_assert(0 == 1); break; } } break; case XLAT_ATTRIBUTE: XLAT_DEBUG("xlat_aprint ATTRIBUTE"); ref = request; if (radius_request(&ref, node->ref) < 0) { return NULL; } /* * Some attributes are virtual <sigh> */ str = xlat_getvp(ctx, ref, node->list, node->da, node->tag, node->num, true); if (str) { XLAT_DEBUG("expand attr %s --> '%s'", node->da->name, str); } break; case XLAT_VIRTUAL: XLAT_DEBUG("xlat_aprint VIRTUAL"); str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */ rcode = node->xlat->func(node->xlat->instance, request, NULL, str, 1024); if (rcode < 0) { talloc_free(str); return NULL; } break; case XLAT_MODULE: XLAT_DEBUG("xlat_aprint MODULE"); if (xlat_process(&child, request, node->child, node->xlat->escape, node->xlat->instance) == 0) { return NULL; } XLAT_DEBUG("%.*sexpand mod %s --> '%s'", lvl, xlat_spaces, node->fmt, child); str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */ *str = '\0'; /* Be sure the string is NULL terminated, we now only free on error */ rcode = node->xlat->func(node->xlat->instance, request, child, str, 1024); talloc_free(child); if (rcode < 0) { talloc_free(str); return NULL; } break; #ifdef HAVE_REGEX_H case XLAT_REGEX: XLAT_DEBUG("xlat_aprint REGEX"); child = request_data_reference(request, request, REQUEST_DATA_REGEX | node->num); if (!child) return NULL; str = talloc_strdup(ctx, child); break; #endif case XLAT_ALTERNATE: XLAT_DEBUG("xlat_aprint ALTERNATE"); rad_assert(node->child != NULL); rad_assert(node->alternate != NULL); str = xlat_aprint(ctx, request, node->child, escape, escape_ctx, lvl); if (str) break; str = xlat_aprint(ctx, request, node->alternate, escape, escape_ctx, lvl); break; } /* * Escape the non-literals we found above. */ if (str && escape) { char *escaped; escaped = talloc_array(ctx, char, 1024); /* FIXME: do something intelligent */ escape(request, escaped, 1024, str, escape_ctx); talloc_free(str); str = escaped; }
static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head, char const **error) { ssize_t slen; char *p; xlat_exp_t *node; rad_assert(fmt[0] == '%'); rad_assert(fmt[1] == '{'); rad_assert(fmt[2] == '%'); rad_assert(fmt[3] == '{'); XLAT_DEBUG("ALTERNATE: %s", fmt); node = talloc_zero(ctx, xlat_exp_t); node->type = XLAT_ALTERNATE; p = fmt + 2; slen = xlat_tokenize_expansion(node, p, &node->child, error); if (slen <= 0) { talloc_free(node); return slen - (p - fmt); } p += slen; if (p[0] != ':') { talloc_free(node); *error = "Expected ':' after first expansion"; return -(p - fmt); } p++; if (p[0] != '-') { talloc_free(node); *error = "Expected '-' after ':'"; return -(p - fmt); } p++; /* * Allow the RHS to be empty as a special case. */ if (*p == '}') { /* * Hack up an empty string. */ node->alternate = talloc_zero(node, xlat_exp_t); node->alternate->type = XLAT_LITERAL; node->alternate->fmt = talloc_strdup(node->alternate, ""); *(p++) = '\0'; } else { slen = xlat_tokenize_literal(node, p, &node->alternate, true, error); if (slen <= 0) { talloc_free(node); return slen - (p - fmt); } if (!node->alternate) { talloc_free(node); *error = "Empty expansion is invalid"; return -(p - fmt); } p += slen; } *head = node; return p - fmt; }
/* * Allocate the thread pool, and seed it with an initial number * of threads. * * FIXME: What to do on a SIGHUP??? */ int thread_pool_init(CONF_SECTION *cs, int *spawn_flag) { int i, rcode; CONF_SECTION *pool_cf; time_t now; now = time(NULL); rad_assert(spawn_flag != NULL); rad_assert(*spawn_flag == TRUE); rad_assert(pool_initialized == FALSE); /* not called on HUP */ pool_cf = cf_subsection_find_next(cs, NULL, "thread"); if (!pool_cf) *spawn_flag = FALSE; /* * Initialize the thread pool to some reasonable values. */ memset(&thread_pool, 0, sizeof(THREAD_POOL)); thread_pool.head = NULL; thread_pool.tail = NULL; thread_pool.total_threads = 0; thread_pool.max_thread_num = 1; thread_pool.cleanup_delay = 5; thread_pool.spawn_flag = *spawn_flag; /* * Don't bother initializing the mutexes or * creating the hash tables. They won't be used. */ if (!*spawn_flag) return 0; #ifdef WNOHANG if ((pthread_mutex_init(&thread_pool.wait_mutex,NULL) != 0)) { radlog(L_ERR, "FATAL: Failed to initialize wait mutex: %s", strerror(errno)); return -1; } /* * Create the hash table of child PID's */ thread_pool.waiters = fr_hash_table_create(pid_hash, pid_cmp, free); if (!thread_pool.waiters) { radlog(L_ERR, "FATAL: Failed to set up wait hash"); return -1; } #endif if (cf_section_parse(pool_cf, NULL, thread_config) < 0) { return -1; } /* * Catch corner cases. */ if (thread_pool.min_spare_threads < 1) thread_pool.min_spare_threads = 1; if (thread_pool.max_spare_threads < 1) thread_pool.max_spare_threads = 1; if (thread_pool.max_spare_threads < thread_pool.min_spare_threads) thread_pool.max_spare_threads = thread_pool.min_spare_threads; /* * The pool has already been initialized. Don't spawn * new threads, and don't forget about forked children, */ if (pool_initialized) { return 0; } /* * Initialize the queue of requests. */ memset(&thread_pool.semaphore, 0, sizeof(thread_pool.semaphore)); rcode = sem_init(&thread_pool.semaphore, 0, SEMAPHORE_LOCKED); if (rcode != 0) { radlog(L_ERR, "FATAL: Failed to initialize semaphore: %s", strerror(errno)); return -1; } rcode = pthread_mutex_init(&thread_pool.queue_mutex,NULL); if (rcode != 0) { radlog(L_ERR, "FATAL: Failed to initialize queue mutex: %s", strerror(errno)); return -1; } /* * Allocate multiple fifos. */ for (i = 0; i < RAD_LISTEN_MAX; i++) { thread_pool.fifo[i] = fr_fifo_create(65536, NULL); if (!thread_pool.fifo[i]) { radlog(L_ERR, "FATAL: Failed to set up request fifo"); return -1; } } #ifdef HAVE_OPENSSL_CRYPTO_H /* * If we're linking with OpenSSL too, then we need * to set up the mutexes and enable the thread callbacks. */ if (!setup_ssl_mutexes()) { radlog(L_ERR, "FATAL: Failed to set up SSL mutexes"); return -1; } #endif /* * Create a number of waiting threads. * * If we fail while creating them, do something intelligent. */ for (i = 0; i < thread_pool.start_threads; i++) { if (spawn_thread(now) == NULL) { return -1; } } DEBUG2("Thread pool initialized"); pool_initialized = TRUE; return 0; }
/** Evaluate a template * * @param[in] request the REQUEST * @param[in] modreturn the previous module return code * @param[in] depth of the recursion (only used for debugging) * @param[in] vpt the template to evaluate * @return -1 on error, 0 for "no match", 1 for "match". */ int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth, value_pair_tmpl_t const *vpt) { int rcode; int modcode; char *buffer; switch (vpt->type) { case VPT_TYPE_LITERAL: modcode = fr_str2int(modreturn_table, vpt->name, RLM_MODULE_UNKNOWN); if (modcode != RLM_MODULE_UNKNOWN) { rcode = (modcode == modreturn); break; } /* * Else it's a literal string. Empty string is * false, non-empty string is true. * * @todo: Maybe also check for digits? * * The VPT *doesn't* have a "bare word" type, * which arguably it should. */ rcode = (vpt->name != '\0'); break; case VPT_TYPE_ATTR: case VPT_TYPE_LIST: if (radius_tmpl_get_vp(NULL, request, vpt) == 0) { rcode = true; } else { rcode = false; } break; /* * FIXME: expand the strings * if not empty, return! */ case VPT_TYPE_XLAT_STRUCT: case VPT_TYPE_XLAT: case VPT_TYPE_EXEC: if (!*vpt->name) return false; rcode = radius_expand_tmpl(&buffer, request, vpt); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return -1; } rcode = (buffer && (*buffer != '\0')); talloc_free(buffer); break; /* * Can't have a bare ... (/foo/) ... */ case VPT_TYPE_REGEX: case VPT_TYPE_REGEX_STRUCT: EVAL_DEBUG("FAIL %d", __LINE__); rad_assert(0 == 1); /* FALL-THROUGH */ default: rcode = -1; break; } return rcode; }
/* * The main thread handler for requests. * * Wait on the semaphore until we have it, and process the request. */ static void *request_handler_thread(void *arg) { RAD_REQUEST_FUNP fun; THREAD_HANDLE *self = (THREAD_HANDLE *) arg; /* * Loop forever, until told to exit. */ do { /* * Wait to be signalled. */ DEBUG2("Thread %d waiting to be assigned a request", self->thread_num); re_wait: if (sem_wait(&thread_pool.semaphore) != 0) { /* * Interrupted system call. Go back to * waiting, but DON'T print out any more * text. */ if (errno == EINTR) { DEBUG2("Re-wait %d", self->thread_num); goto re_wait; } radlog(L_ERR, "Thread %d failed waiting for semaphore: %s: Exiting\n", self->thread_num, strerror(errno)); break; } DEBUG2("Thread %d got semaphore", self->thread_num); #ifdef HAVE_OPENSSL_ERR_H /* * Clear the error queue for the current thread. */ ERR_clear_error (); #endif /* * Try to grab a request from the queue. * * It may be empty, in which case we fail * gracefully. */ if (!request_dequeue(&self->request, &fun)) continue; self->request->child_pid = self->pthread_id; self->request_count++; DEBUG2("Thread %d handling request %d, (%d handled so far)", self->thread_num, self->request->number, self->request_count); radius_handle_request(self->request, fun); /* * Update the active threads. */ pthread_mutex_lock(&thread_pool.queue_mutex); rad_assert(thread_pool.active_threads > 0); thread_pool.active_threads--; pthread_mutex_unlock(&thread_pool.queue_mutex); } while (self->status != THREAD_CANCELLED); DEBUG2("Thread %d exiting...", self->thread_num); #ifdef HAVE_OPENSSL_ERR_H /* * If we linked with OpenSSL, the application * must remove the thread's error queue before * exiting to prevent memory leaks. */ ERR_remove_state(0); #endif /* * Do this as the LAST thing before exiting. */ self->request = NULL; self->status = THREAD_EXITED; return NULL; }
/* * Spawn a new thread, and place it in the thread pool. * * The thread is started initially in the blocked state, waiting * for the semaphore. */ static THREAD_HANDLE *spawn_thread(time_t now) { int rcode; THREAD_HANDLE *handle; pthread_attr_t attr; /* * Ensure that we don't spawn too many threads. */ if (thread_pool.total_threads >= thread_pool.max_threads) { DEBUG2("Thread spawn failed. Maximum number of threads (%d) already running.", thread_pool.max_threads); return NULL; } /* * Allocate a new thread handle. */ handle = (THREAD_HANDLE *) rad_malloc(sizeof(THREAD_HANDLE)); memset(handle, 0, sizeof(THREAD_HANDLE)); handle->prev = NULL; handle->next = NULL; handle->thread_num = thread_pool.max_thread_num++; handle->request_count = 0; handle->status = THREAD_RUNNING; handle->timestamp = time(NULL); /* * Initialize the thread's attributes to detached. * * We could call pthread_detach() later, but if the thread * exits between the create & detach calls, it will need to * be joined, which will never happen. */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); /* * Create the thread detached, so that it cleans up it's * own memory when it exits. * * Note that the function returns non-zero on error, NOT * -1. The return code is the error, and errno isn't set. */ rcode = pthread_create(&handle->pthread_id, &attr, request_handler_thread, handle); if (rcode != 0) { radlog(L_ERR, "Thread create failed: %s", strerror(rcode)); return NULL; } pthread_attr_destroy(&attr); /* * One more thread to go into the list. */ thread_pool.total_threads++; DEBUG2("Thread spawned new child %d. Total threads in pool: %d", handle->thread_num, thread_pool.total_threads); /* * Add the thread handle to the tail of the thread pool list. */ if (thread_pool.tail) { thread_pool.tail->next = handle; handle->prev = thread_pool.tail; thread_pool.tail = handle; } else { rad_assert(thread_pool.head == NULL); thread_pool.head = thread_pool.tail = handle; } /* * Update the time we last spawned a thread. */ thread_pool.time_last_spawned = now; /* * And return the new handle to the caller. */ return handle; }
/* * Remove a request from the queue. */ static int request_dequeue(REQUEST **prequest, RAD_REQUEST_FUNP *fun) { RAD_LISTEN_TYPE i, start; REQUEST *request = NULL; reap_children(); pthread_mutex_lock(&thread_pool.queue_mutex); /* * Clear old requests from all queues. * * We only do one pass over the queue, in order to * amortize the work across the child threads. Since we * do N checks for one request de-queued, the old * requests will be quickly cleared. */ for (i = 0; i < RAD_LISTEN_MAX; i++) { request = fr_fifo_peek(thread_pool.fifo[i]); if (!request || (request->master_state != REQUEST_STOP_PROCESSING)) { continue; } /* * This entry was marked to be stopped. Acknowledge it. */ request = fr_fifo_pop(thread_pool.fifo[i]); rad_assert(request != NULL); request->child_state = REQUEST_DONE; thread_pool.num_queued--; } start = 0; retry: /* * Pop results from the top of the queue */ for (i = start; i < RAD_LISTEN_MAX; i++) { request = fr_fifo_pop(thread_pool.fifo[i]); if (request) { start = i; break; } } if (!request) { pthread_mutex_unlock(&thread_pool.queue_mutex); *prequest = NULL; *fun = NULL; return 0; } rad_assert(thread_pool.num_queued > 0); thread_pool.num_queued--; *prequest = request; *fun = request->process; rad_assert(request->magic == REQUEST_MAGIC); rad_assert(*fun != NULL); /* * If the request has sat in the queue for too long, * kill it. * * The main clean-up code can't delete the request from * the queue, and therefore won't clean it up until we * have acknowledged it as "done". */ if (request->master_state == REQUEST_STOP_PROCESSING) { request->child_state = REQUEST_DONE; goto retry; } /* * The thread is currently processing a request. */ thread_pool.active_threads++; pthread_mutex_unlock(&thread_pool.queue_mutex); return 1; }
/* * Read config files. * * This function can ONLY be called from the main server process. */ int read_mainconfig(int reload) { char const *p = NULL; CONF_SECTION *cs; struct stat statbuf; cached_config_t *cc; char buffer[1024]; if (reload != 0) { ERROR("Reload is not implemented"); return -1; } 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 ..."); /* Initialize the dictionary */ if (!mainconfig.dictionary_dir) mainconfig.dictionary_dir = radius_dir; DEBUG2("including dictionary file %s/%s", mainconfig.dictionary_dir, RADIUS_DICTIONARY); if (dict_init(mainconfig.dictionary_dir, RADIUS_DICTIONARY) != 0) { ERROR("Errors reading dictionary: %s", fr_strerror()); return -1; } /* Read the configuration file */ snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf", radius_dir, mainconfig.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.dest == 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.dest = fr_str2int(log_str2dst, radlog_dest, L_DST_NUM_DEST); if (default_log.dest == L_DST_NUM_DEST) { fprintf(stderr, "radiusd: Error: Unknown log_destination %s\n", radlog_dest); cf_file_free(cs); return -1; } if (default_log.dest == 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; } mainconfig.syslog_facility = fr_str2int(syslog_str2fac, syslog_facility, -1); if (mainconfig.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, mainconfig.syslog_facility); #endif } else if (default_log.dest == L_DST_FILES) { if (!mainconfig.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)) 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.dest == L_DST_FILES) && (default_log.fd < 0)) { default_log.fd = open(mainconfig.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", mainconfig.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. */ if (do_colourise) { p = getenv("TERM"); if (!p || !isatty(default_log.fd) || (strstr(p, "xterm") == 0)) { mainconfig.colourise = false; } else { mainconfig.colourise = true; } p = NULL; } if (mainconfig.max_request_time == 0) mainconfig.max_request_time = 100; if (mainconfig.reject_delay > 5) mainconfig.reject_delay = 5; if (mainconfig.cleanup_delay > 5) mainconfig.cleanup_delay =5; /* * 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(mainconfig.config == NULL); root_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, NULL); xlat_register("client", xlat_client, NULL, 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) { ERROR("Failed to 'chdir %s' after chroot: %s", radlog_dir, fr_syserror(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; }
/** Logging callback to write log messages to a destination * * This allows the logging destination to be customised on a per request basis. * * @note Function does not write log output immediately * * @param[in] type What type of message this is (error, warn, info, debug). * @param[in] lvl At what logging level this message should be output. * @param[in] request The current request. * @param[in] file src file the log message was generated in. * @param[in] line number the log message was generated on. * @param[in] fmt sprintf style fmt string. * @param[in] ap Arguments for the fmt string. * @param[in] uctx Context data for the log function. */ static void logtee_it(fr_log_type_t type, fr_log_lvl_t lvl, REQUEST *request, UNUSED char const *file, UNUSED int line, char const *fmt, va_list ap, void *uctx) { rlm_logtee_thread_t *t = talloc_get_type_abort(uctx, rlm_logtee_thread_t); rlm_logtee_t const *inst = t->inst; char *msg, *exp; fr_cursor_t cursor; VALUE_PAIR *vp; log_dst_t *dst; rad_assert(t->msg->vp_length == 0); /* Should have been cleared before returning */ /* * None of this should involve mallocs unless msg > 1k */ msg = talloc_typed_vasprintf(t->msg, fmt, ap); fr_value_box_strdup_buffer_shallow(NULL, &t->msg->data, attr_log_message, msg, true); t->type->vp_uint32 = (uint32_t) type; t->lvl->vp_uint32 = (uint32_t) lvl; fr_cursor_init(&cursor, &request->packet->vps); fr_cursor_prepend(&cursor, t->msg); fr_cursor_prepend(&cursor, t->type); fr_cursor_prepend(&cursor, t->lvl); fr_cursor_head(&cursor); /* * Now expand our fmt string to encapsulate the * message and any metadata * * Fixme: Would be better to call tmpl_expand * into a variable length ring buffer. */ dst = request->log.dst; request->log.dst = NULL; if (tmpl_aexpand(t, &exp, request, inst->log_fmt, NULL, NULL) < 0) goto finish; request->log.dst = dst; fr_fring_overwrite(t->fring, exp); /* Insert it into the buffer */ if (!t->pending) { t->pending = true; logtee_fd_active(t); /* Listen for when the fd is writable */ } finish: /* * Don't free, we re-use the VALUE_PAIRs for the next message */ vp = fr_cursor_remove(&cursor); if (!fr_cond_assert(vp == t->lvl)) fr_cursor_append(&cursor, vp); vp = fr_cursor_remove(&cursor); if (!fr_cond_assert(vp == t->type)) fr_cursor_append(&cursor, vp); vp = fr_cursor_remove(&cursor); if (!fr_cond_assert(vp == t->msg)) fr_cursor_append(&cursor, vp); fr_value_box_clear(&t->msg->data); /* Clear message data */ }
size_t xlat_sprint(char *buffer, size_t bufsize, xlat_exp_t const *node) { size_t len; char *p, *end; if (!node) { *buffer = '\0'; return 0; } p = buffer; end = buffer + bufsize; while (node) { switch (node->type) { case XLAT_LITERAL: strlcpy(p, node->fmt, end - p); p += strlen(p); break; case XLAT_PERCENT: p[0] = '%'; p[1] = node->fmt[0]; p += 2; break; case XLAT_ATTRIBUTE: *(p++) = '%'; *(p++) = '{'; if (node->ref != REQUEST_CURRENT) { strlcpy(p, fr_int2str(request_refs, node->ref, "??"), end - p); p += strlen(p); *(p++) = '.'; } if ((node->ref != REQUEST_CURRENT) || (node->list != PAIR_LIST_REQUEST)) { strlcpy(p, fr_int2str(pair_lists, node->list, "??"), end - p); p += strlen(p); *(p++) = ':'; } strlcpy(p, node->da->name, end - p); p += strlen(p); if (node->tag != TAG_ANY) { *(p++) = ':'; snprintf(p, end - p, "%u", node->tag); p += strlen(p); } if (node->num != 0) { *(p++) = '['; if (node->num == 65536) { *(p++) = '#'; } else if (node->num == 65537) { *(p++) = '*'; } else { snprintf(p, end - p, "%u", node->num); p += strlen(p); } *(p++) = ']'; } *(p++) = '}'; break; #ifdef HAVE_REGEX_H case XLAT_REGEX: snprintf(p, end - p, "%%{%u}", node->num); p += strlen(p); break; #endif case XLAT_VIRTUAL: *(p++) = '%'; *(p++) = '{'; strlcpy(p, node->fmt, end - p); p += strlen(p); *(p++) = '}'; break; case XLAT_MODULE: *(p++) = '%'; *(p++) = '{'; strlcpy(p, node->xlat->name, end - p); p += strlen(p); *(p++) = ':'; rad_assert(node->child != NULL); len = xlat_sprint(p, end - p, node->child); p += len; *(p++) = '}'; break; case XLAT_ALTERNATE: *(p++) = '%'; *(p++) = '{'; len = xlat_sprint(p, end - p, node->child); p += len; *(p++) = ':'; *(p++) = '-'; len = xlat_sprint(p, end - p, node->alternate); p += len; *(p++) = '}'; break; } if (p == end) break; node = node->next; } *p = '\0'; return p - buffer; }
static int do_regex(REQUEST *request, value_pair_map_t const *map) { int compare, rcode, ret; regex_t reg, *preg; char *lhs, *rhs; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* * Expand and then compile it. */ switch (map->src->type) { case VPT_TYPE_REGEX: rcode = radius_expand_tmpl(&rhs, request, map->src); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return -1; } rad_assert(rhs != NULL); compare = regcomp(®, rhs, REG_EXTENDED | (map->src->vpt_iflag ? REG_ICASE : 0)); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); ERROR("Failed compiling regular expression: %s", errbuf); } EVAL_DEBUG("FAIL %d", __LINE__); return -1; } preg = ® break; case VPT_TYPE_REGEX_STRUCT: preg = map->src->vpt_preg; break; default: rad_assert(0); return -1; } rcode = radius_expand_tmpl(&lhs, request, map->dst); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); ret = -1; goto finish; } rad_assert(lhs != NULL); /* * regexec doesn't initialise unused elements */ memset(&rxmatch, 0, sizeof(rxmatch)); compare = regexec(preg, lhs, REQUEST_MAX_REGEX + 1, rxmatch, 0); rad_regcapture(request, compare, lhs, rxmatch); ret = (compare == 0); finish: /* * regcomp allocs extra memory for the expression, so if the * result wasn't cached we need to free it here. */ if (preg == ®) regfree(®); return ret; }
static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, pair_lists_t list, DICT_ATTR const *da, int8_t tag, int num, bool return_null) { VALUE_PAIR *vp, *vps = NULL; RADIUS_PACKET *packet = NULL; DICT_VALUE *dv; VALUE_PAIR myvp; /* * Arg. Too much abstraction is annoying. */ switch (list) { default: if (return_null) return NULL; return vp_aprinttype(ctx, da->type); case PAIR_LIST_CONTROL: vps = request->config_items; break; case PAIR_LIST_REQUEST: packet = request->packet; if (packet) vps = packet->vps; break; case PAIR_LIST_REPLY: packet = request->reply; if (packet) vps = packet->vps; break; #ifdef WITH_PROXY case PAIR_LIST_PROXY_REQUEST: packet = request->proxy; if (packet) vps = packet->vps; break; case PAIR_LIST_PROXY_REPLY: packet = request->proxy_reply; if (packet) vps = packet->vps; break; #endif #ifdef WITH_COA case PAIR_LIST_COA: case PAIR_LIST_DM: if (request->coa) packet = request->coa->packet; if (packet) vps = packet->vps; break; case PAIR_LIST_COA_REPLY: case PAIR_LIST_DM_REPLY: if (request->coa) packet = request->coa->reply; if (packet) vps = packet->vps; break; #endif } /* * Now that we have the list, etc. handled, * find the VP and print it. */ if ((da->vendor != 0) || (da->attr < 256) || (list == PAIR_LIST_CONTROL)) { print_vp: vp = pairfind(vps, da->attr, da->vendor, tag); if (!vp) { return NULL; } goto do_print; } /* * Some non-packet expansions */ switch (da->attr) { default: break; /* ignore them */ case PW_CLIENT_SHORTNAME: if (request->client && request->client->shortname) { return talloc_strdup(ctx, request->client->shortname); } return talloc_strdup(ctx, "<UNKNOWN-CLIENT>"); case PW_REQUEST_PROCESSING_STAGE: if (request->component) { return talloc_strdup(ctx, request->component); } return talloc_strdup(ctx, "server_core"); case PW_VIRTUAL_SERVER: if (!request->server) return NULL; return talloc_strdup(ctx, request->server); case PW_MODULE_RETURN_CODE: return talloc_asprintf(ctx, "%d", request->simul_max); /* hack */ } /* * All of the attributes must now refer to a packet. If * there's no packet, we can't print any attribute * referencing it. */ if (!packet) { if (return_null) return NULL; return vp_aprinttype(ctx, da->type); } memset(&myvp, 0, sizeof(myvp)); myvp.da = da; vp = NULL; switch (da->attr) { default: goto print_vp; case PW_PACKET_TYPE: dv = dict_valbyattr(PW_PACKET_TYPE, 0, packet->code); if (dv) return talloc_strdup(ctx, dv->name); return talloc_asprintf(ctx, "%d", packet->code); case PW_RESPONSE_PACKET_TYPE: { int code = 0; #ifdef WITH_PROXY if (request->proxy_reply && (!request->reply || !request->reply->code)) { code = request->proxy_reply->code; } else #endif if (request->reply) { code = request->reply->code; } return talloc_strdup(ctx, fr_packet_codes[code]); } case PW_PACKET_AUTHENTICATION_VECTOR: myvp.length = sizeof(packet->vector); memcpy(&myvp.vp_octets, packet->vector, sizeof(packet->vector)); vp = &myvp; break; case PW_CLIENT_IP_ADDRESS: case PW_PACKET_SRC_IP_ADDRESS: if (packet->src_ipaddr.af == AF_INET) { myvp.vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr; vp = &myvp; } break; case PW_PACKET_DST_IP_ADDRESS: if (packet->dst_ipaddr.af == AF_INET) { myvp.vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr; vp = &myvp; } break; case PW_PACKET_SRC_IPV6_ADDRESS: if (packet->src_ipaddr.af == AF_INET6) { memcpy(&myvp.vp_ipv6addr, &packet->src_ipaddr.ipaddr.ip6addr, sizeof(packet->src_ipaddr.ipaddr.ip6addr)); vp = &myvp; } break; case PW_PACKET_DST_IPV6_ADDRESS: if (packet->dst_ipaddr.af == AF_INET6) { memcpy(&myvp.vp_ipv6addr, &packet->dst_ipaddr.ipaddr.ip6addr, sizeof(packet->dst_ipaddr.ipaddr.ip6addr)); vp = &myvp; } break; case PW_PACKET_SRC_PORT: myvp.vp_integer = packet->src_port; vp = &myvp; break; case PW_PACKET_DST_PORT: myvp.vp_integer = packet->dst_port; vp = &myvp; break; } do_print: /* * Hack up the virtual attributes. */ if (num && (vp == &myvp)) { char *p, *q; /* * [*] means only one. */ if (num == 65537) num = 0; /* * [n] means NULL, as there's only one. */ if ((num > 0) && (num < 65536)) { return NULL; } p = vp_aprint(ctx, vp); rad_assert(p != NULL); /* * Get the length of it. */ if (num == 65536) { q = talloc_asprintf(ctx, "%d", (int) strlen(p)); talloc_free(p); return q; } return p; } /* * We want the N'th VP. */ if (num) { int count = 0; vp_cursor_t cursor; /* * Return a count of the VPs. */ if (num == 65536) { fr_cursor_init(&cursor, &vp); while (fr_cursor_next_by_num(&cursor, da->attr, da->vendor, tag) != NULL) { count++; } return talloc_asprintf(ctx, "%d", count); } /* * Ugly, but working. */ if (num == 65537) { char *p, *q; (void) fr_cursor_init(&cursor, &vp); vp = fr_cursor_next_by_num(&cursor, da->attr, da->vendor, tag); if (!vp) return NULL; p = vp_aprint(ctx, vp); while ((vp = fr_cursor_next_by_num(&cursor, da->attr, da->vendor, tag)) != NULL) { q = vp_aprint(ctx, vp); p = talloc_strdup_append(p, ","); p = talloc_strdup_append(p, q); } return p; } (void) fr_cursor_init(&cursor, &vp); while ((vp = fr_cursor_next_by_num(&cursor, da->attr, da->vendor, tag)) != NULL) { if (count == num) { break; } count++; } } if (!vp) { if (return_null) return NULL; return vp_aprinttype(ctx, da->type); } return vp_aprint(ctx, vp); }
/* * Copy data from src to dst, where the attributes are of * different type. */ static int do_cast_copy(VALUE_PAIR *dst, VALUE_PAIR const *src) { rad_assert(dst->da->type != src->da->type); if (dst->da->type == PW_TYPE_STRING) { dst->vp_strvalue = vp_aprint_value(dst, src); dst->length = strlen(dst->vp_strvalue); return 0; } if (dst->da->type == PW_TYPE_OCTETS) { if (src->da->type == PW_TYPE_STRING) { pairmemcpy(dst, src->vp_octets, src->length); /* Copy embedded NULLs */ } else { pairmemcpy(dst, (uint8_t const *) &src->data, src->length); } return 0; } if (src->da->type == PW_TYPE_STRING) { return pairparsevalue(dst, src->vp_strvalue, 0); } if ((src->da->type == PW_TYPE_INTEGER64) && (dst->da->type == PW_TYPE_ETHERNET)) { uint8_t array[8]; uint64_t i; i = htonll(src->vp_integer64); memcpy(array, &i, 8); /* * For OUIs in the DB. */ if ((array[0] != 0) || (array[1] != 0)) return -1; memcpy(&dst->vp_ether, &array[2], 6); dst->length = 6; return 0; } /* * The attribute we've found has to have a size which is * compatible with the type of the destination cast. */ if ((src->length < dict_attr_sizes[dst->da->type][0]) || (src->length > dict_attr_sizes[dst->da->type][1])) { EVAL_DEBUG("Casted attribute is wrong size (%u)", (unsigned int) src->length); return -1; } if (src->da->type == PW_TYPE_OCTETS) { switch (dst->da->type) { case PW_TYPE_INTEGER64: dst->vp_integer = ntohll(*(uint64_t const *) src->vp_octets); break; case PW_TYPE_INTEGER: case PW_TYPE_DATE: case PW_TYPE_SIGNED: dst->vp_integer = ntohl(*(uint32_t const *) src->vp_octets); break; case PW_TYPE_SHORT: dst->vp_integer = ntohs(*(uint16_t const *) src->vp_octets); break; case PW_TYPE_BYTE: dst->vp_integer = src->vp_octets[0]; break; default: memcpy(&dst->data, src->vp_octets, src->length); break; } dst->length = src->length; return 0; } /* * Convert host order to network byte order. */ if ((dst->da->type == PW_TYPE_IPV4_ADDR) && ((src->da->type == PW_TYPE_INTEGER) || (src->da->type == PW_TYPE_DATE) || (src->da->type == PW_TYPE_SIGNED))) { dst->vp_ipaddr = htonl(src->vp_integer); } else if ((src->da->type == PW_TYPE_IPV4_ADDR) && ((dst->da->type == PW_TYPE_INTEGER) || (dst->da->type == PW_TYPE_DATE) || (dst->da->type == PW_TYPE_SIGNED))) { dst->vp_integer = htonl(src->vp_ipaddr); } else { /* they're of the same byte order */ memcpy(&dst->data, &src->data, src->length); } dst->length = src->length; return 0; }
/** Register an xlat function. * * @param[in] name xlat name. * @param[in] func xlat function to be called. * @param[in] escape function to sanitize any sub expansions passed to the xlat function. * @param[in] instance of module that's registering the xlat function. * @return 0 on success, -1 on failure */ int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape, void *instance) { xlat_t *c; xlat_t my_xlat; rbnode_t *node; if (!name || !*name) { DEBUG("xlat_register: Invalid xlat name"); return -1; } /* * First time around, build up the tree... * * FIXME: This code should be hoisted out of this function, * and into a global "initialization". But it isn't critical... */ if (!xlat_root) { #ifdef WITH_UNLANG int i; #endif xlat_root = rbtree_create(xlat_cmp, NULL, 0); if (!xlat_root) { DEBUG("xlat_register: Failed to create tree"); return -1; } #ifdef WITH_UNLANG for (i = 0; xlat_foreach_names[i] != NULL; i++) { xlat_register(xlat_foreach_names[i], xlat_foreach, NULL, &xlat_inst[i]); c = xlat_find(xlat_foreach_names[i]); rad_assert(c != NULL); c->internal = true; } #endif #define XLAT_REGISTER(_x) xlat_register(STRINGIFY(_x), xlat_ ## _x, NULL, NULL); \ c = xlat_find(STRINGIFY(_x)); \ rad_assert(c != NULL); \ c->internal = true XLAT_REGISTER(integer); XLAT_REGISTER(strlen); XLAT_REGISTER(length); XLAT_REGISTER(hex); XLAT_REGISTER(string); XLAT_REGISTER(xlat); XLAT_REGISTER(module); XLAT_REGISTER(debug_attr); xlat_register("debug", xlat_debug, NULL, &xlat_inst[0]); c = xlat_find("debug"); rad_assert(c != NULL); c->internal = true; } /* * If it already exists, replace the instance. */ strlcpy(my_xlat.name, name, sizeof(my_xlat.name)); my_xlat.length = strlen(my_xlat.name); c = rbtree_finddata(xlat_root, &my_xlat); if (c) { if (c->internal) { DEBUG("xlat_register: Cannot re-define internal xlat"); return -1; } c->func = func; c->escape = escape; c->instance = instance; return 0; } /* * Doesn't exist. Create it. */ c = talloc_zero(xlat_root, xlat_t); c->func = func; c->escape = escape; strlcpy(c->name, name, sizeof(c->name)); c->length = strlen(c->name); c->instance = instance; node = rbtree_insertnode(xlat_root, c); if (!node) { talloc_free(c); return -1; } /* * Ensure that the data is deleted when the node is * deleted. * * @todo: Maybe this should be the other way around... * when a thing IN the tree is deleted, it's automatically * removed from the tree. But for now, this works. */ (void) talloc_steal(node, c); return 0; }
/** Evaluate a map * * @param[in] request the REQUEST * @param[in] modreturn the previous module return code * @param[in] depth of the recursion (only used for debugging) * @param[in] c the condition to evaluate * @return -1 on error, 0 for "no match", 1 for "match". */ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth, fr_cond_t const *c) { int rcode; char *lhs, *rhs; value_pair_map_t *map; rad_assert(c->type == COND_TYPE_MAP); map = c->data.map; rad_assert(map->dst->type != VPT_TYPE_UNKNOWN); rad_assert(map->src->type != VPT_TYPE_UNKNOWN); rad_assert(map->dst->type != VPT_TYPE_LIST); rad_assert(map->src->type != VPT_TYPE_LIST); rad_assert(map->dst->type != VPT_TYPE_REGEX); rad_assert(map->dst->type != VPT_TYPE_REGEX_STRUCT); EVAL_DEBUG("Map %s ? %s", fr_int2str(template_names, map->dst->type, "???"), fr_int2str(template_names, map->src->type, "???")); /* * Verify regexes. */ if ((map->src->type == VPT_TYPE_REGEX) || (map->src->type == VPT_TYPE_REGEX_STRUCT)) { rad_assert(map->op == T_OP_REG_EQ); } else { rad_assert(!((map->op == T_OP_REG_EQ) || (map->op == T_OP_REG_NE))); } /* * They're both attributes. Do attribute-specific work. * * LHS is DST. RHS is SRC <sigh> */ if (!c->cast && (map->src->type == VPT_TYPE_ATTR) && (map->dst->type == VPT_TYPE_ATTR)) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to ATTR"); if ((radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) || (radius_tmpl_get_vp(&rhs_vp, request, map->src) < 0)) { return -2; } return paircmp_op(lhs_vp, map->op, rhs_vp); } /* * LHS is a cast. Do type-specific comparisons, as if * the LHS was a real attribute. */ if (c->cast) { VALUE_PAIR *lhs_vp, *rhs_vp; /* * Try to copy data from the VP which is being * casted, instead of printing it to a string and * then re-parsing it. */ if (map->dst->type == VPT_TYPE_ATTR) { VALUE_PAIR *cast_vp; if (radius_tmpl_get_vp(&cast_vp, request, map->dst) < 0) { return false; } lhs_vp = pairalloc(request, c->cast); if (!lhs_vp) return false; /* * In a separate function for clarity */ if (do_cast_copy(lhs_vp, cast_vp) < 0) { talloc_free(lhs_vp); return false; } } else { rcode = get_cast_vp(&lhs_vp, request, map->dst, c->cast); if (rcode < 0) { return rcode; } } rad_assert(lhs_vp); /* * Get either a real VP, or parse the RHS into a * VP, and return that. */ if (map->src->type == VPT_TYPE_ATTR) { if (radius_tmpl_get_vp(&rhs_vp, request, map->src) < 0) { return -2; } } else { rcode = get_cast_vp(&rhs_vp, request, map->src, c->cast); if (rcode < 0) { return rcode; } rad_assert(rhs_vp); } if (!rhs_vp) return -2; EVAL_DEBUG("CAST to %s", fr_int2str(dict_attr_types, c->cast->type, "?Unknown?")); rcode = paircmp_op(lhs_vp, map->op, rhs_vp); pairfree(&lhs_vp); if (map->src->type != VPT_TYPE_ATTR) { pairfree(&rhs_vp); } return rcode; } /* * Might be a virtual comparison */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type != VPT_TYPE_REGEX) && (map->src->type != VPT_TYPE_REGEX_STRUCT) && (c->pass2_fixup == PASS2_PAIRCOMPARE)) { int ret; VALUE_PAIR *lhs_vp; EVAL_DEBUG("virtual ATTR to DATA"); rcode = get_cast_vp(&lhs_vp, request, map->src, map->dst->vpt_da); if (rcode < 0) { return rcode; } rad_assert(lhs_vp); /* * paircompare requires the operator be set for the * check attribute. */ lhs_vp->op = map->op; ret = paircompare(request, request->packet->vps, lhs_vp, NULL); talloc_free(lhs_vp); if (ret == 0) { return true; } return false; } rad_assert(c->pass2_fixup != PASS2_PAIRCOMPARE); /* * RHS has been pre-parsed into binary data. Go check * that. */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type == VPT_TYPE_DATA)) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to DATA"); if (radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) { return -2; } rcode = get_cast_vp(&rhs_vp, request, map->src, map->dst->vpt_da); if (rcode < 0) { return rcode; } rad_assert(rhs_vp); #ifdef WITH_EVAL_DEBUG debug_pair(lhs_vp); debug_pair(rhs_vp); #endif rcode = paircmp_op(lhs_vp, map->op, rhs_vp); pairfree(&rhs_vp); return rcode; } rad_assert(map->src->type != VPT_TYPE_DATA); rad_assert(map->dst->type != VPT_TYPE_DATA); #ifdef HAVE_REGEX_H /* * Parse regular expressions. */ if ((map->src->type == VPT_TYPE_REGEX) || (map->src->type == VPT_TYPE_REGEX_STRUCT)) { return do_regex(request, map); } #endif /* * The RHS now needs to be expanded into a string. */ rcode = radius_expand_tmpl(&rhs, request, map->src); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return rcode; } rad_assert(rhs != NULL); /* * User-Name == FOO * * Parse the RHS to be the same DA as the LHS. do * comparisons. So long as it's not a regex, which does * string comparisons. * * The LHS may be a virtual attribute, too. */ if (map->dst->type == VPT_TYPE_ATTR) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to non-REGEX"); /* * No LHS means no match */ if (radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) { /* * Not a real attr: might be a dynamic comparison. */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->dst->vpt_da->vendor == 0) && radius_find_compare(map->dst->vpt_da)) { rhs_vp = pairalloc(request, map->dst->vpt_da); rad_assert(rhs_vp != NULL); if (pairparsevalue(rhs_vp, rhs, 0) < 0) { talloc_free(rhs); EVAL_DEBUG("FAIL %d", __LINE__); return -1; } talloc_free(rhs); rcode = (radius_callback_compare(request, NULL, rhs_vp, NULL, NULL) == 0); pairfree(&rhs_vp); return rcode; } return -2; } /* * Get VP for RHS */ rhs_vp = pairalloc(request, map->dst->vpt_da); rad_assert(rhs_vp != NULL); if (pairparsevalue(rhs_vp, rhs, 0) < 0) { talloc_free(rhs); pairfree(&rhs_vp); EVAL_DEBUG("FAIL %d", __LINE__); return -1; } rcode = paircmp_op(lhs_vp, map->op, rhs_vp); talloc_free(rhs); pairfree(&rhs_vp); return rcode; } /* * The LHS is a string. Expand it. */ rcode = radius_expand_tmpl(&lhs, request, map->dst); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return rcode; } rad_assert(lhs != NULL); EVAL_DEBUG("LHS is %s", lhs); /* * Loop over the string, doing comparisons */ if (all_digits(lhs) && all_digits(rhs)) { int lint, rint; lint = strtoul(lhs, NULL, 0); rint = strtoul(rhs, NULL, 0); talloc_free(lhs); talloc_free(rhs); switch (map->op) { case T_OP_CMP_EQ: return (lint == rint); case T_OP_NE: return (lint != rint); case T_OP_LT: return (lint < rint); case T_OP_GT: return (lint > rint); case T_OP_LE: return (lint <= rint); case T_OP_GE: return (lint >= rint); default: break; } } else { rad_assert(lhs != NULL); rad_assert(rhs != NULL); rcode = strcmp(lhs, rhs); talloc_free(lhs); talloc_free(rhs); switch (map->op) { case T_OP_CMP_EQ: return (rcode == 0); case T_OP_NE: return (rcode != 0); case T_OP_LT: return (rcode < 0); case T_OP_GT: return (rcode > 0); case T_OP_LE: return (rcode <= 0); case T_OP_GE: return (rcode >= 0); default: break; } } EVAL_DEBUG("FAIL %d", __LINE__); return -1; }
static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head, char const **error) { ssize_t slen; char *p, *q, *brace; char const *attrname; xlat_exp_t *node; rad_assert(fmt[0] == '%'); rad_assert(fmt[1] == '{'); /* * %{%{...}:-bar} */ if ((fmt[2] == '%') && (fmt[3] == '{')) { return xlat_tokenize_alternation(ctx, fmt, head, error); } XLAT_DEBUG("EXPANSION: %s", fmt); node = talloc_zero(ctx, xlat_exp_t); attrname = node->fmt = fmt + 2; node->len = 0; #ifdef HAVE_REGEX_H /* * Handle regex's specially. */ if (isdigit((int) fmt[2]) && (fmt[3] == '}')) { if (fmt[2] == '9') { talloc_free(node); *error = "Invalid regex reference"; return -2; } XLAT_DEBUG("REGEX: %s", fmt); fmt[3] = '\0'; node->num = fmt[2] - '0'; /* ASCII */ node->type = XLAT_REGEX; *head = node; return 4; } #endif /* HAVE_REGEX_H */ /* * %{Attr-Name} * %{Attr-Name[#]} * %{Tunnel-Password:1} * %{Tunnel-Password:1[#]} * %{request:Attr-Name} * %{request:Tunnel-Password:1} * %{request:Tunnel-Password:1[#]} * %{mod:foo} */ q = brace = NULL; for (p = fmt + 2; *p != '\0'; p++) { if (*p == ':') break; if (isspace((int) *p)) break; if (*p == '[') break; if (*p == '}') break; } if (*p != ':') p = NULL; /* * Might be a module name reference. */ if (p) { *p = '\0'; /* * %{mod:foo} */ node->xlat = xlat_find(node->fmt); if (node->xlat) { node->type = XLAT_MODULE; XLAT_DEBUG("MOD: %s --> %s", node->fmt, p); slen = xlat_tokenize_literal(node, p + 1, &node->child, true, error); if (slen <= 0) { talloc_free(node); return slen - (p - fmt); } p += slen + 1; *head = node; rad_assert(node->next == NULL); return p - fmt; } /* * Modules can have '}' in their RHS, so we * didn't check for that until now. * * As of now, node->fmt MUST be a reference to an * attribute, however complicated. So it MUST have a closing brace. */ brace = strchr(p + 1, '}'); if (!brace) goto no_brace; *brace = '\0'; /* * %{User-Name} * %{User-Name[1]} * %{Tunnel-Password:1} * %{request:Tunnel-Password:1} * * <sigh> The syntax is fairly poor. */ XLAT_DEBUG("Looking for list in '%s'", attrname); /* * Not a module. Has to be an attribute * reference. * * As of v3, we've removed %{request: ..>} as * internally registered xlats. */ *p = ':'; node->ref = radius_request_name(&attrname, REQUEST_CURRENT); rad_assert(node->ref != REQUEST_UNKNOWN); node->list = radius_list_name(&attrname, PAIR_LIST_REQUEST); if (node->list == PAIR_LIST_UNKNOWN) { talloc_free(node); *error = "Unknown module"; return -2; } /* * Check for a trailing tag. */ p = strchr(attrname, ':'); if (p) *p = '\0'; } else { brace = strchr(attrname, '}'); if (!brace) { no_brace: talloc_free(node); *error = "No matching closing brace"; return -1; /* second character of format string */ } *brace = '\0'; node->ref = REQUEST_CURRENT; node->list = PAIR_LIST_REQUEST; } *brace = '\0'; XLAT_DEBUG("Looking for attribute name in %s", attrname); /* * Allow for an array reference. They come AFTER the * tag, if the tag exists. Otherwise, they come after * the attribute name. */ if (p) { q = strchr(p + 1, '['); } else { q = strchr(attrname, '['); } if (q) *(q++) = '\0'; if (!*attrname) { talloc_free(node); *error = "Empty expression is invalid"; return -(attrname - fmt); } /* * It's either an attribute name, or a Tunnel-Password:TAG * with the ':' already set to NULL. */ node->da = dict_attrbyname(attrname); if (!node->da) { /* * Foreach. Maybe other stuff, too. */ node->xlat = xlat_find(attrname); if (node->xlat) { node->type = XLAT_VIRTUAL; node->fmt = attrname; XLAT_DEBUG("VIRTUAL: %s", node->fmt); *head = node; rad_assert(node->next == NULL); brace++; return brace - fmt; } talloc_free(node); *error = "Unknown attribute"; return -(attrname - fmt); } /* * Parse the tag. */ if (p) { unsigned long tag; char *end; if (!node->da->flags.has_tag) { talloc_free(node); *error = "Attribute cannot have a tag"; return - (p - fmt); } tag = strtoul(p + 1, &end, 10); p++; if (tag == ULONG_MAX) { talloc_free(node); *error = "Invalid tag value"; return - (p - fmt); } node->tag = tag; p = end; if (*p) { talloc_free(node); *error = "Unexpected text after tag"; return - (p - fmt); } } else { node->tag = TAG_ANY; /* leave p alone */ } /* * Check for array reference */ if (q) { unsigned long num; char *end; p = q; if (*p== '#') { node->num = 65536; p++; } else if (*p == '*') { node->num = 65537; p++; } else if (isdigit((int) *p)) { num = strtoul(p, &end, 10); if ((num == ULONG_MAX) || (num > 65535)) { talloc_free(node); *error = "Invalid number"; return - (p - fmt); } p = end; DEBUG("END %s", p); node->num = num; } else { talloc_free(node); *error = "Invalid array reference"; return - (p - fmt); } if (*p != ']') { talloc_free(node); *error = "Expected ']'"; return - (p - fmt); } p++; if (*p) { talloc_free(node); *error = "Unexpected text after array reference"; return - (p - fmt); } } rad_assert(!p || (p == brace)); node->type = XLAT_ATTRIBUTE; p = brace + 1; *head = node; rad_assert(node->next == NULL); return p - fmt; }
/** Expand the RHS of a template * * @note Length of expanded string can be found with talloc_array_length(*out) - 1 * * @param out where to write a pointer to the newly allocated buffer. * @param request Current request. * @param vpt to evaluate. * @return -1 on error, else 0. */ static int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR *vp; *out = NULL; rad_assert(vpt->type != VPT_TYPE_LIST); switch (vpt->type) { case VPT_TYPE_LITERAL: EVAL_DEBUG("TMPL LITERAL"); *out = talloc_typed_strdup(request, vpt->name); break; case VPT_TYPE_EXEC: EVAL_DEBUG("TMPL EXEC"); *out = talloc_array(request, char, 1024); if (radius_exec_program(request, vpt->name, true, false, *out, 1024, EXEC_TIMEOUT, NULL, NULL) != 0) { TALLOC_FREE(*out); return -1; } break; case VPT_TYPE_REGEX: EVAL_DEBUG("TMPL REGEX"); /* Error in expansion, this is distinct from zero length expansion */ if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) { rad_assert(!*out); return -1; } break; case VPT_TYPE_XLAT: EVAL_DEBUG("TMPL XLAT"); /* Error in expansion, this is distinct from zero length expansion */ if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) { rad_assert(!*out); return -1; } break; case VPT_TYPE_XLAT_STRUCT: EVAL_DEBUG("TMPL XLAT_STRUCT"); /* Error in expansion, this is distinct from zero length expansion */ if (radius_axlat_struct(out, request, vpt->vpt_xlat, NULL, NULL) < 0) { rad_assert(!*out); return -1; } RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */ RDEBUG2(" --> %s", *out); break; case VPT_TYPE_ATTR: EVAL_DEBUG("TMPL ATTR"); if (radius_tmpl_get_vp(&vp, request, vpt) < 0) { return -2; } *out = vp_aprint_value(request, vp); if (!*out) { return -1; } break; case VPT_TYPE_DATA: case VPT_TYPE_REGEX_STRUCT: rad_assert(0 == 1); /* FALL-THROUGH */ default: break; } EVAL_DEBUG("Expand tmpl --> %s", *out); return 0; }
static void work_init(proto_detail_file_thread_t *thread) { proto_detail_file_t const *inst = thread->inst; int fd, rcode; bool has_worker; pthread_mutex_lock(&thread->worker_mutex); has_worker = (thread->num_workers != 0); pthread_mutex_unlock(&thread->worker_mutex); /* * The worker is still processing the file, poll until * it's done. */ if (has_worker) { DEBUG3("proto_detail (%s): worker %s is still alive, waiting for it to finish.", thread->name, inst->filename_work); goto delay; } rad_assert(thread->vnode_fd < 0); /* * See if there is a "detail.work" file. If not, try to * rename an existing file to "detail.work". */ DEBUG3("Trying to open %s", inst->filename_work); fd = open(inst->filename_work, inst->mode); /* * If the work file didn't exist, try to rename detail* -> * detail.work, and return the newly opened file. */ if (fd < 0) { if (errno != ENOENT) { DEBUG("proto_detail (%s): Failed opening %s: %s", thread->name, inst->filename_work, fr_syserror(errno)); goto delay; } retry: fd = work_rename(thread); } /* * The work file still doesn't exist. Go set up timers, * or wait for an event which signals us that something * in the directory changed. */ if (fd < 0) { struct timeval when, now; /* * Wait for the directory to change before * looking for another "detail" file. */ if (!inst->poll_interval) return; delay: /* * Check every N seconds. */ when.tv_sec = inst->poll_interval; when.tv_usec = 0; DEBUG3("Waiting %d.%06ds for new files in %s", (int) when.tv_sec, (int) when.tv_usec, thread->name); gettimeofday(&now, NULL); fr_timeval_add(&when, &when, &now); if (fr_event_timer_insert(thread, thread->el, &thread->ev, &when, work_retry_timer, thread) < 0) { ERROR("Failed inserting poll timer for %s", inst->filename_work); } return; } thread->lock_interval = USEC / 10; /* * It exists, go process it! * * We will get back to the main loop when the * "detail.work" file is deleted. */ rcode = work_exists(thread, fd); if (rcode < 0) goto delay; /* * The file was empty, so we try to get another one. */ if (rcode == 1) goto retry; /* * Otherwise the child is successfully processing the * file. */ }
/* * The pairmove() function in src/lib/valuepair.c does all sorts of * extra magic that we don't want here. * * FIXME: integrate this with the code calling it, so that we * only paircopy() those attributes that we're really going to * use. */ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool do_xlat) { int i, j, count, from_count, to_count, tailto; vp_cursor_t cursor; VALUE_PAIR *vp, *next, **last; VALUE_PAIR **from_list, **to_list; bool *edited = NULL; REQUEST *fixup = NULL; if (!request) return; /* * Set up arrays for editing, to remove some of the * O(N^2) dependencies. This also makes it easier to * insert and remove attributes. * * It also means that the operators apply ONLY to the * attributes in the original list. With the previous * implementation of pairmove(), adding two attributes * via "+=" and then "=" would mean that the second one * wasn't added, because of the existence of the first * one in the "to" list. This implementation doesn't * have that bug. * * Also, the previous implementation did NOT implement * "-=" correctly. If two of the same attributes existed * in the "to" list, and you tried to subtract something * matching the *second* value, then the pairdelete() * function was called, and the *all* attributes of that * number were deleted. With this implementation, only * the matching attributes are deleted. */ count = 0; for (vp = fr_cursor_init(&cursor, &from); vp; vp = fr_cursor_next(&cursor)) count++; from_list = talloc_array(request, VALUE_PAIR *, count); for (vp = fr_cursor_init(&cursor, to); vp; vp = fr_cursor_next(&cursor)) count++; to_list = talloc_array(request, VALUE_PAIR *, count); /* * Move the lists to the arrays, and break the list * chains. */ from_count = 0; for (vp = from; vp != NULL; vp = next) { next = vp->next; from_list[from_count++] = vp; vp->next = NULL; } to_count = 0; for (vp = *to; vp != NULL; vp = next) { next = vp->next; to_list[to_count++] = vp; vp->next = NULL; } tailto = to_count; edited = talloc_zero_array(request, bool, to_count); RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count); /* * Now that we have the lists initialized, start working * over them. */ for (i = 0; i < from_count; i++) { int found; RDEBUG4("::: Examining %s", from_list[i]->da->name); if (do_xlat) radius_xlat_do(request, from_list[i]); /* * Attribute should be appended, OR the "to" list * is empty, and we're supposed to replace or * "add if not existing". */ if (from_list[i]->op == T_OP_ADD) goto append; found = false; for (j = 0; j < to_count; j++) { if (edited[j] || !to_list[j] || !from_list[i]) continue; /* * Attributes aren't the same, skip them. */ if (from_list[i]->da != to_list[j]->da) { continue; } /* * We don't use a "switch" statement here * because we want to break out of the * "for" loop over 'j' in most cases. */ /* * Over-write the FIRST instance of the * matching attribute name. We free the * one in the "to" list, and move over * the one in the "from" list. */ if (from_list[i]->op == T_OP_SET) { RDEBUG4("::: OVERWRITING %s FROM %d TO %d", to_list[j]->da->name, i, j); pairfree(&to_list[j]); to_list[j] = from_list[i]; from_list[i] = NULL; edited[j] = true; break; } /* * Add the attribute only if it does not * exist... but it exists, so we stop * looking. */ if (from_list[i]->op == T_OP_EQ) { found = true; break; } /* * Delete every attribute, independent * of its value. */ if (from_list[i]->op == T_OP_CMP_FALSE) { goto delete; } /* * Delete all matching attributes from * "to" */ if ((from_list[i]->op == T_OP_SUB) || (from_list[i]->op == T_OP_CMP_EQ) || (from_list[i]->op == T_OP_LE) || (from_list[i]->op == T_OP_GE)) { int rcode; int old_op = from_list[i]->op; /* * Check for equality. */ from_list[i]->op = T_OP_CMP_EQ; /* * If equal, delete the one in * the "to" list. */ rcode = radius_compare_vps(NULL, from_list[i], to_list[j]); /* * We may want to do more * subtractions, so we re-set the * operator back to it's original * value. */ from_list[i]->op = old_op; switch (old_op) { case T_OP_CMP_EQ: if (rcode != 0) goto delete; break; case T_OP_SUB: if (rcode == 0) { delete: RDEBUG4("::: DELETING %s FROM %d TO %d", from_list[i]->da->name, i, j); pairfree(&to_list[j]); to_list[j] = NULL; } break; /* * Enforce <=. If it's * >, replace it. */ case T_OP_LE: if (rcode > 0) { RDEBUG4("::: REPLACING %s FROM %d TO %d", from_list[i]->da->name, i, j); pairfree(&to_list[j]); to_list[j] = from_list[i]; from_list[i] = NULL; edited[j] = true; } break; case T_OP_GE: if (rcode < 0) { RDEBUG4("::: REPLACING %s FROM %d TO %d", from_list[i]->da->name, i, j); pairfree(&to_list[j]); to_list[j] = from_list[i]; from_list[i] = NULL; edited[j] = true; } break; } continue; } rad_assert(0 == 1); /* panic! */ }
static int tls_socket_recv(rad_listen_t *listener) { int doing_init = false; ssize_t rcode; RADIUS_PACKET *packet; REQUEST *request; listen_socket_t *sock = listener->data; fr_tls_status_t status; RADCLIENT *client = sock->client; if (!sock->packet) { sock->packet = rad_alloc(sock, 0); if (!sock->packet) return 0; sock->packet->sockfd = listener->fd; sock->packet->src_ipaddr = sock->other_ipaddr; sock->packet->src_port = sock->other_port; sock->packet->dst_ipaddr = sock->my_ipaddr; sock->packet->dst_port = sock->my_port; if (sock->request) { (void) talloc_steal(sock->request, sock->packet); sock->request->packet = sock->packet; } } /* * Allocate a REQUEST for debugging. */ if (!sock->request) { sock->request = request = request_alloc(NULL); if (!sock->request) { ERROR("Out of memory"); return 0; } rad_assert(request->packet == NULL); rad_assert(sock->packet != NULL); request->packet = sock->packet; request->component = "<core>"; request->component = "<tls-connect>"; /* * Not sure if we should do this on every packet... */ request->reply = rad_alloc(request, 0); if (!request->reply) return 0; request->options = RAD_REQUEST_OPTION_DEBUG2; rad_assert(sock->ssn == NULL); sock->ssn = tls_new_session(listener->tls, sock->request, listener->tls->require_client_cert); if (!sock->ssn) { request_free(&sock->request); sock->packet = NULL; return 0; } (void) talloc_steal(sock, sock->ssn); SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_REQUEST, (void *)request); SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_CERTS, (void *)&request->packet->vps); doing_init = true; } rad_assert(sock->request != NULL); rad_assert(sock->request->packet != NULL); rad_assert(sock->packet != NULL); rad_assert(sock->ssn != NULL); request = sock->request; RDEBUG3("Reading from socket %d", request->packet->sockfd); PTHREAD_MUTEX_LOCK(&sock->mutex); rcode = read(request->packet->sockfd, sock->ssn->dirty_in.data, sizeof(sock->ssn->dirty_in.data)); if ((rcode < 0) && (errno == ECONNRESET)) { do_close: PTHREAD_MUTEX_UNLOCK(&sock->mutex); tls_socket_close(listener); return 0; } if (rcode < 0) { RDEBUG("Error reading TLS socket: %s", fr_syserror(errno)); goto do_close; } /* * Normal socket close. */ if (rcode == 0) goto do_close; sock->ssn->dirty_in.used = rcode; dump_hex("READ FROM SSL", sock->ssn->dirty_in.data, sock->ssn->dirty_in.used); /* * Catch attempts to use non-SSL. */ if (doing_init && (sock->ssn->dirty_in.data[0] != handshake)) { RDEBUG("Non-TLS data sent to TLS socket: closing"); goto do_close; } /* * Skip ahead to reading application data. */ if (SSL_is_init_finished(sock->ssn->ssl)) goto app; if (!tls_handshake_recv(request, sock->ssn)) { RDEBUG("FAILED in TLS handshake receive"); goto do_close; } if (sock->ssn->dirty_out.used > 0) { tls_socket_write(listener, request); PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } app: /* * FIXME: Run the packet through a virtual server in * order to see if we like the certificate presented by * the client. */ status = tls_application_data(sock->ssn, request); RDEBUG("Application data status %d", status); if (status == FR_TLS_MORE_FRAGMENTS) { PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } if (sock->ssn->clean_out.used == 0) { PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } dump_hex("TUNNELED DATA", sock->ssn->clean_out.data, sock->ssn->clean_out.used); /* * If the packet is a complete RADIUS packet, return it to * the caller. Otherwise... */ if ((sock->ssn->clean_out.used < 20) || (((sock->ssn->clean_out.data[2] << 8) | sock->ssn->clean_out.data[3]) != (int) sock->ssn->clean_out.used)) { RDEBUG("Received bad packet: Length %d contents %d", sock->ssn->clean_out.used, (sock->ssn->clean_out.data[2] << 8) | sock->ssn->clean_out.data[3]); goto do_close; } packet = sock->packet; packet->data = talloc_array(packet, uint8_t, sock->ssn->clean_out.used); packet->data_len = sock->ssn->clean_out.used; sock->ssn->record_minus(&sock->ssn->clean_out, packet->data, packet->data_len); packet->vps = NULL; PTHREAD_MUTEX_UNLOCK(&sock->mutex); if (!rad_packet_ok(packet, 0, NULL)) { RDEBUG("Received bad packet: %s", fr_strerror()); tls_socket_close(listener); return 0; /* do_close unlocks the mutex */ } /* * Copied from src/lib/radius.c, rad_recv(); */ if (fr_debug_flag) { char host_ipaddr[128]; if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) { RDEBUG("tls_recv: %s packet from host %s port %d, id=%d, length=%d", fr_packet_codes[packet->code], inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), packet->src_port, packet->id, (int) packet->data_len); } else { RDEBUG("tls_recv: Packet from host %s port %d code=%d, id=%d, length=%d", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), packet->src_port, packet->code, packet->id, (int) packet->data_len); } } FR_STATS_INC(auth, total_requests); return 1; }
static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head, int brace, char const **error) { char *p, *q; xlat_exp_t *node; if (!*fmt) return 0; XLAT_DEBUG("LITERAL: %s", fmt); node = talloc_zero(ctx, xlat_exp_t); node->fmt = fmt; node->len = 0; node->type = XLAT_LITERAL; p = fmt; q = fmt; while (*p) { /* * Convert \n to it's literal representation. */ if (p[0] == '\\') switch (p[1]) { case 't': *(q++) = '\t'; p += 2; node->len++; continue; case 'n': *(q++) = '\n'; p += 2; node->len++; continue; case 'x': p += 2; if (!p[0] || !p[1]) { talloc_free(node); *error = "Hex expansion requires two hex digits"; return -(p - fmt); } if (!fr_hex2bin((uint8_t *) q, p, 2)) { talloc_free(node); *error = "Invalid hex characters"; return -(p - fmt); } /* * Don't let people shoot themselves in the foot. * \x00 is forbidden. */ if (!*q) { talloc_free(node); *error = "Cannot add zero byte to printable string"; return -(p - fmt); } p += 2; q++; node->len++; continue; default: *(q++) = *p; p += 2; node->len++; continue; } /* * Process the expansion. */ if ((p[0] == '%') && (p[1] == '{')) { ssize_t slen; XLAT_DEBUG("LITERAL: %s --> %s", node->fmt, p); slen = xlat_tokenize_expansion(node, p, &node->next, error); if (slen <= 0) { talloc_free(node); return slen - (p - fmt); } *p = '\0'; /* end the literal */ p += slen; rad_assert(node->next != NULL); /* * Short-circuit the recursive call. * This saves another function call and * memory allocation. */ if (!*p) break; /* * "foo %{User-Name} bar" * LITERAL "foo " * EXPANSION User-Name * LITERAL " bar" */ slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error); rad_assert(slen != 0); if (slen < 0) { talloc_free(node); return slen - (p - fmt); } p += slen; break; /* stop processing the string */ } /* * Check for valid single-character expansions. */ if (p[0] == '%') { ssize_t slen; xlat_exp_t *next; if (!p[1] || !strchr("%dlmtDGHISTY", p[1])) { talloc_free(node); *error = "Invalid variable expansion"; p++; return - (p - fmt); } XLAT_DEBUG("PERCENT: %s --> %c", node->fmt, p[1]); next = talloc_zero(node, xlat_exp_t); next->fmt = p + 1; next->len = 1; next->type = XLAT_PERCENT; node->next = next; *p = '\0'; p += 2; if (!*p) break; /* * And recurse. */ slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error); rad_assert(slen != 0); if (slen < 0) { talloc_free(node); return slen - (p - fmt); } p += slen; break; /* stop processing the string */ } /* * If required, eat the brace. */ if (brace && (*p == '}')) { *q = '\0'; p++; break; } *(q++) = *(p++); node->len++; } /* * Squash zero-width literals */ if (node->len > 0) { *head = node; } else { (void) talloc_steal(ctx, node->next); *head = node->next; talloc_free(node); } return p - fmt; }
/* * End a session by faking a Stop packet to all accounting modules. */ int session_zap(REQUEST *request, uint32_t nasaddr, unsigned int port, char const *user, char const *sessionid, uint32_t cliaddr, char proto, int session_time) { REQUEST *stopreq; VALUE_PAIR *vp, *userpair; int ret; stopreq = request_alloc_fake(request); stopreq->packet->code = PW_CODE_ACCOUNTING_REQUEST; /* just to be safe */ stopreq->listener = request->listener; rad_assert(stopreq != NULL); /* Hold your breath */ #define PAIR(n,v,e) do { \ if(!(vp = paircreate(stopreq->packet,n, 0))) { \ request_free(&stopreq); \ ERROR("no memory"); \ pairfree(&(stopreq->packet->vps)); \ return 0; \ } \ vp->e = v; \ pairadd(&(stopreq->packet->vps), vp); \ } while(0) #define INTPAIR(n,v) PAIR(n,v,vp_integer) #define IPPAIR(n,v) PAIR(n,v,vp_ipaddr) #define STRINGPAIR(n,v) do { \ if(!(vp = paircreate(stopreq->packet,n, 0))) { \ request_free(&stopreq); \ ERROR("no memory"); \ pairfree(&(stopreq->packet->vps)); \ return 0; \ } \ pairstrcpy(vp, v); \ pairadd(&(stopreq->packet->vps), vp); \ } while(0) INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_STOP); IPPAIR(PW_NAS_IP_ADDRESS, nasaddr); INTPAIR(PW_ACCT_DELAY_TIME, 0); STRINGPAIR(PW_USER_NAME, user); userpair = vp; INTPAIR(PW_NAS_PORT, port); STRINGPAIR(PW_ACCT_SESSION_ID, sessionid); if(proto == 'P') { INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER); INTPAIR(PW_FRAMED_PROTOCOL, PW_PPP); } else if(proto == 'S') { INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER); INTPAIR(PW_FRAMED_PROTOCOL, PW_SLIP); } else { INTPAIR(PW_SERVICE_TYPE, PW_LOGIN_USER); /* A guess, really */ } if(cliaddr != 0) IPPAIR(PW_FRAMED_IP_ADDRESS, cliaddr); INTPAIR(PW_ACCT_SESSION_TIME, session_time); INTPAIR(PW_ACCT_INPUT_OCTETS, 0); INTPAIR(PW_ACCT_OUTPUT_OCTETS, 0); INTPAIR(PW_ACCT_INPUT_PACKETS, 0); INTPAIR(PW_ACCT_OUTPUT_PACKETS, 0); stopreq->username = userpair; stopreq->password = NULL; ret = rad_accounting(stopreq); /* * We've got to clean it up by hand, because no one else will. */ request_free(&stopreq); return ret; }
static void xlat_tokenize_debug(xlat_exp_t const *node, int lvl) { rad_assert(node != NULL); if (lvl >= (int) sizeof(xlat_tabs)) lvl = sizeof(xlat_tabs); while (node) { switch (node->type) { case XLAT_LITERAL: DEBUG("%.*sliteral: '%s'", lvl, xlat_tabs, node->fmt); break; case XLAT_PERCENT: DEBUG("%.*sliteral (with %%): '%c'", lvl, xlat_tabs, node->fmt[0]); break; case XLAT_ATTRIBUTE: rad_assert(node->da != NULL); DEBUG("%.*sattribute: %s", lvl, xlat_tabs, node->da->name); rad_assert(node->child == NULL); if ((node->tag != 0) || (node->num != 0)) { DEBUG("%.*s{", lvl, xlat_tabs); DEBUG("%.*sref %d", lvl + 1, xlat_tabs, node->ref); DEBUG("%.*slist %d", lvl + 1, xlat_tabs, node->list); if (node->tag) DEBUG("%.*stag %d", lvl + 1, xlat_tabs, node->tag); if (node->num) { if (node->num == 65536) { DEBUG("%.*s[#]", lvl + 1, xlat_tabs); } else if (node->num == 65537) { DEBUG("%.*s[*]", lvl + 1, xlat_tabs); } else { DEBUG("%.*s[%d]", lvl + 1, xlat_tabs, node->num); } } DEBUG("%.*s}", lvl, xlat_tabs); } break; case XLAT_VIRTUAL: rad_assert(node->fmt != NULL); DEBUG("%.*svirtual: %s", lvl, xlat_tabs, node->fmt); break; case XLAT_MODULE: rad_assert(node->xlat != NULL); DEBUG("%.*sxlat: %s", lvl, xlat_tabs, node->xlat->name); if (node->child) { DEBUG("%.*s{", lvl, xlat_tabs); xlat_tokenize_debug(node->child, lvl + 1); DEBUG("%.*s}", lvl, xlat_tabs); } break; #ifdef HAVE_REGEX_H case XLAT_REGEX: DEBUG("%.*sregex-var: %d", lvl, xlat_tabs, node->num); break; #endif case XLAT_ALTERNATE: DEBUG("%.*sif {", lvl, xlat_tabs); xlat_tokenize_debug(node->child, lvl + 1); DEBUG("%.*s}", lvl, xlat_tabs); DEBUG("%.*selse {", lvl, xlat_tabs); xlat_tokenize_debug(node->alternate, lvl + 1); DEBUG("%.*s}", lvl, xlat_tabs); break; } node = node->next; } }
/* * For more than 2 modules with redundancy + load balancing * across all of them, layering the "redundant" and * "load-balance" groups gets too complicated. As a result, we * implement a special function to do this. */ static int call_modredundantloadbalance(int component, modgroup *g, REQUEST *request, int default_result) { int count = 1; int myresult = default_result; int priority = 0; /* default result has lowest priority */ modcallable *p, *child = NULL; /* * Catch people who have issues. */ if (!g->children) { DEBUG2(" WARNING! Asked to process empty redundant-load-balance group. Returning %s.", lrad_int2str(rcode_table, default_result, "??")); return default_result; } /* * Pick a random child. */ /* Loop over the children */ for(p = g->children; p; p = p->next) { if (!child) { child = p; count = 1; continue; } /* * Keep track of how many load balancing servers * we've gone through. */ count++; /* * See the "camel book" for why this works. * * If (rand(0..n) < 1), pick the current realm. * We add a scale factor of 65536, to avoid * floating point. */ if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) { child = p; } } rad_assert(child != NULL); /* * Call the chosen child, with fail-over to the next one * if it is down. */ p = child; do { /* * Call the chosen entry. If we're done, then * stop. */ if (!call_one(component, p, request, &priority, &myresult)) { break; } /* * Go to the next one, and wrap around to the beginning if * we reach the end. */ p = p->next; if (!p) p = g->children; } while (p != child); /* * And return whatever was decided. */ return myresult; }
/* * 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 = 2* (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; }