/* * Send post-auth info to a REST API endpoint */ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, UNUSED REQUEST *request) { rlm_rest_t *inst = instance; rlm_rest_section_t *section = &inst->post_auth; 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(inst, section, handle, request, NULL, NULL); if (ret < 0) { rcode = RLM_MODULE_FAIL; goto finish; } hcode = rest_get_handle_code(handle); if (hcode >= 500) { rcode = RLM_MODULE_FAIL; } else if (hcode == 204) { rcode = RLM_MODULE_OK; } else 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; } else { rcode = RLM_MODULE_INVALID; } finish: switch (rcode) { case RLM_MODULE_INVALID: case RLM_MODULE_FAIL: rest_response_error(request, handle); break; default: break; } rlm_rest_cleanup(inst, section, handle); fr_connection_release(inst->conn_pool, handle); return rcode; }
/* * Write accounting information to this modules database. */ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, UNUSED REQUEST *request) { rlm_rest_t *inst = instance; rlm_rest_section_t *section = &inst->accounting; void *handle; int hcode; int rcode = RLM_MODULE_OK; int ret; handle = fr_connection_get(inst->conn_pool); if (!handle) return RLM_MODULE_FAIL; ret = rlm_rest_perform(inst, section, handle, request, NULL, NULL); if (ret < 0) { rcode = RLM_MODULE_FAIL; goto end; } hcode = rest_get_handle_code(handle); if (hcode >= 500) { rcode = RLM_MODULE_FAIL; } else if (hcode == 204) { rcode = RLM_MODULE_OK; } else 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; } else { rcode = RLM_MODULE_INVALID; } end: rlm_rest_cleanup(inst, section, handle); fr_connection_release(inst->conn_pool, handle); return rcode; }
/* * Authenticate the user with the given password. */ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, UNUSED REQUEST *request) { rlm_rest_t *inst = instance; rlm_rest_section_t *section = &inst->authenticate; void *handle; int hcode; int rcode = RLM_MODULE_OK; int ret; VALUE_PAIR const *username; VALUE_PAIR const *password; if (!section->name) return RLM_MODULE_NOOP; username = request->username; if (!request->username) { REDEBUG("Can't perform authentication, 'User-Name' attribute not found in the request"); return RLM_MODULE_INVALID; } password = request->password; if (!password || (password->da->attr != PW_USER_PASSWORD)) { REDEBUG("You set 'Auth-Type = REST' for a request that does not contain a User-Password attribute!"); return RLM_MODULE_INVALID; } handle = fr_connection_get(inst->conn_pool); if (!handle) return RLM_MODULE_FAIL; ret = rlm_rest_perform(instance, section, handle, request, username->vp_strvalue, password->vp_strvalue); 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; }
/* * 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; }
/* * Authenticate the user with the given password. */ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, UNUSED REQUEST *request) { rlm_rest_t *inst = instance; rlm_rest_section_t *section = &inst->authenticate; void *handle; int hcode; int rcode = RLM_MODULE_OK; int ret; VALUE_PAIR const *username; VALUE_PAIR const *password; username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY); if (!username) { REDEBUG("Can't perform authentication, 'User-Name' attribute not found in the request"); return RLM_MODULE_INVALID; } password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY); if (!password) { REDEBUG("Can't perform authentication, 'Cleartext-Password' attribute not found in the control list"); return RLM_MODULE_INVALID; } handle = fr_connection_get(inst->conn_pool); if (!handle) return RLM_MODULE_FAIL; ret = rlm_rest_perform(instance, section, handle, request, username->vp_strvalue, password->vp_strvalue); if (ret < 0) { rcode = RLM_MODULE_FAIL; goto end; } 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; } } end: rlm_rest_cleanup(instance, section, handle); fr_connection_release(inst->conn_pool, handle); return rcode; }