Ejemplo n.º 1
0
Archivo: auth.c Proyecto: ebichu/dd-wrt
/*
 *	Check password.
 *
 *	Returns:	0  OK
 *			-1 Password fail
 *			-2 Rejected (Auth-Type = Reject, send Port-Message back)
 *			1  End check & return, don't reply
 *
 *	NOTE: NOT the same as the RLM_ values !
 */
static int rad_check_password(REQUEST *request)
{
	VALUE_PAIR *auth_type_pair;
	VALUE_PAIR *cur_config_item;
	VALUE_PAIR *password_pair;
	VALUE_PAIR *auth_item;
	uint8_t my_chap[MAX_STRING_LEN];
	int auth_type = -1;
	int result;
	int auth_type_count = 0;
	result = 0;

	/*
	 *	Look for matching check items. We skip the whole lot
	 *	if the authentication type is PW_AUTHTYPE_ACCEPT or
	 *	PW_AUTHTYPE_REJECT.
	 */
	cur_config_item = request->config_items;
	while(((auth_type_pair = pairfind(cur_config_item, PW_AUTH_TYPE))) != NULL) {

		auth_type = auth_type_pair->vp_integer;
		auth_type_count++;

		RDEBUG2("Found Auth-Type = %s",
			dict_valnamebyattr(PW_AUTH_TYPE,
					   auth_type_pair->vp_integer));
		cur_config_item = auth_type_pair->next;

		if (auth_type == PW_AUTHTYPE_REJECT) {
			RDEBUG2("Auth-Type = Reject, rejecting user");
			return -2;
		}
	}

	if (( auth_type_count > 1) && (debug_flag)) {
		radlog_request(L_ERR, 0, request, "Warning:  Found %d auth-types on request for user '%s'",
			auth_type_count, request->username->vp_strvalue);
	}

	/*
	 *	This means we have a proxy reply or an accept
	 *  and it wasn't rejected in the above loop.  So
	 *  that means it is accepted and we do no further
	 *  authentication
	 */
	if ((auth_type == PW_AUTHTYPE_ACCEPT)
#ifdef WITH_PROXY
	    || (request->proxy)
#endif
	    ) {
		RDEBUG2("Auth-Type = Accept, accepting the user");
		return 0;
	}

	password_pair =  pairfind(request->config_items, PW_USER_PASSWORD);
	if (password_pair &&
	    pairfind(request->config_items, PW_CLEARTEXT_PASSWORD)) {
		pairdelete(&request->config_items, PW_USER_PASSWORD);
		password_pair = NULL;
	}

	if (password_pair) {
		DICT_ATTR *da;

		RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
		RDEBUG("!!!    Replacing User-Password in config items with Cleartext-Password.     !!!");
		RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
		RDEBUG("!!! Please update your configuration so that the \"known good\"               !!!");
		RDEBUG("!!! clear text password is in Cleartext-Password, and not in User-Password. !!!");
		RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
		password_pair->attribute = PW_CLEARTEXT_PASSWORD;
		da = dict_attrbyvalue(PW_CLEARTEXT_PASSWORD);
		if (!da) {
			radlog_request(L_ERR, 0, request, "FATAL: You broke the dictionaries.  Please use the default dictionaries!");
			_exit(1);
		}

		password_pair->name = da->name;
	}

	/*
	 *	Find the "known good" password.
	 *
	 *	FIXME: We should get rid of these hacks, and replace
	 *	them with a module.
	 */
	if ((password_pair = pairfind(request->config_items, PW_CRYPT_PASSWORD)) != NULL) {
		/*
		 *	Re-write Auth-Type, but ONLY if it isn't already
		 *	set.
		 */
		if (auth_type == -1) auth_type = PW_AUTHTYPE_CRYPT;
	} else {
		password_pair = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD);
	}

	if (auth_type < 0) {
		if (password_pair) {
			auth_type = PW_AUTHTYPE_LOCAL;
		} else {
			/*
		 	*	The admin hasn't told us how to
		 	*	authenticate the user, so we reject them!
		 	*
		 	*	This is fail-safe.
		 	*/
			RDEBUG2("ERROR: No authenticate method (Auth-Type) found for the request: Rejecting the user");
			return -2;
		}
	}

	switch(auth_type) {
		case PW_AUTHTYPE_CRYPT:
			RDEBUG2("WARNING: Please update your configuration, and remove 'Auth-Type = Crypt'");
			RDEBUG2("WARNING: Use the PAP module instead.");

			/*
			 *	Find the password sent by the user. It
			 *	SHOULD be there, if it's not
			 *	authentication fails.
			 */
			auth_item = request->password;
			if (auth_item == NULL) {
				RDEBUG2("No User-Password or CHAP-Password attribute in the request");
				return -1;
			}

			if (password_pair == NULL) {
				RDEBUG2("No Crypt-Password configured for the user");
				rad_authlog("Login incorrect "
					"(No Crypt-Password configured for the user)", request, 0);
				return -1;
			}

			switch (fr_crypt_check((char *)auth_item->vp_strvalue,
									 (char *)password_pair->vp_strvalue)) {
			case -1:
			  rad_authlog("Login incorrect "
						  "(system failed to supply an encrypted password for comparison)", request, 0);
			  /* FALL-THROUGH */
			case 1:
			  return -1;
			}
			break;
		case PW_AUTHTYPE_LOCAL:
			RDEBUG2("WARNING: Please update your configuration, and remove 'Auth-Type = Local'");
			RDEBUG2("WARNING: Use the PAP or CHAP modules instead.");

			/*
			 *	Find the password sent by the user. It
			 *	SHOULD be there, if it's not
			 *	authentication fails.
			 */
			auth_item = request->password;
			if (!auth_item)
				auth_item = pairfind(request->packet->vps,
						     PW_CHAP_PASSWORD);
			if (!auth_item) {
				RDEBUG2("No User-Password or CHAP-Password attribute in the request.");
				RDEBUG2("Cannot perform authentication.");
				return -1;
			}

			/*
			 *	Plain text password.
			 */
			if (password_pair == NULL) {
				RDEBUG2("No \"known good\" password was configured for the user.");
				RDEBUG2("As a result, we cannot authenticate the user.");
				rad_authlog("Login incorrect "
					"(No password configured for the user)", request, 0);
				return -1;
			}

			/*
			 *	Local password is just plain text.
	 		 */
			if (auth_item->attribute == PW_USER_PASSWORD) {
				if (strcmp((char *)password_pair->vp_strvalue,
					   (char *)auth_item->vp_strvalue) != 0) {
					RDEBUG2("User-Password in the request does NOT match \"known good\" password.");
					return -1;
				}
				RDEBUG2("User-Password in the request is correct.");
				break;

			} else if (auth_item->attribute != PW_CHAP_PASSWORD) {
				RDEBUG2("The user did not supply a User-Password or a CHAP-Password attribute");
				rad_authlog("Login incorrect "
					"(no User-Password or CHAP-Password attribute)", request, 0);
				return -1;
			}

			rad_chap_encode(request->packet, my_chap,
					auth_item->vp_octets[0], password_pair);

			/*
			 *	Compare them
			 */
			if (memcmp(my_chap + 1, auth_item->vp_strvalue + 1,
				   CHAP_VALUE_LENGTH) != 0) {
				RDEBUG2("CHAP-Password is incorrect.");
				return -1;
			}
			RDEBUG2("CHAP-Password is correct.");
			break;
		default:
			/*
			 *	See if there is a module that handles
			 *	this type, and turn the RLM_ return
			 *	status into the values as defined at
			 *	the top of this function.
			 */
			result = module_authenticate(auth_type, request);
			switch (result) {
				/*
				 *	An authentication module FAIL
				 *	return code, or any return code that
				 *	is not expected from authentication,
				 *	is the same as an explicit REJECT!
				 */
				case RLM_MODULE_FAIL:
				case RLM_MODULE_INVALID:
				case RLM_MODULE_NOOP:
				case RLM_MODULE_NOTFOUND:
				case RLM_MODULE_REJECT:
				case RLM_MODULE_UPDATED:
				case RLM_MODULE_USERLOCK:
				default:
					result = -1;
					break;
				case RLM_MODULE_OK:
					result = 0;
					break;
				case RLM_MODULE_HANDLED:
					result = 1;
					break;
			}
			break;
	}

	return result;
}
Ejemplo n.º 2
0
/*
 *	Check password.
 *
 *	Returns:	0  OK
 *			-1 Password fail
 *			-2 Rejected (Auth-Type = Reject, send Port-Message back)
 *			1  End check & return, don't reply
 *
 *	NOTE: NOT the same as the RLM_ values !
 */
static int rad_check_password(REQUEST *request)
{
	VALUE_PAIR *auth_type_pair;
	VALUE_PAIR *cur_config_item;
	int auth_type = -1;
	int result;
	int auth_type_count = 0;
	result = 0;

	/*
	 *	Look for matching check items. We skip the whole lot
	 *	if the authentication type is PW_AUTHTYPE_ACCEPT or
	 *	PW_AUTHTYPE_REJECT.
	 */
	cur_config_item = request->config_items;
	while(((auth_type_pair = pairfind(cur_config_item, PW_AUTH_TYPE, 0, TAG_ANY))) != NULL) {
		auth_type = auth_type_pair->vp_integer;
		auth_type_count++;

		RDEBUG2("Found Auth-Type = %s",
			dict_valnamebyattr(PW_AUTH_TYPE, 0, auth_type));
		cur_config_item = auth_type_pair->next;

		if (auth_type == PW_AUTHTYPE_REJECT) {
			RDEBUG2("Auth-Type = Reject, rejecting user");
			return -2;
		}
	}

	/*
	 *	Warn if more than one Auth-Type was found, because only the last
	 *	one found will actually be used.
	 */
	if (( auth_type_count > 1) && (debug_flag)) {
		radlog_request(L_ERR, 0, request, "Warning:  Found %d auth-types on request for user '%s'",
			auth_type_count, request->username->vp_strvalue);
	}

	/*
	 *	This means we have a proxy reply or an accept and it wasn't
	 *	rejected in the above loop. So that means it is accepted and we
	 *	do no further authentication.
	 */
	if ((auth_type == PW_AUTHTYPE_ACCEPT)
#ifdef WITH_PROXY
	    || (request->proxy)
#endif
	    ) {
		RDEBUG2("Auth-Type = Accept, accepting the user");
		return 0;
	}

	/*
	 *	Check that Auth-Type has been set, and reject if not.
	 *
	 *	Do quick checks to see if Cleartext-Password or Crypt-Password have
	 *	been set, and complain if so.
	 */
	if (auth_type < 0) {
		if (pairfind(request->config_items, PW_CRYPT_PASSWORD, 0, TAG_ANY) != NULL) {
			RDEBUG2W("Please update your configuration, and remove 'Auth-Type = Crypt'");
			RDEBUG2W("Use the PAP module instead.");
		}
		else if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) != NULL) {
			RDEBUG2W("Please update your configuration, and remove 'Auth-Type = Local'");
			RDEBUG2W("Use the PAP or CHAP modules instead.");
		}

		/*
	 	 *	The admin hasn't told us how to
	 	 *	authenticate the user, so we reject them!
	 	 *
	 	 *	This is fail-safe.
	 	 */

		RDEBUG2E("No Auth-Type found: rejecting the user via Post-Auth-Type = Reject");
		return -2;
	}

	/*
	 *	See if there is a module that handles
	 *	this Auth-Type, and turn the RLM_ return
	 *	status into the values as defined at
	 *	the top of this function.
	 */
	result = module_authenticate(auth_type, request);
	switch (result) {
		/*
		 *	An authentication module FAIL
		 *	return code, or any return code that
		 *	is not expected from authentication,
		 *	is the same as an explicit REJECT!
		 */
		case RLM_MODULE_FAIL:
		case RLM_MODULE_INVALID:
		case RLM_MODULE_NOOP:
		case RLM_MODULE_NOTFOUND:
		case RLM_MODULE_REJECT:
		case RLM_MODULE_UPDATED:
		case RLM_MODULE_USERLOCK:
		default:
			result = -1;
			break;
		case RLM_MODULE_OK:
			result = 0;
			break;
		case RLM_MODULE_HANDLED:
			result = 1;
			break;
	}

	return result;
}
Ejemplo n.º 3
0
/*
 *	Authenticate a previously sent challenge.
 */
static int gtc_authenticate(void *type_data, EAP_HANDLER *handler)
{
	VALUE_PAIR *vp;
	EAP_DS *eap_ds = handler->eap_ds;
	rlm_eap_gtc_t *inst = (rlm_eap_gtc_t *) type_data;
	REQUEST *request = handler->request;

	/*
	 *	Get the Cleartext-Password for this user.
	 */
	rad_assert(handler->request != NULL);
	rad_assert(handler->stage == AUTHENTICATE);

	/*
	 *	Sanity check the response.  We need at least one byte
	 *	of data.
	 */
	if (eap_ds->response->length <= 4) {
		radlog(L_ERR, "rlm_eap_gtc: corrupted data");
		eap_ds->request->code = PW_EAP_FAILURE;
		return 0;
	}

#if 0
	if ((debug_flag > 2) && fr_log_fp) {
		int i;

		for (i = 0; i < eap_ds->response->length - 4; i++) {
			if ((i & 0x0f) == 0) fprintf(fr_log_fp, "%d: ", i);

			fprintf(fr_log_fp, "%02x ", eap_ds->response->type.data[i]);

			if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n");
		}
	}
#endif

	/*
	 *	Handle passwords here.
	 */
	if (inst->auth_type == PW_AUTHTYPE_LOCAL) {
		/*
		 *	For now, do clear-text password authentication.
		 */
		vp = pairfind(handler->request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
		if (!vp) {
			RDEBUG2E("Cleartext-Password is required for authentication.");
			eap_ds->request->code = PW_EAP_FAILURE;
			return 0;
		}

		if (eap_ds->response->type.length != vp->length) {
			RDEBUG2E("Passwords are of different length. %u %u", (unsigned) eap_ds->response->type.length, (unsigned) vp->length);
			eap_ds->request->code = PW_EAP_FAILURE;
			return 0;
		}

		if (memcmp(eap_ds->response->type.data,
			   vp->vp_strvalue, vp->length) != 0) {
			RDEBUG2E("Passwords are different");
			eap_ds->request->code = PW_EAP_FAILURE;
			return 0;
		}

		/*
		 *	EAP packets can be ~64k long maximum, and
		 *	we don't like that.
		 */
	} else if (eap_ds->response->type.length <= 128) {
		int rcode;

		/*
		 *	If there was a User-Password in the request,
		 *	why the heck are they using EAP-GTC?
		 */
		pairdelete(&handler->request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);

		vp = pairmake("User-Password", "", T_OP_EQ);
		if (!vp) {
			radlog(L_ERR, "rlm_eap_gtc: out of memory");
			return 0;
		}
		vp->length = eap_ds->response->type.length;
		memcpy(vp->vp_strvalue, eap_ds->response->type.data, vp->length);
		vp->vp_strvalue[vp->length] = 0;

		/*
		 *	Add the password to the request, and allow
		 *	another module to do the work of authenticating it.
		 */
		pairadd(&handler->request->packet->vps, vp);
		handler->request->password = vp;

		/*
		 *	This is a wild & crazy hack.
		 */
		rcode = module_authenticate(inst->auth_type, handler->request);
		if (rcode != RLM_MODULE_OK) {
			eap_ds->request->code = PW_EAP_FAILURE;
			return 0;
		}

	} else {
		radlog(L_ERR, "rlm_eap_gtc: Response is too large to understand");
		eap_ds->request->code = PW_EAP_FAILURE;
		return 0;

	}

	eap_ds->request->code = PW_EAP_SUCCESS;

	return 1;
}
/*
 *	Authenticate a previously sent challenge.
 */
static int mschapv2_authenticate(void *arg, EAP_HANDLER *handler)
{
	int rcode, ccode;
	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;

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

	data = (mschapv2_opaque_t *) handler->opaque;

	/*
	 *	Sanity check the response.
	 */
	if (eap_ds->response->length <= 5) {
		radlog(L_ERR, "rlm_eap_mschapv2: 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) {
				DEBUG2("  rlm_eap_mschapv2: 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;

				DEBUG2("  rlm_eap_mschapv2: password change packet received");

				challenge = pairmake("MS-CHAP-Challenge", "0x00", T_OP_EQ);
				if (!challenge) {
					radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
					return 0;
				}
				challenge->length = MSCHAPV2_CHALLENGE_LEN;
				memcpy(challenge->vp_strvalue, data->challenge, MSCHAPV2_CHALLENGE_LEN);
				pairadd(&handler->request->packet->vps, challenge);

				cpw = pairmake("MS-CHAP2-CPW", "", T_OP_EQ);
				cpw->vp_octets[0] = 7;
				cpw->vp_octets[1] = mschap_id;
				memcpy(cpw->vp_octets+2, eap_ds->response->type.data + 520, 66);
				cpw->length = 68;
				pairadd(&handler->request->packet->vps, cpw);

				/*
				 * 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("MS-CHAP-NT-Enc-PW", "", T_OP_ADD);
					nt_enc->vp_octets[0] = 6;
					nt_enc->vp_octets[1] = mschap_id;
					nt_enc->vp_octets[2] = 0;
					nt_enc->vp_octets[3] = seq++;

					memcpy(nt_enc->vp_octets + 4, eap_ds->response->type.data + 4 + copied, to_copy);
					copied += to_copy;
					nt_enc->length = 4 + to_copy;
					pairadd(&handler->request->packet->vps, nt_enc);
				}

				DEBUG2("  rlm_eap_mschapv2: built change password packet");
				debug_pair_list(handler->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) {
				radlog(L_ERR, "rlm_eap_mschapv2: Sent FAILURE expecting FAILURE but got %d", ccode);
				return 0;
			}

	failure:
			handler->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;
					pairadd(&handler->request->reply->vps, data->mppe_keys);
					data->mppe_keys = NULL;
					/* fall through... */

				case PW_EAP_MSCHAPV2_ACK:
#ifdef WITH_PROXY
					/*
					 *	It's a success.  Don't proxy it.
					 */
					handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
#endif
					pairadd(&handler->request->reply->vps, data->reply);
					data->reply = NULL;
					return 1;
			}
			radlog(L_ERR, "rlm_eap_mschapv2: 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) {
				radlog(L_ERR, "rlm_eap_mschapv2: Sent CHALLENGE expecting RESPONSE but got %d", ccode);
				return 0;
			}
			/* authentication happens below */
			break;


		default:
			/* should never happen */
			radlog(L_ERR, "rlm_eap_mschapv2: 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)) {
		radlog(L_ERR, "rlm_eap_mschapv2: 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) {
		radlog(L_ERR, "rlm_eap_mschapv2: 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)) {
		radlog(L_ERR, "rlm_eap_mschapv2: 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("MS-CHAP-Challenge", "0x00", T_OP_EQ);
	if (!challenge) {
		radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
		return 0;
	}
	challenge->length = MSCHAPV2_CHALLENGE_LEN;
	memcpy(challenge->vp_strvalue, data->challenge, MSCHAPV2_CHALLENGE_LEN);

	response = pairmake("MS-CHAP2-Response", "0x00", T_OP_EQ);
	if (!response) {
		pairfree(&challenge);
		radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
		return 0;
	}

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

	name = pairmake("NTLM-User-Name", "", T_OP_EQ);
	if (!name) {
		pairfree(&challenge);
		pairfree(&response);
		radlog(L_ERR, "rlm_eap_mschapv2: Failed creating NTLM-User-Name: %s", fr_strerror());
		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);
	if (name->length >= sizeof(name->vp_strvalue)) {
		name->length = sizeof(name->vp_strvalue) - 1;
	}

	memcpy(name->vp_strvalue,
	       &eap_ds->response->type.data[4 + MSCHAPV2_RESPONSE_LEN],
	       name->length);
	name->vp_strvalue[name->length] = '\0';

	/*
	 *	Add the pairs to the request, and call the 'mschap'
	 *	module.
	 */
	pairadd(&handler->request->packet->vps, challenge);
	pairadd(&handler->request->packet->vps, response);
	pairadd(&handler->request->packet->vps, name);

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 (handler->request->options & RAD_REQUEST_OPTION_PROXY_EAP) {
		char *username = NULL;
		eap_tunnel_data_t *tunnel;

		DEBUG2("rlm_eap_mschapv2: cancelling authentication and letting it be proxied");

		/*
		 *	Set up the callbacks for the tunnel
		 */
		tunnel = rad_malloc(sizeof(*tunnel));
		memset(tunnel, 0, sizeof(*tunnel));

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

		/*
		 *	Associate the callback with the request.
		 */
		rcode = request_data_add(handler->request,
					 handler->request->proxy,
					 REQUEST_DATA_EAP_TUNNEL_CALLBACK,
					 tunnel, free);
		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(&handler->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(handler->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 \\ */
			memmove(challenge->vp_strvalue,
				username,
				strlen(username) + 1); /* include \0 */
			challenge->length = strlen(challenge->vp_strvalue);
		}

		/*
		 *	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 = module_authenticate(PW_AUTHTYPE_MS_CHAP, handler->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) {
		pairmove2(&response, &handler->request->reply->vps,
			 PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
		data->code = PW_EAP_MSCHAPV2_SUCCESS;

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

			DEBUG2("  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) {
		radlog(L_ERR, "rlm_eap_mschapv2: No MS-CHAPv2-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;
}
Ejemplo n.º 5
0
/*
 *	Check password.
 *
 *	Returns:	0  OK
 *			-1 Password fail
 *			-2 Rejected (Auth-Type = Reject, send Port-Message back)
 *			1  End check & return, don't reply
 *
 *	NOTE: NOT the same as the RLM_ values !
 */
static int rad_check_password(REQUEST *request)
{
	VALUE_PAIR *auth_type_pair;
	VALUE_PAIR *cur_config_item;
	VALUE_PAIR *password_pair;
	VALUE_PAIR *auth_item;
  DICT_VALUE *da;
	char string[MAX_STRING_LEN];
	int auth_type = -1;
	int result;
	int auth_type_count = 0;
	result = 0;

	/*
	 *	Look for matching check items. We skip the whole lot
	 *	if the authentication type is PW_AUTHTYPE_ACCEPT or
	 *	PW_AUTHTYPE_REJECT.
	 */
	cur_config_item = request->config_items;
	while(((auth_type_pair = pairfind(cur_config_item, PW_AUTH_TYPE))) != NULL) {
		auth_type = auth_type_pair->lvalue;
		auth_type_count++;

		DEBUG2("  rad_check_password:  Found Auth-Type %s",
				auth_type_pair->strvalue);
		cur_config_item = auth_type_pair->next;

		if (auth_type == PW_AUTHTYPE_REJECT) {
			DEBUG2("  rad_check_password: Auth-Type = Reject, rejecting user");
			return -2;
		}
	}

	if (( auth_type_count > 1) && (debug_flag)) {
		radlog(L_ERR, "Warning:  Found %d auth-types on request for user '%s'",
			auth_type_count, request->username->strvalue);
	}

	/*
	 *	This means we have a proxy reply or an accept
	 *  and it wasn't rejected in the above loop.  So
	 *  that means it is accepted and we do no further
	 *  authentication
	 */
	if ((auth_type == PW_AUTHTYPE_ACCEPT) || (request->proxy)) {
		DEBUG2("  rad_check_password: Auth-Type = Accept, accepting the user");
		return 0;
	}

	/*
	 *	Find the password from the users file.
	 */
	if ((password_pair = pairfind(request->config_items, PW_CRYPT_PASSWORD)) != NULL) {
		/*
		 *	Re-write Auth-Type, but ONLY if it isn't already
		 *	set.
		 */
		if (auth_type == -1) auth_type = PW_AUTHTYPE_CRYPT;
	} else {
		password_pair = pairfind(request->config_items, PW_PASSWORD);
	}

	if (auth_type < 0) {
		if (password_pair) {
			auth_type = PW_AUTHTYPE_LOCAL;
		} else {
			/*
		 	*	The admin hasn't told us how to
		 	*	authenticate the user, so we reject them!
		 	*
		 	*	This is fail-safe.
		 	*/
			DEBUG2("auth: No authenticate method (Auth-Type) configuration found for the request: Rejecting the user");
			return -2;
		}
	}

	switch(auth_type) {
		case PW_AUTHTYPE_CRYPT:
			/*
			 *	Find the password sent by the user. It
			 *	SHOULD be there, if it's not
			 *	authentication fails.
			 */
			auth_item = request->password;
			if (auth_item == NULL) {
				DEBUG2("auth: No User-Password or CHAP-Password attribute in the request");
				return -1;
			}

			DEBUG2("auth: type Crypt");
			if (password_pair == NULL) {
				DEBUG2("No Crypt-Password configured for the user");
				rad_authlog("Login incorrect "
					"(No Crypt-Password configured for the user)", request, 0);
				return -1;
			}

			switch (lrad_crypt_check((char *)auth_item->strvalue,
									 (char *)password_pair->strvalue)) {
			case -1:
			  rad_authlog("Login incorrect "
						  "(system failed to supply an encrypted password for comparison)", request, 0);
			case 1:
			  return -1;
			}
			break;
		case PW_AUTHTYPE_LOCAL:
			DEBUG2("auth: type Local");

			/*
			 *	Find the password sent by the user. It
			 *	SHOULD be there, if it's not
			 *	authentication fails.
			 */
			auth_item = request->password;
			if (auth_item == NULL) {
				DEBUG2("auth: No User-Password or CHAP-Password attribute in the request");
				return -1;
			}

			/*
			 *	Plain text password.
			 */
			if (password_pair == NULL) {
				DEBUG2("auth: No password configured for the user");
				rad_authlog("Login incorrect "
					"(No password configured for the user)", request, 0);
				return -1;
			}

			/*
			 *	Local password is just plain text.
	 		 */
			if (auth_item->attribute == PW_PASSWORD) {
				if (strcmp((char *)password_pair->strvalue,
					   (char *)auth_item->strvalue) != 0) {
					DEBUG2("auth: user supplied User-Password does NOT match local User-Password");
					return -1;
				}
				DEBUG2("auth: user supplied User-Password matches local User-Password");
				break;

			} else if (auth_item->attribute != PW_CHAP_PASSWORD) {
				DEBUG2("The user did not supply a User-Password or a CHAP-Password attribute");
				rad_authlog("Login incorrect "
					"(no User-Password or CHAP-Password attribute)", request, 0);
				return -1;
			}

			rad_chap_encode(request->packet, string,
					auth_item->strvalue[0], password_pair);

			/*
			 *	Compare them
			 */
			if (memcmp(string + 1, auth_item->strvalue + 1,
				   CHAP_VALUE_LENGTH) != 0) {
				DEBUG2("auth: user supplied CHAP-Password does NOT match local User-Password");
				return -1;
			}
			DEBUG2("auth: user supplied CHAP-Password matches local User-Password");
			break;
		default:
      da = dict_valbyattr(PW_AUTH_TYPE, auth_type);
			DEBUG2("auth: type \"%s\"", da->name);
			/*
			 *	See if there is a module that handles
			 *	this type, and turn the RLM_ return
			 *	status into the values as defined at
			 *	the top of this function.
			 */
			result = module_authenticate(auth_type, request);
			switch (result) {
				/*
				 *	An authentication module FAIL
				 *	return code, or any return code that
				 *	is not expected from authentication,
				 *	is the same as an explicit REJECT!
				 */
				case RLM_MODULE_FAIL:
				case RLM_MODULE_REJECT:
				case RLM_MODULE_USERLOCK:
				case RLM_MODULE_INVALID:
				case RLM_MODULE_NOTFOUND:
				case RLM_MODULE_NOOP:
				case RLM_MODULE_UPDATED:
					result = -1;
					break;
				case RLM_MODULE_OK:
					result = 0;
					break;
				case RLM_MODULE_HANDLED:
					result = 1;
					break;
			}
			break;
	}

	return result;
}