/** Resolve attribute name to a list. * * Check the name string for qualifiers that specify a list and return * an pair_lists_t value for that list. This value may be passed to * radius_list, along with the current request, to get a pointer to the * actual list in the request. * * If qualifiers were consumed, write a new pointer into name to the * char after the last qualifier to be consumed. * * radius_list_name should be called before passing a name string that * may contain qualifiers to dict_attrbyname. * * @see dict_attrbyname * * @param[in,out] name of attribute. * @param[in] unknown 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(const char **name, pair_lists_t unknown) { const char *p = *name; const char *q; /* This should never be a NULL pointer or zero length string */ 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. * * This allows the function to be used to resolve list names 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 */ } else { q = (p + strlen(p)); /* Consume the entire string */ *name = q; } return fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p)); }
/** Resolve attribute name to a request. * * Check the name string for qualifiers that reference a parent request and * write the pointer to this request to 'request'. * * If qualifiers were consumed, write a new pointer into name to the * char after the last qualifier to be consumed. * * radius_ref_request should be called before radius_list_name. * * @see radius_list_name * @param[in,out] name of attribute. * @param[in] def default request ref to return if no request qualifier is present. * @return one of the REQUEST_* definitions or REQUEST_UNKOWN */ request_refs_t radius_request_name(char const **name, request_refs_t def) { char *p; int request; p = strchr(*name, '.'); if (!p) { return REQUEST_CURRENT; } /* * We may get passed "127.0.0.1". */ request = fr_substr2int(request_refs, *name, REQUEST_UNKNOWN, p - *name); /* * If we get a VALID LIST, skip it. */ if (request != REQUEST_UNKNOWN) { *name = p + 1; return request; } /* * Otherwise leave it alone, and return the caller's * default. */ return def; }
/** Resolve attribute name to a request. * * Check the name string for qualifiers that reference a parent request and * write the pointer to this request to 'request'. * * If qualifiers were consumed, write a new pointer into name to the * char after the last qualifier to be consumed. * * radius_ref_request should be called before radius_list_name. * * @see radius_list_name * @param[in,out] name of attribute. * @param[in] unknown Request ref to return if no request qualifier is present. * @return one of the REQUEST_* definitions or REQUEST_UNKOWN */ request_refs_t radius_request_name(const char **name, request_refs_t unknown) { char *p; int request; p = strchr(*name, '.'); if (!p) { return REQUEST_CURRENT; } request = fr_substr2int(request_refs, *name, unknown, p - *name); if (request != REQUEST_UNKNOWN) { *name = p + 1; } return request; }
/* * Simple xlat to read text data from a URL */ static ssize_t rest_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace) { rlm_rest_t *inst = instance; void *handle; int hcode; int ret; ssize_t len, outlen = 0; char *uri = NULL; char const *p = fmt, *q; char const *body; http_method_t method; /* There are no configurable parameters other than the URI */ rlm_rest_section_t section = { .name = "xlat", .method = HTTP_METHOD_GET, .body = HTTP_BODY_NONE, .body_str = "application/x-www-form-urlencoded", .require_auth = false, .timeout = 4, .force_to = HTTP_BODY_PLAIN }; *out = '\0'; rad_assert(fmt); RDEBUG("Expanding URI components"); handle = fr_connection_get(inst->conn_pool); if (!handle) return -1; /* * Extract the method from the start of the format string (if there is one) */ method = fr_substr2int(http_method_table, p, HTTP_METHOD_UNKNOWN, -1); if (method != HTTP_METHOD_UNKNOWN) { section.method = method; p += strlen(http_method_table[method].name); } /* * Trim whitespace */ while (isspace(*p) && p++); /* * Unescape parts of xlat'd URI, this allows REST servers to be specified by * request attributes. */ len = rest_uri_host_unescape(&uri, instance, request, handle, p); if (len <= 0) { outlen = -1; goto finish; } /* * Extract freeform body data (url can't contain spaces) */ q = strchr(p, ' '); if (q && (*++q != '\0')) { section.body = HTTP_BODY_CUSTOM_LITERAL; section.data = q; } RDEBUG("Sending HTTP %s to \"%s\"", fr_int2str(http_method_table, section.method, NULL), uri); /* * Configure various CURL options, and initialise the read/write * context data. * * @todo We could extract the User-Name and password from the URL string. */ ret = rest_request_config(instance, §ion, request, handle, section.method, section.body, uri, NULL, NULL); talloc_free(uri); if (ret < 0) return -1; /* * Send the CURL request, pre-parse headers, aggregate incoming * HTTP body data into a single contiguous buffer. */ ret = rest_request_perform(instance, §ion, request, handle); if (ret < 0) return -1; hcode = rest_get_handle_code(handle); switch (hcode) { case 404: case 410: case 403: case 401: { outlen = -1; error: rest_response_error(request, handle); goto finish; } case 204: goto finish; default: /* * Attempt to parse content if there was any. */ if ((hcode >= 200) && (hcode < 300)) { break; } else if (hcode < 500) { outlen = -2; goto error; } else { outlen = -1; goto error; } } len = rest_get_handle_data(&body, handle); if ((size_t) len >= freespace) { REDEBUG("Insufficient space to write HTTP response, needed %zu bytes, have %zu bytes", len + 1, freespace); outlen = -1; goto finish; } if (len > 0) { outlen = len; strlcpy(out, body, len + 1); /* strlcpy takes the size of the buffer */ } finish: rlm_rest_cleanup(instance, §ion, handle); fr_connection_release(inst->conn_pool, handle); return outlen; } /* * Find the named user in this modules database. Create the set * of attribute-value pairs to check and reply with for this user * from the database. The authentication code only needs to check * the password, the rest is done here. */ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request) { rlm_rest_t *inst = instance; rlm_rest_section_t *section = &inst->authorize; void *handle; int hcode; int rcode = RLM_MODULE_OK; int ret; if (!section->name) return RLM_MODULE_NOOP; handle = fr_connection_get(inst->conn_pool); if (!handle) return RLM_MODULE_FAIL; ret = rlm_rest_perform(instance, section, handle, request, NULL, NULL); if (ret < 0) { rcode = RLM_MODULE_FAIL; goto finish; } hcode = rest_get_handle_code(handle); switch (hcode) { case 404: case 410: rcode = RLM_MODULE_NOTFOUND; break; case 403: rcode = RLM_MODULE_USERLOCK; break; case 401: /* * Attempt to parse content if there was any. */ ret = rest_response_decode(inst, section, request, handle); if (ret < 0) { rcode = RLM_MODULE_FAIL; break; } rcode = RLM_MODULE_REJECT; break; case 204: rcode = RLM_MODULE_OK; break; default: /* * Attempt to parse content if there was any. */ if ((hcode >= 200) && (hcode < 300)) { ret = rest_response_decode(inst, section, request, handle); if (ret < 0) rcode = RLM_MODULE_FAIL; else if (ret == 0) rcode = RLM_MODULE_OK; else rcode = RLM_MODULE_UPDATED; break; } else if (hcode < 500) { rcode = RLM_MODULE_INVALID; } else { rcode = RLM_MODULE_FAIL; } } finish: switch (rcode) { case RLM_MODULE_INVALID: case RLM_MODULE_FAIL: case RLM_MODULE_USERLOCK: rest_response_error(request, handle); break; default: break; } rlm_rest_cleanup(instance, section, handle); fr_connection_release(inst->conn_pool, handle); return rcode; }
/** Resolve attribute name to a list. * * Check the name string for qualifiers that specify a list and return * an pair_lists_t value for that list. This value may be passed to * radius_list, along with the current request, to get a pointer to the * actual list in the request. * * If qualifiers were consumed, write a new pointer into name to the * char after the last qualifier to be consumed. * * radius_list_name should be called before passing a name string that * may contain qualifiers to dict_attrbyname. * * @see dict_attrbyname * * @param[in,out] name of attribute. * @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 default_list) { char const *p = *name; char const *q; pair_lists_t output; /* This should never be a NULL pointer or zero length string */ rad_assert(name && *name); /* * Unfortunately, ':' isn't a definitive separator for * the list name. We may have numeric tags, too. */ q = strchr(p, ':'); if (q) { /* * 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; } /* * 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; } /* * It's just an attribute name. Return the default list * as supplied by the caller. */ return default_list; }