windows-nt/Source/XPSP1/NT/ds/security/base/lsa/server/negotiat.cxx
2020-09-26 16:20:57 +08:00

9177 lines
234 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: negotiat.cxx
//
// Contents:
//
// Classes:
//
// Functions:
//
// History: 11-15-95 RichardW Created for Cairo
// 07-25-96 RichardW Added SNEGO support
// 06-24-97 MikeSw Modified for SPNEGO
//
//----------------------------------------------------------------------------
#include <lsapch.hxx>
#ifdef WIN32_CHICAGO
#include <kerb.hxx>
#endif // WIN32_CHICAGO
extern "C"
{
#include <align.h>
#include <lm.h>
#include <dsgetdc.h>
#include <cryptdll.h>
#ifndef WIN32_CHICAGO
#include <spmgr.h>
#endif
#include "sesmgr.h"
#include "spinit.h"
}
#include "negotiat.hxx"
#ifdef WIN32_CHICAGO
#include <debug.h>
#include <rpc.h> // fpr SEC_WINNT_AUTH_IDENTITY_ANSI
#define LsapChangeBuffer( o, n ) 0 ; CopyMemory( (n), (o),sizeof(SecBuffer) )
#endif // WIN32_CHICAGO
#include <stdio.h>
//
// Possible states for the Accept and Init calls.
//
#define FIRST_CALL_NO_INPUT 0
#define FIRST_CALL_WITH_INPUT 1
#define LATER_CALL_NO_INPUT 2
#define LATER_CALL_WITH_INPUT 3
#define LATER_CALL_BIT 2
#define BUFFER_PRESENT_BIT 1
//
// This define controls the proposed MA behavior v. the existing MA
// behavior
//
#define NEW_MUTUAL_AUTH
//
// This defines the minimum buffer that spnego can use, when
// fragment-to-fit is requested. This is 5 bytes, enough in
// BER to encode the start of a 64K buffer. The five bytes,
// for the curious, are:
//
// 0x60 0x8x 0xLL 0xLL 0xLL
//
// or application[0], and four bytes of length specifier.
//
#define SPNEGO_MINIMUM_BUFFER 5
ULONG NegpUseSpnego = 1;
ULONG NegpUseSnegoServer = 0;
ULONG SyncTest = 0;
SECPKG_FUNCTION_TABLE NegTable = {
NULL,
NULL,
NegCallPackage,
NegLogoffNotify,
NegCallPackage, // UNTRUSTED Is the same!
NegCallPackagePassthrough,
NULL,
#ifndef WIN32_CHICAGO
NegLogonUserEx2,
#else
NULL,
#endif
NegInitialize,
NegShutdown,
NegGetInfo,
NegAcceptCredentials,
NegAcquireCredentialsHandle,
NegQueryCredentialsAttributes,
NegFreeCredentialsHandle,
NegSaveCredentials,
NegGetCredentials,
NegDeleteCredentials,
NegInitLsaModeContext,
NegAcceptLsaModeContext,
NegDeleteLsaModeContext,
NegApplyControlToken,
NegGetUserInfo,
NegGetExtendedInformation,
NegQueryContextAttributes,
#ifndef WIN32_CHICAGO
NegAddCredentials
#endif
};
//
// Microsoft Security Mechanisms OID Branch:
//
// iso(1) org(3) dod(6) internet(1) private(4) enterprise(1) microsoft(311)
// security(2)
// mechanisms(2)
//
// Loopback Detect (9)
// <RPCID> - The RPC Id is stuck here, e.g.
// NTLM (10)
// SSL (12)
//
UCHAR NegSpnegoMechEncodedOid[] =
{ 0x06, 0x7,
0x2b, 0x6,0x1,0x5,0x5,0x2};
ObjectID NegSpnegoMechOid;
UCHAR NegMSMechanismsOid[] = { 0x06, 0x0a, // DER prefix
0x2b, 0x06, 0x01, 0x04, 0x01,
0x82, 0x37, 0x02, 0x02, 0x00 };
const UCHAR NegKerberosOid[] = { 0x06, 0x09, // DER prefix
0x2a, 0x86, 0x48, 0x86, 0xf7,
0x12, 0x01, 0x02, 0x02 };
const UCHAR NegKerberosLegacyOid[] = { 0x06, 0x09, // DER prefix
0x2a, 0x86, 0x48, 0x82, 0xf7,
0x12, 0x01, 0x02, 0x02 };
UNICODE_STRING NegLocalHostName_U ;
WCHAR NegLocalHostName[] = L"localhost";
DWORD_PTR NegPackageId;
DWORD_PTR NtlmPackageId = NEG_INVALID_PACKAGE;
ObjectID NegNtlmMechOid ;
#ifndef WIN32_CHICAGO
WCHAR NegPackageName[] = NEGOSSP_NAME ;
WCHAR NegPackageComment[] = TEXT("Microsoft Package Negotiator");
NT_PRODUCT_TYPE NegProductType;
// computer names (protected by NegComputerNamesLock)
UNICODE_STRING NegNetbiosComputerName_U;
UNICODE_STRING NegDnsComputerName_U;
#else
TCHAR NegPackageName[] = NEGOSSP_NAME ;
TCHAR NegPackageComment[] = TEXT("Microsoft Package Negotiator");
#endif // WIN32_CHICAGO
LIST_ENTRY NegPackageList;
LIST_ENTRY NegCredList;
#ifndef WIN32_CHICAGO
LIST_ENTRY NegDefaultCredList ;
RTL_RESOURCE NegLock;
PLSAP_SECURITY_PACKAGE NegLsaPackage ;
LIST_ENTRY NegLoopbackList ;
LIST_ENTRY NegLogonSessionList ;
RTL_CRITICAL_SECTION NegLogonSessionListLock ;
RTL_CRITICAL_SECTION NegTrustListLock ;
PNEG_TRUST_LIST NegTrustList ;
LARGE_INTEGER NegTrustTime ;
RTL_CRITICAL_SECTION NegComputerNamesLock;
RTL_RESOURCE NegCredListLock;
#else
RTL_CRITICAL_SECTION NegLock;
RTL_CRITICAL_SECTION NegCredListLock;
#endif // WIN32_CHICAGO
PVOID NegNotifyHandle;
DWORD NegPackageCount;
PUCHAR NegBlob;
DWORD NegBlobSize;
DWORD NegOptions;
ULONG NegMachineState;
BOOL NegUplevelDomain ;
ULONG NegNegotiationControl = 1 ;
HANDLE NegRegistryWatchEvent ;
WCHAR NegComputerName[ DNS_MAX_NAME_LENGTH ];
typedef struct _NEG_CONTEXT_REQ_MAP {
#define NEG_CONFIG_REQUIRED 0x00000001
#define NEG_CONFIG_OPTIONAL 0x00000002
ULONG Level ;
ULONG ConfigFlags ;
ULONG ContextReq ;
ULONG PackageFlag ;
} NEG_CONTEXT_REQ_MAP, * PNEG_CONTEXT_REQ_MAP ;
NEG_CONTEXT_REQ_MAP
NegContextReqMap[] = {
{ 0, NEG_CONFIG_REQUIRED, (ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_INTEGRITY), SECPKG_FLAG_INTEGRITY },
{ 0, NEG_CONFIG_REQUIRED, (ISC_REQ_CONFIDENTIALITY) , SECPKG_FLAG_PRIVACY },
{ 2, NEG_CONFIG_OPTIONAL, (ISC_REQ_MUTUAL_AUTH), SECPKG_FLAG_MUTUAL_AUTH },
{ 0, 0, 0, 0 }
};
#ifndef WIN32_CHICAGO
typedef DWORD (APIENTRY LOGON_NOTIFY)(
LPCWSTR, PLUID, LPCWSTR, LPVOID,
LPCWSTR, LPVOID, LPWSTR, LPVOID,
LPWSTR * );
typedef LOGON_NOTIFY * PLOGON_NOTIFY;
VOID
NegpNotifyNetworkProviders(
IN PUNICODE_STRING UserName,
IN PSECPKG_PRIMARY_CRED PrimaryCred
);
#endif // WIN32_CHICAGO
//
// Primary and secondary credentials used for LocalSystem
//
SECPKG_PRIMARY_CRED NegPrimarySystemCredentials;
extern "C"
SECURITY_STATUS
SEC_ENTRY
NegpValidateBuffer(
PUCHAR Buffer,
ULONG Length
)
{
UCHAR Test ;
ULONG ClaimedLength ;
ULONG ByteCount ;
ULONG i ;
if ( Length == 0 )
{
return STATUS_SUCCESS ;
}
//
// This does a poor man's validation of the BER encoded SNEGO buffer
//
//
// First, make sure the first byte is a BER value for Context Specific
//
Test = Buffer[0] & 0xC0 ;
if ( (Test != 0x80 ) &&
(Test != 0x40 ) )
{
DebugLog(( DEB_ERROR, "Neg: Buffer does not lead off with 'Context' or 'Application' specific\n"));
goto Bad_Buffer ;
}
//
// Now, check the claimed size in the header with the size we were passed:
//
Buffer++ ;
ClaimedLength = 0 ;
if (*Buffer & 0x80)
{
ByteCount = *Buffer++ & 0x7f;
for (i = 0; i < ByteCount ; i++ )
{
ClaimedLength <<= 8;
ClaimedLength += *Buffer++;
}
}
else
{
ByteCount = 0;
ClaimedLength = *Buffer++;
}
if ( (ClaimedLength + 2 + ByteCount) != Length )
{
DebugLog(( DEB_ERROR, "Neg: Packet claimed length %x, actual length is %x\n",
ClaimedLength + 2 + ByteCount, Length ));
goto Bad_Buffer ;
}
return STATUS_SUCCESS ;
Bad_Buffer:
return STATUS_DATA_ERROR ;
}
//+---------------------------------------------------------------------------
//
// Function: NegpFindPackage
//
// Synopsis: Scans the list of negotiable packages for a package id
//
// Arguments: [PackageId] --
//
// History: 8-13-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
PNEG_PACKAGE
NegpFindPackage(
ULONG_PTR PackageId )
{
PNEG_PACKAGE Scan;
NegReadLockList();
Scan = (PNEG_PACKAGE) NegPackageList.Flink ;
while ( Scan != (PNEG_PACKAGE) &NegPackageList )
{
if ( Scan->LsaPackage->dwPackageID == PackageId )
{
break;
}
Scan = (PNEG_PACKAGE) Scan->List.Flink ;
}
NegUnlockList();
if ( Scan != (PNEG_PACKAGE) &NegPackageList )
{
return( Scan );
}
return( NULL );
}
//+---------------------------------------------------------------------------
//
// Function: NegpFindPackageByName
//
// Synopsis: Scans the list of negotiable packages for a package name
//
// Arguments: [PackageId] --
//
// History: 8-13-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
PNEG_PACKAGE
NegpFindPackageByName(
PUNICODE_STRING PackageName )
{
PNEG_PACKAGE Scan;
NegReadLockList();
Scan = (PNEG_PACKAGE) NegPackageList.Flink ;
while ( Scan != (PNEG_PACKAGE) &NegPackageList )
{
if ( RtlEqualUnicodeString(
&Scan->LsaPackage->Name,
PackageName,
TRUE // case insensitive
))
{
break;
}
Scan = (PNEG_PACKAGE) Scan->List.Flink ;
}
NegUnlockList();
if ( Scan != (PNEG_PACKAGE) &NegPackageList )
{
return( Scan );
}
return( NULL );
}
//+---------------------------------------------------------------------------
//
// Function: NegpFindPackageByOid
//
// Synopsis: Locates a security package by OID.
//
// Arguments: [Id] --
//
// History: 4-23-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
PNEG_PACKAGE
NegpFindPackageByOid(
ObjectID Id
)
{
PNEG_PACKAGE Scan ;
PLIST_ENTRY List ;
ULONG i ;
NegReadLockList();
List = NegPackageList.Flink ;
Scan = NULL ;
while ( List != &NegPackageList )
{
Scan = CONTAINING_RECORD( List, NEG_PACKAGE, List );
if ( NegpCompareOid( Id, Scan->ObjectId ) == 0 )
{
break;
}
List = List->Flink ;
Scan = NULL ;
}
NegUnlockList();
return Scan ;
}
ULONG
NegGetPackageCaps(
ULONG ContextReq
)
{
ULONG PackageCap = 0;
PNEG_CONTEXT_REQ_MAP Scan ;
Scan = NegContextReqMap ;
while ( Scan->ContextReq )
{
if ( Scan->ConfigFlags & NEG_CONFIG_REQUIRED )
{
if ( (Scan->ContextReq & ContextReq ) != 0 )
{
PackageCap |= Scan->PackageFlag ;
}
}
else if ( Scan->ConfigFlags & NEG_CONFIG_OPTIONAL )
{
if ( NegNegotiationControl >= Scan->Level )
{
if ( ( Scan->ContextReq & ContextReq ) != 0 )
{
PackageCap |= Scan->PackageFlag ;
}
}
else
{
PackageCap |= 0 ;
}
}
Scan++ ;
}
return PackageCap ;
}
#ifndef WIN32_CHICAGO
//+---------------------------------------------------------------------------
//
// Function: NegPackageLoad
//
// Synopsis: Called by LSA whenever a package is loaded
//
// Arguments: [p] --
//
// History: 7-30-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD
WINAPI
NegPackageLoad(
PVOID p)
{
NTSTATUS Status ;
PSECPKG_EVENT_NOTIFY Notify;
PSECPKG_EVENT_PACKAGE_CHANGE Load;
PNEG_PACKAGE Package;
PNEG_PACKAGE ExtraPackage ;
PLSAP_SECURITY_PACKAGE LsaPackage;
PSECPKG_EXTENDED_INFORMATION Info ;
SECPKG_EXTENDED_INFORMATION LocalInfo ;
ObjectID ClaimedOid = NULL;
UCHAR Prefix[ NEGOTIATE_MAX_PREFIX ] = { 0 };
ULONG PrefixLength = 0 ;
ULONG ExtraOidsCount = 0 ;
ULONG i ;
BOOLEAN fKerberosPackage = FALSE;
Notify = (PSECPKG_EVENT_NOTIFY) p;
if ( Notify->EventClass != NOTIFY_CLASS_PACKAGE_CHANGE )
{
return( 0 );
}
Load = ( PSECPKG_EVENT_PACKAGE_CHANGE ) Notify->EventData ;
DebugLog((DEB_TRACE_NEG, "Package Change Event %d: %ws (%p) \n",
Load->ChangeType, Load->PackageName.Buffer, Load->PackageId ));
if ( Load->PackageId == NegPackageId )
{
DebugLog((DEB_TRACE_NEG, "Skipping own load notification\n"));
NegLsaPackage = SpmpLocatePackage( NegPackageId );
return( 0 );
}
//
// If this is a package load, add the package to our list:
//
if ( Load->ChangeType == SECPKG_PACKAGE_CHANGE_LOAD )
{
Info = NULL ;
LsaPackage = SpmpLookupPackage( &Load->PackageName );
if (LsaPackage == NULL)
{
return( 0 );
}
if ( !( LsaPackage->fCapabilities & SECPKG_FLAG_NEGOTIABLE ) )
{
return( 0 );
}
//
// If the package supports SP_INFO, query it to see if it has an
// OID to use in negotiation.
//
if ( LsaPackage->fPackage & SP_INFO )
{
Status = LsapGetExtendedPackageInfo( LsaPackage,
SecpkgGssInfo,
&Info );
if ( !NT_SUCCESS( Status ) )
{
Info = NULL ;
}
//
// Make sure that the claimed OID doesn't conflict with
// someone already loaded.
//
if ( Info )
{
ClaimedOid = NegpDecodeObjectId( Info->Info.GssInfo.EncodedId,
Info->Info.GssInfo.EncodedIdLength );
RtlCopyMemory( Prefix,
Info->Info.GssInfo.EncodedId,
Info->Info.GssInfo.EncodedIdLength );
PrefixLength = Info->Info.GssInfo.EncodedIdLength ;
//
// note whether the primary GSS Oid is the kerberos Oid.
// This allows us to swap the order of the Primary Oid with the legacy Oid
// later on.
//
if ( (Info->Info.GssInfo.EncodedIdLength == sizeof(NegKerberosOid)) &&
(memcmp(Info->Info.GssInfo.EncodedId, NegKerberosOid, sizeof(NegKerberosOid)) == 0 )
)
{
fKerberosPackage = TRUE;
}
LsapFreeLsaHeap( Info );
if (ClaimedOid == NULL)
{
Info = NULL;
}
else if ( NegpFindPackageByOid( ClaimedOid ) )
{
NegpFreeObjectId( ClaimedOid );
return 0 ;
}
}
//
// Check if the package has any additional OIDs to support, or to compensate
// for a spnego encoding problem.
//
Status = LsapGetExtendedPackageInfo( LsaPackage,
SecpkgExtraOids,
&Info );
if ( !NT_SUCCESS( Status ) )
{
Info = NULL ;
}
LocalInfo.Class = SecpkgMutualAuthLevel ;
LocalInfo.Info.MutualAuthLevel.MutualAuthLevel = NegNegotiationControl ;
LsapSetExtendedPackageInfo(
LsaPackage,
SecpkgMutualAuthLevel,
&LocalInfo );
}
//
// If no ID, and Info is NULL, skip it.
//
if ( (LsaPackage->dwRPCID == SECPKG_ID_NONE) &&
(ClaimedOid == NULL ) )
{
return( 0 );
}
if ( ( Info != NULL ) &&
( Info->Class == SecpkgExtraOids ) )
{
ExtraOidsCount = Info->Info.ExtraOids.OidCount ;
}
Package = (PNEG_PACKAGE) LsapAllocateLsaHeap( sizeof( NEG_PACKAGE ) );
if ( Package )
{
Package->LsaPackage = LsaPackage ;
Package->Flags = NEG_PACKAGE_INBOUND | NEG_PACKAGE_OUTBOUND ;
if ( LsaPackage->fPackage & SP_PREFERRED )
{
Package->Flags |= NEG_PREFERRED ;
}
if ( LsaPackage->dwRPCID == RPC_C_AUTHN_WINNT )
{
Package->Flags |= NEG_NT4_COMPAT ;
NtlmPackageId = Load->PackageId;
//
// Cheap and sleazy way of loading the necessary information
// into the logon session, once we have all the packages loaded.
//
NegLsaPolicyChangeCallback( PolicyNotifyDnsDomainInformation );
}
Package->TokenSize = LsaPackage->TokenSize ;
Package->PackageFlags = LsaPackage->fCapabilities ;
Package->PrefixLen = PrefixLength ;
RtlCopyMemory( Package->Prefix,
Prefix,
PrefixLength );
//
// add slack space for negotiate header.
//
NegLsaPackage->TokenSize = max( (LsaPackage->TokenSize+128),
NegLsaPackage->TokenSize );
DebugLog(( DEB_TRACE_NEG, "Loaded package %ws\n",
Load->PackageName.Buffer ));
NegWriteLockList();
if ( ClaimedOid )
{
Package->ObjectId = ClaimedOid ;
NegDumpOid( "Package claimed OID", Package->ObjectId );
}
else
{
NegMSMechanismsOid[ 0xb ] = (UCHAR) LsaPackage->dwRPCID ;
Package->ObjectId = NegpDecodeObjectId( NegMSMechanismsOid,
sizeof( NegMSMechanismsOid ) );
NegDumpOid( "Assigned package OID", Package->ObjectId );
if ( Package->Flags & NEG_NT4_COMPAT )
{
NegNtlmMechOid = NegpDecodeObjectId( NegMSMechanismsOid,
sizeof( NegMSMechanismsOid ) );
}
}
InsertTailList( &NegPackageList,
&Package->List );
NegPackageCount ++;
if ( ExtraOidsCount )
{
Package->Flags |= NEG_PACKAGE_HAS_EXTRAS ;
DebugLog(( DEB_TRACE_NEG, "Creating extra packages for %ws\n",
Load->PackageName.Buffer ));
for ( i = 0 ; i < ExtraOidsCount ; i++ )
{
ClaimedOid = NegpDecodeObjectId( Info->Info.ExtraOids.Oids[ i ].OidValue,
Info->Info.ExtraOids.Oids[ i ].OidLength );
if ( !NegpFindPackageByOid( ClaimedOid ))
{
//
// If no one else has used this OID, allow it.
//
NegDumpOid( "Package claimed extra OID", ClaimedOid );
ExtraPackage = (PNEG_PACKAGE) LsapAllocateLsaHeap( sizeof( NEG_PACKAGE ) );
if ( ExtraPackage )
{
ExtraPackage->LsaPackage = Package->LsaPackage ;
ExtraPackage->ObjectId = ClaimedOid ;
ExtraPackage->RealPackage = Package ;
ExtraPackage->Flags = NEG_PACKAGE_EXTRA_OID ;
if ( Info->Info.ExtraOids.Oids[ i ].OidAttributes & SECPKG_CRED_INBOUND )
{
ExtraPackage->Flags |= NEG_PACKAGE_INBOUND ;
}
if ( Info->Info.ExtraOids.Oids[ i ].OidAttributes & SECPKG_CRED_OUTBOUND )
{
ExtraPackage->Flags |= NEG_PACKAGE_OUTBOUND ;
}
ExtraPackage->TokenSize = Package->TokenSize ;
ExtraPackage->PackageFlags = Package->PackageFlags ;
RtlCopyMemory(
ExtraPackage->Prefix,
Info->Info.ExtraOids.Oids[ i ].OidValue,
Info->Info.ExtraOids.Oids[ i ].OidLength );
ExtraPackage->PrefixLen = Info->Info.ExtraOids.Oids[ i ].OidLength ;
//
// **** NOTE: ****
// For legacy compatibility reasons,
// the broken Kerberos package Oid is re-ordered ahead
// of the correct Oid value. The was done in order
// to avoid an extra round-trip when communicating with
// Win2000 machines. The negotiate protocol itself
// recovers with additional round-trips; however,
// Wininet is unable to reliably handle such a
// circumstance.
//
if( fKerberosPackage &&
(Info->Info.ExtraOids.Oids[ i ].OidLength == sizeof(NegKerberosLegacyOid)) &&
(memcmp(Info->Info.ExtraOids.Oids[ i ].OidValue, NegKerberosLegacyOid, sizeof(NegKerberosLegacyOid)) == 0)
)
{
ObjectID SwapOid;
DebugLog((DEB_TRACE_NEG, "Re-ordering legacy Kerberos Oid\n"));
SwapOid = Package->ObjectId;
Package->ObjectId = ExtraPackage->ObjectId;
ExtraPackage->ObjectId = SwapOid;
}
InsertTailList( &NegPackageList,
&ExtraPackage->List );
NegPackageCount ++;
}
else
{
//
// Free the OID, skip this extra package
//
NegpFreeObjectId( ClaimedOid );
}
}
else
{
//
// Free it
//
NegpFreeObjectId( ClaimedOid );
}
}
}
NegUnlockList();
}
}
else
{
//
// It's either a select or an unload:
//
Package = NegpFindPackage( Load->PackageId );
//
// if we don't have this package (it may not have been negotiable)
// return now.
//
if (Package == NULL)
{
return(0);
}
if ( Load->ChangeType == SECPKG_PACKAGE_CHANGE_SELECT )
{
Package->Flags |= NEG_PREFERRED ;
}
else
{
NegWriteLockList();
RemoveEntryList( &Package->List );
NegUnlockList();
NegpFreeObjectId( Package->ObjectId );
LsapFreeLsaHeap( Package );
}
}
return( 0 );
}
#else // WIN32_CHICAGO
//+---------------------------------------------------------------------------
//
// Function: NegPackageLoad
//
// Synopsis: Called by Negotiate package load only once
//
// Arguments: [p] --
//
// History: 10-02-97 ChandanS Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD
WINAPI
NegPackageLoad(
BOOLEAN fLoad,
DWORD dwPackageID,
LPTSTR Name,
DWORD fCapabilities,
DWORD fPackage,
DWORD dwRPCID,
DWORD TokenSize
)
{
NTSTATUS Status = STATUS_SUCCESS;
PNEG_PACKAGE Package;
PLSAP_SECURITY_PACKAGE LsaPackage;
//
// If this is a package load, add the package to our list:
//
DebugLog(( DEB_TRACE_NEG, "NegPackageLoad called for fLoad %d, dwPackageID %d, Name %d, fCapabilities 0x%x, dwRPCID %d, TokenSize %d\n", fLoad, dwPackageID, Name, fCapabilities, dwRPCID, TokenSize));
if ( fLoad )
{
DebugLog(( DEB_TRACE_NEG, "package %s is being loaded\n", Name));
Package = (PNEG_PACKAGE) LsapAllocateLsaHeap( sizeof( NEG_PACKAGE ) );
if ( Package )
{
LsaPackage = (PLSAP_SECURITY_PACKAGE) LsapAllocateLsaHeap( sizeof( LSAP_SECURITY_PACKAGE ) );
if ( !LsaPackage )
{
LsapFreeLsaHeap( Package );
DebugLog(( DEB_TRACE_NEG, "package %s was not loaded\n", Name));
return SEC_E_INSUFFICIENT_MEMORY;
}
DebugLog(( DEB_TRACE_NEG, "package %s is NOW loaded\n", Name));
LsaPackage->dwPackageID = dwPackageID;
LsaPackage->fCapabilities = fCapabilities;
LsaPackage->fPackage = fPackage;
LsaPackage->dwRPCID = dwRPCID;
LsaPackage->TokenSize = TokenSize;
Status = RtlCreateUnicodeStringFromAsciiz(
&LsaPackage->Name,
Name
);
Package->LsaPackage = LsaPackage ;
Package->TokenSize = LsaPackage->TokenSize ;
DebugLog(( DEB_TRACE_NEG, "Loaded package %ws\n",
LsaPackage->Name.Buffer ));
NegWriteLockList();
NegMSMechanismsOid[ 0xb ] = (UCHAR) LsaPackage->dwRPCID ;
Package->ObjectId = NegpDecodeObjectId( NegMSMechanismsOid,
sizeof( NegMSMechanismsOid ) );
NegDumpOid( "Assigned package OID", Package->ObjectId );
InsertTailList( &NegPackageList,
&Package->List );
NegPackageCount ++;
NegUnlockList();
}
else
{
DebugLog(( DEB_TRACE_NEG, "package %s was never loaded\n", Name));
Status = SEC_E_INSUFFICIENT_MEMORY;
}
}
else
{
DebugLog(( DEB_TRACE_NEG, "package %s is being unloaded\n", Name));
//
// It's either a select or an unload:
//
Package = NegpFindPackage( dwPackageID );
//
// if we don't have this package (it may not have been negotiable)
// return now.
//
if (Package == NULL)
{
return(0);
}
NegWriteLockList();
RemoveEntryList( &Package->List );
NegUnlockList();
NegpFreeObjectId( Package->ObjectId );
LsapFreeLsaHeap( Package );
}
return( Status );
}
#endif // WIN32_CHICAGO
//+---------------------------------------------------------------------------
//
// Function: NegpParseBuffers
//
// Synopsis: Parse out juicy bits
//
// Arguments: [pMessage] --
// [pToken] --
// [pEmpty] --
//
// History: 8-19-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
NegpParseBuffers(
PSecBufferDesc pMessage,
BOOL Map,
PSecBuffer * pToken,
PSecBuffer * pEmpty)
{
ULONG i;
SECURITY_STATUS scRet ;
PSecBuffer pFirstBlank = NULL;
PSecBuffer pWholeMessage = NULL;
PSecBuffer pFirstToken = NULL;
scRet = SEC_E_OK ;
for (i = 0 ; i < pMessage->cBuffers ; i++ )
{
if ( (pMessage->pBuffers[i].BufferType & (~SECBUFFER_ATTRMASK)) == SECBUFFER_TOKEN )
{
pWholeMessage = &pMessage->pBuffers[i];
if ( pFirstToken == NULL )
{
pFirstToken = pWholeMessage;
}
if ( Map )
{
scRet = LsapMapClientBuffer( pWholeMessage, pWholeMessage );
}
if (pFirstBlank)
{
break;
}
}
else if ( (pMessage->pBuffers[i].BufferType & (~SECBUFFER_ATTRMASK)) == SECBUFFER_EMPTY )
{
pFirstBlank = &pMessage->pBuffers[i];
if (pWholeMessage)
{
break;
}
}
}
if (pToken)
{
// *pToken = pWholeMessage;
//
// NTBUG: 405976
// down-level RDR supplies 2 SECBUFFER_TOKEN buffers, the second
// one containing creds. Insure we return the first one.
//
*pToken = pFirstToken;
}
if (pEmpty)
{
*pEmpty = pFirstBlank;
}
return( scRet );
}
//+---------------------------------------------------------------------------
//
// Function: NegInitialize
//
// Synopsis: Initialize the built in negotiate package
//
// Arguments: [dwProtocol] --
// [dwPackageID] --
// [pParameters] --
// [pPkgFunctions] --
//
// History: 7-26-96 RichardW Created
//
// Notes: This package must be the last package, since it must query
// the others to find out their capabilities.
//
//----------------------------------------------------------------------------
SECURITY_STATUS
SEC_ENTRY
NegInitialize(
ULONG_PTR dwPackageID,
PSECPKG_PARAMETERS pParameters,
PLSA_SECPKG_FUNCTION_TABLE Table)
{
HKEY LsaKey ;
int err ;
DWORD size ;
DWORD type ;
NTSTATUS Status ;
NegPackageId = dwPackageID;
#ifndef WIN32_CHICAGO
Status = RtlInitializeCriticalSection( &NegComputerNamesLock );
if ( !NT_SUCCESS( Status ) )
{
return Status ;
}
NegNotifyHandle = LsaIRegisterNotification( NegPackageLoad,
0,
NOTIFIER_TYPE_NOTIFY_EVENT,
NOTIFY_CLASS_PACKAGE_CHANGE,
0,
0,
0 );
InitializeListHead( &NegDefaultCredList );
InitializeListHead( &NegLoopbackList );
InitializeListHead( &NegLogonSessionList );
Status = RtlInitializeCriticalSection( &NegLogonSessionListLock );
if ( !NT_SUCCESS( Status ) )
{
return Status ;
}
Status = RtlInitializeCriticalSection( &NegTrustListLock );
if ( !NT_SUCCESS( Status ) )
{
return Status ;
}
#endif // WIN32_CHICAGO
RtlInitUnicodeString( &NegLocalHostName_U, NegLocalHostName );
__try
{
RtlInitializeResource( &NegLock );
Status = STATUS_SUCCESS ;
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
Status = GetExceptionCode();
}
if ( !NT_SUCCESS( Status ) )
{
return Status ;
}
InitializeListHead( &NegPackageList );
#ifndef WIN32_CHICAGO
__try
{
RtlInitializeResource( &NegCredListLock );
Status = STATUS_SUCCESS ;
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
Status = GetExceptionCode();
}
if ( !NT_SUCCESS( Status ) )
{
return Status ;
}
#else
Status = RtlInitializeCriticalSection( &NegCredListLock );
if ( !NT_SUCCESS( Status ) )
{
return Status ;
}
#endif
InitializeListHead( &NegCredList );
NegPackageCount = 0;
NegSpnegoMechOid = NegpDecodeObjectId(NegSpnegoMechEncodedOid, sizeof(NegSpnegoMechEncodedOid));
if (NegSpnegoMechOid == NULL)
{
return(STATUS_INSUFFICIENT_RESOURCES);
}
NegMachineState = pParameters->MachineState;
if ( ( pParameters->DomainSid != NULL ) &&
( pParameters->DnsDomainName.Length != 0 ) )
{
NegUplevelDomain = TRUE ;
}
#ifndef WIN32_CHICAGO
//
// Ignore the status - if we don't get a callback, then we can't support
// dynamic domain change. If it succeeds, then great.
//
LsaIRegisterPolicyChangeNotificationCallback(
NegLsaPolicyChangeCallback,
PolicyNotifyDnsDomainInformation );
LsaIRegisterNotification( NegParamChange,
0,
NOTIFIER_TYPE_NOTIFY_EVENT,
NOTIFY_CLASS_REGISTRY_CHANGE,
0,
0,
0 );
//
// get the product type for loopback logic.
//
if (!RtlGetNtProductType( &NegProductType ) )
{
NegProductType = NtProductWinNt;
}
#endif
#ifdef WIN32_CHICAGO
err = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
TEXT("System\\CurrentControlSet\\Control\\SecurityProviders\\Negotiate"),
0,
KEY_READ,
&LsaKey );
#else
err = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
TEXT("System\\CurrentControlSet\\Control\\Lsa"),
0,
KEY_READ,
&LsaKey );
#endif
if ( err == 0 )
{
NegpReadRegistryParameters( LsaKey );
RegCloseKey( LsaKey );
}
return(S_OK);
}
VOID
NegpReadRegistryParameters(
HKEY LsaKey
)
{
DWORD size ;
DWORD type ;
int err ;
//
// These values are not MP sensitive, so we just blast the new value
// into them.
//
size = sizeof( DWORD );
err = RegQueryValueEx(
LsaKey,
TEXT("NegotiationLevel"),
NULL,
&type,
(LPBYTE) &NegNegotiationControl,
&size );
if ( err != 0 )
{
NegNegotiationControl = NEG_NEGLEVEL_COMPATIBILITY ;
}
#ifndef WIN32_CHICAGO
size = sizeof( DWORD );
err = RegQueryValueEx(
LsaKey,
TEXT("NegotiationLogLevel"),
NULL,
&type,
(LPBYTE) &NegEventLogLevel,
&size );
if ( err )
{
NegEventLogLevel = (1 << EVENTLOG_ERROR_TYPE ) |
(1 << EVENTLOG_WARNING_TYPE ) ;
}
#endif
}
//+---------------------------------------------------------------------------
//
// Function: NegGetInfo
//
// Synopsis: Negotiate Package GetInfo call
//
// Arguments: [pInfo] --
//
// History: 7-26-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
SEC_ENTRY
NegGetInfo(PSecPkgInfo pInfo)
{
pInfo->wVersion = 1;
pInfo->wRPCID = NEGOSSP_RPCID ;
pInfo->fCapabilities = SECPKG_FLAG_INTEGRITY |
SECPKG_FLAG_PRIVACY |
SECPKG_FLAG_CONNECTION |
SECPKG_FLAG_MULTI_REQUIRED |
SECPKG_FLAG_EXTENDED_ERROR |
SECPKG_FLAG_IMPERSONATION |
SECPKG_FLAG_ACCEPT_WIN32_NAME |
SECPKG_FLAG_NEGOTIABLE |
SECPKG_FLAG_GSS_COMPATIBLE |
SECPKG_FLAG_LOGON;
pInfo->cbMaxToken = 500;
pInfo->Name = NEGOSSP_NAME;
pInfo->Comment = NegPackageComment;
return(S_OK);
}
//+---------------------------------------------------------------------------
//
// Function: NegAcceptCredentials
//
// Synopsis: Notification of a logon
//
// Arguments: [LogonType] --
// [UserName] --
// [PrimaryCred] --
// [SupplementalCreds] --
//
// History: 7-30-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY
NegAcceptCredentials(
IN SECURITY_LOGON_TYPE LogonType,
IN PUNICODE_STRING UserName,
IN PSECPKG_PRIMARY_CRED PrimaryCred,
IN PSECPKG_SUPPLEMENTAL_CRED SupplementalCreds)
{
#ifndef WIN32_CHICAGO
LUID SystemId = SYSTEM_LUID;
NTSTATUS Status;
if (RtlEqualLuid(&PrimaryCred->LogonId, &SystemId))
{
//
// Stash away the LocalSystem credentials to use
// for NetworkService logons later on.
//
Status = NegpCopyCredsToBuffer(PrimaryCred,
NULL,
&NegPrimarySystemCredentials,
NULL);
return Status;
}
else if (LogonType == Service)
{
LUID LocalServiceId = LOCALSERVICE_LUID;
LUID NetworkServiceId = NETWORKSERVICE_LUID;
//
// Notify the network providers of the logon. Don't notify
// for SYSTEM, LocalService, or NetworkService.
//
if (!RtlEqualLuid(&PrimaryCred->LogonId, &LocalServiceId)
&&
!RtlEqualLuid(&PrimaryCred->LogonId, &NetworkServiceId))
{
NegpNotifyNetworkProviders(UserName, PrimaryCred);
}
}
#endif // WIN32_CHICAGO
return SEC_E_OK;
}
#ifndef WIN32_CHICAGO
//+-------------------------------------------------------------------------
//
// Function: NegpNotifyNetworkProviders
//
// Synopsis: Notifies network providers of a logon
//
// Effects:
//
// Arguments: [UserName] --
// [PrimaryCred] --
//
// Requires:
//
// Returns: Nothing since this is an advisory service to
// other network providers.
//
// Notes:
//
//--------------------------------------------------------------------------
VOID
NegpNotifyNetworkProviders(
IN PUNICODE_STRING UserName,
IN PSECPKG_PRIMARY_CRED PrimaryCred
)
{
MSV1_0_INTERACTIVE_LOGON OldLogon;
MSV1_0_INTERACTIVE_LOGON NewLogon;
static HMODULE s_hMprDll = NULL;
PLOGON_NOTIFY pfLogonNotify = NULL;
LPWSTR lpLogonScripts = NULL;
DWORD dwStatus;
if (s_hMprDll == NULL)
{
s_hMprDll = LoadLibrary(L"mpr.dll");
}
if (s_hMprDll != NULL)
{
pfLogonNotify = (PLOGON_NOTIFY) GetProcAddress(s_hMprDll,
"WNetLogonNotify");
if (pfLogonNotify != NULL)
{
NewLogon.MessageType = MsV1_0InteractiveLogon;
NewLogon.LogonDomainName = PrimaryCred->DomainName;
NewLogon.UserName = *UserName;
NewLogon.Password = PrimaryCred->Password;
RtlCopyMemory(&OldLogon, &NewLogon, sizeof(NewLogon));
dwStatus = pfLogonNotify(L"Windows NT Network Provider",
&PrimaryCred->LogonId,
L"MSV1_0:Interactive",
&NewLogon,
L"MSV1_0:Interactive",
&OldLogon,
L"SvcCtl", // StationName
NULL, // StationHandle
&lpLogonScripts); // LogonScripts
if (dwStatus == NO_ERROR)
{
LocalFree(lpLogonScripts);
}
}
}
}
#endif // WIN32_CHICAGO
//+-------------------------------------------------------------------------
//
// Function: NegpCopyCredsToBuffer
//
// Synopsis: Copies primary and supplemental creds into the supplied
// buffers
//
// Effects:
//
// Arguments: [PrimaryCred] --
// [SupplementalCred] --
// [PrimaryCredCopy] --
// [SupplementalCredCopy] --
//
// Requires:
//
// Returns:
//
// Notes: Leaves the SID and LUID blank. It is the caller's
// responsibility to fill these fields in.
//
//--------------------------------------------------------------------------
NTSTATUS
NegpCopyCredsToBuffer(
IN PSECPKG_PRIMARY_CRED PrimaryCred,
IN PSECPKG_SUPPLEMENTAL_CRED SupplementalCred,
OUT PSECPKG_PRIMARY_CRED PrimaryCredCopy OPTIONAL,
OUT PSECPKG_SUPPLEMENTAL_CRED SupplementalCredCopy OPTIONAL
)
{
NTSTATUS Status;
if (PrimaryCredCopy)
{
Status = LsapDuplicateString(&PrimaryCredCopy->DomainName,
&PrimaryCred->DomainName);
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
Status = LsapDuplicateString(&PrimaryCredCopy->DownlevelName,
&PrimaryCred->DownlevelName);
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
Status = LsapDuplicateString(&PrimaryCredCopy->Password,
&PrimaryCred->Password);
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
PrimaryCredCopy->Flags = PRIMARY_CRED_CLEAR_PASSWORD;
}
if (SupplementalCredCopy)
{
Status = LsapDuplicateString(&SupplementalCredCopy->PackageName,
&SupplementalCred->PackageName);
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
SupplementalCredCopy->Credentials = (PUCHAR) (SupplementalCredCopy + 1);
RtlCopyMemory(SupplementalCredCopy,
SupplementalCred->Credentials,
SupplementalCred->CredentialSize);
}
return( SEC_E_OK );
ErrorExit:
if (PrimaryCredCopy)
{
LsapFreeLsaHeap(PrimaryCredCopy->DomainName.Buffer);
LsapFreeLsaHeap(PrimaryCredCopy->DownlevelName.Buffer);
LsapFreeLsaHeap(PrimaryCredCopy->Password.Buffer);
}
return( STATUS_NO_MEMORY );
}
//+-------------------------------------------------------------------------
//
// Function: NegpCaptureSuppliedCreds
//
// Synopsis: Captures a SEC_WINNT_AUTH_IDENTITY_EX structure from
// the client
//
// Effects:
//
// Arguments: AuthorizationData - Client address of auth data
// PackageList - List of packages from the auth data.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
NegpCaptureSuppliedCreds(
IN PVOID AuthorizationData,
OUT PNEG_PACKAGE ** ReturnedPackageList,
OUT PULONG ReturnedPackageCount,
OUT PBOOL ExplicitCreds,
OUT PBOOL DomainExplicitCreds
)
{
NTSTATUS Status = STATUS_SUCCESS;
SEC_WINNT_AUTH_IDENTITY_EXW IdentityEx = {0};
SEC_WINNT_AUTH_IDENTITY_W * Identity;
PSTR PackageList = NULL;
UNICODE_STRING PackageString = {0};
ULONG PackageListLength;
ULONG CharSize = sizeof(WCHAR);
ULONG Index;
ULONG PackageCount;
ULONG PackageIndex;
ULONG ExclusionIndex ;
ULONG FinalIndex ;
ULONG PossiblePackageCount ;
ULONG i, j;
PNEG_PACKAGE * LocalPackageList = NULL;
PNEG_PACKAGE * ExclusionList = NULL ;
PNEG_PACKAGE * FinalList = NULL ;
PNEG_PACKAGE Package ;
PLIST_ENTRY List ;
PNEG_PACKAGE PackageScan ;
UNICODE_STRING TempString;
PWSTR Scan, EndPoint, Comma ;
*ReturnedPackageList = NULL;
*ReturnedPackageCount = 0;
*ExplicitCreds = FALSE ;
*DomainExplicitCreds = FALSE ;
//
// First capture the base structure
//
Status = LsapCopyFromClientBuffer(
NULL,
sizeof(SEC_WINNT_AUTH_IDENTITY_W),
&IdentityEx,
AuthorizationData
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to copy auth data from %p client address: 0x%x\n",
AuthorizationData, Status ));
*ExplicitCreds = TRUE ;
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// Check if this is the right structure
//
if (IdentityEx.Version != SEC_WINNT_AUTH_IDENTITY_VERSION)
{
Identity = (PSEC_WINNT_AUTH_IDENTITY_W) &IdentityEx ;
if ( (Identity->UserLength > 0 ) ||
(Identity->DomainLength > 0 ) ||
(Identity->PasswordLength > 0 ||
Identity->Password != NULL) )
{
*ExplicitCreds = TRUE ;
if( Identity->DomainLength )
{
*DomainExplicitCreds = TRUE;
}
}
goto Cleanup;
}
//
// Copy the whole data structure now
//
Status = LsapCopyFromClientBuffer(
NULL,
sizeof(SEC_WINNT_AUTH_IDENTITY_EXW),
&IdentityEx,
AuthorizationData
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to copy auth data from %p client address: 0x%x\n",
AuthorizationData, Status ));
*ExplicitCreds = TRUE ;
//
// Mask this error, as it may have been data for another package.
//
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// Check to see if this contains a list of packages
//
//
if (IdentityEx.Flags & SEC_WINNT_AUTH_IDENTITY_ANSI)
{
CharSize = sizeof(CHAR);
}
//
// If there was no packge list in the data, return now.
//
if ( ( IdentityEx.UserLength > 0 ) ||
( IdentityEx.DomainLength > 0 ) ||
( IdentityEx.PasswordLength > 0 ||
IdentityEx.Password != NULL ) )
{
*ExplicitCreds = TRUE ;
if( IdentityEx.DomainLength )
{
*DomainExplicitCreds = TRUE;
}
}
if ((IdentityEx.Length < sizeof(SEC_WINNT_AUTH_IDENTITY_EXW)) ||
(IdentityEx.PackageList == NULL) ||
(IdentityEx.PackageListLength == 0))
{
Status = STATUS_SUCCESS;
goto Cleanup;
}
if ( (IdentityEx.PackageListLength + 1) * CharSize < IdentityEx.PackageListLength )
{
//
// Passed size is too large (we rolled over)
//
Status = STATUS_INVALID_PARAMETER ;
goto Cleanup ;
}
//
// Capture the package list itself
//
PackageList = (PSTR) LsapAllocateLsaHeap(CharSize * (IdentityEx.PackageListLength + 1));
if (PackageList == NULL)
{
Status = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
Status = LsapCopyFromClientBuffer(
NULL,
CharSize * IdentityEx.PackageListLength,
PackageList,
IdentityEx.PackageList
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to copy package list\n"));
goto Cleanup;
}
//
// Conver the package list into a useable form, including changing
// character sets.
//
if (CharSize == sizeof(CHAR))
{
((LPSTR)PackageList)[IdentityEx.PackageListLength] = '\0';
if ( !RtlCreateUnicodeStringFromAsciiz(
&PackageString,
PackageList
) )
{
goto Cleanup;
}
}
else
{
((LPWSTR)PackageList)[IdentityEx.PackageListLength] = L'\0';
RtlInitUnicodeString(
&PackageString,
(LPWSTR) PackageList
);
PackageList = NULL;
}
//
// Scan through counting for ',' separators to get a count of packages.
//
PackageCount = 1;
for (Index = 0; Index < PackageString.Length / sizeof(WCHAR) ; Index++ )
{
if (PackageString.Buffer[Index] == L',')
{
PackageCount++;
}
}
//
// If there was nothing in the list, continue as if it wasn't there
//
if (PackageCount == 0)
{
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// Allocate the package list
//
LocalPackageList = (PNEG_PACKAGE *) LsapAllocateLsaHeap( PackageCount * sizeof( PNEG_PACKAGE ) );
if (LocalPackageList == NULL)
{
Status = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
ExclusionList = (PNEG_PACKAGE *) LsapAllocateLsaHeap( PackageCount * sizeof( PNEG_PACKAGE ) );
if ( ExclusionList == NULL )
{
Status = SEC_E_INSUFFICIENT_MEMORY ;
goto Cleanup ;
}
//
// Now go through the string of packages and build the list
//
PackageIndex = 0;
ExclusionIndex = 0;
TempString.Buffer = PackageString.Buffer;
TempString.Length = 0;
TempString.MaximumLength = PackageString.MaximumLength;
Index = 0;
Scan = PackageString.Buffer ;
EndPoint = Scan + (PackageString.Length / sizeof( WCHAR ));
while ( Scan < EndPoint )
{
Comma = wcschr( Scan, L',' );
if ( Comma )
{
*Comma = L'\0' ;
}
if ( *Scan == L'!' )
{
//
// This entry is an exclusion. Skip past the ! char,
// and try to find a package
//
Scan++ ;
if ( Scan != Comma )
{
RtlInitUnicodeString( &TempString, Scan );
ExclusionList[ ExclusionIndex ] = NegpFindPackageByName( &TempString );
if ( ExclusionList[ ExclusionIndex ] != NULL )
{
ExclusionIndex++ ;
}
}
}
else
{
//
// This entry is a request. Try to find the package
//
RtlInitUnicodeString( &TempString, Scan );
LocalPackageList[ PackageIndex ] = NegpFindPackageByName( &TempString );
if ( LocalPackageList[ PackageIndex ] != NULL )
{
PackageIndex++ ;
}
}
if ( Comma )
{
*Comma = L',';
Scan = Comma + 1 ;
}
else
{
Scan = EndPoint ;
}
}
//
// Now, we have two lists. We have an ExclusionList, of packages that the caller
// does not want, and a package list, a list of things that the caller does want.
// Merge the list according to the requests
//
PossiblePackageCount = NegPackageCount ;
FinalIndex = 0 ;
FinalList = (PNEG_PACKAGE *) LsapAllocateLsaHeap( PossiblePackageCount * sizeof( PNEG_PACKAGE ) );
if ( FinalList == NULL )
{
Status = SEC_E_INSUFFICIENT_MEMORY ;
goto Cleanup ;
}
for ( i = 0 ; i < PackageIndex ; i++ )
{
//
// Pick a package off the request list
//
Package = LocalPackageList[ i ];
//
// Scan through the exclusion list, see if we need to skip it.
//
for ( j = 0 ; j < ExclusionIndex ; j++ )
{
if ( Package == ExclusionList[ j ] )
{
break;
}
}
if ( j < ExclusionIndex )
{
//
// if we broke out of the loop, we found this on the exclusion list.
// skip it by continuing the for-i loop.
//
continue;
}
//
// Ok, this package is not excluded. So add it to the final list
//
FinalList[ FinalIndex ] = Package;
FinalIndex++ ;
//
// See if this package has "extra" packages by the same name with
// other OIDs associated with it
//
if ( (Package->Flags & NEG_PACKAGE_HAS_EXTRAS) != 0 )
{
//
// Ok, there are a set of packages associated with this package. Walk the
// package list, and stick the extras into this one.
//
NegReadLockList();
List = NegPackageList.Flink ;
while ( List != &NegPackageList )
{
PackageScan = CONTAINING_RECORD( List, NEG_PACKAGE, List );
if ( PackageScan->RealPackage == Package )
{
FinalList[ FinalIndex ] = PackageScan ;
FinalIndex++ ;
}
List = List->Flink ;
}
NegUnlockList();
}
}
if ( (PackageIndex == 0) &&
(ExclusionIndex != 0 ) )
{
//
// Only an exclusion list was provided. Walk all the packages,
// and add those that are not excluded.
//
NegReadLockList();
List = NegPackageList.Flink ;
while ( List != &NegPackageList )
{
PackageScan = CONTAINING_RECORD( List, NEG_PACKAGE, List );
if ( ( PackageScan->Flags & NEG_PACKAGE_EXTRA_OID ) != 0 )
{
Package = PackageScan->RealPackage ;
}
else
{
Package = PackageScan ;
}
for ( i = 0 ; i < ExclusionIndex ; i++ )
{
if ( Package == ExclusionList[ i ] )
{
break;
}
}
if ( i < ExclusionIndex )
{
continue;
}
FinalList[ FinalIndex ] = PackageScan ;
FinalIndex++ ;
}
NegUnlockList();
}
//
// If no packages succeeded, return an error
//
if (PackageIndex == 0)
{
Status = SEC_E_SECPKG_NOT_FOUND;
goto Cleanup;
}
*ReturnedPackageCount = FinalIndex;
*ReturnedPackageList = FinalList;
FinalList = NULL;
Cleanup:
if (PackageList != NULL)
{
LsapFreeLsaHeap(PackageList);
if (PackageString.Buffer != NULL)
{
RtlFreeUnicodeString( &PackageString );
}
}
else
{
if (PackageString.Buffer != NULL)
{
LsapFreeLsaHeap(PackageString.Buffer);
}
}
if (LocalPackageList != NULL)
{
LsapFreeLsaHeap(LocalPackageList);
}
if ( ExclusionList != NULL )
{
LsapFreeLsaHeap( ExclusionList );
}
if ( FinalList != NULL )
{
LsapFreeLsaHeap( FinalList );
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: NegpBuildPackageList
//
// Synopsis: Builds the list of packages for the caller
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
NegpBuildPackageList(
IN ULONG_PTR LogonPackageId,
IN ULONG fCredentials,
OUT PNEG_PACKAGE ** ReturnedPackageList,
OUT PULONG ReturnedPackageCount
)
{
NTSTATUS Status = STATUS_SUCCESS;
PNEG_PACKAGE Package;
PNEG_PACKAGE ClientNegPackage = NULL;
PNEG_PACKAGE *PackageList = NULL;
PLIST_ENTRY Scan ;
ULONG PackageIndex = 0;
ULONG PackageMask ;
*ReturnedPackageList = NULL;
*ReturnedPackageCount = 0;
PackageList = (PNEG_PACKAGE *) LsapAllocateLsaHeap(NegPackageCount * sizeof(PNEG_PACKAGE));
if (PackageList == NULL)
{
Status = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
//
// Find the client's logon package
//
Scan = NegPackageList.Flink ;
Package = (PNEG_PACKAGE) NegPackageList.Flink ;
while ( Scan != &NegPackageList )
{
Package = CONTAINING_RECORD( Scan, NEG_PACKAGE, List );
if (Package->LsaPackage->dwPackageID == LogonPackageId)
{
ClientNegPackage = Package;
break;
}
Scan = Scan->Flink ;
}
//
// Compute a mask of package flags to use as part of the selection
// process. This is currently based on the credential use flags passed in
//
PackageMask = 0 ;
if ( fCredentials & SECPKG_CRED_INBOUND )
{
PackageMask |= NEG_PACKAGE_INBOUND ;
}
if ( fCredentials & SECPKG_CRED_OUTBOUND )
{
PackageMask |= NEG_PACKAGE_OUTBOUND ;
}
//
// Build the list of packages, with the logon package first
//
if ( ClientNegPackage )
{
PackageList[ PackageIndex ] = ClientNegPackage ;
PackageIndex = 1;
}
Scan = NegPackageList.Flink ;
while ( Scan != &NegPackageList )
{
Package = CONTAINING_RECORD( Scan, NEG_PACKAGE, List );
//
// ClientNegPackage has already been processed, skip it
//
if ( Package != ClientNegPackage )
{
//
// Make sure that the package flags support the request.
//
if ( (Package->Flags & PackageMask ) == PackageMask )
{
PackageList[PackageIndex] = Package;
PackageIndex++;
}
}
Scan = Scan->Flink ;
}
*ReturnedPackageList = PackageList;
PackageList = NULL;
*ReturnedPackageCount = PackageIndex;
Cleanup:
if (PackageList != NULL)
{
LsapFreeLsaHeap(PackageList);
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: NegpCheckForDuplicateCreds
//
// Synopsis: Check to see if this is a duplicate of another
// credential
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
BOOLEAN
NegpCheckForDuplicateCreds(
IN PNEG_CREDS * Credential
)
{
PLIST_ENTRY Next;
PNEG_CREDS MatchCred;
PNEG_CREDS LocalCred = *Credential;
NegReadLockCredList();
for (Next = NegCredList.Flink; Next != &NegCredList; Next = Next->Flink )
{
MatchCred = CONTAINING_RECORD(
Next,
NEG_CREDS,
List
);
if(!( MatchCred->ClientProcessId == LocalCred->ClientProcessId ) )
{
continue;
}
if(!RtlEqualLuid( &MatchCred->ClientLogonId, &LocalCred->ClientLogonId ) )
{
continue;
}
//
// Check if this credential has the same credentials
// as the one we just created. Make sure they are both
// from user or kernel mode (don't mix'n'match)
//
if ( ( MatchCred->Count == LocalCred->Count ) &&
( MatchCred->Flags == LocalCred->Flags ) &&
( (MatchCred->Flags & NEGCRED_DUP_MASK ) == (LocalCred->Flags & NEGCRED_DUP_MASK ) ) )
{
if ( !RtlEqualMemory(
MatchCred->Creds,
LocalCred->Creds,
LocalCred->Count * sizeof( NEG_CRED_HANDLE ) ) )
{
ULONG i ;
DebugLog(( DEB_TRACE_NEG, "Same Process, same count, differing handles?\n" ));
for ( i = 0 ; i < LocalCred->Count ; i++ )
{
DebugLog(( DEB_TRACE_NEG, " %d: new <%p : %p> existing (%p : %p)\n",
i,
LocalCred->Creds[i].Handle.dwLower,
LocalCred->Creds[i].Handle.dwUpper,
MatchCred->Creds[i].Handle.dwLower,
MatchCred->Creds[i].Handle.dwUpper ));
}
}
else
{
ULONG_PTR PackageId;
ULONG i ;
NegWriteLockCreds( MatchCred );
if( MatchCred->RefCount == 0 )
{
NegUnlockCreds( MatchCred );
continue;
}
MatchCred->RefCount++ ;
NegUnlockCreds( MatchCred );
NegUnlockCredList();
//
// blot out the handle so the refcount is kept in sync
// with what the underlying packages believe.
//
PackageId = GetCurrentPackageId();
for ( i = 0 ; i < LocalCred->Count ; i++ )
{
#ifndef WIN32_CHICAGO
if( (LocalCred->Creds[i].Flags & NEG_CREDHANDLE_EXTRA_OID) == 0 )
{
WLsaFreeCredHandle( &LocalCred->Creds[i].Handle );
}
#else
FreeCredentialsHandle( &LocalCred->Creds[i].Handle );
#endif // WIN32_CHICAGO
LocalCred->Creds[i].Handle.dwLower = NEG_INVALID_PACKAGE;
LocalCred->Creds[i].Handle.dwUpper = NEG_INVALID_PACKAGE;
}
SetCurrentPackageId( PackageId );
NegpReleaseCreds( LocalCred, FALSE );
*Credential = MatchCred ;
return TRUE;
}
}
}
NegUnlockCredList( );
return FALSE;
}
SECURITY_STATUS
NegpAcquireCredHandle(
PSECURITY_STRING psPrincipal,
ULONG fCredentials,
PLUID pLogonID,
PVOID pvAuthData,
PVOID pvGetKeyFn,
PVOID pvGetKeyArgument,
PULONG_PTR pdwHandle,
PTimeStamp ptsExpiry)
{
NEG_CRED_HANDLE Creds[ NEG_MECH_LIMIT ];
PNEG_CRED_HANDLE pCreds = NULL;
DWORD i = 0;
ULONG Index;
BOOL FreeCreds = FALSE;
PNEG_PACKAGE Package;
PNEG_PACKAGE ClientNegPackage = NULL;
PNEG_PACKAGE * AuthPackageList = NULL;
ULONG AuthPackageCount = 0;
SECURITY_STATUS scRet = STATUS_SUCCESS;
TimeStamp Expiry = { 0 };
PNEG_CREDS pNegCreds = NULL ;
ULONG_PTR PackageId;
SECPKG_CLIENT_INFO ClientInfo;
#ifndef WIN32_CHICAGO
PLSA_CALL_INFO CallInfo ;
PLSAP_LOGON_SESSION LogonSession = NULL;
PNEG_LOGON_SESSION NegLogonSession = NULL ;
PSession pSession ;
TimeStamp MinExpiry = { 0xFFFFFFFF, 0x7FFFFFFF };
#else
TimeStamp MinExpiry = 0x7FFFFFFFFFFFFFFF ;
#endif // WIN32_CHICAGO
PLUID ClientLogonId;
ULONG_PTR ClientPackage = -1;
ULONG_PTR ClientDefaultPackage = -1;
BOOL ExplicitCreds = FALSE ;
BOOL DomainExplicitCreds = FALSE ;
LUID LocalSystem = SYSTEM_LUID ;
BOOLEAN EnableLoopback = TRUE;
DebugLog(( DEB_TRACE_NEG, "NegAcquireCredentialsHandle: Get a Negotiate CredHandle:\n"));
//
// Determine caller info
//
scRet = LsapGetClientInfo(&ClientInfo);
if (!NT_SUCCESS(scRet))
{
goto Cleanup;
}
#ifndef WIN32_CHICAGO
CallInfo = LsapGetCurrentCall();
pSession = GetCurrentSession();
#endif
//
// Get the callers Logon ID so we can determine what package to try first
//
if (ARGUMENT_PRESENT(pLogonID) && ((pLogonID->LowPart != 0) || (pLogonID->HighPart != 0)))
{
ClientLogonId = pLogonID;
}
else
{
ClientLogonId = &ClientInfo.LogonId;
}
#ifndef WIN32_CHICAGO
//
// Now find out what package logged this user on
//
NegLogonSession = NegpLocateLogonSession( ClientLogonId );
if ( NegLogonSession == NULL )
{
LogonSession = LsapLocateLogonSession( ClientLogonId );
if (LogonSession != NULL)
{
PLSAP_SECURITY_PACKAGE LsaPackage ;
ClientPackage = LogonSession->CreatingPackage;
LsapReleaseLogonSession( LogonSession );
//
// If this was done by an old style package, that is,
// an NT4 style auth pkg, *or* some one calling MSV in
// the old fashioned way, reset the value to the negotiate
// ID to allow full negotiation range.
//
LsaPackage = SpmpLocatePackage( ClientPackage );
if ( LsaPackage )
{
if ( ( LsaPackage->fPackage & SPM_AUTH_PKG_FLAG ) != 0 )
{
ClientPackage = NegPackageId ;
}
}
}
}
else
{
ClientPackage = NegLogonSession->DefaultPackage ;
NegpDerefLogonSession( NegLogonSession );
NegLogonSession = NULL ;
}
#endif // WIN32_CHICAGO
ClientDefaultPackage = ClientPackage;
NegReadLockList();
//
// If authentication data was passed in, capture it now to see
// if it includes a subset of the packages to use.
//
if (ARGUMENT_PRESENT(pvAuthData))
{
ClientPackage = (ULONG_PTR) -1 ;
scRet = NegpCaptureSuppliedCreds(
pvAuthData,
&AuthPackageList,
&AuthPackageCount,
&ExplicitCreds,
&DomainExplicitCreds
);
if (!NT_SUCCESS(scRet))
{
NegUnlockList();
goto Cleanup;
}
}
//
// turn off loopback detection when:
// 1. explicit credentials were supplied.
// 2. Product is domain controller, and client is local system account
// (this will cause system->system to auth using machine account.)
//
if( ExplicitCreds )
{
EnableLoopback = FALSE;
}
if (!RtlEqualLuid( ClientLogonId, &LocalSystem ))
{
if( ClientPackage == NegPackageId )
{
ExplicitCreds = TRUE ;
}
} else {
#ifndef WIN32_CHICAGO
if( NegProductType == NtProductLanManNt )
{
EnableLoopback = FALSE;
}
#endif // !WIN32_CHICAGO
}
//
// Build the list of packages that we'll call to get credentials
//
if (AuthPackageCount == 0)
{
scRet = NegpBuildPackageList(
ClientPackage,
fCredentials,
&AuthPackageList,
&AuthPackageCount
);
if (!NT_SUCCESS(scRet))
{
NegUnlockList();
goto Cleanup;
}
}
if ( AuthPackageCount < NEG_MECH_LIMIT )
{
pCreds = Creds;
ZeroMemory( Creds, sizeof(Creds) );
}
else
{
pCreds = (PNEG_CRED_HANDLE) LsapAllocatePrivateHeap( NegPackageCount *
sizeof( NEG_CRED_HANDLE ) );
if ( !pCreds )
{
NegUnlockList();
scRet = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
}
i = 0;
for (Index = 0; Index < AuthPackageCount ; Index++ )
{
BOOLEAN SkipAcquire = FALSE;
PackageId = GetCurrentPackageId();
#ifdef WIN32_CHICAGO
ANSI_STRING AnsiString1 = { 0 }, AnsiString2 = { 0 };
scRet = RtlUnicodeStringToAnsiString( &AnsiString1,
&AuthPackageList[Index]->LsaPackage->Name,
TRUE);
if ( NT_SUCCESS( scRet ) )
{
scRet = RtlUnicodeStringToAnsiString( &AnsiString2,
psPrincipal,
TRUE);
}
if ( NT_SUCCESS( scRet ) )
{
scRet = AcquireCredentialsHandle( AnsiString2.Buffer,
AnsiString1.Buffer,
fCredentials,
pLogonID,
pvAuthData,
(SEC_GET_KEY_FN) pvGetKeyFn,
pvGetKeyArgument,
&pCreds[i].Handle,
&Expiry );
}
#else
ULONG_PTR ThisPackageId = (ULONG_PTR)AuthPackageList[Index]->LsaPackage->dwPackageID;
DWORD j;
//
// mask off the DEFAULT flag, since only we understand it
//
//
// skip calling the package if there are multiple aliases (oids)
// that point to the same underlying package.
// note: NegpReleaseCreds() duplicate handle values are ignored
// during credential release.
//
for( j = 0 ; j < i ; j ++ )
{
if( pCreds[j].Handle.dwLower == ThisPackageId )
{
CopyMemory( &pCreds[i].Handle, &pCreds[j].Handle, sizeof(pCreds[i].Handle) );
SkipAcquire = TRUE;
break;
}
}
if( !SkipAcquire )
{
scRet = WLsaAcquireCredHandle( psPrincipal,
&AuthPackageList[Index]->LsaPackage->Name,
fCredentials & ( SECPKG_CRED_BOTH),
pLogonID,
pvAuthData,
pvGetKeyFn,
pvGetKeyArgument,
&pCreds[i].Handle,
&Expiry );
pCreds[i].Flags = 0;
} else {
//
// no need to AddCredHandle(), as, that would put us out of sync with
// the underlying package ref count.
//
pCreds[i].Flags = NEG_CREDHANDLE_EXTRA_OID;
scRet = SEC_E_OK;
}
#endif // WIN32_CHICAGO
SetCurrentPackageId( PackageId );
if ( NT_SUCCESS( scRet ) )
{
if( !SkipAcquire )
{
DebugLog((DEB_TRACE_NEG, " Added %p:%p, %ws\n",
pCreds[i].Handle.dwUpper, pCreds[i].Handle.dwLower,
AuthPackageList[Index]->LsaPackage->Name.Buffer ));
} else {
DebugLog((DEB_TRACE_NEG, " Skipped %p:%p, %ws (duplicate)\n",
pCreds[i].Handle.dwUpper, pCreds[i].Handle.dwLower,
AuthPackageList[Index]->LsaPackage->Name.Buffer ));
}
#ifdef WIN32_CHICAGO
if ( Expiry < MinExpiry )
{
MinExpiry = Expiry ;
}
#else
if ( Expiry.QuadPart < MinExpiry.QuadPart )
{
MinExpiry.QuadPart = Expiry.QuadPart ;
}
#endif
pCreds[i].Package = AuthPackageList[Index];
i++;
}
else
{
DebugLog((DEB_TRACE_NEG, "Failed %x to get a cred handle for %ws\n",
scRet, AuthPackageList[Index]->LsaPackage->Name.Buffer ));
}
}
NegUnlockList();
if ( i == 0 )
{
//
// Did not get any subordinate credentials, return an error now
//
scRet = SEC_E_NO_CREDENTIALS;
goto Cleanup;
}
//
// Now, allocate our cred structure, and copy all the found cred handles
// into it.
//
pNegCreds = (PNEG_CREDS) LsapAllocateLsaHeap( sizeof( NEG_CREDS ) +
sizeof( NEG_CRED_HANDLE ) * ( i - ANYSIZE_ARRAY ) );
if ( pNegCreds == NULL)
{
scRet = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
FreeCreds = TRUE ;
pNegCreds->Count = i;
pNegCreds->Flags = 0 ;
pNegCreds->Tag = NEGCRED_TAG ;
pNegCreds->DefaultPackage = ClientDefaultPackage;
if ( fCredentials & NEGOTIATE_ALLOW_NTLM )
{
pNegCreds->Flags |= NEGCRED_ALLOW_NTLM ;
}
if ( fCredentials & NEGOTIATE_NEG_NTLM )
{
pNegCreds->Flags |= NEGCRED_NEG_NTLM ;
}
#ifndef WIN32_CHICAGO
//
// WARNING: Change to w2k behavior. Enabling loopback detection
// to switch to NTLM
//
if ( EnableLoopback )
{
pNegCreds->Flags |= NEGCRED_NTLM_LOOPBACK ;
}
pNegCreds->ClientLogonId = *ClientLogonId ;
#endif
InitializeListHead( &pNegCreds->AdditionalCreds );
pNegCreds->ClientProcessId = ClientInfo.ProcessID;
pNegCreds->Expiry = MinExpiry ;
RtlCopyMemory(
pNegCreds->Creds,
pCreds,
i * sizeof( NEG_CRED_HANDLE ) );
#ifndef WIN32_CHICAGO
if ( ( CallInfo->CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE ) != 0 )
{
pNegCreds->Flags |= NEGCRED_KERNEL_CALLER ;
}
#endif
if ( ExplicitCreds )
{
pNegCreds->Flags |= NEGCRED_EXPLICIT_CREDS ;
if( DomainExplicitCreds )
{
pNegCreds->Flags |= NEGCRED_DOMAIN_EXPLICIT_CREDS ;
}
}
//
// If this isn't a duplicate, return
// a new credential
//
if (!NegpCheckForDuplicateCreds(
&pNegCreds
))
{
//
// Finish creating the credential
//
scRet = RtlInitializeCriticalSection( &pNegCreds->CredLock );
if ( !NT_SUCCESS( scRet ) )
{
goto Cleanup ;
}
pNegCreds->RefCount = 1;
if ( ( fCredentials & NEG_CRED_DONT_LINK ) == 0 )
{
NegWriteLockCredList();
InsertTailList( &NegCredList,
&pNegCreds->List );
NegUnlockCredList();
}
}
*pdwHandle = (ULONG_PTR) pNegCreds ;
*ptsExpiry = pNegCreds->Expiry;
scRet = SEC_E_OK;
FreeCreds = FALSE ;
Cleanup:
if (!NT_SUCCESS(scRet))
{
//
// Free all the handles. Because the WLsa calls set the package
// ID make sure we always reset it.
//
PackageId = GetCurrentPackageId();
while ( i-- )
{
#ifndef WIN32_CHICAGO
if( (pCreds[i].Flags & NEG_CREDHANDLE_EXTRA_OID) == 0 )
{
WLsaFreeCredHandle( &pCreds[i].Handle );
}
#else
FreeCredentialsHandle( &pCreds[i].Handle );
#endif // WIN32_CHICAGO
SetCurrentPackageId( PackageId );
}
}
if ( FreeCreds )
{
if ( pNegCreds )
{
LsapFreeLsaHeap( pNegCreds );
}
}
if ( (pCreds != NULL ) &&
(pCreds != Creds ) )
{
LsapFreePrivateHeap( pCreds );
}
if (AuthPackageList != NULL)
{
LsapFreeLsaHeap(AuthPackageList);
}
DsysAssert( NegLogonSession == NULL );
DebugLog(( DEB_TRACE_NEG, "NegAcquireCredentialsHandle: returned 0x%x\n", scRet));
return(scRet);
}
//+---------------------------------------------------------------------------
//
// Function: NegAcquireCredentialsHandle
//
// Synopsis: Acquire a Negotiate credential handle
//
// Arguments: [psPrincipal] --
// [fCredentials] --
// [pLogonID] --
// [pvAuthData] --
// [pvGetKeyFn] --
// [pvGetKeyArgument] --
// [pdwHandle] --
// [ptsExpiry] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 7-26-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY
NegAcquireCredentialsHandle(
PSECURITY_STRING psPrincipal,
ULONG fCredentials,
PLUID pLogonID,
PVOID pvAuthData,
PVOID pvGetKeyFn,
PVOID pvGetKeyArgument,
PULONG_PTR pdwHandle,
PTimeStamp ptsExpiry)
{
return NegpAcquireCredHandle(
psPrincipal,
fCredentials,
pLogonID,
pvAuthData,
pvGetKeyFn,
pvGetKeyArgument,
pdwHandle,
ptsExpiry );
}
//+---------------------------------------------------------------------------
//
// Function: SpQueryCredentialsAttributes
//
// Synopsis: Implements QueryCredentialsAttributes by passing off to the
// first package that we got a cred handle from.
//
// Arguments: [dwCredHandle] --
// [dwAttribute] --
// [Buffer] --
//
// History: 9-17-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
NegQueryCredentialsAttributes(
LSA_SEC_HANDLE dwCredHandle,
ULONG dwAttribute,
PVOID Buffer)
{
PNEG_CREDS Creds;
SECURITY_STATUS Status;
ULONG_PTR PackageId ;
CredHandle TempCredHandle;
Creds = (PNEG_CREDS) dwCredHandle ;
NegReadLockCreds( Creds );
TempCredHandle = Creds->Creds[0].Handle;
NegUnlockCreds( Creds );
PackageId = GetCurrentPackageId();
#ifndef WIN32_CHICAGO
Status = WLsaQueryCredAttributes(
&TempCredHandle,
dwAttribute,
Buffer );
#else // WIN32_CHICAGO
Status = QueryCredentialsAttributes(
&TempCredHandle,
dwAttribute,
Buffer );
#endif // WIN32_CHICAGO
SetCurrentPackageId( PackageId );
return( Status );
}
//+---------------------------------------------------------------------------
//
// Function: NegpReleaseCreds
//
// Synopsis: Releases credential storage when ref count goes to zero
//
// Arguments: [pCreds] --
//
// History: 8-12-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
VOID
NegpReleaseCreds(
PNEG_CREDS pCreds,
BOOLEAN CleanupCall
)
{
BOOL NoLock = FALSE ;
//
// Remove it from the list:
//
if ( pCreds->List.Flink )
{
RemoveEntryList( &pCreds->List );
DebugLog(( DEB_TRACE_NEG, "Releasing credentials %p\n", pCreds ));
}
else
{
NoLock = TRUE ;
DebugLog(( DEB_TRACE_NEG, "Releasing credentials %p (dups or lockless)\n", pCreds ));
}
#ifndef WIN32_CHICAGO
if ( pCreds->Flags & NEGCRED_MULTI )
{
while ( !IsListEmpty( &pCreds->AdditionalCreds ) )
{
PLIST_ENTRY Scan ;
PNEG_CREDS AltCreds ;
Scan = RemoveHeadList( &pCreds->AdditionalCreds );
AltCreds = CONTAINING_RECORD( Scan, NEG_CREDS, List );
NegFreeCredentialsHandle((ULONG_PTR) AltCreds);
}
}
#endif // WIN32_CHICAGO
if ( !NoLock )
{
NegUnlockCreds( pCreds );
RtlDeleteCriticalSection( &pCreds->CredLock );
}
//
// free the embedded package creds.
//
if( !CleanupCall )
{
ULONG_PTR PackageId;
DWORD i;
//
// Free all associated handles:
//
i = pCreds->Count;
PackageId = GetCurrentPackageId();
while ( i-- )
{
if (((pCreds->Creds[i].Flags & NEG_CREDHANDLE_EXTRA_OID) == 0) &&
(pCreds->Creds[i].Handle.dwLower != 0) &&
(pCreds->Creds[i].Handle.dwLower != SPMGR_PKG_ID ) )
{
#ifndef WIN32_CHICAGO
NTSTATUS scRet;
scRet = WLsaFreeCredHandle( &pCreds->Creds[i].Handle );
if( !NT_SUCCESS(scRet) )
{
DebugLog(( DEB_ERROR, "Failed freeing credential %p:%p %x\n",
pCreds->Creds[i].Handle.dwUpper,
pCreds->Creds[i].Handle.dwLower,
scRet ));
//DsysAssert( NT_SUCCESS(scRet) );
}
#else // WIN32_CHICAGO
FreeCredentialsHandle( &pCreds->Creds[i].Handle );
#endif // WIN32_CHICAGO
}
SetCurrentPackageId( PackageId );
}
}
LsapFreeLsaHeap( pCreds );
}
//+---------------------------------------------------------------------------
//
// Function: NegFreeCredentialsHandle
//
// Synopsis: Release a negotiate cred handle
//
// Arguments: [dwHandle] --
//
// History: 7-26-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
SEC_ENTRY
NegFreeCredentialsHandle(
LSA_SEC_HANDLE dwHandle
)
{
PNEG_CREDS pCreds;
ULONG DereferenceCount = 1;
BOOLEAN CleanupCall = FALSE;
#ifndef WIN32_CHICAGO
SECPKG_CALL_INFO CallInfo;
if(LsapGetCallInfo(&CallInfo))
{
//
// nego internally calls NegFreeCredentialsHandle, so, insure
// the refcount is always non-zero.
// realistically speaking, the callcount is only > 1 on cleanup
// disposition, but there is no reason to potentially destabilize
// this already tenuous issue.
//
if( CallInfo.CallCount )
{
DereferenceCount = CallInfo.CallCount;
}
CleanupCall = ( (CallInfo.Attributes & SECPKG_CALL_CLEANUP) != 0 );
}
#endif
pCreds = (PNEG_CREDS) dwHandle ;
NegWriteLockCreds( pCreds );
ASSERT( pCreds->RefCount >= DereferenceCount );
pCreds->RefCount -= DereferenceCount;
if ( pCreds->RefCount == 0 )
{
NegUnlockCreds( pCreds );
NegWriteLockCredList();
NegWriteLockCreds( pCreds );
if( pCreds->RefCount == 0 )
{
NegpReleaseCreds( pCreds, CleanupCall );
} else {
NegUnlockCreds( pCreds );
}
NegUnlockCredList();
}
else
{
NegUnlockCreds( pCreds );
}
return( SEC_E_OK );
}
//+---------------------------------------------------------------------------
//
// Function: NegpCreateContext
//
// Synopsis: Creates and zeroes a context
//
// Arguments: (none)
//
// History: 10-05-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
PNEG_CONTEXT
NegpCreateContext(
VOID
)
{
PNEG_CONTEXT Context ;
Context = (PNEG_CONTEXT) LsapAllocatePrivateHeap( sizeof( NEG_CONTEXT ) );
if ( Context )
{
ZeroMemory( Context, sizeof( NEG_CONTEXT ) );
Context->CheckMark = NEGCONTEXT_CHECK ;
Context->Flags |= NEG_CONTEXT_UPLEVEL ;
}
return( Context );
}
//+---------------------------------------------------------------------------
//
// Function: NegpDeleteContext
//
// Synopsis: Free the data behind a context
//
// Arguments: [Context] --
//
// History: 10-05-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
VOID
NegpDeleteContext(
PNEG_CONTEXT Context
)
{
ULONG_PTR PackageId;
if ( !Context )
{
return ;
}
DsysAssert( Context->CheckMark == NEGCONTEXT_CHECK );
if (Context->CheckMark != NEGCONTEXT_CHECK )
{
return;
}
if ( Context->Target.Buffer )
{
LsapFreePrivateHeap( Context->Target.Buffer );
}
if ( Context->Check && Context->Buffer )
{
Context->Check->Finish( & Context->Buffer );
}
if ( Context->MappedBuffer.pvBuffer != NULL)
{
LsapFreeLsaHeap(
Context->MappedBuffer.pvBuffer
);
}
if ( Context->Message )
{
LsapFreeLsaHeap( Context->Message );
}
if ( ( Context->Handle.dwLower != 0 ) &&
( Context->Handle.dwLower != SPMGR_PKG_ID ) )
{
PackageId = GetCurrentPackageId();
#ifdef WIN32_CHICAGO
DeleteSecurityContext( &Context->Handle );
#else
WLsaDeleteContext( &Context->Handle );
#endif
SetCurrentPackageId(PackageId);
}
//
// If we referenced the credential, free it now.
//
if (Context->Creds != NULL)
{
NegFreeCredentialsHandle((ULONG_PTR) Context->Creds);
}
if (Context->SupportedMechs != NULL)
{
if ((Context->Flags & NEG_CONTEXT_FREE_EACH_MECH))
{
NegpFreeMechList(Context->SupportedMechs);
}
else
{
LsapFreeLsaHeap(Context->SupportedMechs);
}
}
DebugLog(( DEB_TRACE_NEG, "Deleting context %x\n", Context ));
LsapFreePrivateHeap( Context );
}
//+---------------------------------------------------------------------------
//
// Function: NegpFindPackageForOid
//
// Synopsis: Returns index for a package matching the OID passed in
//
// Arguments: [Creds] --
// [Oid] --
//
// History: 9-25-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
ULONG_PTR
NegpFindPackageForOid(
PNEG_CREDS Creds,
ObjectID Oid)
{
ULONG i;
NegDumpOid( "Compare Mechanism", Oid );
for ( i = 0 ; i < Creds->Count ; i++ )
{
if ( NegpCompareOid( Oid,
Creds->Creds[i].Package->ObjectId ) == 0 )
{
return( i );
}
}
return( NEG_INVALID_PACKAGE );
}
//+---------------------------------------------------------------------------
//
// Function: NegBuildRequestToken
//
// Synopsis: Generates a NegotiateRequest token, for either client or server
// side inits. Generates a NEG_CONTEXT and the token to be sent
// to the other side.
//
// Effects: Lots of work
//
// Arguments: [ServerSideInit] --
// [Creds] --
// [pszTargetName] --
// [fContextReq] --
// [TargetDataRep] --
// [ServerMechs] --
// [pContext] --
// [pOutput] --
//
// History: 9-30-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
NegBuildRequestToken(
IN BOOL ServerSideInit,
IN PNEG_CREDS Creds,
IN PSECURITY_STRING pszTargetName,
IN ULONG fContextReq,
IN ULONG TargetDataRep,
IN struct MechTypeList *ServerMechs,
IN PSECURITY_STRING NegotiateHint,
OUT PNEG_CONTEXT * pContext,
OUT PSecBufferDesc pOutput,
PTimeStamp ptsExpiry)
{
InitialNegToken Request ;
SECURITY_STATUS scRet ;
SECURITY_STATUS scRetPrior = STATUS_SUCCESS;
struct MechTypeList CommonMechs[ NEG_MECH_LIMIT ];
struct MechTypeList *MechList = CommonMechs;
ULONG MatchingPackages[ NEG_MECH_LIMIT ];
struct MechTypeList *pMechs ;
struct MechTypeList *SourceMechs = NULL;
PVOID SourceMechsToFree = NULL ;
ULONG MechCount ;
ULONG i ;
ULONG_PTR CredIndex ;
PNEG_CONTEXT Context = NULL ;
PSession pSession ;
SecPkgCredentials_NamesW Names ;
DWORD NameLength ;
ANSI_STRING NarrowName = {0};
PSession pClientClone ;
ULONG_PTR PackageId ;
PNEG_PACKAGE Package ;
SecBuffer DesiredToken = { 0 } ;
SecBuffer InputBuffer ;
SecBufferDesc DTDescription ;
SecBufferDesc DTInput ;
SecBufferDesc NullInput = { 0 };
PSecBuffer pToken ;
CtxtHandle InitialHandle ;
ASN1octetstring_t EncodedData = {0};
int Result ;
ULONG PackageReq = 0 ;
BOOL DirectSecurityPacket = FALSE ;
BOOL UseHint = FALSE ;
BOOL HintPresent = FALSE ;
ULONG PackageReqFlags ;
BOOL BufferSizeReset = FALSE ;
BOOL OrderByMech = FALSE ;
BOOL MechListReordered = FALSE ;
CredHandle TempCredHandle;
PNEG_PACKAGE LastPackage = NULL ;
#ifndef WIN32_CHICAGO
PLSA_CALL_INFO CallInfo ;
#endif
//
// Make sure there is an output buffer
//
scRet = NegpParseBuffers( pOutput, TRUE, &pToken, NULL );
if ( !NT_SUCCESS( scRet ) )
{
DebugLog(( DEB_ERROR, "Failed to map buffers, %x\n", scRet ));
goto Cleanup ;
}
//
// If there is no output buffer, fail now
//
if (pToken == NULL)
{
DebugLog((DEB_TRACE_NEG,"No output token for NegBuildRequestToken\n"));
scRet = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
#ifndef WIN32_CHICAGO
CallInfo = LsapGetCurrentCall();
if ( !ServerSideInit )
{
if ( CallInfo->CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE )
{
//
// Mask off the mutual bit for now
//
PackageReq = NegGetPackageCaps( fContextReq & (~(ISC_REQ_MUTUAL_AUTH)) );
}
else
{
PackageReq = NegGetPackageCaps( fContextReq );
}
}
else
{
PackageReq = 0xFFFFFFFF ;
}
#else
PackageReq = NegGetPackageCaps( fContextReq );
#endif
//
// First, gather up the mechanisms that the other guys supports. If
// we don't know, assume all of ours:
//
if ( ServerMechs )
{
SourceMechs = ServerMechs ;
}
else
{
scRet = NegpBuildMechListFromCreds(
Creds,
PackageReq,
(ServerSideInit ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND ),
&SourceMechs );
if ( !NT_SUCCESS(scRet) )
{
return( scRet );
}
//
// Save a copy of this pointer. The list may be rearranged later, but
// we need to free this "original" pointer.
//
SourceMechsToFree = SourceMechs ;
}
//
// Initialize some pointers so we know if we need to free anything.
//
DesiredToken.pvBuffer = NULL ;
Context = NULL ;
//
// if we're honoring server hints, then go by the mech list
//
OrderByMech = ( ( NegOptions & NEGOPT_HONOR_SERVER_PREF ) != 0 );
#ifndef WIN32_CHICAGO
//
// Special case the local loopback to use NTLM. Analyze the target name. If it
// is an SPN, and it is our hostname in there, rearrange the mech list to put
// NTLM first (note, for local case, NTLM will essentially dup the token). This
// function cannot fail.
//
if ( ((Creds->Flags & NEGCRED_NTLM_LOOPBACK) != 0)
#if 0
&& ((Creds->Flags & NEGCRED_EXPLICIT_CREDS) == 0)
#endif
)
{
if ( NegpRearrangeMechsIfNeccessary( &SourceMechs, pszTargetName, &DirectSecurityPacket ) )
{
OrderByMech = TRUE ;
MechListReordered = TRUE ;
}
}
#endif
//
// Scan through the list, building up the list of common mechanisms.
// Also, maintain a count, and determine the first matching package to
// generate our desired token. Depending on local configuration, we
// will either honor the server's preferences, or our own.
//
pMechs = SourceMechs ;
MechCount = 0;
NegReadLockCreds( Creds );
//
// BUGUG: check for supported options for the context requirements.
//
if ( OrderByMech )
{
//
// Walk the server list first.
//
while ( pMechs )
{
CredIndex = NegpFindPackageForOid( Creds, pMechs->value );
if ( CredIndex != NEG_INVALID_PACKAGE )
{
CommonMechs[ MechCount ].value =
Creds->Creds[ CredIndex ].Package->ObjectId ;
CommonMechs[ MechCount ].next = &CommonMechs[ MechCount + 1 ];
MatchingPackages[ MechCount] = (ULONG) CredIndex;
MechCount ++ ;
if (MechCount == NEG_MECH_LIMIT)
{
break;
}
}
pMechs = pMechs->next ;
}
}
else
{
//
// Walk the local cred list first:
//
for ( i = 0 ; i < Creds->Count ; i++ )
{
pMechs = SourceMechs ;
while ( pMechs )
{
if ( NegpCompareOid( pMechs->value,
Creds->Creds[ i ].Package->ObjectId ) == 0 )
{
CommonMechs[ MechCount ].value =
Creds->Creds[ i ].Package->ObjectId ;
CommonMechs[ MechCount ].next = &CommonMechs[ MechCount + 1 ];
MatchingPackages[ MechCount] = i;
MechCount++;
break;
}
//
// Note: Right now, the limit on protocols is 16. We may need to
// increase that.
//
if (MechCount == NEG_MECH_LIMIT)
{
break;
}
pMechs = pMechs->next ;
}
}
}
//
// Ok, at this point, we have the desired security package (cred handle)
// in MatchingPackage, MechCount contains the number of mechs in common.
//
// Note: These can be zero, that is that we have no mechs in common.
//
NegUnlockCreds( Creds );
if ( MechCount == 0 )
{
//
// No common packages:
//
DebugLog(( DEB_TRACE_NEG, "No common packages\n"));
scRet = SEC_E_INVALID_TOKEN ;
goto Cleanup ;
}
//
// Patch up list:
//
CommonMechs[ MechCount - 1 ].next = NULL ;
//
// Start assembling request token:
//
ZeroMemory( &Request, sizeof( Request ) );
//
// Create the negotiate context
//
Context = NegpCreateContext() ;
if ( !Context )
{
scRet = SEC_E_INSUFFICIENT_MEMORY ;
goto Cleanup ;
}
if ( !ServerSideInit )
{
//
// Store target name away
//
scRet = LsapDuplicateString2( &Context->Target, pszTargetName );
if ( !NT_SUCCESS( scRet ) )
{
goto Cleanup ;
}
//
// Use the supplied mechs. If these were passed from the server,
// make sure we don't free them before using them.
//
if ( Context->SupportedMechs )
{
DebugLog((DEB_TRACE_NEG, "Context %p already has MechList ?\n", Context ));
}
if ( ( SourceMechs == ServerMechs ) ||
( MechListReordered ) )
{
Context->SupportedMechs = NegpCopyMechList(MechList);
if (Context->SupportedMechs == NULL)
{
scRet = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
Context->Flags |= NEG_CONTEXT_FREE_EACH_MECH;
}
else
{
Context->SupportedMechs = SourceMechs;
SourceMechs = NULL;
}
if ( fContextReq & ISC_REQ_MUTUAL_AUTH )
{
Context->Flags |= NEG_CONTEXT_MUTUAL_AUTH ;
}
}
if (Context->Creds == NULL)
{
Context->Creds = Creds ;
//
// Reference the credentials so they don't go away unexpectedly
//
NegWriteLockCreds(Creds);
Creds->RefCount++;
NegUnlockCreds(Creds);
}
PackageReqFlags = fContextReq | ISC_REQ_MUTUAL_AUTH ;
PackageReqFlags &= (~(ISC_REQ_ALLOCATE_MEMORY ));
i = 0;
do
{
UseHint = FALSE ;
HintPresent = FALSE ;
Context->CredIndex = MatchingPackages[i] ;
NegReadLockCreds( Creds );
Package = Creds->Creds[ MatchingPackages[i] ].Package ;
#ifndef WIN32_CHICAGO
if ( NegNegotiationControl < NEG_NEGLEVEL_NO_DOWNGRADE )
{
//
// If we're allowing downgrade, if the next package is
// the NT4 compatibility package, and we are not
// responding to a server list of mechs, and we haven't
// reordered the list specifically to use NTLM for loopback,
// go direct
//
if ( ( (Package->Flags & NEG_NT4_COMPAT ) != 0 ) &&
( ServerMechs == NULL ) &&
( (Creds->Flags & NEGCRED_NEG_NTLM ) == 0 ) &&
( !MechListReordered ) )
{
DebugLog(( DEB_TRACE_NEG, "Dropping back to pure NTLM\n" ));
DirectSecurityPacket = TRUE ;
}
}
#endif // WIN32_CHICAGO
//
// Now, divergent behavior. For a server side request token, we need to
// grab some hint data. On a client side request, we ping the desired
// mechanism to generate a "hopeful" blob for the server.
//
if ( ServerSideInit )
{
NTSTATUS TempStatus;
if ( Creds->ServerBufferLength == 0 )
{
DebugLog(( DEB_TRACE_NEG, "Gathering up server name for hint\n" ));
//
// We need to query credential handle 0 to find out
// what the name of the person is, so that we can send it
// back in the hints. However, just calling querycredattributes
// would make the package write the data to the client process,
// when we need it here. So, we swap out our session, and substitute
// a clone of the client session, with the INPROC flag set. The
// helpers will check for this flag, and do a little dance to
// keep the memory local.
//
#ifndef WIN32_CHICAGO
pSession = GetCurrentSession();
TempStatus = CloneSession( pSession, &pClientClone, SESFLAG_INPROC );
//
// WARNING: This code block has the braces only in NT builds, not
// in Win9x builds. Balance them carefully if you modify this portion.
//
if ( NT_SUCCESS( TempStatus ) )
{
SpmpReferenceSession( pClientClone );
SetCurrentSession( pClientClone );
PackageId = GetCurrentPackageId();
#endif // WIN32_CHICAGO
//
// Make a copy of the handle because we can't hold a lock
// while calling outside the Negotiate package.
//
TempCredHandle = Creds->Creds[0].Handle;
NegUnlockCreds(Creds);
#ifndef WIN32_CHICAGO
TempStatus = WLsaQueryCredAttributes(
&TempCredHandle,
SECPKG_CRED_ATTR_NAMES,
&Names );
#else
TempStatus = QueryCredentialsAttributes(
&TempCredHandle,
SECPKG_CRED_ATTR_NAMES,
&Names );
#endif // WIN32_CHICAGO
SetCurrentPackageId( PackageId );
NegReadLockCreds( Creds);
if ( NT_SUCCESS( TempStatus ) )
{
UNICODE_STRING TempString;
RtlInitUnicodeString(
&TempString,
Names.sUserName
);
TempStatus = RtlUnicodeStringToAnsiString(
&NarrowName,
&TempString,
TRUE // allocate destination
);
if (NT_SUCCESS(TempStatus))
{
Request.negToken.u.negTokenInit.bit_mask |= NegTokenInit_negHints_present ;
Request.negToken.u.negTokenInit.negHints.hintName = NarrowName.Buffer ;
Request.negToken.u.negTokenInit.negHints.bit_mask |= hintName_present;
}
LsapFreeLsaHeap( Names.sUserName );
}
//
// Ignore failures from above because it was really only a hint.
//
scRet = STATUS_SUCCESS;
#ifndef WIN32_CHICAGO
SetCurrentSession( pSession );
//
// Deref and clean up clone session
//
SpmpDereferenceSession( pClientClone );
}
#endif // WIN32_CHICAGO
}
NegUnlockCreds( Creds );
}
else
{
CredHandle TempCredHandle;
//
// Make a copy of the handle because we can't hold a lock
// while calling outside the Negotiate package.
//
TempCredHandle = Creds->Creds[0].Handle;
NegUnlockCreds(Creds);
//
// Client side call. Here, we call down to the desired package,
// and have it generate a blob to be encoded and sent over to the
// server.
//
if ( DesiredToken.pvBuffer )
{
//
// If we're coming through this loop again, free the current buffer
// and allocate one of appropriate size for the current package.
//
LsapFreeLsaHeap( DesiredToken.pvBuffer );
}
DesiredToken.pvBuffer = LsapAllocateLsaHeap( Package->TokenSize );
if (DesiredToken.pvBuffer == NULL)
{
scRet = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
DesiredToken.cbBuffer = Package->TokenSize ;
DesiredToken.BufferType = SECBUFFER_TOKEN ;
DTDescription.ulVersion = SECBUFFER_VERSION ;
DTDescription.cBuffers = 1;
DTDescription.pBuffers = &DesiredToken ;
DTInput.ulVersion = SECBUFFER_VERSION;
//
// If negotiation information was provided, use it
//
if (ARGUMENT_PRESENT(NegotiateHint) &&
(NegotiateHint->Length != 0)
#ifndef WIN32_CHICAGO
&& ( NegNegotiationControl < NEG_NEGLEVEL_COMPATIBILITY )
#endif
)
{
DTInput.cBuffers = 1;
DTInput.pBuffers = &InputBuffer;
InputBuffer.pvBuffer = NegotiateHint->Buffer;
InputBuffer.cbBuffer = NegotiateHint->Length;
InputBuffer.BufferType = SECBUFFER_NEGOTIATION_INFO;
HintPresent = TRUE ;
}
else
{
DTInput.cBuffers = 0;
DTInput.pBuffers = 0;
HintPresent = FALSE ;
}
InitialHandle.dwUpper = 0;
InitialHandle.dwLower = 0;
DebugLog(( DEB_TRACE_NEG, "Getting initial token from preferred package '%ws'\n",
Package->LsaPackage->Name.Buffer ));
PackageId = GetCurrentPackageId();
RetryWithHintPoint:
//
// This goto label is used for retry with hint in low security setting, and
// retry with a larger buffer in the case where kerberos exceeds its max token
// value.
//
//
// Move this into a local. The WLsaInit code will blot out the cred handle
// for the secur32.dll if the context changes.
//
TempCredHandle = Creds->Creds[ MatchingPackages[i] ].Handle ;
LastPackage = Creds->Creds[ MatchingPackages[ i ] ].Package ;
if( NT_SUCCESS( scRetPrior ) ||
(TempCredHandle.dwLower != Creds->Creds[ MatchingPackages[i-1] ].Handle.dwLower)
)
{
#ifndef WIN32_CHICAGO
scRet = WLsaInitContext(&TempCredHandle,
&InitialHandle,
pszTargetName,
PackageReqFlags,
0,
TargetDataRep,
(UseHint ? &DTInput : &NullInput ),
0,
&Context->Handle,
&DTDescription,
&Context->Attributes,
ptsExpiry,
&Context->Mapped,
&Context->MappedBuffer
);
#else
ANSI_STRING TempAnsiString;
NTSTATUS TempStatus = RtlUnicodeStringToAnsiString(
&TempAnsiString,
pszTargetName,
TRUE // allocate destination
);
scRet = InitializeSecurityContext(
&TempCredHandle,
&InitialHandle,
TempAnsiString.Buffer,
PackageReqFlags,
0,
TargetDataRep,
(UseHint ? &DTInput : &NullInput),
0,
&Context->Handle,
&DTDescription,
&Context->Attributes,
ptsExpiry
);
#endif
scRetPrior = scRet;
}
SetCurrentPackageId( PackageId );
DebugLog(( DEB_TRACE_NEG, "WLsaInitContext( %ws, %ws ) returned %x\n",
pszTargetName->Buffer,
Creds->Creds[ MatchingPackages[i] ].Package->LsaPackage->Name.Buffer,
scRet ));
Context->CallCount++ ;
if ( !NT_SUCCESS( scRet ) )
{
DebugLog(( DEB_TRACE_NEG, "Failed %x getting token from preferred package '%ws'\n",
scRet, Package->LsaPackage->Name.Buffer ));
if ( ( scRet == STATUS_BUFFER_TOO_SMALL ) &&
( BufferSizeReset == FALSE ) )
{
LsapFreeLsaHeap( DesiredToken.pvBuffer );
//
// This is technically not multi thread safe, but this is a comparatively
// rare event. The buffer size will never be set less than the original
// claimed size from the package, so at worst, we'll get stuck in this realloc
// loop twice.
//
if ( DesiredToken.cbBuffer > Package->TokenSize )
{
Package->TokenSize = DesiredToken.cbBuffer ;
}
DesiredToken.pvBuffer = LsapAllocateLsaHeap( DesiredToken.cbBuffer );
if ( DesiredToken.pvBuffer == NULL )
{
scRet = SEC_E_INSUFFICIENT_MEMORY ;
}
else
{
BufferSizeReset = TRUE ;
scRetPrior = STATUS_SUCCESS;
goto RetryWithHintPoint ;
}
}
#ifndef WIN32_CHICAGO
if ( (HintPresent) &&
(!(UseHint)) &&
( ( scRet == SEC_E_TARGET_UNKNOWN ) ||
( scRet == STATUS_NO_TRUST_SAM_ACCOUNT ) ||
( scRet == STATUS_NO_LOGON_SERVERS ) ) &&
(NegNegotiationControl < NEG_NEGLEVEL_COMPATIBILITY ) )
{
DebugLog(( DEB_TRACE_NEG, "Retrying with hint name %ws\n",
NegotiateHint->Buffer ));
UseHint = TRUE ;
scRetPrior = STATUS_SUCCESS;
goto RetryWithHintPoint ;
}
#endif
if ( DesiredToken.pvBuffer )
{
LsapFreeLsaHeap( DesiredToken.pvBuffer );
DesiredToken.pvBuffer = NULL ;
}
}
else
{
//
// On success, check for a null session indication. If we got a "null
// session" from the security package, then we need to make sure that it
// is not uplevel, if NTLM is enabled on the machine.
//
if ( ( Context->Attributes & ISC_RET_NULL_SESSION ) != 0 )
{
if ( NtlmPackageId != NEG_INVALID_PACKAGE )
{
//
// NTLM is enabled. If this is not NTLM, blow away the context
// until we get to NTLM. First, override the returned status with
// a "special" status code that will get us through the retry logic
// below. Then, delete the existing context.
//
if ( (Creds->Creds[ MatchingPackages[ i ] ].Package->Flags & NEG_NT4_COMPAT) == 0 )
{
scRet = SEC_E_BAD_PKGID ;
#ifdef WIN32_CHICAGO
DeleteSecurityContext( &Context->Handle );
#else
WLsaDeleteContext( &Context->Handle );
#endif
Context->Attributes = 0 ;
}
}
}
}
Context->LastStatus = scRet ;
Context->Flags |= NEG_CONTEXT_PACKAGE_CALLED;
}
//
// If the packages failed, take it out of the list
//
if (!NT_SUCCESS(scRet))
{
MechList = CommonMechs[i].next;
MechCount--;
//
// kerberos can authoritatively return STATUS_WRONG_PASSWORD
// if the creds were not valid. No reason to continue at that point.
//
if((scRet == STATUS_WRONG_PASSWORD || scRet == STATUS_LOGON_FAILURE) &&
((Creds->Flags & NEGCRED_DOMAIN_EXPLICIT_CREDS) != 0))
{
DebugLog(( DEB_TRACE_NEG, "Status code %x from Initialize causing us to break out\n",
scRet ));
goto Cleanup;
}
if (( NegNegotiationControl > NEG_NEGLEVEL_NO_SECURITY ) &&
( ( Creds->Flags & NEGCRED_EXPLICIT_CREDS ) == 0 ) &&
( ( Creds->Flags & NEGCRED_ALLOW_NTLM ) == 0 ) )
{
BOOL BreakOut ;
BOOL Downgrade = TRUE;
//
// Ok, we need to do some advance filtering on the
// return status, to see if we should progress or not.
//
switch ( scRet )
{
//
// Special case for null session going to NTLM:
//
case SEC_E_BAD_PKGID:
BreakOut = FALSE ;
break;
case SEC_E_TARGET_UNKNOWN:
case STATUS_NO_TRUST_SAM_ACCOUNT:
case STATUS_KDC_UNKNOWN_ETYPE:
case STATUS_NETWORK_UNREACHABLE:
case SEC_E_NO_CREDENTIALS: // eg, Kerberos has no creds for local account case
BreakOut = FALSE ;
break;
case STATUS_WRONG_PASSWORD:
case STATUS_LOGON_FAILURE:
case STATUS_NO_SUCH_USER:
case STATUS_TIME_DIFFERENCE_AT_DC:
case SEC_E_TIME_SKEW:
case STATUS_SMARTCARD_SUBSYSTEM_FAILURE:
case STATUS_SMARTCARD_WRONG_PIN:
case STATUS_SMARTCARD_NO_CARD:
case STATUS_PASSWORD_MUST_CHANGE:
case STATUS_PASSWORD_EXPIRED:
{
BreakOut = TRUE;
Downgrade = FALSE;
break;
}
case STATUS_NO_LOGON_SERVERS:
{
//
// If we truly logged on with NTLM, keep going
//
if (Creds->DefaultPackage == NtlmPackageId)
{
BreakOut = FALSE;
} else {
BreakOut = TRUE;
}
break;
}
default:
BreakOut = TRUE ;
break;
}
DebugLog(( DEB_TRACE_NEG, "Status code %x causing us to %s\n",
scRet, (BreakOut ? "break out" : "continue") ));
if ( BreakOut )
{
if( Downgrade )
{
#ifndef WIN32_CHICAGO
NegpReportEvent(
EVENTLOG_WARNING_TYPE,
NEGOTIATE_DOWNGRADE_DETECTED,
CATEGORY_NEGOTIATE,
scRet,
2,
pszTargetName,
&LastPackage->LsaPackage->Name
);
#endif
//
// Tell the caller the explicit reason for the failure since
// NTLM might very well have suceeded.
//
scRet = STATUS_DOWNGRADE_DETECTED;
}
break;
}
}
}
i++;
} while (!NT_SUCCESS(scRet) && (MechCount != 0));
if (!NT_SUCCESS(scRet))
{
#ifndef WIN32_CHICAGO
NegpReportEvent(
EVENTLOG_WARNING_TYPE,
NEGOTIATE_INVALID_SERVER,
CATEGORY_NEGOTIATE,
scRet,
1,
pszTargetName
);
#endif
DebugLog((DEB_ERROR,"No packages could initialize\n"));
goto Cleanup;
}
if ( MechCount == 0 )
{
#ifndef WIN32_CHICAGO
NegpReportEvent(
EVENTLOG_WARNING_TYPE,
NEGOTIATE_INVALID_SERVER,
CATEGORY_NEGOTIATE,
0,
1,
pszTargetName
);
#endif
scRet = SEC_E_INVALID_TOKEN ;
//
// No common packages:
//
DebugLog(( DEB_TRACE_NEG, "No common packages\n"));
goto Cleanup ;
}
#ifndef WIN32_CHICAGO
if ( LastPackage )
{
NegpReportEvent(
EVENTLOG_INFORMATION_TYPE,
NEGOTIATE_PACKAGE_SELECTED,
CATEGORY_NEGOTIATE,
0,
2,
pszTargetName,
&LastPackage->LsaPackage->Name
);
}
#endif
if ( !DirectSecurityPacket )
{
Request.negToken.choice = negTokenInit_chosen ;
Request.negToken.u.negTokenInit.mechTypes = MechList ;
Request.negToken.u.negTokenInit.bit_mask |= NegTokenInit_mechTypes_present ;
//
// Okay, now we have all the pieces. Assemble the token:
//
if ( DesiredToken.pvBuffer )
{
Request.negToken.u.negTokenInit.mechToken.length = DesiredToken.cbBuffer ;
Request.negToken.u.negTokenInit.mechToken.value = (PUCHAR) DesiredToken.pvBuffer ;
Request.negToken.u.negTokenInit.bit_mask |= NegTokenInit_mechToken_present ;
}
//
// Add in the SPNEGO mechanism id
//
Request.spnegoMech = NegSpnegoMechOid;
Result = SpnegoPackData(
&Request,
InitialNegToken_PDU,
&(EncodedData.length),
&(EncodedData.value));
if ( Result )
{
DebugLog((DEB_ERROR, "Failed to encode data: %d\n", Result ));
scRet = SEC_E_INSUFFICIENT_MEMORY ;
goto Cleanup ;
}
}
else
{
EncodedData.length = DesiredToken.cbBuffer ;
EncodedData.value = (PUCHAR) DesiredToken.pvBuffer ;
DesiredToken.pvBuffer = NULL ;
}
//
// Okay, got the token into a contiguous mass. Package it up for the caller
//
if ( fContextReq & ISC_REQ_ALLOCATE_MEMORY )
{
//
// Easy: The caller asked for us to allocate memory for them, so
// let the LSA do it.
//
pToken->pvBuffer = EncodedData.value ;
pToken->cbBuffer = EncodedData.length ;
EncodedData.value = NULL;
}
else
{
//
// The caller has a buffer that we're supposed to use. Make sure we
// can fit.
//
if ( (ULONG) EncodedData.length < pToken->cbBuffer )
{
RtlCopyMemory( pToken->pvBuffer,
EncodedData.value,
EncodedData.length );
pToken->cbBuffer = EncodedData.length ;
}
else if ( ( ( fContextReq & ISC_REQ_FRAGMENT_TO_FIT ) != 0 ) &&
( pToken->cbBuffer >= SPNEGO_MINIMUM_BUFFER ) )
{
//
// Ok, we need to whack the context to indicate that we are
// fragmenting, and return only what the caller can handle.
//
Context->Message = EncodedData.value ;
Context->TotalSize = EncodedData.length ;
Context->Flags |= NEG_CONTEXT_FRAGMENTING ;
//
// set this to NULL so it doesn't get freed later
//
EncodedData.value = NULL ;
RtlCopyMemory(
pToken->pvBuffer,
Context->Message,
pToken->cbBuffer );
Context->CurrentSize = pToken->cbBuffer ;
}
else
{
DebugLog(( DEB_TRACE_NEG, "Supplied buffer is too small\n" ));
scRet = SEC_E_INSUFFICIENT_MEMORY ;
goto Cleanup ;
}
}
//
// We have created the token, encoded it, and stuck it in a return buffer.
// We have created the context record, and it is ready. We're done!
//
if ( !DirectSecurityPacket )
{
*pContext = Context ;
Context = NULL;
scRet = SEC_I_CONTINUE_NEEDED ;
}
#ifndef WIN32_CHICAGO
else
{
DebugLog(( DEB_TRACE_NEG, "Replacing handle, current status is %x\n", scRet ));
LsapChangeHandle( HandleReplace,
NULL,
&Context->Handle );
*pContext = NULL ;
Context->Handle.dwLower = 0 ;
Context->Handle.dwUpper = 0 ;
//
// Context will be freed during cleanup.
//
}
#endif
Cleanup:
if ( !ServerMechs )
{
//
// No server mechs means we allocated and used one based on our
// cred handle. Free it.
//
if (SourceMechs != NULL)
{
if ( MechListReordered )
{
LsapFreeLsaHeap( SourceMechsToFree );
}
else
{
LsapFreeLsaHeap( SourceMechs );
}
}
}
if (EncodedData.value != NULL)
{
LsapFreeLsaHeap(EncodedData.value);
}
if ( DesiredToken.pvBuffer )
{
LsapFreeLsaHeap( DesiredToken.pvBuffer );
}
if ( Context )
{
NegpDeleteContext( Context );
}
if (NarrowName.Buffer != NULL)
{
RtlFreeAnsiString(&NarrowName);
}
return( scRet );
}
//+---------------------------------------------------------------------------
//
// Function: NegGenerateInitialToken
//
// Synopsis: Client side init
//
// Arguments: [dwCreds] --
// [Target] --
// [fContextReq] --
// [TargetDataRep] --
// [pInput] --
// [pdwNewContext] --
// [pOutput] --
// [pfContextAttr] --
// [ptsExpiry] --
// [pfMapContext] --
// [pContextData] --
//
// History: 9-30-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
NegGenerateInitialToken(
ULONG_PTR dwCreds,
PSECURITY_STRING Target,
ULONG fContextReq,
ULONG TargetDataRep,
PSecBufferDesc pInput,
PULONG_PTR pdwNewContext,
PSecBufferDesc pOutput,
PULONG pfContextAttr,
PTimeStamp ptsExpiry,
PBYTE pfMapContext,
PSecBuffer pContextData)
{
SECURITY_STATUS scRet;
PSecBuffer Buffer;
PNEG_CREDS Creds;
PNEG_CONTEXT Context;
//
// Initialize stuff:
//
Creds = (PNEG_CREDS) dwCreds ;
//
// Fall through to common code with normal initial token:
//
scRet = NegBuildRequestToken( FALSE,
Creds,
Target,
fContextReq,
TargetDataRep,
NULL,
NULL,
&Context,
pOutput,
ptsExpiry );
if ( NT_SUCCESS( scRet ) )
{
//
// Successfully built token. Set flags:
//
*pfContextAttr = ( ISC_RET_INTERMEDIATE_RETURN ) |
( fContextReq & ISC_REQ_ALLOCATE_MEMORY ?
ISC_RET_ALLOCATED_MEMORY : 0 ) ;
*pfMapContext = FALSE ;
if ( Context )
{
*pdwNewContext = (DWORD_PTR) Context ;
}
}
return( scRet );
}
//+---------------------------------------------------------------------------
//
// Function: NegGenerateServerRequest
//
// Synopsis: Server side init
//
// Arguments: [dwCreds] --
// [fContextReq] --
// [TargetDataRep] --
// [pInput] --
// [pdwNewContext] --
// [pOutput] --
// [pfContextAttr] --
// [ptsExpiry] --
// [pfMapContext] --
// [pContextData] --
//
// History: 9-30-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
NegGenerateServerRequest(
ULONG_PTR dwCreds,
ULONG fContextReq,
ULONG TargetDataRep,
PSecBufferDesc pInput,
PULONG_PTR pdwNewContext,
PSecBufferDesc pOutput,
PULONG pfContextAttr,
PTimeStamp ptsExpiry,
PBYTE pfMapContext,
PSecBuffer pContextData)
{
SECURITY_STATUS scRet;
PSecBuffer Buffer;
PNEG_CREDS Creds;
PNEG_CONTEXT Context;
//
// Initialize stuff:
//
Creds = (PNEG_CREDS) dwCreds ;
//
// Fall through to common code with normal initial token:
//
scRet = NegBuildRequestToken( TRUE,
Creds,
NULL,
fContextReq,
TargetDataRep,
NULL,
NULL,
&Context,
pOutput,
ptsExpiry );
if ( NT_SUCCESS( scRet ) )
{
//
// Successfully built token. Set flags:
//
*pfContextAttr = ( fContextReq & ASC_REQ_ALLOCATE_MEMORY ?
ASC_RET_ALLOCATED_MEMORY : 0 ) ;
*pfMapContext = FALSE ;
*pdwNewContext = (DWORD_PTR) Context ;
}
return( scRet );
}
//+---------------------------------------------------------------------------
//
// Function: NegCrackServerRequestAndReply
//
// Synopsis: Client side Init with Neg token from the server
//
// Arguments: [dwCreds] --
// [Target] --
// [fContextReq] --
// [TargetDataRep] --
// [pInput] --
// [pdwNewContext] --
// [pOutput] --
// [pfContextAttr] --
// [ptsExpiry] --
// [pfMapContext] --
// [pContextData] --
//
// History: 9-30-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
NegCrackServerRequestAndReply(
ULONG_PTR dwCreds,
PSECURITY_STRING Target,
ULONG fContextReq,
ULONG TargetDataRep,
PSecBufferDesc pInput,
PULONG_PTR pdwNewContext,
PSecBufferDesc pOutput,
PULONG pfContextAttr,
PTimeStamp ptsExpiry,
PBYTE pfMapContext,
PSecBuffer pContextData)
{
SECURITY_STATUS scRet;
struct InitialNegToken * Request = NULL ;
ASN1octetstring_t EncodedData;
int Result;
ULONG Pdu = InitialNegToken_PDU;
PSecBuffer Buffer;
struct MechTypeList *pMechs = NULL;
PNEG_CREDS Creds ;
PNEG_CONTEXT Context = NULL ;
UNICODE_STRING NegotiateHint = {0};
ANSI_STRING AnsiHint = {0};
RtlInitUnicodeString(
&NegotiateHint,
NULL
);
//
// Initialize stuff:
//
//
// First, verify the input buffer contains a Request token, and crack it.
//
scRet = NegpParseBuffers( pInput, TRUE, &Buffer, NULL );
if ( !Buffer )
{
scRet = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
if ( !NT_SUCCESS( scRet ) )
{
goto Cleanup;
}
Creds = (PNEG_CREDS) dwCreds ;
EncodedData.value = (PUCHAR) Buffer->pvBuffer ;
EncodedData.length = Buffer->cbBuffer ;
Request = NULL ;
Result = SpnegoUnpackData(
EncodedData.value,
EncodedData.length,
Pdu,
(PVOID *)&Request);
if ( Result != 0 )
{
scRet = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// This function only handles Request tokens. If we ended up with a reply,
// then the server is in trouble, or we are...
//
if ( (Pdu != InitialNegToken_PDU) ||
(Request->negToken.choice == negTokenTarg_chosen) ||
NegpCompareOid(
Request->spnegoMech,
NegSpnegoMechOid)
)
{
scRet = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Okay, the server has sent us a list of the packages that he supports,
// possibly some hints as well. We need to go through the list, and figure
// our common subset. At the same time, we need to select the one that we
// want to use, preferably the same as the first one from the server.
//
if ((Request->negToken.u.negTokenInit.bit_mask & NegTokenInit_mechTypes_present) != 0)
{
pMechs = Request->negToken.u.negTokenInit.mechTypes ;
}
//
// Get the negotation hint out.
//
if ((Request->negToken.u.negTokenInit.bit_mask & NegTokenInit_negHints_present) != 0)
{
if ((Request->negToken.u.negTokenInit.negHints.bit_mask & hintName_present) != 0)
{
RtlInitString(
&AnsiHint,
Request->negToken.u.negTokenInit.negHints.hintName
);
scRet = RtlAnsiStringToUnicodeString(
&NegotiateHint,
&AnsiHint,
TRUE // allocate destination
);
if (!NT_SUCCESS(scRet))
{
scRet= SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
}
}
//
// Fall through to common code with normal initial token:
//
scRet = NegBuildRequestToken(
FALSE, // not server side
Creds,
Target,
fContextReq,
TargetDataRep,
pMechs,
&NegotiateHint,
&Context,
pOutput,
ptsExpiry );
Cleanup:
if ( Request )
{
SpnegoFreeData( Pdu, Request );
}
if (NegotiateHint.Buffer != NULL)
{
RtlFreeUnicodeString(&NegotiateHint);
}
if ( NT_SUCCESS( scRet ) )
{
//
// Successfully built token. Set flags:
//
*pfContextAttr = ( ISC_RET_INTERMEDIATE_RETURN ) |
( fContextReq & ISC_REQ_ALLOCATE_MEMORY ?
ISC_RET_ALLOCATED_MEMORY : 0 ) ;
*pfMapContext = FALSE ;
*pdwNewContext = (DWORD_PTR) Context ;
}
return( scRet );
}
//+---------------------------------------------------------------------------
//
// Function: NegpCrackRequest
//
// Synopsis: Crack a Request package, and, based on the creds, determine
// what is supported.
//
// Arguments: [Creds] -- Creds to compare against
// [Request] -- Request to crack
// [MechIndex] -- selected package
// [MechList] -- Receives mech list from request
// [pPackage] -- Package pointer
//
// History: 9-25-96 RichardW Created
//
// Notes: Creds must be locked
//
//----------------------------------------------------------------------------
SECURITY_STATUS
NegpCrackRequest(
IN PNEG_CREDS Creds,
IN NegotiationToken * Request,
OUT PULONG_PTR MechIndex,
OUT struct MechTypeList ** MechList,
OUT PNEG_PACKAGE * pPackage,
OUT NEG_MATCH * pDesiredMatch)
{
DWORD i;
DWORD j;
ULONG MatchingPackage = (ULONG) -1;
struct MechTypeList *pMechs;
PNEG_PACKAGE Package;
NEG_MATCH DesiredMatch;
ULONG MechCount ;
pMechs = Request->u.negTokenInit.mechTypes ;
//
// First, support the "standard" by going through the whole list,
// and determining which ones we support.
//
Package = NULL ;
DesiredMatch = MatchUnknown ;
//
// For each mechanism, see if we have it in the creds. If we have it,
// mark it as acceptible. If this is the first acceptible mech, capture
// it as the now preferred mechanism.
//
while ( pMechs )
{
NegDumpOid( "Incoming Mechanism", pMechs->value );
for ( i = 0 ; i < Creds->Count ; i++ )
{
NegDumpOid( "Comparing to Mechanism", Creds->Creds[i].Package->ObjectId );
if ( NegpCompareOid( pMechs->value,
Creds->Creds[i].Package->ObjectId ) == 0 )
{
if ( !Package )
{
Package = Creds->Creds[i].Package ;
if ( DesiredMatch == MatchUnknown )
{
DesiredMatch = PreferredSucceed ;
}
else
{
DesiredMatch = MatchSucceed ;
}
MatchingPackage = i;
break;
}
}
}
pMechs = pMechs->next ;
if ( DesiredMatch == MatchUnknown )
{
DesiredMatch = MatchFailed ;
}
}
*MechIndex = MatchingPackage ;
*pPackage = Package ;
*MechList = Request->u.negTokenInit.mechTypes ;
*pDesiredMatch = DesiredMatch ;
return( 0 );
}
//+---------------------------------------------------------------------------
//
// Function: NegHandleSubsequentClientRequest
//
// Synopsis: Handles a client request after the initial NegTokenInit
//
// Arguments:
//
// History: 5-26-97 MikeSw Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
NegHandleSubsequentClientRequest(
ULONG_PTR dwCreds,
PNEG_CONTEXT Context,
ULONG fContextReq,
ULONG TargetDataRep,
ULONG Pdu,
NegotiationToken * Request,
PULONG_PTR pdwNewContext,
PSecBufferDesc pOutput,
PULONG pfContextAttr,
PTimeStamp ptsExpiry,
PBYTE pfMapContext,
PSecBuffer pContextData )
{
SECURITY_STATUS scRet = SEC_E_OK;
#ifndef WIN32_CHICAGO
PNEG_CREDS Creds ;
NegotiationToken Reply;
CredHandle TempCredHandle;
CtxtHandle TempHandle;
SecBufferDesc AcceptBufferDesc;
SecBuffer AcceptBuffer;
SecBufferDesc ResponseBufferDesc;
SecBuffer ResponseBuffer;
SecBuffer MappedBuffer;
PSecBuffer pToken;
BOOLEAN MappedContext;
ULONG_PTR PackageId;
ASN1octetstring_t EncodedData;
int Result;
PNEG_PACKAGE Package ;
EncodedData.value = NULL;
EncodedData.length = 0;
RtlZeroMemory(
&ResponseBuffer,
sizeof(SecBuffer)
);
RtlZeroMemory(
&MappedBuffer,
sizeof(SecBuffer)
);
//
// The negotiation context should have been created during the first call
// to AcceptSecurityContext, so if it isn't present this is an
// error.
//
if (Context == NULL)
{
scRet = SEC_E_INVALID_HANDLE;
goto Cleanup;
}
//
// Verify that there is an output token to return something in.
//
scRet = NegpParseBuffers( pOutput, TRUE, &pToken, NULL );
if ( !NT_SUCCESS( scRet ) || (pToken == NULL) )
{
DebugLog((DEB_TRACE_NEG, "No output token supplied to NegHandleSubs.ClientReq\n"));
scRet = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Verify the creds passed in
//
Creds = (PNEG_CREDS) dwCreds ;
if (Creds == NULL)
{
Creds = Context->Creds;
}
else if (Creds != Context->Creds)
{
//
// Could be a multi:
//
if ( ( Creds->Flags & NEGCRED_MULTI ) == 0 )
{
DebugLog((DEB_TRACE_NEG, "Bad context handle passed to Accept: 0x%p instead of 0x%p\n",
Creds, Context->Creds ));
scRet = SEC_E_INVALID_HANDLE;
goto Cleanup;
}
//
// Walk the multi credential and verify the cred ptr?
//
Creds = Context->Creds ;
}
//
// if the security token area is empty, but the mechListMIC is
// present, then the client has completed (and we should have,
// also), and we should verify the mechListMIC.
//
if ( ( ( Request->u.negTokenTarg.responseToken.length == 0 ) &&
( ( Context->Flags & NEG_CONTEXT_UPLEVEL ) != 0 ) ) ||
( Request->u.negTokenTarg.bit_mask == 0 ) )
{
//
// Check the MIC:
//
SpnegoFreeData( Pdu, Request );
Request = NULL;
if ( Context->LastStatus != STATUS_SUCCESS )
{
return SEC_E_INVALID_TOKEN ;
}
if (Context->Mapped)
{
*pfMapContext = Context->Mapped;
*pContextData = Context->MappedBuffer;
RtlZeroMemory(
&Context->MappedBuffer,
sizeof(SecBuffer)
);
}
//
// Whack the output handle with the one returned from the
// package.
//
LsapChangeHandle( HandleReplace,
NULL,
&Context->Handle );
Context->Handle.dwLower = 0 ;
Context->Handle.dwUpper = 0 ;
pToken->cbBuffer = 0 ;
*ptsExpiry = Context->Expiry ;
Context = NULL ;
return STATUS_SUCCESS ;
}
//
// Get the locked information out of the credentials
//
NegReadLockCreds(Creds);
TempCredHandle = Creds->Creds[ Context->CredIndex ].Handle;
Package = Creds->Creds[Context->CredIndex].Package;
NegUnlockCreds(Creds);
//
// Build the input to AcceptSecurityContext
//
if ( Request->u.negTokenTarg.responseToken.length != 0 )
{
AcceptBuffer.pvBuffer = Request->u.negTokenTarg.responseToken.value;
AcceptBuffer.cbBuffer = Request->u.negTokenTarg.responseToken.length;
Context->Flags |= NEG_CONTEXT_UPLEVEL ;
}
else if ( Request->u.negTokenTarg.mechListMIC.length != 0 )
{
AcceptBuffer.pvBuffer = Request->u.negTokenTarg.mechListMIC.value ;
AcceptBuffer.cbBuffer = Request->u.negTokenTarg.mechListMIC.length ;
}
AcceptBuffer.BufferType = SECBUFFER_READONLY | SECBUFFER_TOKEN ;
AcceptBufferDesc.ulVersion = SECBUFFER_VERSION ;
AcceptBufferDesc.cBuffers = 1;
AcceptBufferDesc.pBuffers = &AcceptBuffer ;
ResponseBuffer.cbBuffer = Package->LsaPackage->TokenSize ;
ResponseBuffer.BufferType = SECBUFFER_TOKEN ;
ResponseBuffer.pvBuffer = LsapAllocateLsaHeap(
ResponseBuffer.cbBuffer );
if ( ResponseBuffer.pvBuffer == NULL )
{
scRet = SEC_E_INSUFFICIENT_MEMORY ;
goto Cleanup ;
}
ResponseBufferDesc.ulVersion = SECBUFFER_VERSION ;
ResponseBufferDesc.cBuffers = 1;
ResponseBufferDesc.pBuffers = &ResponseBuffer ;
if ((Context->Flags & NEG_CONTEXT_PACKAGE_CALLED) != 0)
{
TempHandle = Context->Handle ;
}
else
{
TempHandle.dwUpper = TempHandle.dwLower = 0;
}
PackageId = GetCurrentPackageId();
//
// Call the package. Note that if the package has already mapped the
// context we don't want it overwriting the existing mapping. Hence,
// don't pass in the real value.
//
#ifndef WIN32_CHICAGO
scRet = WLsaAcceptContext(
&TempCredHandle,
&TempHandle,
&AcceptBufferDesc,
(fContextReq & (~(ASC_REQ_ALLOCATE_MEMORY))),
TargetDataRep,
&Context->Handle,
&ResponseBufferDesc,
&Context->Attributes,
&Context->Expiry,
&MappedContext,
&MappedBuffer );
#else
scRet = AcceptSecurityContext(
&TempCredHandle,
&TempHandle,
&AcceptBufferDesc,
(fContextReq & (~(ASC_REQ_ALLOCATE_MEMORY))),
TargetDataRep,
&Context->Handle,
&ResponseBufferDesc,
&Context->Attributes,
&Context->Expiry
);
#endif // WIN32_CHICAGO
#if DBG
NegReadLockCreds( Creds );
DebugLog(( DEB_TRACE_NEG, "WLsaAcceptContext( %ws ) returned %x\n",
Creds->Creds[ Context->CredIndex ].Package->LsaPackage->Name.Buffer,
scRet ));
NegUnlockCreds(Creds);
#endif
Context->CallCount++ ;
SetCurrentPackageId( PackageId );
//
// Done with request data
//
SpnegoFreeData( Pdu, Request );
Request = NULL;
if ( !NT_SUCCESS( scRet ) )
{
DebugLog((DEB_TRACE, "Neg Failure from package %d, %#x\n",
Context->CredIndex, scRet ));
DsysAssert( scRet != SEC_E_INVALID_HANDLE )
goto Cleanup;
}
Context->Flags |= NEG_CONTEXT_PACKAGE_CALLED;
Context->LastStatus = scRet ;
//
// Build the output token, another NegTokenTarg.
//
Reply.choice = negTokenTarg_chosen;
Reply.u.negTokenTarg.bit_mask = negResult_present;
if (ResponseBuffer.cbBuffer != 0)
{
Reply.u.negTokenTarg.bit_mask |= responseToken_present;
}
Reply.u.negTokenTarg.responseToken.value = (PUCHAR) ResponseBuffer.pvBuffer;
Reply.u.negTokenTarg.responseToken.length = (int) ResponseBuffer.cbBuffer;
//
// Fill in the negotation result field. In addition, fill in any
// context mapping data.
//
if ( MappedContext )
{
DsysAssert( !Context->Mapped );
Context->Mapped = TRUE ;
Context->MappedBuffer = MappedBuffer ;
RtlZeroMemory(
&MappedBuffer,
sizeof(SecBuffer)
);
}
//
// generate the MIC on the last blob
//
if ( scRet == SEC_E_OK )
{
//
// Once the mic is generated, the
// list of mechs is no longer needed.
//
if ( Context->SupportedMechs )
{
DebugLog(( DEB_TRACE_NEG, "Freeing mech list for %p\n", Context ));
if ((Context->Flags & NEG_CONTEXT_FREE_EACH_MECH))
{
NegpFreeMechList(Context->SupportedMechs);
}
else
{
LsapFreeLsaHeap(Context->SupportedMechs);
}
Context->SupportedMechs = NULL ;
}
}
if ( Context->LastStatus == SEC_E_OK )
{
Reply.u.negTokenTarg.negResult = accept_completed;
}
else
{
Reply.u.negTokenTarg.negResult = accept_incomplete;
}
if (scRet == SEC_E_OK)
{
if (Context->Mapped)
{
*pfMapContext = Context->Mapped;
*pContextData = Context->MappedBuffer;
RtlZeroMemory(
&Context->MappedBuffer,
sizeof(SecBuffer)
);
}
else if (MappedContext)
{
*pfMapContext = TRUE;
*pContextData = MappedBuffer;
RtlZeroMemory(
&MappedBuffer,
sizeof(SecBuffer)
);
}
}
*ptsExpiry = Context->Expiry;
*pfContextAttr = Context->Attributes;
//
// Encode reply token:
//
Result = SpnegoPackData(
&Reply,
NegotiationToken_PDU,
&(EncodedData.length),
&(EncodedData.value));
if (Result != 0)
{
scRet = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
DsysAssert( NT_SUCCESS(NegpValidateBuffer( EncodedData.value, EncodedData.length ) ) );
if ( fContextReq & ASC_REQ_ALLOCATE_MEMORY )
{
pToken->pvBuffer = EncodedData.value ;
*pfContextAttr |= ASC_RET_ALLOCATED_MEMORY ;
EncodedData.value = NULL ;
pToken->cbBuffer = EncodedData.length ;
}
else
{
if ( pToken->cbBuffer >= (ULONG) EncodedData.length )
{
RtlCopyMemory( pToken->pvBuffer,
EncodedData.value,
EncodedData.length );
pToken->cbBuffer = EncodedData.length ;
}
else if ( ( ( fContextReq & ASC_REQ_FRAGMENT_TO_FIT ) != 0 ) &&
( pToken->cbBuffer >= SPNEGO_MINIMUM_BUFFER ) )
{
//
// Ok, we need to whack the context to indicate that we are
// fragmenting, and return only what the caller can handle.
//
Context->Message = EncodedData.value ;
Context->TotalSize = EncodedData.length ;
Context->Flags |= NEG_CONTEXT_FRAGMENTING ;
//
// set this to NULL so it doesn't get freed later
//
EncodedData.value = NULL ;
RtlCopyMemory(
pToken->pvBuffer,
Context->Message,
pToken->cbBuffer );
Context->CurrentSize = pToken->cbBuffer ;
}
else
{
scRet = SEC_E_INSUFFICIENT_MEMORY ;
goto Cleanup ;
}
}
if (scRet == SEC_E_OK)
{
//
// Whack the output handle with the one returned from the
// package.
//
LsapChangeHandle( HandleReplace,
NULL,
&Context->Handle );
Context->Handle.dwLower = 0 ;
Context->Handle.dwUpper = 0 ;
Context = NULL ;
}
else
{
//
// Make sure we never say that we mapped when we are in the
// intermediate state.
//
DsysAssert( !(*pfMapContext) );
}
Cleanup:
if (EncodedData.value != NULL)
{
LsapFreeLsaHeap(EncodedData.value);
}
if (ResponseBuffer.pvBuffer != NULL)
{
LsapFreeLsaHeap(ResponseBuffer.pvBuffer);
}
if (MappedBuffer.pvBuffer != NULL)
{
LsapFreeLsaHeap(MappedBuffer.pvBuffer);
}
#endif // WIN32_CHICAGO
return(scRet);
}
//+-------------------------------------------------------------------------
//
// Function: NegHandleClientRequest
//
// Synopsis: Handles a call to AcceptSecurityContext other than an
// initial one with no input. This routine either figures our
// what package to call or calls a package already selected to
// do the Accept.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
SECURITY_STATUS
NegHandleClientRequest(
ULONG_PTR dwCreds,
PNEG_CONTEXT pContext,
ULONG fContextReq,
ULONG TargetDataRep,
PSecBufferDesc pInput,
PULONG_PTR pdwNewContext,
PSecBufferDesc pOutput,
PULONG pfContextAttr,
PTimeStamp ptsExpiry,
PBYTE pfMapContext,
PSecBuffer pContextData )
{
PNEG_CREDS Creds ;
PNEG_CONTEXT Context ;
SECURITY_STATUS scRet ;
SECURITY_STATUS MapStatus ;
NegotiationToken * Request = NULL ;
InitialNegToken * InitialRequest = NULL;
NegotiationToken Response = {0};
ASN1octetstring_t EncodedData ;
int Result ;
ULONG Pdu = InitialNegToken_PDU;
PSecBuffer Buffer ;
PSecBuffer pToken ;
PNEG_PACKAGE Package;
NEG_MATCH DesiredMatch;
SecBufferDesc AcceptBufferDesc;
SecBuffer AcceptBuffer;
SecBufferDesc ResponseBufferDesc;
SecBuffer ResponseBuffer;
SecBuffer UserResponseBuffer; // use user buffer for in-place operations if large enough
struct MechTypeList *MechList = NULL;
CtxtHandle TempHandle;
struct _enum1 * Results;
ULONG_PTR PackageId;
BOOLEAN CredentialReferenced = FALSE;
//
// Initialize stuff:
//
ResponseBuffer.pvBuffer = NULL;
Creds = (PNEG_CREDS) dwCreds ;
Context = pContext ;
if ( ( Creds == NULL ) &&
( Context != NULL ) )
{
Creds = Context->Creds ;
}
//
// First, verify the input buffer contains a Request token, and crack it.
//
scRet = NegpParseBuffers( pInput, TRUE, &Buffer, NULL );
if ( !Buffer )
{
return( SEC_E_INVALID_TOKEN );
}
if ( !NT_SUCCESS( scRet ) )
{
return( scRet );
}
//
// Verify that we have an output buffer
//
scRet = NegpParseBuffers( pOutput, TRUE, &pToken, NULL );
if ( !NT_SUCCESS( scRet ) ||
( pToken == NULL ) )
{
goto Cleanup ;
}
//
// We need a return token
//
if (pToken == NULL)
{
DebugLog((DEB_TRACE_NEG,"No output token for NegHandleClientRequest\n"));
scRet = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Compatibility:
//
// If we get a zero length blob, and the context has completed,
// it means we're dealing with old clients
//
if ( ( Buffer->cbBuffer == 0 ) &&
( Context ) )
{
pToken->cbBuffer = 0 ;
if ( Context->LastStatus != STATUS_SUCCESS )
{
return SEC_E_INVALID_TOKEN ;
}
#ifndef WIN32_CHICAGO
if (Context->Mapped)
{
*pfMapContext = Context->Mapped;
*pContextData = Context->MappedBuffer;
RtlZeroMemory(
&Context->MappedBuffer,
sizeof(SecBuffer)
);
}
//
// Whack the output handle with the one returned from the
// package.
//
LsapChangeHandle( HandleReplace,
NULL,
&Context->Handle );
Context->Handle.dwLower = 0 ;
Context->Handle.dwUpper = 0 ;
*ptsExpiry = Context->Expiry ;
pToken->cbBuffer = 0 ;
Context = NULL ;
return STATUS_SUCCESS ;
#endif
}
EncodedData.value = (PUCHAR) Buffer->pvBuffer ;
EncodedData.length = Buffer->cbBuffer ;
Request = NULL ;
Result = SpnegoUnpackData(
EncodedData.value,
EncodedData.length,
Pdu,
(PVOID *)&InitialRequest);
//
// If unable, try it as a second-pass.
//
if ( Result != 0 )
{
Pdu = NegotiationToken_PDU;
Result = SpnegoUnpackData(
EncodedData.value,
EncodedData.length,
Pdu,
(PVOID *)&Request);
//
// if the token didn't match either, give up now.
//
if (Result != 0)
{
#ifndef WIN32_CHICAGO
NegpReportEvent(
EVENTLOG_WARNING_TYPE,
NEGOTIATE_UNKNOWN_PACKET,
CATEGORY_NEGOTIATE,
0,
0 );
#endif
return(SEC_E_INVALID_TOKEN);
}
}
else
{
Request = &InitialRequest->negToken;
}
//
// This function only handles Negotiation tokens. If we ended up with
// anything else, something is wrong.
//
if ( (Pdu != NegotiationToken_PDU) &&
(Pdu != InitialNegToken_PDU) )
{
scRet = SEC_E_INVALID_TOKEN ;
goto Cleanup ;
}
//
// If this is an initial request, verify the OID
//
if (InitialRequest != NULL)
{
if (NegpCompareOid(
NegSpnegoMechOid,
InitialRequest->spnegoMech
))
{
scRet = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
}
//
// Check to see if we already called Accept once on a package. If so,
// we want to use the existing context handle.
//
if ( Request->choice == negTokenTarg_chosen )
{
return(NegHandleSubsequentClientRequest(
dwCreds,
pContext,
fContextReq,
TargetDataRep,
Pdu,
Request,
pdwNewContext,
pOutput,
pfContextAttr,
ptsExpiry,
pfMapContext,
pContextData ) );
}
//
// Ok, we have a request blob. Figure out what they want,
//
NegReadLockCreds( Creds );
scRet = NegpCrackRequest( Creds,
Request,
& PackageId,
& MechList,
& Package,
& DesiredMatch );
if ( !NT_SUCCESS( scRet ) )
{
NegUnlockCreds( Creds );
goto Cleanup ;
}
if (DesiredMatch == MatchFailed)
{
//
// There were no common packages, so return an error.
//
NegUnlockCreds( Creds );
#ifndef WIN32_CHICAGO
NegpReportEvent(
EVENTLOG_WARNING_TYPE,
NEGOTIATE_UNKNOWN_PACKAGE,
CATEGORY_NEGOTIATE,
0,
0 );
#endif
DebugLog(( DEB_TRACE, "No common packages for negotiator\n"));
scRet = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
DsysAssert( Package != NULL );
//
// Found a common package. Was it the first one in the request
// list? If so, then check for a desired token and pass it down
// to the other package
//
DebugLog(( DEB_TRACE_NEG, "Common Package is %ws\n",
Package->LsaPackage->Name.Buffer ));
Response.choice = negTokenTarg_chosen ;
Response.u.negTokenTarg.supportedMech = Package->ObjectId ;
Response.u.negTokenTarg.bit_mask |= supportedMech_present | negResult_present;
if ( !Context )
{
Context = NegpCreateContext();
if ( !Context )
{
NegUnlockCreds( Creds );
scRet = SEC_E_INSUFFICIENT_MEMORY ;
goto Cleanup ;
}
}
//
// Save away the mechlist for the mic at the end
//
if ( Context->SupportedMechs )
{
if ((Context->Flags & NEG_CONTEXT_FREE_EACH_MECH))
{
NegpFreeMechList(Context->SupportedMechs);
}
else
{
LsapFreeLsaHeap(Context->SupportedMechs);
}
}
Context->SupportedMechs = NegpCopyMechList(MechList);
if (Context->SupportedMechs == NULL)
{
NegUnlockCreds(Creds);
scRet = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup ;
}
Context->Flags |= NEG_CONTEXT_FREE_EACH_MECH;
DebugLog(( DEB_TRACE_NEG, "Adding mech list for %p\n", Context));
//
// Reference the credentials so they don't go away before we
// finish with the context.
//
if (Context->Creds == NULL)
{
Context->Creds = Creds ;
NegUnlockCreds(Creds);
NegWriteLockCreds(Creds);
Creds->RefCount++;
NegUnlockCreds(Creds);
NegReadLockCreds(Creds);
}
Context->CredIndex = PackageId ;
if ( ( DesiredMatch == PreferredSucceed ) &&
( Request->u.negTokenInit.bit_mask & NegTokenInit_mechToken_present ) )
{
CredHandle TempCredHandle;
#ifndef WIN32_CHICAGO
NegpReportEvent(
EVENTLOG_INFORMATION_TYPE,
NEGOTIATE_MESSAGE_DECODED,
CATEGORY_NEGOTIATE,
0,
1,
&Package->LsaPackage->Name
);
#endif
DebugLog(( DEB_TRACE_NEG, "Desired Package match with token!\n"));
TempHandle = Context->Handle ;
PackageId = GetCurrentPackageId();
TempCredHandle = Creds->Creds[ Context->CredIndex ].Handle;
//
// Unlock the credentials while we make the call
//
NegUnlockCreds(Creds);
//
// Build up a buffer for accept
//
AcceptBuffer.pvBuffer = Request->u.negTokenInit.mechToken.value;
AcceptBuffer.cbBuffer = Request->u.negTokenInit.mechToken.length;
AcceptBuffer.BufferType = SECBUFFER_READONLY | SECBUFFER_TOKEN ;
AcceptBufferDesc.ulVersion = SECBUFFER_VERSION ;
AcceptBufferDesc.cBuffers = 1;
AcceptBufferDesc.pBuffers = &AcceptBuffer ;
ResponseBufferDesc.ulVersion = SECBUFFER_VERSION ;
ResponseBufferDesc.cBuffers = 1;
if( pToken->cbBuffer >= Package->LsaPackage->TokenSize )
{
UserResponseBuffer.cbBuffer = pToken->cbBuffer;
UserResponseBuffer.BufferType = SECBUFFER_TOKEN ;
UserResponseBuffer.pvBuffer = pToken->pvBuffer;
ResponseBufferDesc.pBuffers = &UserResponseBuffer ;
} else {
ResponseBuffer.cbBuffer = Package->LsaPackage->TokenSize ;
ResponseBuffer.BufferType = SECBUFFER_TOKEN ;
ResponseBuffer.pvBuffer = LsapAllocateLsaHeap(
ResponseBuffer.cbBuffer );
if ( ResponseBuffer.pvBuffer == NULL )
{
scRet = SEC_E_INSUFFICIENT_MEMORY ;
goto Cleanup ;
}
ResponseBufferDesc.pBuffers = &ResponseBuffer ;
}
#ifndef WIN32_CHICAGO
scRet = WLsaAcceptContext(
&TempCredHandle,
&TempHandle,
&AcceptBufferDesc,
(fContextReq & (~(ASC_REQ_ALLOCATE_MEMORY))),
SECURITY_NATIVE_DREP,
&Context->Handle,
&ResponseBufferDesc,
&Context->Attributes,
&Context->Expiry,
&Context->Mapped,
&Context->MappedBuffer );
#else
scRet = AcceptSecurityContext(
&TempCredHandle,
&TempHandle,
&AcceptBufferDesc,
(fContextReq & (~(ASC_REQ_ALLOCATE_MEMORY))),
SECURITY_NATIVE_DREP,
&Context->Handle,
&ResponseBufferDesc,
&Context->Attributes,
&Context->Expiry
);
#endif // WIN32_CHICAGO
SetCurrentPackageId( PackageId );
Context->CallCount++ ;
DebugLog(( DEB_TRACE_NEG, "WLsaAcceptContext( %ws ) returned %x\n",
Creds->Creds[ Context->CredIndex ].Package->LsaPackage->Name.Buffer,
scRet ));
if ( !NT_SUCCESS( scRet ) )
{
DebugLog((DEB_TRACE, "Neg Failure from package %ws, %#x\n",
Package->LsaPackage->Name.Buffer, scRet ));
if( ResponseBuffer.pvBuffer )
{
LsapFreeLsaHeap( ResponseBuffer.pvBuffer );
}
goto Cleanup ;
}
NegReadLockCreds(Creds);
if ( NT_SUCCESS( scRet ) &&
(Context->Attributes & ASC_RET_EXTENDED_ERROR ) )
{
if ( (Creds->Flags & (NEGCRED_MULTI | NEGCRED_MULTI_PART)) != 0 )
{
scRet = SEC_E_LOGON_DENIED ;
DebugLog(( DEB_TRACE, "Multi-cred failure, no return message\n" ));
NegUnlockCreds( Creds );
if( ResponseBuffer.pvBuffer )
{
LsapFreeLsaHeap( ResponseBuffer.pvBuffer );
}
goto Cleanup ;
}
}
//
// Now: push that info back out to our caller,
//
Context->LastStatus = scRet ;
//
// Mark the context to indicate that we already called
// AcceptSecurityContext once.
//
Context->Flags |= NEG_CONTEXT_PACKAGE_CALLED;
//
// Set the response pointers up so they get folded in to the
// response packet
//
Response.u.negTokenTarg.responseToken.length = ResponseBufferDesc.pBuffers[0].cbBuffer ;
Response.u.negTokenTarg.responseToken.value = (PUCHAR) ResponseBufferDesc.pBuffers[0].pvBuffer ;
Response.u.negTokenTarg.bit_mask |= responseToken_present ;
*ptsExpiry = Context->Expiry;
*pfContextAttr = Context->Attributes;
if (scRet == STATUS_SUCCESS)
{
Response.u.negTokenTarg.negResult = accept_completed;
//
// Generate a MIC here
//
//
// Get rid of the mech list now, since
// it has been mic'd. As it were.
//
if ( Context->SupportedMechs )
{
DebugLog(( DEB_TRACE_NEG, "Freeing mech list for %p\n", Context ));
NegpFreeMechList( Context->SupportedMechs );
Context->SupportedMechs = NULL ;
}
// scRet = SEC_I_CONTINUE_NEEDED ;
}
else
{
DsysAssert((scRet == SEC_I_CONTINUE_NEEDED) ||
(scRet == SEC_I_COMPLETE_NEEDED) ||
(scRet == SEC_I_COMPLETE_AND_CONTINUE))
Response.u.negTokenTarg.negResult = accept_incomplete;
}
//
// Fall through to rest of handling
//
}
else
{
//
// We have a common package, but either there is no desired token
// present, or the common package was not the desired one. The
// selected package is in the structure already, so we don't have
// to do anything here.
//
#ifndef WIN32_CHICAGO
NegpReportEvent(
EVENTLOG_INFORMATION_TYPE,
NEGOTIATE_MESSAGE_DECODED_NO_TOKEN,
CATEGORY_NEGOTIATE,
0,
1,
&Package->LsaPackage->Name
);
#endif
scRet = SEC_I_CONTINUE_NEEDED;
DebugLog(( DEB_TRACE_NEG, "Common package has no token\n"));
Response.u.negTokenTarg.negResult = accept_incomplete;
}
NegUnlockCreds( Creds );
//
// Assemble reply token:
//
EncodedData.value = 0 ;
EncodedData.length = 0 ;
Result = SpnegoPackData(
&Response,
NegotiationToken_PDU,
&(EncodedData.length),
&(EncodedData.value));
//
// Clean up:
//
if ( ResponseBuffer.pvBuffer )
{
LsapFreeLsaHeap( ResponseBuffer.pvBuffer );
ResponseBuffer.pvBuffer = NULL ;
}
if (InitialRequest != NULL)
{
SpnegoFreeData( Pdu, InitialRequest );
InitialRequest = NULL;
Request = NULL;
}
else if ( Request )
{
SpnegoFreeData( Pdu, Request );
Request = NULL;
}
if ( Result )
{
DebugLog((DEB_ERROR, "Failed to encode data: %d\n", Result ));
scRet = SEC_E_INVALID_TOKEN ;
goto Cleanup ;
}
else
{
if ( fContextReq & ASC_REQ_ALLOCATE_MEMORY )
{
pToken->pvBuffer = EncodedData.value ;
*pfContextAttr |= ASC_RET_ALLOCATED_MEMORY ;
EncodedData.value = NULL ;
pToken->cbBuffer = EncodedData.length ;
}
else
{
if ( pToken->cbBuffer >= (ULONG) EncodedData.length )
{
RtlCopyMemory( pToken->pvBuffer,
EncodedData.value,
EncodedData.length );
pToken->cbBuffer = EncodedData.length ;
}
else if ( ( ( fContextReq & ASC_REQ_FRAGMENT_TO_FIT ) != 0 ) &&
( pToken->cbBuffer >= SPNEGO_MINIMUM_BUFFER ) )
{
//
// Ok, we need to whack the context to indicate that we are
// fragmenting, and return only what the caller can handle.
//
Context->Message = EncodedData.value ;
Context->TotalSize = EncodedData.length ;
Context->Flags |= NEG_CONTEXT_FRAGMENTING ;
//
// set this to NULL so it doesn't get freed later
//
EncodedData.value = NULL ;
RtlCopyMemory(
pToken->pvBuffer,
Context->Message,
pToken->cbBuffer );
Context->CurrentSize = pToken->cbBuffer ;
}
else
{
scRet = SEC_E_INSUFFICIENT_MEMORY ;
goto Cleanup ;
}
}
//
// If the context has been mapped by the underlying package,
// get out of the way:
//
#ifndef WIN32_CHICAGO
if ( scRet == SEC_E_OK )
{
if ( Context->Mapped )
{
*pfContextAttr = Context->Attributes ;
if ( fContextReq & ASC_REQ_ALLOCATE_MEMORY )
{
*pfContextAttr |= ASC_RET_ALLOCATED_MEMORY ;
}
*ptsExpiry = Context->Expiry ;
*pfMapContext = Context->Mapped ;
*pContextData = Context->MappedBuffer ;
//
// Set this to NULL so we don't later try
// to free it.
//
Context->MappedBuffer.pvBuffer = NULL ;
}
LsapChangeHandle( HandleReplace,
NULL,
&Context->Handle );
Context->Handle.dwLower = 0 ;
Context->Handle.dwUpper = 0 ;
if ( !pContext )
{
//
// If we created this context during this call, get
// rid of it now
//
NegpDeleteContext( Context );
}
Context = NULL ;
}
else
#endif // WIN32_CHICAGO
{
*pdwNewContext = (DWORD_PTR) Context ;
}
}
if ( EncodedData.value )
{
LsapFreeLsaHeap( EncodedData.value );
}
return( scRet );
Cleanup:
if (InitialRequest != NULL)
{
SpnegoFreeData( Pdu, InitialRequest );
}
else if ( Request )
{
SpnegoFreeData( Pdu, Request );
}
if ( Context )
{
if ( Context->Handle.dwLower )
{
#ifndef WIN32_CHICAGO
WLsaDeleteContext( &Context->Handle );
#else
DeleteSecurityContext( &Context->Handle );
#endif // WIN32_CHICAGO
Context->Handle.dwLower = 0;
}
if ( pContext == NULL )
{
NegpDeleteContext( Context );
}
}
return( scRet );
}
//+-------------------------------------------------------------------------
//
// Function: NegHandleServerReply
//
// Synopsis: Handles a subsequent call to InitializeSecurityContext
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
SECURITY_STATUS
NegHandleServerReply(
ULONG_PTR dwCreds,
PNEG_CONTEXT pContext,
PSECURITY_STRING Target,
ULONG fContextReq,
ULONG TargetDataRep,
PSecBufferDesc pInput,
PULONG_PTR pdwNewContext,
PSecBufferDesc pOutput,
PULONG pfContextAttr,
PTimeStamp ptsExpiry,
PBYTE pfMapContext,
PSecBuffer pContextData)
{
SECURITY_STATUS scRet;
NegotiationToken * Reply = NULL ;
NegotiationToken Request;
ASN1octetstring_t EncodedData = {0};
int Result;
ULONG Pdu = NegotiationToken_PDU;
PSecBuffer Buffer;
struct MechTypeList *pMechs;
PNEG_CREDS Creds;
PNEG_CONTEXT Context ;
ULONG_PTR PackageIndex ;
PNEG_PACKAGE Package ;
SecBuffer InitBuffer ;
SecBufferDesc InitBufferDesc ;
ULONG_PTR PackageId ;
PCtxtHandle pInitHandle ;
CtxtHandle TempHandle = { 0, 0 };
ULONG LocalContextReq = fContextReq;
ULONG LocalContextAttr = 0 ;
BOOLEAN ClientFinished = FALSE;
BOOLEAN ServerFinished = FALSE;
SecBuffer OutputToken = {0};
SecBufferDesc OutputDescription ;
PSecBuffer OutputBuffer = NULL;
CredHandle TempCredHandle ;
RtlZeroMemory(
&Request,
sizeof(NegotiationToken)
);
RtlZeroMemory(
&OutputToken,
sizeof(SecBuffer)
);
//
// Find the token buffer:
//
scRet = NegpParseBuffers( pInput, TRUE, &Buffer, NULL );
if ( !NT_SUCCESS( scRet ) )
{
return( scRet );
}
if ( !Buffer )
{
return( SEC_E_INVALID_TOKEN );
}
scRet = NegpParseBuffers( pOutput, TRUE, &OutputBuffer, NULL );
if (!NT_SUCCESS(scRet))
{
goto HSR_ErrorReturn;
}
//
// Get the credential handle. If it wasn't passed in, use the one from
// the context.
//
if (dwCreds != 0)
{
Creds = (PNEG_CREDS) dwCreds ;
}
else
{
Creds = pContext->Creds;
}
Context = pContext ;
NegpValidContext( Context );
//
// Decode the reply token:
//
EncodedData.value = (PUCHAR) Buffer->pvBuffer ;
EncodedData.length = Buffer->cbBuffer ;
Reply = NULL ;
Result = SpnegoUnpackData(
EncodedData.value,
EncodedData.length,
Pdu,
(PVOID *)&Reply);
//
// Reset the encoded data value so we don't free it accidentally
//
EncodedData.value = NULL;
if ( Result != 0 )
{
DebugLog(( DEB_TRACE_NEG, "Unknown token from server: %d\n", Result ));
scRet = SEC_E_INVALID_TOKEN ;
goto HSR_ErrorReturn ;
}
if ( Reply->choice != negTokenTarg_chosen )
{
DebugLog(( DEB_TRACE_NEG, "Found Request token, expecting Reply token\n" ));
scRet = SEC_E_INVALID_TOKEN ;
goto HSR_ErrorReturn ;
}
if ((Reply->u.negTokenTarg.bit_mask & negResult_present) != 0)
{
if (Reply->u.negTokenTarg.negResult == reject)
{
DebugLog((DEB_TRACE_NEG,"Server rejected\n"));
scRet = SEC_E_LOGON_DENIED;
goto HSR_ErrorReturn;
}
if (Reply->u.negTokenTarg.negResult == accept_completed)
{
ServerFinished = TRUE;
}
}
//
// Ok, see what the server sent us. In an ideal world, the server will send
// us a preferred, chosen token.
//
InitBuffer.pvBuffer = NULL ;
InitBuffer.cbBuffer = 0 ;
InitBuffer.BufferType = SECBUFFER_TOKEN ;
InitBufferDesc.ulVersion = SECBUFFER_VERSION ;
InitBufferDesc.cBuffers = 1 ;
InitBufferDesc.pBuffers = &InitBuffer ;
if ( Reply->u.negTokenTarg.bit_mask & supportedMech_present )
{
NegReadLockCreds( Creds );
PackageIndex = NegpFindPackageForOid( Creds,
Reply->u.negTokenTarg.supportedMech );
if ( PackageIndex == NEG_INVALID_PACKAGE )
{
NegUnlockCreds( Creds );
NegDumpOid( "Invalid OID returned by server",
Reply->u.negTokenTarg.supportedMech );
scRet = SEC_E_INVALID_TOKEN ;
goto HSR_ErrorReturn ;
}
Package = Creds->Creds[ PackageIndex ].Package ;
NegUnlockCreds( Creds );
DebugLog(( DEB_TRACE_NEG, "Server supports %ws!\n",
Package->LsaPackage->Name.Buffer ));
if ( Reply->u.negTokenTarg.bit_mask & responseToken_present )
{
//
// Oh boy! A Token too!
//
InitBuffer.pvBuffer = Reply->u.negTokenTarg.responseToken.value ;
InitBuffer.cbBuffer = (ULONG) Reply->u.negTokenTarg.responseToken.length ;
Context->Flags |= NEG_CONTEXT_UPLEVEL ;
}
else if ( Reply->u.negTokenTarg.bit_mask & NegTokenTarg_mechListMIC_present )
{
InitBuffer.pvBuffer = Reply->u.negTokenTarg.mechListMIC.value ;
InitBuffer.cbBuffer = Reply->u.negTokenTarg.mechListMIC.length ;
}
}
else
{
//
// If we haven't settled on a package yet, we need a mechanism.
//
if ((Context->Flags & NEG_CONTEXT_PACKAGE_CALLED ) == 0)
{
//
// No token specified, nor preferred mechanism. Find the first
// acceptible package in the returned list
//
DebugLog((DEB_TRACE_NEG, "No preferred mech from the server?\n"));
DebugLog(( DEB_TRACE_NEG, "We must drop into GSS only mode for this to work\n"));
DebugLog(( DEB_ERROR, "No preferred mech from server, not handled yet\n"));
return( SEC_E_INVALID_TOKEN );
}
NegReadLockCreds( Creds );
Package = Creds->Creds[ Context->CredIndex ].Package ;
PackageIndex = Context->CredIndex;
NegUnlockCreds( Creds );
if ( Reply->u.negTokenTarg.bit_mask & responseToken_present )
{
//
// Oh boy! A Token too!
//
InitBuffer.pvBuffer = Reply->u.negTokenTarg.responseToken.value ;
InitBuffer.cbBuffer = (ULONG) Reply->u.negTokenTarg.responseToken.length ;
Context->Flags |= NEG_CONTEXT_UPLEVEL ;
}
else if ( Reply->u.negTokenTarg.bit_mask & NegTokenTarg_mechListMIC_present )
{
InitBuffer.pvBuffer = Reply->u.negTokenTarg.mechListMIC.value ;
InitBuffer.cbBuffer = Reply->u.negTokenTarg.mechListMIC.length ;
}
}
DebugLog(( DEB_TRACE_NEG, "Calling package %ws\n",
Package->LsaPackage->Name.Buffer ));
//
// Call into the package, possibly again, possibly the first time, and
// let the package have at it.
//
if ( (Context->CredIndex != PackageIndex) &&
((Context->Flags & NEG_CONTEXT_PACKAGE_CALLED) != 0) )
{
DebugLog(( DEB_TRACE_NEG, "Switched packages, package %ws not selected anymore\n",
Creds->Creds[Context->CredIndex].Package->LsaPackage->Name.Buffer ));
//
// Got to delete the context:
//
PackageId = GetCurrentPackageId();
#ifndef WIN32_CHICAGO
WLsaDeleteContext( &Context->Handle );
#else
DeleteSecurityContext( &Context->Handle );
#endif // WIN32_CHICAGO
SetCurrentPackageId( PackageId );
Context->Flags &= ~NEG_CONTEXT_PACKAGE_CALLED;
//
// Clean up the context information in the handle
//
if ( Context->MappedBuffer.pvBuffer != NULL)
{
LsapFreeLsaHeap(
Context->MappedBuffer.pvBuffer
);
Context->MappedBuffer.pvBuffer = NULL;
Context->MappedBuffer.cbBuffer = 0;
}
Context->Mapped = FALSE;
//
// Reset the last status to make sure we call Initailize again.
//
Context->LastStatus = SEC_I_CONTINUE_NEEDED;
//
// Don't modify TempHandle which is already set to 0,0
//
}
else
{
TempHandle = Context->Handle ;
}
if ( Context->LastStatus == SEC_I_CONTINUE_NEEDED )
{
BOOLEAN LocalContextMapped = FALSE;
SecBuffer LocalContextData = {0,0,NULL};
PackageId = GetCurrentPackageId();
//
// Client side call. Here, we call down to the desired package,
// and have it generate a blob to be encoded and sent over to the
// server.
//
#ifndef WIN32_CHICAGO
OutputToken.pvBuffer = NULL;
OutputToken.cbBuffer = 0;
OutputToken.BufferType = SECBUFFER_TOKEN ;
OutputDescription.ulVersion = SECBUFFER_VERSION ;
OutputDescription.cBuffers = 1;
OutputDescription.pBuffers = &OutputToken ;
LocalContextReq |= ISC_REQ_ALLOCATE_MEMORY ;
LocalContextAttr = 0;
#else
OutputToken.pvBuffer = LocalAlloc(0, Creds->Creds[PackageIndex].Package->LsaPackage->TokenSize);
OutputToken.cbBuffer = Creds->Creds[PackageIndex].Package->LsaPackage->TokenSize;
OutputToken.BufferType = SECBUFFER_TOKEN ;
OutputDescription.ulVersion = SECBUFFER_VERSION ;
OutputDescription.cBuffers = 1;
OutputDescription.pBuffers = &OutputToken ;
LocalContextAttr = 0;
#endif
TempCredHandle = Creds->Creds[ PackageIndex ].Handle ;
#ifndef WIN32_CHICAGO
scRet = WLsaInitContext(&TempCredHandle,
&TempHandle,
&Context->Target,
LocalContextReq,
0,
TargetDataRep,
&InitBufferDesc,
0,
&Context->Handle,
&OutputDescription,
&LocalContextAttr,
ptsExpiry,
&LocalContextMapped,
&LocalContextData );
#else
ANSI_STRING TempAnsiString;
NTSTATUS TempStatus = RtlUnicodeStringToAnsiString(
&TempAnsiString,
&Context->Target,
TRUE // allocate destination
);
scRet = InitializeSecurityContext(
&TempCredHandle,
&TempHandle,
TempAnsiString.Buffer,
LocalContextReq,
0,
TargetDataRep,
&InitBufferDesc,
0,
&Context->Handle,
&OutputDescription,
&LocalContextAttr,
ptsExpiry
);
#endif // WIN32_CHICAGO
DebugLog(( DEB_TRACE_NEG, "Subsequent call to WLsaInitContext( %ws ) returned %x\n",
Creds->Creds[ PackageIndex ].Package->LsaPackage->Name.Buffer,
scRet ));
SetCurrentPackageId( PackageId );
Context->CallCount++ ;
Context->LastStatus = scRet;
if (!NT_SUCCESS(scRet))
{
goto HSR_ErrorReturn;
}
Context->Flags |= NEG_CONTEXT_PACKAGE_CALLED;
if (NT_SUCCESS(scRet) && LocalContextMapped)
{
if (Context->Mapped)
{
DebugLog((DEB_ERROR,"Package tried to map a context twice!\n"));
scRet = SEC_E_INTERNAL_ERROR;
LsapFreeLsaHeap(LocalContextData.pvBuffer);
goto HSR_ErrorReturn;
}
Context->Mapped = LocalContextMapped;
Context->MappedBuffer = LocalContextData;
Context->Expiry = *ptsExpiry ;
}
}
else
{
DebugLog(( DEB_TRACE_NEG, "Package did not need to be called again.\n"));
if (OutputBuffer != NULL )
{
OutputBuffer->cbBuffer = 0;
}
scRet = STATUS_SUCCESS;
}
if ( Reply != NULL )
{
SpnegoFreeData( Pdu, Reply );
Reply = NULL ;
}
//
// Build reply buffer:
//
Request.choice = negTokenTarg_chosen;
Request.u.negTokenTarg.bit_mask = 0 ;
//
// If there was an output buffer, package it up to ship back to the server.
//
if ((OutputToken.cbBuffer != 0) && (OutputToken.pvBuffer != NULL))
{
if (ServerFinished)
{
#ifndef WIN32_CHICAGO
NegpReportEvent(
EVENTLOG_WARNING_TYPE,
NEGOTIATE_UNBALANCED_EXCHANGE,
CATEGORY_NEGOTIATE,
0,
2,
Target,
&Creds->Creds[PackageIndex].Package->LsaPackage->Name
);
#endif
DebugLog((DEB_ERROR,"Server finished but client sending back data\n"));
scRet = SEC_E_INTERNAL_ERROR;
goto HSR_ErrorReturn;
}
//
//
// mechSpecInfo is evil, try to take it out.
//
Request.u.negTokenTarg.bit_mask = responseToken_present;
Request.u.negTokenTarg.responseToken.value = (PUCHAR) OutputToken.pvBuffer;
Request.u.negTokenTarg.responseToken.length = (int) OutputToken.cbBuffer;
}
//
// Compute the MIC of the mechList, so that the other
// guy knows we weren't tampered on the wire
//
if ( scRet == STATUS_SUCCESS )
{
//
// not yet
//
NOTHING ;
}
if ( ( OutputToken.cbBuffer != 0 ) )
{
//
// Encode request token:
//
EncodedData.value = 0 ;
EncodedData.length = 0 ;
Result = SpnegoPackData(
&Request,
NegotiationToken_PDU,
&(EncodedData.length),
&(EncodedData.value));
if ( Result )
{
DebugLog((DEB_ERROR, "Failed to encode data: %d\n", Result ));
scRet = SEC_E_INVALID_TOKEN ;
goto HSR_ErrorReturn ;
}
else
{
if ( fContextReq & ISC_REQ_ALLOCATE_MEMORY )
{
OutputBuffer->pvBuffer = EncodedData.value ;
OutputBuffer->cbBuffer = EncodedData.length ;
*pfContextAttr = LocalContextAttr;
EncodedData.value = NULL ;
}
else
{
if ( OutputBuffer->cbBuffer >= (ULONG) EncodedData.length )
{
RtlCopyMemory( OutputBuffer->pvBuffer,
EncodedData.value,
EncodedData.length );
*pfContextAttr = LocalContextAttr & ~ISC_RET_ALLOCATED_MEMORY;
OutputBuffer->cbBuffer = EncodedData.length ;
}
else if ( ( ( fContextReq & ISC_REQ_FRAGMENT_TO_FIT ) != 0 ) &&
( OutputBuffer->cbBuffer >= SPNEGO_MINIMUM_BUFFER ) )
{
//
// Ok, we need to whack the context to indicate that we are
// fragmenting, and return only what the caller can handle.
//
Context->Message = EncodedData.value ;
Context->TotalSize = EncodedData.length ;
Context->Flags |= NEG_CONTEXT_FRAGMENTING ;
//
// set this to NULL so it doesn't get freed later
//
EncodedData.value = NULL ;
RtlCopyMemory(
OutputBuffer->pvBuffer,
Context->Message,
OutputBuffer->cbBuffer );
Context->CurrentSize = OutputBuffer->cbBuffer ;
}
else
{
scRet = SEC_E_INSUFFICIENT_MEMORY ;
goto HSR_ErrorReturn ;
}
}
}
}
else
{
if ( OutputBuffer )
{
OutputBuffer->cbBuffer = 0 ;
}
}
if ( scRet == STATUS_SUCCESS )
{
if ( ( Context->Flags & NEG_CONTEXT_FRAGMENTING ) ||
( ServerFinished == FALSE ) )
{
scRet = SEC_I_CONTINUE_NEEDED ;
}
else
{
ClientFinished = TRUE ;
}
}
//
// On success, we push the handle back to the client. From this point on,
// the selected package is in charge.
//
if ( ClientFinished )
{
//
// If the data was mapped by the package the first time make sure
// we copy it down now.
//
*pfContextAttr = Context->Attributes ;
*ptsExpiry = Context->Expiry ;
#ifndef WIN32_CHICAGO
if (Context->Mapped)
{
*pfMapContext = Context->Mapped;
*pContextData = Context->MappedBuffer;
//
// Set these to FALSE & NULL so we don't try to
// free them later.
//
Context->MappedBuffer.pvBuffer = NULL;
}
LsapChangeHandle( HandleReplace,
NULL,
&Context->Handle );
Context->Handle.dwLower = 0 ;
Context->Handle.dwUpper = 0 ;
if ( pContext == NULL )
{
NegpDeleteContext( Context );
}
#endif // WIN32_CHICAGO
// NegpDeleteContext( Context );
}
HSR_ErrorReturn:
if ( OutputToken.pvBuffer )
{
LsapFreeLsaHeap( OutputToken.pvBuffer );
OutputToken.pvBuffer = NULL ;
}
if ( EncodedData.value )
{
LsapFreeLsaHeap( EncodedData.value );
}
if ( Reply )
{
SpnegoFreeData( Pdu, Reply );
}
return( scRet );
}
SECURITY_STATUS
NegAddFragmentToContext(
PNEG_CONTEXT Context,
PSecBuffer Fragment
)
{
if ( Fragment->cbBuffer <= (Context->TotalSize - Context->CurrentSize) )
{
RtlCopyMemory(
Context->Message + Context->CurrentSize,
Fragment->pvBuffer,
Fragment->cbBuffer );
Context->CurrentSize += Fragment->cbBuffer ;
if ( Context->CurrentSize == Context->TotalSize )
{
Context->Flags &= (~(NEG_CONTEXT_FRAGMENTING));
return STATUS_SUCCESS ;
}
return SEC_I_CONTINUE_NEEDED ;
}
return SEC_E_INSUFFICIENT_MEMORY ;
}
SECURITY_STATUS
SEC_ENTRY
NegCreateContextFromFragment(
LSA_SEC_HANDLE dwCredHandle,
LSA_SEC_HANDLE dwCtxtHandle,
PSecBuffer Buffer,
ULONG fContextReq,
ULONG TargetDataRep,
PLSA_SEC_HANDLE pdwNewContext,
PSecBufferDesc pOutput,
PULONG pfContextAttr
)
{
NEG_CONTEXT * Context ;
NEG_CREDS * Creds ;
LONG ExpectedSize ;
LONG HeaderSize ;
PUCHAR Message ;
LONG MessageSize ;
SECURITY_STATUS scRet ;
PSecBuffer OutBuf ;
ObjectID DecodedOid = NULL;
NTSTATUS Status;
Creds = (NEG_CREDS *) dwCredHandle ;
if ( Buffer->cbBuffer > MAXLONG )
{
return SEC_E_INVALID_TOKEN ;
}
if ( Buffer->cbBuffer <= 1 )
{
return SEC_E_INVALID_TOKEN ;
}
Message = (PUCHAR) Buffer->pvBuffer ;
if ( (*Message != 0xa0 ) &&
(*Message != 0x60 ) )
{
return SEC_E_INVALID_TOKEN ;
}
MessageSize = Buffer->cbBuffer ;
Message++ ;
MessageSize -- ;
ExpectedSize = Neg_der_read_length(
&Message,
&MessageSize,
&HeaderSize );
if ( ExpectedSize > 0 )
{
//
// Header size + 1 since we already incremented above
//
ExpectedSize += HeaderSize + 1;
}
if ( ExpectedSize < 0 )
{
return SEC_E_INVALID_TOKEN ;
}
if ( (ULONG) ExpectedSize < Buffer->cbBuffer )
{
return SEC_E_INVALID_TOKEN ;
}
//
// Get the OID from the token, if possible, to see if it is for SPNEGO
//
Status = NegpGetTokenOid(
(PUCHAR) Buffer->pvBuffer,
Buffer->cbBuffer,
&DecodedOid
);
if (!NT_SUCCESS(Status))
{
return(Status);
}
//
// Check for spnego
//
if (NegpCompareOid(
DecodedOid,
NegSpnegoMechOid
) != 0)
{
NegpFreeObjectId(DecodedOid);
return(SEC_E_INVALID_TOKEN);
}
NegpFreeObjectId(DecodedOid);
if ( (ULONG) ExpectedSize == Buffer->cbBuffer )
{
*pdwNewContext = 0 ;
return SEC_E_OK ;
}
Context = NegpCreateContext();
if ( !Context )
{
return SEC_E_INSUFFICIENT_MEMORY ;
}
Context->Flags = NEG_CONTEXT_FRAGMENTING ;
Context->Message = (PUCHAR) LsapAllocateLsaHeap( ExpectedSize ) ;
if ( !Context->Message )
{
NegpDeleteContext( Context );
return SEC_E_INSUFFICIENT_MEMORY ;
}
Context->CurrentSize = 0 ;
Context->TotalSize = ExpectedSize ;
scRet = NegAddFragmentToContext(
Context,
Buffer );
if ( !NT_SUCCESS( scRet ) )
{
NegpDeleteContext( Context );
}
else
{
*pdwNewContext = (LSA_SEC_HANDLE) Context ;
DsysAssert( scRet != SEC_E_OK );
NegpParseBuffers( pOutput, FALSE, &OutBuf, NULL );
if ( OutBuf )
{
OutBuf->cbBuffer = 0 ;
}
Context->Creds = Creds ;
//
// Reference the credentials so they don't go away unexpectedly
//
NegWriteLockCreds(Creds);
Creds->RefCount++;
NegUnlockCreds(Creds);
}
return scRet ;
}
//+---------------------------------------------------------------------------
//
// Function: NegInitLsaModeContext
//
// Synopsis: Initialize a client side context
//
// Arguments: [dwCredHandle] --
// [dwCtxtHandle] --
// [pszTargetName] --
// [fContextReq] --
// [TargetDataRep] --
// [pInput] --
// [pdwNewContext] --
// [pOutput] --
// [pfContextAttr] --
// [ptsExpiry] --
// [pfMapContext] --
// [pContextData] --
//
// History: 7-26-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY
NegInitLsaModeContext(
LSA_SEC_HANDLE dwCredHandle,
LSA_SEC_HANDLE dwCtxtHandle,
PSECURITY_STRING pszTargetName,
ULONG fContextReq,
ULONG TargetDataRep,
PSecBufferDesc pInput,
PLSA_SEC_HANDLE pdwNewContext,
PSecBufferDesc pOutput,
PULONG pfContextAttr,
PTimeStamp ptsExpiry,
PBYTE pfMapContext,
PSecBuffer pContextData)
{
SECURITY_STATUS scRet = SEC_E_OK;
PSecBuffer Buffer = NULL;
SecBuffer LocalBuffer = {0};
PSecBuffer OutBuf = NULL;
PNEG_CONTEXT Context = NULL;
ULONG PackageIndex ;
ULONG CallState ;
CallState = 0 ;
if ( dwCtxtHandle )
{
CallState |= LATER_CALL_BIT ;
}
scRet = NegpParseBuffers( pInput, FALSE, &Buffer, NULL );
if ( !NT_SUCCESS( scRet ) )
{
DebugLog(( DEB_ERROR, "NegInitLsaModeContext failed to map input buffers, %x\n", scRet ));
return scRet ;
}
if ( ( Buffer != NULL ) &&
( Buffer->cbBuffer != 0 ) )
{
CallState |= BUFFER_PRESENT_BIT ;
}
if ( fContextReq & ISC_REQ_DATAGRAM )
{
return SEC_E_UNSUPPORTED_FUNCTION ;
}
switch ( CallState )
{
case FIRST_CALL_WITH_INPUT:
//
// Initial case: Server initiated blob, may be
// fragmented
//
scRet = NegCreateContextFromFragment(
dwCredHandle,
dwCtxtHandle,
Buffer,
fContextReq,
TargetDataRep,
pdwNewContext,
pOutput,
pfContextAttr );
if ( scRet == SEC_E_OK )
{
Context = (PNEG_CONTEXT) *pdwNewContext ;
if ( Context )
{
//
// final
//
*pdwNewContext = 0 ;
LocalBuffer.BufferType = SECBUFFER_TOKEN ;
LocalBuffer.cbBuffer = Context->TotalSize ;
LocalBuffer.pvBuffer = Context->Message ;
//
// Reset frag buffer to NULL - this will be
// freed when the call completes by the LSA wrappers.
// hence the ChangeBuffer call below:
//
Context->Message = NULL ;
LsapChangeBuffer( Buffer, &LocalBuffer );
//
// Get rid of the context - we have the whole
// message
//
NegpDeleteContext( Context );
}
}
else if ( NT_SUCCESS( scRet ) )
{
//
// building a context, so return now
//
break;
}
if ( !NT_SUCCESS( scRet ) )
{
//
// Check the package in use. It is possible that we are being
// sent the context token from a totally separate package and
// are being asked to dispatch to the appropriate package.
//
scRet = NegpDetermineTokenPackage(
dwCredHandle,
Buffer,
&PackageIndex
);
}
else
{
PackageIndex = (ULONG) -1 ;
}
if ( PackageIndex != (ULONG) -1 )
{
CtxtHandle TempCtxtHandle = {0};
CtxtHandle TempInputCtxtHandle = {0};
CredHandle TempCredHandle;
PNEG_CREDS Creds = (PNEG_CREDS) dwCredHandle;
#ifndef WIN32_CHICAGO
NegpReportEvent(
EVENTLOG_INFORMATION_TYPE,
NEGOTIATE_RAW_PACKET,
CATEGORY_NEGOTIATE,
0,
1,
&Creds->Creds[PackageIndex].Package->LsaPackage->Name
);
#endif
//
// Call into another package to do the accept
//
NegReadLockCreds(Creds);
TempCredHandle = Creds->Creds[PackageIndex].Handle;
NegUnlockCreds(Creds);
DebugLog(( DEB_TRACE_NEG, "Got a blob directly for package %x\n",
TempCredHandle.dwLower ));
#ifndef WIN32_CHICAGO
scRet = WLsaInitContext(
&TempCredHandle,
&TempInputCtxtHandle,
pszTargetName,
fContextReq,
0,
TargetDataRep,
pInput,
0,
&TempCtxtHandle,
pOutput,
pfContextAttr,
ptsExpiry,
pfMapContext,
pContextData
);
#else
{
ANSI_STRING TempAnsiString;
NTSTATUS TempStatus = RtlUnicodeStringToAnsiString(
&TempAnsiString,
pszTargetName,
TRUE // allocate destination
);
scRet = InitializeSecurityContext(
&TempCredHandle,
&TempInputCtxtHandle,
TempAnsiString.Buffer,
fContextReq,
0,
TargetDataRep,
pInput,
0,
&TempCtxtHandle,
pOutput,
pfContextAttr,
ptsExpiry
);
RtlFreeAnsiString( &TempAnsiString );
}
Context->CallCount++ ;
#endif // WIN32_CHICAGO
if (NT_SUCCESS(scRet))
{
#ifndef WIN32_CHICAGO
LsapChangeHandle(
HandleReplace,
NULL,
&TempCtxtHandle
);
#endif // WIN32_CHICAGO
}
}
else
{
scRet = NegCrackServerRequestAndReply(
dwCredHandle,
pszTargetName,
fContextReq | ISC_REQ_MUTUAL_AUTH,
TargetDataRep,
pInput,
pdwNewContext,
pOutput,
pfContextAttr,
ptsExpiry,
pfMapContext,
pContextData );
}
//
// if we couldn't parse it, try to go without the hint:
//
if ( scRet != SEC_E_INVALID_TOKEN )
{
break;
}
DebugLog(( DEB_TRACE_NEG, "Unidentified token, trying without it\n" ));
case FIRST_CALL_NO_INPUT:
//
// First call, but server has provided some hints as to
// what to do.
//
scRet = NegGenerateInitialToken(
dwCredHandle,
pszTargetName,
fContextReq,
TargetDataRep,
pInput,
pdwNewContext,
pOutput,
pfContextAttr,
ptsExpiry,
pfMapContext,
pContextData);
DebugLog(( DEB_TRACE_NEG, "NegGenerateInitialToken returned %x\n", scRet ));
break;
case LATER_CALL_WITH_INPUT:
//
// Subsequent call, with a context working and
// a blob from the server. May be fragmented
//
if ( NegpIsValidContext( dwCtxtHandle ) )
{
//
// See if we're doing fragment reassembly:
//
Context = (PNEG_CONTEXT) dwCtxtHandle ;
if ( Context->Flags & NEG_CONTEXT_FRAGMENTING )
{
scRet = NegAddFragmentToContext(
Context,
Buffer );
//
// More trips needed to construct the fragments.
//
if (scRet == SEC_I_CONTINUE_NEEDED)
{
NegpParseBuffers( pOutput, FALSE, &OutBuf, NULL );
if ( OutBuf )
{
OutBuf->cbBuffer = 0 ;
}
return scRet;
}
else if ( scRet != SEC_E_OK )
{
return scRet ;
}
//
// That was the final blob. Reset the message
// to be the whole thing
//
LocalBuffer.BufferType = SECBUFFER_TOKEN ;
LocalBuffer.cbBuffer = Context->TotalSize ;
LocalBuffer.pvBuffer = Context->Message ;
//
// Reset frag buffer to NULL - this will be
// freed when the call completes by the LSA wrappers.
// hence the ChangeBuffer call below:
//
Context->Message = NULL ;
scRet = LsapChangeBuffer( Buffer, &LocalBuffer );
if ( !NT_SUCCESS( scRet ) )
{
return scRet ;
}
//
// Fall through to the normal processing
//
}
}
else
{
return SEC_E_INVALID_HANDLE;
}
if (NegpIsValidContext(dwCtxtHandle))
{
scRet = NegHandleServerReply(
dwCredHandle,
(PNEG_CONTEXT) dwCtxtHandle,
pszTargetName,
fContextReq,
TargetDataRep,
pInput,
pdwNewContext,
pOutput,
pfContextAttr,
ptsExpiry,
pfMapContext,
pContextData );
}
else
{
scRet = SEC_E_INVALID_HANDLE ;
}
break;
case LATER_CALL_NO_INPUT:
//
// No data from the server,
if ( NegpIsValidContext( dwCtxtHandle ) )
{
//
// See if we're doing fragment reassembly:
//
Context = (PNEG_CONTEXT) dwCtxtHandle ;
if ( ( Context->Flags & NEG_CONTEXT_FRAGMENTING ) &&
( fContextReq & ISC_REQ_FRAGMENT_TO_FIT ) )
{
//
// Pull the next chunk off the stored context:
//
scRet = NegpParseBuffers( pOutput, FALSE, &Buffer, NULL );
if ( ( Buffer != NULL ) &&
( NT_SUCCESS( scRet ) ) )
{
Buffer->cbBuffer = min( Buffer->cbBuffer,
(Context->TotalSize - Context->CurrentSize) );
RtlCopyMemory(
Buffer->pvBuffer,
Context->Message + Context->CurrentSize,
Buffer->cbBuffer );
Context->CurrentSize += Buffer->cbBuffer ;
if ( Context->CurrentSize == Context->TotalSize )
{
//
// Sent the whole thing
//
Context->Flags &= (~(NEG_CONTEXT_FRAGMENTING) );
Context->TotalSize = 0 ;
Context->CurrentSize = 0 ;
LsapFreeLsaHeap( Context->Message );
Context->Message = NULL ;
scRet = Context->LastStatus ;
if ( scRet == SEC_E_OK )
{
*pfMapContext = Context->Mapped;
*pContextData = Context->MappedBuffer;
*pfContextAttr = Context->Attributes ;
RtlZeroMemory(
&Context->MappedBuffer,
sizeof(SecBuffer)
);
#ifndef WIN32_CHICAGO
LsapChangeHandle( HandleReplace,
NULL,
&Context->Handle );
Context->Handle.dwLower = 0xFFFFFFFF ;
#endif
}
}
else
{
scRet = SEC_I_CONTINUE_NEEDED ;
}
}
else
{
DebugLog((DEB_TRACE_NEG, "NegInitLsaModeContext: No buffer found (1)\n" ));
scRet = SEC_E_INVALID_TOKEN ;
}
}
else
{
//
// Last round trip for signed blobs:
//
if ( Context->LastStatus == SEC_E_OK )
{
*pfMapContext = Context->Mapped;
*pContextData = Context->MappedBuffer;
*pfContextAttr = Context->Attributes ;
RtlZeroMemory(
&Context->MappedBuffer,
sizeof(SecBuffer)
);
scRet = NegpParseBuffers( pOutput, FALSE, &Buffer, NULL );
if ( Buffer &&
NT_SUCCESS( scRet ) )
{
Buffer->cbBuffer = 0 ;
}
scRet = SEC_E_OK ;
#ifndef WIN32_CHICAGO
LsapChangeHandle( HandleReplace,
NULL,
&Context->Handle );
Context->Handle.dwLower = 0xFFFFFFFF ;
#endif
}
else
{
DebugLog(( DEB_TRACE_NEG, "NegInitLsaModeContext: Signed exchange not ok\n" ));
scRet = SEC_E_INVALID_TOKEN ;
}
}
}
else
{
scRet = SEC_E_INVALID_TOKEN ;
}
break;
default:
DsysAssert( FALSE );
scRet = SEC_E_INTERNAL_ERROR ;
}
return scRet ;
}
SECURITY_STATUS SEC_ENTRY
NegMoveContextToUser(
LSA_SEC_HANDLE dwCtxtHandle,
PSecBuffer pContextBuffer
)
{
return( SEC_E_UNSUPPORTED_FUNCTION );
}
//+---------------------------------------------------------------------------
//
// Function: NegDeleteLsaModeContext
//
// Synopsis: Deletes the LSA portion of the context
//
// Arguments: [dwCtxtHandle] --
//
// History: 9-24-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
SEC_ENTRY
NegDeleteLsaModeContext( LSA_SEC_HANDLE dwCtxtHandle)
{
SECURITY_STATUS scRet = SEC_E_INVALID_HANDLE;
PNEG_CONTEXT Context;
#ifndef WIN32_CHICAGO
PSession pSession = GetCurrentSession();
#endif
Context = (PNEG_CONTEXT) dwCtxtHandle ;
__try
{
if (NegpIsValidContext( Context ))
{
#ifndef WIN32_CHICAGO
//
// If the session is being run down, don't call WLsaDeleteContext,
// it will complicate things (that entry may already have been
// deleted.
//
if ( pSession->fSession & SESFLAG_CLEANUP )
{
Context->Handle.dwLower = 0 ;
Context->Handle.dwUpper = 0 ;
}
NegpDeleteContext( Context );
scRet = SEC_E_OK ;
#endif
}
}
__except( EXCEPTION_EXECUTE_HANDLER ) {}
return( scRet );
}
SECURITY_STATUS SEC_ENTRY
NegApplyControlToken( LSA_SEC_HANDLE dwCtxtHandle,
PSecBufferDesc pInput)
{
return(SEC_E_UNSUPPORTED_FUNCTION);
}
VOID
SEC_ENTRY
NegLogoffNotify(
PLUID pLogonId
)
{
#ifndef WIN32_CHICAGO
NegpDerefLogonSessionById( pLogonId );
#endif
}
#define TOKEN_MATCHES(_buf_,_oid_,_oidlen_) \
(((_buf_)->cbBuffer >= (_oidlen_)) && \
RtlEqualMemory( \
(_buf_)->pvBuffer, \
(_oid_), \
(_oidlen_) \
))
//+-------------------------------------------------------------------------
//
// Function:
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
/*
* Copyright 1993 by OpenVision Technologies, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appears in all copies and
* that both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of OpenVision not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. OpenVision makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
NTSTATUS
NegpGetTokenOid(
IN PUCHAR Buf,
OUT ULONG BufSize,
OUT ObjectID * ObjectId
)
{
UCHAR sf;
ULONG TokenSize;
ULONG OidLength;
//
// Check for the encoding indicator
//
if (BufSize < 2)
{
return SEC_E_INVALID_TOKEN;
}
if ( (*Buf == 0x60) ||
(*Buf == 0xa0) )
{
Buf++;
}
else
{
return SEC_E_INVALID_TOKEN;
}
sf = *(Buf)++;
(BufSize)--;
if (sf & 0x80)
{
if ((sf &= 0x7f) > ((BufSize)-1))
{
return(SEC_E_INVALID_TOKEN);
}
if (sf > sizeof(ULONG))
{
return (SEC_E_INVALID_TOKEN);
}
TokenSize = 0;
for (; sf; sf--)
{
TokenSize = (TokenSize<<8) + (*(Buf)++);
(BufSize)--;
}
} else {
TokenSize = sf;
}
if ((--BufSize == 0) || *Buf != 0x06)
{
return(SEC_E_INVALID_TOKEN);
}
if (--BufSize == 0)
{
return(SEC_E_INVALID_TOKEN);
}
OidLength = *(Buf+1) + 2; // two extra for OID tag & length field
//
// Now buf should point to the encoded oid
//
*ObjectId = NegpDecodeObjectId(Buf,OidLength);
if (ObjectId == NULL)
{
return(SEC_E_INVALID_TOKEN);
}
return(STATUS_SUCCESS);
}
//+-------------------------------------------------------------------------
//
// Function: NegpDetermineTokenPackage
//
// Synopsis: Determines the package that generated an initial
// context token
//
// Effects:
//
// Arguments: CredHandle - handle to the server's credentials
// InitialToken -Initial context token from client
// Package - NULL if spnego, otherwise the package
// that generated the token.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
NegpDetermineTokenPackage(
IN ULONG_PTR CredHandle,
IN PSecBuffer InitialToken,
OUT PULONG PackageIndex
)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG Index;
PNEG_CREDS Credentials = (PNEG_CREDS) CredHandle;
ObjectID DecodedOid = NULL;
int Length ;
PUCHAR Buffer ;
LONG Header ;
LONG Size ;
ULONG_PTR Package ;
*PackageIndex = (ULONG) -1;
//
// Get the OID from the token, if possible
//
Status = NegpGetTokenOid(
(PUCHAR) InitialToken->pvBuffer,
InitialToken->cbBuffer,
&DecodedOid
);
if (NT_SUCCESS(Status))
{
Status = SEC_E_INVALID_TOKEN;
//
// First check for spnego
//
if (NegpCompareOid(
DecodedOid,
NegSpnegoMechOid
) == 0)
{
Status = STATUS_SUCCESS;
}
else
{
//
// Try the oid for each mech in the credential
//
NegReadLockCreds(Credentials);
Package = NegpFindPackageForOid( Credentials, DecodedOid );
if ( Package != NEG_INVALID_PACKAGE )
{
*PackageIndex = (ULONG) Package ;
Status = STATUS_SUCCESS ;
}
else
{
Status = SEC_E_SECPKG_NOT_FOUND ;
}
NegUnlockCreds(Credentials);
}
NegpFreeObjectId(DecodedOid);
}
else
{
if (TOKEN_MATCHES(InitialToken,NTLMSSP_SIGNATURE,sizeof(NTLMSSP_SIGNATURE)))
{
//
// Find the NTLM package in the list of packages
//
NegReadLockCreds(Credentials);
for (Index = 0; Index < Credentials->Count ; Index++ )
{
if (Credentials->Creds[Index].Package->LsaPackage->dwRPCID == NTLMSP_RPCID)
{
*PackageIndex = Index;
Status = STATUS_SUCCESS;
break;
}
}
//
// If we didn't find ntlm, return invalid token.
//
NegUnlockCreds(Credentials);
if ( NT_SUCCESS( Status ) )
{
return Status;
}
}
Size = InitialToken->cbBuffer ;
Buffer = (PUCHAR) InitialToken->pvBuffer ;
Buffer ++ ;
Length = Neg_der_read_length(
&Buffer,
&Size,
&Header );
if ( Length > 0 )
{
//
// Could be kerb, could be snego. Poke a little to find out
//
if ( (*Buffer & 0xC0) == 0x40 )
{
NegReadLockCreds(Credentials);
for (Index = 0; Index < Credentials->Count ; Index++ )
{
if (Credentials->Creds[Index].Package->LsaPackage->dwRPCID == RPC_C_AUTHN_GSS_KERBEROS)
{
*PackageIndex = Index;
Status = STATUS_SUCCESS;
break;
}
}
//
// If we didn't find kerberos, return invalid token.
//
NegUnlockCreds(Credentials);
}
}
}
return(Status);
}
SECURITY_STATUS
SEC_ENTRY
NegAcceptLsaModeContext(
LSA_SEC_HANDLE dwCredHandle,
LSA_SEC_HANDLE dwCtxtHandle,
PSecBufferDesc pInput,
ULONG fContextReq,
ULONG TargetDataRep,
PLSA_SEC_HANDLE pdwNewContext,
PSecBufferDesc pOutput,
PULONG pfContextAttr,
PTimeStamp ptsExpiry,
PBYTE pfMapContext,
PSecBuffer pContextData)
{
SECURITY_STATUS scRet = STATUS_SUCCESS;
ULONG PackageIndex = 0;
PSecBuffer Buffer;
SecBufferDesc LocalDesc ;
SecBuffer LocalBuffer ;
PSecBuffer OutBuf = NULL;
PNEG_CONTEXT Context = NULL ;
PNEG_CREDS Cred ;
PNEG_CREDS AltCreds ;
PLIST_ENTRY Scan ;
BOOL LocalUseSpnego ;
ULONG CallState ;
CallState = 0 ;
if ( dwCtxtHandle )
{
CallState |= LATER_CALL_BIT ;
}
scRet = NegpParseBuffers( pInput, FALSE, &Buffer, NULL );
if ( !NT_SUCCESS( scRet ) )
{
DebugLog(( DEB_ERROR, "NegAcceptLsaModeContext failed to map input buffers, %x\n", scRet ));
return scRet ;
}
if ( ( Buffer != NULL ) &&
( Buffer->cbBuffer != 0 ) )
{
CallState |= BUFFER_PRESENT_BIT ;
}
ULONG_PTR PackageId = GetCurrentPackageId();
DebugLog(( DEB_TRACE_NEG, "AcceptLsaModeContext( %x, %x )\n",
dwCredHandle, dwCtxtHandle ));
#ifndef WIN32_CHICAGO
ptsExpiry->QuadPart = (LONGLONG) MAXLONGLONG;
#else
*ptsExpiry = (LONGLONG) MAXLONGLONG;
#endif
switch ( CallState )
{
case FIRST_CALL_NO_INPUT:
scRet = NegGenerateServerRequest(
dwCredHandle,
fContextReq,
TargetDataRep,
pInput,
pdwNewContext,
pOutput,
pfContextAttr,
ptsExpiry,
pfMapContext,
pContextData );
break;
case FIRST_CALL_WITH_INPUT:
//
// Determine if this is a fragment, and if so, is it the
// last fragment:
//
scRet = NegCreateContextFromFragment(
dwCredHandle,
dwCtxtHandle,
Buffer,
fContextReq,
TargetDataRep,
pdwNewContext,
pOutput,
pfContextAttr );
*pfMapContext = FALSE ;
if ( scRet == SEC_E_OK )
{
Context = (PNEG_CONTEXT) *pdwNewContext ;
if ( Context )
{
//
// final
//
*pdwNewContext = 0 ;
LocalBuffer.BufferType = SECBUFFER_TOKEN ;
LocalBuffer.cbBuffer = Context->TotalSize ;
LocalBuffer.pvBuffer = Context->Message ;
//
// Reset frag buffer to NULL - this will be
// freed when the call completes by the LSA wrappers.
// hence the ChangeBuffer call below:
//
Context->Message = NULL ;
LsapChangeBuffer( Buffer, &LocalBuffer );
//
// Get rid of the context - we have the whole
// message
//
NegpDeleteContext( Context );
}
}
else if ( NT_SUCCESS( scRet ) )
{
//
// building a context, so return now
//
return scRet ;
}
if ( !NT_SUCCESS( scRet ) )
{
//
// Check the package in use. It is possible that we are being
// sent the context token from a totally separate package and
// are being asked to dispatch to the appropriate package.
//
scRet = NegpDetermineTokenPackage(
dwCredHandle,
Buffer,
&PackageIndex
);
}
else
{
PackageIndex = (ULONG) -1 ;
}
//
// Older clients will send data that returns an error
//
if (!NT_SUCCESS(scRet) || (PackageIndex == (ULONG) -1))
{
scRet = NegHandleClientRequest(
dwCredHandle,
NULL,
fContextReq,
TargetDataRep,
pInput,
pdwNewContext,
pOutput,
pfContextAttr,
ptsExpiry,
pfMapContext,
pContextData );
if ( !NT_SUCCESS( scRet ) ||
( NT_SUCCESS( scRet ) && (*pfContextAttr & ASC_RET_EXTENDED_ERROR) ) )
{
Cred = (PNEG_CREDS) dwCredHandle ;
NegReadLockCreds( Cred );
if ( Cred->Flags & NEGCRED_MULTI )
{
//
// This credential has additional creds hanging off of it.
//
DebugLog(( DEB_TRACE_NEG, "Multi credential handle:\n" ));
Scan = Cred->AdditionalCreds.Flink ;
while ( Scan != &Cred->AdditionalCreds )
{
AltCreds = CONTAINING_RECORD( Scan, NEG_CREDS, List );
DebugLog(( DEB_TRACE_NEG, "Retrying with credential %p\n", AltCreds ));
scRet = NegHandleClientRequest(
(ULONG_PTR) AltCreds,
NULL,
fContextReq,
TargetDataRep,
pInput,
pdwNewContext,
pOutput,
pfContextAttr,
ptsExpiry,
pfMapContext,
pContextData );
if ( NT_SUCCESS( scRet ) &&
( ( *pfContextAttr & ASC_RET_EXTENDED_ERROR ) == 0 ) )
{
break;
}
Scan = Scan->Flink ;
}
}
NegUnlockCreds( Cred );
}
}
else
{
CtxtHandle TempCtxtHandle = {0};
CtxtHandle TempInputCtxtHandle = {0};
CredHandle TempCredHandle;
PNEG_CREDS Creds = (PNEG_CREDS) dwCredHandle;
#ifndef WIN32_CHICAGO
NegpReportEvent(
EVENTLOG_INFORMATION_TYPE,
NEGOTIATE_RAW_PACKET,
CATEGORY_NEGOTIATE,
0,
1,
&Creds->Creds[PackageIndex].Package->LsaPackage->Name
);
#endif
//
// Call into another package to do the accept
//
NegReadLockCreds(Creds);
TempCredHandle = Creds->Creds[PackageIndex].Handle;
NegUnlockCreds(Creds);
DebugLog(( DEB_TRACE_NEG, "Got a blob directly for package %x\n",
TempCredHandle.dwLower ));
PackageId = GetCurrentPackageId();
#ifndef WIN32_CHICAGO
scRet = WLsaAcceptContext(
&TempCredHandle,
&TempInputCtxtHandle,
pInput,
fContextReq,
TargetDataRep,
&TempCtxtHandle,
pOutput,
pfContextAttr,
ptsExpiry,
pfMapContext,
pContextData
);
#else
scRet = AcceptSecurityContext(
&TempCredHandle,
&TempInputCtxtHandle,
pInput,
fContextReq,
TargetDataRep,
&TempCtxtHandle,
pOutput,
pfContextAttr,
ptsExpiry
);
#endif // WIN32_CHICAGO
SetCurrentPackageId(PackageId);
if (Context)
{
Context->CallCount++ ;
}
if (NT_SUCCESS(scRet))
{
#ifndef WIN32_CHICAGO
LsapChangeHandle(
HandleReplace,
NULL,
&TempCtxtHandle
);
#endif // WIN32_CHICAGO
}
}
break;
case LATER_CALL_NO_INPUT:
DebugLog(( DEB_TRACE_NEG, "Missing Input Buffer?\n"));
scRet = SEC_E_INVALID_HANDLE ;
break;
case LATER_CALL_WITH_INPUT:
Context = (PNEG_CONTEXT) dwCtxtHandle ;
if ( !NegpIsValidContext( dwCtxtHandle ) )
{
return SEC_E_INVALID_HANDLE ;
}
if ( Context->Flags & NEG_CONTEXT_FRAGMENTING )
{
scRet = NegAddFragmentToContext(
Context,
Buffer );
//
// More trips needed to reconstruct the fragment.
//
if (scRet == SEC_I_CONTINUE_NEEDED)
{
NegpParseBuffers( pOutput, FALSE, &OutBuf, NULL );
if ( OutBuf )
{
OutBuf->cbBuffer = 0 ;
}
return scRet;
}
else if ( scRet != SEC_E_OK )
{
return scRet ;
}
//
// That was the final blob. Reset the message
// to be the whole thing
//
LocalBuffer.BufferType = SECBUFFER_TOKEN ;
LocalBuffer.cbBuffer = Context->TotalSize ;
LocalBuffer.pvBuffer = Context->Message ;
//
// Reset frag buffer to NULL - this will be
// freed when the call completes by the LSA wrappers.
// hence the ChangeBuffer call below:
//
Context->Message = NULL ;
scRet = LsapChangeBuffer( Buffer, &LocalBuffer );
if ( !NT_SUCCESS( scRet ) )
{
return scRet ;
}
//
// Fall through to the normal processing
//
}
scRet = NegHandleClientRequest(
dwCredHandle,
(PNEG_CONTEXT) dwCtxtHandle,
fContextReq,
TargetDataRep,
pInput,
pdwNewContext,
pOutput,
pfContextAttr,
ptsExpiry,
pfMapContext,
pContextData );
break;
default:
DsysAssert(FALSE);
scRet = SEC_E_INTERNAL_ERROR ;
break;
}
return scRet ;
}
NTSTATUS
NegCallPackage(
IN PLSA_CLIENT_REQUEST ClientRequest,
IN PVOID ProtocolSubmitBuffer,
IN PVOID ClientBufferBase,
IN ULONG SubmitBufferLength,
OUT PVOID *ProtocolReturnBuffer,
OUT PULONG ReturnBufferLength,
OUT PNTSTATUS ProtocolStatus
)
{
#ifdef WIN32_CHICAGO
return SEC_E_UNSUPPORTED_FUNCTION ;
#else
PULONG_PTR MessageTypePtr ;
NEGOTIATE_MESSAGES Messages ;
if ( SubmitBufferLength < sizeof( ULONG_PTR ) )
{
return STATUS_INVALID_PARAMETER ;
}
MessageTypePtr = (PULONG_PTR) ProtocolSubmitBuffer ;
if ( *MessageTypePtr >= NegCallPackageMax )
{
return STATUS_INVALID_PARAMETER ;
}
switch ( *MessageTypePtr )
{
case NegEnumPackagePrefixes:
return NegEnumPackagePrefixesCall(
ClientRequest,
ProtocolSubmitBuffer,
ClientBufferBase,
SubmitBufferLength,
ProtocolReturnBuffer,
ReturnBufferLength,
ProtocolStatus );
break;
case NegGetCallerName:
return NegGetCallerNameCall(
ClientRequest,
ProtocolSubmitBuffer,
ClientBufferBase,
SubmitBufferLength,
ProtocolReturnBuffer,
ReturnBufferLength,
ProtocolStatus );
break;
default:
DsysAssert( FALSE );
return STATUS_NOT_IMPLEMENTED ;
}
#endif
}
NTSTATUS
NegCallPackageUntrusted(
IN PLSA_CLIENT_REQUEST ClientRequest,
IN PVOID ProtocolSubmitBuffer,
IN PVOID ClientBufferBase,
IN ULONG SubmitBufferLength,
OUT PVOID *ProtocolReturnBuffer,
OUT PULONG ReturnBufferLength,
OUT PNTSTATUS ProtocolStatus
)
{
return( SEC_E_UNSUPPORTED_FUNCTION );
}
NTSTATUS
NegCallPackagePassthrough(
IN PLSA_CLIENT_REQUEST ClientRequest,
IN PVOID ProtocolSubmitBuffer,
IN PVOID ClientBufferBase,
IN ULONG SubmitBufferLength,
OUT PVOID *ProtocolReturnBuffer,
OUT PULONG ReturnBufferLength,
OUT PNTSTATUS ProtocolStatus
)
{
return( SEC_E_UNSUPPORTED_FUNCTION );
}
SECURITY_STATUS SEC_ENTRY
NegShutdown(void)
{
return(SEC_E_UNSUPPORTED_FUNCTION);
}
SECURITY_STATUS SEC_ENTRY
NegSystemLogon( PSECURITY_STRING pName,
DWORD cbKey,
PBYTE pbKey,
DWORD * pdwHandle,
PTimeStamp ptsExpiry)
{
return(SEC_E_UNSUPPORTED_FUNCTION);
}
SECURITY_STATUS SEC_ENTRY
NegGetUserInfo( PLUID pLogonId,
ULONG fFlags,
PSecurityUserData * ppUserInfo)
{
return(SEC_E_UNSUPPORTED_FUNCTION);
}
//+---------------------------------------------------------------------------
//
// Function: NegSaveCredentials
//
// Synopsis: Store credentials (not supported)
//
// Arguments: [dwCredHandle] --
// [CredType] --
// [pCredentials] --
//
//
// History: 7-26-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY
NegSaveCredentials( LSA_SEC_HANDLE dwCredHandle,
PSecBuffer pCredentials)
{
return(SEC_E_UNSUPPORTED_FUNCTION);
}
//+---------------------------------------------------------------------------
//
// Function: NegGetCredentials
//
// Synopsis: Get Credentials (not supported)
//
// Arguments: [dwCredHandle] --
// [CredType] --
// [pCredentials] --
//
// History: 7-26-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY
NegGetCredentials( LSA_SEC_HANDLE dwCredHandle,
PSecBuffer pCredentials)
{
return(SEC_E_UNSUPPORTED_FUNCTION);
}
//+---------------------------------------------------------------------------
//
// Function: NegDeleteCredentials
//
// Synopsis: Delete stored creds (not supported)
//
// Arguments: [dwCredHandle] --
// [CredType] --
// [pKey] --
//
// History: 7-26-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY
NegDeleteCredentials( LSA_SEC_HANDLE dwCredHandle,
PSecBuffer pKey)
{
return(SEC_E_UNSUPPORTED_FUNCTION);
}
SECURITY_STATUS SEC_ENTRY
NegAddCredentials(
IN LSA_SEC_HANDLE CredentialHandle,
IN OPTIONAL PUNICODE_STRING PrincipalName,
IN PUNICODE_STRING Package,
IN ULONG CredentialUseFlags,
IN PVOID AuthorizationData,
IN PVOID GetKeyFunction,
IN PVOID GetKeyArgument,
OUT PTimeStamp ExpirationTime
)
{
PNEG_CREDS Cred ;
PNEG_CREDS NewCred ;
NTSTATUS Status ;
TimeStamp Expiration ;
PLIST_ENTRY Prev ;
PNEG_CREDS PreviousCreds ;
Cred = (PNEG_CREDS) CredentialHandle ;
Status = NegpAcquireCredHandle(
PrincipalName,
CredentialUseFlags | NEG_CRED_DONT_LINK,
&Cred->ClientLogonId,
AuthorizationData,
GetKeyFunction,
GetKeyArgument,
(PULONG_PTR) &NewCred,
&Expiration );
if ( !NT_SUCCESS( Status ) )
{
return Status ;
}
NegWriteLockCreds( Cred );
Prev = Cred->AdditionalCreds.Blink ;
if ( Prev != &Cred->AdditionalCreds )
{
//
// If there are other creds, make sure they are marked
//
PreviousCreds = CONTAINING_RECORD( Prev, NEG_CREDS, List );
PreviousCreds->Flags |= NEGCRED_MULTI_PART ;
}
InsertTailList( &Cred->AdditionalCreds, &NewCred->List );
Cred->Flags |= NEGCRED_MULTI ;
NegUnlockCreds( Cred );
return Status ;
}
NTSTATUS
NegGetExtendedInformation(
IN SECPKG_EXTENDED_INFORMATION_CLASS Class,
OUT PSECPKG_EXTENDED_INFORMATION * ppInformation
)
{
PSECPKG_EXTENDED_INFORMATION Thunks ;
NTSTATUS Status ;
switch ( Class )
{
case SecpkgContextThunks:
Thunks = (PSECPKG_EXTENDED_INFORMATION) LsapAllocateLsaHeap( sizeof( SECPKG_EXTENDED_INFORMATION ) + sizeof( DWORD ));
if ( Thunks )
{
Thunks->Class = SecpkgContextThunks;
Thunks->Info.ContextThunks.InfoLevelCount = 2 ;
Thunks->Info.ContextThunks.Levels[0] = SECPKG_ATTR_PACKAGE_INFO;
Thunks->Info.ContextThunks.Levels[1] = SECPKG_ATTR_SIZES ;
Status = STATUS_SUCCESS ;
}
else
{
Status = STATUS_NO_MEMORY ;
}
*ppInformation = Thunks ;
break;
default:
*ppInformation = NULL ;
Status = STATUS_INVALID_INFO_CLASS ;
break;
}
return Status ;
}
//+---------------------------------------------------------------------------
//
// Function: NegQueryContextAttributes
//
// Synopsis:
//
// Arguments: [ContextHandle] --
// [ContextAttribute] --
// [Buffer] --
//
// Returns:
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
NegQueryContextAttributes(
IN LSA_SEC_HANDLE ContextHandle,
IN ULONG ContextAttribute,
IN OUT PVOID Buffer)
{
SecPkgContext_NegotiationInfoW NegInfo = {0};
SecPkgContext_Sizes Sizes ;
NTSTATUS Status = STATUS_SUCCESS;
PNEG_CONTEXT Context = (PNEG_CONTEXT) ContextHandle ;
SECPKG_CALL_INFO CallInfo ;
SecPkgInfoW PackageInfo ;
#ifndef WIN32_CHICAGO
LsapGetCallInfo( &CallInfo );
switch ( ContextAttribute )
{
case SECPKG_ATTR_NEGOTIATION_INFO :
if ( CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE )
{
Status = LsapCopyFromClient(
Buffer,
&NegInfo,
sizeof( NegInfo ) );
}
if ( (Context->Flags & NEG_CONTEXT_NEGOTIATING) != 0 )
{
NegInfo.NegotiationState = SECPKG_NEGOTIATION_IN_PROGRESS ;
}
else
{
NegInfo.NegotiationState = SECPKG_NEGOTIATION_OPTIMISTIC ;
}
if ( NegInfo.NegotiationState == SECPKG_NEGOTIATION_OPTIMISTIC )
{
if ( ( CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE ) == 0 )
{
Status = WLsaQueryPackageInfo(
&Context->Creds->Creds[ Context->CredIndex ].Package->LsaPackage->Name,
&NegInfo.PackageInfo
);
}
else
{
//
// For kernel mode callers, we can't return the package info
// this way due to VM risks. So, we just put the package ID
// into the pointer, and ksec looks it up in kernel space.
//
PackageInfo.wRPCID = (WORD) Context->Creds->Creds[ Context->CredIndex ].Package->LsaPackage->dwRPCID;
PackageInfo.fCapabilities = Context->Creds->Creds[ Context->CredIndex ].Package->LsaPackage->fCapabilities ;
PackageInfo.cbMaxToken = Context->Creds->Creds[ Context->CredIndex ].Package->LsaPackage->TokenSize ;
Status = LsapCopyToClient(
&PackageInfo,
NegInfo.PackageInfo,
sizeof( PackageInfo ) );
}
}
if (NT_SUCCESS(Status))
{
Status = LsapCopyToClient( &NegInfo, Buffer, sizeof( NegInfo ) );
if (!NT_SUCCESS(Status))
{
if (( NegInfo.PackageInfo != NULL ) &&
( ( CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE ) == 0 ) )
{
LsapClientFree(NegInfo.PackageInfo);
}
}
}
return Status ;
case SECPKG_ATTR_SIZES:
Sizes.cbMaxToken = NegLsaPackage->TokenSize ;
Sizes.cbMaxSignature = 64 ;
Sizes.cbBlockSize = 8 ;
Sizes.cbSecurityTrailer = 64 ;
Status = LsapCopyToClient( &Sizes, Buffer, sizeof( Sizes ) );
return Status ;
default:
return SEC_E_UNSUPPORTED_FUNCTION ;
}
#endif
return SEC_E_UNSUPPORTED_FUNCTION ;
}