When opening files for write, ensure they aren't symbolic links

Issue #1048 identified that if, for example, a non privileged user
created a symbolic link from /etc/keepalvied.data to /etc/passwd,
writing to /etc/keepalived.data (which could be invoked via DBus)
would cause /etc/passwd to be overwritten.

This commit stops keepalived writing to pathnames where the ultimate
component is a symbolic link, by setting O_NOFOLLOW whenever opening
a file for writing.

This might break some setups, where, for example, /etc/keepalived.data
was a symbolic link to /home/fred/keepalived.data. If this was the case,
instead create a symbolic link from /home/fred/keepalived.data to
/tmp/keepalived.data, so that the file is still accessible via
/home/fred/keepalived.data.

There doesn't appear to be a way around this backward incompatibility,
since even checking if the pathname is a symbolic link prior to opening
for writing would create a race condition.

Signed-off-by: Quentin Armitage <quentin@armitage.org.uk>
notify_fifo_exec(thread_master_t *m, int (*func) (thread_t *), void *arg, notify_script_t *script)
{
	pid_t pid;
	int retval;
	char *scr;

	pid = local_fork();

	/* In case of fork is error. */
	if (pid < 0) {
		log_message(LOG_INFO, "Failed fork process");
		return -1;
	}

	/* In case of this is parent process */
	if (pid) {
		thread_add_child(m, func, arg, pid, TIMER_NEVER);
		return 0;
	}

#ifdef _MEM_CHECK_
	skip_mem_dump();
#endif

	setpgid(0, 0);
	set_privileges(script->uid, script->gid);

	if (script->flags | SC_EXECABLE) {
		/* If keepalived dies, we want the script to die */
		prctl(PR_SET_PDEATHSIG, SIGTERM);

		execve(script->args[0], script->args, environ);

		if (errno == EACCES)
			log_message(LOG_INFO, "FIFO notify script %s is not executable", script->args[0]);
		else
			log_message(LOG_INFO, "Unable to execute FIFO notify script %s - errno %d - %m", script->args[0], errno);
	}
	else {
		retval = system(scr = cmd_str(script));

		if (retval == 127) {
			/* couldn't exec command */
			log_message(LOG_ALERT, "Couldn't exec FIFO command: %s", scr);
		}
		else if (retval == -1)
			log_message(LOG_ALERT, "Error exec-ing FIFO command: %s", scr);

		exit(0);
	}

	/* unreached unless error */
	exit(0);
}

notify_fifo_exec(thread_master_t *m, int (*func) (thread_t *), void *arg, notify_script_t *script)
{
	pid_t pid;
	int retval;
	char *scr;

	pid = local_fork();

	/* In case of fork is error. */
	if (pid < 0) {
		log_message(LOG_INFO, "Failed fork process");
		return -1;
	}

	/* In case of this is parent process */
	if (pid) {
		thread_add_child(m, func, arg, pid, TIMER_NEVER);
		return 0;
	}

#ifdef _MEM_CHECK_
	skip_mem_dump();
#endif

	setpgid(0, 0);
	set_privileges(script->uid, script->gid);

	if (script->flags | SC_EXECABLE) {
		/* If keepalived dies, we want the script to die */
		prctl(PR_SET_PDEATHSIG, SIGTERM);

		execve(script->args[0], script->args, environ);

		if (errno == EACCES)
			log_message(LOG_INFO, "FIFO notify script %s is not executable", script->args[0]);
		else
			log_message(LOG_INFO, "Unable to execute FIFO notify script %s - errno %d - %m", script->args[0], errno);
	}
	else {
		retval = system(scr = cmd_str(script));

		if (retval == 127) {
			/* couldn't exec command */
			log_message(LOG_ALERT, "Couldn't exec FIFO command: %s", scr);
		}
		else if (retval == -1)
			log_message(LOG_ALERT, "Error exec-ing FIFO command: %s", scr);

		exit(0);
	}

	/* unreached unless error */
	exit(0);
}
