Code: vrrp_print_json(void)
{
	FILE *file;
	element e;
	struct json_object *array;

 	if (LIST_ISEMPTY(vrrp_data->vrrp))
 		return;
 
	file = fopen ("/tmp/keepalived.json","w");
 	if (!file) {
 		log_message(LOG_INFO, "Can't open /tmp/keepalived.json (%d: %s)",
 			errno, strerror(errno));
		return;
	}

	array = json_object_new_array();

	for (e = LIST_HEAD(vrrp_data->vrrp); e; ELEMENT_NEXT(e)) {
		struct json_object *instance_json, *json_stats, *json_data,
			*vips, *evips, *track_ifp, *track_script;
#ifdef _HAVE_FIB_ROUTING_
		struct json_object *vroutes, *vrules;
#endif
		element f;

		vrrp_t *vrrp = ELEMENT_DATA(e);
		instance_json = json_object_new_object();
		json_stats = json_object_new_object();
		json_data = json_object_new_object();
		vips = json_object_new_array();
		evips = json_object_new_array();
		track_ifp = json_object_new_array();
		track_script = json_object_new_array();
#ifdef _HAVE_FIB_ROUTING_
		vroutes = json_object_new_array();
		vrules = json_object_new_array();
#endif

		json_object_object_add(json_data, "iname",
			json_object_new_string(vrrp->iname));
		json_object_object_add(json_data, "dont_track_primary",
			json_object_new_int(vrrp->dont_track_primary));
		json_object_object_add(json_data, "skip_check_adv_addr",
			json_object_new_int(vrrp->skip_check_adv_addr));
		json_object_object_add(json_data, "strict_mode",
			json_object_new_int((int)vrrp->strict_mode));
#ifdef _HAVE_VRRP_VMAC_
		json_object_object_add(json_data, "vmac_ifname",
			json_object_new_string(vrrp->vmac_ifname));
#endif
		if (!LIST_ISEMPTY(vrrp->track_ifp)) {
			for (f = LIST_HEAD(vrrp->track_ifp); f; ELEMENT_NEXT(f)) {
				interface_t *ifp = ELEMENT_DATA(f);
				json_object_array_add(track_ifp,
					json_object_new_string(ifp->ifname));
			}
		}
		json_object_object_add(json_data, "track_ifp", track_ifp);

		if (!LIST_ISEMPTY(vrrp->track_script)) {
			for (f = LIST_HEAD(vrrp->track_script); f; ELEMENT_NEXT(f)) {
				tracked_sc_t *tsc = ELEMENT_DATA(f);
				vrrp_script_t *vscript = tsc->scr;
				json_object_array_add(track_script,
					json_object_new_string(cmd_str(&vscript->script)));
			}
		}
		json_object_object_add(json_data, "track_script", track_script);

		json_object_object_add(json_data, "ifp_ifname",
			json_object_new_string(vrrp->ifp->ifname));
		json_object_object_add(json_data, "master_priority",
			json_object_new_int(vrrp->master_priority));
		json_object_object_add(json_data, "last_transition",
			json_object_new_double(timeval_to_double(&vrrp->last_transition)));
		json_object_object_add(json_data, "garp_delay",
			json_object_new_double(vrrp->garp_delay / TIMER_HZ_FLOAT));
		json_object_object_add(json_data, "garp_refresh",
			json_object_new_int((int)vrrp->garp_refresh.tv_sec));
		json_object_object_add(json_data, "garp_rep",
			json_object_new_int((int)vrrp->garp_rep));
		json_object_object_add(json_data, "garp_refresh_rep",
			json_object_new_int((int)vrrp->garp_refresh_rep));
		json_object_object_add(json_data, "garp_lower_prio_delay",
			json_object_new_int((int)(vrrp->garp_lower_prio_delay / TIMER_HZ)));
		json_object_object_add(json_data, "garp_lower_prio_rep",
			json_object_new_int((int)vrrp->garp_lower_prio_rep));
		json_object_object_add(json_data, "lower_prio_no_advert",
			json_object_new_int((int)vrrp->lower_prio_no_advert));
		json_object_object_add(json_data, "higher_prio_send_advert",
			json_object_new_int((int)vrrp->higher_prio_send_advert));
		json_object_object_add(json_data, "vrid",
			json_object_new_int(vrrp->vrid));
		json_object_object_add(json_data, "base_priority",
			json_object_new_int(vrrp->base_priority));
		json_object_object_add(json_data, "effective_priority",
			json_object_new_int(vrrp->effective_priority));
		json_object_object_add(json_data, "vipset",
			json_object_new_boolean(vrrp->vipset));

		if (!LIST_ISEMPTY(vrrp->vip)) {
			for (f = LIST_HEAD(vrrp->vip); f; ELEMENT_NEXT(f)) {
				ip_address_t *vip = ELEMENT_DATA(f);
				char ipaddr[INET6_ADDRSTRLEN];
				inet_ntop(vrrp->family, &(vip->u.sin.sin_addr.s_addr),
					ipaddr, INET6_ADDRSTRLEN);
				json_object_array_add(vips,
					json_object_new_string(ipaddr));
			}
		}
		json_object_object_add(json_data, "vips", vips);

		if (!LIST_ISEMPTY(vrrp->evip)) {
			for (f = LIST_HEAD(vrrp->evip); f; ELEMENT_NEXT(f)) {
				ip_address_t *evip = ELEMENT_DATA(f);
				char ipaddr[INET6_ADDRSTRLEN];
				inet_ntop(vrrp->family, &(evip->u.sin.sin_addr.s_addr),
					ipaddr, INET6_ADDRSTRLEN);
				json_object_array_add(evips,
					json_object_new_string(ipaddr));
			}
		}
		json_object_object_add(json_data, "evips", evips);

		json_object_object_add(json_data, "promote_secondaries",
			json_object_new_boolean(vrrp->promote_secondaries));

#ifdef _HAVE_FIB_ROUTING_
		if (!LIST_ISEMPTY(vrrp->vroutes)) {
			for (f = LIST_HEAD(vrrp->vroutes); f; ELEMENT_NEXT(f)) {
				ip_route_t *route = ELEMENT_DATA(f);
				char *buf = MALLOC(ROUTE_BUF_SIZE);
				format_iproute(route, buf, ROUTE_BUF_SIZE);
				json_object_array_add(vroutes,
					json_object_new_string(buf));
			}
		}
		json_object_object_add(json_data, "vroutes", vroutes);

		if (!LIST_ISEMPTY(vrrp->vrules)) {
			for (f = LIST_HEAD(vrrp->vrules); f; ELEMENT_NEXT(f)) {
				ip_rule_t *rule = ELEMENT_DATA(f);
				char *buf = MALLOC(RULE_BUF_SIZE);
				format_iprule(rule, buf, RULE_BUF_SIZE);
				json_object_array_add(vrules,
					json_object_new_string(buf));
			}
		}
		json_object_object_add(json_data, "vrules", vrules);
#endif

		json_object_object_add(json_data, "adver_int",
			json_object_new_double(vrrp->adver_int / TIMER_HZ_FLOAT));
		json_object_object_add(json_data, "master_adver_int",
			json_object_new_double(vrrp->master_adver_int / TIMER_HZ_FLOAT));
		json_object_object_add(json_data, "accept",
			json_object_new_int((int)vrrp->accept));
		json_object_object_add(json_data, "nopreempt",
			json_object_new_boolean(vrrp->nopreempt));
		json_object_object_add(json_data, "preempt_delay",
			json_object_new_int((int)(vrrp->preempt_delay / TIMER_HZ)));
		json_object_object_add(json_data, "state",
			json_object_new_int(vrrp->state));
		json_object_object_add(json_data, "wantstate",
			json_object_new_int(vrrp->wantstate));
		json_object_object_add(json_data, "version",
			json_object_new_int(vrrp->version));
		if (vrrp->script_backup)
			json_object_object_add(json_data, "script_backup",
				json_object_new_string(cmd_str(vrrp->script_backup)));
		if (vrrp->script_master)
			json_object_object_add(json_data, "script_master",
				json_object_new_string(cmd_str(vrrp->script_master)));
		if (vrrp->script_fault)
			json_object_object_add(json_data, "script_fault",
				json_object_new_string(cmd_str(vrrp->script_fault)));
		if (vrrp->script_stop)
			json_object_object_add(json_data, "script_stop",
				json_object_new_string(cmd_str(vrrp->script_stop)));
		if (vrrp->script)
			json_object_object_add(json_data, "script",
				json_object_new_string(cmd_str(vrrp->script)));
		if (vrrp->script_master_rx_lower_pri)
			json_object_object_add(json_data, "script_master_rx_lower_pri",
				json_object_new_string(cmd_str(vrrp->script_master_rx_lower_pri)));
		json_object_object_add(json_data, "smtp_alert",
			json_object_new_boolean(vrrp->smtp_alert));
#ifdef _WITH_VRRP_AUTH_
		if (vrrp->auth_type) {
			json_object_object_add(json_data, "auth_type",
				json_object_new_int(vrrp->auth_type));

			if (vrrp->auth_type != VRRP_AUTH_AH) {
				char auth_data[sizeof(vrrp->auth_data) + 1];
				memcpy(auth_data, vrrp->auth_data, sizeof(vrrp->auth_data));
				auth_data[sizeof(vrrp->auth_data)] = '\0';
				json_object_object_add(json_data, "auth_data",
					json_object_new_string(auth_data));
			}
		}
		else
			json_object_object_add(json_data, "auth_type",
				json_object_new_int(0));
#endif

		json_object_object_add(json_stats, "advert_rcvd",
			json_object_new_int64((int64_t)vrrp->stats->advert_rcvd));
		json_object_object_add(json_stats, "advert_sent",
			json_object_new_int64(vrrp->stats->advert_sent));
		json_object_object_add(json_stats, "become_master",
			json_object_new_int64(vrrp->stats->become_master));
		json_object_object_add(json_stats, "release_master",
			json_object_new_int64(vrrp->stats->release_master));
		json_object_object_add(json_stats, "packet_len_err",
			json_object_new_int64((int64_t)vrrp->stats->packet_len_err));
		json_object_object_add(json_stats, "advert_interval_err",
			json_object_new_int64((int64_t)vrrp->stats->advert_interval_err));
		json_object_object_add(json_stats, "ip_ttl_err",
			json_object_new_int64((int64_t)vrrp->stats->ip_ttl_err));
		json_object_object_add(json_stats, "invalid_type_rcvd",
			json_object_new_int64((int64_t)vrrp->stats->invalid_type_rcvd));
		json_object_object_add(json_stats, "addr_list_err",
			json_object_new_int64((int64_t)vrrp->stats->addr_list_err));
		json_object_object_add(json_stats, "invalid_authtype",
			json_object_new_int64(vrrp->stats->invalid_authtype));
#ifdef _WITH_VRRP_AUTH_
		json_object_object_add(json_stats, "authtype_mismatch",
			json_object_new_int64(vrrp->stats->authtype_mismatch));
		json_object_object_add(json_stats, "auth_failure",
			json_object_new_int64(vrrp->stats->auth_failure));
#endif
		json_object_object_add(json_stats, "pri_zero_rcvd",
			json_object_new_int64((int64_t)vrrp->stats->pri_zero_rcvd));
		json_object_object_add(json_stats, "pri_zero_sent",
			json_object_new_int64((int64_t)vrrp->stats->pri_zero_sent));

		json_object_object_add(instance_json, "data", json_data);
		json_object_object_add(instance_json, "stats", json_stats);

		json_object_array_add(array, instance_json);

	}
	fprintf(file, "%s", json_object_to_json_string(array));
	fclose(file);
}

Vulnerability Type: 
CWE ID: CWE-59
Summary: keepalived 2.0.8 didn't check for pathnames with symlinks when writing data to a temporary file upon a call to PrintData or PrintStats. This allowed local users to overwrite arbitrary files if fs.protected_symlinks is set to 0, as demonstrated by a symlink from /tmp/keepalived.data or /tmp/keepalived.stats to /etc/passwd.
Commit Message: 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>