Return correct code for error
[freeradius.git] / src / modules / rlm_rest / rlm_rest.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15
16 /**
17  * $Id$
18  * @file rlm_rest.c
19  * @brief Integrate FreeRADIUS with RESTfull APIs
20  *
21  * @copyright 2012-2014  Arran Cudbard-Bell <arran.cudbardb@freeradius.org>
22  */
23 RCSID("$Id$")
24
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/modules.h>
27 #include <freeradius-devel/token.h>
28 #include <freeradius-devel/rad_assert.h>
29
30 #include <ctype.h>
31 #include "rest.h"
32
33 /*
34  *      TLS Configuration
35  */
36 static CONF_PARSER tls_config[] = {
37         { "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_rest_section_t, tls_ca_file), NULL },
38         { "ca_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_rest_section_t, tls_ca_path), NULL },
39         { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_rest_section_t, tls_certificate_file), NULL },
40         { "private_key_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_rest_section_t, tls_private_key_file), NULL },
41         { "private_key_password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, rlm_rest_section_t, tls_private_key_password), NULL },
42         { "random_file", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, tls_random_file), NULL },
43         { "check_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_rest_section_t, tls_check_cert), "yes" },
44         { "check_cert_cn", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_rest_section_t, tls_check_cert_cn), "yes" },
45
46         { NULL, -1, 0, NULL, NULL }
47 };
48
49 /*
50  *      A mapping of configuration file names to internal variables.
51  *
52  *      Note that the string is dynamically allocated, so it MUST
53  *      be freed.  When the configuration file parse re-reads the string,
54  *      it free's the old one, and strdup's the new one, placing the pointer
55  *      to the strdup'd string into 'config.string'.  This gets around
56  *      buffer over-flows.
57  */
58 static const CONF_PARSER section_config[] = {
59         { "uri", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, uri), ""   },
60         { "method", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, method_str), "GET" },
61         { "body", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, body_str), "none" },
62         { "data", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, data), NULL },
63         { "force_to", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, force_to_str), NULL },
64
65         /* User authentication */
66         { "auth", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, auth_str), "none" },
67         { "username", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, username), NULL },
68         { "password", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, password), NULL },
69         { "require_auth", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_rest_section_t, require_auth), "no" },
70
71         /* Transfer configuration */
72         { "timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_rest_section_t, timeout), "4" },
73         { "chunk", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_rest_section_t, chunk), "0" },
74
75         /* TLS Parameters */
76         { "tls", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) tls_config },
77
78         { NULL, -1, 0, NULL, NULL }
79 };
80
81 static const CONF_PARSER module_config[] = {
82         { "connect_uri", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_t, connect_uri), NULL },
83
84         { NULL, -1, 0, NULL, NULL }
85 };
86
87 static int rlm_rest_perform(rlm_rest_t *instance, rlm_rest_section_t *section, void *handle, REQUEST *request,
88                             char const *username, char const *password)
89 {
90         ssize_t uri_len;
91         char *uri = NULL;
92
93         int ret;
94
95         RDEBUG("Expanding URI components");
96
97         /*
98          *  Build xlat'd URI, this allows REST servers to be specified by
99          *  request attributes.
100          */
101         uri_len = rest_uri_build(&uri, instance, request, section->uri);
102         if (uri_len <= 0) return -1;
103
104         RDEBUG("Sending HTTP %s to \"%s\"", fr_int2str(http_method_table, section->method, NULL), uri);
105
106         /*
107          *  Configure various CURL options, and initialise the read/write
108          *  context data.
109          */
110         ret = rest_request_config(instance, section, request, handle, section->method, section->body,
111                                   uri, username, password);
112         talloc_free(uri);
113         if (ret < 0) return -1;
114
115         /*
116          *  Send the CURL request, pre-parse headers, aggregate incoming
117          *  HTTP body data into a single contiguous buffer.
118          */
119         ret = rest_request_perform(instance, section, request, handle);
120         if (ret < 0) return -1;
121
122         return 0;
123 }
124
125 static void rlm_rest_cleanup(rlm_rest_t *instance, rlm_rest_section_t *section, void *handle)
126 {
127         rest_request_cleanup(instance, section, handle);
128 };
129
130 /*
131  *      Simple xlat to read text data from a URL
132  */
133 static ssize_t rest_xlat(void *instance, REQUEST *request,
134                          char const *fmt, char *out, size_t freespace)
135 {
136         rlm_rest_t      *inst = instance;
137         void            *handle;
138         int             hcode;
139         int             ret;
140         ssize_t         len, outlen = 0;
141         char            *uri = NULL;
142         char const      *p = fmt, *q;
143         char const      *body;
144         http_method_t   method;
145
146         /* There are no configurable parameters other than the URI */
147         rlm_rest_section_t section = {
148                 .name = "xlat",
149                 .method = HTTP_METHOD_GET,
150                 .body = HTTP_BODY_NONE,
151                 .body_str = "application/x-www-form-urlencoded",
152                 .require_auth = false,
153                 .timeout = 4,
154                 .force_to = HTTP_BODY_PLAIN
155         };
156         *out = '\0';
157
158         rad_assert(fmt);
159
160         RDEBUG("Expanding URI components");
161
162         handle = fr_connection_get(inst->conn_pool);
163         if (!handle) return -1;
164
165         /*
166          *  Extract the method from the start of the format string (if there is one)
167          */
168         method = fr_substr2int(http_method_table, p, HTTP_METHOD_UNKNOWN, -1);
169         if (method != HTTP_METHOD_UNKNOWN) {
170                 section.method = method;
171                 p += strlen(http_method_table[method].name);
172         }
173
174         /*
175          *  Trim whitespace
176          */
177         while (isspace(*p) && p++);
178
179         /*
180          *  Unescape parts of xlat'd URI, this allows REST servers to be specified by
181          *  request attributes.
182          */
183         len = rest_uri_host_unescape(&uri, instance, request, handle, p);
184         if (len <= 0) {
185                 outlen = -1;
186                 goto finish;
187         }
188
189         /*
190          *  Extract freeform body data (url can't contain spaces)
191          */
192         q = strchr(p, ' ');
193         if (q && (*++q != '\0')) {
194                 section.body = HTTP_BODY_CUSTOM_LITERAL;
195                 section.data = q;
196         }
197
198         RDEBUG("Sending HTTP %s to \"%s\"", fr_int2str(http_method_table, section.method, NULL), uri);
199
200         /*
201          *  Configure various CURL options, and initialise the read/write
202          *  context data.
203          *
204          *  @todo We could extract the User-Name and password from the URL string.
205          */
206         ret = rest_request_config(instance, &section, request, handle, section.method, section.body,
207                                   uri, NULL, NULL);
208         talloc_free(uri);
209         if (ret < 0) return -1;
210
211         /*
212          *  Send the CURL request, pre-parse headers, aggregate incoming
213          *  HTTP body data into a single contiguous buffer.
214          */
215         ret = rest_request_perform(instance, &section, request, handle);
216         if (ret < 0) return -1;
217
218         hcode = rest_get_handle_code(handle);
219         switch (hcode) {
220         case 404:
221         case 410:
222         case 403:
223         case 401:
224         {
225                 outlen = -1;
226 error:
227                 rest_response_error(request, handle);
228                 goto finish;
229         }
230         case 204:
231                 goto finish;
232
233         default:
234                 /*
235                  *      Attempt to parse content if there was any.
236                  */
237                 if ((hcode >= 200) && (hcode < 300)) {
238                         break;
239                 } else if (hcode < 500) {
240                         outlen = -2;
241                         goto error;
242                 } else {
243                         outlen = -1;
244                         goto error;
245                 }
246         }
247
248         len = rest_get_handle_data(&body, handle);
249         if ((size_t) len >= freespace) {
250                 REDEBUG("Insufficient space to write HTTP response, needed %zu bytes, have %zu bytes", len + 1,
251                         freespace);
252                 outlen = -1;
253                 goto finish;
254         }
255         if (len > 0) {
256                 outlen = len;
257                 strlcpy(out, body, len + 1);    /* strlcpy takes the size of the buffer */
258         }
259
260 finish:
261         rlm_rest_cleanup(instance, &section, handle);
262
263         fr_connection_release(inst->conn_pool, handle);
264
265         return outlen;
266 }
267
268 /*
269  *      Find the named user in this modules database.  Create the set
270  *      of attribute-value pairs to check and reply with for this user
271  *      from the database. The authentication code only needs to check
272  *      the password, the rest is done here.
273  */
274 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
275 {
276         rlm_rest_t *inst = instance;
277         rlm_rest_section_t *section = &inst->authorize;
278
279         void *handle;
280         int hcode;
281         int rcode = RLM_MODULE_OK;
282         int ret;
283
284         if (!section->name) return RLM_MODULE_NOOP;
285
286         handle = fr_connection_get(inst->conn_pool);
287         if (!handle) return RLM_MODULE_FAIL;
288
289         ret = rlm_rest_perform(instance, section, handle, request, NULL, NULL);
290         if (ret < 0) {
291                 rcode = RLM_MODULE_FAIL;
292                 goto finish;
293         }
294
295         hcode = rest_get_handle_code(handle);
296         switch (hcode) {
297         case 404:
298         case 410:
299                 rcode = RLM_MODULE_NOTFOUND;
300                 break;
301
302         case 403:
303                 rcode = RLM_MODULE_USERLOCK;
304                 break;
305
306         case 401:
307                 /*
308                  *      Attempt to parse content if there was any.
309                  */
310                 ret = rest_response_decode(inst, section, request, handle);
311                 if (ret < 0) {
312                         rcode = RLM_MODULE_FAIL;
313                         break;
314                 }
315
316                 rcode = RLM_MODULE_REJECT;
317                 break;
318
319         case 204:
320                 rcode = RLM_MODULE_OK;
321                 break;
322
323         default:
324                 /*
325                  *      Attempt to parse content if there was any.
326                  */
327                 if ((hcode >= 200) && (hcode < 300)) {
328                         ret = rest_response_decode(inst, section, request, handle);
329                         if (ret < 0)       rcode = RLM_MODULE_FAIL;
330                         else if (ret == 0) rcode = RLM_MODULE_OK;
331                         else               rcode = RLM_MODULE_UPDATED;
332                         break;
333                 } else if (hcode < 500) {
334                         rcode = RLM_MODULE_INVALID;
335                 } else {
336                         rcode = RLM_MODULE_FAIL;
337                 }
338         }
339
340 finish:
341         switch (rcode) {
342         case RLM_MODULE_INVALID:
343         case RLM_MODULE_FAIL:
344         case RLM_MODULE_USERLOCK:
345                 rest_response_error(request, handle);
346                 break;
347
348         default:
349                 break;
350         }
351
352         rlm_rest_cleanup(instance, section, handle);
353
354         fr_connection_release(inst->conn_pool, handle);
355
356         return rcode;
357 }
358
359 /*
360  *      Authenticate the user with the given password.
361  */
362 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, UNUSED REQUEST *request)
363 {
364         rlm_rest_t *inst = instance;
365         rlm_rest_section_t *section = &inst->authenticate;
366
367         void *handle;
368         int hcode;
369         int rcode = RLM_MODULE_OK;
370         int ret;
371
372         VALUE_PAIR const *username;
373         VALUE_PAIR const *password;
374
375         if (!section->name) return RLM_MODULE_NOOP;
376
377         username = request->username;
378         if (!request->username) {
379                 REDEBUG("Can't perform authentication, 'User-Name' attribute not found in the request");
380
381                 return RLM_MODULE_INVALID;
382         }
383
384         password = request->password;
385         if (!password ||
386             (password->da->attr != PW_USER_PASSWORD)) {
387                 REDEBUG("You set 'Auth-Type = REST' for a request that does not contain a User-Password attribute!");
388                 return RLM_MODULE_INVALID;
389         }
390
391         handle = fr_connection_get(inst->conn_pool);
392         if (!handle) return RLM_MODULE_FAIL;
393
394         ret = rlm_rest_perform(instance, section, handle, request, username->vp_strvalue, password->vp_strvalue);
395         if (ret < 0) {
396                 rcode = RLM_MODULE_FAIL;
397                 goto finish;
398         }
399
400         hcode = rest_get_handle_code(handle);
401         switch (hcode) {
402         case 404:
403         case 410:
404                 rcode = RLM_MODULE_NOTFOUND;
405                 break;
406
407         case 403:
408                 rcode = RLM_MODULE_USERLOCK;
409                 break;
410
411         case 401:
412                 /*
413                  *      Attempt to parse content if there was any.
414                  */
415                 ret = rest_response_decode(inst, section, request, handle);
416                 if (ret < 0) {
417                         rcode = RLM_MODULE_FAIL;
418                         break;
419                 }
420
421                 rcode = RLM_MODULE_REJECT;
422                 break;
423
424         case 204:
425                 rcode = RLM_MODULE_OK;
426                 break;
427
428         default:
429                 /*
430                  *      Attempt to parse content if there was any.
431                  */
432                 if ((hcode >= 200) && (hcode < 300)) {
433                         ret = rest_response_decode(inst, section, request, handle);
434                         if (ret < 0)       rcode = RLM_MODULE_FAIL;
435                         else if (ret == 0) rcode = RLM_MODULE_OK;
436                         else               rcode = RLM_MODULE_UPDATED;
437                         break;
438                 } else if (hcode < 500) {
439                         rcode = RLM_MODULE_INVALID;
440                 } else {
441                         rcode = RLM_MODULE_FAIL;
442                 }
443         }
444
445 finish:
446         switch (rcode) {
447         case RLM_MODULE_INVALID:
448         case RLM_MODULE_FAIL:
449         case RLM_MODULE_USERLOCK:
450                 rest_response_error(request, handle);
451                 break;
452
453         default:
454                 break;
455         }
456
457         rlm_rest_cleanup(instance, section, handle);
458
459         fr_connection_release(inst->conn_pool, handle);
460
461         return rcode;
462 }
463
464 /*
465  *      Send accounting info to a REST API endpoint
466  */
467 static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, UNUSED REQUEST *request)
468 {
469         rlm_rest_t *inst = instance;
470         rlm_rest_section_t *section = &inst->accounting;
471
472         void *handle;
473         int hcode;
474         int rcode = RLM_MODULE_OK;
475         int ret;
476
477         if (!section->name) return RLM_MODULE_NOOP;
478
479         handle = fr_connection_get(inst->conn_pool);
480         if (!handle) return RLM_MODULE_FAIL;
481
482         ret = rlm_rest_perform(inst, section, handle, request, NULL, NULL);
483         if (ret < 0) {
484                 rcode = RLM_MODULE_FAIL;
485                 goto finish;
486         }
487
488         hcode = rest_get_handle_code(handle);
489         if (hcode >= 500) {
490                 rcode = RLM_MODULE_FAIL;
491         } else if (hcode == 204) {
492                 rcode = RLM_MODULE_OK;
493         } else if ((hcode >= 200) && (hcode < 300)) {
494                 ret = rest_response_decode(inst, section, request, handle);
495                 if (ret < 0)       rcode = RLM_MODULE_FAIL;
496                 else if (ret == 0) rcode = RLM_MODULE_OK;
497                 else               rcode = RLM_MODULE_UPDATED;
498         } else {
499                 rcode = RLM_MODULE_INVALID;
500         }
501
502 finish:
503         switch (rcode) {
504         case RLM_MODULE_INVALID:
505         case RLM_MODULE_FAIL:
506                 rest_response_error(request, handle);
507                 break;
508
509         default:
510                 break;
511         }
512
513         rlm_rest_cleanup(inst, section, handle);
514
515         fr_connection_release(inst->conn_pool, handle);
516
517         return rcode;
518 }
519
520 /*
521  *      Send post-auth info to a REST API endpoint
522  */
523 static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, UNUSED REQUEST *request)
524 {
525         rlm_rest_t *inst = instance;
526         rlm_rest_section_t *section = &inst->post_auth;
527
528         void *handle;
529         int hcode;
530         int rcode = RLM_MODULE_OK;
531         int ret;
532
533         if (!section->name) return RLM_MODULE_NOOP;
534
535         handle = fr_connection_get(inst->conn_pool);
536         if (!handle) return RLM_MODULE_FAIL;
537
538         ret = rlm_rest_perform(inst, section, handle, request, NULL, NULL);
539         if (ret < 0) {
540                 rcode = RLM_MODULE_FAIL;
541                 goto finish;
542         }
543
544         hcode = rest_get_handle_code(handle);
545         if (hcode >= 500) {
546                 rcode = RLM_MODULE_FAIL;
547         } else if (hcode == 204) {
548                 rcode = RLM_MODULE_OK;
549         } else if ((hcode >= 200) && (hcode < 300)) {
550                 ret = rest_response_decode(inst, section, request, handle);
551                 if (ret < 0)       rcode = RLM_MODULE_FAIL;
552                 else if (ret == 0) rcode = RLM_MODULE_OK;
553                 else               rcode = RLM_MODULE_UPDATED;
554         } else {
555                 rcode = RLM_MODULE_INVALID;
556         }
557
558 finish:
559         switch (rcode) {
560         case RLM_MODULE_INVALID:
561         case RLM_MODULE_FAIL:
562                 rest_response_error(request, handle);
563                 break;
564
565         default:
566                 break;
567         }
568
569         rlm_rest_cleanup(inst, section, handle);
570
571         fr_connection_release(inst->conn_pool, handle);
572
573         return rcode;
574 }
575
576 static int parse_sub_section(CONF_SECTION *parent, rlm_rest_section_t *config, rlm_components_t comp)
577 {
578         CONF_SECTION *cs;
579
580         char const *name = section_type_value[comp].section;
581
582         cs = cf_section_sub_find(parent, name);
583         if (!cs) {
584                 config->name = NULL;
585                 return 0;
586         }
587
588         if (cf_section_parse(cs, config, section_config) < 0) {
589                 config->name = NULL;
590                 return -1;
591         }
592
593         /*
594          *  Add section name (Maybe add to headers later?).
595          */
596         config->name = name;
597
598         /*
599          *  Sanity check
600          */
601          if ((config->username && !config->password) || (!config->username && config->password)) {
602                 cf_log_err_cs(cs, "'username' and 'password' must both be set or both be absent");
603
604                 return -1;
605          }
606
607         /*
608          *  Convert HTTP method auth and body type strings into their integer equivalents.
609          */
610         config->auth = fr_str2int(http_auth_table, config->auth_str, HTTP_AUTH_UNKNOWN);
611         if (config->auth == HTTP_AUTH_UNKNOWN) {
612                 cf_log_err_cs(cs, "Unknown HTTP auth type '%s'", config->auth_str);
613
614                 return -1;
615         } else if ((config->auth != HTTP_AUTH_NONE) && !http_curl_auth[config->auth]) {
616                 cf_log_err_cs(cs, "Unsupported HTTP auth type \"%s\", check libcurl version, OpenSSL build "
617                               "configuration, then recompile this module", config->auth_str);
618
619                 return -1;
620         }
621
622         config->method = fr_str2int(http_method_table, config->method_str, HTTP_METHOD_CUSTOM);
623
624         /*
625          *  We don't have any custom user data, so we need to select the right encoder based
626          *  on the body type.
627          *
628          *  To make this slightly more/less confusing, we accept both canonical body_types,
629          *  and content_types.
630          */
631         if (!config->data) {
632                 config->body = fr_str2int(http_body_type_table, config->body_str, HTTP_BODY_UNKNOWN);
633                 if (config->body == HTTP_BODY_UNKNOWN) {
634                         config->body = fr_str2int(http_content_type_table, config->body_str, HTTP_BODY_UNKNOWN);
635                 }
636
637                 if (config->body == HTTP_BODY_UNKNOWN) {
638                         cf_log_err_cs(cs, "Unknown HTTP body type '%s'", config->body_str);
639                         return -1;
640                 }
641
642                 switch (http_body_type_supported[config->body]) {
643                 case HTTP_BODY_UNSUPPORTED:
644                         cf_log_err_cs(cs, "Unsupported HTTP body type \"%s\", please submit patches",
645                                       config->body_str);
646                         return -1;
647
648                 case HTTP_BODY_INVALID:
649                         cf_log_err_cs(cs, "Invalid HTTP body type.  \"%s\" is not a valid web API data "
650                                       "markup format", config->body_str);
651                         return -1;
652
653                 default:
654                         break;
655                 }
656         /*
657          *  We have custom body data so we set HTTP_BODY_CUSTOM_XLAT, but also need to try and
658          *  figure out what content-type to use. So if they've used the canonical form we
659          *  need to convert it back into a proper HTTP content_type value.
660          */
661         } else {
662                 http_body_type_t body;
663
664                 config->body = HTTP_BODY_CUSTOM_XLAT;
665
666                 body = fr_str2int(http_body_type_table, config->body_str, HTTP_BODY_UNKNOWN);
667                 if (body != HTTP_BODY_UNKNOWN) {
668                         config->body_str = fr_int2str(http_content_type_table, body, config->body_str);
669                 }
670         }
671
672         if (config->force_to_str) {
673                 config->force_to = fr_str2int(http_body_type_table, config->force_to_str, HTTP_BODY_UNKNOWN);
674                 if (config->force_to == HTTP_BODY_UNKNOWN) {
675                         config->force_to = fr_str2int(http_content_type_table, config->force_to_str, HTTP_BODY_UNKNOWN);
676                 }
677
678                 if (config->force_to == HTTP_BODY_UNKNOWN) {
679                         cf_log_err_cs(cs, "Unknown forced response body type '%s'", config->force_to_str);
680                         return -1;
681                 }
682
683                 switch (http_body_type_supported[config->force_to]) {
684                 case HTTP_BODY_UNSUPPORTED:
685                         cf_log_err_cs(cs, "Unsupported forced response body type \"%s\", please submit patches",
686                                       config->force_to_str);
687                         return -1;
688
689                 case HTTP_BODY_INVALID:
690                         cf_log_err_cs(cs, "Invalid HTTP forced response body type.  \"%s\" is not a valid web API data "
691                                       "markup format", config->force_to_str);
692                         return -1;
693
694                 default:
695                         break;
696                 }
697         }
698
699         return 0;
700 }
701
702 /*
703  *      Do any per-module initialization that is separate to each
704  *      configured instance of the module.  e.g. set up connections
705  *      to external databases, read configuration files, set up
706  *      dictionary entries, etc.
707  *
708  *      If configuration information is given in the config section
709  *      that must be referenced in later calls, store a handle to it
710  *      in *instance otherwise put a null pointer there.
711  */
712 static int mod_instantiate(CONF_SECTION *conf, void *instance)
713 {
714         rlm_rest_t *inst = instance;
715         char const *xlat_name;
716
717         xlat_name = cf_section_name2(conf);
718         if (!xlat_name) {
719                 xlat_name = cf_section_name1(conf);
720         }
721
722         inst->xlat_name = xlat_name;
723
724         /*
725          *      Register the rest xlat function
726          */
727         xlat_register(inst->xlat_name, rest_xlat, rest_uri_escape, inst);
728
729         /*
730          *      Parse sub-section configs.
731          */
732         if (
733                 (parse_sub_section(conf, &inst->authorize, RLM_COMPONENT_AUTZ) < 0) ||
734                 (parse_sub_section(conf, &inst->authenticate, RLM_COMPONENT_AUTH) < 0) ||
735                 (parse_sub_section(conf, &inst->accounting, RLM_COMPONENT_ACCT) < 0) ||
736
737 /* @todo add behaviour for checksimul */
738 /*              (parse_sub_section(conf, &inst->checksimul, RLM_COMPONENT_SESS) < 0) || */
739                 (parse_sub_section(conf, &inst->post_auth, RLM_COMPONENT_POST_AUTH) < 0))
740         {
741                 return -1;
742         }
743
744         /*
745          *      Initialise REST libraries.
746          */
747         if (rest_init(inst) < 0) {
748                 return -1;
749         }
750
751         inst->conn_pool = fr_connection_pool_module_init(conf, inst, mod_conn_create, mod_conn_alive, NULL);
752         if (!inst->conn_pool) {
753                 return -1;
754         }
755
756         return 0;
757 }
758
759 /*
760  *      Only free memory we allocated.  The strings allocated via
761  *      cf_section_parse() do not need to be freed.
762  */
763 static int mod_detach(void *instance)
764 {
765         rlm_rest_t *inst = instance;
766
767         fr_connection_pool_delete(inst->conn_pool);
768
769         xlat_unregister(inst->xlat_name, rest_xlat, instance);
770
771         /* Free any memory used by libcurl */
772         rest_cleanup();
773
774         return 0;
775 }
776
777 /*
778  *      The module name should be the only globally exported symbol.
779  *      That is, everything else should be 'static'.
780  *
781  *      If the module needs to temporarily modify it's instantiation
782  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
783  *      The server will then take care of ensuring that the module
784  *      is single-threaded.
785  */
786 module_t rlm_rest = {
787         RLM_MODULE_INIT,
788         "rlm_rest",
789         RLM_TYPE_THREAD_SAFE,           /* type */
790         sizeof(rlm_rest_t),
791         module_config,
792         mod_instantiate,                /* instantiation */
793         mod_detach,                     /* detach */
794         {
795                 mod_authenticate,       /* authentication */
796                 mod_authorize,          /* authorization */
797                 NULL,                   /* preaccounting */
798                 mod_accounting,         /* accounting */
799                 NULL,                   /* checksimul */
800                 NULL,                   /* pre-proxy */
801                 NULL,                   /* post-proxy */
802                 mod_post_auth           /* post-auth */
803         },
804 };