res_ari.c 37 KB

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