/*
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
** See the GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see .
**/
#include "nodecommand.h"
#include "zbxtrapper.h"
#include "zbxexpression.h"
#include "zbxscripts.h"
#include "audit/zbxaudit.h"
#include "zbxevent.h"
#include "zbxdbwrap.h"
#include "zbxregexp.h"
#include "zbxstr.h"
#include "zbx_trigger_constants.h"
#include "zbx_scripts_constants.h"
#include "zbx_host_constants.h"
#include "zbxalgo.h"
#include "zbxcacheconfig.h"
#include "zbxdb.h"
#include "zbxdbhigh.h"
#include "zbxexpr.h"
#include "zbxjson.h"
#include "zbxnum.h"
#include "zbxtime.h"
/**********************************************************************************
* *
* Purpose: replaces occurrence of macro in input string with value given in *
* macrovalue, with memory management *
* *
* Parameters: in - [IN] input string to be processed *
* macro - [IN] macro to replace *
* macrovalue - [IN] value to replace macro with *
* out - [IN/OUT] pointer to memory holding result *
* out_alloc - [IN/OUT] size of memory holding result *
* *
* Return value: SUCCEED - remote command was executed successfully *
* FAIL - error occurred *
* *
**********************************************************************************/
static void substitute_macro(const char *in, const char *macro, const char *macrovalue, char **out,
size_t *out_alloc)
{
zbx_token_t token;
int pos = 0;
size_t out_offset = 0, macrovalue_len;
macrovalue_len = strlen(macrovalue);
zbx_strcpy_alloc(out, out_alloc, &out_offset, in);
out_offset++;
for (; SUCCEED == zbx_token_find(*out, pos, &token, ZBX_TOKEN_SIMPLE_MACRO); pos++)
{
pos = token.loc.r;
if (0 == strncmp(*out + token.loc.l, macro, token.loc.r - token.loc.l + 1))
{
pos += zbx_replace_mem_dyn(out, out_alloc, &out_offset, token.loc.l,
token.loc.r - token.loc.l + 1, macrovalue, macrovalue_len);
}
}
}
/******************************************************************************
* *
* Purpose: executes remote command and waits for result *
* *
* Return value: SUCCEED - remote command was executed successfully *
* FAIL - error occurred *
* *
******************************************************************************/
static int execute_remote_script(const zbx_script_t *script, const zbx_dc_host_t *host, char **info, char *error,
size_t max_error_len)
{
zbx_uint64_t taskid;
zbx_db_result_t result = NULL;
zbx_db_row_t row;
if (0 == host->proxyid)
{
zbx_snprintf(error, max_error_len, "Host is monitored by proxy group, "
"but its proxy assignment is still pending.");
return FAIL;
}
if (0 == (taskid = zbx_script_create_task(script, host, 0, time(NULL))))
{
zbx_snprintf(error, max_error_len, "Cannot create remote command task.");
return FAIL;
}
for (int time_start = time(NULL); SEC_PER_MIN > time(NULL) - time_start; sleep(1))
{
result = zbx_db_select(
"select tr.status,tr.info"
" from task t"
" left join task_remote_command_result tr"
" on tr.taskid=t.taskid"
" where tr.parent_taskid=" ZBX_FS_UI64,
taskid);
if (NULL != (row = zbx_db_fetch(result)))
{
int ret;
if (SUCCEED == (ret = atoi(row[0])))
*info = zbx_strdup(*info, row[1]);
else
zbx_strlcpy(error, row[1], max_error_len);
zbx_db_free_result(result);
return ret;
}
zbx_db_free_result(result);
}
zbx_snprintf(error, max_error_len, "Timeout while waiting for remote command result.");
return FAIL;
}
static int zbx_get_script_details(zbx_uint64_t scriptid, zbx_script_t *script, int *scope, zbx_uint64_t *usrgrpid,
zbx_uint64_t *groupid, char *error, size_t error_len)
{
int ret = FAIL;
zbx_db_result_t db_result;
zbx_db_row_t row;
zbx_uint64_t usrgrpid_l, groupid_l;
db_result = zbx_db_select("select name,command,host_access,usrgrpid,groupid,type,execute_on,timeout,scope,port"
",authtype,username,password,publickey,privatekey"
",manualinput,manualinput_validator,manualinput_validator_type"
" from scripts"
" where scriptid=" ZBX_FS_UI64, scriptid);
if (NULL == db_result)
{
zbx_strlcpy(error, "Cannot select from table 'scripts'.", error_len);
return FAIL;
}
if (NULL == (row = zbx_db_fetch(db_result)))
{
zbx_strlcpy(error, "Script not found.", error_len);
goto fail;
}
ZBX_DBROW2UINT64(usrgrpid_l, row[3]);
*usrgrpid = usrgrpid_l;
ZBX_DBROW2UINT64(groupid_l, row[4]);
*groupid = groupid_l;
ZBX_STR2UCHAR(script->type, row[5]);
if (ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT == script->type)
ZBX_STR2UCHAR(script->execute_on, row[6]);
if (ZBX_SCRIPT_TYPE_SSH == script->type)
{
ZBX_STR2UCHAR(script->authtype, row[10]);
script->publickey = zbx_strdup(script->publickey, row[13]);
script->privatekey = zbx_strdup(script->privatekey, row[14]);
}
if (ZBX_SCRIPT_TYPE_SSH == script->type || ZBX_SCRIPT_TYPE_TELNET == script->type)
{
script->port = zbx_strdup(script->port, row[9]);
script->username = zbx_strdup(script->username, row[11]);
script->password = zbx_strdup(script->password, row[12]);
}
ZBX_STR2UCHAR(script->manualinput, row[15]);
if (ZBX_SCRIPT_MANUALINPUT_YES == script->manualinput)
{
script->manualinput_validator = zbx_strdup(script->manualinput_validator, row[16]);
ZBX_STR2UCHAR(script->manualinput_validator_type, row[17]);
}
script->name = zbx_strdup(script->name, row[0]);
script->command = zbx_strdup(script->command, row[1]);
script->command_orig = zbx_strdup(script->command_orig, row[1]);
script->scriptid = scriptid;
ZBX_STR2UCHAR(script->host_access, row[2]);
if (SUCCEED != zbx_is_time_suffix(row[7], &script->timeout, ZBX_LENGTH_UNLIMITED))
{
zbx_strlcpy(error, "Invalid timeout value in script configuration.", error_len);
goto fail;
}
*scope = atoi(row[8]);
ret = SUCCEED;
fail:
zbx_db_free_result(db_result);
return ret;
}
static int is_user_in_allowed_group(zbx_uint64_t userid, zbx_uint64_t usrgrpid, char *error, size_t error_len)
{
zbx_db_result_t result;
int ret = FAIL;
result = zbx_db_select("select null"
" from users_groups"
" where usrgrpid=" ZBX_FS_UI64
" and userid=" ZBX_FS_UI64,
usrgrpid, userid);
if (NULL == result)
{
zbx_strlcpy(error, "Database error, cannot get user rights.", error_len);
goto fail;
}
if (NULL == zbx_db_fetch(result))
zbx_strlcpy(error, "User has no rights to execute this script.", error_len);
else
ret = SUCCEED;
zbx_db_free_result(result);
fail:
return ret;
}
/******************************************************************************
* *
* Purpose: Checks if the specified event id corresponds to a problem event *
* caused by a trigger, finds its recovery event (if it exists). *
* *
* Parameters: eventid - [IN] *
* r_eventid - [OUT] id of recovery event (0 if there is no *
* recovery event) *
* error - [OUT] error message buffer *
* error_len - [IN] size of error message buffer *
* *
* Return value: SUCCEED or FAIL (with 'error' message) *
* *
******************************************************************************/
static int zbx_check_event_end_recovery_event(zbx_uint64_t eventid, zbx_uint64_t *r_eventid, char *error,
size_t error_len)
{
zbx_db_result_t db_result;
zbx_db_row_t row;
if (NULL == (db_result = zbx_db_select("select r_eventid from event_recovery where eventid=" ZBX_FS_UI64,
eventid)))
{
zbx_strlcpy(error, "Database error, cannot read from 'events' and 'event_recovery' tables.",
error_len);
return FAIL;
}
if (NULL == (row = zbx_db_fetch(db_result)))
*r_eventid = 0;
else
ZBX_DBROW2UINT64(*r_eventid, row[0]);
zbx_db_free_result(db_result);
return SUCCEED;
}
/******************************************************************************
* *
* Purpose: validates given user input with validator of given type *
* *
* Parameters: manualinput - [IN] user provided input string *
* validator - [IN] string containing validator *
* validator_type - [IN] indicator for how to interpret *
* validator string *
* *
* Return value: SUCCEED or FAIL *
* *
******************************************************************************/
static int validate_manualinput(const char *manualinput, const char *validator,
const unsigned char validator_type)
{
int ret = FAIL;
zabbix_log(LOG_LEVEL_DEBUG, "In %s() manualinput:%s, validator:%s, validator_type:%hhu",
__func__, manualinput, validator, validator_type);
switch (validator_type)
{
case ZBX_SCRIPT_MANUALINPUT_VALIDATOR_TYPE_REGEX:
ret = (NULL != zbx_regexp_match(manualinput, validator, NULL) ? SUCCEED : FAIL);
break;
case ZBX_SCRIPT_MANUALINPUT_VALIDATOR_TYPE_LIST:
ret = zbx_str_in_list(validator, manualinput, ',');
break;
default:
THIS_SHOULD_NEVER_HAPPEN;
}
zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
return ret;
}
/**************************************************************************************
* *
* Purpose: executes command *
* *
* Parameters: scriptid - [IN] id of script to be executed *
* hostid - [IN] host script will be executed on *
* eventid - [IN] *
* user - [IN] user who executes command *
* clientip - [IN] *
* manualinput - [IN] user provided value to script *
* config_timeout - [IN] *
* config_trapper_timeout - [IN] *
* config_source_ip - [IN] *
* config_ssh_key_location - [IN] *
* config_enable_global_scripts - [IN] *
* get_config_forks - [IN] *
* program_type - [IN] *
* result - [OUT] result of script execution *
* debug - [OUT] debug data (optional) *
* *
* Return value: SUCCEED - processed successfully *
* FAIL - error occurred *
* *
* Comments: either 'hostid' or 'eventid' must be > 0, but not both *
* *
**************************************************************************************/
static int execute_script(zbx_uint64_t scriptid, zbx_uint64_t hostid, zbx_uint64_t eventid, zbx_user_t *user,
const char *clientip, const char *manualinput, int config_timeout, int config_trapper_timeout,
const char *config_source_ip, const char *config_ssh_key_location, int config_enable_global_scripts,
zbx_get_config_forks_f get_config_forks, unsigned char program_type,
char **result, char **debug)
{
int ret = FAIL, scope = 0, macro_type;
zbx_dc_host_t host;
zbx_script_t script;
zbx_uint64_t usrgrpid, groupid;
zbx_vector_uint64_t eventids;
zbx_vector_db_event_t events;
zbx_vector_ptr_pair_t webhook_params;
char *tz = NULL, *webhook_params_json = NULL, error[MAX_STRING_LEN];
zbx_db_event *problem_event = NULL, *recovery_event = NULL;
zbx_dc_um_handle_t *um_handle = NULL;
zabbix_log(LOG_LEVEL_DEBUG, "In %s() scriptid:" ZBX_FS_UI64 " hostid:" ZBX_FS_UI64 " eventid:" ZBX_FS_UI64
" userid:" ZBX_FS_UI64 " clientip:%s, manualinput:%s",
__func__, scriptid, hostid, eventid, user->userid, clientip, manualinput);
*error = '\0';
memset(&host, 0, sizeof(host));
zbx_vector_uint64_create(&eventids);
zbx_vector_db_event_create(&events);
zbx_vector_ptr_pair_create(&webhook_params);
zbx_script_init(&script);
if (SUCCEED != zbx_get_script_details(scriptid, &script, &scope, &usrgrpid, &groupid, error, sizeof(error)))
goto fail;
/* validate script permissions */
if (0 < usrgrpid &&
USER_TYPE_SUPER_ADMIN != user->type &&
SUCCEED != is_user_in_allowed_group(user->userid, usrgrpid, error, sizeof(error)))
{
goto fail;
}
if (0 != hostid)
{
if (ZBX_SCRIPT_SCOPE_HOST != scope)
{
zbx_snprintf(error, sizeof(error), "Script is not allowed in manual host action: scope:%d",
scope);
goto fail;
}
}
else if (ZBX_SCRIPT_SCOPE_EVENT != scope)
{
zbx_snprintf(error, sizeof(error), "Script is not allowed in manual event action: scope:%d", scope);
goto fail;
}
/* get host or event details */
if (0 != hostid)
{
if (SUCCEED != zbx_dc_get_host_by_hostid(&host, hostid))
{
zbx_strlcpy(error, "Unknown host identifier.", sizeof(error));
goto fail;
}
}
else /* eventid */
{
zbx_uint64_t r_eventid;
if (SUCCEED != zbx_check_event_end_recovery_event(eventid, &r_eventid, error, sizeof(error)))
goto fail;
zbx_vector_uint64_reserve(&eventids, 2);
zbx_vector_db_event_reserve(&events, 2);
zbx_vector_uint64_append(&eventids, eventid); /* problem event in element [0]*/
if (0 != r_eventid) /* optional recovery event in element [1] */
zbx_vector_uint64_append(&eventids, r_eventid);
zbx_db_get_events_by_eventids(&eventids, &events);
if (events.values_num != eventids.values_num)
{
zbx_strlcpy(error, "Specified event data not found.", sizeof(error));
goto fail;
}
switch (events.values_num)
{
case 1:
if (eventid == (events.values[0])->eventid)
{
problem_event = events.values[0];
}
else
{
zbx_strlcpy(error, "Specified event data not found.", sizeof(error));
goto fail;
}
break;
case 2:
if (r_eventid == ((events.values[0]))->eventid)
{
problem_event = events.values[1];
recovery_event = events.values[0];
}
else
{
problem_event = events.values[0];
recovery_event = events.values[1];
}
break;
default:
THIS_SHOULD_NEVER_HAPPEN;
zbx_snprintf(error, sizeof(error), "Internal error in %s() events.values_num:%d",
__func__, events.values_num);
goto fail;
}
if (EVENT_SOURCE_TRIGGERS != problem_event->source)
{
zbx_strlcpy(error, "The source of specified event is not a trigger.", sizeof(error));
goto fail;
}
if (TRIGGER_VALUE_PROBLEM != problem_event->value)
{
zbx_strlcpy(error, "The specified event is not a problem event.", sizeof(error));
goto fail;
}
if (SUCCEED != zbx_event_db_get_host((NULL != recovery_event) ? recovery_event : problem_event,
&host, error, sizeof(error)))
{
goto fail;
}
}
if (SUCCEED != zbx_check_script_permissions(groupid, host.hostid))
{
zbx_strlcpy(error, "Script does not have permission to be executed on the host.", sizeof(error));
goto fail;
}
if (USER_TYPE_SUPER_ADMIN != user->type &&
SUCCEED != zbx_check_script_user_permissions(user->userid, host.hostid, &script))
{
zbx_strlcpy(error, "User does not have permission to execute this script on the host.", sizeof(error));
goto fail;
}
tz = zbx_db_get_user_timezone(user->userid);
if (NULL == tz || 0 == strcmp(tz, ZBX_TIMEZONE_DEFAULT_VALUE))
{
zbx_config_t cfg;
zbx_config_get(&cfg, ZBX_CONFIG_FLAGS_DEFAULT_TIMEZONE);
tz = zbx_strdup(tz, cfg.default_timezone);
zbx_config_clean(&cfg);
}
if (ZBX_SCRIPT_TYPE_WEBHOOK == script.type)
{
if (SUCCEED != zbx_db_fetch_webhook_params(script.scriptid, &webhook_params, error, sizeof(error)))
goto fail;
}
/* substitute macros in script body and webhook parameters */
if (ZBX_SCRIPT_MANUALINPUT_YES == script.manualinput)
{
char *expanded_cmd = NULL;
size_t expanded_cmd_size;
if (NULL == manualinput)
{
zbx_strlcpy(error, "Script takes user input, but none was provided.", sizeof(error));
goto fail;
}
if (FAIL == validate_manualinput(manualinput, script.manualinput_validator,
script.manualinput_validator_type))
{
zbx_strlcpy(error, "Provided script user input failed validation.", sizeof(error));
goto fail;
}
substitute_macro(script.command, "{MANUALINPUT}", manualinput, &expanded_cmd, &expanded_cmd_size);
script.command = zbx_strdup(script.command, expanded_cmd);
zbx_free(expanded_cmd);
/* in the case that this is a webhook script, perform the substitution for parameter values as well */
if (ZBX_SCRIPT_TYPE_WEBHOOK == script.type && 0 < webhook_params.values_num)
{
for (int n = 0; n < webhook_params.values_num; n++)
{
char *expanded_value = NULL;
size_t expanded_value_size;
/* avoid unnecessary mem (re)allocation in case the macro isn't present */
if (NULL == strstr(webhook_params.values[n].second, "{MANUALINPUT}"))
continue;
substitute_macro(webhook_params.values[n].second, "{MANUALINPUT}", manualinput,
&expanded_value, &expanded_value_size);
webhook_params.values[n].second = zbx_strdup(webhook_params.values[n].second,
expanded_value);
zbx_free(expanded_value);
}
}
}
else if (NULL != manualinput) /* script does not take additional input yet we've received a value anyway */
{
zabbix_log(LOG_LEVEL_WARNING, "script (name:%s) "
"does not accept additional manual input, but request contains it anyway",
script.name);
}
if (0 != hostid) /* script on host */
macro_type = ZBX_MACRO_TYPE_SCRIPT;
else
macro_type = (NULL != recovery_event) ? ZBX_MACRO_TYPE_SCRIPT_RECOVERY : ZBX_MACRO_TYPE_SCRIPT_NORMAL;
um_handle = zbx_dc_open_user_macros();
if (ZBX_SCRIPT_TYPE_WEBHOOK != script.type)
{
if (SUCCEED != zbx_substitute_simple_macros_unmasked(NULL, problem_event, recovery_event, &user->userid,
NULL, &host, NULL, NULL, NULL, NULL, NULL, tz, &script.command, macro_type, error,
sizeof(error)))
{
goto fail;
}
if (SUCCEED != zbx_substitute_simple_macros(NULL, problem_event, recovery_event, &user->userid, NULL,
&host, NULL, NULL, NULL, NULL, NULL, tz, &script.command_orig, macro_type, error,
sizeof(error)))
{
THIS_SHOULD_NEVER_HAPPEN;
goto fail;
}
}
else
{
for (int i = 0; i < webhook_params.values_num; i++)
{
if (SUCCEED != zbx_substitute_simple_macros_unmasked(NULL, problem_event, recovery_event,
&user->userid, NULL, &host, NULL, NULL, NULL, NULL, NULL, tz,
(char **)&webhook_params.values[i].second, macro_type, error,
sizeof(error)))
{
goto fail;
}
}
zbx_webhook_params_pack_json(&webhook_params, &webhook_params_json);
}
if (SUCCEED == (ret = zbx_script_prepare(&script, &host.hostid, error, sizeof(error))))
{
const char *poutput = NULL, *perror = NULL;
int audit_res;
if (HOST_MONITORED_BY_SERVER == host.monitored_by ||
ZBX_SCRIPT_EXECUTE_ON_SERVER == script.execute_on ||
ZBX_SCRIPT_TYPE_WEBHOOK == script.type)
{
ret = zbx_script_execute(&script, &host, webhook_params_json, config_timeout,
config_trapper_timeout, config_source_ip, config_ssh_key_location,
config_enable_global_scripts, get_config_forks, program_type, result, error,
sizeof(error), debug);
}
else
ret = execute_remote_script(&script, &host, result, error, sizeof(error));
if (SUCCEED == ret)
poutput = *result;
else
perror = error;
audit_res = zbx_auditlog_global_script(script.type, script.execute_on, script.command_orig, host.hostid,
host.name, eventid, host.proxyid, user->userid, user->username, clientip, poutput,
perror);
/* At the moment, there is no special processing of audit failures. */
/* It can fail only due to the DB errors and those are visible in */
/* the log anyway */
ZBX_UNUSED(audit_res);
}
fail:
if (NULL != um_handle)
zbx_dc_close_user_macros(um_handle);
if (SUCCEED != ret)
*result = zbx_strdup(*result, error);
zbx_script_clean(&script);
zbx_free(webhook_params_json);
zbx_free(tz);
for (int i = 0; i < webhook_params.values_num; i++)
{
zbx_free(webhook_params.values[i].first);
zbx_free(webhook_params.values[i].second);
}
zbx_vector_ptr_pair_destroy(&webhook_params);
zbx_vector_db_event_clear_ext(&events, zbx_db_free_event);
zbx_vector_db_event_destroy(&events);
zbx_vector_uint64_destroy(&eventids);
zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
return ret;
}
/* user role permissions */
typedef enum
{
ROLE_PERM_DENY = 0,
ROLE_PERM_ALLOW = 1,
}
zbx_user_role_permission_t;
/******************************************************************************
* *
* Purpose: Checks if the user has specific or default access for *
* administration actions. *
* *
* Return value: SUCCEED - access is granted *
* FAIL - access is denied *
* *
******************************************************************************/
static int check_user_administration_actions_permissions(const zbx_user_t *user, const char *role_rule_default,
const char *role_rule)
{
int ret = FAIL;
zbx_db_result_t result;
zbx_db_row_t row;
zabbix_log(LOG_LEVEL_DEBUG, "In %s() userid:" ZBX_FS_UI64 , __func__, user->userid);
result = zbx_db_select("select value_int,name from role_rule where roleid=" ZBX_FS_UI64
" and (name='%s' or name='%s')", user->roleid, role_rule,
role_rule_default);
while (NULL != (row = zbx_db_fetch(result)))
{
if (0 == strcmp(role_rule, row[1]))
{
if (ROLE_PERM_ALLOW == atoi(row[0]))
ret = SUCCEED;
else
ret = FAIL;
break;
}
else if (0 == strcmp(role_rule_default, row[1]))
{
if (ROLE_PERM_ALLOW == atoi(row[0]))
ret = SUCCEED;
}
else
THIS_SHOULD_NEVER_HAPPEN;
}
zbx_db_free_result(result);
zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
return ret;
}
/******************************************************************************
* *
* Purpose: processes command received from frontend *
* *
* Return value: SUCCEED - processed successfully *
* FAIL - error occurred *
* *
******************************************************************************/
int node_process_command(zbx_socket_t *sock, const char *data, const struct zbx_json_parse *jp, int config_timeout,
int config_trapper_timeout, const char *config_source_ip, const char *config_ssh_key_location,
zbx_get_config_forks_f get_config_forks, int config_enable_global_scripts, unsigned char program_type)
{
char *result = NULL, *send = NULL, *debug = NULL, *manualinput = NULL, tmp[64],
tmp_hostid[64], tmp_eventid[64], clientip[MAX_STRING_LEN],
tmp_manualinput[MAX_STRING_LEN];
int ret = FAIL, got_hostid = 0, got_eventid = 0;
zbx_uint64_t scriptid, hostid = 0, eventid = 0;
struct zbx_json j;
zbx_user_t user;
zabbix_log(LOG_LEVEL_DEBUG, "In %s(): data:%s ", __func__, data);
zbx_json_init(&j, ZBX_JSON_STAT_BUF_LEN);
zbx_user_init(&user);
/* check who is connecting, get user details, check access rights */
if (FAIL == zbx_get_user_from_json(jp, &user, &result))
goto finish;
if (SUCCEED != zbx_db_check_user_perm2system(user.userid))
{
result = zbx_strdup(result, "Permission denied. User is a member of group with disabled access.");
goto finish;
}
#define ZBX_USER_ROLE_PERMISSION_ACTIONS_DEFAULT_ACCESS "actions.default_access"
#define ZBX_USER_ROLE_PERMISSION_ACTIONS_EXECUTE_SCRIPTS "actions.execute_scripts"
if (SUCCEED != check_user_administration_actions_permissions(&user,
ZBX_USER_ROLE_PERMISSION_ACTIONS_DEFAULT_ACCESS,
ZBX_USER_ROLE_PERMISSION_ACTIONS_EXECUTE_SCRIPTS))
{
result = zbx_strdup(result, "Permission denied. No role access.");
goto finish;
}
#undef ZBX_USER_ROLE_PERMISSION_ACTIONS_DEFAULT_ACCESS
#undef ZBX_USER_ROLE_PERMISSION_ACTIONS_EXECUTE_SCRIPTS
/* extract and validate other JSON elements */
if (SUCCEED != zbx_json_value_by_name(jp, ZBX_PROTO_TAG_SCRIPTID, tmp, sizeof(tmp), NULL) ||
FAIL == zbx_is_uint64(tmp, &scriptid))
{
result = zbx_dsprintf(result, "Failed to parse command request tag: %s.", ZBX_PROTO_TAG_SCRIPTID);
goto finish;
}
if (SUCCEED == zbx_json_value_by_name(jp, ZBX_PROTO_TAG_HOSTID, tmp_hostid, sizeof(tmp_hostid), NULL))
got_hostid = 1;
if (SUCCEED == zbx_json_value_by_name(jp, ZBX_PROTO_TAG_EVENTID, tmp_eventid, sizeof(tmp_eventid), NULL))
got_eventid = 1;
if (0 == got_hostid && 0 == got_eventid)
{
result = zbx_dsprintf(result, "Failed to parse command request tag %s or %s.",
ZBX_PROTO_TAG_HOSTID, ZBX_PROTO_TAG_EVENTID);
goto finish;
}
if (1 == got_hostid && 1 == got_eventid)
{
result = zbx_dsprintf(result, "Command request tags %s and %s cannot be used together.",
ZBX_PROTO_TAG_HOSTID, ZBX_PROTO_TAG_EVENTID);
goto finish;
}
if (1 == got_hostid)
{
if (SUCCEED != zbx_is_uint64(tmp_hostid, &hostid))
{
result = zbx_dsprintf(result, "Failed to parse value of command request tag %s.",
ZBX_PROTO_TAG_HOSTID);
goto finish;
}
if (0 == hostid)
{
result = zbx_dsprintf(result, "%s value cannot be 0.", ZBX_PROTO_TAG_HOSTID);
goto finish;
}
}
else
{
if (SUCCEED != zbx_is_uint64(tmp_eventid, &eventid))
{
result = zbx_dsprintf(result, "Failed to parse value of command request tag %s.",
ZBX_PROTO_TAG_EVENTID);
goto finish;
}
if (0 == eventid)
{
result = zbx_dsprintf(result, "%s value cannot be 0.", ZBX_PROTO_TAG_EVENTID);
goto finish;
}
}
/* It appears that IPv6 specification allows entries likeĀ "%" */
/* which do not pass our current IPv6 address validation. In the future, when we */
/* fix our IPv6 address validation we could consider adding it here. */
if (SUCCEED != zbx_json_value_by_name(jp, ZBX_PROTO_TAG_CLIENTIP, clientip, sizeof(clientip), NULL))
*clientip = '\0';
if (SUCCEED == zbx_json_value_by_name(jp, ZBX_PROTO_TAG_MANUALINPUT, tmp_manualinput,
sizeof(tmp_manualinput), NULL))
manualinput = tmp_manualinput;
if (SUCCEED == (ret = execute_script(scriptid, hostid, eventid, &user, clientip, manualinput,
config_timeout, config_trapper_timeout, config_source_ip, config_ssh_key_location,
config_enable_global_scripts, get_config_forks, program_type, &result, &debug)))
{
zbx_json_addstring(&j, ZBX_PROTO_TAG_RESPONSE, ZBX_PROTO_VALUE_SUCCESS, ZBX_JSON_TYPE_STRING);
zbx_json_addstring(&j, ZBX_PROTO_TAG_DATA, result, ZBX_JSON_TYPE_STRING);
if (NULL != debug)
zbx_json_addraw(&j, "debug", debug);
send = j.buffer;
}
finish:
if (SUCCEED != ret)
{
zbx_json_addstring(&j, ZBX_PROTO_TAG_RESPONSE, ZBX_PROTO_VALUE_FAILED, ZBX_JSON_TYPE_STRING);
zbx_json_addstring(&j, ZBX_PROTO_TAG_INFO, (NULL != result ? result : "Unknown error."),
ZBX_JSON_TYPE_STRING);
send = j.buffer;
}
if (SUCCEED != zbx_tcp_send_to(sock, send, config_timeout))
zabbix_log(LOG_LEVEL_WARNING, "Error sending result of command");
else
zabbix_log(LOG_LEVEL_DEBUG, "Sending back command '%s' result '%s'", data, send);
zbx_json_free(&j);
zbx_free(result);
zbx_free(debug);
zbx_user_free(&user);
zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
return ret;
}