diff options
Diffstat (limited to 'net/rose/rose_subr.c')
| -rw-r--r-- | net/rose/rose_subr.c | 102 | 
1 files changed, 69 insertions, 33 deletions
diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c index 1734abba26a..7ca57741b2f 100644 --- a/net/rose/rose_subr.c +++ b/net/rose/rose_subr.c @@ -22,7 +22,6 @@  #include <linux/skbuff.h>  #include <net/sock.h>  #include <net/tcp_states.h> -#include <asm/system.h>  #include <linux/fcntl.h>  #include <linux/mm.h>  #include <linux/interrupt.h> @@ -142,7 +141,7 @@ void rose_write_internal(struct sock *sk, int frametype)  		*dptr++ = ROSE_GFI | lci1;  		*dptr++ = lci2;  		*dptr++ = frametype; -		*dptr++ = 0xAA; +		*dptr++ = ROSE_CALL_REQ_ADDR_LEN_VAL;  		memcpy(dptr, &rose->dest_addr,  ROSE_ADDR_LEN);  		dptr   += ROSE_ADDR_LEN;  		memcpy(dptr, &rose->source_addr, ROSE_ADDR_LEN); @@ -246,12 +245,16 @@ static int rose_parse_national(unsigned char *p, struct rose_facilities_struct *  	do {  		switch (*p & 0xC0) {  		case 0x00: +			if (len < 2) +				return -1;  			p   += 2;  			n   += 2;  			len -= 2;  			break;  		case 0x40: +			if (len < 3) +				return -1;  			if (*p == FAC_NATIONAL_RAND)  				facilities->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF);  			p   += 3; @@ -260,40 +263,61 @@ static int rose_parse_national(unsigned char *p, struct rose_facilities_struct *  			break;  		case 0x80: +			if (len < 4) +				return -1;  			p   += 4;  			n   += 4;  			len -= 4;  			break;  		case 0xC0: +			if (len < 2) +				return -1;  			l = p[1]; +			if (len < 2 + l) +				return -1;  			if (*p == FAC_NATIONAL_DEST_DIGI) {  				if (!fac_national_digis_received) { +					if (l < AX25_ADDR_LEN) +						return -1;  					memcpy(&facilities->source_digis[0], p + 2, AX25_ADDR_LEN);  					facilities->source_ndigis = 1;  				}  			}  			else if (*p == FAC_NATIONAL_SRC_DIGI) {  				if (!fac_national_digis_received) { +					if (l < AX25_ADDR_LEN) +						return -1;  					memcpy(&facilities->dest_digis[0], p + 2, AX25_ADDR_LEN);  					facilities->dest_ndigis = 1;  				}  			}  			else if (*p == FAC_NATIONAL_FAIL_CALL) { +				if (l < AX25_ADDR_LEN) +					return -1;  				memcpy(&facilities->fail_call, p + 2, AX25_ADDR_LEN);  			}  			else if (*p == FAC_NATIONAL_FAIL_ADD) { +				if (l < 1 + ROSE_ADDR_LEN) +					return -1;  				memcpy(&facilities->fail_addr, p + 3, ROSE_ADDR_LEN);  			}  			else if (*p == FAC_NATIONAL_DIGIS) { +				if (l % AX25_ADDR_LEN) +					return -1;  				fac_national_digis_received = 1;  				facilities->source_ndigis = 0;  				facilities->dest_ndigis   = 0;  				for (pt = p + 2, lg = 0 ; lg < l ; pt += AX25_ADDR_LEN, lg += AX25_ADDR_LEN) { -					if (pt[6] & AX25_HBIT) +					if (pt[6] & AX25_HBIT) { +						if (facilities->dest_ndigis >= ROSE_MAX_DIGIS) +							return -1;  						memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN); -					else +					} else { +						if (facilities->source_ndigis >= ROSE_MAX_DIGIS) +							return -1;  						memcpy(&facilities->source_digis[facilities->source_ndigis++], pt, AX25_ADDR_LEN); +					}  				}  			}  			p   += l + 2; @@ -314,25 +338,38 @@ static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *fac  	do {  		switch (*p & 0xC0) {  		case 0x00: +			if (len < 2) +				return -1;  			p   += 2;  			n   += 2;  			len -= 2;  			break;  		case 0x40: +			if (len < 3) +				return -1;  			p   += 3;  			n   += 3;  			len -= 3;  			break;  		case 0x80: +			if (len < 4) +				return -1;  			p   += 4;  			n   += 4;  			len -= 4;  			break;  		case 0xC0: +			if (len < 2) +				return -1;  			l = p[1]; + +			/* Prevent overflows*/ +			if (l < 10 || l > 20) +				return -1; +  			if (*p == FAC_CCITT_DEST_NSAP) {  				memcpy(&facilities->source_addr, p + 7, ROSE_ADDR_LEN);  				memcpy(callsign, p + 12,   l - 10); @@ -355,45 +392,44 @@ static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *fac  	return n;  } -int rose_parse_facilities(unsigned char *p, +int rose_parse_facilities(unsigned char *p, unsigned packet_len,  	struct rose_facilities_struct *facilities)  {  	int facilities_len, len;  	facilities_len = *p++; -	if (facilities_len == 0) +	if (facilities_len == 0 || (unsigned int)facilities_len > packet_len)  		return 0; -	while (facilities_len > 0) { -		if (*p == 0x00) { -			facilities_len--; -			p++; - -			switch (*p) { -			case FAC_NATIONAL:		/* National */ -				len = rose_parse_national(p + 1, facilities, facilities_len - 1); -				facilities_len -= len + 1; -				p += len + 1; -				break; - -			case FAC_CCITT:		/* CCITT */ -				len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1); -				facilities_len -= len + 1; -				p += len + 1; -				break; - -			default: -				printk(KERN_DEBUG "ROSE: rose_parse_facilities - unknown facilities family %02X\n", *p); -				facilities_len--; -				p++; -				break; -			} -		} else -			break;	/* Error in facilities format */ +	while (facilities_len >= 3 && *p == 0x00) { +		facilities_len--; +		p++; + +		switch (*p) { +		case FAC_NATIONAL:		/* National */ +			len = rose_parse_national(p + 1, facilities, facilities_len - 1); +			break; + +		case FAC_CCITT:		/* CCITT */ +			len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1); +			break; + +		default: +			printk(KERN_DEBUG "ROSE: rose_parse_facilities - unknown facilities family %02X\n", *p); +			len = 1; +			break; +		} + +		if (len < 0) +			return 0; +		if (WARN_ON(len >= facilities_len)) +			return 0; +		facilities_len -= len + 1; +		p += len + 1;  	} -	return 1; +	return facilities_len == 0;  }  static int rose_create_facilities(unsigned char *buffer, struct rose_sock *rose)  | 
