Pass a threadsafe ctx into fr_connection_pool create callback
[freeradius.git] / src / modules / rlm_rest / rest.c
1 /*
2  *   This program is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or
5  *   (at your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16
17 /*
18  * $Id$
19  *
20  * @brief Functions and datatypes for the REST (HTTP) transport.
21  * @file rest.c
22  *
23  * @copyright 2012-2014  Arran Cudbard-Bell <a.cudbard-bell@freeradius.org>
24  */
25
26 RCSID("$Id$")
27
28 #include <ctype.h>
29 #include <string.h>
30 #include <time.h>
31
32 #include <freeradius-devel/rad_assert.h>
33 #include <freeradius-devel/radiusd.h>
34 #include <freeradius-devel/libradius.h>
35 #include <freeradius-devel/connection.h>
36
37 #include "rest.h"
38
39 /** Table of encoder/decoder support.
40  *
41  * Indexes in this table match the http_body_type_t enum, and should be
42  * updated if additional enum values are added.
43  *
44  * @see http_body_type_t
45  */
46 const http_body_type_t http_body_type_supported[HTTP_BODY_NUM_ENTRIES] = {
47         HTTP_BODY_UNKNOWN,              // HTTP_BODY_UNKOWN
48         HTTP_BODY_UNSUPPORTED,          // HTTP_BODY_UNSUPPORTED
49         HTTP_BODY_UNSUPPORTED,          // HTTP_BODY_UNAVAILABLE
50         HTTP_BODY_UNSUPPORTED,          // HTTP_BODY_INVALID
51         HTTP_BODY_NONE,                 // HTTP_BODY_NONE
52         HTTP_BODY_CUSTOM_XLAT,          // HTTP_BODY_CUSTOM_XLAT
53         HTTP_BODY_CUSTOM_LITERAL,       // HTTP_BODY_CUSTOM_LITERAL
54         HTTP_BODY_POST,                 // HTTP_BODY_POST
55 #ifdef HAVE_JSON
56         HTTP_BODY_JSON,                 // HTTP_BODY_JSON
57 #else
58         HTTP_BODY_UNAVAILABLE,
59 #endif
60         HTTP_BODY_UNSUPPORTED,          // HTTP_BODY_XML
61         HTTP_BODY_UNSUPPORTED,          // HTTP_BODY_YAML
62         HTTP_BODY_INVALID,              // HTTP_BODY_HTML
63         HTTP_BODY_INVALID               // HTTP_BODY_PLAIN
64 };
65
66 /*
67  *      Lib CURL doesn't define symbols for unsupported auth methods
68  */
69 #ifndef CURLOPT_TLSAUTH_SRP
70 #  define CURLOPT_TLSAUTH_SRP   0
71 #endif
72 #ifndef CURLAUTH_BASIC
73 #  define CURLAUTH_BASIC        0
74 #endif
75 #ifndef CURLAUTH_DIGEST
76 #  define CURLAUTH_DIGEST       0
77 #endif
78 #ifndef CURLAUTH_DIGEST_IE
79 #  define CURLAUTH_DIGEST_IE    0
80 #endif
81 #ifndef CURLAUTH_GSSNEGOTIATE
82 #  define CURLAUTH_GSSNEGOTIATE 0
83 #endif
84 #ifndef CURLAUTH_NTLM
85 #  define CURLAUTH_NTLM         0
86 #endif
87 #ifndef CURLAUTH_NTLM_WB
88 #  define CURLAUTH_NTLM_WB      0
89 #endif
90
91 #define SET_OPTION(_x, _y)\
92 do {\
93         if ((ret = curl_easy_setopt(candle, _x, _y)) != CURLE_OK) {\
94                 option = STRINGIFY(_x);\
95                 goto error;\
96         }\
97 } while (0)
98
99 const unsigned long http_curl_auth[HTTP_AUTH_NUM_ENTRIES] = {
100         0,                      // HTTP_AUTH_UNKNOWN
101         0,                      // HTTP_AUTH_NONE
102         CURLOPT_TLSAUTH_SRP,    // HTTP_AUTH_TLS_SRP
103         CURLAUTH_BASIC,         // HTTP_AUTH_BASIC
104         CURLAUTH_DIGEST,        // HTTP_AUTH_DIGEST
105         CURLAUTH_DIGEST_IE,     // HTTP_AUTH_DIGEST_IE
106         CURLAUTH_GSSNEGOTIATE,  // HTTP_AUTH_GSSNEGOTIATE
107         CURLAUTH_NTLM,          // HTTP_AUTH_NTLM
108         CURLAUTH_NTLM_WB,       // HTTP_AUTH_NTLM_WB
109         CURLAUTH_ANY,           // HTTP_AUTH_ANY
110         CURLAUTH_ANYSAFE        // HTTP_AUTH_ANY_SAFE
111 };
112
113
114 /** Conversion table for method config values.
115  *
116  * HTTP verb strings for http_method_t enum values. Used by libcurl in the
117  * status line of the outgoing HTTP header, by rest_response_header for decoding
118  * incoming HTTP responses, and by the configuration parser.
119  *
120  * @note must be kept in sync with http_method_t enum.
121  *
122  * @see http_method_t
123  * @see fr_str2int
124  * @see fr_int2str
125  */
126 const FR_NAME_NUMBER http_method_table[] = {
127         { "UNKNOWN",                            HTTP_METHOD_UNKNOWN     },
128         { "GET",                                HTTP_METHOD_GET         },
129         { "POST",                               HTTP_METHOD_POST        },
130         { "PUT",                                HTTP_METHOD_PUT         },
131         { "DELETE",                             HTTP_METHOD_DELETE      },
132
133         {  NULL , -1 }
134 };
135
136 /** Conversion table for type config values.
137  *
138  * Textual names for http_body_type_t enum values, used by the
139  * configuration parser.
140  *
141  * @see http_body_Type_t
142  * @see fr_str2int
143  * @see fr_int2str
144  */
145 const FR_NAME_NUMBER http_body_type_table[] = {
146         { "unknown",                            HTTP_BODY_UNKNOWN       },
147         { "unsupported",                        HTTP_BODY_UNSUPPORTED   },
148         { "unavailable",                        HTTP_BODY_UNAVAILABLE   },
149         { "invalid",                            HTTP_BODY_INVALID       },
150         { "none",                               HTTP_BODY_NONE          },
151         { "post",                               HTTP_BODY_POST          },
152         { "json",                               HTTP_BODY_JSON          },
153         { "xml",                                HTTP_BODY_XML           },
154         { "yaml",                               HTTP_BODY_YAML          },
155         { "html",                               HTTP_BODY_HTML          },
156         { "plain",                              HTTP_BODY_PLAIN         },
157
158         {  NULL , -1 }
159 };
160
161 const FR_NAME_NUMBER http_auth_table[] = {
162         { "none",                               HTTP_AUTH_NONE          },
163         { "srp",                                HTTP_AUTH_TLS_SRP       },
164         { "basic",                              HTTP_AUTH_BASIC         },
165         { "digest",                             HTTP_AUTH_DIGEST        },
166         { "digest-ie",                          HTTP_AUTH_DIGEST_IE     },
167         { "gss-negotiate",                      HTTP_AUTH_GSSNEGOTIATE  },
168         { "ntlm",                               HTTP_AUTH_NTLM          },
169         { "ntlm-winbind",                       HTTP_AUTH_NTLM_WB       },
170         { "any",                                HTTP_AUTH_ANY           },
171         { "safe",                               HTTP_AUTH_ANY_SAFE      },
172
173         {  NULL , -1 }
174 };
175
176 /** Conversion table for "Content-Type" header values.
177  *
178  * Used by rest_response_header for parsing incoming headers.
179  *
180  * Values we expect to see in the 'Content-Type:' header of the incoming
181  * response.
182  *
183  * Some data types (like YAML) do no have standard MIME types defined,
184  * so multiple types, are listed here.
185  *
186  * @see http_body_Type_t
187  * @see fr_str2int
188  * @see fr_int2str
189  */
190 const FR_NAME_NUMBER http_content_type_table[] = {
191         { "application/x-www-form-urlencoded",  HTTP_BODY_POST          },
192         { "application/json",                   HTTP_BODY_JSON          },
193         { "text/html",                          HTTP_BODY_HTML          },
194         { "text/plain",                         HTTP_BODY_PLAIN         },
195         { "text/xml",                           HTTP_BODY_XML           },
196         { "text/yaml",                          HTTP_BODY_YAML          },
197         { "text/x-yaml",                        HTTP_BODY_YAML          },
198         { "application/yaml",                   HTTP_BODY_YAML          },
199         { "application/x-yaml",                 HTTP_BODY_YAML          },
200
201         {  NULL , -1 }
202 };
203
204 /*
205  *      Encoder specific structures.
206  *      @todo split encoders/decoders into submodules.
207  */
208 typedef struct rest_custom_data {
209         char const *p;          //!< how much text we've sent so far.
210 } rest_custom_data_t;
211
212 #ifdef HAVE_JSON
213 /** Flags to control the conversion of JSON values to VALUE_PAIRs.
214  *
215  * These fields are set when parsing the expanded format for value pairs in
216  * JSON, and control how json_pairmake_leaf and json_pairmake convert the JSON
217  * value, and move the new VALUE_PAIR into an attribute list.
218  *
219  * @see json_pairmake
220  * @see json_pairmake_leaf
221  */
222 typedef struct json_flags {
223         int do_xlat;            //!< If true value will be expanded with xlat.
224         int is_json;            //!< If true value will be inserted as raw JSON
225                                 // (multiple values not supported).
226         FR_TOKEN op;            //!< The operator that determines how the new VP
227                                 // is processed. @see fr_tokens
228 } json_flags_t;
229 #endif
230
231 /** Initialises libcurl.
232  *
233  * Allocates global variables and memory required for libcurl to fundtion.
234  * MUST only be called once per module instance.
235  *
236  * rest_cleanup must not be called if rest_init fails.
237  *
238  * @see rest_cleanup
239  *
240  * @param[in] instance configuration data.
241  * @return 0 if init succeeded -1 if it failed.
242  */
243 int rest_init(rlm_rest_t *instance)
244 {
245         static bool version_done;
246         CURLcode ret;
247
248         /* developer sanity */
249         rad_assert((sizeof(http_body_type_supported) / sizeof(*http_body_type_supported)) == HTTP_BODY_NUM_ENTRIES);
250
251         ret = curl_global_init(CURL_GLOBAL_ALL);
252         if (ret != CURLE_OK) {
253                 ERROR("rlm_rest (%s): CURL init returned error: %i - %s",
254                       instance->xlat_name,
255                       ret, curl_easy_strerror(ret));
256
257                 curl_global_cleanup();
258                 return -1;
259         }
260
261         if (!version_done) {
262                 curl_version_info_data *curlversion;
263
264                 version_done = true;
265
266                 curlversion = curl_version_info(CURLVERSION_NOW);
267                 if (strcmp(LIBCURL_VERSION, curlversion->version) != 0) {
268                         WARN("rlm_rest: libcurl version changed since the server was built");
269                         WARN("rlm_rest: linked: %s built: %s", curlversion->version, LIBCURL_VERSION);
270                 }
271
272                 INFO("rlm_rest: libcurl version: %s", curl_version());
273         }
274
275         return 0;
276 }
277
278 /** Cleans up after libcurl.
279  *
280  * Wrapper around curl_global_cleanup, frees any memory allocated by rest_init.
281  * Must only be called once per call of rest_init.
282  *
283  * @see rest_init
284  */
285 void rest_cleanup(void)
286 {
287         curl_global_cleanup();
288 }
289
290
291 /** Frees a libcurl handle, and any additional memory used by context data.
292  *
293  * @param[in] randle rlm_rest_handle_t to close and free.
294  * @return returns true.
295  */
296 static int _mod_conn_free(rlm_rest_handle_t *randle)
297 {
298         curl_easy_cleanup(randle->handle);
299
300         return 0;
301 }
302
303 /** Creates a new connection handle for use by the FR connection API.
304  *
305  * Matches the fr_connection_create_t function prototype, is passed to
306  * fr_connection_pool_init, and called when a new connection is required by the
307  * connection pool API.
308  *
309  * Creates an instances of rlm_rest_handle_t, and rlm_rest_curl_context_t
310  * which hold the context data required for generating requests and parsing
311  * responses.
312  *
313  * If instance->connect_uri is not NULL libcurl will attempt to open a
314  * TCP socket to the server specified in the URI. This is done so that when the
315  * socket is first used, there will already be a cached TCP connection to the
316  * REST server associated with the curl handle.
317  *
318  * @see fr_connection_pool_init
319  * @see fr_connection_create_t
320  * @see connection.c
321  *
322  * @param[in] instance configuration data.
323  * @return connection handle or NULL if the connection failed or couldn't
324  *      be initialised.
325  */
326 void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
327 {
328         rlm_rest_t *inst = instance;
329
330         rlm_rest_handle_t       *randle = NULL;
331         rlm_rest_curl_context_t *curl_ctx = NULL;
332
333         CURL *candle = curl_easy_init();
334
335         CURLcode ret = CURLE_OK;
336         char const *option = "unknown";
337
338         if (!candle) {
339                 ERROR("rlm_rest (%s): Failed to create CURL handle", inst->xlat_name);
340                 return NULL;
341         }
342
343         if (inst->connect_uri) {
344                 /*
345                  *  re-establish TCP connection to webserver. This would usually be
346                  *  done on the first request, but we do it here to minimise
347                  *  latency.
348                  */
349                 SET_OPTION(CURLOPT_CONNECT_ONLY, 1);
350                 SET_OPTION(CURLOPT_URL, inst->connect_uri);
351
352                 DEBUG("rlm_rest (%s): Connecting to \"%s\"", inst->xlat_name, inst->connect_uri);
353
354                 ret = curl_easy_perform(candle);
355                 if (ret != CURLE_OK) {
356                         ERROR("rlm_rest (%s): Connection failed: %i - %s", inst->xlat_name, ret, curl_easy_strerror(ret));
357
358                         goto connection_error;
359                 }
360         } else {
361                 DEBUG2("rlm_rest (%s): Skipping pre-connect, connect_uri not specified", inst->xlat_name);
362         }
363
364         /*
365          *  Allocate memory for the connection handle abstraction.
366          */
367         randle = talloc_zero(ctx, rlm_rest_handle_t);
368         curl_ctx = talloc_zero(randle, rlm_rest_curl_context_t);
369
370         curl_ctx->headers = NULL; /* CURL needs this to be NULL */
371         curl_ctx->request.instance = inst;
372
373         randle->ctx = curl_ctx;
374         randle->handle = candle;
375         talloc_set_destructor(randle, _mod_conn_free);
376
377         /*
378          *  Clear any previously configured options for the first request.
379          */
380         curl_easy_reset(candle);
381
382         return randle;
383
384         /*
385          *  Cleanup for error conditions.
386          */
387 error:
388         ERROR("rlm_rest (%s): Failed setting curl option %s: %s (%i)", inst->xlat_name, option,
389               curl_easy_strerror(ret), ret);
390
391         /*
392          *  So we don't leak CURL handles.
393          */
394 connection_error:
395         curl_easy_cleanup(candle);
396         if (randle) talloc_free(randle);
397
398         return NULL;
399 }
400
401 /** Verifies that the last TCP socket associated with a handle is still active.
402  *
403  * Quieries libcurl to try and determine if the TCP socket associated with a
404  * connection handle is still viable.
405  *
406  * @param[in] instance configuration data.
407  * @param[in] handle to check.
408  * @returns false if the last socket is dead, or if the socket state couldn't be
409  *      determined, else true.
410  */
411 int mod_conn_alive(void *instance, void *handle)
412 {
413         rlm_rest_t              *inst = instance;
414         rlm_rest_handle_t       *randle = handle;
415         CURL                    *candle = randle->handle;
416
417         long last_socket;
418         CURLcode ret;
419
420         ret = curl_easy_getinfo(candle, CURLINFO_LASTSOCKET, &last_socket);
421         if (ret != CURLE_OK) {
422                 ERROR("rlm_rest (%s): Couldn't determine socket state: %i - %s", inst->xlat_name, ret,
423                       curl_easy_strerror(ret));
424
425                 return false;
426         }
427
428         if (last_socket == -1) {
429                 return false;
430         }
431
432         return true;
433 }
434
435 /** Copies a pre-expanded xlat string to the output buffer
436  *
437  * @param[out] out Char buffer to write encoded data to.
438  * @param[in] size Multiply by nmemb to get the length of ptr.
439  * @param[in] nmemb Multiply by size to get the length of ptr.
440  * @param[in] userdata rlm_rest_request_t to keep encoding state between calls.
441  * @return length of data (including NULL) written to ptr, or 0 if no more
442  *      data to write.
443  */
444 static size_t rest_encode_custom(void *out, size_t size, size_t nmemb, void *userdata)
445 {
446         rlm_rest_request_t *ctx = userdata;
447         rest_custom_data_t *data = ctx->encoder;
448
449         size_t  freespace = (size * nmemb) - 1;
450         size_t  len;
451
452         len = strlcpy(out, data->p, freespace);
453         if (is_truncated(len, freespace)) {
454                 data->p += (freespace - 1);
455                 return freespace - 1;
456         }
457         data->p += len;
458
459         return len;
460 }
461
462 /** Encodes VALUE_PAIR linked list in POST format
463  *
464  * This is a stream function matching the rest_read_t prototype. Multiple
465  * successive calls will return additional encoded VALUE_PAIRs.
466  * Only complete attribute headers @verbatim '<name>=' @endverbatim and values
467  * will be written to the ptr buffer.
468  *
469  * POST request format is:
470  * @verbatim <attribute0>=<value0>&<attribute1>=<value1>&<attributeN>=<valueN>@endverbatim
471  *
472  * All attributes and values are url encoded. There is currently no support for
473  * nested attributes, or attribute qualifiers.
474  *
475  * Nested attributes may be added in the future using
476  * @verbatim <attribute-outer>:<attribute-inner>@endverbatim
477  * to denotate nesting.
478  *
479  * Requires libcurl for url encoding.
480  *
481  * @see rest_decode_post
482  *
483  * @param[out] out Char buffer to write encoded data to.
484  * @param[in] size Multiply by nmemb to get the length of ptr.
485  * @param[in] nmemb Multiply by size to get the length of ptr.
486  * @param[in] userdata rlm_rest_request_t to keep encoding state between calls.
487  * @return length of data (including NULL) written to ptr, or 0 if no more
488  *      data to write.
489  */
490 static size_t rest_encode_post(void *out, size_t size, size_t nmemb, void *userdata)
491 {
492         rlm_rest_request_t      *ctx = userdata;
493         REQUEST                 *request = ctx->request; /* Used by RDEBUG */
494         VALUE_PAIR              *vp;
495
496         char *p = out;          /* Position in buffer */
497         char *encoded = p;      /* Position in buffer of last fully encoded attribute or value */
498         char *escaped;          /* Pointer to current URL escaped data */
499
500         size_t len = 0;
501         size_t freespace = (size * nmemb) - 1;
502
503         /* Allow manual chunking */
504         if ((ctx->chunk) && (ctx->chunk <= freespace)) {
505                 freespace = (ctx->chunk - 1);
506         }
507
508         if (ctx->state == READ_STATE_END) return 0;
509
510         /* Post data requires no headers */
511         if (ctx->state == READ_STATE_INIT) ctx->state = READ_STATE_ATTR_BEGIN;
512
513         while (freespace > 0) {
514                 vp = fr_cursor_current(&ctx->cursor);
515                 if (!vp) {
516                         ctx->state = READ_STATE_END;
517
518                         break;
519                 }
520
521                 RDEBUG2("Encoding attribute \"%s\"", vp->da->name);
522
523                 if (ctx->state == READ_STATE_ATTR_BEGIN) {
524                         escaped = curl_escape(vp->da->name, strlen(vp->da->name));
525                         if (!escaped) {
526                                 REDEBUG("Failed escaping string \"%s\"", vp->da->name);
527                                 return 0;
528                         }
529
530                         len = strlen(escaped);
531                         if (freespace < (1 + len)) {
532                                 curl_free(escaped);
533                                 goto no_space;
534                         }
535
536                         len = sprintf(p, "%s=", escaped);
537                         curl_free(escaped);
538                         p += len;
539                         freespace -= len;
540
541                         /*
542                          *  We wrote the attribute header, record progress.
543                          */
544                         encoded = p;
545                         ctx->state = READ_STATE_ATTR_CONT;
546                 }
547
548                 /*
549                  *  Write out single attribute string.
550                  */
551                 len = vp_prints_value(p, freespace, vp, 0);
552                 if (is_truncated(len, freespace)) goto no_space;
553
554                 RDEBUG3("\tLength : %zd", len);
555                 if (len > 0) {
556                         escaped = curl_escape(p, len);
557                         if (!escaped) {
558                                 REDEBUG("Failed escaping string \"%s\"", vp->da->name);
559                                 return 0;
560                         }
561                         len = strlen(escaped);
562
563                         if (freespace < len) {
564                                 curl_free(escaped);
565                                 goto no_space;
566                         }
567
568                         len = strlcpy(p, escaped, len + 1);
569
570                         curl_free(escaped);
571
572                         RDEBUG3("\tValue  : %s", p);
573
574                         p += len;
575                         freespace -= len;
576                 }
577
578                 /*
579                  *  there are more attributes, insert a separator
580                  */
581                 if (fr_cursor_next(&ctx->cursor)) {
582                         if (freespace < 1) goto no_space;
583                         *p++ = '&';
584                         freespace--;
585                 }
586
587                 /*
588                  *  We wrote one full attribute value pair, record progress.
589                  */
590                 encoded = p;
591
592                 ctx->state = READ_STATE_ATTR_BEGIN;
593         }
594
595         *p = '\0';
596
597         len = p - (char *)out;
598
599         RDEBUG3("POST Data: %s", (char *)out);
600         RDEBUG3("Returning %zd bytes of POST data", len);
601
602         return len;
603
604         /*
605          *  Cleanup for error conditions
606          */
607 no_space:
608         *encoded = '\0';
609
610         len = encoded - (char *)out;
611
612         RDEBUG3("POST Data: %s", (char *)out);
613
614         /*
615          *  The buffer wasn't big enough to encode a single attribute chunk.
616          */
617         if (len == 0) {
618                 REDEBUG("Failed encoding attribute");
619         } else {
620                 RDEBUG3("Returning %zd bytes of POST data (buffer full or chunk exceeded)", len);
621         }
622
623         return len;
624 }
625
626 #ifdef HAVE_JSON
627 /** Encodes VALUE_PAIR linked list in JSON format
628  *
629  * This is a stream function matching the rest_read_t prototype. Multiple
630  * successive calls will return additional encoded VALUE_PAIRs.
631  *
632  * Only complete attribute headers
633  * @verbatim "<name>":{"type":"<type>","value":[' @endverbatim
634  * and complete attribute values will be written to ptr.
635  *
636  * If an attribute occurs multiple times in the request the attribute values
637  * will be concatenated into a single value array.
638  *
639  * JSON request format is:
640 @verbatim
641 {
642         "<attribute0>":{
643                 "type":"<type0>",
644                 "value":[<value0>,<value1>,<valueN>]
645         },
646         "<attribute1>":{
647                 "type":"<type1>",
648                 "value":[...]
649         },
650         "<attributeN>":{
651                 "type":"<typeN>",
652                 "value":[...]
653         },
654 }
655 @endverbatim
656  *
657  * @param[out] out Char buffer to write encoded data to.
658  * @param[in] size Multiply by nmemb to get the length of ptr.
659  * @param[in] nmemb Multiply by size to get the length of ptr.
660  * @param[in] userdata rlm_rest_request_t to keep encoding state between calls.
661  * @return length of data (including NULL) written to ptr, or 0 if no more
662  *      data to write.
663  */
664 static size_t rest_encode_json(void *out, size_t size, size_t nmemb, void *userdata)
665 {
666         rlm_rest_request_t      *ctx = userdata;
667         REQUEST                 *request = ctx->request; /* Used by RDEBUG */
668         VALUE_PAIR              *vp, *next;
669
670         char *p = out;          /* Position in buffer */
671         char *encoded = p;      /* Position in buffer of last fully encoded attribute or value */
672
673         char const *type;
674
675         size_t len = 0;
676         size_t freespace = (size * nmemb) - 1;
677
678         rad_assert(freespace > 0);
679
680         /* Allow manual chunking */
681         if ((ctx->chunk) && (ctx->chunk <= freespace)) {
682                 freespace = (ctx->chunk - 1);
683         }
684
685         if (ctx->state == READ_STATE_END) return 0;
686
687         if (ctx->state == READ_STATE_INIT) {
688                 ctx->state = READ_STATE_ATTR_BEGIN;
689
690                 if (freespace < 1) goto no_space;
691                 *p++ = '{';
692                 freespace--;
693         }
694
695         for (;;) {
696                 vp = fr_cursor_current(&ctx->cursor);
697
698                 /*
699                  *  We've encoded all the VPs
700                  */
701                 if (!vp) {
702                         ctx->state = READ_STATE_END;
703
704                         if (freespace < 1) goto no_space;
705                         *p++ = '}';
706                         freespace--;
707
708                         break;
709                 }
710
711                 /*
712                  *  New attribute, write name, type, and beginning of value array.
713                  */
714                 RDEBUG2("Encoding attribute \"%s\"", vp->da->name);
715                 if (ctx->state == READ_STATE_ATTR_BEGIN) {
716                         type = fr_int2str(dict_attr_types, vp->da->type, "<INVALID>");
717
718                         len = snprintf(p, freespace + 1, "\"%s\":{\"type\":\"%s\",\"value\":[", vp->da->name, type);
719                         if (len >= freespace) goto no_space;
720                         p += len;
721                         freespace -= len;
722
723                         RDEBUG3("\tType   : %s", type);
724
725                         /*
726                          *  We wrote the attribute header, record progress
727                          */
728                         encoded = p;
729                         ctx->state = READ_STATE_ATTR_CONT;
730                 }
731
732                 for (;;) {
733                         len = vp_prints_value_json(p, freespace, vp);
734                         if (is_truncated(len, freespace)) goto no_space;
735
736                         /*
737                          *  Show actual value length minus quotes
738                          */
739                         RDEBUG3("\tLength : %zu", (size_t) (*p == '"') ? (len - 2) : len);
740                         RDEBUG3("\tValue  : %s", p);
741
742                         p += len;
743                         freespace -= len;
744
745                         /*
746                          *  Multivalued attribute, we sorted all the attributes earlier, so multiple
747                          *  instances should occur in a contiguous block.
748                          */
749                         if ((next = fr_cursor_next(&ctx->cursor)) && (vp->da == next->da)) {
750                                 if (freespace < 1) goto no_space;
751                                 *p++ = ',';
752                                 freespace--;
753
754                                 /*
755                                  *  We wrote one attribute value, record progress.
756                                  */
757                                 encoded = p;
758                                 vp = next;
759                                 continue;
760                         }
761                         break;
762                 }
763
764                 if (freespace < 2) goto no_space;
765                 *p++ = ']';
766                 *p++ = '}';
767                 freespace -= 2;
768
769                 if (next) {
770                         if (freespace < 1) goto no_space;
771                         *p++ = ',';
772                         freespace--;
773                 }
774
775                 /*
776                  *  We wrote one full attribute value pair, record progress.
777                  */
778                 encoded = p;
779                 ctx->state = READ_STATE_ATTR_BEGIN;
780         }
781
782         *p = '\0';
783
784         len = p - (char *)out;
785
786         RDEBUG3("JSON Data: %s", (char *)out);
787         RDEBUG3("Returning %zd bytes of JSON data", len);
788
789         return len;
790
791         /*
792          *  Were out of buffer space
793          */
794 no_space:
795         *encoded = '\0';
796
797         len = encoded - (char *)out;
798
799         RDEBUG3("JSON Data: %s", (char *)out);
800
801         /*
802          *  The buffer wasn't big enough to encode a single attribute chunk.
803          */
804         if (len == 0) {
805                 REDEBUG("AVP exceeds buffer length or chunk");
806         } else {
807                 RDEBUG2("Returning %zd bytes of JSON data (buffer full or chunk exceeded)", len);
808         }
809
810         return len;
811 }
812 #endif
813
814 /** Emulates successive libcurl calls to an encoding function
815  *
816  * This function is used when the request will be sent to the HTTP server as one
817  * contiguous entity. A buffer of REST_BODY_INIT bytes is allocated and passed
818  * to the stream encoding function.
819  *
820  * If the stream function does not return 0, a new buffer is allocated which is
821  * the size of the previous buffer + REST_BODY_INIT bytes, the data from the
822  * previous buffer is copied, and freed, and another call is made to the stream
823  * function, passing a pointer into the new buffer at the end of the previously
824  * written data.
825  *
826  * This process continues until the stream function signals (by returning 0)
827  * that it has no more data to write.
828  *
829  * @param[out] buffer where the pointer to the alloced buffer should
830  *      be written.
831  * @param[in] func Stream function.
832  * @param[in] limit Maximum buffer size to alloc.
833  * @param[in] userdata rlm_rest_request_t to keep encoding state between calls to
834  *      stream function.
835  * @return the length of the data written to the buffer (excluding NULL) or -1
836  *      if alloc >= limit.
837  */
838 static ssize_t rest_request_encode_wrapper(char **buffer, rest_read_t func, size_t limit, void *userdata)
839 {
840         char *previous = NULL;
841         char *current = NULL;
842
843         size_t alloc = REST_BODY_INIT;  /* Size of buffer to alloc */
844         size_t used = 0;                /* Size of data written */
845         size_t len = 0;
846
847         while (alloc <= limit) {
848                 current = rad_malloc(alloc);
849
850                 if (previous) {
851                         strlcpy(current, previous, used + 1);
852                         free(previous);
853                 }
854
855                 len = func(current + used, alloc - used, 1, userdata);
856                 used += len;
857                 if (!len) {
858                         *buffer = current;
859                         return used;
860                 }
861
862                 alloc = alloc * 2;
863                 previous = current;
864         };
865
866         free(current);
867
868         return -1;
869 }
870
871 /** (Re-)Initialises the data in a rlm_rest_request_t.
872  *
873  * Resets the values of a rlm_rest_request_t to their defaults.
874  *
875  * @param[in] request Current request.
876  * @param[in] ctx to initialise.
877  * @param[in] sort If true VALUE_PAIRs will be sorted within the VALUE_PAIR
878  *      pointer array.
879  */
880 static void rest_request_init(REQUEST *request, rlm_rest_request_t *ctx, bool sort)
881 {
882         /*
883          *      Setup stream read data
884          */
885         ctx->request = request;
886         ctx->state = READ_STATE_INIT;
887
888         /*
889          *      Sorts pairs in place, oh well...
890          */
891         if (sort) {
892                 pairsort(&request->packet->vps, attrtagcmp);
893         }
894         fr_cursor_init(&ctx->cursor, &request->packet->vps);
895 }
896
897 /** Converts POST response into VALUE_PAIRs and adds them to the request
898  *
899  * Accepts VALUE_PAIRS in the same format as rest_encode_post, but with the
900  * addition of optional attribute list qualifiers as part of the attribute name
901  * string.
902  *
903  * If no qualifiers are specified, will default to the request list.
904  *
905  * POST response format is:
906  * @verbatim [outer.][<list>:]<attribute0>=<value0>&[outer.][<list>:]<attribute1>=<value1>&[outer.][<list>:]<attributeN>=<valueN> @endverbatim
907  *
908  * @see rest_encode_post
909  *
910  * @param[in] instance configuration data.
911  * @param[in] section configuration data.
912  * @param[in] handle rlm_rest_handle_t to use.
913  * @param[in] request Current request.
914  * @param[in] raw buffer containing POST data.
915  * @param[in] rawlen Length of data in raw buffer.
916  * @return the number of VALUE_PAIRs processed or -1 on unrecoverable error.
917  */
918 static int rest_decode_post(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
919                             REQUEST *request, void *handle, char *raw, UNUSED size_t rawlen)
920 {
921         rlm_rest_handle_t       *randle = handle;
922         CURL                    *candle = randle->handle;
923
924         char const *p = raw, *q;
925
926         char const *attribute;
927         char *name  = NULL;
928         char *value = NULL;
929
930         char *expanded = NULL;
931
932         DICT_ATTR const *da;
933         VALUE_PAIR *vp;
934
935         pair_lists_t list_name;
936         request_refs_t request_name;
937         REQUEST *reference = request;
938         VALUE_PAIR **vps;
939         TALLOC_CTX *ctx;
940
941         size_t len;
942         int curl_len; /* Length from last curl_easy_unescape call */
943
944         int count = 0;
945         int ret;
946
947         /*
948          *      Empty response?
949          */
950         while (isspace(*p)) p++;
951         if (*p == '\0') return 0;
952
953         while (((q = strchr(p, '=')) != NULL) && (count < REST_BODY_MAX_ATTRS)) {
954                 reference = request;
955
956                 name = curl_easy_unescape(candle, p, (q - p), &curl_len);
957                 p = (q + 1);
958
959                 RDEBUG2("Parsing attribute \"%s\"", name);
960
961                 /*
962                  *  The attribute pointer is updated to point to the portion of
963                  *  the string after the list qualifier.
964                  */
965                 attribute = name;
966                 request_name = radius_request_name(&attribute, REQUEST_CURRENT);
967                 if (request_name == REQUEST_UNKNOWN) {
968                         RWDEBUG("Invalid request qualifier, skipping");
969
970                         curl_free(name);
971
972                         continue;
973                 }
974
975                 if (radius_request(&reference, request_name) < 0) {
976                         RWDEBUG("Attribute name refers to outer request but not in a tunnel, skipping");
977
978                         curl_free(name);
979
980                         continue;
981                 }
982
983                 list_name = radius_list_name(&attribute, PAIR_LIST_REPLY);
984                 if (list_name == PAIR_LIST_UNKNOWN) {
985                         RWDEBUG("Invalid list qualifier, skipping");
986
987                         curl_free(name);
988
989                         continue;
990                 }
991
992                 da = dict_attrbyname(attribute);
993                 if (!da) {
994                         RWDEBUG("Attribute \"%s\" unknown, skipping", attribute);
995
996                         curl_free(name);
997
998                         continue;
999                 }
1000
1001                 vps = radius_list(reference, list_name);
1002                 rad_assert(vps);
1003
1004                 RDEBUG3("\tType  : %s", fr_int2str(dict_attr_types, da->type, "<INVALID>"));
1005
1006                 ctx = radius_list_ctx(reference, list_name);
1007
1008                 q = strchr(p, '&');
1009                 len = (!q) ? (rawlen - (p - raw)) : (unsigned)(q - p);
1010
1011                 value = curl_easy_unescape(candle, p, len, &curl_len);
1012
1013                 /*
1014                  *  If we found a delimiter we want to skip over it,
1015                  *  if we didn't we do *NOT* want to skip over the end
1016                  *  of the buffer...
1017                  */
1018                 p += (!q) ? len : (len + 1);
1019
1020                 RDEBUG3("\tLength : %i", curl_len);
1021                 RDEBUG3("\tValue  : \"%s\"", value);
1022
1023                 RDEBUG2("Performing xlat expansion of response value");
1024
1025                 if (radius_axlat(&expanded, request, value, NULL, NULL) < 0) {
1026                         goto skip;
1027                 }
1028
1029                 vp = pairalloc(ctx, da);
1030                 if (!vp) {
1031                         REDEBUG("Failed creating valuepair");
1032                         talloc_free(expanded);
1033
1034                         goto error;
1035                 }
1036
1037                 ret = pairparsevalue(vp, expanded, 0);
1038                 TALLOC_FREE(expanded);
1039                 if (ret < 0) {
1040                         RWDEBUG("Incompatible value assignment, skipping");
1041                         talloc_free(vp);
1042                         goto skip;
1043                 }
1044
1045                 pairadd(vps, vp);
1046
1047                 count++;
1048
1049         skip:
1050                 curl_free(name);
1051                 curl_free(value);
1052
1053                 continue;
1054
1055         error:
1056                 curl_free(name);
1057                 curl_free(value);
1058
1059                 return count;
1060         }
1061
1062         if (!count) {
1063                 REDEBUG("Malformed POST data \"%s\"", raw);
1064         }
1065
1066         return count;
1067
1068 }
1069
1070 #ifdef HAVE_JSON
1071 /** Converts JSON "value" key into VALUE_PAIR.
1072  *
1073  * If leaf is not in fact a leaf node, but contains JSON data, the data will
1074  * written to the attribute in JSON string format.
1075  *
1076  * @param[in] instance configuration data.
1077  * @param[in] section configuration data.
1078  * @param[in] ctx to allocate new VALUE_PAIRs in.
1079  * @param[in] request Current request.
1080  * @param[in] da Attribute to create.
1081  * @param[in] flags containing the operator other flags controlling value
1082  *      expansion.
1083  * @param[in] leaf object containing the VALUE_PAIR value.
1084  * @return The VALUE_PAIR just created, or NULL on error.
1085  */
1086 static VALUE_PAIR *json_pairmake_leaf(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
1087                                       TALLOC_CTX *ctx, REQUEST *request, DICT_ATTR const *da,
1088                                       json_flags_t *flags, json_object *leaf)
1089 {
1090         char const *value, *to_parse;
1091         char *expanded = NULL;
1092         int ret;
1093
1094         VALUE_PAIR *vp;
1095
1096         /*
1097          *      Should encode any nested JSON structures into JSON strings.
1098          *
1099          *      "I knew you liked JSON so I put JSON in your JSON!"
1100          */
1101         value = json_object_get_string(leaf);
1102
1103         RDEBUG3("\tType   : %s", fr_int2str(dict_attr_types, da->type, "<INVALID>"));
1104         RDEBUG3("\tLength : %zu", strlen(value));
1105         RDEBUG3("\tValue  : \"%s\"", value);
1106
1107         if (flags->do_xlat) {
1108                 if (radius_axlat(&expanded, request, value, NULL, NULL) < 0) {
1109                         return NULL;
1110                 }
1111
1112                 to_parse = expanded;
1113         } else {
1114                 to_parse = value;
1115         }
1116
1117         vp = pairalloc(ctx, da);
1118         if (!vp) {
1119                 RWDEBUG("Failed creating valuepair, skipping...");
1120                 talloc_free(expanded);
1121
1122                 return NULL;
1123         }
1124
1125         vp->op = flags->op;
1126
1127         ret = pairparsevalue(vp, to_parse, 0);
1128         talloc_free(expanded);
1129         if (ret < 0) {
1130                 RWDEBUG("Incompatible value assignment, skipping...");
1131                 talloc_free(vp);
1132
1133                 return NULL;
1134         }
1135
1136         return vp;
1137 }
1138
1139 /** Processes JSON response and converts it into multiple VALUE_PAIRs
1140  *
1141  * Processes JSON attribute declarations in the format below. Will recurse when
1142  * processing nested attributes. When processing nested attributes flags and
1143  * operators from previous attributes are not inherited.
1144  *
1145  * JSON response format is:
1146 @verbatim
1147 {
1148         "<attribute0>":{
1149                 do_xlat:<bool>,
1150                 is_json:<bool>,
1151                 "op":"<operator>",
1152                 "value":[<value0>,<value1>,<valueN>]
1153         },
1154         "<attribute1>":{
1155                 "value":{
1156                         "<nested-attribute0>":{
1157                                 "op":"<operator>",
1158                                 "value":<value0>
1159                         }
1160                 }
1161         },
1162         "<attribute2>":"<value0>",
1163         "<attributeN>":"[<value0>,<value1>,<valueN>]"
1164 }
1165 @endverbatim
1166  *
1167  * JSON valuepair flags (bools):
1168  *  - do_xlat   (optional) Controls xlat expansion of values. Defaults to true.
1169  *  - is_json   (optional) If true, any nested JSON data will be copied to the
1170  *                         VALUE_PAIR in string form. Defaults to true.
1171  *  - op        (optional) Controls how the attribute is inserted into
1172  *                         the target list. Defaults to ':=' (T_OP_SET).
1173  *
1174  * If "op" is ':=' or '=', it will be automagically changed to '+=' for the
1175  * second and subsequent values in multivalued attributes. This does not work
1176  * between multiple attribute declarations.
1177  *
1178  * @see fr_tokens
1179  *
1180  * @param[in] instance configuration data.
1181  * @param[in] section configuration data.
1182  * @param[in] request Current request.
1183  * @param[in] object containing root node, or parent node.
1184  * @param[in] level Current nesting level.
1185  * @param[in] max counter, decremented after each VALUE_PAIR is created,
1186  *            when 0 no more attributes will be processed.
1187  * @return number of attributes created or < 0 on error.
1188  */
1189 static int json_pairmake(rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
1190                          REQUEST *request, json_object *object, UNUSED int level, int max)
1191 {
1192         struct lh_entry *entry;
1193         int max_attrs = max;
1194
1195         if (!json_object_is_type(object, json_type_object)) {
1196                 REDEBUG("Can't process VP container, expected JSON object"
1197 #ifdef HAVE_JSON_TYPE_TO_NAME
1198                         "got \"%s\", skipping...",
1199                         json_type_to_name(json_object_get_type(object)));
1200 #else
1201                         ", skipping...");
1202 #endif
1203                 return -1;
1204         }
1205
1206         /*
1207          *      Process VP container
1208          */
1209         for (entry = json_object_get_object(object)->head;
1210              entry;
1211              entry = entry->next) {
1212                 int i = 0, elements;
1213                 struct json_object *value, *element, *tmp;
1214                 TALLOC_CTX *ctx;
1215
1216                 char const *name = (char const *)entry->k;
1217
1218                 json_flags_t flags = {
1219                         .op = T_OP_SET,
1220                         .do_xlat = 1,
1221                         .is_json = 0
1222                 };
1223
1224                 value_pair_tmpl_t dst;
1225                 REQUEST *current = request;
1226                 VALUE_PAIR **vps, *vp = NULL;
1227
1228                 memset(&dst, 0, sizeof(dst));
1229
1230                 /* Fix the compiler warnings regarding const... */
1231                 memcpy(&value, &entry->v, sizeof(value));
1232
1233                 /*
1234                  *  Resolve attribute name to a dictionary entry and pairlist.
1235                  */
1236                 RDEBUG2("Parsing attribute \"%s\"", name);
1237
1238                 if (radius_parse_attr(&dst, name, REQUEST_CURRENT, PAIR_LIST_REPLY) < 0) {
1239                         RWDEBUG("Failed parsing attribute: %s, skipping...", fr_strerror());
1240                         continue;
1241                 }
1242
1243                 if (radius_request(&current, dst.vpt_request) < 0) {
1244                         RWDEBUG("Attribute name refers to outer request but not in a tunnel, skipping...");
1245                         continue;
1246                 }
1247
1248                 vps = radius_list(current, dst.vpt_list);
1249                 if (!vps) {
1250                         RWDEBUG("List not valid in this context, skipping...");
1251                         continue;
1252                 }
1253                 ctx = radius_list_ctx(current, dst.vpt_list);
1254
1255                 /*
1256                  *  Alternative JSON structure which allows operator,
1257                  *  and other flags to be specified.
1258                  *
1259                  *      "<name>":{
1260                  *              "do_xlat":<bool>,
1261                  *              "is_json":<bool>,
1262                  *              "op":"<op>",
1263                  *              "value":<value>
1264                  *      }
1265                  *
1266                  *      Where value is a:
1267                  *        - []  Multivalued array
1268                  *        - {}  Nested Valuepair
1269                  *        - *   Integer or string value
1270                  */
1271                 if (json_object_is_type(value, json_type_object)) {
1272                         /*
1273                          *  Process operator if present.
1274                          */
1275                         tmp = json_object_object_get(value, "op");
1276                         if (tmp) {
1277                                 flags.op = fr_str2int(fr_tokens, json_object_get_string(tmp), 0);
1278                                 if (!flags.op) {
1279                                         RWDEBUG("Invalid operator value \"%s\", skipping...",
1280                                                 json_object_get_string(tmp));
1281                                         continue;
1282                                 }
1283                         }
1284
1285                         /*
1286                          *  Process optional do_xlat bool.
1287                          */
1288                         tmp = json_object_object_get(value, "do_xlat");
1289                         if (tmp) {
1290                                 flags.do_xlat = json_object_get_boolean(tmp);
1291                         }
1292
1293                         /*
1294                          *  Process optional is_json bool.
1295                          */
1296                         tmp = json_object_object_get(value, "is_json");
1297                         if (tmp) {
1298                                 flags.is_json = json_object_get_boolean(tmp);
1299                         }
1300
1301                         /*
1302                          *  Value key must be present if were using the expanded syntax.
1303                          */
1304                         value = json_object_object_get(value, "value");
1305                         if (!value) {
1306                                 RWDEBUG("Value key missing, skipping...");
1307                                 continue;
1308                         }
1309                 }
1310
1311                 /*
1312                  *  Setup pairmake / recursion loop.
1313                  */
1314                 if (!flags.is_json && json_object_is_type(value, json_type_array)) {
1315                         elements = json_object_array_length(value);
1316                         if (!elements) {
1317                                 RWDEBUG("Zero length value array, skipping...");
1318                                 continue;
1319                         }
1320                         element = json_object_array_get_idx(value, 0);
1321                 } else {
1322                         elements = 1;
1323                         element = value;
1324                 }
1325
1326                 /*
1327                  *  A JSON 'value' key, may have multiple elements, iterate
1328                  *  over each of them, creating a new VALUE_PAIR.
1329                  */
1330                 do {
1331                         if (max_attrs-- <= 0) {
1332                                 RWDEBUG("At maximum attribute limit");
1333                                 return max;
1334                         }
1335
1336                         /*
1337                          *  Automagically switch the op for multivalued attributes.
1338                          */
1339                         if (((flags.op == T_OP_SET) || (flags.op == T_OP_EQ)) && (i >= 1)) {
1340                                 flags.op = T_OP_ADD;
1341                         }
1342
1343                         if (json_object_is_type(element, json_type_object) && !flags.is_json) {
1344                                 /* TODO: Insert nested VP into VP structure...*/
1345                                 RWDEBUG("Found nested VP, these are not yet supported, skipping...");
1346
1347                                 continue;
1348
1349                                 /*
1350                                 vp = json_pairmake(instance, section,
1351                                                    request, value,
1352                                                    level + 1, max_attrs);*/
1353                         } else {
1354                                 vp = json_pairmake_leaf(instance, section, ctx, request,
1355                                                         dst.vpt_da, &flags, element);
1356                                 if (!vp) continue;
1357                         }
1358                         debug_pair(vp);
1359                         radius_pairmove(current, vps, vp, false);
1360                 /*
1361                  *  If we call json_object_array_get_idx on something that's not an array
1362                  *  the behaviour appears to be to occasionally segfault.
1363                  */
1364                 } while ((++i < elements) && (element = json_object_array_get_idx(value, i)));
1365         }
1366
1367         return max - max_attrs;
1368 }
1369
1370 /** Converts JSON response into VALUE_PAIRs and adds them to the request.
1371  *
1372  * Converts the raw JSON string into a json-c object tree and passes it to
1373  * json_pairmake. After the tree has been parsed json_object_put is called
1374  * which decrements the reference count of the root node by one, and frees
1375  * the entire tree.
1376  *
1377  * @see rest_encode_json
1378  * @see json_pairmake
1379  *
1380  * @param[in] instance configuration data.
1381  * @param[in] section configuration data.
1382  * @param[in,out] request Current request.
1383  * @param[in] handle REST handle.
1384  * @param[in] raw buffer containing JSON data.
1385  * @param[in] rawlen Length of data in raw buffer.
1386  * @return the number of VALUE_PAIRs processed or -1 on unrecoverable error.
1387  */
1388 static int rest_decode_json(rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
1389                             REQUEST *request, UNUSED void *handle, char *raw, UNUSED size_t rawlen)
1390 {
1391         char const *p = raw;
1392
1393         struct json_object *json;
1394
1395         int ret;
1396
1397         /*
1398          *  Empty response?
1399          */
1400         while (isspace(*p)) p++;
1401         if (*p == '\0') return 0;
1402
1403         json = json_tokener_parse(p);
1404         if (!json) {
1405                 REDEBUG("Malformed JSON data \"%s\"", raw);
1406                 return -1;
1407         }
1408
1409         ret = json_pairmake(instance, section, request, json, 0, REST_BODY_MAX_ATTRS);
1410
1411         /*
1412          *  Decrement reference count for root object, should free entire JSON tree.
1413          */
1414         json_object_put(json);
1415
1416         return ret;
1417 }
1418 #endif
1419
1420 /** Processes incoming HTTP header data from libcurl.
1421  *
1422  * Processes the status line, and Content-Type headers from the incoming HTTP
1423  * response.
1424  *
1425  * Matches prototype for CURLOPT_HEADERFUNCTION, and will be called directly
1426  * by libcurl.
1427  *
1428  * @param[in] in Char buffer where inbound header data is written.
1429  * @param[in] size Multiply by nmemb to get the length of ptr.
1430  * @param[in] nmemb Multiply by size to get the length of ptr.
1431  * @param[in] userdata rlm_rest_response_t to keep parsing state between calls.
1432  * @return Length of data processed, or 0 on error.
1433  */
1434 static size_t rest_response_header(void *in, size_t size, size_t nmemb, void *userdata)
1435 {
1436         rlm_rest_response_t *ctx = userdata;
1437         REQUEST *request = ctx->request; /* Used by RDEBUG */
1438
1439         char const *p = in, *q;
1440
1441         size_t const t = (size * nmemb);
1442         size_t s = t;
1443         size_t len;
1444
1445         http_body_type_t type;
1446
1447         /*
1448          *  Curl seems to throw these (\r\n) in before the next set of headers when
1449          *  looks like it's just a body separator and safe to ignore after we
1450          *  receive a 100 Continue.
1451          */
1452         if (t == 2 && ((p[0] == '\r') && (p[1] == '\n'))) return t;
1453
1454         switch (ctx->state) {
1455         case WRITE_STATE_INIT:
1456                 RDEBUG2("Processing response header");
1457
1458                 /*
1459                  *  HTTP/<version> <reason_code>[ <reason_phrase>]\r\n
1460                  *
1461                  *  "HTTP/1.1 " (8) + "100 " (4) + "\r\n" (2) = 14
1462                  */
1463                 if (s < 14) {
1464                         REDEBUG("Malformed HTTP header: Status line too short");
1465                         goto malformed;
1466                 }
1467                 /*
1468                  *  Check start of header matches...
1469                  */
1470                 if (strncasecmp("HTTP/", p, 5) != 0) {
1471                         REDEBUG("Malformed HTTP header: Missing HTTP version");
1472                         goto malformed;
1473                 }
1474                 p += 5;
1475                 s -= 5;
1476
1477                 /*
1478                  *  Skip the version field, next space should mark start of reason_code.
1479                  */
1480                 q = memchr(p, ' ', s);
1481                 if (!q) {
1482                         RDEBUG("Malformed HTTP header: Missing reason code");
1483                         goto malformed;
1484                 }
1485
1486                 s -= (q - p);
1487                 p  = q;
1488
1489                 /*
1490                  *  Process reason_code.
1491                  *
1492                  *  " 100" (4) + "\r\n" (2) = 6
1493                  */
1494                 if (s < 6) {
1495                         REDEBUG("Malformed HTTP header: Reason code too short");
1496                         goto malformed;
1497                 }
1498                 p++;
1499                 s--;
1500
1501                 /*  Char after reason code must be a space, or \r */
1502                 if (!((p[3] == ' ') || (p[3] == '\r'))) goto malformed;
1503
1504                 ctx->code = atoi(p);
1505
1506                 /*
1507                  *  Process reason_phrase (if present).
1508                  */
1509                 if (p[3] == ' ') {
1510                         p += 4;
1511                         s -= 4;
1512
1513                         q = memchr(p, '\r', s);
1514                         if (!q) goto malformed;
1515
1516                         len = (q - p);
1517
1518                         RDEBUG2("\tStatus : %i (%.*s)", ctx->code, (int) len, p);
1519                 } else {
1520                         RDEBUG2("\tStatus : %i", ctx->code);
1521                 }
1522
1523                 ctx->state = WRITE_STATE_PARSE_HEADERS;
1524
1525                 break;
1526
1527         case WRITE_STATE_PARSE_HEADERS:
1528                 if ((s >= 14) &&
1529                     (strncasecmp("Content-Type: ", p, 14) == 0)) {
1530                         p += 14;
1531                         s -= 14;
1532
1533                         /*
1534                          *  Check to see if there's a parameter separator.
1535                          */
1536                         q = memchr(p, ';', s);
1537
1538                         /*
1539                          *  If there's not, find the end of this header.
1540                          */
1541                         if (!q) q = memchr(p, '\r', s);
1542
1543                         len = !q ? s : (size_t) (q - p);
1544                         type = fr_substr2int(http_content_type_table, p, HTTP_BODY_UNKNOWN, len);
1545
1546
1547                         RDEBUG2("\tType   : %s (%.*s)", fr_int2str(http_body_type_table, type, "<INVALID>"),
1548                                 (int) len, p);
1549
1550                         /*
1551                          *  Figure out if the type is supported by one of the decoders.
1552                          */
1553                         if (ctx->force_to != HTTP_BODY_UNKNOWN) {
1554                                 if (ctx->force_to != ctx->type) {
1555                                         RDEBUG3("Forcing body type to \"%s\"",
1556                                                 fr_int2str(http_body_type_table, ctx->force_to, "<INVALID>"));
1557                                         ctx->type = ctx->force_to;
1558                                 }
1559                         /*
1560                          *  Assume the force_to value has already been validated.
1561                          */
1562                         } else switch (http_body_type_supported[ctx->type]) {
1563                         case HTTP_BODY_UNKNOWN:
1564                                 RWDEBUG("Couldn't determine type, using the request's type \"%s\".",
1565                                         fr_int2str(http_body_type_table, ctx->type, "<INVALID>"));
1566                                 break;
1567
1568                         case HTTP_BODY_UNSUPPORTED:
1569                                 REDEBUG("Type \"%s\" is currently unsupported",
1570                                         fr_int2str(http_body_type_table, ctx->type, "<INVALID>"));
1571                                 ctx->type = HTTP_BODY_UNSUPPORTED;
1572                                 break;
1573
1574                         case HTTP_BODY_UNAVAILABLE:
1575                                 REDEBUG("Type \"%s\" is unavailable, please rebuild this module with the required "
1576                                         "library", fr_int2str(http_body_type_table, ctx->type, "<INVALID>"));
1577                                 ctx->type = HTTP_BODY_UNAVAILABLE;
1578                                 break;
1579
1580                         case HTTP_BODY_INVALID:
1581                                 REDEBUG("Type \"%s\" is not a valid web API data markup format",
1582                                         fr_int2str(http_body_type_table, ctx->type, "<INVALID>"));
1583                                 ctx->type = HTTP_BODY_INVALID;
1584                                 break;
1585
1586                         /* supported type */
1587                         default:
1588                                 ctx->type = type;
1589                                 break;
1590                         }
1591                 }
1592                 break;
1593
1594         default:
1595                 break;
1596         }
1597
1598         /*
1599          *  If we got a 100 Continue, we need to send additional payload data.
1600          *  reset the state to WRITE_STATE_INIT, so that when were called again
1601          *  we overwrite previous header data with that from the proper header.
1602          */
1603         if (ctx->code == 100) {
1604                 RDEBUG2("Continuing...");
1605                 ctx->state = WRITE_STATE_INIT;
1606         }
1607
1608         return t;
1609
1610 malformed:
1611         {
1612                 char escaped[1024];
1613
1614                 fr_print_string((char *) in, t, escaped, sizeof(escaped));
1615
1616                 REDEBUG("Received %zu bytes of response data: %s", t, escaped);
1617                 ctx->code = -1;
1618         }
1619
1620         return (t - s);
1621 }
1622
1623 /** Processes incoming HTTP body data from libcurl.
1624  *
1625  * Writes incoming body data to an intermediary buffer for later parsing by
1626  * one of the decode functions.
1627  *
1628  * @param[in] ptr Char buffer where inbound header data is written
1629  * @param[in] size Multiply by nmemb to get the length of ptr.
1630  * @param[in] nmemb Multiply by size to get the length of ptr.
1631  * @param[in] userdata rlm_rest_response_t to keep parsing state between calls.
1632  * @return length of data processed, or 0 on error.
1633  */
1634 static size_t rest_response_body(void *ptr, size_t size, size_t nmemb, void *userdata)
1635 {
1636         rlm_rest_response_t *ctx = userdata;
1637         REQUEST *request = ctx->request; /* Used by RDEBUG */
1638
1639         char const *p = ptr, *q;
1640         char *tmp;
1641
1642         size_t const t = (size * nmemb);
1643
1644         if (t == 0) return 0;
1645
1646         /*
1647          *  Any post processing of headers should go here...
1648          */
1649         if (ctx->state == WRITE_STATE_PARSE_HEADERS) {
1650                 ctx->state = WRITE_STATE_PARSE_CONTENT;
1651         }
1652
1653         switch (ctx->type) {
1654         case HTTP_BODY_UNSUPPORTED:
1655         case HTTP_BODY_UNAVAILABLE:
1656         case HTTP_BODY_INVALID:
1657                 while ((q = memchr(p, '\n', t - (p - (char *)ptr)))) {
1658                         REDEBUG("%.*s", (int) (q - p), p);
1659                         p = q + 1;
1660                 }
1661
1662                 if (*p != '\0') {
1663                         REDEBUG("%.*s", (int)(t - (p - (char *)ptr)), p);
1664                 }
1665
1666                 return t;
1667
1668         case HTTP_BODY_NONE:
1669                 while ((q = memchr(p, '\n', t - (p - (char *)ptr)))) {
1670                         RDEBUG3("%.*s", (int) (q - p), p);
1671                         p = q + 1;
1672                 }
1673
1674                 if (*p != '\0') {
1675                         RDEBUG3("%.*s", (int)(t - (p - (char *)ptr)), p);
1676                 }
1677
1678                 return t;
1679
1680         default:
1681                 if (t > (ctx->alloc - ctx->used)) {
1682                         ctx->alloc += ((t + 1) > REST_BODY_INIT) ? t + 1 : REST_BODY_INIT;
1683
1684                         tmp = ctx->buffer;
1685
1686                         ctx->buffer = rad_malloc(ctx->alloc);
1687
1688                         /* If data has been written previously */
1689                         if (tmp) {
1690                                 strlcpy(ctx->buffer, tmp, (ctx->used + 1));
1691                                 free(tmp);
1692                         }
1693                 }
1694                 strlcpy(ctx->buffer + ctx->used, p, t + 1);
1695                 ctx->used += t;
1696
1697                 break;
1698         }
1699
1700         return t;
1701 }
1702
1703 /** (Re-)Initialises the data in a rlm_rest_response_t.
1704  *
1705  * This resets the values of the a rlm_rest_response_t to their defaults.
1706  * Must be called between encoding sessions.
1707  *
1708  * @see rest_response_body
1709  * @see rest_response_header
1710  *
1711  * @param[in] request Current request.
1712  * @param[in] ctx data to initialise.
1713  * @param[in] type Default http_body_type to use when decoding raw data, may be
1714  * overwritten by rest_response_header.
1715  */
1716 static void rest_response_init(REQUEST *request, rlm_rest_response_t *ctx, http_body_type_t type)
1717 {
1718         ctx->request = request;
1719         ctx->type = type;
1720         ctx->state = WRITE_STATE_INIT;
1721         ctx->alloc = 0;
1722         ctx->used = 0;
1723         ctx->buffer = NULL;
1724 }
1725
1726 /** Extracts pointer to buffer containing response data
1727  *
1728  * @param[out] out Where to write the pointer to the buffer.
1729  * @param[in] handle used for the last request.
1730  * @return > 0 if data is available.
1731  */
1732 size_t rest_get_handle_data(char const **out, rlm_rest_handle_t *handle)
1733 {
1734         rlm_rest_curl_context_t *ctx = handle->ctx;
1735
1736         rad_assert(ctx->response.buffer || (!ctx->response.buffer && !ctx->response.used));
1737
1738         *out = ctx->response.buffer;
1739         return ctx->response.used;
1740 }
1741
1742 /** Configures body specific curlopts.
1743  *
1744  * Configures libcurl handle to use either chunked mode, where the request
1745  * data will be sent using multiple HTTP requests, or contiguous mode where
1746  * the request data will be sent in a single HTTP request.
1747  *
1748  * @param[in] instance configuration data.
1749  * @param[in] section configuration data.
1750  * @param[in] request Current request.
1751  * @param[in] handle rlm_rest_handle_t to configure.
1752  * @param[in] func to pass to libcurl for chunked.
1753  *            transfers (NULL if not using chunked mode).
1754  * @return 0 on success -1 on error.
1755  */
1756 static int rest_request_config_body(UNUSED rlm_rest_t *instance, rlm_rest_section_t *section,
1757                                     REQUEST *request, rlm_rest_handle_t *handle, rest_read_t func)
1758 {
1759         rlm_rest_curl_context_t *ctx = handle->ctx;
1760         CURL                    *candle = handle->handle;
1761
1762         CURLcode ret = CURLE_OK;
1763         char const *option = "unknown";
1764
1765         ssize_t len;
1766
1767         /*
1768          *  We were provided with no read function, assume this means
1769          *  no body should be sent.
1770          */
1771         if (!func) {
1772                 SET_OPTION(CURLOPT_POSTFIELDSIZE, 0);
1773                 return 0;
1774         }
1775
1776         /*
1777          *  Chunked transfer encoding means the body will be sent in
1778          *  multiple parts.
1779          */
1780         if (section->chunk > 0) {
1781                 SET_OPTION(CURLOPT_READDATA, &ctx->request);
1782                 SET_OPTION(CURLOPT_READFUNCTION, func);
1783
1784                 return 0;
1785         }
1786
1787         /*
1788          *  If were not doing chunked encoding then we read the entire
1789          *  body into a buffer, and send it in one go.
1790          */
1791         len = rest_request_encode_wrapper(&ctx->body, func, REST_BODY_MAX_LEN, &ctx->request);
1792         if (len <= 0) {
1793                 REDEBUG("Failed creating HTTP body content");
1794                 return -1;
1795         }
1796
1797         SET_OPTION(CURLOPT_POSTFIELDS, ctx->body);
1798         SET_OPTION(CURLOPT_POSTFIELDSIZE, len);
1799
1800         return 0;
1801
1802 error:
1803         REDEBUG("Failed setting curl option %s: %s (%i)", option, curl_easy_strerror(ret), ret);
1804
1805         return -1;
1806 }
1807
1808 /** Configures request curlopts.
1809  *
1810  * Configures libcurl handle setting various curlopts for things like local
1811  * client time, Content-Type, and other FreeRADIUS custom headers.
1812  *
1813  * Current FreeRADIUS custom headers are:
1814  *  - X-FreeRADIUS-Section      The module section being processed.
1815  *  - X-FreeRADIUS-Server       The current virtual server the REQUEST is
1816  *                              passing through.
1817  *
1818  * Sets up callbacks for all response processing (buffers and body data).
1819  *
1820  * @param[in] instance configuration data.
1821  * @param[in] section configuration data.
1822  * @param[in] handle to configure.
1823  * @param[in] request Current request.
1824  * @param[in] method to use (HTTP verbs PUT, POST, DELETE etc...).
1825  * @param[in] type Content-Type for request encoding, also sets the default for decoding.
1826  * @param[in] username to use for HTTP authentication, may be NULL in which case configured defaults will be used.
1827  * @param[in] password to use for HTTP authentication, may be NULL in which case configured defaults will be used.
1828  * @param[in] uri buffer containing the expanded URI to send the request to.
1829  * @return 0 on success (all opts configured) -1 on error.
1830  */
1831 int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section,
1832                         REQUEST *request, void *handle, http_method_t method,
1833                         http_body_type_t type,
1834                         char const *uri, char const *username, char const *password)
1835 {
1836         rlm_rest_handle_t       *randle = handle;
1837         rlm_rest_curl_context_t *ctx = randle->ctx;
1838         CURL                    *candle = randle->handle;
1839
1840         http_auth_type_t        auth = section->auth;
1841
1842         CURLcode        ret = CURLE_OK;
1843         char const      *option = "unknown";
1844         char const      *content_type;
1845         long            val = 1;
1846
1847         VALUE_PAIR      *header;
1848         vp_cursor_t     headers;
1849
1850         char buffer[512];
1851
1852         rad_assert(candle);
1853         rad_assert((!username && !password) || (username && password));
1854
1855         buffer[(sizeof(buffer) - 1)] = '\0';
1856
1857         /*
1858          *      Setup any header options and generic headers.
1859          */
1860         SET_OPTION(CURLOPT_URL, uri);
1861         SET_OPTION(CURLOPT_USERAGENT, "FreeRADIUS " RADIUSD_VERSION_STRING);
1862
1863         content_type = fr_int2str(http_content_type_table, type, section->body_str);
1864         snprintf(buffer, sizeof(buffer), "Content-Type: %s", content_type);
1865         ctx->headers = curl_slist_append(ctx->headers, buffer);
1866         if (!ctx->headers) goto error_header;
1867
1868         if (section->timeout) {
1869                 SET_OPTION(CURLOPT_TIMEOUT, section->timeout);
1870         }
1871
1872         SET_OPTION(CURLOPT_PROTOCOLS, (CURLPROTO_HTTP | CURLPROTO_HTTPS));
1873
1874         /*
1875          *      FreeRADIUS custom headers
1876          */
1877         RDEBUG3("Adding custom headers:");
1878         snprintf(buffer, sizeof(buffer), "X-FreeRADIUS-Section: %s", section->name);
1879         RDEBUG3("\t%s", buffer);
1880         ctx->headers = curl_slist_append(ctx->headers, buffer);
1881         if (!ctx->headers) goto error_header;
1882
1883         snprintf(buffer, sizeof(buffer), "X-FreeRADIUS-Server: %s", request->server);
1884         RDEBUG3("\t%s", buffer);
1885         ctx->headers = curl_slist_append(ctx->headers, buffer);
1886         if (!ctx->headers) goto error_header;
1887
1888         fr_cursor_init(&headers, &request->config_items);
1889         while (fr_cursor_next_by_num(&headers, PW_REST_HTTP_HEADER, 0, TAG_ANY)) {
1890                 header = fr_cursor_remove(&headers);
1891                 if (!strchr(header->vp_strvalue, ':')) {
1892                         RWDEBUG("Invalid HTTP header \"%s\" must be in format '<attribute>: <value>'.  Skipping...",
1893                                 header->vp_strvalue);
1894                         talloc_free(header);
1895                         continue;
1896                 }
1897                 RDEBUG3("\t%s", header->vp_strvalue);
1898                 ctx->headers = curl_slist_append(ctx->headers, header->vp_strvalue);
1899                 talloc_free(header);
1900         }
1901
1902         /*
1903          *      Configure HTTP verb (GET, POST, PUT, DELETE, other...)
1904          */
1905         switch (method) {
1906         case HTTP_METHOD_GET :
1907                 SET_OPTION(CURLOPT_HTTPGET, val);
1908                 break;
1909
1910         case HTTP_METHOD_POST :
1911                 SET_OPTION(CURLOPT_POST, val);
1912                 break;
1913
1914         case HTTP_METHOD_PUT :
1915                 SET_OPTION(CURLOPT_PUT, val);
1916                 break;
1917
1918         case HTTP_METHOD_DELETE :
1919                 SET_OPTION(CURLOPT_HTTPGET, val);
1920                 SET_OPTION(CURLOPT_CUSTOMREQUEST, "DELETE");
1921                 break;
1922
1923         case HTTP_METHOD_CUSTOM :
1924                 SET_OPTION(CURLOPT_HTTPGET, val);
1925                 SET_OPTION(CURLOPT_CUSTOMREQUEST, section->method_str);
1926                 break;
1927
1928         default:
1929                 rad_assert(0);
1930                 break;
1931         };
1932
1933         /*
1934          *      Set user based authentication parameters
1935          */
1936         if (auth) {
1937                 if ((auth >= HTTP_AUTH_BASIC) &&
1938                     (auth <= HTTP_AUTH_ANY_SAFE)) {
1939                         SET_OPTION(CURLOPT_HTTPAUTH, http_curl_auth[auth]);
1940
1941                         if (username) {
1942                                 SET_OPTION(CURLOPT_USERNAME, username);
1943                         } else if (section->username) {
1944                                 if (radius_xlat(buffer, sizeof(buffer), request, section->username, NULL, NULL) < 0) {
1945                                         option = STRINGIFY(CURLOPT_USERNAME);
1946                                         goto error;
1947                                 }
1948                                 SET_OPTION(CURLOPT_USERNAME, buffer);
1949                         }
1950
1951                         if (password) {
1952                                 SET_OPTION(CURLOPT_PASSWORD, password);
1953                         } else if (section->password) {
1954                                 if (radius_xlat(buffer, sizeof(buffer), request, section->password, NULL, NULL) < 0) {
1955                                         option = STRINGIFY(CURLOPT_PASSWORD);
1956                                         goto error;
1957                                 }
1958                                 SET_OPTION(CURLOPT_PASSWORD, buffer);
1959                         }
1960 #ifdef CURLOPT_TLSAUTH_USERNAME
1961                 } else if (type == HTTP_AUTH_TLS_SRP) {
1962                         SET_OPTION(CURLOPT_TLSAUTH_TYPE, http_curl_auth[auth]);
1963
1964                         if (username) {
1965                                 SET_OPTION(CURLOPT_TLSAUTH_USERNAME, username);
1966                         } else if (section->username) {
1967                                 if (radius_xlat(buffer, sizeof(buffer), request, section->username, NULL, NULL) < 0) {
1968                                         option = STRINGIFY(CURLOPT_TLSAUTH_USERNAME);
1969                                         goto error;
1970                                 }
1971                                 SET_OPTION(CURLOPT_TLSAUTH_USERNAME, buffer);
1972                         }
1973
1974                         if (password) {
1975                                 SET_OPTION(CURLOPT_TLSAUTH_PASSWORD, password);
1976                         } else if (section->password) {
1977                                 if (radius_xlat(buffer, sizeof(buffer), request, section->password, NULL, NULL) < 0) {
1978                                         option = STRINGIFY(CURLOPT_TLSAUTH_PASSWORD);
1979                                         goto error;
1980                                 }
1981                                 SET_OPTION(CURLOPT_TLSAUTH_PASSWORD, buffer);
1982                         }
1983 #endif
1984                 }
1985         }
1986
1987         /*
1988          *      Set SSL/TLS authentication parameters
1989          */
1990         if (section->tls_certificate_file) {
1991                 SET_OPTION(CURLOPT_SSLCERT, section->tls_certificate_file);
1992         }
1993
1994         if (section->tls_private_key_file) {
1995                 SET_OPTION(CURLOPT_SSLKEY, section->tls_private_key_file);
1996         }
1997
1998         if (section->tls_private_key_password) {
1999                 SET_OPTION(CURLOPT_KEYPASSWD, section->tls_private_key_password);
2000         }
2001
2002         if (section->tls_ca_file) {
2003                 SET_OPTION(CURLOPT_ISSUERCERT, section->tls_ca_file);
2004         }
2005
2006         if (section->tls_ca_path) {
2007                 SET_OPTION(CURLOPT_CAPATH, section->tls_ca_path);
2008         }
2009
2010         if (section->tls_random_file) {
2011                 SET_OPTION(CURLOPT_RANDOM_FILE, section->tls_random_file);
2012         }
2013
2014         if (section->tls_check_cert) {
2015                 SET_OPTION(CURLOPT_SSL_VERIFYHOST, (section->tls_check_cert_cn == true) ? 2 : 0);
2016         } else {
2017                 SET_OPTION(CURLOPT_SSL_VERIFYPEER, 0);
2018         }
2019
2020         /*
2021          *      Tell CURL how to get HTTP body content, and how to process incoming data.
2022          */
2023         rest_response_init(request, &ctx->response, type);
2024
2025         SET_OPTION(CURLOPT_HEADERFUNCTION, rest_response_header);
2026         SET_OPTION(CURLOPT_HEADERDATA, &ctx->response);
2027         SET_OPTION(CURLOPT_WRITEFUNCTION, rest_response_body);
2028         SET_OPTION(CURLOPT_WRITEDATA, &ctx->response);
2029
2030         /*
2031          *  Force parsing the body text as a particular encoding.
2032          */
2033         ctx->response.force_to = section->force_to;
2034
2035         switch (method) {
2036         case HTTP_METHOD_GET :
2037         case HTTP_METHOD_DELETE :
2038                 RDEBUG3("Using a HTTP method which does not require a body.  Forcing request body type to \"none\"");
2039                 goto finish;
2040
2041         case HTTP_METHOD_POST :
2042         case HTTP_METHOD_PUT :
2043         case HTTP_METHOD_CUSTOM :
2044                 if (section->chunk > 0) {
2045                         ctx->request.chunk = section->chunk;
2046
2047                         ctx->headers = curl_slist_append(ctx->headers, "Expect:");
2048                         if (!ctx->headers) goto error_header;
2049
2050                         ctx->headers = curl_slist_append(ctx->headers, "Transfer-Encoding: chunked");
2051                         if (!ctx->headers) goto error_header;
2052                 }
2053
2054                 RDEBUG3("Request body content-type will be \"%s\"",
2055                         fr_int2str(http_content_type_table, type, section->body_str));
2056                 break;
2057
2058         default:
2059                 rad_assert(0);
2060         };
2061
2062         /*
2063          *  Setup encoder specific options
2064          */
2065         switch (type) {
2066         case HTTP_BODY_NONE:
2067                 if (rest_request_config_body(instance, section, request, handle,
2068                                              NULL) < 0) {
2069                         return -1;
2070                 }
2071
2072                 break;
2073
2074         case HTTP_BODY_CUSTOM_XLAT:
2075         {
2076                 rest_custom_data_t *data;
2077                 char *expanded = NULL;
2078
2079                 if (radius_axlat(&expanded, request, section->data, NULL, NULL) < 0) {
2080                         return -1;
2081                 }
2082
2083                 data = talloc_zero(request, rest_custom_data_t);
2084                 data->p = expanded;
2085
2086                 /* Use the encoder specific pointer to store the data we need to encode */
2087                 ctx->request.encoder = data;
2088                 if (rest_request_config_body(instance, section, request, handle,
2089                                              rest_encode_custom) < 0) {
2090                         TALLOC_FREE(ctx->request.encoder);
2091                         return -1;
2092                 }
2093
2094                 break;
2095         }
2096
2097         case HTTP_BODY_CUSTOM_LITERAL:
2098         {
2099                 rest_custom_data_t *data;
2100
2101                 data = talloc_zero(request, rest_custom_data_t);
2102                 data->p = section->data;
2103
2104                 /* Use the encoder specific pointer to store the data we need to encode */
2105                 ctx->request.encoder = data;
2106                 if (rest_request_config_body(instance, section, request, handle,
2107                                              rest_encode_custom) < 0) {
2108                         TALLOC_FREE(ctx->request.encoder);
2109                         return -1;
2110                 }
2111         }
2112                 break;
2113
2114 #ifdef HAVE_JSON
2115         case HTTP_BODY_JSON:
2116                 rest_request_init(request, &ctx->request, true);
2117
2118                 if (rest_request_config_body(instance, section, request, handle,
2119                                              rest_encode_json) < 0) {
2120                         return -1;
2121                 }
2122
2123                 break;
2124 #endif
2125
2126         case HTTP_BODY_POST:
2127                 rest_request_init(request, &ctx->request, false);
2128
2129                 if (rest_request_config_body(instance, section, request, handle,
2130                                              rest_encode_post) < 0) {
2131                         return -1;
2132                 }
2133
2134                 break;
2135
2136         default:
2137                 rad_assert(0);
2138         }
2139
2140
2141 finish:
2142         SET_OPTION(CURLOPT_HTTPHEADER, ctx->headers);
2143
2144         return 0;
2145
2146 error:
2147         REDEBUG("Failed setting curl option %s: %s (%i)", option, curl_easy_strerror(ret), ret);
2148         return -1;
2149
2150 error_header:
2151         REDEBUG("Failed creating header");
2152         return -1;
2153 }
2154
2155 /** Sends a REST (HTTP) request.
2156  *
2157  * Send the actual REST request to the server. The response will be handled by
2158  * the numerous callbacks configured in rest_request_config.
2159  *
2160  * @param[in] instance configuration data.
2161  * @param[in] section configuration data.
2162  * @param[in] request Current request.
2163  * @param[in] handle to use.
2164  * @return 0 on success or -1 on error.
2165  */
2166 int rest_request_perform(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
2167                          REQUEST *request, void *handle)
2168 {
2169         rlm_rest_handle_t       *randle = handle;
2170         CURL                    *candle = randle->handle;
2171         CURLcode                ret;
2172
2173         ret = curl_easy_perform(candle);
2174         if (ret != CURLE_OK) {
2175                 REDEBUG("Request failed: %i - %s", ret, curl_easy_strerror(ret));
2176
2177                 return -1;
2178         }
2179
2180         return 0;
2181 }
2182
2183 /** Sends the response to the correct decode function.
2184  *
2185  * Uses the Content-Type information written in rest_response_header to
2186  * determine the correct decode function to use. The decode function will
2187  * then convert the raw received data into VALUE_PAIRs.
2188  *
2189  * @param[in] instance configuration data.
2190  * @param[in] section configuration data.
2191  * @param[in] request Current request.
2192  * @param[in] handle to use.
2193  * @return 0 on success or -1 on error.
2194  */
2195 int rest_response_decode(rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
2196                          REQUEST *request, void *handle)
2197 {
2198         rlm_rest_handle_t       *randle = handle;
2199         rlm_rest_curl_context_t *ctx = randle->ctx;
2200
2201         int ret = -1;   /* -Wsometimes-uninitialized */
2202
2203         if (!ctx->response.buffer) {
2204                 RDEBUG2("Skipping attribute processing, no valid body data received");
2205                 return 0;
2206         }
2207
2208         RDEBUG3("Processing response body");
2209
2210         switch (ctx->response.type) {
2211         case HTTP_BODY_NONE:
2212                 return 0;
2213
2214         case HTTP_BODY_POST:
2215                 ret = rest_decode_post(instance, section, request, handle, ctx->response.buffer, ctx->response.used);
2216                 break;
2217
2218 #ifdef HAVE_JSON
2219         case HTTP_BODY_JSON:
2220                 ret = rest_decode_json(instance, section, request, handle, ctx->response.buffer, ctx->response.used);
2221                 break;
2222 #endif
2223
2224         case HTTP_BODY_UNSUPPORTED:
2225         case HTTP_BODY_UNAVAILABLE:
2226         case HTTP_BODY_INVALID:
2227                 return -1;
2228
2229         default:
2230                 rad_assert(0);
2231         }
2232
2233         return ret;
2234 }
2235
2236 /** Cleans up after a REST request.
2237  *
2238  * Resets all options associated with a CURL handle, and frees any headers
2239  * associated with it.
2240  *
2241  * Calls rest_read_ctx_free and rest_response_free to free any memory used by
2242  * context data.
2243  *
2244  * @param[in] instance configuration data.
2245  * @param[in] section configuration data.
2246  * @param[in] handle to cleanup.
2247  */
2248 void rest_request_cleanup(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_section_t *section, void *handle)
2249 {
2250         rlm_rest_handle_t       *randle = handle;
2251         rlm_rest_curl_context_t *ctx = randle->ctx;
2252         CURL                    *candle = randle->handle;
2253
2254         /*
2255          *  Clear any previously configured options
2256          */
2257         curl_easy_reset(candle);
2258
2259         /*
2260          *  Free header list
2261          */
2262         if (ctx->headers != NULL) {
2263                 curl_slist_free_all(ctx->headers);
2264                 ctx->headers = NULL;
2265         }
2266
2267         /*
2268          *  Free body data (only used if chunking is disabled)
2269          */
2270         if (ctx->body != NULL) {
2271                 free(ctx->body);
2272                 ctx->body = NULL;
2273         }
2274
2275         /*
2276          *  Free response data
2277          */
2278         if (ctx->response.buffer) {
2279                 free(ctx->response.buffer);
2280                 ctx->response.buffer = NULL;
2281         }
2282
2283         TALLOC_FREE(ctx->request.encoder);
2284         TALLOC_FREE(ctx->response.decoder);
2285 }
2286
2287 /** URL encodes a string.
2288  *
2289  * Encode special chars as per RFC 3986 section 4.
2290  *
2291  * @param[in] request Current request.
2292  * @param[out] out Where to write escaped string.
2293  * @param[in] outlen Size of out buffer.
2294  * @param[in] raw string to be urlencoded.
2295  * @param[in] arg pointer, gives context for escaping.
2296  * @return length of data written to out (excluding NULL).
2297  */
2298 size_t rest_uri_escape(UNUSED REQUEST *request, char *out, size_t outlen, char const *raw, UNUSED void *arg)
2299 {
2300         char *escaped;
2301
2302         escaped = curl_escape(raw, strlen(raw));
2303         strlcpy(out, escaped, outlen);
2304         curl_free(escaped);
2305
2306         return strlen(out);
2307 }
2308
2309 /** Builds URI; performs XLAT expansions and encoding.
2310  *
2311  * Splits the URI into "http://example.org" and "/%{xlat}/query/?bar=foo"
2312  * Both components are expanded, but values expanded for the second component
2313  * are also url encoded.
2314  *
2315  * @param[out] out Where to write the pointer to the new buffer containing the escaped URI.
2316  * @param[in] instance configuration data.
2317  * @param[in] uri configuration data.
2318  * @param[in] request Current request
2319  * @return length of data written to buffer (excluding NULL) or < 0 if an error
2320  *      occurred.
2321  */
2322 ssize_t rest_uri_build(char **out, UNUSED rlm_rest_t *instance, REQUEST *request, char const *uri)
2323 {
2324         char const      *p;
2325         char            *path_exp = NULL;
2326
2327         char            *scheme;
2328         char const      *path;
2329
2330         ssize_t         len;
2331
2332         p = uri;
2333
2334         /*
2335          *  All URLs must contain at least <scheme>://<server>/
2336          */
2337         p = strchr(p, ':');
2338         if (!p || (*++p != '/') || (*++p != '/')) {
2339                 malformed:
2340                 REDEBUG("Error URI is malformed, can't find start of path");
2341                 return -1;
2342         }
2343         p = strchr(p + 1, '/');
2344         if (!p) {
2345                 goto malformed;
2346         }
2347
2348         len = (p - uri);
2349
2350         /*
2351          *  Allocate a temporary buffer to hold the first part of the URI
2352          */
2353         scheme = talloc_array(request, char, len + 1);
2354         strlcpy(scheme, uri, len + 1);
2355
2356         path = (uri + len);
2357
2358         len = radius_axlat(out, request, scheme, NULL, NULL);
2359         talloc_free(scheme);
2360         if (len < 0) {
2361                 TALLOC_FREE(*out);
2362
2363                 return 0;
2364         }
2365
2366         len = radius_axlat(&path_exp, request, path, rest_uri_escape, NULL);
2367         if (len < 0) {
2368                 TALLOC_FREE(*out);
2369
2370                 return 0;
2371         }
2372
2373         MEM(*out = talloc_strdup_append(*out, path_exp));
2374         talloc_free(path_exp);
2375
2376         return talloc_array_length(*out) - 1;   /* array_length includes \0 */
2377 }
2378
2379 /** Unescapes the host portion of a URI string
2380  *
2381  * This is required because the xlat functions which operate on the input string
2382  * cannot distinguish between host and path components.
2383  *
2384  * @param[out] out Where to write the pointer to the new buffer containing the escaped URI.
2385  * @param[in] instance configuration data.
2386  * @param[in] request Current request
2387  * @param[in] handle to use.
2388  * @param[in] uri configuration data.
2389  * @return length of data written to buffer (excluding NULL) or < 0 if an error
2390  *      occurred.
2391  */
2392 ssize_t rest_uri_host_unescape(char **out, UNUSED rlm_rest_t *instance, REQUEST *request,
2393                                void *handle, char const *uri)
2394 {
2395         rlm_rest_handle_t       *randle = handle;
2396         CURL                    *candle = randle->handle;
2397
2398         char const              *p, *q;
2399
2400         char                    *scheme;
2401
2402         ssize_t                 len;
2403
2404         p = uri;
2405
2406         /*
2407          *  All URLs must contain at least <scheme>://<server>/
2408          */
2409         p = strchr(p, ':');
2410         if (!p || (*++p != '/') || (*++p != '/')) {
2411                 malformed:
2412                 REDEBUG("Error URI is malformed, can't find start of path");
2413                 return -1;
2414         }
2415         p = strchr(p + 1, '/');
2416         if (!p) {
2417                 goto malformed;
2418         }
2419
2420         len = (p - uri);
2421
2422         /*
2423          *  Unescape any special sequences in the first part of the URI
2424          */
2425         scheme = curl_easy_unescape(candle, uri, len, NULL);
2426         if (!scheme) {
2427                 REDEBUG("Error unescaping host");
2428                 return -1;
2429         }
2430
2431         /*
2432          *  URIs can't contain spaces, so anything after the space must
2433          *  be something else.
2434          */
2435         q = strchr(p, ' ');
2436         *out = q ? talloc_typed_asprintf(request, "%s%.*s", scheme, (int)(q - p), p) :
2437                    talloc_typed_asprintf(request, "%s%s", scheme, p);
2438
2439         MEM(*out);
2440         curl_free(scheme);
2441
2442         return talloc_array_length(*out) - 1;   /* array_length includes \0 */
2443 }