/*
 *	If we're proxying EAP, then there may be magic we need
 *	to do.
 */
static rlm_rcode_t mod_post_proxy(void *inst, REQUEST *request)
{
	size_t		i;
	size_t		len;
	VALUE_PAIR	*vp;
	eap_handler_t	*handler;

	/*
	 *	Just in case the admin lists EAP in post-proxy-type Fail.
	 */
	if (!request->proxy_reply) return RLM_MODULE_NOOP;

	/*
	 *	If there was a handler associated with this request,
	 *	then it's a tunneled request which was proxied...
	 */
	handler = request_data_get(request, inst, REQUEST_DATA_eap_handler_t);
	if (handler != NULL) {
		rlm_rcode_t rcode;
		eap_tunnel_data_t *data;

		/*
		 *	Grab the tunnel callbacks from the request.
		 */
		data = (eap_tunnel_data_t *) request_data_get(request,
							      request->proxy,
							      REQUEST_DATA_EAP_TUNNEL_CALLBACK);
		if (!data) {
			radlog_request(L_ERR, 0, request, "Failed to retrieve callback for tunneled session!");
			eap_handler_free(inst, handler);
			return RLM_MODULE_FAIL;
		}

		/*
		 *	Do the callback...
		 */
		RDEBUG2("Doing post-proxy callback");
		rcode = data->callback(handler, data->tls_session);
		free(data);
		if (rcode == 0) {
			RDEBUG2("Failed in post-proxy callback");
			eap_fail(handler);
			eap_handler_free(inst, handler);
			return RLM_MODULE_REJECT;
		}

		/*
		 *	We are done, wrap the EAP-request in RADIUS to send
		 *	with all other required radius attributes
		 */
		eap_compose(handler);

		/*
		 *	Add to the list only if it is EAP-Request, OR if
		 *	it's LEAP, and a response.
		 */
		if ((handler->eap_ds->request->code == PW_EAP_REQUEST) &&
		    (handler->eap_ds->request->type.num >= PW_EAP_MD5)) {
			if (!eaplist_add(inst, handler)) {
				eap_fail(handler);
				eap_handler_free(inst, handler);
				return RLM_MODULE_FAIL;
			}
			
		} else {	/* couldn't have been LEAP, there's no tunnel */
			RDEBUG2("Freeing handler");
			/* handler is not required any more, free it now */
			eap_handler_free(inst, handler);
		}

		/*
		 *	If it's an Access-Accept, RFC 2869, Section 2.3.1
		 *	says that we MUST include a User-Name attribute in the
		 *	Access-Accept.
		 */
		if ((request->reply->code == PW_AUTHENTICATION_ACK) &&
		    request->username) {
			/*
			 *	Doesn't exist, add it in.
			 */
			vp = pairfind(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
			if (!vp) {
				pairmake_reply("User-Name",
					       request->username->vp_strvalue,
					       T_OP_EQ);
			}
		}

		return RLM_MODULE_OK;
	} else {
		RDEBUG2("No pre-existing handler found");
	}

	/*
	 *	There may be more than one Cisco-AVPair.
	 *	Ensure we find the one with the LEAP attribute.
	 */
	vp = request->proxy_reply->vps;
	for (;;) {
		/*
		 *	Hmm... there's got to be a better way to
		 *	discover codes for vendor attributes.
		 *
		 *	This is vendor Cisco (9), Cisco-AVPair
		 *	attribute (1)
		 */
		vp = pairfind(vp, 1, 9, TAG_ANY);
		if (!vp) {
			return RLM_MODULE_NOOP;
		}

		/*
		 *	If it's "leap:session-key", then stop.
		 *
		 *	The format is VERY specific!
		 */
		if (strncasecmp(vp->vp_strvalue, "leap:session-key=", 17) == 0) {
			break;
		}

		/*
		 *	Not this AV-pair.  Go to the next one.
		 */
		vp = vp->next;
	}

	/*
	 *	The format is very specific.
	 */
	if (vp->length != 17 + 34) {
		RDEBUG2("Cisco-AVPair with leap:session-key has incorrect length %d: Expected %d",
		       vp->length, 17 + 34);
		return RLM_MODULE_NOOP;
	}

	/*
	 *	Decrypt the session key, using the proxy data.
	 */
	i = 34;			/* starts off with 34 octets */
	len = rad_tunnel_pwdecode(vp->vp_octets + 17, &i,
				  request->home_server->secret,
				  request->proxy->vector);

	/*
	 *	FIXME: Assert that i == 16.
	 */

	/*
	 *	Encrypt the session key again, using the request data.
	 */
	rad_tunnel_pwencode(vp->vp_strvalue + 17, &len,
			    request->client->secret,
			    request->packet->vector);

	return RLM_MODULE_UPDATED;
}
Beispiel #2
0
/*
 *	Reply to the request.  Also attach
 *	reply attribute value pairs and any user message provided.
 */
int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
	     const char *secret)
{
	VALUE_PAIR		*reply;
	struct	sockaddr_in	saremote;
	struct	sockaddr_in	*sa;
	const char		*what;
	uint8_t			ip_buffer[16];

	if ((packet->code > 0) && (packet->code < 52)) {
		what = packet_codes[packet->code];
	} else {
		what = "Reply";
	}

	/*
	 *  First time through, allocate room for the packet
	 */
	if (!packet->data) {
		  radius_packet_t	*hdr;
		  uint32_t		lvalue;
		  uint8_t		*ptr, *length_ptr, *vsa_length_ptr;
		  uint8_t		digest[16];
		  int			secretlen;
		  int			vendorcode, vendorpec;
		  u_short		total_length;
		  int			len, allowed;
		  int			msg_auth_offset = 0;

		  /*
		   *	For simplicity in the following logic, we allow
		   *	the attributes to "overflow" the 4k maximum
		   *	RADIUS packet size, by one attribute.
		   */
		  uint8_t		data[MAX_PACKET_LEN + 256];
		  
		  /*
		   *	Use memory on the stack, until we know how
		   *	large the packet will be.
		   */
		  hdr = (radius_packet_t *) data;

		  /*
		   *	Build standard header
		   */
		  hdr->code = packet->code;
		  hdr->id = packet->id;
		  if ((packet->code == PW_ACCOUNTING_REQUEST) ||
		      (packet->code == PW_DISCONNECT_REQUEST)) {
			  memset(hdr->vector, 0, AUTH_VECTOR_LEN);
		  } else {
			  memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN);
		  }

		  DEBUG("Sending %s of id %d to %s:%d\n",
			what, packet->id,
			ip_ntoa((char *)ip_buffer, packet->dst_ipaddr),
			packet->dst_port);
		  
		  total_length = AUTH_HDR_LEN;
		  
		  /*
		   *	Load up the configuration values for the user
		   */
		  ptr = hdr->data;
		  vendorcode = 0;
		  vendorpec = 0;
		  vsa_length_ptr = NULL;

		  for (reply = packet->vps; reply; reply = reply->next) {
			  /*
			   *	Ignore non-wire attributes
			   */
			  if ((VENDOR(reply->attribute) == 0) &&
			      ((reply->attribute & 0xFFFF) > 0xff)) {
				  continue;
			  }

			  /*
			   *	Check that the packet is no more than
			   *	4k in size, AFTER over-flowing the 4k
			   *	boundary.  Note that the 'data'
			   *	buffer, above, is one attribute longer
			   *	than necessary, in order to permit
			   *	this overflow.
			   */
			  if (total_length > MAX_PACKET_LEN) {
				  librad_log("ERROR: Too many attributes for packet, result is larger than RFC maximum of 4k");
				  return -1;
			  }

			  /*
			   *	Do stuff for Message-Authenticator
			   */
			  if (reply->attribute == PW_MESSAGE_AUTHENTICATOR) {
				  /*
				   *  Set it to zero!
				   */
				  reply->length = AUTH_VECTOR_LEN;
				  memset(reply->strvalue, 0, AUTH_VECTOR_LEN);
				  msg_auth_offset = total_length;
			  }

			  /*
			   *	Print out ONLY the attributes which
			   *	we're sending over the wire, and print
			   *	them out BEFORE they're encrypted.
			   */
			  debug_pair(reply);

			  /*
			   *	We have a different vendor.  Re-set
			   *	the vendor codes.
			   */
			  if (vendorcode != VENDOR(reply->attribute)) {
				  vendorcode = 0;
				  vendorpec = 0;
				  vsa_length_ptr = NULL;
			  }

			  /*
			   *	If the Vendor-Specific attribute is getting
			   *	full, then create a new VSA attribute
			   *
			   *	FIXME: Multiple VSA's per Vendor-Specific
			   *	SHOULD be configurable.  When that's done,
			   *	the (1), below, can be changed to point to
			   *	a configuration variable which is set TRUE
			   *	if the NAS cannot understand multiple VSA's
			   *	per Vendor-Specific
			   */
			  if ((1) || /* ALWAYS create a new Vendor-Specific */
			      (vsa_length_ptr &&
			       (reply->length + *vsa_length_ptr) >= MAX_STRING_LEN)) {
				  vendorcode = 0;
				  vendorpec = 0;
				  vsa_length_ptr = NULL;
			  }

			  /*
			   *	Maybe we have the start of a set of
			   *	(possibly many) VSA attributes from
			   *	one vendor.  Set a global VSA wrapper
			   */
			  if ((vendorcode == 0) &&
			      ((vendorcode = VENDOR(reply->attribute)) != 0)) {
				  vendorpec  = dict_vendorpec(vendorcode);
				  
				  /*
				   *	This is a potentially bad error...
				   *	we can't find the vendor ID!
				   */
				  if (vendorpec == 0) {
					  /* FIXME: log an error */
					  continue;
				  }

				  /*
				   *	Build a VSA header.
				   */
				  *ptr++ = PW_VENDOR_SPECIFIC;
				  vsa_length_ptr = ptr;
				  *ptr++ = 6;
				  lvalue = htonl(vendorpec);
				  memcpy(ptr, &lvalue, 4);
				  ptr += 4;
				  total_length += 6;
			  }

			  if (vendorpec == VENDORPEC_USR) {
				  lvalue = htonl(reply->attribute & 0xFFFF);
				  memcpy(ptr, &lvalue, 4);

				  length_ptr = vsa_length_ptr;

				  total_length += 4;
				  *length_ptr  += 4;
				  ptr          += 4;

				  /*
				   *	Each USR attribute gets it's own
				   *	VSA wrapper, so we re-set the
				   *	vendor specific information.
				   */
				  vendorcode = 0;
				  vendorpec = 0;
				  vsa_length_ptr = NULL;

			  } else {
				  /*
				   *	All other attributes are as
				   *	per the RFC spec.
				   */

				  *ptr++ = (reply->attribute & 0xFF);
				  length_ptr = ptr;
				  if (vsa_length_ptr) *vsa_length_ptr += 2;
				  *ptr++ = 2;
				  total_length += 2;
			  }
			  
			  switch(reply->type) {
				  
				  /*
				   *	Ascend binary attributes are
				   *	stored internally in binary form.
				   */
			  case PW_TYPE_ABINARY:
			  case PW_TYPE_STRING:
			  case PW_TYPE_OCTETS:
				  /*
				   *  FIXME: HACK for non-updated dictionaries.
				   *  REMOVE in a future release.
				   */
				  if ((strcmp(reply->name, "Ascend-Send-Secret") == 0) ||
				      (strcmp(reply->name, "Ascend-Receive-Secret") == 0)) {
					  reply->flags.encrypt = FLAG_ENCRYPT_ASCEND_SECRET;
				  }
				  if (reply->attribute == PW_USER_PASSWORD) {
					  reply->flags.encrypt = FLAG_ENCRYPT_USER_PASSWORD;
				  }

				  /*
				   *  Encrypt the various password styles
				   */
				  switch (reply->flags.encrypt) {
				  default:
					  break;

				  case FLAG_ENCRYPT_USER_PASSWORD:
				    rad_pwencode((char *)reply->strvalue,
						 &(reply->length),
						 (const char *)secret,
						 (const char *)packet->vector);
				    break;

				  case FLAG_ENCRYPT_TUNNEL_PASSWORD:
					  rad_tunnel_pwencode(reply->strvalue,
							      &(reply->length),
							      secret,
							      packet->vector);
					  break;


				  case FLAG_ENCRYPT_ASCEND_SECRET:
					  make_secret(digest, packet->vector,
						      secret, reply->strvalue);
					  memcpy(reply->strvalue, digest, AUTH_VECTOR_LEN );
					  reply->length = AUTH_VECTOR_LEN;
				  } /* switch over encryption flags */

				  len = reply->length;

				  /*
				   *    Set the TAG at the beginning
				   *    of the string if tagged.  If
				   *    tag value is not valid for
				   *    tagged attribute, make it 0x00
				   *    per RFC 2868.  -cparker
				   */
				  if (reply->flags.has_tag) {
					  if (TAG_VALID(reply->flags.tag)) {
						  len++;
						  *ptr++ = reply->flags.tag;

					  } else if (reply->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD) {
						  /*
						   *  Tunnel passwords
						   *  REQUIRE a tag,
						   *  even if we don't
						   *  have a valid
						   *  tag.
						   */
						  len++;
						  *ptr++ = 0x00;
					  } /* else don't write a tag */
				  } /* else the attribute doesn't have a tag */
				 
				  /*
				   *	Ensure we don't go too far.
				   *	The 'length' of the attribute
				   *	may be 0..255, minus whatever
				   *	octets are used in the attribute
				   *	header.
				   */
				  allowed = 255;
				  if (vsa_length_ptr) {
					  allowed -= *vsa_length_ptr;
				  } else {
					  allowed -= *length_ptr;
				  }
				  
				  if (len > allowed) {
					  len = allowed;
				  }
				  
				  *length_ptr += len;
				  if (vsa_length_ptr) *vsa_length_ptr += len;
				  /*
				   *  If we have tagged attributes we can't assume that
				   *  len == reply->length.  Use reply->length for copying
				   *  the string data into the packet.  Use len for the
				   *  true length of the string+tags.
				   */
				  memcpy(ptr, reply->strvalue, reply->length);
				  ptr += reply->length;
				  total_length += len;
				  break;
				  
			  case PW_TYPE_INTEGER:
			  case PW_TYPE_IPADDR:
				  *length_ptr += 4;
				  if (vsa_length_ptr) *vsa_length_ptr += 4;

				  if (reply->type == PW_TYPE_INTEGER ) {
				          /*  If tagged, the tag becomes the MSB of the value */
				          if(reply->flags.has_tag) {
					         /*  Tag must be ( 0x01 -> 0x1F ) OR 0x00  */
					         if(!TAG_VALID(reply->flags.tag)) {
						       reply->flags.tag = 0x00;
						 }
					         lvalue = htonl((reply->lvalue & 0xffffff) |
								((reply->flags.tag & 0xff) << 24));
					  } else {
					         lvalue = htonl(reply->lvalue);
					  }
				  } else {
					  /*
					   *  IP address is already in
					   *  network byte order.
					   */
					  lvalue = reply->lvalue;
				  }
				  memcpy(ptr, &lvalue, 4);
				  ptr += 4;
				  total_length += 4;
				  break;

				  /*
				   *  There are no tagged date attributes.
				   */
			  case PW_TYPE_DATE:
				  *length_ptr += 4;
				  if (vsa_length_ptr) *vsa_length_ptr += 4;

				  lvalue = htonl(reply->lvalue);
				  memcpy(ptr, &lvalue, 4);
				  ptr += 4;
				  total_length += 4;
				  break;
			  default:
				  break;
			  }
		  } /* done looping over all attributes */

		  /*
		   *	Fill in the rest of the fields, and copy
		   *	the data over from the local stack to
		   *	the newly allocated memory.
		   *
		   *	Yes, all this 'memcpy' is slow, but it means
		   *	that we only allocate the minimum amount of
		   *	memory for a request.
		   */
		  packet->data_len = total_length;
		  packet->data = (uint8_t *) malloc(packet->data_len);
		  if (!packet->data) {
			  librad_log("Out of memory");
			  return -1;
		  }
		  memcpy(packet->data, data, packet->data_len);
		  hdr = (radius_packet_t *) packet->data;

		  total_length = htons(total_length);
		  memcpy(hdr->length, &total_length, sizeof(u_short));

		  /*
		   *	If this is not an authentication request, we
		   *	need to calculate the md5 hash over the entire packet
		   *	and put it in the vector.
		   */
		  secretlen = strlen(secret);
		  if (packet->code != PW_AUTHENTICATION_REQUEST &&
		      packet->code != PW_STATUS_SERVER) {
		    MD5_CTX	context;
		      /*
		       *	Set the Message-Authenticator attribute,
		       *	BEFORE setting the reply authentication vector
		       *	for CHALLENGE, ACCEPT and REJECT.
		       */
		      if (msg_auth_offset) {
			      uint8_t calc_auth_vector[AUTH_VECTOR_LEN];

			      switch (packet->code) {
			      default:
				break;
				
			      case PW_AUTHENTICATION_ACK:
			      case PW_AUTHENTICATION_REJECT:
			      case PW_ACCESS_CHALLENGE:
				if (original) {
				  memcpy(hdr->vector, original->vector, AUTH_VECTOR_LEN);
				}
				break;
			      }

			      memset(packet->data + msg_auth_offset + 2, 0,
				     AUTH_VECTOR_LEN);
			      lrad_hmac_md5(packet->data, packet->data_len,
					    secret, secretlen, calc_auth_vector);
			      memcpy(packet->data + msg_auth_offset + 2,
				     calc_auth_vector, AUTH_VECTOR_LEN);
			      memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN);
		      }

		      MD5Init(&context);
		      MD5Update(&context, packet->data, packet->data_len);
		      MD5Update(&context, secret, strlen(secret));
		      MD5Final(digest, &context);

		      memcpy(hdr->vector, digest, AUTH_VECTOR_LEN);
		      memcpy(packet->vector, digest, AUTH_VECTOR_LEN);
		  }

		  /*
		   *	Set the Message-Authenticator attribute,
		   *	AFTER setting the authentication vector
		   *	only for ACCESS-REQUESTS
		   */
		  else if (msg_auth_offset) {
			  uint8_t calc_auth_vector[AUTH_VECTOR_LEN];

			  switch (packet->code) {
			  default:
			    break;
			    
			  case PW_AUTHENTICATION_ACK:
			  case PW_AUTHENTICATION_REJECT:
			  case PW_ACCESS_CHALLENGE:
			    if (original) {
				    memcpy(hdr->vector, original->vector,
					   AUTH_VECTOR_LEN);
			    }
			    break;
			  }

			  memset(packet->data + msg_auth_offset + 2,
				 0, AUTH_VECTOR_LEN);
			  lrad_hmac_md5(packet->data, packet->data_len,
					secret, secretlen, calc_auth_vector);
			  memcpy(packet->data + msg_auth_offset + 2,
				  calc_auth_vector, AUTH_VECTOR_LEN);
			  memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN);
		  }

		  /*
		   *	If packet->data points to data, then we print out
		   *	the VP list again only for debugging.
		   */
	} else if (librad_debug) {
	  	DEBUG("Re-sending %s of id %d to %s:%d\n", what, packet->id,
		      ip_ntoa((char *)ip_buffer, packet->dst_ipaddr),
		      packet->dst_port);
		
		for (reply = packet->vps; reply; reply = reply->next) {
			/* FIXME: ignore attributes > 0xff */
			debug_pair(reply);
		}
	}
	
	/*
	 *	And send it on it's way.
	 */
	sa = (struct sockaddr_in *) &saremote;
        memset ((char *) sa, '\0', sizeof (saremote));
	sa->sin_family = AF_INET;
	sa->sin_addr.s_addr = packet->dst_ipaddr;
	sa->sin_port = htons(packet->dst_port);

	return sendto(packet->sockfd, packet->data, (int)packet->data_len, 0,
		      (struct sockaddr *)&saremote, sizeof(struct sockaddr_in));
}