/*++ Copyright (c) 1993 Microsoft Corporation Module Name: credhand.cxx Abstract: API and support routines for handling credential handles. Author: Cliff Van Dyke (CliffV) 26-Jun-1993 Revision History: ChandanS 03-Aug-1996 Stolen from net\svcdlls\ntlmssp\common\credhand.c --*/ // Common include files. #include #include // ALIGN_WCHAR extern "C" { #include } // Crit Sect to protect various globals in this module. CRITICAL_SECTION SspCredentialCritSect; LIST_ENTRY SspCredentialList; // This is the definition of a null session string. // Change this if the definition changes #define IsNullSessionString(x) (((x)->Length == 0) && \ ((x)->Buffer != NULL)) BOOLEAN AlterRtlEqualUnicodeString( IN PUNICODE_STRING String1, IN PUNICODE_STRING String2, IN BOOLEAN CaseInSensitive ) /*++ This is here to catch cases that RtlEqualUnicodeString does not. For e.g, if String1 is (NULL,0,0) and String2 is ("",0,2), RtlEqualUnicodeString returned TRUE but we really want it to return FALSE --*/ { BOOL fRet = RtlEqualUnicodeString(String1, String2, CaseInSensitive); if (fRet && (IsNullSessionString(String1) != IsNullSessionString(String2))) { fRet = FALSE; } return (fRet != 0); } BOOL EnableAllPrivileges( IN HANDLE ClientTokenHandle ); NTSTATUS SspCredentialReferenceCredential( IN ULONG_PTR CredentialHandle, IN BOOLEAN DereferenceCredential, IN BOOLEAN ForceRemoveCredential, OUT PSSP_CREDENTIAL * UserCredential ) /*++ Routine Description: This routine checks to see if the Credential is from a currently active client, and references the Credential if it is valid. The caller may optionally request that the client's Credential be removed from the list of valid Credentials - preventing future requests from finding this Credential. For a client's Credential to be valid, the Credential value must be on our list of active Credentials. Arguments: CredentialHandle - Points to the CredentialHandle of the Credential to be referenced. DereferenceCredential - This boolean value indicates that that a call a single instance of this credential handle should be freed. If there are multiple instances, they should still continue to work. ForceRemoveCredential - This boolean value indicates whether the caller wants the logon process's Credential to be removed from the list of Credentials. TRUE indicates the Credential is to be removed. FALSE indicates the Credential is not to be removed. Return Value: NULL - the Credential was not found. Otherwise - returns a pointer to the referenced credential. --*/ { PLIST_ENTRY ListEntry; PSSP_CREDENTIAL Credential = NULL; NTSTATUS Status = STATUS_SUCCESS; SECPKG_CLIENT_INFO ClientInfo; ULONG DereferenceCount = 1; // Make sure that nobody tries to force removal without also // trying to dereference the credential. ASSERT(!(ForceRemoveCredential && !DereferenceCredential)); *UserCredential = NULL ; Status = LsaFunctions->GetClientInfo(&ClientInfo); if(!NT_SUCCESS(Status)) { SECPKG_CALL_INFO CallInfo; // this call can fail during a cleanup call. so ignore that for now, // and check for cleanup disposition. if(LsaFunctions->GetCallInfo(&CallInfo)) { if ((CallInfo.Attributes & SECPKG_CALL_CLEANUP) != 0) { Status = STATUS_SUCCESS; RtlZeroMemory( &ClientInfo, sizeof(SECPKG_CLIENT_INFO) ); ClientInfo.HasTcbPrivilege = TRUE; ClientInfo.ProcessID = CallInfo.ProcessId; DereferenceCount = CallInfo.CallCount; } } if( !NT_SUCCESS( Status ) ) { SspPrint(( SSP_CRITICAL, "SspCredentialReferenceCredential: GetClientInfo returned 0x%lx\n", Status)); return( Status ); } } // Acquire exclusive access to the Credential list EnterCriticalSection( &SspCredentialCritSect ); // Now walk the list of Credentials looking for a match. for ( ListEntry = SspCredentialList.Flink; ListEntry != &SspCredentialList; ListEntry = ListEntry->Flink ) { Credential = CONTAINING_RECORD( ListEntry, SSP_CREDENTIAL, Next ); // Found a match ... reference this Credential // (if the Credential is being removed, we would increment // and then decrement the reference, so don't bother doing // either - since they cancel each other out). if ( Credential == (PSSP_CREDENTIAL) CredentialHandle ) { // Make sure we have the privilege of accessing // this handle if (!ClientInfo.HasTcbPrivilege && (Credential->ClientProcessID != ClientInfo.ProcessID) ) { break; } if (!DereferenceCredential) { Credential->References += 1; } else { ASSERT( DereferenceCount > 0 ); // we've eliminated the two separate ref counters. // therefore, we let SspCredentialDereferenceCredential // take care of reducing the refcount. DereferenceCount--; // Decremenent the credential references, indicating // that a call to free ASSERT( DereferenceCount <= Credential->References ); Credential->References -= DereferenceCount; ASSERT( Credential->References > 0 ); } LeaveCriticalSection( &SspCredentialCritSect ); *UserCredential = Credential ; return STATUS_SUCCESS ; } } // No match found SspPrint(( SSP_API_MORE, "Tried to reference unknown Credential 0x%lx\n", CredentialHandle )); LeaveCriticalSection( &SspCredentialCritSect ); return STATUS_INVALID_HANDLE ; } NTSTATUS SspGetToken ( OUT PHANDLE ReturnedTokenHandle) { HANDLE TokenHandle = NULL; NTSTATUS Status = STATUS_SUCCESS; HANDLE TmpHandle = NULL; SECURITY_IMPERSONATION_LEVEL SecImpLevel = SecurityImpersonation; Status = LsaFunctions->ImpersonateClient(); if (!NT_SUCCESS (Status)) { goto Cleanup; } // get the token // note: there MUST be an impersonation token. // LsaFunctions->ImpersonateClient will call ImpersonateSelf() when necessary Status = NtOpenThreadToken(NtCurrentThread(), TOKEN_DUPLICATE | TOKEN_QUERY | WRITE_DAC, TRUE, &TokenHandle); if (!NT_SUCCESS (Status)) { goto Cleanup; } Status = SspDuplicateToken(TokenHandle, SecImpLevel, &TmpHandle); if (!NT_SUCCESS (Status)) { goto Cleanup; } Cleanup: if (ReturnedTokenHandle != NULL) { if (!NT_SUCCESS (Status)) { *ReturnedTokenHandle = NULL; } else { *ReturnedTokenHandle = TmpHandle; TmpHandle = NULL; } } if (TokenHandle != NULL) { NtClose(TokenHandle); } if (TmpHandle != NULL) { NtClose(TmpHandle); } // ignore return value, we may not have impersonated successfully.. RevertToSelf(); return Status; } NTSTATUS SspCredentialGetPassword(IN PSSP_CREDENTIAL Credential, OUT PUNICODE_STRING Password) /*++ Routine Description: This routine copies the password out of credential. It requires locking the credential list because other threads may be hiding/revealing the password and we need exclusive access to do that. NOTE: Locking is no longer required, because the caller is expected to NtLmDuplicateUnicodeString() the cipher text Password prior to passing it to this routine. This change allows the following advantages: 1. Avoid taking Credential list lock. 2. Avoid having to avoid having to Re-hide the password after reveal. 3. Avoid having to take locks elsewhere associated with hiding/revealing. Arguments: Credential - Credential record to retrieve the password from. Password - UNICODE_STRING to store the password in. Return Value: STATUS_NO_MEMORY - there was not enough memory to copy the password. --*/ { NTSTATUS Status = STATUS_SUCCESS; if ( Credential->Password.Buffer != NULL ) { Status = NtLmDuplicateUnicodeString(Password, &Credential->Password); } else { RtlInitUnicodeString(Password, NULL); } return(Status); } PSSP_CREDENTIAL SspCredentialLookupSupplementalCredential( IN PLUID LogonId, IN ULONG CredentialUseFlags, IN PUNICODE_STRING UserName, IN PUNICODE_STRING DomainName, IN PUNICODE_STRING Password ) /*++ Routine Description: This routine walks the list of credentials for this client looking for one that has the same supplemental credentials as those passed in. If it is found, its reference count is increased and a pointer to it is returned. Arguments: UserName - User name to match. DomainName - Domain name to match. Password - Password to match. Return Value: NULL - the Credential was not found. Otherwise - returns a pointer to the referenced credential. --*/ { SspPrint((SSP_API_MORE, "Entering SspCredentialLookupSupplementalCredential\n")); NTSTATUS Status; PLIST_ENTRY ListEntry; PSSP_CREDENTIAL Credential = NULL; PLIST_ENTRY ListHead; SECPKG_CLIENT_INFO ClientInfo; SECPKG_CALL_INFO CallInfo ; Status = LsaFunctions->GetClientInfo(&ClientInfo); if (!NT_SUCCESS(Status)) { SspPrint(( SSP_CRITICAL, "SspCredentialLookupSupplementalCredential: GetClientInfo returned 0x%lx\n", Status)); return(NULL); } if ( !LsaFunctions->GetCallInfo( &CallInfo ) ) { SspPrint(( SSP_CRITICAL, "SspCredentialLookupSupplementalCredential: GetCallInfo returned FALSE\n" )); return NULL ; } // Acquire exclusive access to the Credential list EnterCriticalSection( &SspCredentialCritSect ); ListHead = &SspCredentialList; // Now walk the list of Credentials looking for a match. for ( ListEntry = ListHead->Flink; ListEntry != ListHead; ListEntry = ListEntry->Flink ) { Credential = CONTAINING_RECORD( ListEntry, SSP_CREDENTIAL, Next ); // We are only looking for outbound credentials. if ((Credential->CredentialUseFlags & SECPKG_CRED_OUTBOUND) == 0) { continue; } // We only want credentials from the same caller if (Credential->ClientProcessID != ClientInfo.ProcessID) { continue; } // if the caller is from kernel mode, only return creds // granted to kernel mode if ( ( CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE ) != 0 ) { if ( !Credential->KernelClient ) { continue; } } // Check for a match // The credential use check was added because null session // credentials were being returned when default credentials // were being asked. This happened becuase RtlEqualUnicodeString // for NULL,0,0 and "",0,2 is TRUE if ( (CredentialUseFlags == Credential->CredentialUseFlags) && AlterRtlEqualUnicodeString( UserName, &Credential->UserName, FALSE ) && AlterRtlEqualUnicodeString( DomainName, &Credential->DomainName, FALSE ) && RtlEqualLuid( LogonId, &Credential->LogonId )) { UNICODE_STRING RevealedPassword; BOOLEAN fMatch; Status = NtLmDuplicateUnicodeString( &RevealedPassword, &Credential->Password ); if(!NT_SUCCESS( Status ) ) { SspPrint(( SSP_CRITICAL, "SspCredentialLookupSupplementalCredential: NtLmDuplicateUnicodeString returned %d\n",Status)); // an allocation failure is catastrophic, bail out now. break; } SspRevealPassword(&RevealedPassword); fMatch = AlterRtlEqualUnicodeString( Password, &RevealedPassword, FALSE ); if( RevealedPassword.Buffer != NULL ) { ZeroMemory( RevealedPassword.Buffer, RevealedPassword.Length ); NtLmFree( RevealedPassword.Buffer ); } if( fMatch ) { // Found a match - reference the credential // Reference the credential and indicate that // it is in use as two different handles to the caller // (who may call FreeCredentialsHandle twice) Credential->References++; LeaveCriticalSection( &SspCredentialCritSect ); SspPrint((SSP_API_MORE, "Leaving SspCredentialLookupSupplementalCredential\n")); return Credential; } } } // No match found SspPrint(( SSP_API_MORE, "Tried to reference unknown Credential\n" )); LeaveCriticalSection( &SspCredentialCritSect ); SspPrint((SSP_API_MORE, "Leaving SspCredentialLookupSupplementalCredential\n")); return NULL; } VOID SspCredentialDereferenceCredential( IN PSSP_CREDENTIAL Credential ) /*++ Routine Description: This routine decrements the specified Credential's reference count. If the reference count drops to zero, then the Credential is deleted Arguments: Credential - Points to the Credential to be dereferenced. Return Value: None. --*/ { ULONG References; // Decrement the reference count EnterCriticalSection( &SspCredentialCritSect ); ASSERT( Credential->References >= 1 ); References = -- Credential->References; // If the count dropped to zero, then run-down the Credential if ( References == 0 ) { LPWSTR DomainName = Credential->DomainName.Buffer; LPWSTR UserName = Credential->UserName.Buffer; LPWSTR Password = Credential->Password.Buffer; // note: Password.Length may contain run-encoding hint, so size may be illegal. DWORD cbPassword = Credential->Password.MaximumLength; HANDLE ClientTokenHandle = Credential->ClientTokenHandle; SspPrint(( SSP_API_MORE, "Deleting Credential 0x%lx\n", Credential )); if (!Credential->Unlinked) { RemoveEntryList( &Credential->Next ); } ZeroMemory( Credential, sizeof(SSP_CREDENTIAL) ); LeaveCriticalSection( &SspCredentialCritSect ); if ( DomainName ) { (VOID) NtLmFree( DomainName ); } if ( UserName ) { (VOID) NtLmFree( UserName ); } if ( Password ) { ZeroMemory( Password, cbPassword ); (VOID) NtLmFree( Password ); } if ( ClientTokenHandle ) { (VOID) NtClose( ClientTokenHandle ); } (VOID) NtLmFree( Credential ); return; } LeaveCriticalSection( &SspCredentialCritSect ); return; } BOOLEAN SsprCheckMachineLogon( IN OUT PLUID pLogonId, IN PUNICODE_STRING UserName, IN PUNICODE_STRING DomainName, IN PUNICODE_STRING Password ) /*++ Routine Description: This routine determines if the input credential matches a special machine account logon over-ride. Return Value: TRUE - the intput credential was the special machine account logon over-ride. the pLogonId is updated to utilize the machine credential. --*/ { UNICODE_STRING MachineAccountName; static LUID LogonIdSystem = SYSTEM_LUID ; BOOLEAN fMachineLogon = FALSE; MachineAccountName.Buffer = NULL; // check if caller was system, and requested machine credential // eg: user=computername$, domain=NULL, password=NULL if( !RtlEqualLuid( pLogonId, &LogonIdSystem ) ) { return FALSE; } if( UserName->Buffer == NULL ) { return FALSE; } if( DomainName->Buffer != NULL ) { return FALSE; } if( Password->Buffer != NULL ) { return FALSE; } EnterCriticalSection (&NtLmGlobalCritSect); MachineAccountName.Length = NtLmGlobalUnicodeComputerNameString.Length + sizeof(WCHAR); if( MachineAccountName.Length == UserName->Length ) { MachineAccountName.MaximumLength = MachineAccountName.Length; MachineAccountName.Buffer = (PWSTR)NtLmAllocate( MachineAccountName.Length ); if( MachineAccountName.Buffer != NULL ) { RtlCopyMemory( MachineAccountName.Buffer, NtLmGlobalUnicodeComputerNameString.Buffer, NtLmGlobalUnicodeComputerNameString.Length ); MachineAccountName.Buffer[ (MachineAccountName.Length / sizeof(WCHAR)) - 1 ] = L'$'; } } LeaveCriticalSection (&NtLmGlobalCritSect); if( MachineAccountName.Buffer == NULL ) { goto Cleanup; } if( RtlEqualUnicodeString( &MachineAccountName, UserName, TRUE ) ) { // yes, it's a machine account logon request, update the // requested LogonId to match our mapped logon session. *pLogonId = NtLmGlobalLuidMachineLogon; fMachineLogon = TRUE; } Cleanup: if( MachineAccountName.Buffer ) { NtLmFree( MachineAccountName.Buffer ); } return fMachineLogon; } NTSTATUS SsprAcquireCredentialHandle( IN PHANDLE TokenHandle, IN PLUID LogonId, IN ULONG ClientProcessID, IN ULONG CredentialUseFlags, OUT PLSA_SEC_HANDLE CredentialHandle, OUT PTimeStamp Lifetime, IN OPTIONAL PUNICODE_STRING DomainName, IN OPTIONAL PUNICODE_STRING UserName, IN OPTIONAL PUNICODE_STRING Password ) /*++ Routine Description: This API allows applications to acquire a handle to pre-existing credentials associated with the user on whose behalf the call is made i.e. under the identity this application is running. These pre-existing credentials have been established through a system logon not described here. Note that this is different from "login to the network" and does not imply gathering of credentials. This API returns a handle to the credentials of a principal (user, client) as used by a specific security package. This handle can then be used in subsequent calls to the Context APIs. This API will not let a process obtain a handle to credentials that are not related to the process; i.e. we won't allow a process to grab the credentials of another user logged into the same machine. There is no way for us to determine if a process is a trojan horse or not, if it is executed by the user. Arguments: CredentialUseFlags - Flags indicating the way with which these credentials will be used. #define CRED_INBOUND 0x00000001 #define CRED_OUTBOUND 0x00000002 #define CRED_BOTH 0x00000003 The credentials created with CRED_INBOUND option can only be used for (validating incoming calls and can not be used for making accesses. CredentialHandle - Returned credential handle. Lifetime - Time that these credentials expire. The value returned in this field depends on the security package. DomainName, DomainNameSize, UserName, UserNameSize, Password, PasswordSize - Optional credentials for this user. Return Value: STATUS_SUCCESS -- Call completed successfully SEC_E_PRINCIPAL_UNKNOWN -- No such principal SEC_E_NOT_OWNER -- caller does not own the specified credentials STATUS_NO_MEMORY -- Not enough memory --*/ { SspPrint((SSP_API_MORE, "Entering SsprAcquireCredentialHandle\n")); NTSTATUS Status = STATUS_SUCCESS; PSSP_CREDENTIAL Credential = NULL; SECPKG_CALL_INFO CallInfo ; if ( !LsaFunctions->GetCallInfo( &CallInfo ) ) { return STATUS_UNSUCCESSFUL ; } // If this is an outbound credential, and supplemental credentials // were supplied, look to see if we have already // created one with this set of credentials. Note - this leaves // the credential referenced, so if we fail further down we need to // dereference the credential. if ((CredentialUseFlags & SECPKG_CRED_OUTBOUND) != 0) { // check if machine account logon over-ride. SsprCheckMachineLogon( LogonId, UserName, DomainName, Password ); Credential = SspCredentialLookupSupplementalCredential( LogonId, CredentialUseFlags, UserName, DomainName, Password ); // If we're using a common set of data, free the captured stuff if ( Credential ) { if ( (UserName) && (UserName->Buffer) ) { NtLmFree( UserName->Buffer ); UserName->Buffer = NULL ; } if ( ( DomainName ) && (DomainName->Buffer) ) { NtLmFree( DomainName->Buffer ); DomainName->Buffer = NULL ; } if ( ( Password ) && ( Password->Buffer ) ) { ZeroMemory( Password->Buffer, Password->Length ); NtLmFree( Password->Buffer ); Password->Buffer = NULL ; } } } // If we didn't just find a credential, create one now. if (Credential == NULL) { // Allocate a credential block and initialize it. Credential = (PSSP_CREDENTIAL)NtLmAllocate(sizeof(SSP_CREDENTIAL) ); if ( Credential == NULL ) { Status = STATUS_NO_MEMORY; SspPrint((SSP_CRITICAL, "Error from NtLmAllocate 0x%lx\n", Status)); goto Cleanup; } Credential->References = 1; Credential->ClientProcessID = ClientProcessID; Credential->CredentialUseFlags = CredentialUseFlags; Credential->Unlinked = FALSE; if ( ( CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE ) != 0 ) { Credential->KernelClient = TRUE ; } else { Credential->KernelClient = FALSE ; } // Stick the token and logon ID in the credential Credential->ClientTokenHandle = *TokenHandle; // If the supplied access token is NULL, and the supplied LogonId // specifies the SYSTEM account, then we need to pickup a copy of // the SYSTEM access token. This is required so subsequent calls // so usage of that credential can be treated as Local. Otherwise, // subsequent calls in this scenario get treated as Anonymous // (no accesstoken, null creds) // See SsprHandleFirstCall() where it checks for NULL ClientTokenHandle // and NULL buffers for logic behind local calls. if( Credential->ClientTokenHandle == NULL ) { LUID LogonIdSystem = SYSTEM_LUID ; // do this only for SYSTEM originated calls when no creds specified. if( RtlEqualLuid( LogonId, &LogonIdSystem ) && NtLmGlobalAccessTokenSystem && DomainName && DomainName->Buffer == NULL && UserName && UserName->Buffer == NULL && Password && Password->Buffer == NULL ) { Status = SspDuplicateToken( NtLmGlobalAccessTokenSystem, SecurityImpersonation, &(Credential->ClientTokenHandle) ); } } // for loopback, enable all privileges in token to be consistent // with kerberos and ntlm network logon. if( (Credential->ClientTokenHandle != NULL) && !(CredentialUseFlags & NTLM_CRED_NULLSESSION) && (DomainName == NULL || DomainName->Length == 0) && (UserName == NULL || UserName->Length == 0) && (Password == NULL || Password->Length == 0) ) { if(!EnableAllPrivileges( Credential->ClientTokenHandle )) { SspPrint((SSP_CRITICAL, "Failed to EnableAllPrivileges for loopback logon %lx\n", Credential->ClientTokenHandle)); } } // so we don't close it in SpAcquireCredentialsHandle if( NT_SUCCESS( Status ) ) *TokenHandle = NULL; Credential->LogonId = *LogonId; // Stick the supplemental credentials into the credential. if (ARGUMENT_PRESENT(DomainName)) { Credential->DomainName = *DomainName; } if (ARGUMENT_PRESENT(UserName)) { Credential->UserName = *UserName; } if (ARGUMENT_PRESENT(Password)) { SspHidePassword(Password); Credential->Password = *Password; } // Add it to the list of valid credential handles. EnterCriticalSection( &SspCredentialCritSect ); InsertHeadList( &SspCredentialList, &Credential->Next ); LeaveCriticalSection( &SspCredentialCritSect ); SspPrint((SSP_API_MORE, "Added Credential 0x%lx\n", Credential )); // Don't bother dereferencing because we already set the // reference count to 1. } // Return output parameters to the caller. *CredentialHandle = (LSA_SEC_HANDLE) Credential; *Lifetime = NtLmGlobalForever; Cleanup: if ( !NT_SUCCESS(Status) ) { if ( Credential != NULL ) { (VOID)NtLmFree( Credential ); } } SspPrint((SSP_API_MORE, "Leaving SsprAcquireCredentialHandle\n")); return Status; } NTSTATUS SsprFreeCredentialHandle(IN ULONG_PTR CredentialHandle) /*++ Routine Description: This API is used to notify the security system that the credentials are no longer needed and allows the application to free the handle acquired in the call described above. When all references to this credential set has been removed then the credentials may themselves be removed. Arguments: CredentialHandle - Credential Handle obtained through AcquireCredentialHandle. Return Value: STATUS_SUCCESS -- Call completed successfully SEC_E_NO_SPM -- Security Support Provider is not running STATUS_INVALID_HANDLE -- Credential Handle is invalid --*/ { NTSTATUS Status = STATUS_SUCCESS; PSSP_CREDENTIAL Credential; SspPrint(( SSP_API_MORE, "SspFreeCredentialHandle Entered\n" )); // Find the referenced credential and delink it. Status = SspCredentialReferenceCredential( CredentialHandle, TRUE, // remove the instance of the credential FALSE, &Credential ); if ( !NT_SUCCESS( Status ) ) { goto Cleanup ; } // Dereferencing the Credential will remove the client's reference to it, causing it to be rundown if nobody else is using it. SspCredentialDereferenceCredential( Credential ); // Free and locally used resources. Cleanup: SspPrint(( SSP_API_MORE, "SspFreeCredentialHandle returns 0x%lx\n", Status )); return Status; } NTSTATUS SspCredentialInitialize(VOID) /*++ Routine Description: This function initializes this module. Return Value: Status of the operation. --*/ { // Initialize the Credential list to be empty. InitializeCriticalSection(&SspCredentialCritSect); InitializeListHead( &SspCredentialList ); return STATUS_SUCCESS; } VOID SspCredentialTerminate(VOID) /*++ Routine Description: This function cleans up any dangling credentials. Return Value: Status of the operation. --*/ { NTSTATUS Status ; // Drop any lingering Credentials EnterCriticalSection( &SspCredentialCritSect ); while ( !IsListEmpty( &SspCredentialList ) ) { ULONG_PTR CredentialHandle; PSSP_CREDENTIAL Credential; CredentialHandle = (LSA_SEC_HANDLE) CONTAINING_RECORD( SspCredentialList.Flink, SSP_CREDENTIAL, Next ); // note: we don't need to release and re-enter critsec because // the same thread can Enter and Leave a critical section multiple // times without dead-locking. Status = SspCredentialReferenceCredential(CredentialHandle,TRUE,TRUE,&Credential ); // Remove Credential if ( Credential != NULL ) { SspCredentialDereferenceCredential(Credential); } } LeaveCriticalSection( &SspCredentialCritSect ); // Delete the critical section DeleteCriticalSection(&SspCredentialCritSect); } BOOL EnableAllPrivileges(IN HANDLE ClientTokenHandle) { PTOKEN_PRIVILEGES pPrivileges; BYTE FastBuffer[ 512 ]; PBYTE SlowBuffer = NULL; DWORD cbPrivileges; BOOL fSuccess; pPrivileges = (PTOKEN_PRIVILEGES)FastBuffer; cbPrivileges = sizeof( FastBuffer ); fSuccess = GetTokenInformation(ClientTokenHandle,TokenPrivileges,pPrivileges,cbPrivileges,&cbPrivileges); if( !fSuccess ) { if( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) return FALSE; SlowBuffer = (PBYTE)NtLmAllocate( cbPrivileges ); if( SlowBuffer == NULL ) return FALSE; pPrivileges = (PTOKEN_PRIVILEGES)SlowBuffer; fSuccess = GetTokenInformation(ClientTokenHandle,TokenPrivileges,pPrivileges,cbPrivileges,&cbPrivileges); } if( fSuccess && pPrivileges->PrivilegeCount != 0 ) { DWORD indexPrivilege; for( indexPrivilege = 0 ; indexPrivilege < pPrivileges->PrivilegeCount ; indexPrivilege ++ ) { pPrivileges->Privileges[ indexPrivilege ].Attributes |= SE_PRIVILEGE_ENABLED; } fSuccess = AdjustTokenPrivileges(ClientTokenHandle,FALSE,pPrivileges,0,NULL,NULL); if( fSuccess && GetLastError() != ERROR_SUCCESS ) fSuccess = FALSE; } if( SlowBuffer ) NtLmFree( SlowBuffer ); return fSuccess; }