1 /******************************************************************************
2  *
3  * worker-ping.c - Nagios Core 4 worker to handle ping checke
4  *
5  * Program: Nagios Core
6  * License: GPL
7  *
8  * First Written:   01-03-2013 (start of development)
9  *
10  * Description:
11  *
12  * License:
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License version 2 as
16  * published by the Free Software Foundation.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26  *
27  *****************************************************************************/
28 
29 #include "config.h"
30 #include "workers.h"
31 
32 /* Local macro definitions */
33 #define PING_WORKER_VERSION "0.2"
34 #define PING_WORKER_MODIFICATION_DATE "02-Feb-13"
35 
36 #define PW_OK		0
37 #define PW_ERROR	1
38 
39 /* Local function declarations */
40 int			daemon_init( void);
41 int			drop_privileges( char *, char *);
42 void		print_license( void);
43 void		print_usage( char *);
44 void		print_version( void);
45 void		parse_worker_command_line( int, char **, char **, int *, char **,
46 					char **, char **);
47 static int	worker( const char *);
48 
49 /* Everything starts here */
main(int argc,char ** argv,char ** env)50 int main( int argc, char **argv, char **env) {
51 	int daemon_mode = FALSE;
52 	char *worker_socket;
53 	char *worker_user = ( char *)0;
54 	char *worker_group = ( char *)0;
55 
56 	if( FALSE == daemon_mode) {
57 		printf( "Greetings from the ping worker.\n");
58 	}
59 
60 	parse_worker_command_line( argc, argv, env, &daemon_mode, &worker_socket,
61 			&worker_user, &worker_group);
62 
63 	if( FALSE == daemon_mode) {
64 		print_version();
65 		printf( "Worker socket is %s.\n", worker_socket);
66 	}
67 
68 	/* drop privileges */
69 	if( drop_privileges( worker_user, worker_group) == PW_ERROR) {
70 		fprintf( stderr, "Failed to drop privileges. Aborting.\n");
71 		exit( 1);
72 	}
73 
74 	if( TRUE == daemon_mode) {
75 		if( daemon_init() == ERROR) {
76 			fprintf( stderr,
77 					"Bailing out due to failure to daemonize. (PID=%d)\n",
78 					( int)getpid());
79 			exit( 1);
80 		}
81 	}
82 
83 	/* Enter the worker code */
84 	if( worker( worker_socket)) {
85 		exit( 1);
86 	}
87 }
88 
parse_worker_command_line(int argc,char ** argv,char ** env,int * daemon_mode,char ** worker_socket,char ** worker_user,char ** worker_group)89 void parse_worker_command_line( int argc, char **argv, char **env,
90 		int *daemon_mode, char **worker_socket, char **worker_user,
91 		char **worker_group) {
92 	int c = 0;
93 	int display_usage = FALSE;
94 	int display_license = FALSE;
95 
96 #ifdef HAVE_GETOPT_H
97 	int option_index = 0;
98 	static struct option long_options[] = {
99 		{ "help", no_argument, 0, 'h'},
100 		{ "version", no_argument, 0, 'V'},
101 		{ "license", no_argument, 0, 'V'},
102 		{ "daemon", no_argument, 0, 'd'},
103 		{ "worker", required_argument, 0, 'W'},
104 		{ "user", required_argument, 0, 'u'},
105 		{ "group", required_argument, 0, 'g'},
106 		{ 0, 0, 0, 0}
107 	};
108 #define getopt( argc, argv, o) getopt_long( argc, argv, o, long_options, &option_index)
109 #endif
110 
111 	/* get all command line arguments */
112 	while( 1) {
113 		c = getopt( argc, argv, "+:hVdW:u:g:");
114 
115 		if( -1 == c || EOF == c) break;
116 
117 		switch( c) {
118 
119 			case '?': /* usage */
120 			case 'h':
121 				display_usage = TRUE;
122 				break;
123 
124 			case 'V': /* version */
125 				display_license = TRUE;
126 				break;
127 
128 			case 'd': /* daemon mode */
129 				*daemon_mode = TRUE;
130 				break;
131 
132 			case 'W':
133 				*worker_socket = optarg;
134 				break;
135 
136 			case 'u':
137 				*worker_user = optarg;
138 				break;
139 
140 			case 'g':
141 				*worker_group = optarg;
142 				break;
143 
144 			case ':':
145 				printf( "Missing argument for command line option '%c'.\n\n",
146 						optopt);
147 				print_usage( argv[ 0]);
148 				exit( 1);
149 				break;
150 
151 			default:
152 				printf( "Unknown command line option '%c'.\n\n", c);
153 				print_usage( argv[ 0]);
154 				exit( 1);
155 				break;
156 		}
157 	}
158 
159 	if( TRUE == display_license) {
160 		print_version();
161 		print_license();
162 		exit( 0);
163 	}
164 
165 	if( TRUE == display_usage) {
166 		print_usage( argv[ 0]);
167 		exit( 0);
168 	}
169 
170 }
171 
print_license(void)172 void print_license( void) {
173 	printf( "\nThis program is free software; you can redistribute it and/or modify\n");
174 	printf( "it under the terms of the GNU General Public License version 2 as\n");
175 	printf( "published by the Free Software Foundation.\n\n");
176 	printf( "This program is distributed in the hope that it will be useful,\n");
177 	printf( "but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
178 	printf( "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n");
179 	printf( "GNU General Public License for more details.\n\n");
180 	printf( "You should have received a copy of the GNU General Public License\n");
181 	printf( "along with this program; if not, write to the Free Software\n");
182 	printf( "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");
183 }
184 
print_usage(char * program_name)185 void print_usage( char *program_name) {
186 	printf( "\nUsage: %s [-?|-h|--help] [-V|--version] [-d]\n", program_name);
187 	printf( "        -W /path/to/socket -u user -g group\n");
188 	printf( "\n");
189 	printf( "Options:\n");
190 	printf( "\n");
191 	printf( "  -d, --daemon                 Starts the ping worker in daemon mode,\n");
192 	printf( "                               instead of as a foreground process\n");
193 	printf( "  -W, --worker /path/to/socket Act as a worker for an already running daemon\n");
194 	printf( "  -u, --user user              Drop privileges to the specified user\n");
195 	printf( "  -g, --group group            Drop privileges to the specified user\n");
196 	printf( "\n");
197 	printf( "Visit the Nagios website at https://www.nagios.org/ for bug fixes, new\n");
198 	printf( "releases, online documentation, FAQs, information on subscribing to\n");
199 	printf( "the mailing lists, and commercial support options for Nagios.\n");
200 	printf( "\n");
201 }
202 
print_version(void)203 void print_version( void) {
204 	printf( "\nNagios Core 4 Ping Worker %s\n", PING_WORKER_VERSION);
205 	printf( "Copyright (c) 2013-present Nagios Core Development Team\n");
206 	printf( "        and Community Contributors\n");
207 	printf( "Last Modified: %s\n", PING_WORKER_MODIFICATION_DATE);
208 	printf( "License: GPL\n");
209 	printf( "Website: https://www.nagios.org\n");
210 }
211 
worker(const char * path)212 static int worker( const char *path)
213 {
214 	int sd, ret;
215 	char response[128];
216 
217 	/*set_loadctl_defaults();*/
218 
219 	sd = nsock_unix( path, NSOCK_TCP | NSOCK_CONNECT);
220 	if( sd < 0) {
221 		printf( "Failed to connect to query socket '%s': %s: %s\n",
222 				path, nsock_strerror( sd), strerror( errno));
223 		return 1;
224 	}
225 
226 	ret = nsock_printf_nul( sd, "@wproc register name=Core Ping Worker %d;pid=%d;plugin=check_ping", getpid(), getpid());
227 	if( ret < 0) {
228 		printf( "Failed to register as worker.\n");
229 		return 1;
230 	}
231 
232 	ret = read( sd, response, 3);
233 	if( ret != 3) {
234 		printf( "Failed to read response from wproc manager\n");
235 		return 1;
236 	}
237 	if( memcmp( response, "OK", 3)) {
238 		read( sd, response + 3, sizeof(response) - 4);
239 		response[ sizeof( response) - 2] = 0;
240 		printf( "Failed to register with wproc manager: %s\n", response);
241 		return 1;
242 	}
243 
244 	enter_worker( sd, start_cmd);
245 	return 0;
246 }
247 
drop_privileges(char * user,char * group)248 int drop_privileges( char *user, char *group) {
249 	uid_t uid = -1;
250 	gid_t gid = -1;
251 	struct group *grp = NULL;
252 	struct passwd *pw = NULL;
253 	int result = PW_OK;
254 
255 	/* only drop privileges if we're running as root, so we don't
256 		interfere with being debugged while running as some random user */
257 	if( getuid() != 0) {
258 		return PW_OK;
259 	}
260 
261 	/* set effective group ID */
262 	if( NULL != group) {
263 
264 		/* see if this is a group name */
265 		if( strspn( group, "0123456789") < strlen( group)) {
266 			grp = ( struct group *)getgrnam( group);
267 			if( NULL != grp) {
268 				gid = ( gid_t)(grp->gr_gid);
269 			}
270 			else {
271 				fprintf( stderr,
272 					"Warning: Could not get group entry for '%s'\n", group);
273 			}
274 		}
275 
276 		/* else we were passed the GID */
277 		else {
278 			gid = ( gid_t)atoi( group);
279 		}
280 
281 		/* set effective group ID if other than current EGID */
282 		if( gid != getegid()) {
283 			if( setgid( gid) == -1) {
284 				fprintf( stderr, "Warning: Could not set effective GID=%d\n",
285 						( int)gid);
286 				result = PW_ERROR;
287 			}
288 		}
289 	}
290 
291 	/* set effective user ID */
292 	if( NULL != user) {
293 
294 		/* see if this is a user name */
295 		if( strspn( user, "0123456789") < strlen( user)) {
296 			pw = ( struct passwd *)getpwnam( user);
297 			if( NULL != pw) {
298 				uid = ( uid_t)(pw->pw_uid);
299 			}
300 			else {
301 				fprintf( stderr,
302 						"Warning: Could not get passwd entry for '%s'\n", user);
303 			}
304 		}
305 
306 		/* else we were passed the UID */
307 		else {
308 			uid = ( uid_t)atoi( user);
309 		}
310 
311 #ifdef HAVE_INITGROUPS
312 
313 		if( uid != geteuid()) {
314 
315 			/* initialize supplementary groups */
316 			if( initgroups( user, gid) == -1) {
317 				if( EPERM == errno) {
318 					fprintf( stderr, "Warning: Unable to change supplementary "
319 							"groups using initgroups() -- I hope you know what "
320 							"you're doing\n");
321 				}
322 				else {
323 					fprintf( stderr, "Warning: Possibly root user failed "
324 							"dropping privileges with initgroups()\n");
325 					return PW_ERROR;
326 				}
327 			}
328 		}
329 #endif
330 		if( setuid( uid) == -1) {
331 			fprintf( stderr, "Warning: Could not set effective UID=%d\n",
332 					( int)uid);
333 			result = PW_ERROR;
334 		}
335 	}
336 
337 	return result;
338 }
339 
daemon_init(void)340 int daemon_init( void) {
341 	pid_t pid = -1;
342 	int pidno = 0;
343 	int lockfile = 0;
344 	int val = 0;
345 	char buf[256];
346 	struct flock lock;
347 	char *homedir = NULL;
348 
349 #ifdef RLIMIT_CORE
350 	struct rlimit limit;
351 #endif
352 
353 	/* change working directory. scuttle home if we're dumping core */
354 	homedir = getenv( "HOME");
355 #ifdef DAEMON_DUMPS_CORE
356 	if( NULL != homedir) {
357 		chdir( homedir);
358 	}
359 	else {
360 #endif
361 		chdir( "/");
362 #ifdef DAEMON_DUMPS_CORE
363 	}
364 #endif
365 
366 	umask( S_IWGRP | S_IWOTH);
367 
368 	/* close existing stdin, stdout, stderr */
369 	close( 0);
370 	close( 1);
371 	close( 2);
372 
373 	/* THIS HAS TO BE DONE TO AVOID PROBLEMS WITH STDERR BEING REDIRECTED TO SERVICE MESSAGE PIPE! */
374 	/* re-open stdin, stdout, stderr with known values */
375 	open( "/dev/null", O_RDONLY);
376 	open( "/dev/null", O_WRONLY);
377 	open( "/dev/null", O_WRONLY);
378 
379 #ifdef USE_LOCKFILE
380 	lockfile = open( lock_file,
381 			O_RDWR | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
382 
383 	if( lockfile < 0) {
384 		logit( NSLOG_RUNTIME_ERROR, TRUE, "Failed to obtain lock on file %s: %s\n", lock_file, strerror(errno));
385 		logit( NSLOG_PROCESS_INFO | NSLOG_RUNTIME_ERROR, TRUE, "Bailing out due to errors encountered while attempting to daemonize... (PID=%d)", (int)getpid());
386 
387 		exit( 1);
388 	}
389 
390 	/* see if we can read the contents of the lockfile */
391 	if(( val = read( lockfile, buf, ( size_t)10)) < 0) {
392 		logit(NSLOG_RUNTIME_ERROR, TRUE, "Lockfile exists but cannot be read");
393 		exit( 1);
394 	}
395 
396 	/* we read something - check the PID */
397 	if( val > 0) {
398 		if(( val = sscanf( buf, "%d", &pidno)) < 1) {
399 			logit( NSLOG_RUNTIME_ERROR, TRUE, "Lockfile '%s' does not contain a valid PID (%s)", lock_file, buf);
400 			exit( 1);
401 		}
402 	}
403 
404 	/* check for SIGHUP */
405 	if( val == 1 && ( pid = ( pid_t)pidno) == getpid()) {
406 		close( lockfile);
407 		return OK;
408 	}
409 #endif
410 
411 	/* exit on errors... */
412 	if(( pid = fork()) < 0) {
413 		return( PW_ERROR);
414 	}
415 
416 	/* parent process goes away.. */
417 	else if((int)pid != 0) {
418 		exit( 0);
419 	}
420 
421 	/* child continues... */
422 
423 	/* child becomes session leader... */
424 	setsid();
425 
426 #ifdef USE_LOCKFILE
427 	/* place a file lock on the lock file */
428 	lock.l_type = F_WRLCK;
429 	lock.l_start = 0;
430 	lock.l_whence = SEEK_SET;
431 	lock.l_len = 0;
432 	if( fcntl( lockfile, F_SETLK, &lock) < 0) {
433 		if( EACCES == errno || EAGAIN == errno) {
434 			fcntl( lockfile, F_GETLK, &lock);
435 			logit( NSLOG_RUNTIME_ERROR, TRUE, "Lockfile '%s' looks like its already held by another instance of Nagios (PID %d).  Bailing out...", lock_file, (int)lock.l_pid);
436 			}
437 		else {
438 			logit(NSLOG_RUNTIME_ERROR, TRUE, "Cannot lock lockfile '%s': %s. Bailing out...", lock_file, strerror(errno));
439 
440 		}
441 		exit( 1);
442 	}
443 #endif
444 
445 	/* prevent daemon from dumping a core file... */
446 #if defined( RLIMIT_CORE) && defined( DAEMON_DUMPS_CORE)
447 	getrlimit( RLIMIT_CORE, &limit);
448 	limit.rlim_cur = 0;
449 	setrlimit( RLIMIT_CORE, &limit);
450 #endif
451 
452 #ifdef USE_LOCKFILE
453 	/* write PID to lockfile... */
454 	lseek( lockfile, 0, SEEK_SET);
455 	ftruncate( lockfile, 0);
456 	sprintf( buf, "%d\n", ( int)getpid());
457 	write( lockfile, buf, strlen( buf));
458 
459 	/* make sure lock file stays open while program is executing... */
460 	val = fcntl( lockfile, F_GETFD, 0);
461 	val |= FD_CLOEXEC;
462 	fcntl( lockfile, F_SETFD, val);
463 #endif
464 
465 	return PW_OK;
466 }
467