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