Skip to content
Snippets Groups Projects
manager.c 282 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	cfg = ast_config_load2("manager.conf", "manager", config_flags);
    	if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
    		return 0;
    	} else if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
    		ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf, or configuration is invalid.\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    	/* If this wasn't performed due to a forced reload (because those can be created by ACL change events, we need to unsubscribe to ACL change events. */
    	if (!by_external_config) {
    
    	if (reload) {
    		/* Reset all settings before reloading configuration */
    		tls_was_enabled = ami_tls_cfg.enabled;
    		manager_set_defaults();
    	}
    
    	ast_sockaddr_parse(&ami_desc_local_address_tmp, "[::]", 0);
    	ast_sockaddr_set_port(&ami_desc_local_address_tmp, DEFAULT_MANAGER_PORT);
    
    	for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
    		val = var->value;
    
    		/* read tls config options while preventing unsupported options from being set */
    		if (strcasecmp(var->name, "tlscafile")
    			&& strcasecmp(var->name, "tlscapath")
    			&& strcasecmp(var->name, "tlscadir")
    			&& strcasecmp(var->name, "tlsverifyclient")
    			&& strcasecmp(var->name, "tlsdontverifyserver")
    			&& strcasecmp(var->name, "tlsclientmethod")
    			&& strcasecmp(var->name, "sslclientmethod")
    			&& !ast_tls_read_conf(&ami_tls_cfg, &amis_desc, var->name, val)) {
    
    			continue;
    		}
    
    		if (!strcasecmp(var->name, "enabled")) {
    
    			manager_enabled = ast_true(val);
    
    		} else if (!strcasecmp(var->name, "webenabled")) {
    
    			webmanager_enabled = ast_true(val);
    
    		} else if (!strcasecmp(var->name, "port")) {
    
    			int bindport;
    			if (ast_parse_arg(val, PARSE_UINT32|PARSE_IN_RANGE, &bindport, 1024, 65535)) {
    				ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
    			}
    			ast_sockaddr_set_port(&ami_desc_local_address_tmp, bindport);
    
    		} else if (!strcasecmp(var->name, "bindaddr")) {
    
    			/* remember port if it has already been set */
    			int setport = ast_sockaddr_port(&ami_desc_local_address_tmp);
    
    			if (ast_parse_arg(val, PARSE_ADDR|PARSE_PORT_IGNORE, NULL)) {
    				ast_log(LOG_WARNING, "Invalid address '%s' specified, default '%s' will be used\n", val,
    						ast_sockaddr_stringify_addr(&ami_desc_local_address_tmp));
    			} else {
    				ast_sockaddr_parse(&ami_desc_local_address_tmp, val, PARSE_PORT_IGNORE);
    
    
    			if (setport) {
    				ast_sockaddr_set_port(&ami_desc_local_address_tmp, setport);
    			}
    
    
    		} else if (!strcasecmp(var->name, "brokeneventsaction")) {
    			broken_events_action = ast_true(val);
    
    		} else if (!strcasecmp(var->name, "allowmultiplelogin")) {
    
    		} else if (!strcasecmp(var->name, "displayconnects")) {
    			displayconnects = ast_true(val);
    		} else if (!strcasecmp(var->name, "timestampevents")) {
    			timestampevents = ast_true(val);
    		} else if (!strcasecmp(var->name, "debug")) {
    			manager_debug = ast_true(val);
    		} else if (!strcasecmp(var->name, "httptimeout")) {
    			newhttptimeout = atoi(val);
    
    		} else if (!strcasecmp(var->name, "authtimeout")) {
    			int timeout = atoi(var->value);
    
    			if (timeout < 1) {
    				ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", var->value);
    			} else {
    				authtimeout = timeout;
    			}
    		} else if (!strcasecmp(var->name, "authlimit")) {
    			int limit = atoi(var->value);
    
    			if (limit < 1) {
    				ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", var->value);
    			} else {
    				authlimit = limit;
    			}
    
    		} else if (!strcasecmp(var->name, "channelvars")) {
    
    			load_channelvars(var);
    
    			ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
    				var->name, val);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (manager_enabled && !subscribed) {
    		if (subscribe_all() != 0) {
    			ast_log(LOG_ERROR, "Manager subscription error\n");
    			return -1;
    		}
    	}
    
    
    	ast_sockaddr_copy(&amis_desc_local_address_tmp, &amis_desc.local_address);
    
    	/* if the amis address has not been set, default is the same as non secure ami */
    
    	if (ast_sockaddr_isnull(&amis_desc_local_address_tmp)) {
    		ast_sockaddr_copy(&amis_desc_local_address_tmp, &ami_desc_local_address_tmp);
    
    	/* if the amis address was not set, it will have non-secure ami port set; if
    	   amis address was set, we need to check that a port was set or not, if not
    	   use the default tls port */
    	if (ast_sockaddr_port(&amis_desc_local_address_tmp) == 0 ||
    			(ast_sockaddr_port(&ami_desc_local_address_tmp) == ast_sockaddr_port(&amis_desc_local_address_tmp))) {
    
    		ast_sockaddr_set_port(&amis_desc_local_address_tmp, DEFAULT_MANAGER_TLS_PORT);
    
    	if (manager_enabled) {
    
    		ast_sockaddr_copy(&ami_desc.local_address, &ami_desc_local_address_tmp);
    		ast_sockaddr_copy(&amis_desc.local_address, &amis_desc_local_address_tmp);
    
    	AST_RWLIST_WRLOCK(&users);
    
    	ucfg = ast_config_load2("users.conf", "manager", config_flags);
    
    	if (ucfg && (ucfg != CONFIG_STATUS_FILEUNCHANGED) && ucfg != CONFIG_STATUS_FILEINVALID) {
    
    		const char *hasmanager;
    		int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager"));
    
    		while ((cat = ast_category_browse(ucfg, cat))) {
    
    			if (!strcasecmp(cat, "general")) {
    
    			hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager");
    			if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) {
    				const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret");
    				const char *user_read = ast_variable_retrieve(ucfg, cat, "read");
    				const char *user_write = ast_variable_retrieve(ucfg, cat, "write");
    				const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects");
    
    				const char *user_allowmultiplelogin = ast_variable_retrieve(ucfg, cat, "allowmultiplelogin");
    
    				const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout");
    
    				/* Look for an existing entry,
    				 * if none found - create one and add it to the list
    				 */
    				if (!(user = get_manager_by_name_locked(cat))) {
    
    					if (!(user = ast_calloc(1, sizeof(*user)))) {
    
    
    					/* Copy name over */
    					ast_copy_string(user->username, cat, sizeof(user->username));
    					/* Insert into list */
    					AST_LIST_INSERT_TAIL(&users, user, list);
    
    					user->readperm = -1;
    					user->writeperm = -1;
    					/* Default displayconnect from [general] */
    					user->displayconnects = displayconnects;
    
    					/* Default allowmultiplelogin from [general] */
    					user->allowmultiplelogin = allowmultiplelogin;
    
    					user_secret = ast_variable_retrieve(ucfg, "general", "secret");
    
    					user_read = ast_variable_retrieve(ucfg, "general", "read");
    
    					user_write = ast_variable_retrieve(ucfg, "general", "write");
    
    					user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects");
    
    				if (!user_allowmultiplelogin) {
    					user_allowmultiplelogin = ast_variable_retrieve(ucfg, "general", "allowmultiplelogin");
    				}
    
    					user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout");
    
    					ast_free(user->secret);
    
    					user->displayconnects = ast_true(user_displayconnects);
    
    				if (user_allowmultiplelogin) {
    					user->allowmultiplelogin = ast_true(user_allowmultiplelogin);
    				}
    
    					int value = atoi(user_writetimeout);
    
    						ast_log(LOG_WARNING, "Invalid writetimeout value '%d' in users.conf\n", value);
    
    	while ((cat = ast_category_browse(cfg, cat))) {
    
    		struct ast_acl_list *oldacl;
    
    		if (!strcasecmp(cat, "general")) {
    
    
    		/* Look for an existing entry, if none found - create one and add it to the list */
    
    		if (!(user = get_manager_by_name_locked(cat))) {
    
    			if (!(user = ast_calloc(1, sizeof(*user)))) {
    
    			/* Copy name over */
    			ast_copy_string(user->username, cat, sizeof(user->username));
    
    			user->readperm = 0;
    			user->writeperm = 0;
    			/* Default displayconnect from [general] */
    			user->displayconnects = displayconnects;
    
    			/* Default allowmultiplelogin from [general] */
    			user->allowmultiplelogin = allowmultiplelogin;
    
    			user->whitefilters = ao2_container_alloc(1, NULL, NULL);
    			user->blackfilters = ao2_container_alloc(1, NULL, NULL);
    
    			if (!user->whitefilters || !user->blackfilters) {
    				manager_free_user(user);
    				break;
    			}
    
    			/* Insert into list */
    
    			AST_RWLIST_INSERT_TAIL(&users, user, list);
    
    		} else {
    			ao2_t_callback(user->whitefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all white filters");
    			ao2_t_callback(user->blackfilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all black filters");
    
    		}
    
    		/* Make sure we keep this user and don't destroy it during cleanup */
    		user->keep = 1;
    
    		oldacl = user->acl;
    		user->acl = NULL;
    
    		ast_variables_destroy(user->chanvars);
    
    
    		var = ast_variable_browse(cfg, cat);
    
    			if (!strcasecmp(var->name, "secret")) {
    
    				ast_free(user->secret);
    
    				user->secret = ast_strdup(var->value);
    
    			} else if (!strcasecmp(var->name, "deny") ||
    
    				       !strcasecmp(var->name, "permit") ||
    				       !strcasecmp(var->name, "acl")) {
    				ast_append_acl(var->name, var->value, &user->acl, NULL, &acl_subscription_flag);
    
    			}  else if (!strcasecmp(var->name, "read") ) {
    
    			}  else if (!strcasecmp(var->name, "write") ) {
    
    				user->writeperm = get_perm(var->value);
    			}  else if (!strcasecmp(var->name, "displayconnects") ) {
    
    				user->displayconnects = ast_true(var->value);
    
    			}  else if (!strcasecmp(var->name, "allowmultiplelogin") ) {
    				user->allowmultiplelogin = ast_true(var->value);
    
    			} else if (!strcasecmp(var->name, "writetimeout")) {
    
    					ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
    
    			} else if (!strcasecmp(var->name, "setvar")) {
    				struct ast_variable *tmpvar;
    				char varbuf[256];
    				char *varval;
    				char *varname;
    
    				ast_copy_string(varbuf, var->value, sizeof(varbuf));
    				varname = varbuf;
    
    				if ((varval = strchr(varname,'='))) {
    					*varval++ = '\0';
    					if ((tmpvar = ast_variable_new(varname, varval, ""))) {
    						tmpvar->next = user->chanvars;
    						user->chanvars = tmpvar;
    					}
    				}
    
    			} else if (!strcasecmp(var->name, "eventfilter")) {
    				const char *value = var->value;
    
    				manager_add_filter(value, user->whitefilters, user->blackfilters);
    
    				ast_debug(1, "%s is an unknown option.\n", var->name);
    
    
    		oldacl = ast_free_acl_list(oldacl);
    
    	/* Check the flag for named ACL event subscription and if we need to, register a subscription. */
    	if (acl_subscription_flag && !by_external_config) {
    
    	/* Perform cleanup - essentially prune out old users that no longer exist */
    
    	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
    
    		if (user->keep) {	/* valid record. clear flag for the next round */
    
    			user->keep = 0;
    
    
    			/* Calculate A1 for Digest auth */
    			snprintf(a1, sizeof(a1), "%s:%s:%s", user->username, global_realm, user->secret);
    			ast_md5_hash(a1_hash,a1);
    
    			ast_free(user->a1_hash);
    
    			user->a1_hash = ast_strdup(a1_hash);
    
    			continue;
    		}
    		/* We do not need to keep this user so take them out of the list */
    
    		AST_RWLIST_REMOVE_CURRENT(list);
    
    		ast_debug(4, "Pruning user '%s'\n", user->username);
    
    		manager_free_user(user);
    
    	AST_RWLIST_TRAVERSE_SAFE_END;
    
    	AST_RWLIST_UNLOCK(&users);
    
    	if (webmanager_enabled && manager_enabled) {
    
    		if (!webregged) {
    			ast_http_uri_link(&rawmanuri);
    			ast_http_uri_link(&manageruri);
    			ast_http_uri_link(&managerxmluri);
    
    
    			ast_http_uri_link(&arawmanuri);
    			ast_http_uri_link(&amanageruri);
    			ast_http_uri_link(&amanagerxmluri);
    
    			webregged = 1;
    		}
    	} else {
    		if (webregged) {
    			ast_http_uri_unlink(&rawmanuri);
    			ast_http_uri_unlink(&manageruri);
    			ast_http_uri_unlink(&managerxmluri);
    
    
    			ast_http_uri_unlink(&arawmanuri);
    			ast_http_uri_unlink(&amanageruri);
    			ast_http_uri_unlink(&amanagerxmluri);
    
    	ast_tcptls_server_start(&ami_desc);
    
    	if (tls_was_enabled && !ami_tls_cfg.enabled) {
    		ast_tcptls_server_stop(&amis_desc);
    	} else if (ast_ssl_setup(amis_desc.tls_cfg)) {
    
    		ast_tcptls_server_start(&amis_desc);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub,
    
    	struct stasis_message *message)
    
    	if (stasis_message_type(message) != ast_named_acl_change_type()) {
    		return;
    	}
    
    
    	/* For now, this is going to be performed simply and just execute a forced reload. */
    	ast_log(LOG_NOTICE, "Reloading manager in response to ACL change event.\n");
    	__init_manager(1, 1);
    }
    
    
    	return __init_manager(0, 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    int reload_manager(void)
    {
    
    	return __init_manager(1, 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    
    int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)
    {
    
    	AST_LIST_INSERT_HEAD(&s->session->datastores, datastore, entry);
    
    
    	return 0;
    }
    
    int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastore)
    {
    
    	return AST_LIST_REMOVE(&s->session->datastores, datastore, entry) ? 0 : -1;
    
    }
    
    struct ast_datastore *astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid)
    {
    	struct ast_datastore *datastore = NULL;
    
    	AST_LIST_TRAVERSE_SAFE_BEGIN(&s->session->datastores, datastore, entry) {
    
    		if (datastore->info != info) {
    			continue;
    		}
    
    		if (uid == NULL) {
    			/* matched by type only */
    			break;
    		}
    
    		if ((datastore->uid != NULL) && !strcasecmp(uid, datastore->uid)) {
    			/* Matched by type AND uid */
    			break;
    		}
    	}
    	AST_LIST_TRAVERSE_SAFE_END;
    
    	return datastore;
    }
    
    int ast_str_append_event_header(struct ast_str **fields_string,
    					const char *header, const char *value)
    {
    	struct ast_str *working_str = *fields_string;
    
    	if (!working_str) {
    		working_str = ast_str_create(128);
    		if (!working_str) {
    			return -1;
    		}
    		*fields_string = working_str;
    	}
    
    	ast_str_append(&working_str, 0,
    		"%s: %s\r\n",
    		header, value);
    
    	return 0;
    }
    
    
    static void manager_event_blob_dtor(void *obj)
    {
    	struct ast_manager_event_blob *ev = obj;
    
    	ast_string_field_free_memory(ev);
    }
    
    struct ast_manager_event_blob *
    __attribute__((format(printf, 3, 4)))
    ast_manager_event_blob_create(
    	int event_flags,
    	const char *manager_event,
    	const char *extra_fields_fmt,
    	...)
    {
    
    	struct ast_manager_event_blob *ev;
    
    	va_list argp;
    
    	ast_assert(extra_fields_fmt != NULL);
    	ast_assert(manager_event != NULL);
    
    
    	ev = ao2_alloc_options(sizeof(*ev), manager_event_blob_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
    
    	if (!ev) {
    		return NULL;
    	}
    
    	if (ast_string_field_init(ev, 20)) {
    
    		return NULL;
    	}
    
    	ev->manager_event = manager_event;
    	ev->event_flags = event_flags;
    
    	va_start(argp, extra_fields_fmt);
    
    	ast_string_field_ptr_build_va(ev, &ev->extra_fields, extra_fields_fmt, argp);