Commit message: set_interface_var() doesn't check interface name and blindly does
fopen(path "/" ifname, "w") on it.  As "ifname" is an untrusted input, it
should be checked for ".." and/or "/" in it.  Otherwise, an infected
unprivileged daemon may overwrite contents of file named "mtu",
"hoplimit", etc. in arbitrary location with arbitrary 32-bit value in
decimal representation ("%d").  If an attacker has a local account or
may create arbitrary symlinks with these names in any location (e.g.
/tmp), any file may be overwritten with a decimal value.
CVE: CVE-2011-3602
CWE: CWE-22
Vulnerability description: Directory traversal vulnerability in device-linux.c in the router advertisement daemon (radvd) before 1.8.2 allows local users to overwrite arbitrary files, and remote attackers to overwrite certain files, via a .. (dot dot) in an interface name.  NOTE: this can be leveraged with a symlink to overwrite arbitrary files.

Project: radvd
File: device-linux.c

int check_allrouters_membership(struct Interface *iface)
{
	#define ALL_ROUTERS_MCAST "ff020000000000000000000000000002"

	FILE *fp;
	unsigned int if_idx, allrouters_ok=0;
	char addr[32+1];
	char buffer[301] = {""}, *str;
	int ret=0;

	if ((fp = fopen(PATH_PROC_NET_IGMP6, "r")) == NULL)
	{
		flog(LOG_ERR, "can't open %s: %s", PATH_PROC_NET_IGMP6,
			strerror(errno));
		return (-1);
	}

	str = fgets(buffer, 300, fp);

	while (str && (ret = sscanf(str, "%u %*s %32[0-9A-Fa-f]", &if_idx, addr)) ) {
		if (ret == 2) {
			if (iface->if_index == if_idx) {
				if (strncmp(addr, ALL_ROUTERS_MCAST, sizeof(addr)) == 0){
					allrouters_ok = 1;
					break;
				}
			}
		}
		str = fgets(buffer, 300, fp);
	}

	fclose(fp);

	if (!allrouters_ok) {
		flog(LOG_WARNING, "resetting ipv6-allrouters membership on %s", iface->Name);
		setup_allrouters_membership(iface);
	}

	return(0);
}

Project: radvd
File: device-linux.c

set_interface_linkmtu(const char *iface, uint32_t mtu)
{
	if (privsep_enabled())
		return privsep_interface_linkmtu(iface, mtu);

	return set_interface_var(iface,
				 PROC_SYS_IP6_LINKMTU, "LinkMTU",
				 mtu);
}

Project: radvd
File: device-linux.c

set_interface_reachtime(const char *iface, uint32_t rtime)
{
	int ret;

	if (privsep_enabled())
		return privsep_interface_reachtime(iface, rtime);

	ret = set_interface_var(iface,
				PROC_SYS_IP6_BASEREACHTIME_MS,
				NULL,
				rtime);
	if (ret)
		ret = set_interface_var(iface,
					PROC_SYS_IP6_BASEREACHTIME,
					"BaseReachableTimer",
					rtime / 1000); /* sec */
	return ret;
}

Project: radvd
File: device-linux.c

set_interface_retranstimer(const char *iface, uint32_t rettimer)
{
	int ret;

	if (privsep_enabled())
		return privsep_interface_retranstimer(iface, rettimer);

	ret = set_interface_var(iface,
				PROC_SYS_IP6_RETRANSTIMER_MS,
				NULL,
				rettimer);
	if (ret)
		ret = set_interface_var(iface,
					PROC_SYS_IP6_RETRANSTIMER,
					"RetransTimer",
					rettimer / 1000 * USER_HZ); /* XXX user_hz */
	return ret;
}

Project: radvd
File: device-linux.c

setup_deviceinfo(struct Interface *iface)
{
	struct ifreq	ifr;
	struct AdvPrefix *prefix;
	char zero[sizeof(iface->if_addr)];

	strncpy(ifr.ifr_name, iface->Name, IFNAMSIZ-1);
	ifr.ifr_name[IFNAMSIZ-1] = '\0';

	if (ioctl(sock, SIOCGIFMTU, &ifr) < 0) {
		flog(LOG_ERR, "ioctl(SIOCGIFMTU) failed for %s: %s",
			iface->Name, strerror(errno));
		return (-1);
	}

	dlog(LOG_DEBUG, 3, "mtu for %s is %d", iface->Name, ifr.ifr_mtu);
	iface->if_maxmtu = ifr.ifr_mtu;

	if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
	{
		flog(LOG_ERR, "ioctl(SIOCGIFHWADDR) failed for %s: %s",
			iface->Name, strerror(errno));
		return (-1);
	}

	dlog(LOG_DEBUG, 3, "hardware type for %s is %d", iface->Name,
		ifr.ifr_hwaddr.sa_family);

	switch(ifr.ifr_hwaddr.sa_family)
        {
	case ARPHRD_ETHER:
		iface->if_hwaddr_len = 48;
		iface->if_prefix_len = 64;
		break;
#ifdef ARPHRD_FDDI
	case ARPHRD_FDDI:
		iface->if_hwaddr_len = 48;
		iface->if_prefix_len = 64;
		break;
#endif /* ARPHDR_FDDI */
#ifdef ARPHRD_ARCNET
	case ARPHRD_ARCNET:
		iface->if_hwaddr_len = 8;
		iface->if_prefix_len = -1;
		iface->if_maxmtu = -1;
		break;
#endif /* ARPHDR_ARCNET */
	default:
		iface->if_hwaddr_len = -1;
		iface->if_prefix_len = -1;
		iface->if_maxmtu = -1;
		break;
	}

	dlog(LOG_DEBUG, 3, "link layer token length for %s is %d", iface->Name,
		iface->if_hwaddr_len);

	dlog(LOG_DEBUG, 3, "prefix length for %s is %d", iface->Name,
		iface->if_prefix_len);

	if (iface->if_hwaddr_len != -1) {
		unsigned int if_hwaddr_len_bytes = (iface->if_hwaddr_len + 7) >> 3;

		if (if_hwaddr_len_bytes > sizeof(iface->if_hwaddr)) {
			flog(LOG_ERR, "address length %d too big for %s", if_hwaddr_len_bytes, iface->Name);
			return(-2);
		}
		memcpy(iface->if_hwaddr, ifr.ifr_hwaddr.sa_data, if_hwaddr_len_bytes);

		memset(zero, 0, sizeof(zero));
		if (!memcmp(iface->if_hwaddr, zero, if_hwaddr_len_bytes))
			flog(LOG_WARNING, "WARNING, MAC address on %s is all zero!",
				iface->Name);
	}

	prefix = iface->AdvPrefixList;
	while (prefix)
	{
		if ((iface->if_prefix_len != -1) &&
		   (iface->if_prefix_len != prefix->PrefixLen))
		{
			flog(LOG_WARNING, "prefix length should be %d for %s",
				iface->if_prefix_len, iface->Name);
 		}

 		prefix = prefix->next;
	}

	return (0);
}