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