6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2012 Arran Cudbard-Bell <arran.cudbardb@freeradius.org>>
23 #include <freeradius-devel/ident.h>
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/token.h>
33 * A mapping of configuration file names to internal variables.
35 * Note that the string is dynamically allocated, so it MUST
36 * be freed. When the configuration file parse re-reads the string,
37 * it free's the old one, and strdup's the new one, placing the pointer
38 * to the strdup'd string into 'config.string'. This gets around
41 static const CONF_PARSER section_config[] = {
42 { "uri", PW_TYPE_STRING_PTR,
43 offsetof(rlm_rest_section_t, uri), NULL, "" },
44 { "method", PW_TYPE_STRING_PTR,
45 offsetof(rlm_rest_section_t, method_str), NULL, "GET" },
46 { "body", PW_TYPE_STRING_PTR,
47 offsetof(rlm_rest_section_t, body_str), NULL, "post" },
49 /* User authentication */
50 { "auth", PW_TYPE_STRING_PTR,
51 offsetof(rlm_rest_section_t, auth_str), NULL, "none" },
52 { "username", PW_TYPE_STRING_PTR,
53 offsetof(rlm_rest_section_t, username), NULL, "" },
54 { "password", PW_TYPE_STRING_PTR,
55 offsetof(rlm_rest_section_t, password), NULL, "" },
56 { "require_auth", PW_TYPE_BOOLEAN,
57 offsetof(rlm_rest_section_t, require_auth), NULL, "no"},
59 /* SSL authentication */
60 { "certificate_file", PW_TYPE_FILENAME,
61 offsetof(rlm_rest_section_t, certificate_file), NULL, NULL },
62 { "pem_file_type", PW_TYPE_BOOLEAN,
63 offsetof(rlm_rest_section_t, file_type), NULL, "yes" },
64 { "private_key_file", PW_TYPE_FILENAME,
65 offsetof(rlm_rest_section_t, private_key_file), NULL, NULL },
66 { "private_key_password", PW_TYPE_STRING_PTR,
67 offsetof(rlm_rest_section_t, private_key_password), NULL, NULL },
68 { "CA_file", PW_TYPE_FILENAME,
69 offsetof(rlm_rest_section_t, ca_file), NULL, NULL },
70 { "CA_path", PW_TYPE_FILENAME,
71 offsetof(rlm_rest_section_t, ca_path), NULL, NULL },
72 { "random_file", PW_TYPE_STRING_PTR,
73 offsetof(rlm_rest_section_t, random_file), NULL, NULL },
74 { "check_cert_cn", PW_TYPE_BOOLEAN,
75 offsetof(rlm_rest_section_t, check_cert_cn), NULL, "yes"},
77 /* Transfer configuration */
78 { "timeout", PW_TYPE_INTEGER,
79 offsetof(rlm_rest_section_t, timeout), NULL, "0" },
80 { "chunk", PW_TYPE_INTEGER,
81 offsetof(rlm_rest_section_t, chunk), NULL, "0" },
83 { NULL, -1, 0, NULL, NULL }
86 static const CONF_PARSER module_config[] = {
87 { "connect_uri", PW_TYPE_STRING_PTR,
88 offsetof(rlm_rest_t, connect_uri), NULL, "http://localhost/" },
90 { NULL, -1, 0, NULL, NULL }
93 static int rlm_rest_perform (rlm_rest_t *instance, rlm_rest_section_t *section,
94 void *handle, REQUEST *request)
97 char uri[REST_URI_MAX_LEN];
101 RDEBUG("Expanding URI components");
103 * Build xlat'd URI, this allows REST servers to be specified by
104 * request attributes.
106 uri_len = rest_uri_build(instance, section, request, uri, sizeof(uri));
107 if (uri_len <= 0) return -1;
109 RDEBUG("Sending HTTP %s to \"%s\"",
110 fr_int2str(http_method_table, section->method, NULL),
114 * Configure various CURL options, and initialise the read/write
117 ret = rest_request_config(instance, section, request,
122 if (ret <= 0) return -1;
125 * Send the CURL request, pre-parse headers, aggregate incoming
126 * HTTP body data into a single contiguous buffer.
128 ret = rest_request_perform(instance, section, handle);
129 if (ret <= 0) return -1;
134 static void rlm_rest_cleanup (rlm_rest_t *instance, rlm_rest_section_t *section,
137 rest_request_cleanup(instance, section, handle);
140 static int parse_sub_section(CONF_SECTION *parent,
141 rlm_rest_t *instance, rlm_rest_section_t *config,
142 rlm_components_t comp)
146 const char *name = section_type_value[comp].section;
148 cs = cf_section_sub_find(parent, name);
150 /* TODO: Should really setup section with default values */
153 cf_section_parse(cs, config, section_config);
156 * Add section name (Maybe add to headers later?).
161 * Convert HTTP method auth and body type strings into their
162 * integer equivalents.
164 config->auth = fr_str2int(http_auth_table, config->auth_str,
167 if (config->auth == HTTP_AUTH_UNKNOWN) {
168 radlog(L_ERR, "rlm_rest (%s): Unknown HTTP auth type \"%s\"",
169 instance->xlat_name, config->auth_str);
173 if (!http_curl_auth[config->auth]) {
174 radlog(L_ERR, "rlm_rest (%s): Unsupported HTTP auth type \"%s\""
175 ", check libcurl version, OpenSSL build configuration,"
176 " then recompile this module",
177 instance->xlat_name, config->body_str);
181 config->method = fr_str2int(http_method_table, config->method_str,
184 config->body = fr_str2int(http_body_table, config->body_str,
187 if (config->body == HTTP_BODY_UNKNOWN) {
188 radlog(L_ERR, "rlm_rest (%s): Unknown HTTP body type \"%s\"",
189 instance->xlat_name, config->body_str);
193 if (http_body_type_supported[config->body] == HTTP_BODY_UNSUPPORTED) {
194 radlog(L_ERR, "rlm_rest (%s): Unsupported HTTP body type \"%s\""
195 ", please submit patches", instance->xlat_name,
204 * Do any per-module initialization that is separate to each
205 * configured instance of the module. e.g. set up connections
206 * to external databases, read configuration files, set up
207 * dictionary entries, etc.
209 * If configuration information is given in the config section
210 * that must be referenced in later calls, store a handle to it
211 * in *instance otherwise put a null pointer there.
213 static int rlm_rest_instantiate(CONF_SECTION *conf, void **instance)
216 const char *xlat_name;
219 * Allocate memory for instance data.
221 data = rad_malloc(sizeof(*data));
225 memset(data, 0, sizeof(*data));
228 * If the configuration parameters can't be parsed, then
231 if (cf_section_parse(conf, data, module_config) < 0) {
236 xlat_name = cf_section_name2(conf);
237 if (xlat_name == NULL) {
238 xlat_name = cf_section_name1(conf);
241 data->xlat_name = xlat_name;
244 * Parse sub-section configs.
247 (parse_sub_section(conf, data, &data->authorize,
248 RLM_COMPONENT_AUTZ) < 0) ||
249 (parse_sub_section(conf, data, &data->authenticate,
250 RLM_COMPONENT_AUTH) < 0) ||
251 (parse_sub_section(conf, data, &data->accounting,
252 RLM_COMPONENT_ACCT) < 0) ||
253 (parse_sub_section(conf, data, &data->checksimul,
254 RLM_COMPONENT_SESS) < 0) ||
255 (parse_sub_section(conf, data, &data->postauth,
256 RLM_COMPONENT_POST_AUTH) < 0))
262 * Initialise REST libraries.
264 if (!rest_init(data)) {
268 data->conn_pool = fr_connection_pool_init(conf, data,
273 if (!data->conn_pool) {
283 * Find the named user in this modules database. Create the set
284 * of attribute-value pairs to check and reply with for this user
285 * from the database. The authentication code only needs to check
286 * the password, the rest is done here.
288 static int rlm_rest_authorize(void *instance, REQUEST *request)
290 rlm_rest_t *my_instance = instance;
291 rlm_rest_section_t *section = &my_instance->authorize;
295 int rcode = RLM_MODULE_OK;
298 handle = fr_connection_get(my_instance->conn_pool);
299 if (!handle) return RLM_MODULE_FAIL;
301 ret = rlm_rest_perform(instance, section, handle, request);
303 rcode = RLM_MODULE_FAIL;
307 hcode = rest_get_handle_code(handle);
312 rcode = RLM_MODULE_NOTFOUND;
315 rcode = RLM_MODULE_USERLOCK;
319 * Attempt to parse content if there was any.
321 ret = rest_request_decode(my_instance, section,
324 rcode = RLM_MODULE_FAIL;
328 rcode = RLM_MODULE_REJECT;
331 rcode = RLM_MODULE_OK;
335 * Attempt to parse content if there was any.
337 if ((hcode >= 200) && (hcode < 300)) {
338 ret = rest_request_decode(my_instance, section,
340 if (ret < 0) rcode = RLM_MODULE_FAIL;
341 else if (ret == 0) rcode = RLM_MODULE_OK;
342 else rcode = RLM_MODULE_UPDATED;
344 } else if (hcode < 500) {
345 rcode = RLM_MODULE_INVALID;
347 rcode = RLM_MODULE_FAIL;
353 rlm_rest_cleanup(instance, section, handle);
355 fr_connection_release(my_instance->conn_pool, handle);
361 * Authenticate the user with the given password.
363 static int rlm_rest_authenticate(void *instance, REQUEST *request)
365 /* quiet the compiler */
369 return RLM_MODULE_OK;
373 * Write accounting information to this modules database.
375 static int rlm_rest_accounting(void *instance, REQUEST *request)
377 /* quiet the compiler */
381 return RLM_MODULE_OK;
385 * See if a user is already logged in. Sets request->simul_count to the
386 * current session count for this user and sets request->simul_mpp to 2
387 * if it looks like a multilink attempt based on the requested IP
388 * address, otherwise leaves request->simul_mpp alone.
390 * Check twice. If on the first pass the user exceeds his
391 * max. number of logins, do a second pass and validate all
392 * logins by querying the terminal server (using eg. SNMP).
394 static int rlm_rest_checksimul(void *instance, REQUEST *request)
398 request->simul_count=0;
400 return RLM_MODULE_OK;
404 * Only free memory we allocated. The strings allocated via
405 * cf_section_parse() do not need to be freed.
407 static int rlm_rest_detach(void *instance)
409 rlm_rest_t *my_instance = instance;
411 fr_connection_pool_delete(my_instance->conn_pool);
415 /* Free any memory used by libcurl */
422 * The module name should be the only globally exported symbol.
423 * That is, everything else should be 'static'.
425 * If the module needs to temporarily modify it's instantiation
426 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
427 * The server will then take care of ensuring that the module
428 * is single-threaded.
430 module_t rlm_rest = {
433 RLM_TYPE_THREAD_SAFE, /* type */
434 rlm_rest_instantiate, /* instantiation */
435 rlm_rest_detach, /* detach */
437 rlm_rest_authenticate, /* authentication */
438 rlm_rest_authorize, /* authorization */
439 NULL, /* preaccounting */
440 rlm_rest_accounting, /* accounting */
441 rlm_rest_checksimul, /* checksimul */
442 NULL, /* pre-proxy */
443 NULL, /* post-proxy */