Esempio n. 1
0
static int eap_req2vp(EAP_HANDLER *handler)
{
	int		encoded, total, size;
	const uint8_t	*ptr;
	VALUE_PAIR	*head = NULL;
	VALUE_PAIR	**tail = &head;
	VALUE_PAIR	*vp;

	ptr = wpabuf_head(handler->server_ctx.eap_if->eapReqData);
	encoded = total = wpabuf_len(handler->server_ctx.eap_if->eapReqData);

	do {
		size = total;
		if (size > 253) size = 253;

		vp = paircreate(handler->request->reply, PW_EAP_MESSAGE, 0);
		if (!vp) {
			pairfree(&head);
			return -1;
		}
		pairmemcpy(vp, ptr, size);

		*tail = vp;
		tail = &(vp->next);

		ptr += size;
		total -= size;
	} while (total > 0);

	pairdelete(&handler->request->reply->vps, PW_EAP_MESSAGE, TAG_ANY);
	pairadd(&handler->request->reply->vps, head);

	return encoded;
}
static int eap_sim_sendstart(eap_handler_t *handler)
{
	VALUE_PAIR **vps, *newvp;
	uint16_t words[3];
	eap_sim_state_t *ess;
	RADIUS_PACKET *packet;
	uint8_t *p;

	rad_assert(handler->request != NULL);
	rad_assert(handler->request->reply);

	ess = (eap_sim_state_t *)handler->opaque;

	/* these are the outgoing attributes */
	packet = handler->request->reply;
	vps = &packet->vps;
	rad_assert(vps != NULL);


	/*
	 *	Add appropriate TLVs for the EAP things we wish to send.
	 */

	/* the version list. We support only version 1. */
	words[0] = htons(sizeof(words[1]));
	words[1] = htons(EAP_SIM_VERSION);
	words[2] = 0;

	newvp = paircreate(packet, PW_EAP_SIM_VERSION_LIST, 0);
	pairmemcpy(newvp, (uint8_t const *) words, sizeof(words));

	pairadd(vps, newvp);

	/* set the EAP_ID - new value */
	newvp = paircreate(packet, PW_EAP_ID, 0);
	newvp->vp_integer = ess->sim_id++;
	pairreplace(vps, newvp);

	/* record it in the ess */
	ess->keys.versionlistlen = 2;
	memcpy(ess->keys.versionlist, words + 1, ess->keys.versionlistlen);

	/* the ANY_ID attribute. We do not support re-auth or pseudonym */
	newvp = paircreate(packet, PW_EAP_SIM_FULLAUTH_ID_REQ, 0);
	newvp->length = 2;
	newvp->vp_octets = p = talloc_array(newvp, uint8_t, 2);

	p[0] = 0;
	p[0] = 1;
	pairadd(vps, newvp);

	/* the SUBTYPE, set to start. */
	newvp = paircreate(packet, PW_EAP_SIM_SUBTYPE, 0);
	newvp->vp_integer = EAPSIM_START;
	pairreplace(vps, newvp);

	return 1;
}
/** Allocate a request packet
 *
 * This is done once per request with the same packet being sent to multiple realms.
 */
static rlm_rcode_t rlm_replicate_alloc(RADIUS_PACKET **out, REQUEST *request, pair_lists_t list, PW_CODE code)
{
	rlm_rcode_t rcode = RLM_MODULE_OK;
	RADIUS_PACKET *packet = NULL;
	VALUE_PAIR *vp, **vps;

	*out = NULL;

	packet = rad_alloc(request, 1);
	if (!packet) {
		return RLM_MODULE_FAIL;
	}
	packet->code = code;

	/*
	 *	Figure out which list in the request were replicating
	 */
	vps = radius_list(request, list);
	if (!vps) {
		RWDEBUG("List '%s' doesn't exist for this packet", fr_int2str(pair_lists, list, "<INVALID>"));
		rcode = RLM_MODULE_INVALID;
		goto error;
	}

	/*
	 *	Don't assume the list actually contains any attributes.
	 */
	if (*vps) {
		packet->vps = paircopy(packet, *vps);
		if (!packet->vps) {
			rcode = RLM_MODULE_FAIL;
			goto error;
		}
	}

	/*
	 *	For CHAP, create the CHAP-Challenge if it doesn't exist.
	 */
	if ((code == PW_CODE_ACCESS_REQUEST) &&
	    (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
	    (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
		vp = radius_paircreate(packet, &packet->vps, PW_CHAP_CHALLENGE, 0);
		pairmemcpy(vp, request->packet->vector, AUTH_VECTOR_LEN);
	}

	*out = packet;
	return rcode;

error:
	talloc_free(packet);
	return rcode;
}
Esempio n. 4
0
/** Copy a single valuepair
 *
 * Allocate a new valuepair and copy the da from the old vp.
 *
 * @param[in] ctx for talloc
 * @param[in] vp to copy.
 * @return a copy of the input VP or NULL on error.
 */
VALUE_PAIR *paircopyvp(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
{
	VALUE_PAIR *n;

	if (!vp) return NULL;

	VERIFY_VP(vp);

	n = pairalloc(ctx, vp->da);
	if (!n) return NULL;

	memcpy(n, vp, sizeof(*n));

	/*
	 *	If the DA is unknown, steal "n" to "ctx".  This does
	 *	nothing for "n", but will also copy the unknown "da".
	 */
	if (n->da->flags.is_unknown) {
		pairsteal(ctx, n);
	}

	n->next = NULL;

	/*
	 *	If it's an xlat, copy the raw string and return early,
	 *	so we don't pre-expand or otherwise mangle the VALUE_PAIR.
	 */
	if (vp->type == VT_XLAT) {
		n->xlat = talloc_typed_strdup(n, n->xlat);
		return n;
	}

	switch (vp->da->type) {
	case PW_TYPE_TLV:
	case PW_TYPE_OCTETS:
		n->vp_octets = NULL;	/* else pairmemcpy will free vp's value */
		pairmemcpy(n, vp->vp_octets, n->vp_length);
		break;

	case PW_TYPE_STRING:
		n->vp_strvalue = NULL;	/* else pairstrnpy will free vp's value */
		pairstrncpy(n, vp->vp_strvalue, n->vp_length);
		break;

	default:
		break;
	}

	return n;
}
Esempio n. 5
0
/*
 *	Add raw hex data to the reply.
 */
void eap_add_reply(REQUEST *request,
		   char const *name, uint8_t const *value, int len)
{
	VALUE_PAIR *vp;

	vp = pairmake_reply(name, NULL, T_OP_EQ);
	if (!vp) {
		REDEBUG("Did not create attribute %s: %s\n",
			name, fr_strerror());
		return;
	}

	pairmemcpy(vp, value, len);
}
Esempio n. 6
0
/*
 *	Access-Requests can have the CHAP-Challenge implicitly taken
 *	from the request authenticator.  If the NAS has done that,
 *	then we need to copy the data to a real CHAP-Challenge
 *	attribute when proxying.  Otherwise when we proxy the request,
 *	the new authenticator is different, and the CHAP calculations
 *	will fail.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_pre_proxy(UNUSED void *instance,
				 REQUEST *request)
{
	VALUE_PAIR *vp;

	/*
	 *	For Access-Requests, which have CHAP-Password,
	 *	and no CHAP-Challenge, copy it over from the request.
	 */
	if (request->packet->code != PW_CODE_AUTHENTICATION_REQUEST) return RLM_MODULE_NOOP;

	if (!pairfind(request->proxy->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) return RLM_MODULE_NOOP;

	vp = radius_paircreate(request, &request->proxy->vps, PW_CHAP_CHALLENGE, 0);
	if (!vp) return RLM_MODULE_FAIL;

	pairmemcpy(vp, request->packet->vector, sizeof(request->packet->vector));

	return RLM_MODULE_OK;
}
Esempio n. 7
0
VALUE_PAIR *eap_packet2vp(RADIUS_PACKET *packet, eap_packet_raw_t const *eap)
{
	int		total, size;
	uint8_t const *ptr;
	VALUE_PAIR	*head = NULL;
	VALUE_PAIR	*vp;
	vp_cursor_t	out;

	total = eap->length[0] * 256 + eap->length[1];

	if (total == 0) {
		DEBUG("Asked to encode empty EAP-Message!");
		return NULL;
	}

	ptr = (uint8_t const *) eap;

	fr_cursor_init(&out, &head);
	do {
		size = total;
		if (size > 253) size = 253;

		vp = paircreate(packet, PW_EAP_MESSAGE, 0);
		if (!vp) {
			pairfree(&head);
			return NULL;
		}
		pairmemcpy(vp, ptr, size);

		fr_cursor_insert(&out, vp);

		ptr += size;
		total -= size;
	} while (total > 0);

	return head;
}
Esempio n. 8
0
/*
 *
 *     Verify that a Perl SV is a string and save it in FreeRadius
 *     Value Pair Format
 *
 */
static int pairadd_sv(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, char *key, SV *sv, FR_TOKEN op,
		      const char *hashname, const char *list_name)
{
	char	    *val;
	VALUE_PAIR      *vp;

	if (SvOK(sv)) {
		STRLEN len;
		val = SvPV(sv, len);
		vp = pairmake(ctx, vps, key, NULL, op);
		if (!vp) {
		fail:
			REDEBUG("Failed to create pair %s:%s %s %s", list_name, key,
				fr_int2str(fr_tokens, op, "<INVALID>"), val);
			return -1;
		}

		switch (vp->da->type) {
		case PW_TYPE_STRING:
			pairstrncpy(vp, val, len);
			break;

		case PW_TYPE_OCTETS:
			pairmemcpy(vp, (uint8_t const *)val, len);
			break;

		default:
			if (pairparsevalue(vp, val, len) < 0) goto fail;
		}

		RDEBUG("&%s:%s %s $%s{'%s'} -> '%s'", list_name, key, fr_int2str(fr_tokens, op, "<INVALID>"),
		       hashname, key, val);
		return 0;
	}
	return -1;
}
Esempio n. 9
0
/*
 *	Copy data from src to dst, where the attributes are of
 *	different type.
 */
static bool do_cast_copy(VALUE_PAIR *dst, VALUE_PAIR const *src)
{
	rad_assert(dst->da->type != src->da->type);

	if (dst->da->type == PW_TYPE_STRING) {
		dst->vp_strvalue = vp_aprint(dst, src);
		dst->length = strlen(dst->vp_strvalue);
		return true;
	}

	if (dst->da->type == PW_TYPE_OCTETS) {
		if (src->da->type == PW_TYPE_STRING) {
			pairmemcpy(dst, src->vp_octets, src->length);	/* Copy embedded NULLs */
		} else {
			pairmemcpy(dst, (uint8_t const *) &src->data, src->length);
		}
		return true;
	}

	if (src->da->type == PW_TYPE_STRING) {
		return pairparsevalue(dst, src->vp_strvalue);
	}

	if ((src->da->type == PW_TYPE_INTEGER64) &&
	    (dst->da->type == PW_TYPE_ETHERNET)) {
		uint8_t array[8];
		uint64_t i;

		i = htonll(src->vp_integer64);
		memcpy(array, &i, 8);

		/*
		 *	For OUIs in the DB.
		 */
		if ((array[0] != 0) || (array[1] != 0)) return false;

		memcpy(&dst->vp_ether, &array[2], 6);
		dst->length = 6;
		return true;
	}

	/*
	 *	The attribute we've found has to have a size which is
	 *	compatible with the type of the destination cast.
	 */
	if ((src->length < dict_attr_sizes[dst->da->type][0]) ||
	    (src->length > dict_attr_sizes[dst->da->type][1])) {
		EVAL_DEBUG("Casted attribute is wrong size (%u)", (unsigned int) src->length);
		return false;
	}

	if (src->da->type == PW_TYPE_OCTETS) {
		switch (dst->da->type) {
		case PW_TYPE_INTEGER64:
			dst->vp_integer = ntohll(*(uint64_t const *) src->vp_octets);
			break;


		case PW_TYPE_INTEGER:
		case PW_TYPE_DATE:
		case PW_TYPE_SIGNED:
			dst->vp_integer = ntohl(*(uint32_t const *) src->vp_octets);
			break;

		case PW_TYPE_SHORT:
			dst->vp_integer = ntohs(*(uint16_t const *) src->vp_octets);
			break;

		case PW_TYPE_BYTE:
			dst->vp_integer = src->vp_octets[0];
			break;

		default:
			memcpy(&dst->data, src->vp_octets, src->length);
			break;
		}

		dst->length = src->length;
		return true;
	}

	/*
	 *	Convert host order to network byte order.
	 */
	if ((dst->da->type == PW_TYPE_IPADDR) &&
	    ((src->da->type == PW_TYPE_INTEGER) ||
	     (src->da->type == PW_TYPE_DATE) ||
	     (src->da->type == PW_TYPE_SIGNED))) {
		dst->vp_ipaddr = htonl(src->vp_integer);

	} else if ((src->da->type == PW_TYPE_IPADDR) &&
		   ((dst->da->type == PW_TYPE_INTEGER) ||
		    (dst->da->type == PW_TYPE_DATE) ||
		    (dst->da->type == PW_TYPE_SIGNED))) {
		dst->vp_integer = htonl(src->vp_ipaddr);

	} else {		/* they're of the same byte order */
		memcpy(&dst->data, &src->data, src->length);
	}

	dst->length = src->length;

	return true;
}
Esempio n. 10
0
/*
 *	Generate a challenge to be presented to the user.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
{
	rlm_otp_t *inst = (rlm_otp_t *) instance;

	char challenge[OTP_MAX_CHALLENGE_LEN + 1];	/* +1 for '\0' terminator */
	int auth_type_found;

	/* Early exit if Auth-Type != inst->name */
	{
		VALUE_PAIR *vp;

		auth_type_found = 0;
		vp = pairfind(request->config_items, PW_AUTHTYPE, 0, TAG_ANY);
		if (vp) {
			auth_type_found = 1;
			if (strcmp(vp->vp_strvalue, inst->name)) {
				return RLM_MODULE_NOOP;
			}
		}
	}

	/* The State attribute will be present if this is a response. */
	if (pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY) != NULL) {
		DEBUG("rlm_otp: autz: Found response to Access-Challenge");

		return RLM_MODULE_OK;
	}

	/* User-Name attribute required. */
	if (!request->username) {
		RWDEBUG("Attribute \"User-Name\" "
		       "required for authentication");

		return RLM_MODULE_INVALID;
	}

	if (otp_pwe_present(request) == 0) {
		RWDEBUG("Attribute "
			"\"User-Password\" or equivalent required "
			"for authentication");

		return RLM_MODULE_INVALID;
	}

	/*
	 * 	We used to check for special "challenge" and "resync" passcodes
	 * 	here, but these are complicated to explain and application is
	 * 	limited.  More importantly, since we've removed all actual OTP
	 * 	code (now we ask otpd), it's awkward for us to support them.
	 * 	Should the need arise to reinstate these options, the most
	 *	likely choice is to duplicate some otpd code here.
	 */
	if (inst->allow_sync && !inst->allow_async) {
		/* This is the token sync response. */
		if (!auth_type_found) {
			pairmake_config("Auth-Type", inst->name, T_OP_EQ);
		}

		return RLM_MODULE_OK;
	}

	/*
	 *	Generate a random challenge.
	 */
	otp_async_challenge(challenge, inst->challenge_len);

	/*
	 *	Create the State attribute, which will be returned to
	 *	us along with the response.
	 *
	 *	We will need this to verify the response.
	 *
	 *	It must be hmac protected to prevent insertion of arbitrary
	 *	State by an inside attacker.
	 *
	 *	If we won't actually use the State (server config doesn't
	 *	allow async), we just use a trivial State.
	 *
	 *	We always create at least a trivial State, so mod_authorize()
	 *	can quickly pass on to mod_authenticate().
	 */
	{
		int32_t now = htonl(time(NULL)); //!< Low-order 32 bits on LP64.

		char gen_state[OTP_MAX_RADSTATE_LEN];
		size_t len;
		VALUE_PAIR *vp;

		len = otp_gen_state(gen_state, challenge, inst->challenge_len,
				    0, now, inst->hmac_key);

		vp = paircreate(request->reply, PW_STATE, 0);
		if (!vp) {
			return RLM_MODULE_FAIL;
		}

		pairmemcpy(vp, (uint8_t const *) gen_state, len);
		pairadd(&request->reply->vps, vp);
	}

	/*
	 *	Add the challenge to the reply.
	 */
	{
		VALUE_PAIR *vp;

		char *expanded = NULL;
		ssize_t len;

		/*
		 *	First add the internal OTP challenge attribute to
		 *	the reply list.
		 */
		vp = paircreate(request->reply, PW_OTP_CHALLENGE, 0);
		if (!vp) {
			return RLM_MODULE_FAIL;
		}

		pairstrcpy(vp, challenge);
		vp->op = T_OP_SET;

		pairadd(&request->reply->vps, vp);

		/*
		 *	Then add the message to the user to they known
		 *	what the challenge value is.
		 */

		len = radius_axlat(&expanded, request, inst->chal_prompt, NULL, NULL);
		if (len < 0) {
			return RLM_MODULE_FAIL;
		}

		vp = paircreate(request->reply, PW_REPLY_MESSAGE, 0);
		if (!vp) {
			talloc_free(expanded);
			return RLM_MODULE_FAIL;
		}

		(void) talloc_steal(vp, expanded);
		vp->vp_strvalue = expanded;
		vp->length = len;
		vp->op = T_OP_SET;
		vp->type = VT_DATA;

		pairadd(&request->reply->vps, vp);
	}

	/*
	 *	Mark the packet as an Access-Challenge packet.
	 * 	The server will take care of sending it to the user.
	 */
	request->reply->code = PW_CODE_ACCESS_CHALLENGE;
	DEBUG("rlm_otp: Sending Access-Challenge");

	if (!auth_type_found) {
		pairmake_config("Auth-Type", inst->name, T_OP_EQ);
	}

	return RLM_MODULE_HANDLED;
}
Esempio n. 11
0
/*
 * filterBinary:
 *
 * This routine will call routines to parse entries from an ASCII format
 * to a binary format recognized by the Ascend boxes.
 *
 *	pair:			Pointer to value_pair to place return.
 *
 *	valstr:			The string to parse
 *
 *	return:			-1 for error or 0.
 */
int
ascend_parse_filter(VALUE_PAIR *pair)
{
	int		token, type;
	int		rcode;
	int		argc;
	char		*argv[32];
	ascend_filter_t filter;
	char		*p;

	rcode = -1;

	/*
	 *	Rather than printing specific error messages, we create
	 *	a general one here, which won't be used if the function
	 *	returns OK.
	 */
	fr_strerror_printf("Text is not in proper format");

	/*
	 *	Tokenize the input string in the VP.
	 *
	 *	Once the filter is *completelty* parsed, then we will
	 *	over-write it with the final binary filter.
	 */
	p = talloc_strdup(pair, pair->vp_strvalue);
	argc = str2argv(p, argv, 32);
	if (argc < 3) {
		talloc_free(p);
		return -1;
	}

	/*
	 *	Decide which filter type it is: ip, ipx, or generic
	 */
	type = fr_str2int(filterType, argv[0], -1);
	memset(&filter, 0, sizeof(filter));

	/*
	 *	Validate the filter type.
	 */
	switch (type) {
	case RAD_FILTER_GENERIC:
	case RAD_FILTER_IP:
	case RAD_FILTER_IPX:
		filter.type = type;
		break;

	default:
		fr_strerror_printf("Unknown Ascend filter type \"%s\"", argv[0]);
		talloc_free(p);
		return -1;
		break;
	}

	/*
	 *	Parse direction
	 */
	token = fr_str2int(filterKeywords, argv[1], -1);
	switch (token) {
	case FILTER_IN:
		filter.direction = 1;
		break;

	case FILTER_OUT:
		filter.direction = 0;
		break;

	default:
		fr_strerror_printf("Unknown Ascend filter direction \"%s\"", argv[1]);
		talloc_free(p);
		return -1;
		break;
	}

	/*
	 *	Parse action
	 */
	token = fr_str2int(filterKeywords, argv[2], -1);
	switch (token) {
	case FILTER_FORWARD:
		filter.forward = 1;
		break;

	case FILTER_DROP:
		filter.forward = 0;
		break;

	default:
		fr_strerror_printf("Unknown Ascend filter action \"%s\"", argv[2]);
		talloc_free(p);
		return -1;
		break;
	}


	switch (type) {
	case RAD_FILTER_GENERIC:
		rcode = ascend_parse_generic(argc - 3, &argv[3],
					  &filter.u.generic);
		break;

	case RAD_FILTER_IP:
		rcode = ascend_parse_ip(argc - 3, &argv[3], &filter.u.ip);
		break;

	case RAD_FILTER_IPX:
		rcode = ascend_parse_ipx(argc - 3, &argv[3], &filter.u.ipx);
		break;
	}

	/*
	 *	Touch the VP only if everything was OK.
	 */
	if (rcode == 0) {
		pair->length = sizeof(filter);
		memcpy(pair->vp_filter, &filter, sizeof(filter));
	}

	talloc_free(p);
	return rcode;

#if 0
    /*
     * if 'more' is set then this new entry must exist, be a
     * FILTER_GENERIC_TYPE, direction and disposition must match for
     * the previous 'more' to be valid. If any should fail then TURN OFF
     * previous 'more'
     */
    if( prevRadPair ) {
	filt = ( RadFilter * )prevRadPair->vp_strvalue;
	if(( tok != FILTER_GENERIC_TYPE ) || (rc == -1 ) ||
	   ( prevRadPair->attribute != pair->attribute ) ||
	   ( filt->indirection != radFil.indirection ) ||
	   ( filt->forward != radFil.forward ) ) {
	    gen = &filt->u.generic;
	    gen->more = false;
	    fr_strerror_printf("filterBinary:  'more' for previous entry doesn't match: %s.\n",
		     valstr);
	}
    }
    prevRadPair = NULL;
    if( rc != -1 && tok == FILTER_GENERIC_TYPE ) {
	if( radFil.u.generic.more ) {
	    prevRadPair = pair;
	}
    }

    if( rc != -1 ) {
	    pairmemcpy(pair, &radFil, pair->length );
    }
    return(rc);

#endif
}
Esempio n. 12
0
/*
 * we got an EAP-Request/Sim/Challenge message in a legal state.
 *
 * use the RAND challenge to produce the SRES result, and then
 * use that to generate a new MAC.
 *
 * for the moment, we ignore the RANDs, then just plug in the SRES
 * values.
 *
 */
static int process_eap_challenge(RADIUS_PACKET *req,
				 RADIUS_PACKET *rep)
{
	VALUE_PAIR *newvp;
	VALUE_PAIR *mac, *randvp;
	VALUE_PAIR *sres1,*sres2,*sres3;
	VALUE_PAIR *Kc1, *Kc2, *Kc3;
	uint8_t calcmac[20];

	/* look for the AT_MAC and the challenge data */
	mac   = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0, TAG_ANY);
	randvp= pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_RAND, 0, TAG_ANY);
	if(!mac || !randvp) {
		fprintf(stderr, "radeapclient: challenge message needs to contain RAND and MAC\n");
		return 0;
	}

	/*
	 * compare RAND with randX, to verify this is the right response
	 * to this challenge.
	 */
	{
	  VALUE_PAIR *randcfgvp[3];
	  uint8_t const *randcfg[3];

	  randcfg[0] = &randvp->vp_octets[2];
	  randcfg[1] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE];
	  randcfg[2] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE*2];

	  randcfgvp[0] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND1, 0, TAG_ANY);
	  randcfgvp[1] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND2, 0, TAG_ANY);
	  randcfgvp[2] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND3, 0, TAG_ANY);

	  if(!randcfgvp[0] ||
	     !randcfgvp[1] ||
	     !randcfgvp[2]) {
	    fprintf(stderr, "radeapclient: needs to have rand1, 2 and 3 set.\n");
	    return 0;
	  }

	  if(memcmp(randcfg[0], randcfgvp[0]->vp_octets, EAPSIM_RAND_SIZE)!=0 ||
	     memcmp(randcfg[1], randcfgvp[1]->vp_octets, EAPSIM_RAND_SIZE)!=0 ||
	     memcmp(randcfg[2], randcfgvp[2]->vp_octets, EAPSIM_RAND_SIZE)!=0) {
	    int rnum,i,j;

	    fprintf(stderr, "radeapclient: one of rand 1,2,3 didn't match\n");
	    for(rnum = 0; rnum < 3; rnum++) {
	      fprintf(stderr, "received   rand %d: ", rnum);
	      j=0;
	      for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
		if(j==4) {
		  printf("_");
		  j=0;
		}
		j++;

		fprintf(stderr, "%02x", randcfg[rnum][i]);
	      }
	      fprintf(stderr, "\nconfigured rand %d: ", rnum);
	      j=0;
	      for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
		if(j==4) {
		  printf("_");
		  j=0;
		}
		j++;

		fprintf(stderr, "%02x", randcfgvp[rnum]->vp_octets[i]);
	      }
	      fprintf(stderr, "\n");
	    }
	    return 0;
	  }
	}

	/*
	 * now dig up the sres values from the response packet,
	 * which were put there when we read things in.
	 *
	 * Really, they should be calculated from the RAND!
	 *
	 */
	sres1 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES1, 0, TAG_ANY);
	sres2 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES2, 0, TAG_ANY);
	sres3 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES3, 0, TAG_ANY);

	if(!sres1 ||
	   !sres2 ||
	   !sres3) {
		fprintf(stderr, "radeapclient: needs to have sres1, 2 and 3 set.\n");
		return 0;
	}
	memcpy(eapsim_mk.sres[0], sres1->vp_strvalue, sizeof(eapsim_mk.sres[0]));
	memcpy(eapsim_mk.sres[1], sres2->vp_strvalue, sizeof(eapsim_mk.sres[1]));
	memcpy(eapsim_mk.sres[2], sres3->vp_strvalue, sizeof(eapsim_mk.sres[2]));

	Kc1 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC1, 0, TAG_ANY);
	Kc2 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC2, 0, TAG_ANY);
	Kc3 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC3, 0, TAG_ANY);

	if(!Kc1 ||
	   !Kc2 ||
	   !Kc3) {
		fprintf(stderr, "radeapclient: needs to have Kc1, 2 and 3 set.\n");
		return 0;
	}
	memcpy(eapsim_mk.Kc[0], Kc1->vp_strvalue, sizeof(eapsim_mk.Kc[0]));
	memcpy(eapsim_mk.Kc[1], Kc2->vp_strvalue, sizeof(eapsim_mk.Kc[1]));
	memcpy(eapsim_mk.Kc[2], Kc3->vp_strvalue, sizeof(eapsim_mk.Kc[2]));

	/* all set, calculate keys */
	eapsim_calculate_keys(&eapsim_mk);

	if(debug_flag) {
	  eapsim_dump_mk(&eapsim_mk);
	}

	/* verify the MAC, now that we have all the keys. */
	if(eapsim_checkmac(NULL, req->vps, eapsim_mk.K_aut,
			   eapsim_mk.nonce_mt, sizeof(eapsim_mk.nonce_mt),
			   calcmac)) {
		printf("MAC check succeed\n");
	} else {
		int i, j;
		j=0;
		printf("calculated MAC (");
		for (i = 0; i < 20; i++) {
			if(j==4) {
				printf("_");
				j=0;
			}
			j++;

			printf("%02x", calcmac[i]);
		}
		printf(" did not match\n");
		return 0;
	}

	/* form new response clear of any EAP stuff */
	cleanresp(rep);

	/* mark the subtype as being EAP-SIM/Response/Start */
	newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_SUBTYPE, 0);
	newvp->vp_integer = eapsim_challenge;
	pairreplace(&(rep->vps), newvp);

	{
		uint8_t *p;
		/*
		 * fill the SIM_MAC with a field that will in fact get appended
		 * to the packet before the MAC is calculated
		 */
		newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0);

		p = talloc_zero_array(newvp, uint8_t, EAPSIM_SRES_SIZE*3);
		memcpy(p+EAPSIM_SRES_SIZE * 0, sres1->vp_strvalue, EAPSIM_SRES_SIZE);
		memcpy(p+EAPSIM_SRES_SIZE * 1, sres2->vp_strvalue, EAPSIM_SRES_SIZE);
		memcpy(p+EAPSIM_SRES_SIZE * 2, sres3->vp_strvalue, EAPSIM_SRES_SIZE);
		pairmemsteal(newvp, p);

		pairreplace(&(rep->vps), newvp);
	}

	newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_KEY, 0);
	pairmemcpy(newvp, eapsim_mk.K_aut, EAPSIM_AUTH_SIZE);

	pairreplace(&(rep->vps), newvp);

	return 1;
}
Esempio n. 13
0
/*
 *	Copy data from src to dst, where the attributes are of
 *	different type.
 */
static int do_cast_copy(VALUE_PAIR *dst, VALUE_PAIR const *src)
{
	rad_assert(dst->da->type != src->da->type);

	if (dst->da->type == PW_TYPE_STRING) {
		dst->vp_strvalue = vp_aprint_value(dst, src, false);
		dst->length = strlen(dst->vp_strvalue);
		return 0;
	}

	if (dst->da->type == PW_TYPE_OCTETS) {
		if (src->da->type == PW_TYPE_STRING) {
			pairmemcpy(dst, src->vp_octets, src->length);	/* Copy embedded NULLs */
		} else {
			pairmemcpy(dst, (uint8_t const *) &src->data, src->length);
		}
		return 0;
	}

	if (src->da->type == PW_TYPE_STRING) {
		return pairparsevalue(dst, src->vp_strvalue, 0);
	}

	if ((src->da->type == PW_TYPE_INTEGER64) &&
	    (dst->da->type == PW_TYPE_ETHERNET)) {
		uint8_t array[8];
		uint64_t i;

		i = htonll(src->vp_integer64);
		memcpy(array, &i, 8);

		/*
		 *	For OUIs in the DB.
		 */
		if ((array[0] != 0) || (array[1] != 0)) return -1;

		memcpy(&dst->vp_ether, &array[2], 6);
		dst->length = 6;
		return 0;
	}

	/*
	 *	For integers, we allow the casting of a SMALL type to
	 *	a larger type, but not vice-versa.
	 */
	if (dst->da->type == PW_TYPE_INTEGER64) {
		switch (src->da->type) {
		case PW_TYPE_BYTE:
			dst->vp_integer64 = src->vp_byte;
			break;

		case PW_TYPE_SHORT:
			dst->vp_integer64 = src->vp_short;
			break;

		case PW_TYPE_INTEGER:
			dst->vp_integer64 = src->vp_integer;
			break;

		case PW_TYPE_OCTETS:
			goto do_octets;

		default:
			EVAL_DEBUG("Invalid cast to integer64");
			return -1;

		}
		return 0;
	}

	/*
	 *	We can compare LONG integers to SHORTER ones, so long
	 *	as the long one is on the LHS.
	 */
	if (dst->da->type == PW_TYPE_INTEGER) {
		switch (src->da->type) {
		case PW_TYPE_BYTE:
			dst->vp_integer = src->vp_byte;
			break;

		case PW_TYPE_SHORT:
			dst->vp_integer = src->vp_short;
			break;

		case PW_TYPE_OCTETS:
			goto do_octets;

		default:
			EVAL_DEBUG("Invalid cast to integer");
			return -1;

		}
		return 0;
	}

	if (dst->da->type == PW_TYPE_SHORT) {
		switch (src->da->type) {
		case PW_TYPE_BYTE:
			dst->vp_short = src->vp_byte;
			break;

		case PW_TYPE_OCTETS:
			goto do_octets;

		default:
			EVAL_DEBUG("Invalid cast to short");
			return -1;

		}
		return 0;
	}

	/*
	 *	The attribute we've found has to have a size which is
	 *	compatible with the type of the destination cast.
	 */
	if ((src->length < dict_attr_sizes[dst->da->type][0]) ||
	    (src->length > dict_attr_sizes[dst->da->type][1])) {
		EVAL_DEBUG("Casted attribute is wrong size (%u)", (unsigned int) src->length);
		return -1;
	}

	if (src->da->type == PW_TYPE_OCTETS) {
	do_octets:
		switch (dst->da->type) {
		case PW_TYPE_INTEGER64:
			dst->vp_integer = ntohll(*(uint64_t const *) src->vp_octets);
			break;

		case PW_TYPE_INTEGER:
		case PW_TYPE_DATE:
		case PW_TYPE_SIGNED:
			dst->vp_integer = ntohl(*(uint32_t const *) src->vp_octets);
			break;

		case PW_TYPE_SHORT:
			dst->vp_integer = ntohs(*(uint16_t const *) src->vp_octets);
			break;

		case PW_TYPE_BYTE:
			dst->vp_integer = src->vp_octets[0];
			break;

		default:
			memcpy(&dst->data, src->vp_octets, src->length);
			break;
		}

		dst->length = src->length;
		return 0;
	}

	/*
	 *	Convert host order to network byte order.
	 */
	if ((dst->da->type == PW_TYPE_IPV4_ADDR) &&
	    ((src->da->type == PW_TYPE_INTEGER) ||
	     (src->da->type == PW_TYPE_DATE) ||
	     (src->da->type == PW_TYPE_SIGNED))) {
		dst->vp_ipaddr = htonl(src->vp_integer);

	} else if ((src->da->type == PW_TYPE_IPV4_ADDR) &&
		   ((dst->da->type == PW_TYPE_INTEGER) ||
		    (dst->da->type == PW_TYPE_DATE) ||
		    (dst->da->type == PW_TYPE_SIGNED))) {
		dst->vp_integer = htonl(src->vp_ipaddr);

	} else {		/* they're of the same byte order */
		memcpy(&dst->data, &src->data, src->length);
	}

	dst->length = src->length;

	return 0;
}
Esempio n. 14
0
/*
 *	Add a handler to the set of active sessions.
 *
 *	Since we're adding it to the list, we guess that this means
 *	the packet needs a State attribute.  So add one.
 */
int eaplist_add(rlm_eap_t *inst, eap_handler_t *handler)
{
	int		status = 0;
	VALUE_PAIR	*state;
	REQUEST		*request = handler->request;

	rad_assert(handler != NULL);
	rad_assert(request != NULL);

	/*
	 *	Generate State, since we've been asked to add it to
	 *	the list.
	 */
	state = pairmake_reply("State", NULL, T_OP_EQ);
	if (!state) return 0;

	/*
	 *	The time at which this request was made was the time
	 *	at which it was received by the RADIUS server.
	 */
	handler->timestamp = request->timestamp;
	handler->status = 1;

	handler->src_ipaddr = request->packet->src_ipaddr;
	handler->eap_id = handler->eap_ds->request->id;

	/*
	 *	Playing with a data structure shared among threads
	 *	means that we need a lock, to avoid conflict.
	 */
	PTHREAD_MUTEX_LOCK(&(inst->session_mutex));

	/*
	 *	If we have a DoS attack, discard new sessions.
	 */
	if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) {
		status = -1;
		eaplist_expire(inst, request, handler->timestamp);
		goto done;
	}

	/*
	 *	Create a unique content for the State variable.
	 *	It will be modified slightly per round trip, but less so
	 *	than in 1.x.
	 */
	if (handler->trips == 0) {
		int i;

		for (i = 0; i < 4; i++) {
			uint32_t lvalue;

			lvalue = eap_rand(&inst->rand_pool);

			memcpy(handler->state + i * 4, &lvalue,
			       sizeof(lvalue));
		}
	}

	/*
	 *	Add some more data to distinguish the sessions.
	 */
	handler->state[4] = handler->trips ^ handler->state[0];
	handler->state[5] = handler->eap_id ^ handler->state[1];
	handler->state[6] = handler->type ^ handler->state[2];

	pairmemcpy(state, handler->state, sizeof(handler->state));

	/*
	 *	Big-time failure.
	 */
	status = rbtree_insert(inst->session_tree, handler);

	/*
	 *	Catch Access-Challenge without response.
	 */
	if (inst->handler_tree) {
		check_handler_t *check = rad_malloc(sizeof(*check));

		check->inst = inst;
		check->handler = handler;
		check->trips = handler->trips;
		request_data_add(request, inst, 0, check, check_handler);
	}

	if (status) {
		eap_handler_t *prev;

		prev = inst->session_tail;
		if (prev) {
			prev->next = handler;
			handler->prev = prev;
			handler->next = NULL;
			inst->session_tail = handler;
		} else {
			inst->session_head = inst->session_tail = handler;
			handler->next = handler->prev = NULL;
		}
	}

	/*
	 *	Now that we've finished mucking with the list,
	 *	unlock it.
	 */
 done:

	/*
	 *	We don't need this any more.
	 */
	if (status > 0) handler->request = NULL;

	PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));

	if (status <= 0) {
		pairfree(&state);

		if (status < 0) {
			static time_t last_logged = 0;

			if (last_logged < handler->timestamp) {
				last_logged = handler->timestamp;
				ERROR("rlm_eap (%s): Too many open sessions. Try increasing \"max_sessions\" "
				      "in the EAP module configuration", inst->xlat_name);
			}
		} else {
			ERROR("rlm_eap (%s): Failed to store handler", inst->xlat_name);
		}
		return 0;
	}

	RDEBUG("New EAP session, adding 'State' attribute to reply 0x%02x%02x%02x%02x%02x%02x%02x%02x",
	       state->vp_octets[0], state->vp_octets[1], state->vp_octets[2], state->vp_octets[3],
	       state->vp_octets[4], state->vp_octets[5], state->vp_octets[6], state->vp_octets[7]);

	return 1;
}
Esempio n. 15
0
static int eap_example_server_step(EAP_HANDLER *handler)
{
	int res, process = 0;
	REQUEST *request = handler->request;

	res = eap_server_sm_step(handler->server_ctx.eap);

	if (handler->server_ctx.eap_if->eapReq) {
		DEBUG("==> Request");
		process = 1;
		handler->server_ctx.eap_if->eapReq = 0;
	}

	if (handler->server_ctx.eap_if->eapSuccess) {
		DEBUG("==> Success");
		process = 1;
		res = 0;

		if (handler->server_ctx.eap_if->eapKeyAvailable) {
			int length = handler->server_ctx.eap_if->eapKeyDataLen;
			VALUE_PAIR *vp;

			if (length > 64) {
				length = 32;
			} else {
				length /= 2;
				/*
				 *	FIXME: Length is zero?
				 */
			}

			vp = pairmake_reply("MS-MPPE-Recv-Key", NULL, T_OP_EQ);
			if (vp) {
				pairmemcpy(vp,
					   handler->server_ctx.eap_if->eapKeyData,
					   length);
			}
			
			vp = pairmake_reply("MS-MPPE-Send-Key", NULL, T_OP_EQ);
			if (vp) {
				pairmemcpy(vp,
					   handler->server_ctx.eap_if->eapKeyData + length,
					   length);
			}
		}
	}

	if (handler->server_ctx.eap_if->eapFail) {
		DEBUG("==> Fail");
		process = 1;
	}

	if (process) {
		if (wpabuf_head(handler->server_ctx.eap_if->eapReqData)) {
			if (!eap_req2vp(handler)) return -1;
		} else {
			return -1;
		}
	}

	return res;
}
Esempio n. 16
0
/*
 * given a radius request with an EAP-Message body, decode it specific
 * attributes.
 */
static void unmap_eap_methods(RADIUS_PACKET *rep)
{
	VALUE_PAIR *eap1;
	eap_packet_raw_t *e;
	int len;
	int type;

	if (!rep) return;

	/* find eap message */
	e = eap_vp2packet(NULL, rep->vps);

	/* nothing to do! */
	if(!e) return;

	/* create EAP-ID and EAP-CODE attributes to start */
	eap1 = paircreate(rep, ATTRIBUTE_EAP_ID, 0);
	eap1->vp_integer = e->id;
	pairadd(&(rep->vps), eap1);

	eap1 = paircreate(rep, ATTRIBUTE_EAP_CODE, 0);
	eap1->vp_integer = e->code;
	pairadd(&(rep->vps), eap1);

	switch(e->code) {
	default:
	case PW_EAP_SUCCESS:
	case PW_EAP_FAILURE:
		/* no data */
		break;

	case PW_EAP_REQUEST:
	case PW_EAP_RESPONSE:
		/* there is a type field, which we use to create
		 * a new attribute */

		/* the length was decode already into the attribute
		 * length, and was checked already. Network byte
		 * order, just pull it out using math.
		 */
		len = e->length[0]*256 + e->length[1];

		/* verify the length is big enough to hold type */
		if(len < 5)
		{
			talloc_free(e);
			return;
		}

		type = e->data[0];

		type += ATTRIBUTE_EAP_BASE;
		len -= 5;

		if (len > MAX_STRING_LEN) {
			len = MAX_STRING_LEN;
		}

		eap1 = paircreate(rep, type, 0);
		pairmemcpy(eap1, e->data + 1, len);

		pairadd(&(rep->vps), eap1);
		break;
	}

	talloc_free(e);
	return;
}
Esempio n. 17
0
/*
 * we got an EAP-Request/Sim/Start message in a legal state.
 *
 * pick a supported version, put it into the reply, and insert a nonce.
 */
static int process_eap_start(RADIUS_PACKET *req,
			     RADIUS_PACKET *rep)
{
	VALUE_PAIR *vp, *newvp;
	VALUE_PAIR *anyidreq_vp, *fullauthidreq_vp, *permanentidreq_vp;
	uint16_t const *versions;
	uint16_t selectedversion;
	unsigned int i,versioncount;

	/* form new response clear of any EAP stuff */
	cleanresp(rep);

	if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_VERSION_LIST, 0, TAG_ANY)) == NULL) {
		fprintf(stderr, "illegal start message has no VERSION_LIST\n");
		return 0;
	}

	versions = (uint16_t const *) vp->vp_strvalue;

	/* verify that the attribute length is big enough for a length field */
	if(vp->length < 4)
	{
		fprintf(stderr, "start message has illegal VERSION_LIST. Too short: %u\n", (unsigned int) vp->length);
		return 0;
	}

	versioncount = ntohs(versions[0])/2;
	/* verify that the attribute length is big enough for the given number
	 * of versions present.
	 */
	if((unsigned)vp->length <= (versioncount*2 + 2))
	{
		fprintf(stderr, "start message is too short. Claimed %d versions does not fit in %u bytes\n", versioncount, (unsigned int) vp->length);
		return 0;
	}

	/*
	 * record the versionlist for the MK calculation.
	 */
	eapsim_mk.versionlistlen = versioncount*2;
	memcpy(eapsim_mk.versionlist, (unsigned char const *)(versions+1),
	       eapsim_mk.versionlistlen);

	/* walk the version list, and pick the one we support, which
	 * at present, is 1, EAP_SIM_VERSION.
	 */
	selectedversion=0;
	for(i=0; i < versioncount; i++)
	{
		if(ntohs(versions[i+1]) == EAP_SIM_VERSION)
		{
			selectedversion=EAP_SIM_VERSION;
			break;
		}
	}
	if(selectedversion == 0)
	{
		fprintf(stderr, "eap-sim start message. No compatible version found. We need %d\n", EAP_SIM_VERSION);
		for(i=0; i < versioncount; i++)
		{
			fprintf(stderr, "\tfound version %d\n",
				ntohs(versions[i+1]));
		}
	}

	/*
	 * now make sure that we have only FULLAUTH_ID_REQ.
	 * I think that it actually might not matter - we can answer in
	 * anyway we like, but it is illegal to have more than one
	 * present.
	 */
	anyidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_ANY_ID_REQ, 0, TAG_ANY);
	fullauthidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ, 0, TAG_ANY);
	permanentidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_PERMANENT_ID_REQ, 0, TAG_ANY);

	if(!fullauthidreq_vp ||
	   anyidreq_vp != NULL ||
	   permanentidreq_vp != NULL) {
		fprintf(stderr, "start message has %sanyidreq, %sfullauthid and %spermanentid. Illegal combination.\n",
			(anyidreq_vp != NULL ? "a " : "no "),
			(fullauthidreq_vp != NULL ? "a " : "no "),
			(permanentidreq_vp != NULL ? "a " : "no "));
		return 0;
	}

	/* okay, we have just any_id_req there, so fill in response */

	/* mark the subtype as being EAP-SIM/Response/Start */
	newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_SUBTYPE, 0);
	newvp->vp_integer = eapsim_start;
	pairreplace(&(rep->vps), newvp);

	/* insert selected version into response. */
	{
		uint16_t no_versions;

		no_versions = htons(selectedversion);

		newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE + PW_EAP_SIM_SELECTED_VERSION, 0);
		pairmemcpy(newvp, (uint8_t *) &no_versions, 2);
		pairreplace(&(rep->vps), newvp);

		/* record the selected version */
		memcpy(eapsim_mk.versionselect, &no_versions, 2);
	}

	vp = newvp = NULL;

	{
		uint32_t nonce[4];
		uint8_t *p;
		/*
		 * insert a nonce_mt that we make up.
		 */
		nonce[0]=fr_rand();
		nonce[1]=fr_rand();
		nonce[2]=fr_rand();
		nonce[3]=fr_rand();

		newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT, 0);

		p = talloc_zero_array(newvp, uint8_t, 18); /* 18 = 16 bytes of nonce + padding */
		memcpy(&p[2], nonce, 16);
		pairmemsteal(newvp, p);

		pairreplace(&(rep->vps), newvp);

		/* also keep a copy of the nonce! */
		memcpy(eapsim_mk.nonce_mt, nonce, 16);
	}

	{
		uint16_t idlen;
		uint8_t *p;
		uint16_t no_idlen;

		/*
		 * insert the identity here.
		 */
		vp = pairfind(rep->vps, PW_USER_NAME, 0, TAG_ANY);
		if(!vp)
		{
			fprintf(stderr, "eap-sim: We need to have a User-Name attribute!\n");
			return 0;
		}
		newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_IDENTITY, 0);

		idlen = strlen(vp->vp_strvalue);
		p = talloc_zero_array(newvp, uint8_t, idlen + 2);
		no_idlen = htons(idlen);
		memcpy(p, &no_idlen, 2);
		memcpy(p + 2, vp->vp_strvalue, idlen);
		pairmemsteal(newvp, p);

		pairreplace(&(rep->vps), newvp);

		/* record it */
		memcpy(eapsim_mk.identity, vp->vp_strvalue, idlen);
		eapsim_mk.identitylen = idlen;
	}

	return 1;
}
Esempio n. 18
0
/** Decrypt a Yubikey OTP AES block
 *
 * @param inst Module configuration.
 * @param passcode string to decrypt.
 * @return one of the RLM_RCODE_* constants.
 */
rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, char const *passcode)
{
	uint32_t counter;
	yubikey_token_st token;

	DICT_ATTR const *da;

	char private_id[(YUBIKEY_UID_SIZE * 2) + 1];
	VALUE_PAIR *key, *vp;

	da = dict_attrbyname("Yubikey-Key");
	if (!da) {
		REDEBUG("Dictionary missing entry for 'Yubikey-Key'");
		return RLM_MODULE_FAIL;
	}

	key = pairfind(request->config_items, da->attr, da->vendor, TAG_ANY);
	if (!key) {
		REDEBUG("Yubikey-Key attribute not found in control list, can't decrypt OTP data");
		return RLM_MODULE_INVALID;
	}

	if (key->length != YUBIKEY_KEY_SIZE) {
		REDEBUG("Yubikey-Key length incorrect, expected %u got %zu", YUBIKEY_KEY_SIZE, key->length);
		return RLM_MODULE_INVALID;
	}

	yubikey_parse((uint8_t const *) passcode + inst->id_len, key->vp_octets, &token);

	/*
	 *	Apparently this just uses byte offsets...
	 */
	if (!yubikey_crc_ok_p((uint8_t *) &token)) {
		REDEBUG("Decrypting OTP token data failed, rejecting");
		return RLM_MODULE_REJECT;
	}

	RDEBUG("Token data decrypted successfully");

	if (request->log.lvl && request->log.func) {
		(void) fr_bin2hex((char *) &private_id, (uint8_t*) &token.uid, YUBIKEY_UID_SIZE);
		RDEBUG2("Private ID	: 0x%s", private_id);
		RDEBUG2("Session counter   : %u", yubikey_counter(token.ctr));
		RDEBUG2("# used in session : %u", token.use);
		RDEBUG2("Token timestamp    : %u",
			(token.tstph << 16) | token.tstpl);
		RDEBUG2("Random data       : %u", token.rnd);
		RDEBUG2("CRC data          : 0x%x", token.crc);
	}

	/*
	 *	Private ID used for validation purposes
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Private-ID", NULL, T_OP_SET);
	if (!vp) {
		REDEBUG("Failed creating Yubikey-Private-ID");

		return RLM_MODULE_FAIL;
	}
	pairmemcpy(vp, token.uid, YUBIKEY_UID_SIZE);

	/*
	 *	Token timestamp
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Timestamp", NULL, T_OP_SET);
	if (!vp) {
		REDEBUG("Failed creating Yubikey-Timestamp");

		return RLM_MODULE_FAIL;
	}
	vp->vp_integer = (token.tstph << 16) | token.tstpl;
	vp->length = 4;

	/*
	 *	Token random
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Random", NULL, T_OP_SET);
	if (!vp) {
		REDEBUG("Failed creating Yubikey-Random");

		return RLM_MODULE_FAIL;
	}
	vp->vp_integer = token.rnd;
	vp->length = 4;

	/*
	 *	Combine the two counter fields together so we can do
	 *	replay attack checks.
	 */
	counter = (yubikey_counter(token.ctr) << 16) | token.use;

	vp = pairmake(request, &request->packet->vps, "Yubikey-Counter", NULL, T_OP_SET);
	if (!vp) {
		REDEBUG("Failed creating Yubikey-Counter");

		return RLM_MODULE_FAIL;
	}
	vp->vp_integer = counter;
	vp->length = 4;

	/*
	 *	Now we check for replay attacks
	 */
	vp = pairfind(request->config_items, vp->da->attr, vp->da->vendor, TAG_ANY);
	if (!vp) {
		RWDEBUG("Yubikey-Counter not found in control list, skipping replay attack checks");
		return RLM_MODULE_OK;
	}

	if (counter <= vp->vp_integer) {
		REDEBUG("Replay attack detected! Counter value %u, is lt or eq to last known counter value %u",
			counter, vp->vp_integer);
		return RLM_MODULE_REJECT;
	}

	return RLM_MODULE_OK;
}
/** Send the challenge itself
 *
 * Challenges will come from one of three places eventually:
 *
 * 1  from attributes like PW_EAP_SIM_RANDx
 *	    (these might be retrived from a database)
 *
 * 2  from internally implemented SIM authenticators
 *	    (a simple one based upon XOR will be provided)
 *
 * 3  from some kind of SS7 interface.
 *
 * For now, they only come from attributes.
 * It might be that the best way to do 2/3 will be with a different
 * module to generate/calculate things.
 *
 */
static int eap_sim_sendchallenge(eap_handler_t *handler)
{
	REQUEST *request = handler->request;
	eap_sim_state_t *ess;
	VALUE_PAIR **invps, **outvps, *newvp;
	RADIUS_PACKET *packet;
	uint8_t *p;

	ess = (eap_sim_state_t *)handler->opaque;
	rad_assert(handler->request != NULL);
	rad_assert(handler->request->reply);

	/*
	 *	Invps is the data from the client but this is for non-protocol data here.
	 *	We should already have consumed any client originated data.
	 */
	invps = &handler->request->packet->vps;

	/*
	 *	Outvps is the data to the client
	 */
	packet = handler->request->reply;
	outvps = &packet->vps;

	if (RDEBUG_ENABLED2) {
		RDEBUG2("EAP-SIM decoded packet:");
		debug_pair_list(*invps);
	}

	/*
	 *	Okay, we got the challenges! Put them into an attribute.
	 */
	newvp = paircreate(packet, PW_EAP_SIM_RAND, 0);
	newvp->length = 2 + (EAPSIM_RAND_SIZE * 3);
	newvp->vp_octets = p = talloc_array(newvp, uint8_t, newvp->length);

	memset(p, 0, 2); /* clear reserved bytes */
	p += 2;
	memcpy(p, ess->keys.rand[0], EAPSIM_RAND_SIZE);
	p += EAPSIM_RAND_SIZE;
	memcpy(p, ess->keys.rand[1], EAPSIM_RAND_SIZE);
	p += EAPSIM_RAND_SIZE;
	memcpy(p, ess->keys.rand[2], EAPSIM_RAND_SIZE);
	pairadd(outvps, newvp);

	/*
	 *	Set the EAP_ID - new value
	 */
	newvp = paircreate(packet, PW_EAP_ID, 0);
	newvp->vp_integer = ess->sim_id++;
	pairreplace(outvps, newvp);

	/*
	 *	Make a copy of the identity
	 */
	ess->keys.identitylen = strlen(handler->identity);
	memcpy(ess->keys.identity, handler->identity, ess->keys.identitylen);

	/*
	 *	Use the SIM identity, if available
	 */
	newvp = pairfind(*invps, PW_EAP_SIM_IDENTITY, 0, TAG_ANY);
	if (newvp && newvp->length > 2) {
		uint16_t len;

		memcpy(&len, newvp->vp_octets, sizeof(uint16_t));
		len = ntohs(len);
		if (len <= newvp->length - 2 && len <= MAX_STRING_LEN) {
			ess->keys.identitylen = len;
			memcpy(ess->keys.identity, newvp->vp_octets + 2, ess->keys.identitylen);
		}
	}

	/*
	 *	All set, calculate keys!
	 */
	eapsim_calculate_keys(&ess->keys);

#ifdef EAP_SIM_DEBUG_PRF
	eapsim_dump_mk(&ess->keys);
#endif

	/*
	 *	Need to include an AT_MAC attribute so that it will get
	 *	calculated. The NONCE_MT and the MAC are both 16 bytes, so
	 *	We store the NONCE_MT in the MAC for the encoder, which
	 *	will pull it out before it does the operation.
	 */
	newvp = paircreate(packet, PW_EAP_SIM_MAC, 0);
	pairmemcpy(newvp, ess->keys.nonce_mt, 16);
	pairreplace(outvps, newvp);

	newvp = paircreate(packet, PW_EAP_SIM_KEY, 0);
	pairmemcpy(newvp, ess->keys.K_aut, 16);
	pairreplace(outvps, newvp);

	/* the SUBTYPE, set to challenge. */
	newvp = paircreate(packet, PW_EAP_SIM_SUBTYPE, 0);
	newvp->vp_integer = EAPSIM_CHALLENGE;
	pairreplace(outvps, newvp);

	return 1;
}
Esempio n. 20
0
/**
 * @brief Parses the MS-SOH type/value (note: NOT type/length/value) data and
 * 	update the sohvp list
 *
 * See section 2.2.4 of MS-SOH. Because there's no "length" field we CANNOT just skip
 * unknown types; we need to know their length ahead of time. Therefore, we abort
 * if we find an unknown type. Note that sohvp may still have been modified in the
 * failure case.
 *
 * @param request Current request
 * @param p binary blob
 * @param data_len length of blob
 * @return 1 on success, 0 on failure
 */
static int eapsoh_mstlv(REQUEST *request, uint8_t const *p, unsigned int data_len)
{
	VALUE_PAIR *vp;
	uint8_t c;
	int t;
	char *q;

	while (data_len > 0) {
		c = *p++;
		data_len--;

		switch (c) {
		case 1:
			/* MS-Machine-Inventory-Packet
			 * MS-SOH section 2.2.4.1
			 */
			if (data_len < 18) {
				RDEBUG("insufficient data for MS-Machine-Inventory-Packet");
				return 0;
			}
			data_len -= 18;

			vp = pairmake_packet("SoH-MS-Machine-OS-vendor", "Microsoft", T_OP_EQ);
			if (!vp) return 0;

			vp = pairmake_packet("SoH-MS-Machine-OS-version", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_32(p); p+=4;

			vp = pairmake_packet("SoH-MS-Machine-OS-release", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_32(p); p+=4;

			vp = pairmake_packet("SoH-MS-Machine-OS-build", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_32(p); p+=4;

			vp = pairmake_packet("SoH-MS-Machine-SP-version", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_16(p); p+=2;

			vp = pairmake_packet("SoH-MS-Machine-SP-release", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_16(p); p+=2;

			vp = pairmake_packet("SoH-MS-Machine-Processor", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_16(p); p+=2;
			break;

		case 2:
			/* MS-Quarantine-State - FIXME: currently unhandled
			 * MS-SOH 2.2.4.1
			 *
			 * 1 byte reserved
			 * 1 byte flags
			 * 8 bytes NT Time field (100-nanosec since 1 Jan 1601)
			 * 2 byte urilen
			 * N bytes uri
			 */
			p += 10;
			t = soh_pull_be_16(p);	/* t == uri len */
			p += 2;
			p += t;
			data_len -= 12 + t;
			break;

		case 3:
			/* MS-Packet-Info
			 * MS-SOH 2.2.4.3
			 */
			RDEBUG3("SoH MS-Packet-Info %s vers=%i", *p & 0x10 ? "request" : "response", *p & 0xf);
			p++;
			data_len--;
			break;

		case 4:
			/* MS-SystemGenerated-Ids - FIXME: currently unhandled
			 * MS-SOH 2.2.4.4
			 *
			 * 2 byte length
			 * N bytes (3 bytes IANA enterprise# + 1 byte component id#)
			 */
			t = soh_pull_be_16(p);
			p += 2;
			p += t;
			data_len -= 2 + t;
			break;

		case 5:
			/* MS-MachineName
			 * MS-SOH 2.2.4.5
			 *
			 * 1 byte namelen
			 * N bytes name
			 */
			t = soh_pull_be_16(p);
			p += 2;

			vp = pairmake_packet("SoH-MS-Machine-Name", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_strvalue = q = talloc_array(vp, char, t);
			vp->type = VT_DATA;

			memcpy(q, p, t);
			q[t] = 0;

			p += t;
			data_len -= 2 + t;
			break;

		case 6:
			/* MS-CorrelationId
			 * MS-SOH 2.2.4.6
			 *
			 * 24 bytes opaque binary which we might, in future, have
			 * to echo back to the client in a final SoHR
			 */
			vp = pairmake_packet("SoH-MS-Correlation-Id", NULL, T_OP_EQ);
			if (!vp) return 0;

			pairmemcpy(vp, p, 24);
			p += 24;
			data_len -= 24;
			break;

		case 7:
			/* MS-Installed-Shvs - FIXME: currently unhandled
			 * MS-SOH 2.2.4.7
			 *
			 * 2 bytes length
			 * N bytes (3 bytes IANA enterprise# + 1 byte component id#)
			 */
			t = soh_pull_be_16(p);
			p += 2;
			p += t;
			data_len -= 2 + t;
			break;

		case 8:
			/* MS-Machine-Inventory-Ex
			 * MS-SOH 2.2.4.8
			 *
			 * 4 bytes reserved
			 * 1 byte product type (client=1 domain_controller=2 server=3)
			 */
			p += 4;
			vp = pairmake_packet("SoH-MS-Machine-Role", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = *p;
			p++;
			data_len -= 5;
			break;

		default:
			RDEBUG("SoH Unknown MS TV %i stopping", c);
			return 0;
		}
	}
	return 1;
}
/*
 *	Preprocess a request.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
{
	int r;
	rlm_preprocess_t *inst = instance;

	/*
	 *	Mangle the username, to get rid of stupid implementation
	 *	bugs.
	 */
	rad_mangle(inst, request);

	if (inst->with_ascend_hack) {
		/*
		 *	If we're using Ascend systems, hack the NAS-Port-Id
		 *	in place, to go from Ascend's weird values to something
		 *	approaching rationality.
		 */
		ascend_nasport_hack(pairfind(request->packet->vps, PW_NAS_PORT, 0, TAG_ANY),
				    inst->ascend_channels_per_line);
	}

	if (inst->with_cisco_vsa_hack) {
		/*
		 *	We need to run this hack because the h323-conf-id
		 *	attribute should be used.
		 */
		cisco_vsa_hack(request);
	}

	if (inst->with_alvarion_vsa_hack) {
		/*
		 *	We need to run this hack because the Alvarion
		 *	people are crazy.
		 */
		alvarion_vsa_hack(request->packet->vps);
	}

	if (inst->with_cablelabs_vsa_hack) {
		/*
		 *	We need to run this hack because the Cablelabs
		 *	people are crazy.
		 */
		cablelabs_vsa_hack(&request->packet->vps);
	}

	/*
	 *	Note that we add the Request-Src-IP-Address to the request
	 *	structure BEFORE checking huntgroup access.  This allows
	 *	the Request-Src-IP-Address to be used for huntgroup
	 *	comparisons.
	 */
	if (add_nas_attr(request) < 0) {
		return RLM_MODULE_FAIL;
	}

	hints_setup(inst->hints, request);

	/*
	 *      If there is a PW_CHAP_PASSWORD attribute but there
	 *      is PW_CHAP_CHALLENGE we need to add it so that other
	 *	modules can use it as a normal attribute.
	 */
	if (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) &&
	    pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) {
		VALUE_PAIR *vp;

		vp = radius_paircreate(request->packet, &request->packet->vps, PW_CHAP_CHALLENGE, 0);
		pairmemcpy(vp, request->packet->vector, AUTH_VECTOR_LEN);
	}

	if ((r = huntgroup_access(request, inst->huntgroups)) != RLM_MODULE_OK) {
		char buf[1024];
		RIDEBUG("No huntgroup access: [%s] (%s)",
			request->username ? request->username->vp_strvalue : "<NO User-Name>",
			auth_name(buf, sizeof(buf), request, 1));

		return r;
	}

	return RLM_MODULE_OK; /* Meaning: try next authorization module */
}
Esempio n. 22
0
/*
 *	Authenticate a previously sent challenge.
 */
static int mschapv2_authenticate(void *arg, eap_handler_t *handler)
{
	int rcode, ccode;
	uint8_t *p;
	mschapv2_opaque_t *data;
	EAP_DS *eap_ds = handler->eap_ds;
	VALUE_PAIR *challenge, *response, *name;
	rlm_eap_mschapv2_t *inst = (rlm_eap_mschapv2_t *) arg;
	REQUEST *request = handler->request;

	rad_assert(request != NULL);
	rad_assert(handler->stage == AUTHENTICATE);

	data = (mschapv2_opaque_t *) handler->opaque;

	/*
	 *	Sanity check the response.
	 */
	if (eap_ds->response->length <= 5) {
		REDEBUG("corrupted data");
		return 0;
	}

	ccode = eap_ds->response->type.data[0];

	switch (data->code) {
		case PW_EAP_MSCHAPV2_FAILURE:
			if (ccode == PW_EAP_MSCHAPV2_RESPONSE) {
				RDEBUG2("authentication re-try from client after we sent a failure");
				break;
			}

			/*
			 * if we sent error 648 (password expired) to the client
			 * we might get an MSCHAP-CPW packet here; turn it into a
			 * regular MS-CHAP2-CPW packet and pass it to rlm_mschap
			 * (or proxy it, I guess)
			 */
			if (ccode == PW_EAP_MSCHAPV2_CHGPASSWD) {
				VALUE_PAIR *cpw;
				int mschap_id = eap_ds->response->type.data[1];
				int copied=0,seq=1;

				RDEBUG2("password change packet received");

				challenge = pairmake_packet("MS-CHAP-Challenge", NULL, T_OP_EQ);
				if (!challenge) {
					return 0;
				}
				pairmemcpy(challenge, data->challenge, MSCHAPV2_CHALLENGE_LEN);

				cpw = pairmake_packet("MS-CHAP2-CPW", NULL, T_OP_EQ);
				cpw->length = 68;

				cpw->vp_octets = p = talloc_array(cpw, uint8_t, cpw->length);
				p[0] = 7;
				p[1] = mschap_id;
				memcpy(p + 2, eap_ds->response->type.data + 520, 66);

				/*
				 * break the encoded password into VPs (3 of them)
				 */
				while (copied < 516) {
					VALUE_PAIR *nt_enc;

					int to_copy = 516 - copied;
					if (to_copy > 243)
						to_copy = 243;

					nt_enc = pairmake_packet("MS-CHAP-NT-Enc-PW", NULL, T_OP_ADD);
					nt_enc->length = 4 + to_copy;

					nt_enc->vp_octets = p = talloc_array(nt_enc, uint8_t, nt_enc->length);

					p[0] = 6;
					p[1] = mschap_id;
					p[2] = 0;
					p[3] = seq++;

					memcpy(p + 4, eap_ds->response->type.data + 4 + copied, to_copy);
					copied += to_copy;
				}

				RDEBUG2("built change password packet");
				debug_pair_list(request->packet->vps);

				/*
				 * jump to "authentication"
				 */
				goto packet_ready;
			}

			/*
			 * we sent a failure and are expecting a failure back
			 */
			if (ccode != PW_EAP_MSCHAPV2_FAILURE) {
				REDEBUG("Sent FAILURE expecting FAILURE but got %d", ccode);
				return 0;
			}

	failure:
			request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
			eap_ds->request->code = PW_EAP_FAILURE;
			return 1;

		case PW_EAP_MSCHAPV2_SUCCESS:
			/*
			 * we sent a success to the client; some clients send a
			 * success back as-per the RFC, some send an ACK. Permit
			 * both, I guess...
			 */

			switch (ccode) {
				case PW_EAP_MSCHAPV2_SUCCESS:
					eap_ds->request->code = PW_EAP_SUCCESS;

					pairfilter(request->reply,
						  &request->reply->vps,
						  &data->mppe_keys, 0, 0, TAG_ANY);
					/* fall through... */

				case PW_EAP_MSCHAPV2_ACK:
#ifdef WITH_PROXY
					/*
					 *	It's a success.  Don't proxy it.
					 */
					request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
#endif
					pairfilter(request->reply,
						  &request->reply->vps,
						  &data->reply, 0, 0, TAG_ANY);
					return 1;
			}
			REDEBUG("Sent SUCCESS expecting SUCCESS (or ACK) but got %d", ccode);
			return 0;

		case PW_EAP_MSCHAPV2_CHALLENGE:
			if (ccode == PW_EAP_MSCHAPV2_FAILURE) goto failure;

			/*
			 * we sent a challenge, expecting a response
			 */
			if (ccode != PW_EAP_MSCHAPV2_RESPONSE) {
				REDEBUG("Sent CHALLENGE expecting RESPONSE but got %d", ccode);
				return 0;
			}
			/* authentication happens below */
			break;


		default:
			/* should never happen */
			REDEBUG("unknown state %d", data->code);
			return 0;
	}


	/*
	 *	Ensure that we have at least enough data
	 *	to do the following checks.
	 *
	 *	EAP header (4), EAP type, MS-CHAP opcode,
	 *	MS-CHAP ident, MS-CHAP data length (2),
	 *	MS-CHAP value length.
	 */
	if (eap_ds->response->length < (4 + 1 + 1 + 1 + 2 + 1)) {
		REDEBUG("Response is too short");
		return 0;
	}

	/*
	 *	The 'value_size' is the size of the response,
	 *	which is supposed to be the response (48
	 *	bytes) plus 1 byte of flags at the end.
	 */
	if (eap_ds->response->type.data[4] != 49) {
		REDEBUG("Response is of incorrect length %d", eap_ds->response->type.data[4]);
		return 0;
	}

	/*
	 *	The MS-Length field is 5 + value_size + length
	 *	of name, which is put after the response.
	 */
	if (((eap_ds->response->type.data[2] << 8) |
	     eap_ds->response->type.data[3]) < (5 + 49)) {
		REDEBUG("Response contains contradictory length %d %d",
			(eap_ds->response->type.data[2] << 8) |
		       eap_ds->response->type.data[3], 5 + 49);
		return 0;
	}

	/*
	 *	We now know that the user has sent us a response
	 *	to the challenge.  Let's try to authenticate it.
	 *
	 *	We do this by taking the challenge from 'data',
	 *	the response from the EAP packet, and creating VALUE_PAIR's
	 *	to pass to the 'mschap' module.  This is a little wonky,
	 *	but it works.
	 */
	challenge = pairmake_packet("MS-CHAP-Challenge", NULL, T_OP_EQ);
	if (!challenge) {
		return 0;
	}
	pairmemcpy(challenge, data->challenge, MSCHAPV2_CHALLENGE_LEN);

	response = pairmake_packet("MS-CHAP2-Response", NULL, T_OP_EQ);
	if (!response) {
		return 0;
	}

	response->length = MSCHAPV2_RESPONSE_LEN;
	response->vp_octets = p = talloc_array(response, uint8_t, response->length);

	p[0] = eap_ds->response->type.data[1];
	p[1] = eap_ds->response->type.data[5 + MSCHAPV2_RESPONSE_LEN];
	memcpy(p + 2, &eap_ds->response->type.data[5],
	       MSCHAPV2_RESPONSE_LEN - 2);

	name = pairmake_packet("MS-CHAP-User-Name", NULL, T_OP_EQ);
	if (!name) {
		return 0;
	}
	
	/*
	 *	MS-Length - MS-Value - 5.
	 */
	name->length = (((eap_ds->response->type.data[2] << 8) |
			 eap_ds->response->type.data[3]) -
			eap_ds->response->type.data[4] - 5);
	name->vp_octets = p = talloc_array(name, uint8_t, name->length + 1);
	memcpy(p,
	       &eap_ds->response->type.data[4 + MSCHAPV2_RESPONSE_LEN],
	       name->length);
	p[name->length] = '\0';

packet_ready:

#ifdef WITH_PROXY
	/*
	 *	If this options is set, then we do NOT authenticate the
	 *	user here.  Instead, now that we've added the MS-CHAP
	 *	attributes to the request, we STOP, and let the outer
	 *	tunnel code handle it.
	 *
	 *	This means that the outer tunnel code will DELETE the
	 *	EAP attributes, and proxy the MS-CHAP attributes to a
	 *	home server.
	 */
	if (request->options & RAD_REQUEST_OPTION_PROXY_EAP) {
		char *username = NULL;
		eap_tunnel_data_t *tunnel;

		RDEBUG2("cancelling authentication and letting it be proxied");

		/*
		 *	Set up the callbacks for the tunnel
		 */
		tunnel = talloc_zero(request, eap_tunnel_data_t);

		tunnel->tls_session = arg;
		tunnel->callback = mschap_postproxy;

		/*
		 *	Associate the callback with the request.
		 */
		rcode = request_data_add(request,
					 request->proxy,
					 REQUEST_DATA_EAP_TUNNEL_CALLBACK,
					 tunnel, NULL);
		rad_assert(rcode == 0);

		/*
		 *	The State attribute is NOT supposed to
		 *	go into the proxied packet, it will confuse
		 *	other RADIUS servers, and they will discard
		 *	the request.
		 *
		 *	The PEAP module will take care of adding
		 *	the State attribute back, before passing
		 *	the handler & request back into the tunnel.
		 */
		pairdelete(&request->packet->vps, PW_STATE, 0, TAG_ANY);

		/*
		 *	Fix the User-Name when proxying, to strip off
		 *	the NT Domain, if we're told to, and a User-Name
		 *	exists, and there's a \\, meaning an NT-Domain
		 *	in the user name, THEN discard the user name.
		 */
		if (inst->with_ntdomain_hack &&
		    ((challenge = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY)) != NULL) &&
		    ((username = strchr(challenge->vp_strvalue, '\\')) != NULL)) {
			/*
			 *	Wipe out the NT domain.
			 *
			 *	FIXME: Put it into MS-CHAP-Domain?
			 */
			username++; /* skip the \\ */
			pairstrcpy(challenge, username);
		}

		/*
		 *	Remember that in the post-proxy stage, we've got
		 *	to do the work below, AFTER the call to MS-CHAP
		 *	authentication...
		 */
		return 1;
	}
#endif

	/*
	 *	This is a wild & crazy hack.
	 */
	rcode = process_authenticate(PW_AUTHTYPE_MS_CHAP, request);

	/*
	 *	Delete MPPE keys & encryption policy.  We don't
	 *	want these here.
	 */
	fix_mppe_keys(handler, data);

	/*
	 *	Take the response from the mschap module, and
	 *	return success or failure, depending on the result.
	 */
	response = NULL;
	if (rcode == RLM_MODULE_OK) {
		pairfilter(data, &response, &request->reply->vps,
			 PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
		data->code = PW_EAP_MSCHAPV2_SUCCESS;

	} else if (inst->send_error) {
		pairfilter(data, &response, &request->reply->vps,
			  PW_MSCHAP_ERROR, VENDORPEC_MICROSOFT, TAG_ANY);
		if (response) {
			int n,err,retry;
			char buf[34];

			RDEBUG2("MSCHAP-Error: %s", response->vp_strvalue);

			/*
			 *	Pxarse the new challenge out of the
			 *	MS-CHAP-Error, so that if the client
			 *	issues a re-try, we will know which
			 *	challenge value that they used.
			 */
			n = sscanf(response->vp_strvalue, "%*cE=%d R=%d C=%32s", &err, &retry, &buf[0]);
			if (n == 3) {
				DEBUG2("Found new challenge from MS-CHAP-Error: err=%d retry=%d challenge=%s", err, retry, buf);
				fr_hex2bin(buf, data->challenge, 16);
			} else {
				DEBUG2("Could not parse new challenge from MS-CHAP-Error: %d", n);
			}
		}
		data->code = PW_EAP_MSCHAPV2_FAILURE;
	} else {
		eap_ds->request->code = PW_EAP_FAILURE;
		return 1;
	}

	/*
	 *	No response, die.
	 */
	if (!response) {
		REDEBUG("No MS-CHAP-Success or MS-CHAP-Error was found.");
		return 0;
	}

	/*
	 *	Compose the response (whatever it is),
	 *	and return it to the over-lying EAP module.
	 */
	eapmschapv2_compose(handler, response);
	pairfree(&response);

	return 1;
}