config.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 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 Config framework stuffz for ARI.
  21. * \author David M. Lee, II <dlee@digium.com>
  22. */
  23. #include "asterisk.h"
  24. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  25. #include "asterisk/config_options.h"
  26. #include "asterisk/http_websocket.h"
  27. #include "internal.h"
  28. /*! \brief Locking container for safe configuration access. */
  29. static AO2_GLOBAL_OBJ_STATIC(confs);
  30. /*! \brief Mapping of the ARI conf struct's globals to the
  31. * general context in the config file. */
  32. static struct aco_type general_option = {
  33. .type = ACO_GLOBAL,
  34. .name = "general",
  35. .item_offset = offsetof(struct ast_ari_conf, general),
  36. .category = "general",
  37. .category_match = ACO_WHITELIST_EXACT,
  38. };
  39. static struct aco_type *general_options[] = ACO_TYPES(&general_option);
  40. /*! \brief Encoding format handler converts from boolean to enum. */
  41. static int encoding_format_handler(const struct aco_option *opt,
  42. struct ast_variable *var, void *obj)
  43. {
  44. struct ast_ari_conf_general *general = obj;
  45. if (!strcasecmp(var->name, "pretty")) {
  46. general->format = ast_true(var->value) ?
  47. AST_JSON_PRETTY : AST_JSON_COMPACT;
  48. } else {
  49. return -1;
  50. }
  51. return 0;
  52. }
  53. /*! \brief Parses the ast_ari_password_format enum from a config file */
  54. static int password_format_handler(const struct aco_option *opt,
  55. struct ast_variable *var, void *obj)
  56. {
  57. struct ast_ari_conf_user *user = obj;
  58. if (strcasecmp(var->value, "plain") == 0) {
  59. user->password_format = ARI_PASSWORD_FORMAT_PLAIN;
  60. } else if (strcasecmp(var->value, "crypt") == 0) {
  61. user->password_format = ARI_PASSWORD_FORMAT_CRYPT;
  62. } else {
  63. return -1;
  64. }
  65. return 0;
  66. }
  67. /*! \brief Destructor for \ref ast_ari_conf_user */
  68. static void user_dtor(void *obj)
  69. {
  70. struct ast_ari_conf_user *user = obj;
  71. ast_debug(3, "Disposing of user %s\n", user->username);
  72. ast_free(user->username);
  73. }
  74. /*! \brief Allocate an \ref ast_ari_conf_user for config parsing */
  75. static void *user_alloc(const char *cat)
  76. {
  77. RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup);
  78. if (!cat) {
  79. return NULL;
  80. }
  81. ast_debug(3, "Allocating user %s\n", cat);
  82. user = ao2_alloc_options(sizeof(*user), user_dtor,
  83. AO2_ALLOC_OPT_LOCK_NOLOCK);
  84. if (!user) {
  85. return NULL;
  86. }
  87. user->username = ast_strdup(cat);
  88. if (!user->username) {
  89. return NULL;
  90. }
  91. ao2_ref(user, +1);
  92. return user;
  93. }
  94. /*! \brief Sorting function for use with red/black tree */
  95. static int user_sort_cmp(const void *obj_left, const void *obj_right, int flags)
  96. {
  97. const struct ast_ari_conf_user *user_left = obj_left;
  98. const struct ast_ari_conf_user *user_right = obj_right;
  99. const char *key_right = obj_right;
  100. int cmp;
  101. switch (flags & OBJ_SEARCH_MASK) {
  102. case OBJ_SEARCH_OBJECT:
  103. key_right = user_right->username;
  104. /* Fall through */
  105. case OBJ_SEARCH_KEY:
  106. cmp = strcasecmp(user_left->username, key_right);
  107. break;
  108. case OBJ_SEARCH_PARTIAL_KEY:
  109. /*
  110. * We could also use a partial key struct containing a length
  111. * so strlen() does not get called for every comparison instead.
  112. */
  113. cmp = strncasecmp(user_left->username, key_right, strlen(key_right));
  114. break;
  115. default:
  116. /* Sort can only work on something with a full or partial key. */
  117. ast_assert(0);
  118. cmp = 0;
  119. break;
  120. }
  121. return cmp;
  122. }
  123. /*! \brief \ref aco_type item_find function */
  124. static void *user_find(struct ao2_container *tmp_container, const char *cat)
  125. {
  126. if (!cat) {
  127. return NULL;
  128. }
  129. return ao2_find(tmp_container, cat, OBJ_SEARCH_KEY);
  130. }
  131. static struct aco_type user_option = {
  132. .type = ACO_ITEM,
  133. .name = "user",
  134. .category_match = ACO_BLACKLIST_EXACT,
  135. .category = "general",
  136. .matchfield = "type",
  137. .matchvalue = "user",
  138. .item_alloc = user_alloc,
  139. .item_find = user_find,
  140. .item_offset = offsetof(struct ast_ari_conf, users),
  141. };
  142. static struct aco_type *user[] = ACO_TYPES(&user_option);
  143. static void conf_general_dtor(void *obj)
  144. {
  145. struct ast_ari_conf_general *general = obj;
  146. ast_string_field_free_memory(general);
  147. }
  148. /*! \brief \ref ast_ari_conf destructor. */
  149. static void conf_destructor(void *obj)
  150. {
  151. struct ast_ari_conf *cfg = obj;
  152. ao2_cleanup(cfg->general);
  153. ao2_cleanup(cfg->users);
  154. }
  155. /*! \brief Allocate an \ref ast_ari_conf for config parsing */
  156. static void *conf_alloc(void)
  157. {
  158. struct ast_ari_conf *cfg;
  159. cfg = ao2_alloc_options(sizeof(*cfg), conf_destructor,
  160. AO2_ALLOC_OPT_LOCK_NOLOCK);
  161. if (!cfg) {
  162. return NULL;
  163. }
  164. cfg->general = ao2_alloc_options(sizeof(*cfg->general), conf_general_dtor,
  165. AO2_ALLOC_OPT_LOCK_NOLOCK);
  166. cfg->users = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
  167. AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, user_sort_cmp, NULL);
  168. if (!cfg->users
  169. || !cfg->general
  170. || ast_string_field_init(cfg->general, 64)
  171. || aco_set_defaults(&general_option, "general", cfg->general)) {
  172. ao2_ref(cfg, -1);
  173. return NULL;
  174. }
  175. return cfg;
  176. }
  177. #define CONF_FILENAME "ari.conf"
  178. /*! \brief The conf file that's processed for the module. */
  179. static struct aco_file conf_file = {
  180. /*! The config file name. */
  181. .filename = CONF_FILENAME,
  182. /*! The mapping object types to be processed. */
  183. .types = ACO_TYPES(&general_option, &user_option),
  184. };
  185. CONFIG_INFO_STANDARD(cfg_info, confs, conf_alloc,
  186. .files = ACO_FILES(&conf_file));
  187. struct ast_ari_conf *ast_ari_config_get(void)
  188. {
  189. struct ast_ari_conf *res = ao2_global_obj_ref(confs);
  190. if (!res) {
  191. ast_log(LOG_ERROR,
  192. "Error obtaining config from " CONF_FILENAME "\n");
  193. }
  194. return res;
  195. }
  196. struct ast_ari_conf_user *ast_ari_config_validate_user(const char *username,
  197. const char *password)
  198. {
  199. RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup);
  200. RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup);
  201. int is_valid = 0;
  202. conf = ast_ari_config_get();
  203. if (!conf) {
  204. return NULL;
  205. }
  206. user = ao2_find(conf->users, username, OBJ_SEARCH_KEY);
  207. if (!user) {
  208. return NULL;
  209. }
  210. if (ast_strlen_zero(user->password)) {
  211. ast_log(LOG_WARNING,
  212. "User '%s' missing password; authentication failed\n",
  213. user->username);
  214. return NULL;
  215. }
  216. switch (user->password_format) {
  217. case ARI_PASSWORD_FORMAT_PLAIN:
  218. is_valid = strcmp(password, user->password) == 0;
  219. break;
  220. case ARI_PASSWORD_FORMAT_CRYPT:
  221. is_valid = ast_crypt_validate(password, user->password);
  222. break;
  223. }
  224. if (!is_valid) {
  225. return NULL;
  226. }
  227. ao2_ref(user, +1);
  228. return user;
  229. }
  230. /*! \brief Callback to validate a user object */
  231. static int validate_user_cb(void *obj, void *arg, int flags)
  232. {
  233. struct ast_ari_conf_user *user = obj;
  234. if (ast_strlen_zero(user->password)) {
  235. ast_log(LOG_WARNING, "User '%s' missing password\n",
  236. user->username);
  237. }
  238. return 0;
  239. }
  240. /*! \brief Load (or reload) configuration. */
  241. static int process_config(int reload)
  242. {
  243. RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup);
  244. switch (aco_process_config(&cfg_info, reload)) {
  245. case ACO_PROCESS_ERROR:
  246. return -1;
  247. case ACO_PROCESS_OK:
  248. case ACO_PROCESS_UNCHANGED:
  249. break;
  250. }
  251. conf = ast_ari_config_get();
  252. if (!conf) {
  253. ast_assert(0); /* We just configured; it should be there */
  254. return -1;
  255. }
  256. if (conf->general->enabled) {
  257. if (ao2_container_count(conf->users) == 0) {
  258. ast_log(LOG_ERROR, "No configured users for ARI\n");
  259. } else {
  260. ao2_callback(conf->users, OBJ_NODATA, validate_user_cb, NULL);
  261. }
  262. }
  263. return 0;
  264. }
  265. int ast_ari_config_init(void)
  266. {
  267. if (aco_info_init(&cfg_info)) {
  268. aco_info_destroy(&cfg_info);
  269. return -1;
  270. }
  271. /* ARI general category options */
  272. aco_option_register(&cfg_info, "enabled", ACO_EXACT, general_options,
  273. "yes", OPT_BOOL_T, 1,
  274. FLDSET(struct ast_ari_conf_general, enabled));
  275. aco_option_register_custom(&cfg_info, "pretty", ACO_EXACT,
  276. general_options, "no", encoding_format_handler, 0);
  277. aco_option_register(&cfg_info, "auth_realm", ACO_EXACT, general_options,
  278. "Asterisk REST Interface", OPT_CHAR_ARRAY_T, 0,
  279. FLDSET(struct ast_ari_conf_general, auth_realm),
  280. ARI_AUTH_REALM_LEN);
  281. aco_option_register(&cfg_info, "allowed_origins", ACO_EXACT, general_options,
  282. "", OPT_STRINGFIELD_T, 0,
  283. STRFLDSET(struct ast_ari_conf_general, allowed_origins));
  284. aco_option_register(&cfg_info, "websocket_write_timeout", ACO_EXACT, general_options,
  285. AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR, OPT_INT_T, PARSE_IN_RANGE,
  286. FLDSET(struct ast_ari_conf_general, write_timeout), 1, INT_MAX);
  287. /* ARI type=user category options */
  288. aco_option_register(&cfg_info, "type", ACO_EXACT, user, NULL,
  289. OPT_NOOP_T, 0, 0);
  290. aco_option_register(&cfg_info, "read_only", ACO_EXACT, user,
  291. "no", OPT_BOOL_T, 1,
  292. FLDSET(struct ast_ari_conf_user, read_only));
  293. aco_option_register(&cfg_info, "password", ACO_EXACT, user,
  294. "", OPT_CHAR_ARRAY_T, 0,
  295. FLDSET(struct ast_ari_conf_user, password), ARI_PASSWORD_LEN);
  296. aco_option_register_custom(&cfg_info, "password_format", ACO_EXACT,
  297. user, "plain", password_format_handler, 0);
  298. return process_config(0);
  299. }
  300. int ast_ari_config_reload(void)
  301. {
  302. return process_config(1);
  303. }
  304. void ast_ari_config_destroy(void)
  305. {
  306. aco_info_destroy(&cfg_info);
  307. ao2_global_obj_release(confs);
  308. }