14dece8e4e07aa2241759eff5ef31af407d2e986
[asterisk/asterisk.git] / res / res_ari.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2012 - 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief HTTP binding for the Stasis API
22  * \author David M. Lee, II <dlee@digium.com>
23  *
24  * The API itself is documented using <a
25  * href="https://developers.helloreverb.com/swagger/">Swagger</a>, a lightweight
26  * mechanism for documenting RESTful API's using JSON. This allows us to use <a
27  * href="https://github.com/wordnik/swagger-ui">swagger-ui</a> to provide
28  * executable documentation for the API, generate client bindings in different
29  * <a href="https://github.com/asterisk/asterisk_rest_libraries">languages</a>,
30  * and generate a lot of the boilerplate code for implementing the RESTful
31  * bindings. The API docs live in the \c rest-api/ directory.
32  *
33  * The RESTful bindings are generated from the Swagger API docs using a set of
34  * <a href="http://mustache.github.io/mustache.5.html">Mustache</a> templates.
35  * The code generator is written in Python, and uses the Python implementation
36  * <a href="https://github.com/defunkt/pystache">pystache</a>. Pystache has no
37  * dependencies, and be installed easily using \c pip. Code generation code
38  * lives in \c rest-api-templates/.
39  *
40  * The generated code reduces a lot of boilerplate when it comes to handling
41  * HTTP requests. It also helps us have greater consistency in the REST API.
42  *
43  * The structure of the generated code is:
44  *
45  *  - res/ari/resource_{resource}.h
46  *    - For each operation in the resouce, a generated argument structure
47  *      (holding the parsed arguments from the request) and function
48  *      declarations (to implement in res/ari/resource_{resource}.c)
49  *  - res_ari_{resource}.c
50  *    - A set of \ref stasis_rest_callback functions, which glue the two
51  *      together. They parse out path variables and request parameters to
52  *      populate a specific \c *_args which is passed to the specific request
53  *      handler (in res/ari/resource_{resource}.c)
54  *    - A tree of \ref stasis_rest_handlers for routing requests to its
55  *      \ref stasis_rest_callback
56  *
57  * The basic flow of an HTTP request is:
58  *
59  *  - ast_ari_callback()
60  *    1. Initial request validation
61  *    2. Routes as either a doc request (ast_ari_get_docs) or API
62  *       request (ast_ari_invoke)
63  *       - ast_ari_invoke()
64  *         1. Further request validation
65  *         2. Routes the request through the tree of generated
66  *            \ref stasis_rest_handlers.
67  *         3. Dispatch to the generated callback
68  *            - \c ast_ari_*_cb
69  *              1. Populate \c *_args struct with path and get params
70  *              2. Invoke the request handler
71  *    3. Validates and sends response
72  */
73
74 /*** MODULEINFO
75         <depend type="module">res_http_websocket</depend>
76         <support_level>core</support_level>
77  ***/
78
79 /*** DOCUMENTATION
80         <configInfo name="res_ari" language="en_US">
81                 <synopsis>HTTP binding for the Stasis API</synopsis>
82                 <configFile name="ari.conf">
83                         <configObject name="general">
84                                 <synopsis>General configuration settings</synopsis>
85                                 <configOption name="enabled">
86                                         <synopsis>Enable/disable the ARI module</synopsis>
87                                         <description>
88                                                 <para>This option enables or disables the ARI module.</para>
89                                                 <note>
90                                                         <para>ARI uses Asterisk's HTTP server, which must also be enabled in <filename>http.conf</filename>.</para>
91                                                 </note>
92                                         </description>
93                                         <see-also>
94                                                 <ref type="filename">http.conf</ref>
95                                                 <ref type="link">https://wiki.asterisk.org/wiki/display/AST/Asterisk+Builtin+mini-HTTP+Server</ref>
96                                         </see-also>
97                                 </configOption>
98                                 <configOption name="websocket_write_timeout">
99                                         <synopsis>The timeout (in milliseconds) to set on WebSocket connections.</synopsis>
100                                         <description>
101                                                 <para>If a websocket connection accepts input slowly, the timeout
102                                                 for writes to it can be increased to keep it from being disconnected.
103                                                 Value is in milliseconds; default is 100 ms.</para>
104                                         </description>
105                                 </configOption>
106                                 <configOption name="pretty">
107                                         <synopsis>Responses from ARI are formatted to be human readable</synopsis>
108                                 </configOption>
109                                 <configOption name="auth_realm">
110                                         <synopsis>Realm to use for authentication. Defaults to Asterisk REST Interface.</synopsis>
111                                 </configOption>
112                                 <configOption name="allowed_origins">
113                                         <synopsis>Comma separated list of allowed origins, for Cross-Origin Resource Sharing. May be set to * to allow all origins.</synopsis>
114                                 </configOption>
115                         </configObject>
116
117                         <configObject name="user">
118                                 <synopsis>Per-user configuration settings</synopsis>
119                                 <configOption name="type">
120                                         <synopsis>Define this configuration section as a user.</synopsis>
121                                         <description>
122                                                 <enumlist>
123                                                         <enum name="user"><para>Configure this section as a <replaceable>user</replaceable></para></enum>
124                                                 </enumlist>
125                                         </description>
126                                 </configOption>
127                                 <configOption name="read_only">
128                                         <synopsis>When set to yes, user is only authorized for read-only requests</synopsis>
129                                 </configOption>
130                                 <configOption name="password">
131                                         <synopsis>Crypted or plaintext password (see password_format)</synopsis>
132                                 </configOption>
133                                 <configOption name="password_format">
134                                         <synopsis>password_format may be set to plain (the default) or crypt. When set to crypt, crypt(3) is used to validate the password. A crypted password can be generated using mkpasswd -m sha-512. When set to plain, the password is in plaintext</synopsis>
135                                 </configOption>
136                         </configObject>
137                 </configFile>
138         </configInfo>
139 ***/
140
141 #include "asterisk.h"
142
143 ASTERISK_REGISTER_FILE()
144
145 #include "ari/internal.h"
146 #include "asterisk/ari.h"
147 #include "asterisk/astobj2.h"
148 #include "asterisk/module.h"
149 #include "asterisk/paths.h"
150
151 #include <string.h>
152 #include <sys/stat.h>
153 #include <unistd.h>
154
155 /*! \brief Helper function to check if module is enabled. */
156 static int is_enabled(void)
157 {
158         RAII_VAR(struct ast_ari_conf *, cfg, ast_ari_config_get(), ao2_cleanup);
159         return cfg && cfg->general && cfg->general->enabled;
160 }
161
162 /*! Lock for \ref root_handler */
163 static ast_mutex_t root_handler_lock;
164
165 /*! Handler for root RESTful resource. */
166 static struct stasis_rest_handlers *root_handler;
167
168 /*! Pre-defined message for allocation failures. */
169 static struct ast_json *oom_json;
170
171 struct ast_json *ast_ari_oom_json(void)
172 {
173         return oom_json;
174 }
175
176 int ast_ari_add_handler(struct stasis_rest_handlers *handler)
177 {
178         RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup);
179         size_t old_size, new_size;
180
181         SCOPED_MUTEX(lock, &root_handler_lock);
182
183         old_size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
184         new_size = old_size + sizeof(handler);
185
186         new_handler = ao2_alloc(new_size, NULL);
187         if (!new_handler) {
188                 return -1;
189         }
190         memcpy(new_handler, root_handler, old_size);
191         new_handler->children[new_handler->num_children++] = handler;
192
193         ao2_cleanup(root_handler);
194         ao2_ref(new_handler, +1);
195         root_handler = new_handler;
196         ast_module_ref(ast_module_info->self);
197         return 0;
198 }
199
200 int ast_ari_remove_handler(struct stasis_rest_handlers *handler)
201 {
202         struct stasis_rest_handlers *new_handler;
203         size_t size;
204         size_t i;
205         size_t j;
206
207         ast_assert(root_handler != NULL);
208
209         ast_mutex_lock(&root_handler_lock);
210         size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
211
212         new_handler = ao2_alloc(size, NULL);
213         if (!new_handler) {
214                 ast_mutex_unlock(&root_handler_lock);
215                 return -1;
216         }
217
218         /* Create replacement root_handler less the handler to remove. */
219         memcpy(new_handler, root_handler, sizeof(*new_handler));
220         for (i = 0, j = 0; i < root_handler->num_children; ++i) {
221                 if (root_handler->children[i] == handler) {
222                         ast_module_unref(ast_module_info->self);
223                         continue;
224                 }
225                 new_handler->children[j++] = root_handler->children[i];
226         }
227         new_handler->num_children = j;
228
229         /* Replace the old root_handler with the new. */
230         ao2_cleanup(root_handler);
231         root_handler = new_handler;
232
233         ast_mutex_unlock(&root_handler_lock);
234         return 0;
235 }
236
237 static struct stasis_rest_handlers *get_root_handler(void)
238 {
239         SCOPED_MUTEX(lock, &root_handler_lock);
240         ao2_ref(root_handler, +1);
241         return root_handler;
242 }
243
244 static struct stasis_rest_handlers *root_handler_create(void)
245 {
246         RAII_VAR(struct stasis_rest_handlers *, handler, NULL, ao2_cleanup);
247
248         handler = ao2_alloc(sizeof(*handler), NULL);
249         if (!handler) {
250                 return NULL;
251         }
252         handler->path_segment = "ari";
253
254         ao2_ref(handler, +1);
255         return handler;
256 }
257
258 void ast_ari_response_error(struct ast_ari_response *response,
259                                 int response_code,
260                                 const char *response_text,
261                                 const char *message_fmt, ...)
262 {
263         RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
264         va_list ap;
265
266         va_start(ap, message_fmt);
267         message = ast_json_vstringf(message_fmt, ap);
268         va_end(ap);
269         response->message = ast_json_pack("{s: o}",
270                                           "message", ast_json_ref(message));
271         response->response_code = response_code;
272         response->response_text = response_text;
273 }
274
275 void ast_ari_response_ok(struct ast_ari_response *response,
276                              struct ast_json *message)
277 {
278         response->message = message;
279         response->response_code = 200;
280         response->response_text = "OK";
281 }
282
283 void ast_ari_response_no_content(struct ast_ari_response *response)
284 {
285         response->message = ast_json_null();
286         response->response_code = 204;
287         response->response_text = "No Content";
288 }
289
290 void ast_ari_response_accepted(struct ast_ari_response *response)
291 {
292         response->message = ast_json_null();
293         response->response_code = 202;
294         response->response_text = "Accepted";
295 }
296
297 void ast_ari_response_alloc_failed(struct ast_ari_response *response)
298 {
299         response->message = ast_json_ref(oom_json);
300         response->response_code = 500;
301         response->response_text = "Internal Server Error";
302 }
303
304 void ast_ari_response_created(struct ast_ari_response *response,
305         const char *url, struct ast_json *message)
306 {
307         RAII_VAR(struct stasis_rest_handlers *, root, get_root_handler(), ao2_cleanup);
308         response->message = message;
309         response->response_code = 201;
310         response->response_text = "Created";
311         ast_str_append(&response->headers, 0, "Location: /%s%s\r\n", root->path_segment, url);
312 }
313
314 static void add_allow_header(struct stasis_rest_handlers *handler,
315                              struct ast_ari_response *response)
316 {
317         enum ast_http_method m;
318         ast_str_append(&response->headers, 0,
319                        "Allow: OPTIONS");
320         for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
321                 if (handler->callbacks[m] != NULL) {
322                         ast_str_append(&response->headers, 0,
323                                        ",%s", ast_get_http_method(m));
324                 }
325         }
326         ast_str_append(&response->headers, 0, "\r\n");
327 }
328
329 static int origin_allowed(const char *origin)
330 {
331         RAII_VAR(struct ast_ari_conf *, cfg, ast_ari_config_get(), ao2_cleanup);
332
333         char *allowed = ast_strdupa(cfg->general->allowed_origins);
334         char *current;
335
336         while ((current = strsep(&allowed, ","))) {
337                 if (!strcmp(current, "*")) {
338                         return 1;
339                 }
340
341                 if (!strcmp(current, origin)) {
342                         return 1;
343                 }
344         }
345
346         return 0;
347 }
348
349 #define ACR_METHOD "Access-Control-Request-Method"
350 #define ACR_HEADERS "Access-Control-Request-Headers"
351 #define ACA_METHODS "Access-Control-Allow-Methods"
352 #define ACA_HEADERS "Access-Control-Allow-Headers"
353
354 /*!
355  * \brief Handle OPTIONS request, mainly for CORS preflight requests.
356  *
357  * Some browsers will send this prior to non-simple methods (i.e. DELETE).
358  * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2.
359  */
360 static void handle_options(struct stasis_rest_handlers *handler,
361                            struct ast_variable *headers,
362                            struct ast_ari_response *response)
363 {
364         struct ast_variable *header;
365         char const *acr_method = NULL;
366         char const *acr_headers = NULL;
367         char const *origin = NULL;
368
369         RAII_VAR(struct ast_str *, allow, NULL, ast_free);
370         enum ast_http_method m;
371         int allowed = 0;
372
373         /* Regular OPTIONS response */
374         add_allow_header(handler, response);
375         ast_ari_response_no_content(response);
376
377         /* Parse CORS headers */
378         for (header = headers; header != NULL; header = header->next) {
379                 if (strcmp(ACR_METHOD, header->name) == 0) {
380                         acr_method = header->value;
381                 } else if (strcmp(ACR_HEADERS, header->name) == 0) {
382                         acr_headers = header->value;
383                 } else if (strcmp("Origin", header->name) == 0) {
384                         origin = header->value;
385                 }
386         }
387
388         /* CORS 6.2, #1 - "If the Origin header is not present terminate this
389          * set of steps."
390          */
391         if (origin == NULL) {
392                 return;
393         }
394
395         /* CORS 6.2, #2 - "If the value of the Origin header is not a
396          * case-sensitive match for any of the values in list of origins do not
397          * set any additional headers and terminate this set of steps.
398          *
399          * Always matching is acceptable since the list of origins can be
400          * unbounded.
401          *
402          * The Origin header can only contain a single origin as the user agent
403          * will not follow redirects."
404          */
405         if (!origin_allowed(origin)) {
406                 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
407                 return;
408         }
409
410         /* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
411          * or if parsing failed, do not set any additional headers and terminate
412          * this set of steps."
413          */
414         if (acr_method == NULL) {
415                 return;
416         }
417
418         /* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers
419          * headers let header field-names be the empty list."
420          */
421         if (acr_headers == NULL) {
422                 acr_headers = "";
423         }
424
425         /* CORS 6.2, #5 - "If method is not a case-sensitive match for any of
426          * the values in list of methods do not set any additional headers and
427          * terminate this set of steps."
428          */
429         allow = ast_str_create(20);
430
431         if (!allow) {
432                 ast_ari_response_alloc_failed(response);
433                 return;
434         }
435
436         /* Go ahead and build the ACA_METHODS header at the same time */
437         for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
438                 if (handler->callbacks[m] != NULL) {
439                         char const *m_str = ast_get_http_method(m);
440                         if (strcmp(m_str, acr_method) == 0) {
441                                 allowed = 1;
442                         }
443                         ast_str_append(&allow, 0, ",%s", m_str);
444                 }
445         }
446
447         if (!allowed) {
448                 return;
449         }
450
451         /* CORS 6.2 #6 - "If any of the header field-names is not a ASCII
452          * case-insensitive match for any of the values in list of headers do
453          * not set any additional headers and terminate this set of steps.
454          *
455          * Note: Always matching is acceptable since the list of headers can be
456          * unbounded."
457          */
458
459         /* CORS 6.2 #7 - "If the resource supports credentials add a single
460          * Access-Control-Allow-Origin header, with the value of the Origin
461          * header as value, and add a single Access-Control-Allow-Credentials
462          * header with the case-sensitive string "true" as value."
463          *
464          * Added by process_cors_request() earlier in the request.
465          */
466
467         /* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age
468          * header..."
469          */
470
471         /* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers
472          * consisting of (a subset of) the list of methods."
473          */
474         ast_str_append(&response->headers, 0, "%s: OPTIONS%s\r\n",
475                        ACA_METHODS, ast_str_buffer(allow));
476
477
478         /* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
479          * consisting of (a subset of) the list of headers.
480          *
481          * Since the list of headers can be unbounded simply returning headers
482          * can be enough."
483          */
484         if (!ast_strlen_zero(acr_headers)) {
485                 ast_str_append(&response->headers, 0, "%s: %s\r\n",
486                                ACA_HEADERS, acr_headers);
487         }
488 }
489
490 void ast_ari_invoke(struct ast_tcptls_session_instance *ser,
491         const char *uri, enum ast_http_method method,
492         struct ast_variable *get_params, struct ast_variable *headers,
493         struct ast_ari_response *response)
494 {
495         RAII_VAR(struct stasis_rest_handlers *, root, NULL, ao2_cleanup);
496         struct stasis_rest_handlers *handler;
497         RAII_VAR(struct ast_variable *, path_vars, NULL, ast_variables_destroy);
498         char *path = ast_strdupa(uri);
499         char *path_segment;
500         stasis_rest_callback callback;
501
502         root = handler = get_root_handler();
503         ast_assert(root != NULL);
504
505         while ((path_segment = strsep(&path, "/")) && (strlen(path_segment) > 0)) {
506                 struct stasis_rest_handlers *found_handler = NULL;
507                 int i;
508                 ast_uri_decode(path_segment, ast_uri_http_legacy);
509                 ast_debug(3, "Finding handler for %s\n", path_segment);
510                 for (i = 0; found_handler == NULL && i < handler->num_children; ++i) {
511                         struct stasis_rest_handlers *child = handler->children[i];
512
513                         ast_debug(3, "  Checking %s\n", child->path_segment);
514                         if (child->is_wildcard) {
515                                 /* Record the path variable */
516                                 struct ast_variable *path_var = ast_variable_new(child->path_segment, path_segment, __FILE__);
517                                 path_var->next = path_vars;
518                                 path_vars = path_var;
519                                 found_handler = child;
520                         } else if (strcmp(child->path_segment, path_segment) == 0) {
521                                 found_handler = child;
522                         }
523                 }
524
525                 if (found_handler == NULL) {
526                         /* resource not found */
527                         ast_debug(3, "  Handler not found\n");
528                         ast_ari_response_error(
529                                 response, 404, "Not Found",
530                                 "Resource not found");
531                         return;
532                 } else {
533                         ast_debug(3, "  Got it!\n");
534                         handler = found_handler;
535                 }
536         }
537
538         ast_assert(handler != NULL);
539         if (method == AST_HTTP_OPTIONS) {
540                 handle_options(handler, headers, response);
541                 return;
542         }
543
544         if (method < 0 || method >= AST_HTTP_MAX_METHOD) {
545                 add_allow_header(handler, response);
546                 ast_ari_response_error(
547                         response, 405, "Method Not Allowed",
548                         "Invalid method");
549                 return;
550         }
551
552         if (handler->ws_server && method == AST_HTTP_GET) {
553                 /* WebSocket! */
554                 ari_handle_websocket(handler->ws_server, ser, uri, method,
555                         get_params, headers);
556                 /* Since the WebSocket code handles the connection, we shouldn't
557                  * do anything else; setting no_response */
558                 response->no_response = 1;
559                 return;
560         }
561
562         callback = handler->callbacks[method];
563         if (callback == NULL) {
564                 add_allow_header(handler, response);
565                 ast_ari_response_error(
566                         response, 405, "Method Not Allowed",
567                         "Invalid method");
568                 return;
569         }
570
571         callback(ser, get_params, path_vars, headers, response);
572         if (response->message == NULL && response->response_code == 0) {
573                 /* Really should not happen */
574                 ast_log(LOG_ERROR, "ARI %s %s not implemented\n",
575                         ast_get_http_method(method), uri);
576                 ast_ari_response_error(
577                         response, 501, "Not Implemented",
578                         "Method not implemented");
579         }
580 }
581
582 void ast_ari_get_docs(const char *uri, struct ast_variable *headers,
583                           struct ast_ari_response *response)
584 {
585         RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
586         RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
587         RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
588         struct ast_json *obj = NULL;
589         struct ast_variable *host = NULL;
590         struct ast_json_error error = {};
591         struct stat file_stat;
592
593         ast_debug(3, "%s(%s)\n", __func__, uri);
594
595         absolute_path_builder = ast_str_create(80);
596         if (absolute_path_builder == NULL) {
597                 ast_ari_response_alloc_failed(response);
598                 return;
599         }
600
601         /* absolute path to the rest-api directory */
602         ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
603         ast_str_append(&absolute_path_builder, 0, "/rest-api/");
604         absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
605         if (absolute_api_dirname == NULL) {
606                 ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
607                 ast_ari_response_error(
608                         response, 500, "Internal Server Error",
609                         "Cannot find rest-api directory");
610                 return;
611         }
612
613         /* absolute path to the requested file */
614         ast_str_append(&absolute_path_builder, 0, "%s", uri);
615         absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
616         if (absolute_filename == NULL) {
617                 switch (errno) {
618                 case ENAMETOOLONG:
619                 case ENOENT:
620                 case ENOTDIR:
621                         ast_ari_response_error(
622                                 response, 404, "Not Found",
623                                 "Resource not found");
624                         break;
625                 case EACCES:
626                         ast_ari_response_error(
627                                 response, 403, "Forbidden",
628                                 "Permission denied");
629                         break;
630                 default:
631                         ast_log(LOG_ERROR,
632                                 "Error determining real path for uri '%s': %s\n",
633                                 uri, strerror(errno));
634                         ast_ari_response_error(
635                                 response, 500, "Internal Server Error",
636                                 "Cannot find file");
637                         break;
638                 }
639                 return;
640         }
641
642         if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
643                 /* HACKERZ! */
644                 ast_log(LOG_ERROR,
645                         "Invalid attempt to access '%s' (not in %s)\n",
646                         absolute_filename, absolute_api_dirname);
647                 ast_ari_response_error(
648                         response, 404, "Not Found",
649                         "Resource not found");
650                 return;
651         }
652
653         if (stat(absolute_filename, &file_stat) == 0) {
654                 if (!(file_stat.st_mode & S_IFREG)) {
655                         /* Not a file */
656                         ast_ari_response_error(
657                                 response, 403, "Forbidden",
658                                 "Invalid access");
659                         return;
660                 }
661         } else {
662                 /* Does not exist */
663                 ast_ari_response_error(
664                         response, 404, "Not Found",
665                         "Resource not found");
666                 return;
667         }
668
669         /* Load resource object from file */
670         obj = ast_json_load_new_file(absolute_filename, &error);
671         if (obj == NULL) {
672                 ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
673                         error.source, error.line, error.column, error.text);
674                 ast_ari_response_error(
675                         response, 500, "Internal Server Error",
676                         "Yikes! Cannot parse resource");
677                 return;
678         }
679
680         /* Update the basePath properly */
681         if (ast_json_object_get(obj, "basePath") != NULL) {
682                 for (host = headers; host; host = host->next) {
683                         if (strcasecmp(host->name, "Host") == 0) {
684                                 break;
685                         }
686                 }
687                 if (host != NULL) {
688                         ast_json_object_set(
689                                 obj, "basePath",
690                                 ast_json_stringf("http://%s/ari", host->value));
691                 } else {
692                         /* Without the host, we don't have the basePath */
693                         ast_json_object_del(obj, "basePath");
694                 }
695         }
696
697         ast_ari_response_ok(response, obj);
698 }
699
700 static void remove_trailing_slash(const char *uri,
701                                   struct ast_ari_response *response)
702 {
703         char *slashless = ast_strdupa(uri);
704         slashless[strlen(slashless) - 1] = '\0';
705
706         /* While it's tempting to redirect the client to the slashless URL,
707          * that is problematic. A 302 Found is the most appropriate response,
708          * but most clients issue a GET on the location you give them,
709          * regardless of the method of the original request.
710          *
711          * While there are some ways around this, it gets into a lot of client
712          * specific behavior and corner cases in the HTTP standard. There's also
713          * very little practical benefit of redirecting; only GET and HEAD can
714          * be redirected automagically; all other requests "MUST NOT
715          * automatically redirect the request unless it can be confirmed by the
716          * user, since this might change the conditions under which the request
717          * was issued."
718          *
719          * Given all of that, a 404 with a nice message telling them what to do
720          * is probably our best bet.
721          */
722         ast_ari_response_error(response, 404, "Not Found",
723                 "ARI URLs do not end with a slash. Try /ari/%s", slashless);
724 }
725
726 /*!
727  * \brief Handle CORS headers for simple requests.
728  *
729  * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.1.
730  */
731 static void process_cors_request(struct ast_variable *headers,
732                                  struct ast_ari_response *response)
733 {
734         char const *origin = NULL;
735         struct ast_variable *header;
736
737         /* Parse CORS headers */
738         for (header = headers; header != NULL; header = header->next) {
739                 if (strcmp("Origin", header->name) == 0) {
740                         origin = header->value;
741                 }
742         }
743
744         /* CORS 6.1, #1 - "If the Origin header is not present terminate this
745          * set of steps."
746          */
747         if (origin == NULL) {
748                 return;
749         }
750
751         /* CORS 6.1, #2 - "If the value of the Origin header is not a
752          * case-sensitive match for any of the values in list of origins, do not
753          * set any additional headers and terminate this set of steps.
754          *
755          * Note: Always matching is acceptable since the list of origins can be
756          * unbounded."
757          */
758         if (!origin_allowed(origin)) {
759                 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
760                 return;
761         }
762
763         /* CORS 6.1, #3 - "If the resource supports credentials add a single
764          * Access-Control-Allow-Origin header, with the value of the Origin
765          * header as value, and add a single Access-Control-Allow-Credentials
766          * header with the case-sensitive string "true" as value.
767          *
768          * Otherwise, add a single Access-Control-Allow-Origin header, with
769          * either the value of the Origin header or the string "*" as value."
770          */
771         ast_str_append(&response->headers, 0,
772                        "Access-Control-Allow-Origin: %s\r\n", origin);
773         ast_str_append(&response->headers, 0,
774                        "Access-Control-Allow-Credentials: true\r\n");
775
776         /* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
777          * or more Access-Control-Expose-Headers headers, with as values the
778          * header field names given in the list of exposed headers."
779          *
780          * No exposed headers; skipping
781          */
782 }
783
784 enum ast_json_encoding_format ast_ari_json_format(void)
785 {
786         RAII_VAR(struct ast_ari_conf *, cfg, NULL, ao2_cleanup);
787         cfg = ast_ari_config_get();
788         return cfg->general->format;
789 }
790
791 /*!
792  * \brief Authenticate a <code>?api_key=userid:password</code>
793  *
794  * \param api_key API key query parameter
795  * \return User object for the authenticated user.
796  * \return \c NULL if authentication failed.
797  */
798 static struct ast_ari_conf_user *authenticate_api_key(const char *api_key)
799 {
800         RAII_VAR(char *, copy, NULL, ast_free);
801         char *username;
802         char *password;
803
804         password = copy = ast_strdup(api_key);
805         if (!copy) {
806                 return NULL;
807         }
808
809         username = strsep(&password, ":");
810         if (!password) {
811                 ast_log(LOG_WARNING, "Invalid api_key\n");
812                 return NULL;
813         }
814
815         return ast_ari_config_validate_user(username, password);
816 }
817
818 /*!
819  * \brief Authenticate an HTTP request.
820  *
821  * \param get_params GET parameters of the request.
822  * \param header HTTP headers.
823  * \return User object for the authenticated user.
824  * \return \c NULL if authentication failed.
825  */
826 static struct ast_ari_conf_user *authenticate_user(struct ast_variable *get_params,
827         struct ast_variable *headers)
828 {
829         RAII_VAR(struct ast_http_auth *, http_auth, NULL, ao2_cleanup);
830         struct ast_variable *v;
831
832         /* HTTP Basic authentication */
833         http_auth = ast_http_get_auth(headers);
834         if (http_auth) {
835                 return ast_ari_config_validate_user(http_auth->userid,
836                         http_auth->password);
837         }
838
839         /* ?api_key authentication */
840         for (v = get_params; v; v = v->next) {
841                 if (strcasecmp("api_key", v->name) == 0) {
842                         return authenticate_api_key(v->value);
843                 }
844         }
845
846         return NULL;
847 }
848
849 /*!
850  * \internal
851  * \brief ARI HTTP handler.
852  *
853  * This handler takes the HTTP request and turns it into the appropriate
854  * RESTful request (conversion to JSON, routing, etc.)
855  *
856  * \param ser TCP session.
857  * \param urih URI handler.
858  * \param uri URI requested.
859  * \param method HTTP method.
860  * \param get_params HTTP \c GET params.
861  * \param headers HTTP headers.
862  */
863 static int ast_ari_callback(struct ast_tcptls_session_instance *ser,
864                                 const struct ast_http_uri *urih,
865                                 const char *uri,
866                                 enum ast_http_method method,
867                                 struct ast_variable *get_params,
868                                 struct ast_variable *headers)
869 {
870         RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup);
871         RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
872         RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup);
873         struct ast_ari_response response = {};
874         RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);
875
876         if (!response_body) {
877                 ast_http_request_close_on_completion(ser);
878                 ast_http_error(ser, 500, "Server Error", "Out of memory");
879                 return 0;
880         }
881
882         response.headers = ast_str_create(40);
883         if (!response.headers) {
884                 ast_http_request_close_on_completion(ser);
885                 ast_http_error(ser, 500, "Server Error", "Out of memory");
886                 return 0;
887         }
888
889         conf = ast_ari_config_get();
890         if (!conf || !conf->general) {
891                 ast_free(response.headers);
892                 ast_http_request_close_on_completion(ser);
893                 ast_http_error(ser, 500, "Server Error", "URI handler config missing");
894                 return 0;
895         }
896
897         process_cors_request(headers, &response);
898
899         /* Process form data from a POST. It could be mixed with query
900          * parameters, which seems a bit odd. But it's allowed, so that's okay
901          * with us.
902          */
903         post_vars = ast_http_get_post_vars(ser, headers);
904         if (!post_vars) {
905                 switch (errno) {
906                 case EFBIG:
907                         ast_ari_response_error(&response, 413,
908                                 "Request Entity Too Large",
909                                 "Request body too large");
910                         goto request_failed;
911                 case ENOMEM:
912                         ast_http_request_close_on_completion(ser);
913                         ast_ari_response_error(&response, 500,
914                                 "Internal Server Error",
915                                 "Out of memory");
916                         goto request_failed;
917                 case EIO:
918                         ast_ari_response_error(&response, 400,
919                                 "Bad Request", "Error parsing request body");
920                         goto request_failed;
921                 }
922         }
923         if (get_params == NULL) {
924                 get_params = post_vars;
925         } else if (get_params && post_vars) {
926                 /* Has both post_vars and get_params */
927                 struct ast_variable *last_var = post_vars;
928                 while (last_var->next) {
929                         last_var = last_var->next;
930                 }
931                 /* The duped get_params will get freed when post_vars gets
932                  * ast_variables_destroyed.
933                  */
934                 last_var->next = ast_variables_dup(get_params);
935                 get_params = post_vars;
936         }
937
938         user = authenticate_user(get_params, headers);
939         if (response.response_code > 0) {
940                 /* POST parameter processing error. Do nothing. */
941         } else if (!user) {
942                 /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response
943                  * message is used by an origin server to challenge the
944                  * authorization of a user agent. This response MUST include a
945                  * WWW-Authenticate header field containing at least one
946                  * challenge applicable to the requested resource.
947                  */
948                 ast_ari_response_error(&response, 401, "Unauthorized", "Authentication required");
949
950                 /* Section 1.2:
951                  *   realm       = "realm" "=" realm-value
952                  *   realm-value = quoted-string
953                  * Section 2:
954                  *   challenge   = "Basic" realm
955                  */
956                 ast_str_append(&response.headers, 0,
957                         "WWW-Authenticate: Basic realm=\"%s\"\r\n",
958                         conf->general->auth_realm);
959         } else if (!ast_fully_booted) {
960                 ast_http_request_close_on_completion(ser);
961                 ast_ari_response_error(&response, 503, "Service Unavailable", "Asterisk not booted");
962         } else if (user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
963                 ast_ari_response_error(&response, 403, "Forbidden", "Write access denied");
964         } else if (ast_ends_with(uri, "/")) {
965                 remove_trailing_slash(uri, &response);
966         } else if (ast_begins_with(uri, "api-docs/")) {
967                 /* Serving up API docs */
968                 if (method != AST_HTTP_GET) {
969                         ast_ari_response_error(&response, 405, "Method Not Allowed", "Unsupported method");
970                 } else {
971                         /* Skip the api-docs prefix */
972                         ast_ari_get_docs(strchr(uri, '/') + 1, headers, &response);
973                 }
974         } else {
975                 /* Other RESTful resources */
976                 ast_ari_invoke(ser, uri, method, get_params, headers,
977                         &response);
978         }
979
980         if (response.no_response) {
981                 /* The handler indicates no further response is necessary.
982                  * Probably because it already handled it */
983                 ast_free(response.headers);
984                 return 0;
985         }
986
987 request_failed:
988         /* If you explicitly want to have no content, set message to
989          * ast_json_null().
990          */
991         ast_assert(response.message != NULL);
992         ast_assert(response.response_code > 0);
993
994         /* response.message could be NULL, in which case the empty response_body
995          * is correct
996          */
997         if (response.message && !ast_json_is_null(response.message)) {
998                 ast_str_append(&response.headers, 0,
999                                "Content-type: application/json\r\n");
1000                 if (ast_json_dump_str_format(response.message, &response_body,
1001                                 conf->general->format) != 0) {
1002                         /* Error encoding response */
1003                         response.response_code = 500;
1004                         response.response_text = "Internal Server Error";
1005                         ast_str_set(&response_body, 0, "%s", "");
1006                         ast_str_set(&response.headers, 0, "%s", "");
1007                 }
1008         }
1009
1010         ast_debug(3, "Examining ARI response:\n%d %s\n%s\n%s\n", response.response_code,
1011                 response.response_text, ast_str_buffer(response.headers), ast_str_buffer(response_body));
1012         ast_http_send(ser, method, response.response_code,
1013                       response.response_text, response.headers, response_body,
1014                       0, 0);
1015         /* ast_http_send takes ownership, so we don't have to free them */
1016         response_body = NULL;
1017
1018         ast_json_unref(response.message);
1019         return 0;
1020 }
1021
1022 static struct ast_http_uri http_uri = {
1023         .callback = ast_ari_callback,
1024         .description = "Asterisk RESTful API",
1025         .uri = "ari",
1026
1027         .has_subtree = 1,
1028         .data = NULL,
1029         .key = __FILE__,
1030         .no_decode_uri = 1,
1031 };
1032
1033 static int load_module(void)
1034 {
1035         ast_mutex_init(&root_handler_lock);
1036
1037         /* root_handler may have been built during a declined load */
1038         if (!root_handler) {
1039                 root_handler = root_handler_create();
1040         }
1041         if (!root_handler) {
1042                 return AST_MODULE_LOAD_FAILURE;
1043         }
1044
1045         /* oom_json may have been built during a declined load */
1046         if (!oom_json) {
1047                 oom_json = ast_json_pack(
1048                         "{s: s}", "error", "Allocation failed");
1049         }
1050         if (!oom_json) {
1051                 /* Ironic */
1052                 return AST_MODULE_LOAD_FAILURE;
1053         }
1054
1055         if (ast_ari_config_init() != 0) {
1056                 return AST_MODULE_LOAD_DECLINE;
1057         }
1058
1059         if (is_enabled()) {
1060                 ast_debug(3, "ARI enabled\n");
1061                 ast_http_uri_link(&http_uri);
1062         } else {
1063                 ast_debug(3, "ARI disabled\n");
1064         }
1065
1066         if (ast_ari_cli_register() != 0) {
1067                 return AST_MODULE_LOAD_FAILURE;
1068         }
1069
1070         return AST_MODULE_LOAD_SUCCESS;
1071 }
1072
1073 static int unload_module(void)
1074 {
1075         ast_ari_cli_unregister();
1076
1077         if (is_enabled()) {
1078                 ast_debug(3, "Disabling ARI\n");
1079                 ast_http_uri_unlink(&http_uri);
1080         }
1081
1082         ast_ari_config_destroy();
1083
1084         ao2_cleanup(root_handler);
1085         root_handler = NULL;
1086         ast_mutex_destroy(&root_handler_lock);
1087
1088         ast_json_unref(oom_json);
1089         oom_json = NULL;
1090
1091         return 0;
1092 }
1093
1094 static int reload_module(void)
1095 {
1096         char was_enabled = is_enabled();
1097
1098         if (ast_ari_config_reload() != 0) {
1099                 return AST_MODULE_LOAD_DECLINE;
1100         }
1101
1102         if (was_enabled && !is_enabled()) {
1103                 ast_debug(3, "Disabling ARI\n");
1104                 ast_http_uri_unlink(&http_uri);
1105         } else if (!was_enabled && is_enabled()) {
1106                 ast_debug(3, "Enabling ARI\n");
1107                 ast_http_uri_link(&http_uri);
1108         }
1109
1110         return AST_MODULE_LOAD_SUCCESS;
1111 }
1112
1113 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Asterisk RESTful Interface",
1114         .support_level = AST_MODULE_SUPPORT_CORE,
1115         .load = load_module,
1116         .unload = unload_module,
1117         .reload = reload_module,
1118         .nonoptreq = "res_http_websocket",
1119         .load_pri = AST_MODPRI_APP_DEPEND,
1120 );