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