1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "../poller/checks_agent.h"
22 #include "../ipmi/ipmi.h"
23 #include "../poller/checks_ssh.h"
24 #include "../poller/checks_telnet.h"
25 #include "zbxexec.h"
26 #include "zbxserver.h"
27 #include "db.h"
28 #include "log.h"
29 #include "zbxtasks.h"
30 #include "scripts.h"
31 #include "zbxjson.h"
32 #include "zbxembed.h"
33 #include "../events.h"
34
35 extern int CONFIG_TRAPPER_TIMEOUT;
36 extern int CONFIG_IPMIPOLLER_FORKS;
37
zbx_execute_script_on_agent(const DC_HOST * host,const char * command,char ** result,char * error,size_t max_error_len)38 static int zbx_execute_script_on_agent(const DC_HOST *host, const char *command, char **result,
39 char *error, size_t max_error_len)
40 {
41 int ret;
42 AGENT_RESULT agent_result;
43 char *param = NULL, *port = NULL;
44 DC_ITEM item;
45
46 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
47
48 *error = '\0';
49 memset(&item, 0, sizeof(item));
50 memcpy(&item.host, host, sizeof(item.host));
51
52 if (SUCCEED != (ret = DCconfig_get_interface_by_type(&item.interface, host->hostid, INTERFACE_TYPE_AGENT)))
53 {
54 zbx_snprintf(error, max_error_len, "Zabbix agent interface is not defined for host [%s]", host->host);
55 goto fail;
56 }
57
58 port = zbx_strdup(port, item.interface.port_orig);
59 substitute_simple_macros(NULL, NULL, NULL, NULL, &host->hostid, NULL, NULL, NULL, NULL, NULL,
60 &port, MACRO_TYPE_COMMON, NULL, 0);
61
62 if (SUCCEED != (ret = is_ushort(port, &item.interface.port)))
63 {
64 zbx_snprintf(error, max_error_len, "Invalid port number [%s]", item.interface.port_orig);
65 goto fail;
66 }
67
68 param = zbx_strdup(param, command);
69 if (SUCCEED != (ret = quote_key_param(¶m, 0)))
70 {
71 zbx_snprintf(error, max_error_len, "Invalid param [%s]", param);
72 goto fail;
73 }
74
75 item.key = zbx_dsprintf(item.key, "system.run[%s%s]", param, NULL == result ? ",nowait" : "");
76 item.value_type = ITEM_VALUE_TYPE_TEXT;
77
78 init_result(&agent_result);
79
80 zbx_alarm_on(CONFIG_TIMEOUT);
81
82 if (SUCCEED != (ret = get_value_agent(&item, &agent_result)))
83 {
84 if (ISSET_MSG(&agent_result))
85 zbx_strlcpy(error, agent_result.msg, max_error_len);
86 ret = FAIL;
87 }
88 else if (NULL != result && ISSET_TEXT(&agent_result))
89 *result = zbx_strdup(*result, agent_result.text);
90
91 zbx_alarm_off();
92
93 free_result(&agent_result);
94
95 zbx_free(item.key);
96 fail:
97 zbx_free(port);
98 zbx_free(param);
99
100 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
101
102 return ret;
103 }
104
zbx_execute_script_on_terminal(const DC_HOST * host,const zbx_script_t * script,char ** result,char * error,size_t max_error_len)105 static int zbx_execute_script_on_terminal(const DC_HOST *host, const zbx_script_t *script, char **result,
106 char *error, size_t max_error_len)
107 {
108 int ret = FAIL, i;
109 AGENT_RESULT agent_result;
110 DC_ITEM item;
111 int (*function)(DC_ITEM *, AGENT_RESULT *);
112
113 #if defined(HAVE_SSH2) || defined(HAVE_SSH)
114 assert(ZBX_SCRIPT_TYPE_SSH == script->type || ZBX_SCRIPT_TYPE_TELNET == script->type);
115 #else
116 assert(ZBX_SCRIPT_TYPE_TELNET == script->type);
117 #endif
118
119 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
120
121 *error = '\0';
122 memset(&item, 0, sizeof(item));
123 memcpy(&item.host, host, sizeof(item.host));
124
125 for (i = 0; INTERFACE_TYPE_COUNT > i; i++)
126 {
127 if (SUCCEED == (ret = DCconfig_get_interface_by_type(&item.interface, host->hostid,
128 INTERFACE_TYPE_PRIORITY[i])))
129 {
130 break;
131 }
132 }
133
134 if (FAIL == ret)
135 {
136 zbx_snprintf(error, max_error_len, "No interface defined for host [%s]", host->host);
137 goto fail;
138 }
139
140 switch (script->type)
141 {
142 case ZBX_SCRIPT_TYPE_SSH:
143 item.authtype = script->authtype;
144 item.publickey = script->publickey;
145 item.privatekey = script->privatekey;
146 ZBX_FALLTHROUGH;
147 case ZBX_SCRIPT_TYPE_TELNET:
148 item.username = script->username;
149 item.password = script->password;
150 break;
151 }
152
153 #if defined(HAVE_SSH2) || defined(HAVE_SSH)
154 if (ZBX_SCRIPT_TYPE_SSH == script->type)
155 {
156 item.key = zbx_dsprintf(item.key, "ssh.run[,,%s]", script->port);
157 function = get_value_ssh;
158 }
159 else
160 {
161 #endif
162 item.key = zbx_dsprintf(item.key, "telnet.run[,,%s]", script->port);
163 function = get_value_telnet;
164 #if defined(HAVE_SSH2) || defined(HAVE_SSH)
165 }
166 #endif
167 item.value_type = ITEM_VALUE_TYPE_TEXT;
168 item.params = zbx_strdup(item.params, script->command);
169
170 init_result(&agent_result);
171
172 zbx_alarm_on(CONFIG_TIMEOUT);
173
174 if (SUCCEED != (ret = function(&item, &agent_result)))
175 {
176 if (ISSET_MSG(&agent_result))
177 zbx_strlcpy(error, agent_result.msg, max_error_len);
178 ret = FAIL;
179 }
180 else if (NULL != result && ISSET_TEXT(&agent_result))
181 *result = zbx_strdup(*result, agent_result.text);
182
183 zbx_alarm_off();
184
185 free_result(&agent_result);
186
187 zbx_free(item.params);
188 zbx_free(item.key);
189 fail:
190 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
191
192 return ret;
193 }
194
zbx_check_script_permissions(zbx_uint64_t groupid,zbx_uint64_t hostid)195 int zbx_check_script_permissions(zbx_uint64_t groupid, zbx_uint64_t hostid)
196 {
197 DB_RESULT result;
198 int ret = SUCCEED;
199 zbx_vector_uint64_t groupids;
200 char *sql = NULL;
201 size_t sql_alloc = 0, sql_offset = 0;
202
203 zabbix_log(LOG_LEVEL_DEBUG, "In %s() groupid:" ZBX_FS_UI64 " hostid:" ZBX_FS_UI64, __func__, groupid, hostid);
204
205 if (0 == groupid)
206 goto exit;
207
208 zbx_vector_uint64_create(&groupids);
209 zbx_dc_get_nested_hostgroupids(&groupid, 1, &groupids);
210
211 zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
212 "select hostid"
213 " from hosts_groups"
214 " where hostid=" ZBX_FS_UI64
215 " and",
216 hostid);
217
218 DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "groupid", groupids.values,
219 groupids.values_num);
220
221 result = DBselect("%s", sql);
222
223 zbx_free(sql);
224 zbx_vector_uint64_destroy(&groupids);
225
226 if (NULL == DBfetch(result))
227 ret = FAIL;
228
229 DBfree_result(result);
230 exit:
231 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
232
233 return ret;
234 }
235
zbx_check_script_user_permissions(zbx_uint64_t userid,zbx_uint64_t hostid,zbx_script_t * script)236 int zbx_check_script_user_permissions(zbx_uint64_t userid, zbx_uint64_t hostid, zbx_script_t *script)
237 {
238 int ret = SUCCEED;
239 DB_RESULT result;
240
241 zabbix_log(LOG_LEVEL_DEBUG, "In %s() userid:" ZBX_FS_UI64 " hostid:" ZBX_FS_UI64 " scriptid:" ZBX_FS_UI64,
242 __func__, userid, hostid, script->scriptid);
243
244 result = DBselect(
245 "select null"
246 " from hosts_groups hg,rights r,users_groups ug"
247 " where hg.groupid=r.id"
248 " and r.groupid=ug.usrgrpid"
249 " and hg.hostid=" ZBX_FS_UI64
250 " and ug.userid=" ZBX_FS_UI64
251 " group by hg.hostid"
252 " having min(r.permission)>%d"
253 " and max(r.permission)>=%d",
254 hostid,
255 userid,
256 PERM_DENY,
257 script->host_access);
258
259 if (NULL == DBfetch(result))
260 ret = FAIL;
261
262 DBfree_result(result);
263
264 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
265
266 return ret;
267 }
268
zbx_script_init(zbx_script_t * script)269 void zbx_script_init(zbx_script_t *script)
270 {
271 memset(script, 0, sizeof(zbx_script_t));
272 }
273
zbx_script_clean(zbx_script_t * script)274 void zbx_script_clean(zbx_script_t *script)
275 {
276 zbx_free(script->port);
277 zbx_free(script->username);
278 zbx_free(script->publickey);
279 zbx_free(script->privatekey);
280 zbx_free(script->password);
281 zbx_free(script->command);
282 zbx_free(script->command_orig);
283 }
284
285 /******************************************************************************
286 * *
287 * Function: zbx_webhook_params_pack_json *
288 * *
289 * Purpose: pack webhook script parameters into JSON *
290 * *
291 * Parameters: params - [IN] vector of pairs of pointers to parameter *
292 * names and values *
293 * params_json - [OUT] JSON string *
294 * *
295 ******************************************************************************/
zbx_webhook_params_pack_json(const zbx_vector_ptr_pair_t * params,char ** params_json)296 void zbx_webhook_params_pack_json(const zbx_vector_ptr_pair_t *params, char **params_json)
297 {
298 struct zbx_json json_data;
299 int i;
300
301 zbx_json_init(&json_data, ZBX_JSON_STAT_BUF_LEN);
302
303 for (i = 0; i < params->values_num; i++)
304 {
305 zbx_ptr_pair_t pair = params->values[i];
306
307 zbx_json_addstring(&json_data, pair.first, pair.second, ZBX_JSON_TYPE_STRING);
308 }
309
310 zbx_json_close(&json_data);
311 *params_json = zbx_strdup(*params_json, json_data.buffer);
312 zbx_json_free(&json_data);
313 }
314
315 /***********************************************************************************
316 * *
317 * Function: zbx_script_prepare *
318 * *
319 * Purpose: prepares user script *
320 * *
321 * Parameters: script - [IN] the script to prepare *
322 * host - [IN] the host the script will be executed on *
323 * error - [OUT] the error message buffer *
324 * max_error_len - [IN] the size of error message output buffer *
325 * *
326 * Return value: SUCCEED - the script has been prepared successfully *
327 * FAIL - otherwise, error contains error message *
328 * *
329 * Comments: This function prepares script for execution by loading global *
330 * script/expanding macros (except in script body). *
331 * Prepared scripts must be always freed with zbx_script_clean() *
332 * function. *
333 * *
334 ***********************************************************************************/
zbx_script_prepare(zbx_script_t * script,const zbx_uint64_t * hostid,char * error,size_t max_error_len)335 int zbx_script_prepare(zbx_script_t *script, const zbx_uint64_t *hostid, char *error, size_t max_error_len)
336 {
337 int ret = FAIL;
338
339 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
340
341 switch (script->type)
342 {
343 case ZBX_SCRIPT_TYPE_SSH:
344 substitute_simple_macros(NULL, NULL, NULL, NULL, hostid, NULL, NULL, NULL, NULL, NULL,
345 &script->publickey, MACRO_TYPE_COMMON, NULL, 0);
346 substitute_simple_macros(NULL, NULL, NULL, NULL, hostid, NULL, NULL, NULL, NULL, NULL,
347 &script->privatekey, MACRO_TYPE_COMMON, NULL, 0);
348 ZBX_FALLTHROUGH;
349 case ZBX_SCRIPT_TYPE_TELNET:
350 substitute_simple_macros(NULL, NULL, NULL, NULL, hostid, NULL, NULL, NULL, NULL, NULL,
351 &script->port, MACRO_TYPE_COMMON, NULL, 0);
352
353 if ('\0' != *script->port && SUCCEED != (ret = is_ushort(script->port, NULL)))
354 {
355 zbx_snprintf(error, max_error_len, "Invalid port number \"%s\"", script->port);
356 goto out;
357 }
358
359 substitute_simple_macros_unmasked(NULL, NULL, NULL, NULL, hostid, NULL, NULL, NULL, NULL,
360 NULL, &script->username, MACRO_TYPE_COMMON, NULL, 0);
361 substitute_simple_macros_unmasked(NULL, NULL, NULL, NULL, hostid, NULL, NULL, NULL, NULL,
362 NULL, &script->password, MACRO_TYPE_COMMON, NULL, 0);
363 break;
364 case ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT:
365 dos2unix(script->command); /* CR+LF (Windows) => LF (Unix) */
366 break;
367 case ZBX_SCRIPT_TYPE_WEBHOOK:
368 case ZBX_SCRIPT_TYPE_IPMI:
369 break;
370 default:
371 zbx_snprintf(error, max_error_len, "Invalid command type \"%d\".", (int)script->type);
372 goto out;
373 }
374
375 ret = SUCCEED;
376 out:
377 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
378 return ret;
379 }
380
381 /******************************************************************************
382 * *
383 * Function: DBfetch_webhook_params *
384 * *
385 * Purpose: fetch webhook parameters *
386 * *
387 * Parameters: scriptid - [IN] the id of script to be executed *
388 * params - [OUT] parameters name-value pairs *
389 * error - [IN/OUT] the error message *
390 * error_len - [IN] the maximum error length *
391 * *
392 * Return value: SUCCEED - processed successfully *
393 * FAIL - an error occurred *
394 * *
395 ******************************************************************************/
DBfetch_webhook_params(zbx_uint64_t scriptid,zbx_vector_ptr_pair_t * params,char * error,size_t error_len)396 int DBfetch_webhook_params(zbx_uint64_t scriptid, zbx_vector_ptr_pair_t *params, char *error, size_t error_len)
397 {
398 int ret = SUCCEED;
399 DB_RESULT result;
400 DB_ROW row;
401 zbx_ptr_pair_t pair;
402
403 zabbix_log(LOG_LEVEL_DEBUG, "In %s() scriptid:" ZBX_FS_UI64, __func__, scriptid);
404
405 result = DBselect("select name,value from script_param where scriptid=" ZBX_FS_UI64, scriptid);
406
407 if (NULL == result)
408 {
409 zbx_strlcpy(error, "Database error, cannot get webhook script parameters.", error_len);
410 ret = FAIL;
411 goto out;
412 }
413
414 while (NULL != (row = DBfetch(result)))
415 {
416 pair.first = zbx_strdup(NULL, row[0]);
417 pair.second = zbx_strdup(NULL, row[1]);
418 zbx_vector_ptr_pair_append(params, pair);
419 }
420
421 DBfree_result(result);
422 out:
423 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
424
425 return ret;
426 }
427
428 /******************************************************************************
429 * *
430 * Function: zbx_script_execute *
431 * *
432 * Purpose: executing user scripts or remote commands *
433 * *
434 * Parameters: script - [IN] the script to be executed *
435 * host - [IN] the host the script will be executed on *
436 * params - [IN] parameters for the script *
437 * result - [OUT] the result of a script execution *
438 * error - [OUT] the error reported by the script *
439 * max_error_len - [IN] the maximum error length *
440 * debug - [OUT] the debug data (optional) *
441 * *
442 * Return value: SUCCEED - processed successfully *
443 * FAIL - an error occurred *
444 * TIMEOUT_ERROR - a timeout occurred *
445 * *
446 ******************************************************************************/
zbx_script_execute(const zbx_script_t * script,const DC_HOST * host,const char * params,char ** result,char * error,size_t max_error_len,char ** debug)447 int zbx_script_execute(const zbx_script_t *script, const DC_HOST *host, const char *params, char **result,
448 char *error, size_t max_error_len, char **debug)
449 {
450 int ret = FAIL;
451
452 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
453
454 *error = '\0';
455
456 switch (script->type)
457 {
458 case ZBX_SCRIPT_TYPE_WEBHOOK:
459 ret = zbx_es_execute_command(script->command, params, script->timeout, result, error,
460 max_error_len, debug);
461 break;
462 case ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT:
463 switch (script->execute_on)
464 {
465 case ZBX_SCRIPT_EXECUTE_ON_AGENT:
466 ret = zbx_execute_script_on_agent(host, script->command, result, error,
467 max_error_len);
468 break;
469 case ZBX_SCRIPT_EXECUTE_ON_SERVER:
470 case ZBX_SCRIPT_EXECUTE_ON_PROXY:
471 ret = zbx_execute(script->command, result, error, max_error_len,
472 CONFIG_TRAPPER_TIMEOUT, ZBX_EXIT_CODE_CHECKS_ENABLED, NULL);
473 break;
474 default:
475 zbx_snprintf(error, max_error_len, "Invalid 'Execute on' option \"%d\".",
476 (int)script->execute_on);
477 }
478 break;
479 case ZBX_SCRIPT_TYPE_IPMI:
480 #ifdef HAVE_OPENIPMI
481 if (0 == CONFIG_IPMIPOLLER_FORKS)
482 {
483 zbx_strlcpy(error, "Cannot perform IPMI request: configuration parameter"
484 " \"StartIPMIPollers\" is 0.", max_error_len);
485 break;
486 }
487
488 if (SUCCEED == (ret = zbx_ipmi_execute_command(host, script->command, error, max_error_len)))
489 {
490 if (NULL != result)
491 *result = zbx_strdup(*result, "IPMI command successfully executed.");
492 }
493 #else
494 zbx_strlcpy(error, "Support for IPMI commands was not compiled in.", max_error_len);
495 #endif
496 break;
497 case ZBX_SCRIPT_TYPE_SSH:
498 #if !defined(HAVE_SSH2) && !defined(HAVE_SSH)
499 zbx_strlcpy(error, "Support for SSH script was not compiled in.", max_error_len);
500 break;
501 #endif
502 case ZBX_SCRIPT_TYPE_TELNET:
503 ret = zbx_execute_script_on_terminal(host, script, result, error, max_error_len);
504 break;
505 default:
506 zbx_snprintf(error, max_error_len, "Invalid command type \"%d\".", (int)script->type);
507 }
508
509 if (SUCCEED != ret && NULL != result)
510 *result = zbx_strdup(*result, "");
511
512 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
513
514 return ret;
515 }
516
517 /******************************************************************************
518 * *
519 * Function: zbx_script_create_task *
520 * *
521 * Purpose: creates remote command task from a script *
522 * *
523 * Return value: the identifier of the created task or 0 in the case of *
524 * error *
525 * *
526 ******************************************************************************/
zbx_script_create_task(const zbx_script_t * script,const DC_HOST * host,zbx_uint64_t alertid,int now)527 zbx_uint64_t zbx_script_create_task(const zbx_script_t *script, const DC_HOST *host, zbx_uint64_t alertid, int now)
528 {
529 zbx_tm_task_t *task;
530 unsigned short port;
531 zbx_uint64_t taskid;
532
533 if (NULL != script->port && '\0' != script->port[0])
534 is_ushort(script->port, &port);
535 else
536 port = 0;
537
538 DBbegin();
539
540 taskid = DBget_maxid("task");
541
542 task = zbx_tm_task_create(taskid, ZBX_TM_TASK_REMOTE_COMMAND, ZBX_TM_STATUS_NEW, now,
543 ZBX_REMOTE_COMMAND_TTL, host->proxy_hostid);
544
545 task->data = zbx_tm_remote_command_create(script->type, script->command, script->execute_on, port,
546 script->authtype, script->username, script->password, script->publickey, script->privatekey,
547 taskid, host->hostid, alertid);
548
549 if (FAIL == zbx_tm_save_task(task))
550 taskid = 0;
551
552 DBcommit();
553
554 zbx_tm_task_free(task);
555
556 return taskid;
557 }
558