res/ari/config.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2013, Digium, Inc.
00005  *
00006  * David M. Lee, II <dlee@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Config framework stuffz for ARI.
00022  * \author David M. Lee, II <dlee@digium.com>
00023  */
00024 
00025 #include "asterisk.h"
00026 
00027 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 417317 $")
00028 
00029 #include "asterisk/config_options.h"
00030 #include "asterisk/http_websocket.h"
00031 #include "internal.h"
00032 
00033 /*! \brief Locking container for safe configuration access. */
00034 static AO2_GLOBAL_OBJ_STATIC(confs);
00035 
00036 /*! \brief Mapping of the ARI conf struct's globals to the
00037  *         general context in the config file. */
00038 static struct aco_type general_option = {
00039    .type = ACO_GLOBAL,
00040    .name = "general",
00041    .item_offset = offsetof(struct ast_ari_conf, general),
00042    .category = "^general$",
00043    .category_match = ACO_WHITELIST,
00044 };
00045 
00046 static struct aco_type *general_options[] = ACO_TYPES(&general_option);
00047 
00048 /*! \brief Encoding format handler converts from boolean to enum. */
00049 static int encoding_format_handler(const struct aco_option *opt,
00050    struct ast_variable *var, void *obj)
00051 {
00052    struct ast_ari_conf_general *general = obj;
00053 
00054    if (!strcasecmp(var->name, "pretty")) {
00055       general->format = ast_true(var->value) ?
00056          AST_JSON_PRETTY : AST_JSON_COMPACT;
00057    } else {
00058       return -1;
00059    }
00060 
00061    return 0;
00062 }
00063 
00064 /*! \brief Parses the ast_ari_password_format enum from a config file */
00065 static int password_format_handler(const struct aco_option *opt,
00066    struct ast_variable *var, void *obj)
00067 {
00068    struct ast_ari_conf_user *user = obj;
00069 
00070    if (strcasecmp(var->value, "plain") == 0) {
00071       user->password_format = ARI_PASSWORD_FORMAT_PLAIN;
00072    } else if (strcasecmp(var->value, "crypt") == 0) {
00073       user->password_format = ARI_PASSWORD_FORMAT_CRYPT;
00074    } else {
00075       return -1;
00076    }
00077 
00078    return 0;
00079 }
00080 
00081 /*! \brief Destructor for \ref ast_ari_conf_user */
00082 static void user_dtor(void *obj)
00083 {
00084    struct ast_ari_conf_user *user = obj;
00085    ast_debug(3, "Disposing of user %s\n", user->username);
00086    ast_free(user->username);
00087 }
00088 
00089 /*! \brief Allocate an \ref ast_ari_conf_user for config parsing */
00090 static void *user_alloc(const char *cat)
00091 {
00092    RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup);
00093 
00094    if (!cat) {
00095       return NULL;
00096    }
00097 
00098    ast_debug(3, "Allocating user %s\n", cat);
00099 
00100    user = ao2_alloc_options(sizeof(*user), user_dtor,
00101       AO2_ALLOC_OPT_LOCK_NOLOCK);
00102    if (!user) {
00103       return NULL;
00104    }
00105 
00106    user->username = ast_strdup(cat);
00107    if (!user->username) {
00108       return NULL;
00109    }
00110 
00111    ao2_ref(user, +1);
00112    return user;
00113 }
00114 
00115 /*! \brief Sorting function for use with red/black tree */
00116 static int user_sort_cmp(const void *obj_left, const void *obj_right, int flags)
00117 {
00118    const struct ast_ari_conf_user *user_left = obj_left;
00119 
00120    if (flags & OBJ_PARTIAL_KEY) {
00121       const char *key_right = obj_right;
00122       return strncasecmp(user_left->username, key_right,
00123          strlen(key_right));
00124    } else if (flags & OBJ_KEY) {
00125       const char *key_right = obj_right;
00126       return strcasecmp(user_left->username, key_right);
00127    } else {
00128       const struct ast_ari_conf_user *user_right = obj_right;
00129       const char *key_right = user_right->username;
00130       return strcasecmp(user_left->username, key_right);
00131    }
00132 }
00133 
00134 /*! \brief \ref aco_type item_find function */
00135 static void *user_find(struct ao2_container *tmp_container, const char *cat)
00136 {
00137    if (!cat) {
00138       return NULL;
00139    }
00140 
00141    return ao2_find(tmp_container, cat, OBJ_KEY);
00142 }
00143 
00144 static struct aco_type user_option = {
00145    .type = ACO_ITEM,
00146    .name = "user",
00147    .category_match = ACO_BLACKLIST,
00148    .category = "^general$",
00149    .matchfield = "type",
00150    .matchvalue = "user",
00151    .item_alloc = user_alloc,
00152    .item_find = user_find,
00153    .item_offset = offsetof(struct ast_ari_conf, users),
00154 };
00155 
00156 static struct aco_type *user[] = ACO_TYPES(&user_option);
00157 
00158 /*! \brief \ref ast_ari_conf destructor. */
00159 static void conf_destructor(void *obj)
00160 {
00161    struct ast_ari_conf *cfg = obj;
00162 
00163    ast_string_field_free_memory(cfg->general);
00164 
00165    ao2_cleanup(cfg->general);
00166    ao2_cleanup(cfg->users);
00167 }
00168 
00169 /*! \brief Allocate an \ref ast_ari_conf for config parsing */
00170 static void *conf_alloc(void)
00171 {
00172    RAII_VAR(struct ast_ari_conf *, cfg, NULL, ao2_cleanup);
00173 
00174    cfg = ao2_alloc_options(sizeof(*cfg), conf_destructor,
00175       AO2_ALLOC_OPT_LOCK_NOLOCK);
00176    if (!cfg) {
00177       return NULL;
00178    }
00179 
00180    cfg->general = ao2_alloc_options(sizeof(*cfg->general), NULL,
00181       AO2_ALLOC_OPT_LOCK_NOLOCK);
00182    if (!cfg->general) {
00183       return NULL;
00184    }
00185    aco_set_defaults(&general_option, "general", cfg->general);
00186 
00187    if (ast_string_field_init(cfg->general, 64)) {
00188       return NULL;
00189    }
00190 
00191    cfg->users = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
00192       AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, user_sort_cmp, NULL);
00193 
00194    ao2_ref(cfg, +1);
00195    return cfg;
00196 }
00197 
00198 #define CONF_FILENAME "ari.conf"
00199 
00200 /*! \brief The conf file that's processed for the module. */
00201 static struct aco_file conf_file = {
00202    /*! The config file name. */
00203    .filename = CONF_FILENAME,
00204    /*! The mapping object types to be processed. */
00205    .types = ACO_TYPES(&general_option, &user_option),
00206 };
00207 
00208 CONFIG_INFO_STANDARD(cfg_info, confs, conf_alloc,
00209            .files = ACO_FILES(&conf_file));
00210 
00211 struct ast_ari_conf *ast_ari_config_get(void)
00212 {
00213    struct ast_ari_conf *res = ao2_global_obj_ref(confs);
00214    if (!res) {
00215       ast_log(LOG_ERROR,
00216          "Error obtaining config from " CONF_FILENAME "\n");
00217    }
00218    return res;
00219 }
00220 
00221 struct ast_ari_conf_user *ast_ari_config_validate_user(const char *username,
00222    const char *password)
00223 {
00224    RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup);
00225    RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup);
00226    int is_valid = 0;
00227 
00228    conf = ast_ari_config_get();
00229    if (!conf) {
00230       return NULL;
00231    }
00232 
00233    user = ao2_find(conf->users, username, OBJ_KEY);
00234    if (!user) {
00235       return NULL;
00236    }
00237 
00238    if (ast_strlen_zero(user->password)) {
00239       ast_log(LOG_WARNING,
00240          "User '%s' missing password; authentication failed\n",
00241          user->username);
00242       return NULL;
00243    }
00244 
00245    switch (user->password_format) {
00246    case ARI_PASSWORD_FORMAT_PLAIN:
00247       is_valid = strcmp(password, user->password) == 0;
00248       break;
00249    case ARI_PASSWORD_FORMAT_CRYPT:
00250       is_valid = ast_crypt_validate(password, user->password);
00251       break;
00252    }
00253 
00254    if (!is_valid) {
00255       return NULL;
00256    }
00257 
00258    ao2_ref(user, +1);
00259    return user;
00260 }
00261 
00262 /*! \brief Callback to validate a user object */
00263 static int validate_user_cb(void *obj, void *arg, int flags)
00264 {
00265    struct ast_ari_conf_user *user = obj;
00266 
00267    if (ast_strlen_zero(user->password)) {
00268       ast_log(LOG_WARNING, "User '%s' missing password\n",
00269          user->username);
00270    }
00271 
00272    return 0;
00273 }
00274 
00275 /*! \brief Load (or reload) configuration. */
00276 static int process_config(int reload)
00277 {
00278    RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup);
00279 
00280    switch (aco_process_config(&cfg_info, reload)) {
00281    case ACO_PROCESS_ERROR:
00282       return -1;
00283    case ACO_PROCESS_OK:
00284    case ACO_PROCESS_UNCHANGED:
00285       break;
00286    }
00287 
00288    conf = ast_ari_config_get();
00289    if (!conf) {
00290       ast_assert(0); /* We just configured; it should be there */
00291       return -1;
00292    }
00293 
00294    if (conf->general->enabled) {
00295       if (ao2_container_count(conf->users) == 0) {
00296          ast_log(LOG_ERROR, "No configured users for ARI\n");
00297       } else {
00298          ao2_callback(conf->users, OBJ_NODATA, validate_user_cb, NULL);
00299       }
00300    }
00301 
00302    return 0;
00303 }
00304 
00305 int ast_ari_config_init(void)
00306 {
00307    if (aco_info_init(&cfg_info)) {
00308       aco_info_destroy(&cfg_info);
00309       return -1;
00310    }
00311 
00312    aco_option_register(&cfg_info, "enabled", ACO_EXACT, general_options,
00313       "yes", OPT_BOOL_T, 1,
00314       FLDSET(struct ast_ari_conf_general, enabled));
00315    aco_option_register_custom(&cfg_info, "pretty", ACO_EXACT,
00316       general_options, "no",  encoding_format_handler, 0);
00317    aco_option_register(&cfg_info, "auth_realm", ACO_EXACT, general_options,
00318       "Asterisk REST Interface", OPT_CHAR_ARRAY_T, 0,
00319       FLDSET(struct ast_ari_conf_general, auth_realm),
00320       ARI_AUTH_REALM_LEN);
00321    aco_option_register(&cfg_info, "allowed_origins", ACO_EXACT, general_options,
00322       "", OPT_STRINGFIELD_T, 0,
00323       STRFLDSET(struct ast_ari_conf_general, allowed_origins));
00324    aco_option_register(&cfg_info, "websocket_write_timeout", ACO_EXACT, general_options,
00325       AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR, OPT_INT_T, PARSE_IN_RANGE,
00326       FLDSET(struct ast_ari_conf_general, write_timeout), 1, INT_MAX);
00327 
00328    aco_option_register(&cfg_info, "type", ACO_EXACT, user, NULL,
00329       OPT_NOOP_T, 0, 0);
00330    aco_option_register(&cfg_info, "read_only", ACO_EXACT, user,
00331       "no", OPT_BOOL_T, 1,
00332       FLDSET(struct ast_ari_conf_user, read_only));
00333    aco_option_register(&cfg_info, "password", ACO_EXACT, user,
00334       "", OPT_CHAR_ARRAY_T, 0,
00335       FLDSET(struct ast_ari_conf_user, password), ARI_PASSWORD_LEN);
00336    aco_option_register_custom(&cfg_info, "password_format", ACO_EXACT,
00337       user, "plain",  password_format_handler, 0);
00338 
00339    return process_config(0);
00340 }
00341 
00342 int ast_ari_config_reload(void)
00343 {
00344    return process_config(1);
00345 }
00346 
00347 void ast_ari_config_destroy(void)
00348 {
00349    aco_info_destroy(&cfg_info);
00350    ao2_global_obj_release(confs);
00351 }

Generated on Thu Apr 16 06:27:31 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6