linklayerprotocols/pppnif/SPPP/MSCHAP.CPP
changeset 0 af10295192d8
child 47 a96f0f8e6602
equal deleted inserted replaced
-1:000000000000 0:af10295192d8
       
     1 // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // Extensions (MS-CHAP) - RFC 2433, except the authenticator-controlled
       
    15 // authentication retry mechanisms and the password changing mechanisms -
       
    16 // this is in accordance with the requirements.
       
    17 // 
       
    18 //
       
    19 
       
    20 /**
       
    21  @file
       
    22  @brief Source file for the implementation of Microsoft PPP CHAP
       
    23  @internalComponent 
       
    24 */
       
    25 
       
    26 #include "MSCHAP.H"
       
    27 #include "PPPConfig.h"
       
    28 
       
    29 // using DES
       
    30 #include <symmetric.h>
       
    31 
       
    32 //using MD4
       
    33 #include "MD4.H"
       
    34 
       
    35 
       
    36 CPppMsChap::CPppMsChap()
       
    37 /**
       
    38    Constructor.
       
    39    @internalComponent
       
    40 */
       
    41 	: iResponseValue(KPppMsChapResponseValueSize)
       
    42 	{
       
    43 	}
       
    44 
       
    45 void CPppMsChap::InitL(CPppLcp* aLcp)
       
    46 /**
       
    47    @copydoc CPppChap::InitL(CPppLcp*)
       
    48    @see CPppChap::InitL(CPppLcp*)
       
    49    @internalComponent
       
    50 */
       
    51 	{
       
    52 	CPppChap::InitL(aLcp);
       
    53 
       
    54 #ifdef __MS_CHAP_WITH_LAN_MANAGER__
       
    55 	iUseNTResponse = ETrue;
       
    56 #endif // __MS_CHAP_WITH_LAN_MANAGER__
       
    57 	}
       
    58 
       
    59 CPppMsChap::~CPppMsChap()
       
    60 /**
       
    61    Destructor.
       
    62    @internalComponent
       
    63 */
       
    64 	{
       
    65 #ifdef _UNICODE
       
    66 	delete iUserName;
       
    67 #endif
       
    68 	}
       
    69 
       
    70 
       
    71 void CPppMsChap::CheckChallengePacketL(RMBufPacket& aPacket)
       
    72 /**
       
    73    @copydoc CPppChap::CheckChallengePacketL(RMBufPacket&)
       
    74    @copydoc CPppChap::CheckChallengePacketL(RMBufPacket&)
       
    75    @internalComponent
       
    76 */
       
    77 	{
       
    78 	__ASSERT_ALWAYS(aPacket.Length() >= KPppChapCodeFieldSize +
       
    79 				KPppChapIdFieldSize + 
       
    80 				KPppChapLengthFieldSize +
       
    81 				KPppChapValueSizeFieldSize + 
       
    82 				KPppMsChapChallengeSize,
       
    83 			User::Leave(KErrUnderflow));	
       
    84 
       
    85 	__ASSERT_ALWAYS(*(aPacket.First()->Ptr() +
       
    86 							KPppChapCodeFieldSize +
       
    87 							KPppChapIdFieldSize +
       
    88 							KPppChapLengthFieldSize) == 
       
    89 						KPppMsChapChallengeSize,
       
    90 				User::Leave(KErrOverflow));	
       
    91 	}
       
    92 
       
    93 
       
    94 void CPppMsChap::MakeResponseL(TUint8 /*aChallengeId*/, 
       
    95 				const TDesC8& aChallengeValue, 
       
    96 				TPtrC8& aResponseValue, 
       
    97 				TPtrC8& aResponseName)
       
    98 /**
       
    99    @copydoc CPppChap::MakeResponseL(TUint8, const TDesC8&, TPtrC8&, TPtrC8&)
       
   100    @see CPppChap::MakeResponseL(TUint8, const TDesC8&, TPtrC8&, TPtrC8&)
       
   101    @internalComponent
       
   102 */
       
   103 	{
       
   104 	ASSERT(aChallengeValue.Length() == KPppMsChapChallengeSize);
       
   105 
       
   106     const CCredentialsConfig* credentials = iPppLcp->GetCredentials();
       
   107 	const TDesC& password = credentials->GetPassword();
       
   108 
       
   109 #ifndef __MS_CHAP_WITH_LAN_MANAGER__
       
   110 
       
   111 // The NT password shall not be longer than
       
   112 // KPppMsChapMaxNTPasswordLength
       
   113 	__ASSERT_ALWAYS(password.Length() <=
       
   114 				KPppMsChapMaxNTPasswordLength,
       
   115 			User::Leave(KErrTooBig));
       
   116 
       
   117 #else // __MS_CHAP_WITH_LAN_MANAGER__
       
   118 
       
   119 	if (iUseNTResponse)
       
   120 // The NT password shall not be longer than
       
   121 // KPppMsChapMaxNTPasswordLength
       
   122 		__ASSERT_ALWAYS(password.Length() <=
       
   123 					KPppMsChapMaxNTPasswordLength,
       
   124 				User::Leave(KErrTooBig));
       
   125 	else
       
   126 // The LAN Manager password shall not be longer than
       
   127 // KPppMsChapMaxNTPasswordLength OEM characters
       
   128 		__ASSERT_ALWAYS(password.Length() <=
       
   129 				KPppMsChapMaxLANManagerPasswordLength,
       
   130 				User::Leave(KErrTooBig));
       
   131 
       
   132 #endif // __MS_CHAP_WITH_LAN_MANAGER__
       
   133 
       
   134 #ifdef _UNICODE
       
   135 	TPtrC16 uniPassword(password);
       
   136 #else // ! _UNICODE
       
   137 // The following MS-CHAP routines require the password to be
       
   138 // represented as 0-to-256-unicode-char (RFC 2433), so convert the
       
   139 // password to Unicode.
       
   140 	HBufC16& uniPassword = *HBufC16::NewLC(password.Length());
       
   141  	uniPassword.Des().Copy(password);
       
   142 #endif // ! _UNICODE
       
   143 
       
   144 	TPtr8 ntResponse(const_cast<TUint8*>(iResponseValue.Ptr()) +
       
   145 		KPppMsChapLanManResponseSize,
       
   146 		KPppMsChapNTResponseSize,
       
   147 		KPppMsChapNTResponseSize);
       
   148 	NTChallengeResponseL(aChallengeValue, 
       
   149 				uniPassword,
       
   150 				ntResponse);
       
   151 
       
   152 	TPtr8 lmResponse(const_cast<TUint8*>(iResponseValue.Ptr()),
       
   153 			KPppMsChapLanManResponseSize,
       
   154 			KPppMsChapLanManResponseSize);
       
   155 
       
   156 #ifdef __MS_CHAP_WITH_LAN_MANAGER__
       
   157 
       
   158 #ifdef _UNICODE
       
   159 // The following MS-CHAP routines require the password to be
       
   160 // represented as 0-to-14-oem-char (RFC 2433), so convert the
       
   161 // password.
       
   162 	HBufC8& oemPassword = *HBufC8::NewLC(password.Length());
       
   163  	oemPassword.Des().Copy(password);
       
   164 #else // ! _UNICODE
       
   165 	TPtrC8 oemPassword(password);
       
   166 #endif // ! _UNICODE
       
   167 
       
   168 	LmChallengeResponseL(aChallengeValue, 
       
   169 				oemPassword,
       
   170 				lmResponse);
       
   171 	*(const_cast<TUint8*>(iResponseValue.Ptr()) +
       
   172 			KPppMsChapLanManResponseSize +
       
   173 			KPppMsChapNTResponseSize) 
       
   174 		= static_cast<TUint8>(iUseNTResponse);
       
   175 
       
   176 #else //! __MS_CHAP_WITH_LAN_MANAGER__
       
   177 
       
   178 	lmResponse.FillZ();
       
   179 	*(const_cast<TUint8*>(iResponseValue.Ptr()) +
       
   180 			KPppMsChapLanManResponseSize +
       
   181          		KPppMsChapNTResponseSize) = 1;
       
   182 #endif //! __MS_CHAP_WITH_LAN_MANAGER__
       
   183 
       
   184 	aResponseValue.Set(iResponseValue);
       
   185 
       
   186 // The NT username shall not be longer than
       
   187 // KPppMsChapMaxNTUserNameLength
       
   188 	const TDesC& username = credentials->GetUserName();
       
   189 	__ASSERT_ALWAYS(username.Length() <=
       
   190 		KPppMsChapMaxNTUserNameLength,
       
   191 		User::Leave(KErrTooBig));
       
   192 
       
   193 #ifdef _UNICODE
       
   194 // The MS-CHAP routines require the username to be represented as
       
   195 // 0-to-256-char (RFC 2433), so convert the username.
       
   196 	delete iUserName;
       
   197 	iUserName = 0;
       
   198 	iUserName = HBufC8::NewL(username.Length());
       
   199 	iUserName->Des().Copy(username);
       
   200 	aResponseName.Set(*iUserName);
       
   201 #else //! _UNICODE
       
   202 	aResponseName.Set(username);
       
   203 #endif //! _UNICODE
       
   204 
       
   205 #ifdef _UNICODE
       
   206 
       
   207 #ifdef __MS_CHAP_WITH_LAN_MANAGER__ 
       
   208 	CleanupStack::PopAndDestroy(&oemPassword);
       
   209 #endif // __MS_CHAP_WITH_LAN_MANAGER__ 
       
   210 
       
   211 #else //! _UNICODE
       
   212 	CleanupStack::PopAndDestroy(&uniPassword);
       
   213 #endif //! _UNICODE
       
   214 
       
   215 	ASSERT(aResponseValue.Length() == KPppMsChapResponseValueSize);
       
   216 	ASSERT(aResponseName.Length() >= KPppChapMinNameSize &&
       
   217 		   aResponseName.Length() <= KPppMsChapMaxNTUserNameLength);
       
   218 	}
       
   219 
       
   220 
       
   221 void CPppMsChap::FailureL(RMBufPacket& aPacket)
       
   222 /**
       
   223    @copydoc CPppChap::FailureL(RMBufPacket&)
       
   224    @see CPppChap::FailureL(RMBufPacket&)
       
   225    @internalComponent
       
   226 */
       
   227 	{
       
   228 	__ASSERT_ALWAYS(aPacket.Length() >= KPppChapCodeFieldSize +
       
   229 					KPppChapIdFieldSize + 
       
   230 					KPppChapLengthFieldSize ,
       
   231 			User::Leave(KErrUnderflow));
       
   232 	if (!CheckIdentifier(aPacket))
       
   233 		User::Leave(KErrGeneral);
       
   234 
       
   235 	TimerCancel();
       
   236 
       
   237 	TPtrC8 failureMessage(aPacket.First()->Ptr() +
       
   238 					KPppChapCodeFieldSize + 
       
   239 					KPppChapIdFieldSize +
       
   240 					KPppChapLengthFieldSize, 
       
   241 				aPacket.Length() -
       
   242 					KPppChapCodeFieldSize - 
       
   243 					KPppChapIdFieldSize -
       
   244 					KPppChapLengthFieldSize);
       
   245 
       
   246 	if (failureMessage.Length()==0)
       
   247 		{
       
   248 		DoFail(KErrIfAuthenticationFailure);
       
   249 		return;
       
   250 		}
       
   251 
       
   252 	TUint msChapError;
       
   253 	TUint8 isRetryAllowed;
       
   254 	TUint8 passwordProtoVersion;
       
   255 	TBool hasNewChallenge;
       
   256 	TInt sysError = KErrIfAuthenticationFailure;
       
   257 
       
   258 	ProcessFailureMessageL(failureMessage, 
       
   259 				msChapError,
       
   260 				isRetryAllowed, 
       
   261 				hasNewChallenge, 
       
   262 				iChallengeRef,
       
   263 				passwordProtoVersion);
       
   264 
       
   265 	sysError = TranslateMsChapError(msChapError);
       
   266 
       
   267 #ifndef __MS_CHAP_WITH_LAN_MANAGER__
       
   268 
       
   269 // The authenticator-controlled authentication retry mechanisms and
       
   270 // the password changing mechanisms have not been implemented in this
       
   271 // release - this is in accordance with the project requirements.
       
   272 // Consequently simply fail.
       
   273 
       
   274 	DoFail(sysError);
       
   275 
       
   276 #else // __MS_CHAP_WITH_LAN_MANAGER__
       
   277 
       
   278 // The code only handles KPppMsChapAuthenticationFailure, and no other
       
   279 // MS-CHAP specific errors.  In particular, this code does not handle
       
   280 // KPppMsChapErrorPasswordExpired, since the password changing
       
   281 // mechanisms have not been implemented in this release - this is in
       
   282 // accordance with the project requirements.
       
   283 	if (msChapError != KPppMsChapAuthenticationFailure)
       
   284 		{
       
   285 		DoFail(sysError);
       
   286 		return;
       
   287 		}
       
   288 
       
   289 	if (!isRetryAllowed)
       
   290 		{
       
   291 		DoFail(sysError);
       
   292 		return;
       
   293 		}
       
   294 
       
   295 	if (!hasNewChallenge)
       
   296 		iChallengeRef[0] += 23; // magic constant from RFC 2433
       
   297 
       
   298 
       
   299 	if(!iUseNTResponse)
       
   300 		{
       
   301 // Has already retried authentication using a LAN Manager compatible
       
   302 // Challenge Response.  Have tried Microsoft Windows NT compatible
       
   303 // Challenge Response and LAN Manager compatible Challenge Response,
       
   304 // so fail now
       
   305 		iUseNTResponse = ETrue;
       
   306 		DoFail(sysError);
       
   307 		return;
       
   308 		}
       
   309 
       
   310 // Retry authentication using a LAN Manager compatible Challenge
       
   311 // Response.
       
   312 	iUseNTResponse = EFalse;
       
   313 
       
   314 	RetryPasswordL();
       
   315 
       
   316 #endif //  __MS_CHAP_WITH_LAN_MANAGER__
       
   317 	}
       
   318 
       
   319 
       
   320 inline void CPppMsChap::ProcessFailureMessageL(
       
   321 					const TDesC8& aFailureMessage,
       
   322 					TUint& aErrorCode, 
       
   323 					TUint8& aRetryFlag, 
       
   324 					TBool& aHasNewChallenge, 
       
   325 					TDes8& aChallenge, 
       
   326 					TUint8&	aPasswordProtoVersion)
       
   327 /**
       
   328    Processes a MS-CHAP Failure Message.
       
   329    @param aFailureMessage [in] A MS-CHAP Failure Message.  The Failure
       
   330    Message needs to be in the format specified in RFC 2433:
       
   331    "E=eeeeeeeeee R=r C=cccccccccccccccc V=vvvvvvvvvv".
       
   332    @param aErrorCode [out] The MS-CHAP Failure error code.
       
   333    @param aRetryFlag [out] The retry flag.  The flag will be set to
       
   334    "1" if a retry is allowed, and "0" if not.  When the authenticator
       
   335    sets this flag to "1" it disables short timeouts, expecting the
       
   336    peer to prompt the user for new credentials and resubmit the
       
   337    response.
       
   338    @param aHasNewChallenge [out] The flag that indicates if the
       
   339    Failure Message contains a new Challenge Value.
       
   340    @param aChallenge [out] The new Challenge Value, if the Failure
       
   341    Message contains a one.
       
   342    @param aPasswordProtoVersion [out] The password changing protocol
       
   343    supported by the peer.
       
   344    @internalComponent
       
   345 */
       
   346 	{
       
   347 	ASSERT(aChallenge.Length() == KPppMsChapChallengeSize);
       
   348 	
       
   349 	TLex8 input(aFailureMessage);
       
   350 
       
   351 	if (input.Get() != 'E')
       
   352 		User::Leave(KErrGeneral);
       
   353 
       
   354 	if (input.Get() != '=')
       
   355 		User::Leave(KErrGeneral);
       
   356 
       
   357 
       
   358 // RFC 2433: ""eeeeeeeeee" is the ASCII representation of a decimal
       
   359 // error code (need not be 10 digits) corresponding to one of those
       
   360 // listed below, though implementations should deal with codes not on
       
   361 // this list gracefully."
       
   362 
       
   363 	
       
   364 	TInt ret;
       
   365 	if ((ret = input.Val(aErrorCode)) != KErrNone)
       
   366 		if (ret != KErrOverflow)
       
   367 			User::Leave(KErrGeneral);
       
   368 		else
       
   369 // Gracefully handle unusually large, yet valid, MS-CHAP specific
       
   370 // error code values.  This code only handles the MS-CHAP specific
       
   371 // error code values specified in RFC 2433.
       
   372 			aErrorCode=0;
       
   373 
       
   374 	input.SkipSpace();
       
   375 
       
   376 	if (input.Get() != 'R')
       
   377 		User::Leave(KErrGeneral);
       
   378 
       
   379 	if (input.Get() != '=')
       
   380 		User::Leave(KErrGeneral);
       
   381 
       
   382 	if (input.Val(aRetryFlag, EDecimal)!=KErrNone)
       
   383 		User::Leave(KErrGeneral);
       
   384 
       
   385 	input.SkipSpace();
       
   386 
       
   387 	switch (input.Get())
       
   388 		{
       
   389 	case 'C':
       
   390 		{		
       
   391 		if (input.Get() != '=')
       
   392 			User::Leave(KErrGeneral);
       
   393 
       
   394 		TPtrC8 token(input.NextToken());
       
   395 // This field is 16 hexadecimal digits representing an ASCII
       
   396 // representation of a new challenge value.  Each octet is represented
       
   397 // in 2 hexadecimal digits.
       
   398 		if (token.Length() != KPppMsChapChallengeSize*2)
       
   399 			User::Leave(KErrGeneral);
       
   400 
       
   401 		TLex8 lex;
       
   402 		TUint8 octet;
       
   403 		TUint8* pChallengeOctet = 
       
   404 			const_cast<TUint8*>(aChallenge.Ptr());
       
   405 		TUint8 i = 0;
       
   406 		do
       
   407 			{
       
   408 			lex.Assign(token.Mid(i*2, 2));
       
   409 			if (lex.Val(octet, EHex) != KErrNone)
       
   410 				User::Leave(KErrGeneral);
       
   411 
       
   412 			*(pChallengeOctet + i) = octet;
       
   413 			}
       
   414 		while (++i < KPppMsChapChallengeSize);
       
   415 
       
   416 		aHasNewChallenge = ETrue;
       
   417 
       
   418 		input.SkipSpace();
       
   419 
       
   420 		if (input.Get()!='V')
       
   421 			{
       
   422 			aPasswordProtoVersion = 1;
       
   423 			User::Leave(KErrGeneral);
       
   424 			}
       
   425 		}
       
   426 
       
   427 // As specified in RFC 2433, the field containing the ASCII
       
   428 // representation of a new challenge value is optional, so fall
       
   429 // through.
       
   430 		
       
   431 
       
   432 	case 'V':
       
   433 
       
   434 // RFC 2433: "The "vvvvvvvvvv" is the decimal version code (need not
       
   435 // be 10 digits) indicating the MS-CHAP protocol version supported on
       
   436 // the server.  Currently, this is interesting only in selecting a
       
   437 // Change Password packet type.  If the field is not present the
       
   438 // version should be assumed to be 1; since use of the version 1
       
   439 // Change Password packet has been deprecated, this field SHOULD
       
   440 // always contain a value greater than or equal to 2."
       
   441 
       
   442 
       
   443 		aHasNewChallenge = EFalse;
       
   444 
       
   445 		if (input.Get() != '=')
       
   446 			User::Leave(KErrGeneral);
       
   447 
       
   448 		if ((ret=input.Val(aPasswordProtoVersion,
       
   449 				EDecimal)) != KErrNone)
       
   450 			if (ret!= KErrOverflow)
       
   451 				User::Leave(KErrGeneral);
       
   452 			else
       
   453 // Gracefully handle unusually large, yet valid, password changing
       
   454 // protocol version values.  This code only handles the password
       
   455 // changing protocol version codes values specified in RFC 2433.
       
   456 				aPasswordProtoVersion = 0;
       
   457 
       
   458 		break;
       
   459 	 
       
   460 	default:
       
   461 		aHasNewChallenge = EFalse;
       
   462 		aPasswordProtoVersion = 1;
       
   463 		}
       
   464 	}
       
   465 
       
   466 
       
   467 inline void CPppMsChap::RetryPasswordL()
       
   468 /**
       
   469    Retries the authentication.
       
   470    @internalComponent
       
   471 */
       
   472 	{
       
   473 	++iCurrentId;
       
   474 
       
   475 	iResponseRetryCount = 0;
       
   476 	RespondL();
       
   477 	}
       
   478 
       
   479 
       
   480 inline void CPppMsChap::NTChallengeResponseL(const TDesC8& aChallenge,
       
   481 					     const TDesC16& aPassword,
       
   482 					     TDes8& aResponse)
       
   483 /**
       
   484    Computes a MS-CHAP Windows NT compatible Challenge Response.
       
   485    @param aChallenge [in] A MS-CHAP Challenge (8 octets).
       
   486    @param aPassword [in] The Microsoft Windows NT password (0 to 256
       
   487    Unicode char).
       
   488    @param aResponse [out] The MS-CHAP Windows NT compatible Challenge
       
   489    Response (24 octets).
       
   490    @note This function implements the NTChallengeResponse routine
       
   491    specified in RFC 2433.
       
   492    @internalComponent
       
   493 */
       
   494 	{
       
   495 	ASSERT(aChallenge.Length() == KPppMsChapChallengeSize);
       
   496 	ASSERT(aPassword.Length() <= KPppMsChapMaxNTPasswordLength);
       
   497 	ASSERT(aResponse.Length() == KPppMsChapNTResponseSize);
       
   498 
       
   499 	HBufC8* paddedPasswordHashBuf =
       
   500 			HBufC8::NewLC(KPppMsChapPaddedHashSize);
       
   501 	TPtr8 paddablePasswordHash(paddedPasswordHashBuf->Des());
       
   502 
       
   503 	paddablePasswordHash.SetLength(KPppMsChapHashSize);
       
   504 	NtPasswordHashL(aPassword, paddablePasswordHash);
       
   505 
       
   506 	ChallengeResponseL(aChallenge, 
       
   507 			paddablePasswordHash,
       
   508 			aResponse);
       
   509 
       
   510 	CleanupStack::PopAndDestroy(paddedPasswordHashBuf);
       
   511 	ASSERT(aResponse.Length() == KPppMsChapNTResponseSize);
       
   512 	}
       
   513 
       
   514 
       
   515 inline void CPppMsChap::NtPasswordHashL(const TDesC16& aPassword,
       
   516 					TDes8& aPasswordHash)
       
   517 /**
       
   518    Computes the hash of the Microsoft Windows NT password using MD4.
       
   519    @param aPassword [in] The Microsoft Windows NT password (0 to 256
       
   520    Unicode char).
       
   521    @param aPasswordHash [out] The MD4 hash of the Microsoft Windows NT
       
   522    password (16 octets).
       
   523    @note This function implements the NtPasswordHash routine specified
       
   524    in RFC 2433.
       
   525    @internalComponent
       
   526 */
       
   527 	{
       
   528 	ASSERT(aPassword.Length() <= KPppMsChapMaxNTPasswordLength);
       
   529 	ASSERT(aPasswordHash.Length() == KPppMsChapHashSize);
       
   530 
       
   531 // The following code does not use the Symbian Security subsystem
       
   532 // components, because they do not provide a MD4 implementation yet.
       
   533 // This is a provisional solution until the Symbian Security subsystem
       
   534 // components will provide a MD4 implementation.
       
   535 
       
   536 	CMd4* md4 = CMd4::NewL();
       
   537 	CleanupStack::PushL(md4);
       
   538 
       
   539 
       
   540 // The following code assumes that the data in TDesC16 descriptors is
       
   541 // stored in little endian byte order, which is currently a
       
   542 // characteristic of Symbian OS, so the reinterpret_cast is assumed to
       
   543 // be safe here.
       
   544 	md4->Input(TPtrC8(reinterpret_cast<const TUint8*>(
       
   545 			aPassword.Ptr()),
       
   546 		aPassword.Size()));
       
   547 	md4->Output(aPasswordHash);
       
   548 
       
   549 	CleanupStack::PopAndDestroy(md4);
       
   550 
       
   551 	ASSERT(aPasswordHash.Length() == KPppMsChapHashSize);
       
   552 	}
       
   553 
       
   554 
       
   555 inline void CPppMsChap::ChallengeResponseL(const TDesC8& aChallenge,
       
   556 					TDes8& aPaddablePasswordHash,
       
   557 					TDes8& aResponse)
       
   558 /**
       
   559    Computes the Challenge Response.
       
   560    @param aChallenge [in] A MS-CHAP Challenge (8 octets).
       
   561    @param aPaddablePasswordHash [in/out] The hash of the password in a
       
   562    paddable buffer (16 octets in a buffer with at least 21 octets
       
   563    maximum length).
       
   564    @param aResponse [out] The Challenge Response (24 octets).
       
   565    @note This function implements the ChallengeResponse routine
       
   566    specified in RFC 2433.
       
   567    @internalComponent
       
   568 */
       
   569 	{
       
   570 	ASSERT(aChallenge.Length()==KPppMsChapChallengeSize);
       
   571 	ASSERT(aPaddablePasswordHash.Length()==KPppMsChapHashSize &&
       
   572 		aPaddablePasswordHash.MaxLength() >=
       
   573 			KPppMsChapPaddedHashSize);
       
   574 	ASSERT(aResponse.Length() == KPppMsChapNTResponseSize);
       
   575 
       
   576 // aPaddablePasswordHash contains the hash of the password (16 octets)
       
   577 // zero-padded to 21 octets
       
   578 
       
   579 // RFC 2433 - ChallengeResponse(): "Set ZPasswordHash to
       
   580 // PasswordHash zero-padded to 21 octets", i.e. 5 octets
       
   581 	aPaddablePasswordHash.AppendFill(0, 
       
   582 					KPppMsChapPaddedHashSize -
       
   583 						KPppMsChapHashSize);
       
   584 
       
   585 // The first 8 octets of aResponse
       
   586 	TPtr8 responseChunk(const_cast<TUint8*>(aResponse.Ptr()),
       
   587 				KPppDESKeySize, 
       
   588 				KPppDESKeySize);
       
   589 	DesEncryptL(aChallenge,
       
   590 		aPaddablePasswordHash.Left(KPppMsChapDESKeySize),
       
   591 		responseChunk);
       
   592 
       
   593 // The second 8 octets of aResponse
       
   594 	responseChunk.Set(const_cast<TUint8*>(aResponse.Ptr()) +
       
   595 					KPppDESKeySize, 
       
   596 			KPppDESKeySize,
       
   597 			KPppDESKeySize);
       
   598   	DesEncryptL(aChallenge,
       
   599 		aPaddablePasswordHash.Mid(KPppMsChapDESKeySize,
       
   600 			KPppMsChapDESKeySize), 
       
   601 		responseChunk);
       
   602 
       
   603 // The third 8 octets of aResponse
       
   604 	responseChunk.Set(const_cast<TUint8*>(aResponse.Ptr()) +
       
   605 		2*KPppDESKeySize, KPppDESKeySize, KPppDESKeySize);
       
   606  	DesEncryptL(aChallenge,
       
   607 		aPaddablePasswordHash.Mid(2*KPppMsChapDESKeySize,
       
   608 			KPppMsChapDESKeySize),
       
   609 		responseChunk);
       
   610 
       
   611 
       
   612 // Restore the original length of the password hash
       
   613 	aPaddablePasswordHash.SetLength(KPppMsChapHashSize);
       
   614 
       
   615 	ASSERT(aResponse.Length() == KPppMsChapNTResponseSize);
       
   616 	}
       
   617 
       
   618 void CPppMsChap::DesEncryptL(const TDesC8& aClear, 
       
   619 			const TDesC8& aKey,
       
   620 			TDes8& aCypher)
       
   621 /**
       
   622    Encrypts a plaintext into a ciphertext using the DES encryption
       
   623    algorithm in ECB mode.
       
   624    @param aClear [in] A plaintext (8 octets).
       
   625    @param aKey [in] A key (7 octets).
       
   626    @param aCypher [out] The ciphertext (8 octets).
       
   627    @note This function implements the DesEncrypt routine specified in
       
   628    RFC 2433.
       
   629    @internalComponent
       
   630 */
       
   631 	{
       
   632 	ASSERT(aClear.Length() == KPppMsChapDESClearTextSize);
       
   633 	ASSERT(aKey.Length() == KPppMsChapDESKeySize);
       
   634 	ASSERT(aCypher.Length() == KPppMsChapDESCipherTextSize);
       
   635 
       
   636 	HBufC8* desKeyBuf=HBufC8::NewMaxLC(KPppDESKeySize);
       
   637 	TPtr8 desKey(desKeyBuf->Des());
       
   638 
       
   639 // RFC 2433: "Use the DES encryption algorithm [4] in ECB mode [10] to
       
   640 // encrypt Clear into Cypher such that Cypher can only be decrypted
       
   641 // back to Clear by providing Key.  Note that the DES algorithm takes
       
   642 // as input a 64-bit stream where the 8th, 16th, 24th, etc.  bits are
       
   643 // parity bits ignored by the encrypting algorithm.  Unless you write
       
   644 // your own DES to accept 56-bit input without parity, you will need
       
   645 // to insert the parity bits yourself."
       
   646 
       
   647 	MakeDesKey(aKey, desKey);
       
   648 
       
   649 
       
   650  	CBlockTransformation* encryptor = 
       
   651 		CDESEncryptor::NewLC(desKey, EFalse);
       
   652 	CPaddingNone* padding = CPaddingNone::NewLC();
       
   653  	CBufferedEncryptor* bufEncryptor =
       
   654  		CBufferedEncryptor::NewL(encryptor, padding);
       
   655 	CleanupStack::Pop(padding);
       
   656 	CleanupStack::Pop(encryptor);
       
   657 	CleanupStack::PushL(bufEncryptor);
       
   658 
       
   659 	aCypher.Zero();
       
   660 	bufEncryptor->ProcessFinalL(aClear, aCypher);
       
   661 
       
   662  	CleanupStack::PopAndDestroy(bufEncryptor);
       
   663 
       
   664 
       
   665 	CleanupStack::PopAndDestroy(desKeyBuf);
       
   666 	ASSERT(aCypher.Length() == KPppMsChapDESCipherTextSize);
       
   667 	}
       
   668 
       
   669 
       
   670 inline void CPppMsChap::MakeDesKey(const TDesC8& aMsChapKey, 
       
   671 				TDes8& aDesKey)
       
   672 /**
       
   673    Creates a DES key by inserting the parity bits.  The DES algorithm
       
   674    takes as input a 64-bit stream where the 8th, 16th, 24th, etc. bits
       
   675    are parity bits ignored by the encrypting algorithm.
       
   676    @param aMsChapKey [in] A key used by MS-CHAP for DES encryption. (7
       
   677    octets).
       
   678    @param aDesKey [out] A DES key (8 octets).
       
   679    @internalComponent
       
   680 */
       
   681 	{
       
   682 	ASSERT(aMsChapKey.Length() == KPppMsChapDESKeySize);
       
   683 	ASSERT(aDesKey.Length() == KPppDESKeySize);
       
   684 
       
   685 // RFC 2433, RFC 2759: "Use the DES encryption algorithm [4] in ECB
       
   686 // mode [10] to encrypt Clear into Cypher such that Cypher can only be
       
   687 // decrypted back to Clear by providing Key.  Note that the DES
       
   688 // algorithm takes as input a 64-bit stream where the 8th, 16th, 24th,
       
   689 // etc.  bits are parity bits ignored by the encrypting algorithm.
       
   690 // Unless you write your own DES to accept 56-bit input without
       
   691 // parity, you will need to insert the parity bits yourself."
       
   692 
       
   693 	TUint8* pdk = const_cast<TUint8*>(aDesKey.Ptr());
       
   694 	const TUint8* pmk = aMsChapKey.Ptr();
       
   695 	TUint16 high, low;
       
   696 	TUint8 i = 0;
       
   697     do
       
   698 		{
       
   699 		high = *(pmk + i/8);
       
   700 		low = *(pmk + i/8 + 1);
       
   701 		*(pdk + i/7) = static_cast<TUint8>(
       
   702 			((high << 8 | low) >> (8 - i%8)) & 0xfe);
       
   703 		i += 7;
       
   704 		}
       
   705 	while (i < 49);
       
   706 
       
   707 	*(pdk + 7) = static_cast<TUint8>(*(pmk + 6) << 1 & 0xfe);
       
   708 
       
   709 	ASSERT(aDesKey.Length() == KPppDESKeySize);
       
   710 	}
       
   711 
       
   712 
       
   713 TInt CPppMsChap::TranslateMsChapError(TUint aMsChapError)
       
   714 /**
       
   715    Translates a MS-CHAP-V1 error code into a Symbian OS PPP NIF
       
   716    specific error code.
       
   717    @param aMsChapError A MS-CHAP-V1 error code.
       
   718    @return The Symbian OS PPP NIF specific error code corresponding to
       
   719    the MS-CHAP error code.
       
   720    @internalComponent
       
   721 */
       
   722 	{
       
   723 	// Always return from every conditional branch of the following
       
   724 	// switch statement
       
   725 	switch(aMsChapError)
       
   726 		{
       
   727 	case KPppMsChapErrorRestrictedLogon:
       
   728 		return KErrIfRestrictedLogonHours;
       
   729 		
       
   730 	case KPppMsChapErrorAccountDisabled:
       
   731 		return KErrIfAccountDisabled;
       
   732 		
       
   733 	case KPppMsChapErrorPasswordExpired:
       
   734 		return KErrIfPasswdExpired;
       
   735 		
       
   736 	case KPppMsChapErrorNoDialinPermission:
       
   737 		return KErrIfNoDialInPermission;
       
   738 		
       
   739 	case KPppMsChapErrorChangingPassword:
       
   740 		return KErrIfChangingPassword;
       
   741 		
       
   742 	case KPppMsChapAuthenticationFailure:
       
   743 		return KErrIfAuthenticationFailure;
       
   744 		}
       
   745 
       
   746 	// If unknown MS-CHAP failure code 
       
   747 	// use KErrIfAuthenticationFailure
       
   748 	// default:
       
   749 	return KErrIfAuthenticationFailure;
       
   750 	}
       
   751 
       
   752 
       
   753 #ifdef __MS_CHAP_WITH_LAN_MANAGER__
       
   754 
       
   755 // NB The use of the LAN Manager compatible challenge response has
       
   756 // been deprecated according to RFC 2433.
       
   757 inline void CPppMsChap::LmChallengeResponseL(const TDesC8& aChallenge,
       
   758 					const TDesC8& aPassword, 
       
   759 					TDes8& aResponse)
       
   760 /**
       
   761    Computes a MS-CHAP LAN Manager compatible Challenge Response.
       
   762    @param aChallenge [in] A MS-CHAP Challenge (8 octets).
       
   763    @param aPassword [in] The LAN Manager password (0 to 14 OEM char).
       
   764    @param aResponse [out] The MS-CHAP LAN Manager compatible Challenge
       
   765    Response (24 octets).
       
   766    @note This function implements the LmChallengeResponse routine
       
   767    specified in RFC 2433.
       
   768    @note The use of the LAN Manager compatible Challenge Response has
       
   769    been deprecated according to RFC 2433.
       
   770    @internalComponent
       
   771 */
       
   772 	{
       
   773 	ASSERT(aChallenge.Length() == KPppMsChapChallengeSize);
       
   774 	ASSERT(aPassword.Length() <=
       
   775 		KPppMsChapMaxLANManagerPasswordLength);
       
   776 	ASSERT(aResponse.Length() == KPppMsChapLanManResponseSize);
       
   777 
       
   778 	HBufC8* paddedPasswordHashBuf =
       
   779 			HBufC8::NewLC(KPppMsChapPaddedHashSize);
       
   780 	TPtr8 paddablePasswordHash(paddedPasswordHashBuf->Des());
       
   781 
       
   782 	paddablePasswordHash.SetLength(KPppMsChapHashSize);
       
   783 	LmPasswordHashL(aPassword, paddablePasswordHash);
       
   784 
       
   785 	ChallengeResponseL(aChallenge, 
       
   786 			paddablePasswordHash,
       
   787 			aResponse);
       
   788 
       
   789 	CleanupStack::PopAndDestroy(paddedPasswordHashBuf);
       
   790 	ASSERT(aResponse.Length() == KPppMsChapLanManResponseSize);
       
   791 	}
       
   792 
       
   793 
       
   794 // NB The use of the LAN Manager compatible challenge response has
       
   795 // been deprecated according to RFC 2433.
       
   796 inline void CPppMsChap::LmPasswordHashL(const TDesC8& aPassword,
       
   797 					TDes8& aPasswordHash)
       
   798 /**
       
   799    Computes the hash of the LAN Manager password using DES.
       
   800    @param aPassword [in] The LAN Manager password (0 to 14 OEM char).
       
   801    @param aPasswordHash [out] The DES hash of the LAN Manager password
       
   802    (16 octets).
       
   803    @note This function implements the LmPasswordHash routine specified
       
   804    in RFC 2433.
       
   805    @note The use of the LAN Manager compatible Challenge Response has
       
   806    been deprecated according to RFC 2433.
       
   807    @internalComponent
       
   808 */
       
   809 	{
       
   810 	ASSERT(aPassword.Length() <=
       
   811 		KPppMsChapMaxLANManagerPasswordLength);
       
   812 	ASSERT(aPasswordHash.Length() == KPppMsChapHashSize);
       
   813 
       
   814 	HBufC8* ucasePasswordBuf =
       
   815 	       HBufC8::NewLC(KPppMsChapMaxLANManagerPasswordLength);
       
   816 	TPtr8 ucasePassword(ucasePasswordBuf->Des());
       
   817 	ucasePassword.Copy(aPassword);
       
   818 	ucasePassword.UpperCase();
       
   819 	ucasePassword.AppendFill(0,
       
   820 		KPppMsChapMaxLANManagerPasswordLength -
       
   821 			ucasePassword.Length());
       
   822 
       
   823 // The first 8 octets of aResponse
       
   824 	TPtr8 responseChunk(const_cast<TUint8*>(aPasswordHash.Ptr()),
       
   825 				KPppDESKeySize, KPppDESKeySize);
       
   826 	DesHashL(ucasePassword.Left(KPppMsChapDESKeySize), 
       
   827 		responseChunk);
       
   828 
       
   829 	responseChunk.Set(const_cast<TUint8*>(aPasswordHash.Ptr()) +
       
   830 				KPppDESKeySize, 
       
   831 			KPppDESKeySize,
       
   832 			KPppDESKeySize);
       
   833 	DesHashL(ucasePassword.Mid(KPppMsChapDESKeySize,
       
   834 			KPppMsChapDESKeySize), 
       
   835 		responseChunk);
       
   836 
       
   837 	CleanupStack::PopAndDestroy(ucasePasswordBuf);
       
   838 	ASSERT(aPasswordHash.Length() == KPppMsChapHashSize);
       
   839 	}
       
   840 
       
   841 // NB The use of the LAN Manager compatible challenge response has
       
   842 // been deprecated according to RFC 2433.
       
   843 inline void CPppMsChap::DesHashL(const TDesC8& aClear, TDes8& aCypher)
       
   844 /**
       
   845    Makes aCypher an irreversibly encrypted form of aClear by
       
   846    encrypting known text using aClear as the secret key.  The known
       
   847    text consists of the string "KGS!@#$%".
       
   848    @param aClear [in] A plaintext used as the secret key for
       
   849    encryption (7 octets).
       
   850    @param aCypher [out] The ciphertext (8 octets).
       
   851    @note This function implements the DesHash routine specified in RFC
       
   852    2433.
       
   853    @note The use of the LAN Manager compatible challenge response has
       
   854    been deprecated according to RFC 2433.
       
   855    @internalComponent
       
   856 */
       
   857 	{
       
   858 	ASSERT(aClear.Length() == KPppMsChapDESKeySize);
       
   859 	ASSERT(aCypher.Length() == KPppMsChapDESCipherTextSize);
       
   860 
       
   861 	HBufC8* desKeyBuf = HBufC8::NewMaxLC(KPppDESKeySize);
       
   862 	TPtr8 desKey(desKeyBuf->Des());
       
   863 
       
   864 	MakeDesKey(aClear, desKey);
       
   865 
       
   866 // A magic string literal specified in RFC 2433 used as clear text for
       
   867 // making aCypher an irreversibly encrypted form of aClear by
       
   868 // encrypting this clear text using aClear as the secret key.
       
   869 	_LIT8(KStdText, "KGS!@#$%");
       
   870 
       
   871 
       
   872  	CBlockTransformation* encryptor = 
       
   873 		CDESEncryptor::NewLC(desKey, EFalse);
       
   874 	CPaddingNone* padding = CPaddingNone::NewLC();
       
   875  	CBufferedEncryptor* bufEncryptor =
       
   876 		CBufferedEncryptor::NewL(encryptor, padding);
       
   877 	CleanupStack::Pop(padding);
       
   878 	CleanupStack::Pop(encryptor);
       
   879 	CleanupStack::PushL(bufEncryptor);
       
   880 
       
   881 	aCypher.Zero();
       
   882 	bufEncryptor->ProcessFinalL(KStdText, aCypher);
       
   883 
       
   884  	CleanupStack::PopAndDestroy(bufEncryptor);
       
   885 
       
   886 
       
   887 	CleanupStack::PopAndDestroy(desKeyBuf);
       
   888 	ASSERT(aCypher.Length() == KPppMsChapDESCipherTextSize);
       
   889 	}
       
   890 
       
   891 #endif // __MS_CHAP_WITH_LAN_MANAGER__