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(¤t_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