1 /****************************************************************************
2  *
3  * utils.c - NRPE Utility Functions
4  *
5  * License: GPLv2
6  * Copyright (c) 2009-2017 Nagios Enterprises
7  *               1999-2008 Ethan Galstad (nagios@nagios.org)
8  *
9  * Description:
10  *
11  * This file contains common network functions used in nrpe and check_nrpe.
12  *
13  * License Notice:
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28  *
29  ****************************************************************************/
30 
31 #include "../include/common.h"
32 #include "../include/utils.h"
33 #include <stdarg.h>
34 #ifdef HAVE_PATHS_H
35 #include <paths.h>
36 #endif
37 
38 #ifndef HAVE_ASPRINTF
39 extern int asprintf(char **ptr, const char *format, ...);
40 #endif
41 #ifndef HAVE_VASPRINTF
42 extern int vasprintf(char **ptr, const char *format, va_list ap);
43 #endif
44 
45 #ifndef NI_MAXSERV
46 # define NI_MAXSERV 32
47 #endif
48 
49 #ifndef NI_MAXHOST
50 # define NI_MAXHOST 1025
51 #endif
52 
53 extern char **environ;
54 
55 static unsigned long crc32_table[256];
56 
57 char *log_file = NULL;
58 FILE *log_fp = NULL;
59 
60 static int my_create_socket(struct addrinfo *ai, const char *bind_address, int redirect_stderr);
61 
62 
63 /* build the crc table - must be called before calculating the crc value */
generate_crc32_table(void)64 void generate_crc32_table(void)
65 {
66 	unsigned long crc, poly;
67 	int i, j;
68 
69 	poly = 0xEDB88320L;
70 	for (i = 0; i < 256; i++) {
71 		crc = i;
72 		for (j = 8; j > 0; j--) {
73 			if (crc & 1)
74 				crc = (crc >> 1) ^ poly;
75 			else
76 				crc >>= 1;
77 		}
78 		crc32_table[i] = crc;
79 	}
80 
81 	return;
82 }
83 
84 /* calculates the CRC 32 value for a buffer */
calculate_crc32(char * buffer,int buffer_size)85 unsigned long calculate_crc32(char *buffer, int buffer_size)
86 {
87 	register unsigned long crc = 0xFFFFFFFF;
88 	int this_char;
89 	int current_index;
90 
91 	for (current_index = 0; current_index < buffer_size; current_index++) {
92 		this_char = (int)buffer[current_index];
93 		crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32_table[(crc ^ this_char) & 0xFF];
94 	}
95 
96 	return (crc ^ 0xFFFFFFFF);
97 }
98 
99 /* fill a buffer with semi-random data */
randomize_buffer(char * buffer,int buffer_size)100 void randomize_buffer(char *buffer, int buffer_size)
101 {
102 	FILE *fp;
103 	int x;
104 	int seed;
105 
106 	/**** FILL BUFFER WITH RANDOM ALPHA-NUMERIC CHARACTERS ****/
107 
108 	/***************************************************************
109 	   Only use alpha-numeric characters because plugins usually
110 	   only generate numbers and letters in their output.  We
111 	   want the buffer to contain the same set of characters as
112 	   plugins, so its harder to distinguish where the real output
113 	   ends and the rest of the buffer (padded randomly) starts.
114 	 ***************************************************************/
115 
116 	/* try to get seed value from /dev/urandom, as its a better source of entropy */
117 	fp = fopen("/dev/urandom", "r");
118 	if (fp != NULL) {
119 		seed = fgetc(fp);
120 		fclose(fp);
121 	}
122 	/* else fallback to using the current time as the seed */
123 	else
124 		seed = (int)time(NULL);
125 
126 	srand(seed);
127 	for (x = 0; x < buffer_size; x++)
128 		buffer[x] = (int)'0' + (int)(72.0 * rand() / (RAND_MAX + 1.0));
129 
130 	return;
131 }
132 
133 /* opens a connection to a remote host */
134 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE
my_connect(const char * host,struct sockaddr_storage * hostaddr,u_short port,int address_family,const char * bind_address,int redirect_stderr)135 int my_connect(const char *host, struct sockaddr_storage *hostaddr, u_short port,
136 			   int address_family, const char *bind_address, int redirect_stderr)
137 #else
138 int my_connect(const char *host, struct sockaddr *hostaddr, u_short port,
139 			   int address_family, const char *bind_address, int redirect_stderr)
140 #endif
141 {
142 	struct addrinfo hints, *ai, *aitop;
143 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
144 	int gaierr;
145 	int sock = -1;
146 
147 	FILE *output = stderr;
148 	if (redirect_stderr)
149 		output = stdout;
150 
151 	memset(&hints, 0, sizeof(hints));
152 	hints.ai_family = address_family;
153 	hints.ai_socktype = SOCK_STREAM;
154 	snprintf(strport, sizeof strport, "%u", port);
155 	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
156 		fprintf(output, "Could not resolve hostname %.100s: %s\n", host, gai_strerror(gaierr));
157 		exit(1);
158 	}
159 
160 	/*
161 	 * Loop through addresses for this host, and try each one in
162 	 * sequence until the connection succeeds.
163 	 */
164 	for (ai = aitop; ai; ai = ai->ai_next) {
165 		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
166 			continue;
167 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
168 						strport, sizeof(strport), NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
169 			fprintf(output, "my_connect: getnameinfo failed\n");
170 			continue;
171 		}
172 
173 		/* Create a socket for connecting. */
174 		sock = my_create_socket(ai, bind_address, redirect_stderr);
175 		if (sock < 0)
176 			continue;			/* Any error is already output */
177 
178 		if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) {
179 			/* Successful connection. */
180 			memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
181 			break;
182 		} else {
183 			fprintf(output, "connect to address %s port %s: %s\n", ntop, strport,
184 					strerror(errno));
185 			close(sock);
186 			sock = -1;
187 		}
188 	}
189 
190 	freeaddrinfo(aitop);
191 
192 	/* Return failure if we didn't get a successful connection. */
193 	if (sock == -1) {
194 		fprintf(output, "connect to host %s port %s: %s\n", host, strport, strerror(errno));
195 		return -1;
196 	}
197 	return sock;
198 }
199 
200 /* Creates a socket for the connection. */
my_create_socket(struct addrinfo * ai,const char * bind_address,int redirect_stderr)201 int my_create_socket(struct addrinfo *ai, const char *bind_address, int redirect_stderr)
202 {
203 	int sock, gaierr;
204 	struct addrinfo hints, *res;
205 
206 	FILE *output = stderr;
207 	if (redirect_stderr)
208 		output = stdout;
209 
210 	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
211 	if (sock < 0)
212 		fprintf(output, "socket: %.100s\n", strerror(errno));
213 
214 	/* Bind the socket to an alternative local IP address */
215 	if (bind_address == NULL)
216 		return sock;
217 
218 	memset(&hints, 0, sizeof(hints));
219 	hints.ai_family = ai->ai_family;
220 	hints.ai_socktype = ai->ai_socktype;
221 	hints.ai_protocol = ai->ai_protocol;
222 	hints.ai_flags = AI_PASSIVE;
223 	gaierr = getaddrinfo(bind_address, NULL, &hints, &res);
224 	if (gaierr) {
225 		fprintf(output, "getaddrinfo: %s: %s\n", bind_address, gai_strerror(gaierr));
226 		close(sock);
227 		return -1;
228 	}
229 	if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
230 		fprintf(output, "bind: %s: %s\n", bind_address, strerror(errno));
231 		close(sock);
232 		freeaddrinfo(res);
233 		return -1;
234 	}
235 	freeaddrinfo(res);
236 	return sock;
237 }
238 
add_listen_addr(struct addrinfo ** listen_addrs,int address_family,char * addr,int port)239 void add_listen_addr(struct addrinfo **listen_addrs, int address_family, char *addr, int port)
240 {
241 	struct addrinfo hints, *ai, *aitop;
242 	char strport[NI_MAXSERV];
243 	int gaierr;
244 
245 	memset(&hints, 0, sizeof(hints));
246 	hints.ai_family = address_family;
247 	hints.ai_socktype = SOCK_STREAM;
248 	hints.ai_flags = (addr == NULL) ? AI_PASSIVE : 0;
249 	snprintf(strport, sizeof strport, "%d", port);
250 	if ((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
251 		logit(LOG_ERR, "bad addr or host: %s (%s)\n", addr ? addr : "<NULL>",
252 			   gai_strerror(gaierr));
253 		exit(1);
254 	}
255 	for (ai = aitop; ai->ai_next; ai = ai->ai_next) ;
256 	ai->ai_next = *listen_addrs;
257 	*listen_addrs = aitop;
258 }
259 
clean_environ(const char * keep_env_vars,const char * nrpe_user)260 int clean_environ(const char *keep_env_vars, const char *nrpe_user)
261 {
262 #if defined(HAVE_PATHS_H) && defined(_PATH_STDPATH)
263 	static char	*path = _PATH_STDPATH;
264 #else
265 	static char	*path = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
266 #endif
267 	struct passwd *pw;
268 	size_t len, var_sz = 0;
269 	char **kept = NULL, *value, *var, *keep = NULL;
270 	int i, j, keepcnt = 0;
271 
272 	if (keep_env_vars && *keep_env_vars)
273 		asprintf(&keep, "%s,NRPE_MULTILINESUPPORT,NRPE_PROGRAMVERSION", keep_env_vars);
274 	else
275 		asprintf(&keep, "NRPE_MULTILINESUPPORT,NRPE_PROGRAMVERSION");
276 	if (keep == NULL) {
277 		logit(LOG_ERR, "Could not sanitize the environment. Aborting!");
278 		return ERROR;
279 	}
280 
281 	++keepcnt;
282 	i = strlen(keep);
283 	while (i--) {
284 		if (keep[i] == ',')
285 			++keepcnt;
286 	}
287 
288 	if ((kept = calloc(keepcnt + 1, sizeof(char *))) == NULL) {
289 		logit(LOG_ERR, "Could not sanitize the environment. Aborting!");
290 		return ERROR;
291 	}
292 	for (i = 0, var = my_strsep(&keep, ","); var != NULL; var = my_strsep(&keep, ","))
293 		kept[i++] = strip(var);
294 
295 	var = NULL;
296 	i = 0;
297 	while (environ[i]) {
298 		value = environ[i];
299 		if ((len = strcspn(value, "=")) == 0) {
300 			free(keep);
301 			free(kept);
302 			free(var);
303 			logit(LOG_ERR, "Could not sanitize the environment. Aborting!");
304 			return ERROR;
305 		}
306 		if (len >= var_sz) {
307 			var_sz = len + 1;
308 			var = realloc(var, var_sz);
309 		}
310 		strncpy(var, environ[i], var_sz);
311 		var[len] = 0;
312 
313 		for (j = 0; kept[j]; ++j) {
314 			if (!strncmp(var, kept[j], strlen(kept[j])))
315 				break;
316 		}
317 		if (kept[j]) {
318 			++i;
319 			continue;
320 		}
321 
322 		unsetenv(var);
323 	}
324 
325 	free(var);
326 	free(keep);
327 	free(kept);
328 
329 
330 	char * user = NULL;
331 
332 	if (nrpe_user != NULL) {
333 		user = strdup(nrpe_user);
334 		pw = (struct passwd *)getpwnam(nrpe_user);
335 	}
336 
337 	if (nrpe_user == NULL || pw == NULL) {
338 		pw = (struct passwd *)getpwuid(getuid());
339 		if (pw != NULL) {
340 			user = strdup(pw->pw_name);
341 		}
342 	}
343 
344 	if (pw == NULL) {
345 		free(user);
346 		return OK;
347 	}
348 
349 	setenv("PATH", path, 1);
350 	setenv("IFS", " \t\n", 1);
351 	setenv("LOGNAME", user, 0);
352 	setenv("USER", user, 0);
353 	setenv("HOME", pw->pw_dir, 0);
354 	setenv("SHELL", pw->pw_shell, 0);
355 
356 	free(user);
357 
358 	return OK;
359 }
360 
strip(char * buffer)361 char *strip(char *buffer)
362 {
363 	int x;
364 	int index;
365 	char *buf = buffer;
366 
367 	for (x = strlen(buffer); x >= 1; x--) {
368 		index = x - 1;
369 		if (buffer[index] == ' ' || buffer[index] == '\r' || buffer[index] == '\n'
370 			|| buffer[index] == '\t')
371 			buffer[index] = '\x0';
372 		else
373 			break;
374 	}
375 
376 	while (*buf == ' ' || *buf == '\r' || *buf == '\n' || *buf == '\t') {
377 		++buf;
378 		--x;
379 	}
380 	if (buf != buffer) {
381 		memmove(buffer, buf, x);
382 		buffer[x] = '\x0';
383 	}
384 
385 	return buffer;
386 }
387 
388 /* sends all data - thanks to Beej's Guide to Network Programming */
sendall(int s,char * buf,int * len)389 int sendall(int s, char *buf, int *len)
390 {
391 	int total = 0;
392 	int bytesleft = *len;
393 	int n = 0;
394 
395 	/* send all the data */
396 	while (total < *len) {
397 		n = send(s, buf + total, bytesleft, 0);	/* send some data */
398 		if (n == -1)			/* break on error */
399 			break;
400 		/* apply bytes we sent */
401 		total += n;
402 		bytesleft -= n;
403 	}
404 
405 	*len = total;				/* return number of bytes actually sent here */
406 	return n == -1 ? -1 : 0;	/* return -1 on failure, 0 on success */
407 }
408 
409 /* receives all data - modelled after sendall() */
recvall(int s,char * buf,int * len,int timeout)410 int recvall(int s, char *buf, int *len, int timeout)
411 {
412 	time_t start_time;
413 	time_t current_time;
414 	int total = 0;
415 	int bytesleft = *len;
416 	int n = 0;
417 
418 	bzero(buf, *len);			/* clear the receive buffer */
419 	time(&start_time);
420 
421 	/* receive all data */
422 	while (total < *len) {
423 		n = recv(s, buf + total, bytesleft, 0);	/* receive some data */
424 
425 		if (n == -1 && errno == EAGAIN) {
426 			/* no data has arrived yet (non-blocking socket) */
427 			time(&current_time);
428 			if (current_time - start_time > timeout)
429 				break;
430 			sleep(1);
431 			continue;
432 		} else if (n <= 0)
433 			break;				/* receive error or client disconnect */
434 
435 		/* apply bytes we received */
436 		total += n;
437 		bytesleft -= n;
438 	}
439 
440 	/* return number of bytes actually received here */
441 	*len = total;
442 
443 	/* return <=0 on failure, bytes received on success */
444 	return (n <= 0) ? n : total;
445 }
446 
447 
448 /* fixes compiler problems under Solaris, since strsep() isn't included */
449 
450 /* this code is taken from the glibc source */
my_strsep(char ** stringp,const char * delim)451 char *my_strsep(char **stringp, const char *delim)
452 {
453 	char *begin, *end;
454 
455 	begin = *stringp;
456 	if (begin == NULL)
457 		return NULL;
458 
459 	/* A frequent case is when the delimiter string contains only one
460 	   character.  Here we don't need to call the expensive `strpbrk'
461 	   function and instead work using `strchr'.  */
462 	if (delim[0] == '\0' || delim[1] == '\0') {
463 		char ch = delim[0];
464 
465 		if (ch == '\0')
466 			end = NULL;
467 		else {
468 			if (*begin == ch)
469 				end = begin;
470 			else
471 				end = strchr(begin + 1, ch);
472 		}
473 
474 	} else
475 		end = strpbrk(begin, delim);	/* Find the end of the token.  */
476 
477 	if (end) {
478 		/* Terminate the token and set *STRINGP past NUL character.  */
479 		*end++ = '\0';
480 		*stringp = end;
481 	} else
482 		/* No more delimiters; this is the last token.  */
483 		*stringp = NULL;
484 
485 	return begin;
486 }
487 
open_log_file()488 void open_log_file()
489 {
490 	int fh;
491 	int flags = O_RDWR|O_APPEND|O_CREAT;
492 	struct stat st;
493 
494 	close_log_file();
495 
496 	if (!log_file)
497 		return;
498 
499 #ifdef O_NOFOLLOW
500 	flags |= O_NOFOLLOW;
501 #endif
502 	if ((fh = open(log_file, flags, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
503 		printf("Warning: Cannot open log file '%s' for writing\n", log_file);
504 		logit(LOG_WARNING, "Warning: Cannot open log file '%s' for writing", log_file);
505 		return;
506 	}
507 	log_fp = fdopen(fh, "a+");
508 	if(log_fp == NULL) {
509 		printf("Warning: Cannot open log file '%s' for writing\n", log_file);
510 		logit(LOG_WARNING, "Warning: Cannot open log file '%s' for writing", log_file);
511 		return;
512 		}
513 
514 	if ((fstat(fh, &st)) == -1) {
515 		log_fp = NULL;
516 		close(fh);
517 		printf("Warning: Cannot fstat log file '%s'\n", log_file);
518 		logit(LOG_WARNING, "Warning: Cannot fstat log file '%s'", log_file);
519 		return;
520 	}
521 	if (st.st_nlink != 1 || (st.st_mode & S_IFMT) != S_IFREG) {
522 		log_fp = NULL;
523 		close(fh);
524 		printf("Warning: log file '%s' has an invalid mode\n", log_file);
525 		logit(LOG_WARNING, "Warning: log file '%s' has an invalid mode", log_file);
526 		return;
527 	}
528 
529 	(void)fcntl(fileno(log_fp), F_SETFD, FD_CLOEXEC);
530 }
531 
logit(int priority,const char * format,...)532 void logit(int priority, const char *format, ...)
533 {
534 	time_t	log_time = 0L;
535 	va_list	ap;
536 	char	*buffer = NULL;
537 
538 	if (!format || !*format)
539 		return;
540 
541 	va_start(ap, format);
542 	if(vasprintf(&buffer, format, ap) > 0) {
543 		if (log_fp) {
544 			time(&log_time);
545 			/* strip any newlines from the end of the buffer */
546 			strip(buffer);
547 
548 			/* write the buffer to the log file */
549 			fprintf(log_fp, "[%llu] %s\n", (unsigned long long)log_time, buffer);
550 			fflush(log_fp);
551 
552 		} else
553 			syslog(priority, "%s", buffer);
554 
555 		free(buffer);
556 	}
557 	va_end(ap);
558 }
559 
close_log_file()560 void close_log_file()
561 {
562 	if(!log_fp)
563 		return;
564 
565 	fflush(log_fp);
566 	fclose(log_fp);
567 	log_fp = NULL;
568 	return;
569 }
570 
571 /* show license */
display_license(void)572 void display_license(void)
573 {
574 	printf("This program is released under the GPL (see below) with the additional\n");
575 	printf("exemption that compiling, linking, and/or using OpenSSL is allowed.\n\n");
576 
577 	printf("This program is free software; you can redistribute it and/or modify\n");
578 	printf("it under the terms of the GNU General Public License as published by\n");
579 	printf("the Free Software Foundation; either version 2 of the License, or\n");
580 	printf("(at your option) any later version.\n\n");
581 	printf("This program is distributed in the hope that it will be useful,\n");
582 	printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
583 	printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n");
584 	printf("GNU General Public License for more details.\n\n");
585 	printf("You should have received a copy of the GNU General Public License\n");
586 	printf("along with this program; if not, write to the Free Software\n");
587 	printf("Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");
588 
589 	return;
590 }
591