#include <freeradius-devel/rad_assert.h>
#include <ctype.h>
-#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
void *unique_ptr;
int unique_int;
void *opaque;
- void (*free_opaque)(void *);
+ bool free_opaque;
};
-static int request_data_free_opaque(void *ctx)
-{
- request_data_t *this;
-
- this = talloc_get_type_abort(ctx, request_data_t);
- this->free_opaque(this->opaque);
-
- return 0;
-}
-
/*
* Add opaque data (with a "free" function) to a REQUEST.
*
*/
int request_data_add(REQUEST *request,
void *unique_ptr, int unique_int,
- void *opaque, void (*free_opaque)(void *))
+ void *opaque, bool free_opaque)
{
request_data_t *this, **last, *next;
next = this->next;
- if (this->opaque && /* free it, if necessary */
- this->free_opaque) {
- this->free_opaque(this->opaque);
+ /*
+ * If caller requires custom behaviour on free
+ * they must set a destructor.
+ */
+ if (this->opaque && this->free_opaque) {
+ talloc_free(this->opaque);
}
- break; /* replace the existing entry */
+ break; /* replace the existing entry */
}
}
this->unique_ptr = unique_ptr;
this->unique_int = unique_int;
this->opaque = opaque;
-
if (free_opaque) {
this->free_opaque = free_opaque;
- talloc_set_destructor((void *) this, request_data_free_opaque);
}
*last = this;
* Remove the entry from the list, and free it.
*/
*last = this->next;
- talloc_set_destructor((void *) this, NULL);
talloc_free(this);
- return ptr; /* don't free it, the caller does that */
+ return ptr; /* don't free it, the caller does that */
}
}
return NULL; /* wasn't found, too bad... */
}
-
-/*
- * Free a REQUEST struct.
- */
-void request_free(REQUEST **request_ptr)
-{
- REQUEST *request;
-
- if (!request_ptr || !*request_ptr) {
- return;
- }
-
- request = *request_ptr;
-
- rad_assert(!request->in_request_hash);
-#ifdef WITH_PROXY
- rad_assert(!request->in_proxy_hash);
-#endif
- rad_assert(!request->ev);
-
-#ifdef WITH_COA
- if (request->coa) {
- request->coa->parent = NULL;
- request_free(&request->coa);
- }
-
- if (request->parent && (request->parent->coa == request)) {
- request->parent->coa = NULL;
- }
-#endif
-
-#ifndef NDEBUG
- request->magic = 0x01020304; /* set the request to be nonsense */
-#endif
- request->client = NULL;
-#ifdef WITH_PROXY
- request->home_server = NULL;
-#endif
- talloc_free(request);
- *request_ptr = NULL;
-}
-
-/*
- * Check a filename for sanity.
- *
- * Allow only uppercase/lowercase letters, numbers, and '-_/.'
- */
-int rad_checkfilename(char const *filename)
-{
- if (strspn(filename, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/.") == strlen(filename)) {
- return 0;
- }
-
- return -1;
-}
-
-/** Check if file exists
- *
- * @param filename to check.
- * @return 0 if the file does not exist, 1 if the file exists, -1 if the file
- * exists but there was an error opening it. errno value should be usable
- * for error messages.
- */
-int rad_file_exists(char const *filename)
-{
- int des;
- int ret = 1;
-
- if ((des = open(filename, O_RDONLY)) == -1) {
- if (errno == ENOENT) {
- ret = 0;
- } else {
- ret = -1;
- }
- } else {
- close(des);
- }
-
- return ret;
-}
-
/*
* Create possibly many directories.
*
{
int rcode;
char *p;
- struct stat st;
/*
- * If the directory exists, don't do anything.
+ * Try to make the directory. If it exists, chmod it.
+ * If a path doesn't exist, that's OK. Otherwise
+ * return with an error.
*/
- if (stat(directory, &st) == 0) {
- return 0;
- }
-
- /*
- * Look for the LAST directory name. Try to create that,
- * failing on any error.
- */
- p = strrchr(directory, FR_DIR_SEP);
- if (p != NULL) {
- *p = '\0';
- rcode = rad_mkdir(directory, mode);
+ rcode = mkdir(directory, mode & 0777);
+ if (rcode < 0) {
+ if (errno == EEXIST) {
+ return chmod(directory, mode);
+ }
- /*
- * On error, we leave the directory name as the
- * one which caused the error.
- */
- if (rcode < 0) {
- if (errno == EEXIST) return 0;
+ if (errno != ENOENT) {
return rcode;
}
/*
- * Reset the directory delimiter, and go ask
- * the system to make the directory.
+ * A component in the directory path doesn't
+ * exist. Look for the LAST directory name. Try
+ * to create that. If there's an error, we leave
+ * the directory path as the one at which the
+ * error occured.
*/
- *p = FR_DIR_SEP;
- } else {
- return 0;
- }
+ p = strrchr(directory, FR_DIR_SEP);
+ if (!p || (p == directory)) return -1;
- /*
- * Having done everything successfully, we do the
- * system call to actually go create the directory.
- */
- rcode = mkdir(directory, mode & 0777);
- if (rcode < 0) {
- return rcode;
- }
+ *p = '\0';
+ rcode = rad_mkdir(directory, mode);
+ if (rcode < 0) return rcode;
- /*
- * Set things like sticky bits that aren't supported by
- * mkdir.
- */
- if (mode & ~0777) {
- rcode = chmod(directory, mode);
- }
+ /*
+ * Reset the directory path, and try again to
+ * make the directory.
+ */
+ *p = FR_DIR_SEP;
+ rcode = mkdir(directory, mode & 0777);
+ if (rcode < 0) return rcode;
+ } /* else we successfully created the directory */
- return rcode;
+ return chmod(directory, mode);
}
if (ptr == NULL) {
ERROR("no memory");
- exit(1);
+ fr_exit(1);
}
return ptr;
}
-void *rad_calloc(size_t size)
-{
- void *ptr = rad_malloc(size);
- memset(ptr, 0, size);
- return ptr;
-}
-
void rad_const_free(void const *ptr)
{
void *tmp;
*
*/
-void NEVER_RETURNS rad_assert_fail (char const *file, unsigned int line,
- char const *expr)
+void NEVER_RETURNS rad_assert_fail(char const *file, unsigned int line, char const *expr)
{
ERROR("ASSERT FAILED %s[%u]: %s", file, line, expr);
- abort();
+ fr_fault(SIGABRT);
+ fr_exit_now(1);
}
+/*
+ * Free a REQUEST struct.
+ */
+static int _request_free(REQUEST *request)
+{
+ rad_assert(!request->in_request_hash);
+#ifdef WITH_PROXY
+ rad_assert(!request->in_proxy_hash);
+#endif
+ rad_assert(!request->ev);
+
+#ifdef WITH_COA
+ if (request->coa) {
+ request->coa->parent = NULL;
+ }
+
+ if (request->parent && (request->parent->coa == request)) {
+ request->parent->coa = NULL;
+ }
+#endif
+
+#ifndef NDEBUG
+ request->magic = 0x01020304; /* set the request to be nonsense */
+#endif
+ request->client = NULL;
+#ifdef WITH_PROXY
+ request->home_server = NULL;
+#endif
+
+ return 0;
+}
/*
* Create a new REQUEST data structure.
REQUEST *request;
request = talloc_zero(ctx, REQUEST);
+ talloc_set_destructor(request, _request_free);
#ifndef NDEBUG
request->magic = REQUEST_MAGIC;
#endif
request->username = NULL;
request->password = NULL;
request->timestamp = time(NULL);
- request->options = debug_flag; /* Default to global debug level */
+ request->log.lvl = debug_flag; /* Default to global debug level */
request->module = "";
request->component = "<core>";
- request->radlog = radlog_request;
+ request->log.func = vradlog_request;
return request;
}
*/
fake->server = request->server;
- fake->packet = rad_alloc(request, 1);
+ fake->packet = rad_alloc(fake, true);
if (!fake->packet) {
- request_free(&fake);
+ talloc_free(fake);
return NULL;
}
- fake->reply = rad_alloc(request, 0);
+ fake->reply = rad_alloc(fake, false);
if (!fake->reply) {
- request_free(&fake);
+ talloc_free(fake);
return NULL;
}
fake->packet->id = fake->number & 0xff;
fake->packet->code = request->packet->code;
fake->timestamp = request->timestamp;
+ fake->packet->timestamp = request->packet->timestamp;
/*
* Required for new identity support
/*
* Copy debug information.
*/
- fake->options = request->options;
- fake->radlog = request->radlog;
+ memcpy(&(fake->log), &(request->log), sizeof(fake->log));
return fake;
}
/*
* Originate CoA requests only when necessary.
*/
- if ((request->packet->code != PW_AUTHENTICATION_REQUEST) &&
- (request->packet->code != PW_ACCOUNTING_REQUEST)) return NULL;
+ if ((request->packet->code != PW_CODE_ACCESS_REQUEST) &&
+ (request->packet->code != PW_CODE_ACCOUNTING_REQUEST)) return NULL;
request->coa = request_alloc_fake(request);
if (!request->coa) return NULL;
request->coa->packet->code = 0; /* unknown, as of yet */
request->coa->child_state = REQUEST_RUNNING;
- request->coa->proxy = rad_alloc(request->coa, 0);
+ request->coa->proxy = rad_alloc(request->coa, false);
if (!request->coa->proxy) {
- request_free(&request->coa);
+ TALLOC_FREE(request->coa);
return NULL;
}
#define USEC 1000000
#endif
-int rad_pps(int *past, int *present, time_t *then, struct timeval *now)
+uint32_t rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval *now)
{
- int pps;
+ uint32_t pps;
if (*then != now->tv_sec) {
*then = now->tv_sec;
* We have to have SOMETHING, at least.
*/
if (argc <= 0) {
- ERROR("rad_expand_xlat: Empty command line.");
+ ERROR("rad_expand_xlat: Empty command line");
return -1;
}
left--;
if (left <= 0) {
- ERROR("rad_expand_xlat: Ran out of space while expanding arguments.");
+ ERROR("rad_expand_xlat: Ran out of space while expanding arguments");
return -1;
}
}
const FR_NAME_NUMBER pair_lists[] = {
{ "request", PAIR_LIST_REQUEST },
{ "reply", PAIR_LIST_REPLY },
+ { "control", PAIR_LIST_CONTROL }, /* New name should have priority */
{ "config", PAIR_LIST_CONTROL },
- { "control", PAIR_LIST_CONTROL },
#ifdef WITH_PROXY
{ "proxy-request", PAIR_LIST_PROXY_REQUEST },
{ "proxy-reply", PAIR_LIST_PROXY_REPLY },
* @see dict_attrbyname
*
* @param[in,out] name of attribute.
- * @param[in] unknown the list to return if no qualifiers were found.
+ * @param[in] default_list the list to return if no qualifiers were found.
* @return PAIR_LIST_UNKOWN if qualifiers couldn't be resolved to a list.
*/
-pair_lists_t radius_list_name(char const **name, pair_lists_t unknown)
+pair_lists_t radius_list_name(char const **name, pair_lists_t default_list)
{
char const *p = *name;
char const *q;
rad_assert(name && *name);
/*
- * We couldn't determine the list if:
- *
- * A colon delimiter was found, but the next char was a
- * number, indicating a tag, not a list qualifier.
- *
- * No colon was found and the first char was upper case
- * indicating an attribute.
- *
+ * Unfortunately, ':' isn't a definitive separator for
+ * the list name. We may have numeric tags, too.
*/
q = strchr(p, ':');
- if (((q && (q[1] >= '0') && (q[1] <= '9'))) ||
- (!q && isupper((int) *p))) {
- return unknown;
- }
-
if (q) {
- *name = (q + 1); /* Consume the list and delimiter */
- return fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p));
+ /*
+ * Check for tagged attributes. They have
+ * "name:tag", where tag is a decimal number.
+ * Valid tags are invalid attributes, so that's
+ * OK.
+ *
+ * Also allow "name:tag[#]" as a tag.
+ *
+ * However, "request:" is allowed, too, and
+ * shouldn't be interpreted as a tag.
+ *
+ * We do this check first rather than just
+ * looking up the request name, because this
+ * check is cheap, and looking up the request
+ * name is expensive.
+ */
+ if (isdigit((int) q[1])) {
+ char const *d = q + 1;
+
+ while (isdigit((int) *d)) {
+ d++;
+ }
+
+ /*
+ * Return the DEFAULT list as supplied by
+ * the caller. This is usually
+ * PAIRLIST_REQUEST.
+ */
+ if (!*d || (*d == '[')) {
+ return default_list;
+ }
+ }
+
+ /*
+ * If the first part is a list name, then treat
+ * it as a list. This means that we CANNOT have
+ * an attribute which is named "request",
+ * "reply", etc. Allowing a tagged attribute
+ * "request:3" would just be insane.
+ */
+ output = fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p));
+ if (output != PAIR_LIST_UNKNOWN) {
+ *name = (q + 1); /* Consume the list and delimiter */
+ return output;
+ }
+
+ /*
+ * It's not a known list, say so.
+ */
+ return PAIR_LIST_UNKNOWN;
}
- q = (p + strlen(p)); /* Consume the entire string */
+ /*
+ * The input string may be just a list name,
+ * e.g. "request". Check for that.
+ */
+ q = (p + strlen(p));
output = fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p));
if (output != PAIR_LIST_UNKNOWN) {
*name = q;
return output;
}
- return unknown;
+ /*
+ * It's just an attribute name. Return the default list
+ * as supplied by the caller.
+ */
+ return default_list;
}
p = strchr(*name, '.');
if (!p) {
- return REQUEST_CURRENT;
+ return def;
}
/*
p - *name);
/*
- * If we get a VALID LIST, skip it.
+ * If we get a valid name, skip it.
*/
if (request != REQUEST_UNKNOWN) {
*name = p + 1;
case REQUEST_PARENT: /* for future use in request chaining */
case REQUEST_OUTER:
if (!request->parent) {
- REDEBUG("Specified request \"%s\" is not available in this context",
- fr_int2str(request_refs, name, "<INVALID>"));
return -1;
}
-
*context = request->parent;
-
break;
case REQUEST_UNKNOWN:
return 0;
}
+/** Adds subcapture values to request data
+ *
+ * Allows use of %{n} expansions.
+ *
+ * @param request Current request.
+ * @param compare Result returned by regexec.
+ * @param value The original value.
+ * @param rxmatch Pointers into value.
+ */
+void rad_regcapture(REQUEST *request, int compare, char const *value, regmatch_t rxmatch[])
+{
+ int i;
+ char *p;
+ size_t len;
+
+ if (compare == REG_NOMATCH) {
+ return;
+ }
+
+ /*
+ * Add new %{0}, %{1}, etc.
+ */
+ for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
+ /*
+ * Didn't match: delete old match, if it existed.
+ */
+ if (rxmatch[i].rm_so == -1) {
+ p = request_data_get(request, request, REQUEST_DATA_REGEX | i);
+ if (p) {
+ RDEBUG4("%%{%i}: Clearing old value \"%s\"", i, p);
+ talloc_free(p);
+ } else {
+ RDEBUG4("%%{%i}: Was empty", i);
+ }
+
+ continue;
+ }
+
+ len = rxmatch[i].rm_eo - rxmatch[i].rm_so;
+ p = talloc_array(request, char, len + 1);
+ if (!p) {
+ ERROR("Out of memory");
+ return;
+ }
+
+ memcpy(p, value + rxmatch[i].rm_so, len);
+ p[len] = '\0';
+
+ RDEBUG4("%%{%i}: Inserting new value \"%s\"", i, p);
+ /*
+ * Copy substring, and add it to
+ * the request.
+ *
+ * Note that we don't check
+ * for out of memory, which is
+ * the only error we can get...
+ */
+ request_data_add(request, request, REQUEST_DATA_REGEX | i, p, true);
+ }
+}
+
+#ifndef NDEBUG
+/*
+ * Verify a packet.
+ */
+static void verify_packet(char const *file, int line, REQUEST *request, RADIUS_PACKET *packet, char const *type)
+{
+ TALLOC_CTX *parent;
+
+ if (!packet) {
+ fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: RADIUS_PACKET %s pointer was NULL", file, line, type);
+ fr_assert(0);
+ fr_exit_now(0);
+ }
+
+ parent = talloc_parent(packet);
+ if (parent != request) {
+ ERROR("CONSISTENCY CHECK FAILED %s[%u]: Expected RADIUS_PACKET %s to be parented by %p (%s), "
+ "but parented by %p (%s)", file, line, type, request, talloc_get_name(request),
+ parent, parent ? talloc_get_name(parent) : "NULL");
+
+ fr_log_talloc_report(packet);
+ if (parent) fr_log_talloc_report(parent);
+
+ rad_assert(0);
+ }
+
+ VERIFY_PACKET(packet);
+
+ if (!packet->vps) return;
+
+#ifdef WITH_VERIFY_PTR
+ fr_verify_list(file, line, packet, packet->vps);
+#endif
+}
+/*
+ * Catch horrible talloc errors.
+ */
+void verify_request(char const *file, int line, REQUEST *request)
+{
+ if (!request) {
+ fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: REQUEST pointer was NULL", file, line);
+ fr_assert(0);
+ fr_exit_now(0);
+ }
+
+ (void) talloc_get_type_abort(request, REQUEST);
+
+#ifdef WITH_VERIFY_PTR
+ fr_verify_list(file, line, request, request->config_items);
+#endif
+
+ if (request->packet) verify_packet(file, line, request, request->packet, "request");
+ if (request->reply) verify_packet(file, line, request, request->reply, "reply");
+#ifdef WITH_PROXY
+ if (request->proxy) verify_packet(file, line, request, request->proxy, "proxy-request");
+ if (request->proxy_reply) verify_packet(file, line, request, request->proxy_reply, "proxy-reply");
+#endif
+
+#ifdef WITH_COA
+ if (request->coa) {
+ void *parent;
+
+ (void) talloc_get_type_abort(request->coa, REQUEST);
+ parent = talloc_parent(request->coa);
+
+ rad_assert(parent == request);
+
+ verify_request(file, line, request->coa);
+ }
+#endif
+}
+#endif