www.pudn.com > RadiusSrv.rar > radius.cpp, change:2003-10-27,size:26216b


#include "libRadius.h" 
#include "radius.h" 
#include "md5.h" 
 
int librad_max_attributes = 0; 
 
static lrad_randctx lrad_rand_pool;	/* across multiple calls */ 
 
typedef struct radius_packet_t { 
  uint8_t	code; 
  uint8_t	id; 
  uint8_t	length[2]; 
  uint8_t	vector[AUTH_VECTOR_LEN]; 
  uint8_t	data[1]; 
} radius_packet_t; 
 
static const char *packet_codes[] = { 
  "", 
  "Access-Request", 
  "Access-Accept", 
  "Access-Reject", 
  "Accounting-Request", 
  "Accounting-Response", 
  "Accounting-Status", 
  "Password-Request", 
  "Password-Accept", 
  "Password-Reject", 
  "Accounting-Message", 
  "Access-Challenge", 
  "Status-Server", 
  "Status-Client", 
  "14", 
  "15", 
  "16", 
  "17", 
  "18", 
  "19", 
  "20", 
  "Resource-Free-Request", 
  "Resource-Free-Response", 
  "Resource-Query-Request", 
  "Resource-Query-Response", 
  "Alternate-Resource-Reclaim-Request", 
  "NAS-Reboot-Request", 
  "NAS-Reboot-Response", 
  "28", 
  "Next-Passcode", 
  "New-Pin", 
  "Terminate-Session", 
  "Password-Expired", 
  "Event-Request", 
  "Event-Response", 
  "35", 
  "36", 
  "37", 
  "38", 
  "39", 
  "Disconnect-Request", 
  "Disconnect-ACK", 
  "Disconnect-NAK", 
  "CoF-Request", 
  "CoF-ACK", 
  "CoF-NAK", 
  "46", 
  "47", 
  "48", 
  "49", 
  "IP-Address-Allocate", 
  "IP-Address-Release" 
}; 
 
/* 
 *	Receive UDP client requests, and fill in 
 *	the basics of a RADIUS_PACKET structure. 
 */ 
RADIUS_PACKET *rad_recv(int fd) 
{ 
	RADIUS_PACKET		*packet; 
	struct sockaddr_in	saremote; 
	int			totallen; 
	int		salen; 
	u_short			len; 
	uint8_t			*attr; 
	uint8_t			*vendorattr; 
	int			count; 
	radius_packet_t		*hdr; 
	char			host_ipaddr[16]; 
	int			seen_eap; 
	char			data[MAX_PACKET_LEN]; 
	int			num_attributes; 
	uint32_t                vendorcode; 
	int			vendorlen; 
	 
	/* 
	 *	Allocate the new request data structure 
	 */ 
	if ((packet = (radius_packet *)malloc(sizeof(RADIUS_PACKET))) == NULL) { 
		printf("out of memory"); 
		errno = ENOMEM; 
		return NULL; 
	} 
	memset(packet, 0, sizeof(RADIUS_PACKET)); 
 
	/* 
	 *	Receive the packet. 
	 */ 
	salen = sizeof(saremote); 
	memset(&saremote, 0, sizeof(saremote)); 
 //	packet->data_len = recvfrom(fd, data, sizeof(data), 
//		0, (struct sockaddr *)&saremote, &salen); 
	packet->data_len = recvfrom(fd, data, sizeof(data), 
		0, (struct sockaddr *) &saremote, &salen); 
	/* 
	 *	Check for socket errors. 
	 */ 
	if (packet->data_len < 0) { 
		printf("Error receiving packet: %s\n", strerror(errno)); 
		free(packet); 
		return NULL; 
	} 
 
	/* 
	 *	Fill IP header fields.  We need these for the error 
	 *	messages which may come later. 
	 */ 
	packet->sockfd = fd; 
	packet->src_ipaddr = saremote.sin_addr.s_addr; 
	packet->src_port = ntohs(saremote.sin_port); 
 
	/* 
	 *	FIXME: Do even more filtering by only permitting 
	 *	certain IP's.  The problem is that we don't know 
	 *	how to do this properly for all possible clients... 
	 */ 
 
	/* 
	 *	Explicitely set the VP list to empty. 
	 */ 
	packet->vps = NULL; 
 
	/* 
	 *	Check for packets smaller than the packet header. 
	 * 
	 *	RFC 2865, Section 3., subsection 'length' says: 
	 * 
	 *	"The minimum length is 20 ..." 
	 */ 
	if (packet->data_len < AUTH_HDR_LEN) { 
		printf("WARNING: Malformed RADIUS packet from host %s: too short (received %d < minimum %d)", 
			   ip_ntoa(host_ipaddr, packet->src_ipaddr), 
			   packet->data_len, AUTH_HDR_LEN); 
		free(packet); 
		return NULL; 
	} 
 
	/* 
	 *	RFC 2865, Section 3., subsection 'length' says: 
	 * 
	 *	" ... and maximum length is 4096." 
	 */ 
	if (packet->data_len > MAX_PACKET_LEN) { 
		printf("WARNING: Malformed RADIUS packet from host %s: too long (received %d > maximum %d)", 
			   ip_ntoa(host_ipaddr, packet->src_ipaddr), 
			   packet->data_len, MAX_PACKET_LEN); 
		free(packet); 
		return NULL; 
	} 
 
	/* 
	 *	Check for packets with mismatched size. 
	 *	i.e. We've received 128 bytes, and the packet header 
	 *	says it's 256 bytes long. 
	 */ 
	hdr = (radius_packet_t *)data; 
	memcpy(&len, hdr->length, sizeof(u_short)); 
	totallen = ntohs(len); 
 
	/* 
	 *	Code of 0 is not understood. 
	 *	Code of 16 or greate is not understood. 
	 */ 
	if ((hdr->code == 0) || 
	    (hdr->code >= 52)) { 
		printf("WARNING: Bad RADIUS packet from host %s: unknown packet code %d", 
			   ip_ntoa(host_ipaddr, packet->src_ipaddr), 
			   hdr->code); 
		free(packet); 
		return NULL; 
	} 
 
	/* 
	 *	Repeat the length checks.  This time, instead of 
	 *	looking at the data we received, look at the value 
	 *	of the 'length' field inside of the packet. 
	 * 
	 *	Check for packets smaller than the packet header. 
	 * 
	 *	RFC 2865, Section 3., subsection 'length' says: 
	 * 
	 *	"The minimum length is 20 ..." 
	 */ 
	if (totallen < AUTH_HDR_LEN) { 
		printf("WARNING: Malformed RADIUS packet from host %s: too short (length %d < minimum %d)", 
			   ip_ntoa(host_ipaddr, packet->src_ipaddr), 
			   totallen, AUTH_HDR_LEN); 
		free(packet); 
		return NULL; 
	} 
 
	/* 
	 *	And again, for the value of the 'length' field. 
	 * 
	 *	RFC 2865, Section 3., subsection 'length' says: 
	 * 
	 *	" ... and maximum length is 4096." 
	 */ 
	if (totallen > MAX_PACKET_LEN) { 
		printf("WARNING: Malformed RADIUS packet from host %s: too long (length %d > maximum %d)", 
			   ip_ntoa(host_ipaddr, packet->src_ipaddr), 
			   totallen, MAX_PACKET_LEN); 
		free(packet); 
		return NULL; 
	} 
 
	/* 
	 *	RFC 2865, Section 3., subsection 'length' says: 
	 * 
	 *	"If the packet is shorter than the Length field 
	 *	indicates, it MUST be silently discarded." 
	 * 
	 *	i.e. No response to the NAS. 
	 */ 
	if (packet->data_len < totallen) { 
		printf("WARNING: Malformed RADIUS packet from host %s: received %d octets, packet length says %d", 
			   ip_ntoa(host_ipaddr, packet->src_ipaddr), 
			   packet->data_len, totallen); 
		free(packet); 
		return NULL; 
	} 
 
	/* 
	 *	RFC 2865, Section 3., subsection 'length' says: 
	 * 
	 *	"Octets outside the range of the Length field MUST be 
	 *	treated as padding and ignored on reception." 
	 */ 
	if (packet->data_len > totallen) { 
		/* 
		 *	We're shortening the packet below, but just 
		 *	to be paranoid, zero out the extra data. 
		 */ 
		memset(data + totallen, 0, packet->data_len - totallen); 
		packet->data_len = totallen; 
	} 
 
	/* 
	 *	Walk through the packet's attributes, ensuring that 
	 *	they add up EXACTLY to the size of the packet. 
	 * 
	 *	If they don't, then the attributes either under-fill 
	 *	or over-fill the packet.  Any parsing of the packet 
	 *	is impossible, and will result in unknown side effects. 
	 * 
	 *	This would ONLY happen with buggy RADIUS implementations, 
	 *	or with an intentional attack.  Either way, we do NOT want 
	 *	to be vulnerable to this problem. 
	 */ 
	attr = hdr->data; 
	count = totallen - AUTH_HDR_LEN; 
	seen_eap = 0; 
	num_attributes = 0; 
 
	while (count > 0) { 
		/* 
		 *	Attribute number zero is NOT defined. 
		 */ 
		if (attr[0] == 0) { 
			printf("WARNING: Malformed RADIUS packet from host %s: Invalid attribute 0", 
				   ip_ntoa(host_ipaddr, packet->src_ipaddr)); 
			free(packet); 
			return NULL; 
		} 
		 
		/* 
		 *	Attributes are at LEAST as long as the ID & length 
		 *	fields.  Anything shorter is an invalid attribute. 
		 */ 
       		if (attr[1] < 2) { 
			printf("WARNING: Malformed RADIUS packet from host %s: attribute %d too short", 
				   ip_ntoa(host_ipaddr, packet->src_ipaddr), 
				   attr[0]); 
			free(packet); 
			return NULL; 
		} 
 
		/* 
		 *	Sanity check the attributes for length. 
		 */ 
		switch (attr[0])  
		{ 
		case PW_EAP_MESSAGE: 
			seen_eap |= PW_EAP_MESSAGE; 
			break; 
 
		case PW_MESSAGE_AUTHENTICATOR: 
			if (attr[1] != 2 + AUTH_VECTOR_LEN) { 
				printf("WARNING: Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d", 
					   ip_ntoa(host_ipaddr, packet->src_ipaddr), 
					   attr[1] - 2); 
				free(packet); 
				return NULL; 
			} 
			seen_eap |= PW_MESSAGE_AUTHENTICATOR; 
			break; 
			 
		case PW_VENDOR_SPECIFIC: 
			if (attr[1] < 6) { 
				printf("WARNING: Malformed RADIUS packet from host %s: Vendor-Specific has invalid length %d", 
					   ip_ntoa(host_ipaddr, packet->src_ipaddr), 
					   attr[1] - 2); 
				free(packet); 
				return NULL; 
			} 
			memcpy(&vendorcode, attr + 2, 4); 
			vendorcode = ntohl(vendorcode); 
			if (vendorcode == VENDORPEC_USR) { 
				if (attr[1] < 8){ 
					printf("WARNING: Malformed RADIUS packet from host %s: USR attribute has invalid length %d", 
					   ip_ntoa(host_ipaddr, packet->src_ipaddr), 
					   attr[1] - 2); 
					free(packet); 
					return NULL; 
				} 
				break; 
			} 
			vendorlen = attr[1] - 6; 
			vendorattr = attr + 6; 
			while (vendorlen >= 2) { 
				if (vendorattr[1] < 2){ 
					printf("WARNING: Malformed RADIUS packet from host %s: Vendor specific attribute has invalid length %d", 
					   ip_ntoa(host_ipaddr, packet->src_ipaddr), 
					   vendorattr[1] - 2); 
					free(packet); 
					return NULL; 
				} 
				vendorlen -= vendorattr[1]; 
				vendorattr += vendorattr[1]; 
			} 
			if (vendorlen != 0){ 
				printf("WARNING: Malformed RADIUS packet from host %s: Vendor specific attributes do not exactly fill Vendor-Specific", 
					   ip_ntoa(host_ipaddr, packet->src_ipaddr)); 
				free(packet); 
				return NULL; 
			} 
			break; 
		default:	/* don't do anything by default */ 
			break; 
		} 
 
		/* 
		 *	FIXME: Look up the base 255 attributes in the 
		 *	dictionary, and switch over their type.  For 
		 *	integer/date/ip, the attribute length SHOULD 
		 *	be 6. 
		 */ 
		count -= attr[1];	/* grab the attribute length */ 
		attr += attr[1]; 
		num_attributes++;	/* seen one more attribute */ 
	} 
 
	/* 
	 *	If the attributes add up to a packet, it's allowed. 
	 * 
	 *	If not, we complain, and throw the packet away. 
	 */ 
	if (count != 0) { 
		printf("WARNING: Malformed RADIUS packet from host %s: packet attributes do NOT exactly fill the packet", 
			   ip_ntoa(host_ipaddr, packet->src_ipaddr)); 
		free(packet); 
		return NULL; 
	} 
 
	/* 
	 *	If we're configured to look for a maximum number of 
	 *	attributes, and we've seen more than that maximum, 
	 *	then throw the packet away, as a possible DoS. 
	 */ 
	if ((librad_max_attributes > 0) && 
	    (num_attributes > librad_max_attributes)) { 
		printf("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).", 
			   ip_ntoa(host_ipaddr, packet->src_ipaddr), 
			   num_attributes, librad_max_attributes); 
		free(packet); 
		return NULL; 
	} 
/* 
	if (librad_debug) { 
		if ((hdr->code > 0) && (hdr->code < 52)) { 
			printf("rad_recv: %s packet from host %s:%d", 
			       packet_codes[hdr->code], 
			       ip_ntoa(host_ipaddr, packet->src_ipaddr), packet->src_port); 
		} else { 
			printf("rad_recv: Packet from host %s:%d code=%d",	 
			       ip_ntoa(host_ipaddr, packet->src_ipaddr), packet->src_port, 
			       hdr->code); 
		} 
		printf(", id=%d, length=%d\n", hdr->id, totallen); 
	} 
*/ 
	/* 
	 *	Fill RADIUS header fields 
	 */ 
	packet->code = hdr->code; 
	packet->id = hdr->id; 
	memcpy(packet->vector, hdr->vector, AUTH_VECTOR_LEN); 
 
	/* 
	 *  Now that we've sanity checked the packet, we can allocate 
	 *  memory for it, and copy the data from the local area to 
	 *  the packet buffer. 
	 */ 
	if ((packet->data = (unsigned char *)malloc(packet->data_len)) == NULL) { 
	  free(packet); 
	  printf("out of memory"); 
	  return NULL; 
	} 
	memcpy(packet->data, data, packet->data_len); 
 
	return packet; 
} 
 
/* 
 *	Free a RADIUS_PACKET 
 */ 
void rad_free(RADIUS_PACKET **radius_packet_ptr) 
{ 
	RADIUS_PACKET *radius_packet; 
 
	if (!radius_packet_ptr) return; 
	radius_packet = *radius_packet_ptr; 
 
	if (radius_packet->data) free(radius_packet->data); 
	if (radius_packet->vps) pairfree(&radius_packet->vps); 
 
	free(radius_packet); 
 
	*radius_packet_ptr = NULL; 
} 
 
/* 
 *	Create a random vector of AUTH_VECTOR_LEN bytes. 
 */ 
static void random_vector(uint8_t *vector) 
{ 
/*	int i; 
 
	if (!lrad_pool_initialized) { 
		memset(&lrad_rand_pool, 0, sizeof(lrad_rand_pool)); 
 
		/* 
		 *	Initialize the state to something, using 
		 *	numbers which aren't random, but which also 
		 *	aren't static. 
		 */ 
	/*	lrad_rand_pool.randrsl[0] = (uint32_t) &lrad_pool_initialized; 
		lrad_rand_pool.randrsl[1] = (uint32_t) &i; 
		lrad_rand_pool.randrsl[2] = (uint32_t) vector; 
 
		lrad_randinit(&lrad_rand_pool, 1); 
	} 
 
	lrad_isaac(&lrad_rand_pool); 
 
	/* 
	 *	Copy the random data over. 
	 */ 
/*	for (i = 0; i < AUTH_VECTOR_LEN; i++) { 
		*(vector++) = lrad_rand_pool.randrsl[i] & 0xff; 
	}*/ 
 
} 
 
/* 
 *	Allocate a new RADIUS_PACKET 
 */ 
RADIUS_PACKET *rad_alloc(int newvector) 
{ 
	RADIUS_PACKET	*rp; 
 
	if ((rp = (radius_packet *)malloc(sizeof(RADIUS_PACKET))) == NULL) { 
		printf("out of memory"); 
		return NULL; 
	} 
	memset(rp, 0, sizeof(RADIUS_PACKET)); 
	if (newvector) 
		random_vector(rp->vector); 
 
	return rp; 
} 
 
 
/* 
 *	Encode password. 
 * 
 *	We assume that the passwd buffer passed is big enough. 
 *	RFC2138 says the password is max 128 chars, so the size 
 *	of the passwd buffer must be at least 129 characters. 
 *	Preferably it's just MAX_STRING_LEN. 
 * 
 *	int *pwlen is updated to the new length of the encrypted 
 *	password - a multiple of 16 bytes. 
 */ 
#define AUTH_PASS_LEN (16) 
int rad_pwencode(char *passwd, int *pwlen, const char *secret, 
		 const char *vector) 
{ 
 
	return 0; 
} 
 
/* 
 *	Calculate/check digest, and decode radius attributes. 
 */ 
int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, 
	       const char *secret) 
{ 
	DICT_ATTR		*attr; 
	uint32_t		lvalue; 
	uint32_t		vendorcode; 
	VALUE_PAIR		**tail; 
	VALUE_PAIR		*pair; 
	uint8_t			*ptr; 
	int			length; 
	int			attribute; 
	int			attrlen; 
	int			vendorlen; 
	radius_packet_t		*hdr; 
 
	hdr = (radius_packet_t *)packet->data; 
 
	/* 
	 *	Before we allocate memory for the attributes, do more 
	 *	sanity checking. 
	 */ 
	ptr = hdr->data; 
	length = packet->data_len - AUTH_HDR_LEN; 
	while (length > 0) { 
		uint8_t	msg_auth_vector[AUTH_VECTOR_LEN]; 
		uint8_t calc_auth_vector[AUTH_VECTOR_LEN]; 
 
		attrlen = ptr[1]; 
 
		switch (ptr[0]) { 
		default:	/* don't do anything. */ 
			break; 
 
			/* 
			 *	Note that more than one Message-Authenticator 
			 *	attribute is invalid. 
			 */ 
		case PW_MESSAGE_AUTHENTICATOR: 
			memcpy(msg_auth_vector, &ptr[2], sizeof(msg_auth_vector)); 
			memset(&ptr[2], 0, AUTH_VECTOR_LEN); 
 
			switch (packet->code) { 
			default: 
			  break; 
 
			case PW_AUTHENTICATION_ACK: 
			case PW_AUTHENTICATION_REJECT: 
			case PW_ACCESS_CHALLENGE: 
			  if (original) { 
				  memcpy(packet->data + 4, original->vector, AUTH_VECTOR_LEN); 
			  } 
			  break; 
			} 
 
			lrad_hmac_md5(packet->data, packet->data_len, 
				      (const unsigned char *)secret, strlen(secret), calc_auth_vector); 
			if (memcmp(calc_auth_vector, msg_auth_vector, 
				    sizeof(calc_auth_vector)) != 0) { 
				char buffer[32]; 
				printf("Received packet from %s with invalid Message-Authenticator!  (Shared secret is incorrect.)", 
					   ip_ntoa(buffer, packet->src_ipaddr)); 
				return 1; 
			} /* else the message authenticator was good */ 
 
			/* 
			 *	Reinitialize Authenticators. 
			 */ 
			memcpy(&ptr[2], msg_auth_vector, AUTH_VECTOR_LEN); 
			memcpy(packet->data + 4, packet->vector, AUTH_VECTOR_LEN); 
			break; 
		} /* switch over the attributes */ 
 
		ptr += attrlen; 
		length -= attrlen; 
	} /* loop over the packet, sanity checking the attributes */ 
 
	/* 
	 *	Calculate and/or verify digest. 
	 */ 
	switch(packet->code) { 
//int rcode; 
 
		case PW_AUTHENTICATION_REQUEST: 
		case PW_STATUS_SERVER: 
		case PW_DISCONNECT_REQUEST: 
			/* 
			 *	The authentication vector is random 
			 *	nonsense, invented by the client. 
			 */ 
			break; 
 
		case PW_ACCOUNTING_REQUEST: 
/*			if (calc_acctdigest(packet, secret) > 1) { 
				char buffer[32]; 
				printf("Received Accounting-Request packet " 
				    "from %s with invalid signature!  (Shared secret is incorrect.)", 
				    ip_ntoa(buffer, packet->src_ipaddr)); 
				return 1; 
			}*/ 
			break; 
 
			/* Verify the reply digest */ 
		case PW_AUTHENTICATION_ACK: 
		case PW_AUTHENTICATION_REJECT: 
		case PW_ACCOUNTING_RESPONSE: 
/*			rcode = calc_replydigest(packet, original, secret); 
			if (rcode > 1) { 
				char buffer[32]; 
				printf("Received %s packet " 
					   "from %s with invalid signature (err=%d)!  (Shared secret is incorrect.)", 
					   packet_codes[packet->code], 
					   ip_ntoa(buffer, packet->src_ipaddr), 
					   rcode); 
				return 1; 
			}*/ 
		  break; 
	} 
 
	/* 
	 *	Extract attribute-value pairs 
	 */ 
	ptr = hdr->data; 
	length = packet->data_len - AUTH_HDR_LEN; 
	packet->vps = NULL; 
	tail = &packet->vps; 
 
	vendorcode = 0; 
	vendorlen  = 0; 
 
	while(length > 0) { 
		if (vendorlen > 0) { 
			attribute = *ptr++ | (vendorcode << 16); 
			attrlen   = *ptr++; 
		} else { 
			attribute = *ptr++; 
			attrlen   = *ptr++; 
		} 
 
		attrlen -= 2; 
		length  -= 2; 
 
		/* 
		 *	This could be a Vendor-Specific attribute. 
		 * 
		 */ 
		if ((vendorlen <= 0) && 
		    (attribute == PW_VENDOR_SPECIFIC) &&  
		    (attrlen > 6)) { 
			memcpy(&lvalue, ptr, 4); 
			vendorcode = ntohl(lvalue); 
			if (vendorcode != 0) { 
 
				if (vendorcode == VENDORPEC_USR) { 
					ptr += 4; 
					memcpy(&lvalue, ptr, 4); 
					/*printf("received USR %04x\n", ntohl(lvalue));*/ 
					attribute = (ntohl(lvalue) & 0xFFFF) | 
							(vendorcode << 16); 
					ptr += 4; 
					attrlen -= 8; 
					length -= 8; 
 
				} else { 
					ptr += 4; 
					vendorlen = attrlen - 4; 
					attribute = *ptr++ | (vendorcode << 16); 
					attrlen   = *ptr++; 
					attrlen -= 2; 
					length -= 6; 
				} 
			} 
			/* 
			 *  Else the vendor wasn't found... 
			 */ 
		} 
 
		/* 
		 *	FIXME: should we us paircreate() ? 
		 */ 
		if ((pair = (value_pair *)malloc(sizeof(VALUE_PAIR))) == NULL) { 
			pairfree(&packet->vps); 
			printf("out of memory"); 
			errno = ENOMEM; 
			return -1; 
		} 
		 
		memset(pair, 0, sizeof(VALUE_PAIR)); 
		if ((attr = dict_attrbyvalue(attribute)) == NULL) { 
			_snprintf(pair->name, sizeof(pair->name), "Attr-%d", attribute); 
			pair->type = PW_TYPE_OCTETS; 
		} else { 
			strcpy(pair->name, attr->name); 
			pair->type = attr->type; 
			pair->flags = attr->flags; 
		} 
		pair->attribute = attribute; 
		pair->length = attrlen; 
		pair->oper = T_OP_EQ; 
		pair->next = NULL; 
		 
		switch (pair->type) { 
			 
		case PW_TYPE_OCTETS: 
		case PW_TYPE_ABINARY: 
		case PW_TYPE_STRING: 
			if (pair->flags.has_tag && 
			    pair->type == PW_TYPE_STRING) { 
				int offset = 0; 
 
				if(TAG_VALID(*ptr)) { 
					pair->flags.tag = *ptr; 
					pair->length--; 
					offset = 1; 
				} else if (pair->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD) { 
					/* 
					 * from RFC2868 - 3.5.  Tunnel-Password 
					 * If the value of the Tag field is greater than 
					 * 0x00 and less than or equal to 0x1F, it SHOULD 
					 * be interpreted as indicating which tunnel 
					 * (of several alternatives) this attribute pertains; 
					 * otherwise, the Tag field SHOULD be ignored. 
					 */ 
					pair->flags.tag = 0x00; 
					pair->length--; 
					offset = 1; 
				} else { 
				       pair->flags.tag = 0x00; 
				} 
				memcpy(pair->strvalue, ptr + offset, 
				       pair->length); 
			} else { 
				/* attrlen always < MAX_STRING_LEN */ 
				memcpy(pair->strvalue, ptr, attrlen); 
			        pair->flags.tag = 0; 
			} 
 
			/* 
			 *  FIXME: HACK for non-updated dictionaries. 
			 *  REMOVE in a future release. 
			 */ 
			if ((strcmp(pair->name, "Ascend-Send-Secret") == 0) || 
			    (strcmp(pair->name, "Ascend-Receive-Secret") == 0)) { 
				pair->flags.encrypt = FLAG_ENCRYPT_ASCEND_SECRET; 
			} 
			if (pair->attribute == PW_USER_PASSWORD) { 
				pair->flags.encrypt = FLAG_ENCRYPT_USER_PASSWORD; 
			} 
 
			/* 
			 *	Decrypt passwords here. 
			 */ 
			switch (pair->flags.encrypt) { 
			default: 
				break; 
 
				/* 
				 *  User-Password 
				 */ 
			case FLAG_ENCRYPT_USER_PASSWORD: 
				if (original) { 
					rad_pwdecode((char *)pair->strvalue, 
						     pair->length, secret, 
						     (char *)original->vector); 
				} else { 
					rad_pwdecode((char *)pair->strvalue, 
						     pair->length, secret, 
						     (char *)packet->vector); 
				} 
				if (pair->attribute == PW_USER_PASSWORD) { 
					pair->length = strlen((char *)pair->strvalue); 
				} 
				break; 
 
				/* 
				 *  Tunnel-Password 
				 */ 
			case FLAG_ENCRYPT_TUNNEL_PASSWORD: 
			        rad_tunnel_pwdecode((char *)pair->strvalue, 
						    &pair->length,  
						    secret, 
						    (char *)original->vector); 
				break; 
 
				/* 
				 *  Ascend-Send-Secret 
				 *  Ascend-Receive-Secret 
				 */ 
			case FLAG_ENCRYPT_ASCEND_SECRET: 
/*				{ 
					uint8_t my_digest[AUTH_VECTOR_LEN]; 
					make_secret(my_digest, 
						    original->vector, 
						    secret, ptr); 
					memcpy(pair->strvalue, my_digest, 
					       AUTH_VECTOR_LEN ); 
					pair->strvalue[AUTH_VECTOR_LEN] = '\0'; 
					pair->length = strlen(pair->strvalue); 
				}*/ 
				break; 
			} /* switch over encryption flags */ 
			break;	/* from octets/string/abinary */ 
			 
		case PW_TYPE_INTEGER: 
		case PW_TYPE_DATE: 
		case PW_TYPE_IPADDR: 
			/* 
			 *	Check for RFC compliance.  If the 
			 *	attribute isn't compliant, turn it 
			 *	into a string of raw octets. 
			 * 
			 *	Also set the lvalue to something 
			 *	which should never match anything. 
			 */ 
			if (attrlen != 4) { 
				pair->type = PW_TYPE_OCTETS; 
				memcpy(pair->strvalue, ptr, attrlen); 
				pair->lvalue = 0xbad1bad1; 
				break; 
			} 
 
      			memcpy(&lvalue, ptr, 4); 
 
			if (attr->type != PW_TYPE_IPADDR) { 
				pair->lvalue = ntohl(lvalue); 
			} else { 
				 /* 
				  *  It's an IP address, keep it in network 
				  *  byte order, and put the ASCII IP 
				  *  address or host name into the string 
				  *  value. 
				  */ 
				pair->lvalue = lvalue; 
				ip_ntoa(pair->strvalue, pair->lvalue); 
			} 
 
			/* 
			 *  Only PW_TYPE_INTEGER should have tags. 
			 */ 
			if (pair->flags.has_tag && 
			    pair->type == PW_TYPE_INTEGER) { 
			        pair->flags.tag = (pair->lvalue >> 24) & 0xff; 
				pair->lvalue &= 0x00ffffff; 
			} 
 
			if (attr->type == PW_TYPE_INTEGER) { 
				DICT_VALUE *dval; 
				dval = dict_valbyattr(pair->attribute, 
						      pair->lvalue); 
				if (dval) { 
					strNcpy(pair->strvalue, 
						dval->name, 
						sizeof(pair->strvalue)); 
				} 
			} 
			break; 
			 
		default: 
			printf("    %s (Unknown Type %d)\n", 
			      attr->name,attr->type); 
			free(pair); 
			pair = NULL; 
			break; 
		} 
		 
		if (pair) { 
			//debug_pair(pair); 
			*tail = pair; 
			tail = &pair->next; 
		} 
 
		ptr += attrlen; 
		length -= attrlen; 
		if (vendorlen > 0) vendorlen -= (attrlen + 2); 
	} 
 
	/* 
	 *	Merge information from the outside world into our 
	 *	random pool 
	 */ 
	for (length = 0; length < AUTH_VECTOR_LEN; length++) { 
		lrad_rand_pool.randmem[length] += packet->vector[length]; 
	} 
	lrad_rand_pool.randmem[lrad_rand_pool.randmem[0] & 0xff] += packet->id; 
	lrad_rand_pool.randmem[lrad_rand_pool.randmem[1] & 0xff] += packet->data_len; 
 
	return 0; 
} 
 
#define AUTH_PASS_LEN (16) 
 
/* 
 *	Decode password. 
 */ 
int rad_pwdecode(char *passwd, int pwlen, const char *secret, 
		 const char *vector) 
{ 
	uint8_t	buffer[AUTH_VECTOR_LEN + MAX_STRING_LEN + 1]; 
	char	digest[AUTH_VECTOR_LEN]; 
	char	r[AUTH_VECTOR_LEN]; 
	char	*s; 
	int	i, n, secretlen; 
	int	rlen; 
 
	/* 
	 *	Use the secret to setup the decryption digest 
	 */ 
	secretlen = strlen(secret); 
	memcpy(buffer, secret, secretlen); 
	memcpy(buffer + secretlen, vector, AUTH_VECTOR_LEN); 
	librad_md5_calc((u_char *)digest, buffer, secretlen + AUTH_VECTOR_LEN); 
 
	/* 
	 *	Now we can decode the password *in place* 
	 */ 
	memcpy(r, passwd, AUTH_PASS_LEN); 
	for (i = 0; i < AUTH_PASS_LEN && i < pwlen; i++) 
		passwd[i] ^= digest[i]; 
 
	if (pwlen <= AUTH_PASS_LEN) { 
		passwd[pwlen+1] = 0; 
		return pwlen; 
	} 
 
	/* 
	 *	Length > AUTH_PASS_LEN, so we need to use the extended 
	 *	algorithm. 
	 */ 
	rlen = ((pwlen - 1) / AUTH_PASS_LEN) * AUTH_PASS_LEN; 
 
	for (n = rlen; n > 0; n -= AUTH_PASS_LEN ) {  
		s = (n == AUTH_PASS_LEN) ? r : (passwd + n - AUTH_PASS_LEN); 
		memcpy(buffer + secretlen, s, AUTH_PASS_LEN); 
		librad_md5_calc((u_char *)digest, buffer, secretlen + AUTH_PASS_LEN); 
		for (i = 0; i < AUTH_PASS_LEN && (i + n) < pwlen; i++) 
			passwd[i + n] ^= digest[i]; 
	} 
	passwd[pwlen] = 0; 
 
	return pwlen; 
} 
 
 
/* 
 *	Decode Tunnel-Password encrypted attributes. 
 * 
 *      Defined in RFC-2868, this adds a two char SALT to the initial intermediate 
 *      value, to differentiate it from the above. 
 */ 
 
int rad_tunnel_pwdecode(char *passwd, int * pwlen, const char *secret, const char *vector) 
{ 
 
	return 0; 
} 
 
/* 
 *	Encode a CHAP password 
 * 
 *	FIXME: might not work with Ascend because 
 *	we use vp->length, and Ascend gear likes 
 *	to send an extra '\0' in the string! 
 */ 
int rad_chap_encode(RADIUS_PACKET *packet, char *output, int id, 
		    VALUE_PAIR *password) 
{ 
	int		i; 
	char		*ptr; 
	char		string[MAX_STRING_LEN * 2 + 1]; 
	VALUE_PAIR	*challenge; 
 
	/* 
	 *	Sanity check the input parameters 
	 */ 
	if ((packet == NULL) || (password == NULL)) { 
		return -1; 
	} 
 
	/* 
	 *	Note that the password VP can be EITHER 
	 *	a User-Password attribute (from a check-item list), 
	 *	or a CHAP-Password attribute (the client asking 
	 *	the library to encode it). 
	 */ 
 
	i = 0; 
	ptr = string; 
	*ptr++ = id; 
 
	i++; 
	memcpy(ptr, password->strvalue, password->length); 
	ptr += password->length; 
	i += password->length; 
 
	/* 
	 *	Use Chap-Challenge pair if present, 
	 *	Request-Authenticator otherwise. 
	 */ 
	challenge = pairfind(packet->vps, PW_CHAP_CHALLENGE); 
	if (challenge) { 
		memcpy(ptr, challenge->strvalue, challenge->length); 
		i += challenge->length; 
	} else { 
		memcpy(ptr, packet->vector, AUTH_VECTOR_LEN); 
		i += AUTH_VECTOR_LEN;  
	} 
 
	*output = id; 
	librad_md5_calc((u_char *)output + 1, (u_char *)string, i); 
 
	return 0; 
}