Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

509 lines
14 KiB

//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation
//
// File: ktkerb.cxx
//
// Contents: Kerberos Tunneller, routines that parse the kerb req/req
//
// History: 23-Jul-2001 t-ryanj Created
//
//------------------------------------------------------------------------
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#ifndef SECURITY_WIN32
#define SECURITY_WIN32
#endif
#include <wincrypt.h>
#include <asn1util.h>
#include <kerbcomm.h>
#include "ktdebug.h"
#include "ktkerb.h"
#include "ktmem.h"
//#define KT_REGKEY_REALMS TEXT("System\\CurrentControlSet\\Services\\kerbtunnel\\Realms\\")
TCHAR KT_REGKEY_REALMS[] = TEXT("System\\CurrentControlSet\\Services\\kerbtunnel\\Realms\\");
#define KT_INITIAL_KDCBUFFER_SIZE 64
//+-------------------------------------------------------------------------
//
// Function: KtSetPduValue
//
// Synopsis: Parses the kerberos request and sets the PduValue memeber
// of pContext to the appropriate value to be passed to
// KerbUnpackData
//
// Effects:
//
// Arguments: pContext - context that has the coalesced request in
// pContext->emptybuf->buffer
//
// Requires:
//
// Returns: Success value.
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL
KtSetPduValue(
PKTCONTEXT pContext
)
{
BOOL fRet = TRUE;
BYTE AsnTag;
//
// If we don't have the first byte of the request, we can't parse it.
//
if( pContext->emptybuf->buflen < sizeof(DWORD) + 1 )
goto Error;
//
// The last five bits of the first byte of the kerb message tell us
// what kind of message it is. We set the pdu value accordingly.
//
switch( (AsnTag = pContext->emptybuf->buffer[sizeof(DWORD)] & 0x1f) ) /* last 5 bits */
{
case 0x0a: /* AS-REQUEST */
DebugLog( DEB_TRACE, "%s(%d): Handing AS-REQUEST.\n", __FILE__, __LINE__ );
pContext->PduValue = KERB_AS_REQUEST_PDU;
break;
case 0x0b: /* AS-REPLY */
DebugLog( DEB_TRACE, "%s(%d): Handling AS-REPLY.\n", __FILE__, __LINE__ );
pContext->PduValue = KERB_AS_REPLY_PDU;
break;
case 0x0c: /* TGS-REQUEST */
DebugLog( DEB_TRACE, "%s(%d): Handling TGS-REQUEST.\n", __FILE__, __LINE__ );
pContext->PduValue = KERB_TGS_REQUEST_PDU;
break;
case 0x0d: /* TGS-REPLY */
DebugLog( DEB_TRACE, "%s(%d): Handing TGS-REPLY.\n", __FILE__, __LINE__ );
pContext->PduValue = KERB_TGS_REPLY_PDU;
break;
case 0x1e: /* KERB_ERROR */
DebugLog( DEB_TRACE, "%s(%d): Handling KERB-ERROR.\n", __FILE__, __LINE__ );
pContext->PduValue = KERB_ERROR_PDU;
break;
default:
DebugLog( DEB_WARN, "%s(%d): Unhandled message type. ASN tag: 0x%x.\n", __FILE__, __LINE__, AsnTag );
DsysAssert( (AsnTag >= 0x0a && AsnTag <= 0x0d) ||
AsnTag == 0x1e );
break;
}
Cleanup:
return fRet;
Error:
fRet = FALSE;
goto Cleanup;
}
//+-------------------------------------------------------------------------
//
// Function: KtParseExpectedLength
//
// Synopsis: Parses the expected length of the message.
// When using TCP, the message is prepended by four bytes
// indicating its length in network byte order.
// When using UDP, the ASN.1 encoded length in the message
// must be parsed out.
//
// Effects:
//
// Arguments: pContext - context that has the beginning of the request in
// pContext->emptybuf->buffer
//
// Requires:
//
// Returns: Success value.
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL
KtParseExpectedLength(
PKTCONTEXT pContext
)
{
BOOL fRet = TRUE;
DWORD cbContent;
#if 0 /* this is the udp way, not the tcp way */
LONG cbLength;
#endif
//
// If we don't have the four bytes, we can't parse the length.
//
if( pContext->emptybuf->bytesused < sizeof(DWORD) )
{
DebugLog( DEB_ERROR, "%s(%d): Not enough data to parse length.\n", __FILE__, __LINE__ );
goto Error;
}
//
// Otherwise, we just take the first four bytes and convert them
// to host byte order.
//
cbContent = ntohl( *(u_long*)pContext->emptybuf->buffer );
//
// If the most significant bit is set, this indicates that more
// bytes are needed for the length. We're just going to discard
// any messages that long.
//
if( cbContent & 0x80000000 )
{
DebugLog( DEB_ERROR, "%s(%d): Length won't fit in DWORD.\n", __FILE__, __LINE__ );
goto Error;
}
//
// Then the total length is the length of the length plus the length
// of the content.
//
pContext->ExpectedLength = sizeof(DWORD) + cbContent;
if( !KtSetPduValue(pContext) ) /* TODO: this call should be elsewhere */
goto Error;
#if 0 /* this will need to be added back in for udp support */
cbLength = Asn1UtilDecodeLength( &cbContent,
&(pContext->emptybuf->buffer[1]),
pContext->emptybuf->bytesused - 1 );
if( cbLength < 0 )
{
goto Error;
}
pContext->ExpectedLength = 1 + cbLength + cbContent;
#endif
Cleanup:
return fRet;
Error:
fRet = FALSE;
goto Cleanup;
}
//+-------------------------------------------------------------------------
//
// Function: KtFindProxy
//
// Synopsis: Unpacks the AS or TGS request, examines the realm, and
// attempts to find some way to contact that realm using http,
// first by looking on the Realms registry key, then by doing a
// DNS SRV lookup.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns: rets
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL
KtFindProxy(
PKTCONTEXT pContext
)
{
BOOL fRet = TRUE;
PKERB_KDC_REQUEST pKdcReq = NULL;
KERBERR KerbErr;
HKEY RealmKey = NULL;
LONG RegError;
LPBYTE pbKdcBuffer = NULL;
DWORD cbKdcBuffer = KT_INITIAL_KDCBUFFER_SIZE;
DWORD cbKdcBufferWritten;
LPWSTR RealmKeyName = NULL;
LPWSTR WideRealm = NULL;
ULONG ccRealm;
int WideCharSuccess;
//
// Lets attempt to unpack the data into a struct.
// This call will allocate pKdcReq if successful.
//
KerbErr = KerbUnpackData( pContext->buffers->buffer + sizeof(DWORD),
pContext->buffers->bytesused - sizeof(DWORD),
pContext->PduValue,
(PVOID*)&pKdcReq );
if( !KERB_SUCCESS(KerbErr) )
{
DebugLog( DEB_ERROR, "%s(%d): Kerberos Error unpacking ticket: 0x%x\n", __FILE__, __LINE__, KerbErr );
goto Error;
}
DebugLog( DEB_TRACE, "%s(%d): Realm found to be %s.\n", __FILE__, __LINE__, pKdcReq->request_body.realm );
//
// This call discovers the number of wchars needed to hold
// the converted realmname.
//
ccRealm = strlen(pKdcReq->request_body.realm)*sizeof(WCHAR);
//
// Allocate memory to hold the wchar converted realm name, and
// allocate memory to hold the full name of the registry key to
// peek at.
//
WideRealm = (LPWSTR)KtAlloc((ccRealm + 1)*sizeof(WCHAR));
RealmKeyName = (LPWSTR)KtAlloc( sizeof(KT_REGKEY_REALMS) + ccRealm*sizeof(WCHAR) );
if( !WideRealm || !RealmKeyName )
{
DebugLog( DEB_ERROR, "%s(%d): Allocation error.", __FILE__, __LINE__ );
goto Error;
}
//
// Copy the beginning of the realmkey.
//
wcscpy( RealmKeyName, KT_REGKEY_REALMS );
//
// Convert the realm to unicode.
//
WideCharSuccess = MultiByteToWideChar( CP_ACP,
0,
pKdcReq->request_body.realm,
-1,
WideRealm,
ccRealm );
if( !WideCharSuccess )
{
DebugLog( DEB_ERROR, "%s(%d): Error converting realm to unicode: 0x%x\n", __FILE__, __LINE__, GetLastError() );
goto Error;
}
//
// Concat the realm to the regkey to get the full key.
//
wcscat( RealmKeyName, WideRealm );
//
// Now we look in the registry to see if a kproxy
// server is provided for this realm.
//
/* TODO: All registry access should be moved to another file,
read on startup, and cached. */
RegError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
RealmKeyName,
NULL, /* reserved */
KEY_QUERY_VALUE,
&RealmKey );
if( RegError != ERROR_SUCCESS &&
RegError != ERROR_FILE_NOT_FOUND )
{
DebugLog( DEB_ERROR, "%s(%d): Error opening regkey HKLM\\%ws: 0x%x.\n", __FILE__, __LINE__, KT_REGKEY_REALMS, RegError );
fRet = FALSE;
goto Cleanup;
}
//
// If the registry key was there, read it.
//
if( RegError == ERROR_SUCCESS )
{
//
// There's no way to determine how much data is in the
// key, so we'll just try with increasing buffer sizes
// until we succeed.
//
do
{
//
// If we've already allocated memory, it must not have been enough,
// so we need to free it then allocate the new amount of memory.
// This should be faster than realloc, because realloc would copy
// the memory if relocation were necessary.
//
if( pbKdcBuffer )
{
KtFree( pbKdcBuffer );
}
pbKdcBuffer = (LPBYTE)KtAlloc( cbKdcBuffer );
if( !pbKdcBuffer )
{
DebugLog( DEB_ERROR, "%s(%d): Error allocating memory for reg values.\n", __FILE__, __LINE__ );
goto Error;
}
//
// Since RegQueryValueEx clobbers the size of our buffer if it
// fails, we need to make sure we keep a good copy.
//
cbKdcBufferWritten = cbKdcBuffer;
RegError = RegQueryValueEx( RealmKey,
TEXT("KdcNames"),
NULL, /* reserved */
NULL, /* type is known: REG_MULTI_SZ */
pbKdcBuffer,
&cbKdcBufferWritten );
if( RegError == ERROR_FILE_NOT_FOUND )
{
DebugLog( DEB_TRACE, "%s(%d): KdcNames key not found.\n", __FILE__, __LINE__ );
break;
}
if( RegError != ERROR_SUCCESS &&
RegError != ERROR_MORE_DATA )
{
DebugLog( DEB_ERROR, "%s(%d): Error querying regkey HKLM\\%ws\\%ws: 0x%x.\n", __FILE__, __LINE__, KT_REGKEY_REALMS, WideRealm, RegError );
goto Error;
}
} while( RegError == ERROR_MORE_DATA &&
(cbKdcBuffer *= 2) );
}
//
// If either the realm key wasn't found or for some reason there was
// no KdcNames value, we'll use DNS SRV lookup.
//
if( RegError == ERROR_FILE_NOT_FOUND )
{
DebugLog( DEB_ERROR, "%s(%d): Registry key not found, and DNS SRV not yet implemented.\n", __FILE__, __LINE__ );
goto Error;
}
DebugLog( DEB_TRACE, "%s(%d): First proxy found to be %ws.\n", __FILE__, __LINE__, pbKdcBuffer );
pContext->pbProxies = pbKdcBuffer;
pbKdcBuffer = NULL;
Cleanup:
if( RealmKeyName )
KtFree( RealmKeyName );
if( WideRealm )
KtFree( WideRealm );
if( pbKdcBuffer )
KtFree( pbKdcBuffer );
if( RealmKey )
RegCloseKey( RealmKey );
if( pKdcReq )
KerbFreeData( pContext->PduValue, pKdcReq );
return fRet;
Error:
fRet = FALSE;
goto Cleanup;
}
//+-------------------------------------------------------------------------
//
// Function: KtParseKerbError
//
// Synopsis: Since it's difficult to understand sniffs of tunnelled
// traffic, this routine allows for the on-the-fly parsing
// of KERB_ERROR codes.
//
// Effects:
//
// Arguments: pContext - context that has the message
//
// Requires:
//
// Returns:
//
// Notes:
//
//--------------------------------------------------------------------------
VOID
KtParseKerbError(
PKTCONTEXT pContext
)
{
PKERB_ERROR pKerbError = NULL;
KERBERR KerbErr;
if( !(pContext->PduValue == KERB_ERROR_PDU) )
goto Cleanup;
KerbErr = KerbUnpackKerbError( pContext->emptybuf->buffer + sizeof(DWORD),
pContext->ExpectedLength,
&pKerbError );
if( !KERB_SUCCESS(KerbErr) )
{
DebugLog( DEB_ERROR, "%s(%d): Found KERB_ERROR, but couldn't unpack: 0x%x\n", __FILE__, __LINE__, KerbErr );
goto Cleanup;
}
DebugLog( DEB_ERROR, "%s(%d): KERB_ERROR. Error code = 0x%x\n", __FILE__, __LINE__, pKerbError->error_code );
if( pKerbError->bit_mask && error_text_present )
DebugLog( DEB_ERROR, "%s(%d): KERB_ERROR, Error text = %s.\n", __FILE__, __LINE__, pKerbError->error_text );
if( pKerbError->bit_mask && error_data_present )
DebugLog( DEB_ERROR, "%s(%d): KERB_ERROR, Error data is present.\n", __FILE__, __LINE__ );
Cleanup:
if( pKerbError )
KerbFreeData( pContext->PduValue,
pKerbError );
}
//+-------------------------------------------------------------------------
//
// Function: KtIsAsRequest
//
// Synopsis: Determines if the message is an AS-REQUEST.
//
// Effects:
//
// Arguments: pContext - context that has the message
//
// Requires:
//
// Returns: TRUE if AS-REQUEST, otherwise FALSE.
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL
KtIsAsRequest(
PKTCONTEXT pContext
)
{
return (pContext->PduValue == KERB_AS_REQUEST_PDU);
}