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 "cfg.h"
22 #include "log.h"
23 #include "comms.h"
24 
25 extern unsigned char	program_type;
26 
27 char	*CONFIG_FILE		= NULL;
28 
29 char	*CONFIG_LOG_TYPE_STR	= NULL;
30 int	CONFIG_LOG_TYPE		= LOG_TYPE_UNDEFINED;
31 char	*CONFIG_LOG_FILE	= NULL;
32 int	CONFIG_LOG_FILE_SIZE	= 1;
33 int	CONFIG_ALLOW_ROOT	= 0;
34 int	CONFIG_TIMEOUT		= 3;
35 
36 static int	__parse_cfg_file(const char *cfg_file, struct cfg_line *cfg, int level, int optional, int strict);
37 
38 /******************************************************************************
39  *                                                                            *
40  * Function: match_glob                                                       *
41  *                                                                            *
42  * Purpose: see whether a file (e.g., "parameter.conf")                       *
43  *          matches a pattern (e.g., "p*.conf")                               *
44  *                                                                            *
45  * Return value: SUCCEED - file matches a pattern                             *
46  *               FAIL - otherwise                                             *
47  *                                                                            *
48  ******************************************************************************/
match_glob(const char * file,const char * pattern)49 static int	match_glob(const char *file, const char *pattern)
50 {
51 	const char	*f, *g, *p, *q;
52 
53 	f = file;
54 	p = pattern;
55 
56 	while (1)
57 	{
58 		/* corner case */
59 
60 		if ('\0' == *p)
61 			return '\0' == *f ? SUCCEED : FAIL;
62 
63 		/* find a set of literal characters */
64 
65 		while ('*' == *p)
66 			p++;
67 
68 		for (q = p; '\0' != *q && '*' != *q; q++)
69 			;
70 
71 		/* if literal characters are at the beginning... */
72 
73 		if (pattern == p)
74 		{
75 #ifdef _WINDOWS
76 			if (0 != zbx_strncasecmp(f, p, q - p))
77 #else
78 			if (0 != strncmp(f, p, q - p))
79 #endif
80 				return FAIL;
81 
82 			f += q - p;
83 			p = q;
84 
85 			continue;
86 		}
87 
88 		/* if literal characters are at the end... */
89 
90 		if ('\0' == *q)
91 		{
92 			for (g = f; '\0' != *g; g++)
93 				;
94 
95 			if (g - f < q - p)
96 				return FAIL;
97 #ifdef _WINDOWS
98 			return 0 == strcasecmp(g - (q - p), p) ? SUCCEED : FAIL;
99 #else
100 			return 0 == strcmp(g - (q - p), p) ? SUCCEED : FAIL;
101 #endif
102 		}
103 
104 		/* if literal characters are in the middle... */
105 
106 		while (1)
107 		{
108 			if ('\0' == *f)
109 				return FAIL;
110 #ifdef _WINDOWS
111 			if (0 == zbx_strncasecmp(f, p, q - p))
112 #else
113 			if (0 == strncmp(f, p, q - p))
114 #endif
115 			{
116 				f += q - p;
117 				p = q;
118 
119 				break;
120 			}
121 
122 			f++;
123 		}
124 	}
125 }
126 
127 /******************************************************************************
128  *                                                                            *
129  * Function: parse_glob                                                       *
130  *                                                                            *
131  * Purpose: parse a glob like "/usr/local/etc/zabbix_agentd.conf.d/p*.conf"   *
132  *          into "/usr/local/etc/zabbix_agentd.conf.d" and "p*.conf" parts    *
133  *                                                                            *
134  * Parameters: glob    - [IN] glob as specified in Include directive          *
135  *             path    - [OUT] parsed path, either directory or file          *
136  *             pattern - [OUT] parsed pattern, if path is directory           *
137  *                                                                            *
138  * Return value: SUCCEED - glob is valid and was parsed successfully          *
139  *               FAIL - otherwise                                             *
140  *                                                                            *
141  ******************************************************************************/
parse_glob(const char * glob,char ** path,char ** pattern)142 static int	parse_glob(const char *glob, char **path, char **pattern)
143 {
144 	const char	*p;
145 
146 	if (NULL == (p = strchr(glob, '*')))
147 	{
148 		*path = zbx_strdup(NULL, glob);
149 		*pattern = NULL;
150 
151 		goto trim;
152 	}
153 
154 	if (NULL != strchr(p + 1, PATH_SEPARATOR))
155 	{
156 		zbx_error("%s: glob pattern should be the last component of the path", glob);
157 		return FAIL;
158 	}
159 
160 	do
161 	{
162 		if (glob == p)
163 		{
164 			zbx_error("%s: path should be absolute", glob);
165 			return FAIL;
166 		}
167 
168 		p--;
169 	}
170 	while (PATH_SEPARATOR != *p);
171 
172 	*path = zbx_strdup(NULL, glob);
173 	(*path)[p - glob] = '\0';
174 
175 	*pattern = zbx_strdup(NULL, p + 1);
176 trim:
177 #ifdef _WINDOWS
178 	if (0 != zbx_rtrim(*path, "\\") && NULL == *pattern)
179 		*pattern = zbx_strdup(NULL, "*");			/* make sure path is a directory */
180 
181 	if (':' == (*path)[1] && '\0' == (*path)[2] && '\\' == glob[2])	/* retain backslash for "C:\" */
182 	{
183 		(*path)[2] = '\\';
184 		(*path)[3] = '\0';
185 	}
186 #else
187 	if (0 != zbx_rtrim(*path, "/") && NULL == *pattern)
188 		*pattern = zbx_strdup(NULL, "*");			/* make sure path is a directory */
189 
190 	if ('\0' == (*path)[0] && '/' == glob[0])			/* retain forward slash for "/" */
191 	{
192 		(*path)[0] = '/';
193 		(*path)[1] = '\0';
194 	}
195 #endif
196 	return SUCCEED;
197 }
198 
199 /******************************************************************************
200  *                                                                            *
201  * Function: parse_cfg_dir                                                    *
202  *                                                                            *
203  * Purpose: parse directory with configuration files                          *
204  *                                                                            *
205  * Parameters: path    - full path to directory                               *
206  *             pattern - pattern that files in the directory should match     *
207  *             cfg     - pointer to configuration parameter structure         *
208  *             level   - a level of included file                             *
209  *             strict  - treat unknown parameters as error                    *
210  *                                                                            *
211  * Return value: SUCCEED - parsed successfully                                *
212  *               FAIL - error processing directory                            *
213  *                                                                            *
214  ******************************************************************************/
215 #ifdef _WINDOWS
parse_cfg_dir(const char * path,const char * pattern,struct cfg_line * cfg,int level,int strict)216 static int	parse_cfg_dir(const char *path, const char *pattern, struct cfg_line *cfg, int level, int strict)
217 {
218 	WIN32_FIND_DATAW	find_file_data;
219 	HANDLE			h_find;
220 	char 			*find_path = NULL, *file = NULL, *file_name;
221 	wchar_t			*wfind_path = NULL;
222 	int			ret = FAIL;
223 
224 	find_path = zbx_dsprintf(find_path, "%s\\*", path);
225 	wfind_path = zbx_utf8_to_unicode(find_path);
226 
227 	if (INVALID_HANDLE_VALUE == (h_find = FindFirstFileW(wfind_path, &find_file_data)))
228 		goto clean;
229 
230 	while (0 != FindNextFileW(h_find, &find_file_data))
231 	{
232 		if (0 != (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
233 			continue;
234 
235 		file_name = zbx_unicode_to_utf8(find_file_data.cFileName);
236 
237 		if (NULL != pattern && SUCCEED != match_glob(file_name, pattern))
238 		{
239 			zbx_free(file_name);
240 			continue;
241 		}
242 
243 		file = zbx_dsprintf(file, "%s\\%s", path, file_name);
244 
245 		zbx_free(file_name);
246 
247 		if (SUCCEED != __parse_cfg_file(file, cfg, level, ZBX_CFG_FILE_REQUIRED, strict))
248 			goto close;
249 	}
250 
251 	ret = SUCCEED;
252 close:
253 	zbx_free(file);
254 	FindClose(h_find);
255 clean:
256 	zbx_free(wfind_path);
257 	zbx_free(find_path);
258 
259 	return ret;
260 }
261 #else
parse_cfg_dir(const char * path,const char * pattern,struct cfg_line * cfg,int level,int strict)262 static int	parse_cfg_dir(const char *path, const char *pattern, struct cfg_line *cfg, int level, int strict)
263 {
264 	DIR		*dir;
265 	struct dirent	*d;
266 	zbx_stat_t	sb;
267 	char		*file = NULL;
268 	int		ret = FAIL;
269 
270 	if (NULL == (dir = opendir(path)))
271 	{
272 		zbx_error("%s: %s", path, zbx_strerror(errno));
273 		goto out;
274 	}
275 
276 	while (NULL != (d = readdir(dir)))
277 	{
278 		file = zbx_dsprintf(file, "%s/%s", path, d->d_name);
279 
280 		if (0 != zbx_stat(file, &sb) || 0 == S_ISREG(sb.st_mode))
281 			continue;
282 
283 		if (NULL != pattern && SUCCEED != match_glob(d->d_name, pattern))
284 			continue;
285 
286 		if (SUCCEED != __parse_cfg_file(file, cfg, level, ZBX_CFG_FILE_REQUIRED, strict))
287 			goto close;
288 	}
289 
290 	ret = SUCCEED;
291 close:
292 	if (0 != closedir(dir))
293 	{
294 		zbx_error("%s: %s", path, zbx_strerror(errno));
295 		ret = FAIL;
296 	}
297 
298 	zbx_free(file);
299 out:
300 	return ret;
301 }
302 #endif
303 
304 /******************************************************************************
305  *                                                                            *
306  * Function: parse_cfg_object                                                 *
307  *                                                                            *
308  * Purpose: parse "Include=..." line in configuration file                    *
309  *                                                                            *
310  * Parameters: cfg_file - full name of config file                            *
311  *             cfg      - pointer to configuration parameter structure        *
312  *             level    - a level of included file                            *
313  *             strict   - treat unknown parameters as error                   *
314  *                                                                            *
315  * Return value: SUCCEED - parsed successfully                                *
316  *               FAIL - error processing object                               *
317  *                                                                            *
318  ******************************************************************************/
parse_cfg_object(const char * cfg_file,struct cfg_line * cfg,int level,int strict)319 static int	parse_cfg_object(const char *cfg_file, struct cfg_line *cfg, int level, int strict)
320 {
321 	int		ret = FAIL;
322 	char		*path = NULL, *pattern = NULL;
323 	zbx_stat_t	sb;
324 
325 	if (SUCCEED != parse_glob(cfg_file, &path, &pattern))
326 		goto clean;
327 
328 	if (0 != zbx_stat(path, &sb))
329 	{
330 		zbx_error("%s: %s", path, zbx_strerror(errno));
331 		goto clean;
332 	}
333 
334 	if (0 == S_ISDIR(sb.st_mode))
335 	{
336 		if (NULL == pattern)
337 		{
338 			ret = __parse_cfg_file(path, cfg, level, ZBX_CFG_FILE_REQUIRED, strict);
339 			goto clean;
340 		}
341 
342 		zbx_error("%s: base path is not a directory", cfg_file);
343 		goto clean;
344 	}
345 
346 	ret = parse_cfg_dir(path, pattern, cfg, level, strict);
347 clean:
348 	zbx_free(pattern);
349 	zbx_free(path);
350 
351 	return ret;
352 }
353 
354 /******************************************************************************
355  *                                                                            *
356  * Function: parse_cfg_file                                                   *
357  *                                                                            *
358  * Purpose: parse configuration file                                          *
359  *                                                                            *
360  * Parameters: cfg_file - full name of config file                            *
361  *             cfg      - pointer to configuration parameter structure        *
362  *             level    - a level of included file                            *
363  *             optional - do not treat missing configuration file as error    *
364  *             strict   - treat unknown parameters as error                   *
365  *                                                                            *
366  * Return value: SUCCEED - parsed successfully                                *
367  *               FAIL - error processing config file                          *
368  *                                                                            *
369  * Author: Alexei Vladishev, Eugene Grigorjev                                 *
370  *                                                                            *
371  ******************************************************************************/
__parse_cfg_file(const char * cfg_file,struct cfg_line * cfg,int level,int optional,int strict)372 static int	__parse_cfg_file(const char *cfg_file, struct cfg_line *cfg, int level, int optional, int strict)
373 {
374 #define ZBX_MAX_INCLUDE_LEVEL	10
375 
376 #define ZBX_CFG_LTRIM_CHARS	"\t "
377 #define ZBX_CFG_RTRIM_CHARS	ZBX_CFG_LTRIM_CHARS "\r\n"
378 
379 	FILE		*file;
380 	int		i, lineno, param_valid;
381 	char		line[MAX_STRING_LEN + 3], *parameter, *value;
382 	zbx_uint64_t	var;
383 	size_t		len;
384 #ifdef _WINDOWS
385 	wchar_t		*wcfg_file;
386 #endif
387 	if (++level > ZBX_MAX_INCLUDE_LEVEL)
388 	{
389 		zbx_error("Recursion detected! Skipped processing of '%s'.", cfg_file);
390 		return FAIL;
391 	}
392 
393 	if (NULL != cfg_file)
394 	{
395 #ifdef _WINDOWS
396 		wcfg_file = zbx_utf8_to_unicode(cfg_file);
397 		file = _wfopen(wcfg_file, L"r");
398 		zbx_free(wcfg_file);
399 
400 		if (NULL == file)
401 			goto cannot_open;
402 #else
403 		if (NULL == (file = fopen(cfg_file, "r")))
404 			goto cannot_open;
405 #endif
406 		for (lineno = 1; NULL != fgets(line, sizeof(line), file); lineno++)
407 		{
408 			/* check if line length exceeds limit (max. 2048 bytes) */
409 			len = strlen(line);
410 			if (MAX_STRING_LEN < len && NULL == strchr("\r\n", line[MAX_STRING_LEN]))
411 				goto line_too_long;
412 
413 			zbx_ltrim(line, ZBX_CFG_LTRIM_CHARS);
414 			zbx_rtrim(line, ZBX_CFG_RTRIM_CHARS);
415 
416 			if ('#' == *line || '\0' == *line)
417 				continue;
418 
419 			/* we only support UTF-8 characters in the config file */
420 			if (SUCCEED != zbx_is_utf8(line))
421 				goto non_utf8;
422 
423 			parameter = line;
424 			if (NULL == (value = strchr(line, '=')))
425 				goto non_key_value;
426 
427 			*value++ = '\0';
428 
429 			zbx_rtrim(parameter, ZBX_CFG_RTRIM_CHARS);
430 			zbx_ltrim(value, ZBX_CFG_LTRIM_CHARS);
431 
432 			zabbix_log(LOG_LEVEL_DEBUG, "cfg: para: [%s] val [%s]", parameter, value);
433 
434 			if (0 == strcmp(parameter, "Include"))
435 			{
436 				if (FAIL == parse_cfg_object(value, cfg, level, strict))
437 				{
438 					fclose(file);
439 					goto error;
440 				}
441 
442 				continue;
443 			}
444 
445 			param_valid = 0;
446 
447 			for (i = 0; NULL != cfg[i].parameter; i++)
448 			{
449 				if (0 != strcmp(cfg[i].parameter, parameter))
450 					continue;
451 
452 				param_valid = 1;
453 
454 				zabbix_log(LOG_LEVEL_DEBUG, "accepted configuration parameter: '%s' = '%s'",
455 						parameter, value);
456 
457 				switch (cfg[i].type)
458 				{
459 					case TYPE_INT:
460 						if (FAIL == str2uint64(value, "KMGT", &var))
461 							goto incorrect_config;
462 
463 						if (cfg[i].min > var || (0 != cfg[i].max && var > cfg[i].max))
464 							goto incorrect_config;
465 
466 						*((int *)cfg[i].variable) = (int)var;
467 						break;
468 					case TYPE_STRING_LIST:
469 						zbx_trim_str_list(value, ',');
470 						ZBX_FALLTHROUGH;
471 					case TYPE_STRING:
472 						*((char **)cfg[i].variable) =
473 								zbx_strdup(*((char **)cfg[i].variable), value);
474 						break;
475 					case TYPE_MULTISTRING:
476 						zbx_strarr_add((char ***)cfg[i].variable, value);
477 						break;
478 					case TYPE_UINT64:
479 						if (FAIL == str2uint64(value, "KMGT", &var))
480 							goto incorrect_config;
481 
482 						if (cfg[i].min > var || (0 != cfg[i].max && var > cfg[i].max))
483 							goto incorrect_config;
484 
485 						*((zbx_uint64_t *)cfg[i].variable) = var;
486 						break;
487 					case TYPE_CUSTOM:
488 						if (NULL != cfg[i].variable)
489 						{
490 							cfg_custom_parameter_parser_t custom_parser =
491 									(cfg_custom_parameter_parser_t)cfg[i].variable;
492 
493 							if (SUCCEED != custom_parser(value, &cfg[i]))
494 								goto incorrect_config;
495 
496 							continue;
497 						}
498 						break;
499 					default:
500 						assert(0);
501 				}
502 			}
503 
504 			if (0 == param_valid && ZBX_CFG_STRICT == strict)
505 				goto unknown_parameter;
506 		}
507 		fclose(file);
508 	}
509 
510 	if (1 != level)	/* skip mandatory parameters check for included files */
511 		return SUCCEED;
512 
513 	for (i = 0; NULL != cfg[i].parameter; i++) /* check for mandatory parameters */
514 	{
515 		if (PARM_MAND != cfg[i].mandatory)
516 			continue;
517 
518 		switch (cfg[i].type)
519 		{
520 			case TYPE_INT:
521 				if (0 == *((int *)cfg[i].variable))
522 					goto missing_mandatory;
523 				break;
524 			case TYPE_STRING:
525 			case TYPE_STRING_LIST:
526 				if (NULL == (*(char **)cfg[i].variable))
527 					goto missing_mandatory;
528 				break;
529 			default:
530 				assert(0);
531 		}
532 	}
533 
534 	return SUCCEED;
535 cannot_open:
536 	if (ZBX_CFG_FILE_REQUIRED != optional)
537 		return SUCCEED;
538 	zbx_error("cannot open config file \"%s\": %s", cfg_file, zbx_strerror(errno));
539 	goto error;
540 line_too_long:
541 	fclose(file);
542 	zbx_error("line %d exceeds %d byte length limit in config file \"%s\"", lineno, MAX_STRING_LEN, cfg_file);
543 	goto error;
544 non_utf8:
545 	fclose(file);
546 	zbx_error("non-UTF-8 character at line %d \"%s\" in config file \"%s\"", lineno, line, cfg_file);
547 	goto error;
548 non_key_value:
549 	fclose(file);
550 	zbx_error("invalid entry \"%s\" (not following \"parameter=value\" notation) in config file \"%s\", line %d",
551 			line, cfg_file, lineno);
552 	goto error;
553 incorrect_config:
554 	fclose(file);
555 	zbx_error("wrong value of \"%s\" in config file \"%s\", line %d", cfg[i].parameter, cfg_file, lineno);
556 	goto error;
557 unknown_parameter:
558 	fclose(file);
559 	zbx_error("unknown parameter \"%s\" in config file \"%s\", line %d", parameter, cfg_file, lineno);
560 	goto error;
561 missing_mandatory:
562 	zbx_error("missing mandatory parameter \"%s\" in config file \"%s\"", cfg[i].parameter, cfg_file);
563 error:
564 	exit(EXIT_FAILURE);
565 }
566 
parse_cfg_file(const char * cfg_file,struct cfg_line * cfg,int optional,int strict)567 int	parse_cfg_file(const char *cfg_file, struct cfg_line *cfg, int optional, int strict)
568 {
569 	return __parse_cfg_file(cfg_file, cfg, 0, optional, strict);
570 }
571 
check_cfg_feature_int(const char * parameter,int value,const char * feature)572 int	check_cfg_feature_int(const char *parameter, int value, const char *feature)
573 {
574 	if (0 != value)
575 	{
576 		zbx_error("\"%s\" configuration parameter cannot be used: Zabbix %s was compiled without %s",
577 				parameter, get_program_type_string(program_type), feature);
578 		return FAIL;
579 	}
580 
581 	return SUCCEED;
582 }
583 
check_cfg_feature_str(const char * parameter,const char * value,const char * feature)584 int	check_cfg_feature_str(const char *parameter, const char *value, const char *feature)
585 {
586 	if (NULL != value)
587 	{
588 		zbx_error("\"%s\" configuration parameter cannot be used: Zabbix %s was compiled without %s",
589 				parameter, get_program_type_string(program_type), feature);
590 		return FAIL;
591 	}
592 
593 	return SUCCEED;
594 }
595 
596 /******************************************************************************
597  *                                                                            *
598  * Function: zbx_set_data_destination_hosts                                   *
599  *                                                                            *
600  * Purpose: parse "ServerActive' parameter value and set destination servers  *
601  *          using a callback function                                         *
602  *                                                                            *
603  ******************************************************************************/
zbx_set_data_destination_hosts(char * active_hosts,add_serveractive_host_f cb)604 void	zbx_set_data_destination_hosts(char *active_hosts, add_serveractive_host_f cb)
605 {
606 	char	*l = active_hosts, *r;
607 
608 	do
609 	{
610 		char		*host = NULL;
611 		unsigned short	port;
612 
613 		if (NULL != (r = strchr(l, ',')))
614 			*r = '\0';
615 
616 		if (SUCCEED != parse_serveractive_element(l, &host, &port, (unsigned short)ZBX_DEFAULT_SERVER_PORT))
617 		{
618 			zbx_error("error parsing the \"ServerActive\" parameter: address \"%s\" is invalid", l);
619 			exit(EXIT_FAILURE);
620 		}
621 
622 		if (SUCCEED != cb(host, port))
623 		{
624 			zbx_error("error parsing the \"ServerActive\" parameter: address \"%s\" specified more than"
625 					" once", l);
626 			zbx_free(host);
627 			exit(EXIT_FAILURE);
628 		}
629 
630 		zbx_free(host);
631 
632 		if (NULL != r)
633 		{
634 			*r = ',';
635 			l = r + 1;
636 		}
637 	}
638 	while (NULL != r);
639 }
640