mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1525 lines
39 KiB
1525 lines
39 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: security.c
|
|
*
|
|
* Copyright (c) 1991, Microsoft Corporation
|
|
*
|
|
* Handles security aspects of winlogon operation.
|
|
*
|
|
* History:
|
|
* 12-05-91 Davidc Created - mostly taken from old winlogon.c
|
|
\***************************************************************************/
|
|
|
|
#include "msgina.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
//
|
|
// 'Constants' used in this module only.
|
|
//
|
|
SID_IDENTIFIER_AUTHORITY gSystemSidAuthority = SECURITY_NT_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY gLocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY;
|
|
PSID gLocalSid; // Initialized in 'InitializeSecurityGlobals'
|
|
PSID gAdminSid; // Initialized in 'InitializeSecurityGlobals'
|
|
PSID pWinlogonSid; // Initialized in 'InitializeSecurityGlobals'
|
|
|
|
|
|
typedef LONG ACEINDEX;
|
|
typedef ACEINDEX *PACEINDEX;
|
|
|
|
typedef struct _MYACE {
|
|
PSID Sid;
|
|
ACCESS_MASK AccessMask;
|
|
UCHAR InheritFlags;
|
|
} MYACE;
|
|
typedef MYACE *PMYACE;
|
|
|
|
BOOL
|
|
InitializeWindowsSecurity(
|
|
PGLOBALS pGlobals
|
|
);
|
|
|
|
BOOL
|
|
InitializeAuthentication(
|
|
IN PGLOBALS pGlobals
|
|
);
|
|
|
|
VOID
|
|
InitializeSecurityGlobals(
|
|
VOID
|
|
);
|
|
|
|
/***************************************************************************\
|
|
* SetMyAce
|
|
*
|
|
* Helper routine that fills in a MYACE structure.
|
|
*
|
|
* History:
|
|
* 02-06-92 Davidc Created
|
|
\***************************************************************************/
|
|
VOID
|
|
SetMyAce(
|
|
PMYACE MyAce,
|
|
PSID Sid,
|
|
ACCESS_MASK Mask,
|
|
UCHAR InheritFlags
|
|
)
|
|
{
|
|
MyAce->Sid = Sid;
|
|
MyAce->AccessMask= Mask;
|
|
MyAce->InheritFlags = InheritFlags;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* CreateAccessAllowedAce
|
|
*
|
|
* Allocates memory for an ACCESS_ALLOWED_ACE and fills it in.
|
|
* The memory should be freed by calling DestroyACE.
|
|
*
|
|
* Returns pointer to ACE on success, NULL on failure
|
|
*
|
|
* History:
|
|
* 12-05-91 Davidc Created
|
|
\***************************************************************************/
|
|
PVOID
|
|
CreateAccessAllowedAce(
|
|
PSID Sid,
|
|
ACCESS_MASK AccessMask,
|
|
UCHAR AceFlags,
|
|
UCHAR InheritFlags
|
|
)
|
|
{
|
|
ULONG LengthSid = RtlLengthSid(Sid);
|
|
ULONG LengthACE = sizeof(ACE_HEADER) + sizeof(ACCESS_MASK) + LengthSid;
|
|
PACCESS_ALLOWED_ACE Ace;
|
|
|
|
Ace = (PACCESS_ALLOWED_ACE)Alloc(LengthACE);
|
|
if (Ace == NULL) {
|
|
DebugLog((DEB_ERROR, "CreateAccessAllowedAce : Failed to allocate ace\n"));
|
|
return NULL;
|
|
}
|
|
|
|
Ace->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
|
|
Ace->Header.AceSize = (UCHAR)LengthACE;
|
|
Ace->Header.AceFlags = AceFlags | InheritFlags;
|
|
Ace->Mask = AccessMask;
|
|
RtlCopySid(LengthSid, (PSID)(&(Ace->SidStart)), Sid );
|
|
|
|
return(Ace);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* DestroyAce
|
|
*
|
|
* Frees the memory allocate for an ACE
|
|
*
|
|
* History:
|
|
* 12-05-91 Davidc Created
|
|
\***************************************************************************/
|
|
VOID
|
|
DestroyAce(
|
|
PVOID Ace
|
|
)
|
|
{
|
|
Free(Ace);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* CreateSecurityDescriptor
|
|
*
|
|
* Creates a security descriptor containing an ACL containing the specified ACEs
|
|
*
|
|
* A SD created with this routine should be destroyed using
|
|
* DeleteSecurityDescriptor
|
|
*
|
|
* Returns a pointer to the security descriptor or NULL on failure.
|
|
*
|
|
* 02-06-92 Davidc Created.
|
|
\***************************************************************************/
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
CreateSecurityDescriptor(
|
|
PMYACE MyAce,
|
|
ACEINDEX AceCount
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
ACEINDEX AceIndex;
|
|
PACCESS_ALLOWED_ACE *Ace;
|
|
PACL Acl = NULL;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
|
|
ULONG LengthAces;
|
|
ULONG LengthAcl;
|
|
ULONG LengthSd;
|
|
|
|
//
|
|
// Allocate space for the ACE pointer array
|
|
//
|
|
|
|
Ace = (PACCESS_ALLOWED_ACE *)Alloc(sizeof(PACCESS_ALLOWED_ACE) * AceCount);
|
|
if (Ace == NULL) {
|
|
DebugLog((DEB_ERROR, "Failed to allocated ACE array\n"));
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Create the ACEs and calculate total ACE size
|
|
//
|
|
|
|
LengthAces = 0;
|
|
for (AceIndex=0; AceIndex < AceCount; AceIndex ++) {
|
|
Ace[AceIndex] = CreateAccessAllowedAce(MyAce[AceIndex].Sid,
|
|
MyAce[AceIndex].AccessMask,
|
|
0,
|
|
MyAce[AceIndex].InheritFlags);
|
|
if (Ace[AceIndex] == NULL) {
|
|
DebugLog((DEB_ERROR, "Failed to allocate ace\n"));
|
|
} else {
|
|
LengthAces += Ace[AceIndex]->Header.AceSize;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Calculate ACL and SD sizes
|
|
//
|
|
|
|
LengthAcl = sizeof(ACL) + LengthAces;
|
|
LengthSd = SECURITY_DESCRIPTOR_MIN_LENGTH;
|
|
|
|
//
|
|
// Create the ACL
|
|
//
|
|
|
|
Acl = Alloc(LengthAcl);
|
|
|
|
if (Acl != NULL) {
|
|
|
|
Status = RtlCreateAcl(Acl, LengthAcl, ACL_REVISION);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
//
|
|
// Add the ACES to the ACL and destroy the ACEs
|
|
//
|
|
|
|
for (AceIndex = 0; AceIndex < AceCount; AceIndex ++) {
|
|
|
|
if (Ace[AceIndex] != NULL) {
|
|
|
|
Status = RtlAddAce(Acl, ACL_REVISION, 0, Ace[AceIndex],
|
|
Ace[AceIndex]->Header.AceSize);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugLog((DEB_ERROR, "AddAce failed, status = 0x%lx", Status));
|
|
}
|
|
|
|
DestroyAce(Ace[AceIndex]);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
DebugLog((DEB_ERROR, "Failed to allocate ACL\n"));
|
|
}
|
|
|
|
//
|
|
// Free the ACE pointer array
|
|
//
|
|
Free(Ace);
|
|
|
|
//
|
|
// Create the security descriptor
|
|
//
|
|
|
|
SecurityDescriptor = Alloc(LengthSd);
|
|
|
|
if (SecurityDescriptor != NULL) {
|
|
|
|
Status = RtlCreateSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
//
|
|
// Set the DACL on the security descriptor
|
|
//
|
|
Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Acl, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugLog((DEB_ERROR, "SetDACLSD failed, status = 0x%lx", Status));
|
|
}
|
|
} else {
|
|
|
|
DebugLog((DEB_ERROR, "Failed to allocate security descriptor\n"));
|
|
|
|
Free( Acl );
|
|
}
|
|
|
|
//
|
|
// Return with our spoils
|
|
//
|
|
return(SecurityDescriptor);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: FreeSecurityDescriptor
|
|
//
|
|
// Synopsis: Frees security descriptors created by CreateSecurityDescriptor
|
|
//
|
|
// Arguments: [SecurityDescriptor] --
|
|
//
|
|
// History: 5-09-96 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
FreeSecurityDescriptor(
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor
|
|
)
|
|
{
|
|
PACL Acl;
|
|
BOOL Present;
|
|
BOOL Defaulted;
|
|
|
|
Acl = NULL;
|
|
|
|
GetSecurityDescriptorDacl( SecurityDescriptor,
|
|
&Present,
|
|
&Acl,
|
|
&Defaulted );
|
|
|
|
if ( Acl )
|
|
{
|
|
Free( Acl );
|
|
}
|
|
|
|
Free( SecurityDescriptor );
|
|
|
|
}
|
|
/***************************************************************************\
|
|
* CreateUserThreadTokenSD
|
|
*
|
|
* Creates a security descriptor to protect tokens on user threads
|
|
*
|
|
* History:
|
|
* 12-05-91 Davidc Created
|
|
\***************************************************************************/
|
|
PSECURITY_DESCRIPTOR
|
|
CreateUserThreadTokenSD(
|
|
PSID UserSid,
|
|
PSID WinlogonSid
|
|
)
|
|
{
|
|
MYACE Ace[2];
|
|
ACEINDEX AceCount = 0;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|
|
|
ASSERT(UserSid != NULL); // should always have a non-null user sid
|
|
|
|
//
|
|
// Define the User ACEs
|
|
//
|
|
|
|
SetMyAce(&(Ace[AceCount++]),
|
|
UserSid,
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS |
|
|
TOKEN_ADJUST_DEFAULT | TOKEN_QUERY |
|
|
TOKEN_DUPLICATE | TOKEN_IMPERSONATE | READ_CONTROL,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Define the Winlogon ACEs
|
|
//
|
|
|
|
SetMyAce(&(Ace[AceCount++]),
|
|
WinlogonSid,
|
|
TOKEN_ALL_ACCESS,
|
|
0
|
|
);
|
|
|
|
// Check we didn't goof
|
|
ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount);
|
|
|
|
//
|
|
// Create the security descriptor
|
|
//
|
|
|
|
SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount);
|
|
if (SecurityDescriptor == NULL) {
|
|
DebugLog((DEB_ERROR, "failed to create user process token security descriptor\n"));
|
|
}
|
|
|
|
return(SecurityDescriptor);
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* InitializeSecurityGlobals
|
|
*
|
|
* Initializes the various global constants (mainly Sids used in this module.
|
|
*
|
|
* History:
|
|
* 12-05-91 Davidc Created
|
|
\***************************************************************************/
|
|
VOID
|
|
InitializeSecurityGlobals(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
|
|
ULONG SidLength;
|
|
BOOL Result;
|
|
|
|
//
|
|
// Get our sid so it can be put on object ACLs
|
|
//
|
|
|
|
SidLength = RtlLengthRequiredSid(1);
|
|
pWinlogonSid = (PSID)Alloc(SidLength);
|
|
if (!pWinlogonSid)
|
|
{
|
|
//
|
|
// We're dead. Couldn't even allocate memory for a measly SID...
|
|
//
|
|
return;
|
|
}
|
|
|
|
RtlInitializeSid(pWinlogonSid, &SystemSidAuthority, 1);
|
|
*(RtlSubAuthoritySid(pWinlogonSid, 0)) = SECURITY_LOCAL_SYSTEM_RID;
|
|
|
|
//
|
|
// Initialize the local sid for later
|
|
//
|
|
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&gLocalSidAuthority,
|
|
1,
|
|
SECURITY_LOCAL_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&gLocalSid
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
WLPrint(("Failed to initialize local sid, status = 0x%lx", Status));
|
|
}
|
|
|
|
//
|
|
// Initialize the admin sid for later
|
|
//
|
|
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&gSystemSidAuthority,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&gAdminSid
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
WLPrint(("Failed to initialize admin alias sid, status = 0x%lx", Status));
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InitializeAuthentication
|
|
*
|
|
* Initializes the authentication service. i.e. connects to the authentication
|
|
* package using the Lsa.
|
|
*
|
|
* On successful return, the following fields of our global structure are
|
|
* filled in :
|
|
* LsaHandle
|
|
* SecurityMode
|
|
* AuthenticationPackage
|
|
*
|
|
* Returns TRUE on success, FALSE on failure
|
|
*
|
|
* History:
|
|
* 12-05-91 Davidc Created
|
|
\***************************************************************************/
|
|
BOOL
|
|
InitializeAuthentication(
|
|
IN PGLOBALS pGlobals
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
STRING LogonProcessName, PackageName;
|
|
|
|
if (!EnablePrivilege(SE_TCB_PRIVILEGE, TRUE))
|
|
{
|
|
DebugLog((DEB_ERROR, "Failed to enable SeTcbPrivilege!\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Hookup to the LSA and locate our authentication package.
|
|
//
|
|
|
|
RtlInitString(&LogonProcessName, "Winlogon\\MSGina");
|
|
Status = LsaRegisterLogonProcess(
|
|
&LogonProcessName,
|
|
&pGlobals->LsaHandle,
|
|
&pGlobals->SecurityMode
|
|
);
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugLog((DEB_ERROR, "Unable to connect to LSA: %#x\n", Status));
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
//
|
|
// Connect with the MSV1_0 authentication package
|
|
//
|
|
RtlInitString(&PackageName, "MICROSOFT_AUTHENTICATION_PACKAGE_V1_0");
|
|
Status = LsaLookupAuthenticationPackage (
|
|
pGlobals->LsaHandle,
|
|
&PackageName,
|
|
&pGlobals->AuthenticationPackage
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugLog((DEB_ERROR, "Failed to find MSV1_0 authentication package, status = 0x%lx", Status));
|
|
return(FALSE);
|
|
}
|
|
|
|
InitializeSecurityGlobals();
|
|
|
|
pGlobals->WinlogonSid = pWinlogonSid;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* LogonUser
|
|
*
|
|
* Calls the Lsa to logon the specified user.
|
|
*
|
|
* The LogonSid and a LocalSid is added to the user's groups on successful logon
|
|
*
|
|
* For this release, password lengths are restricted to 255 bytes in length.
|
|
* This allows us to use the upper byte of the String.Length field to
|
|
* carry a seed needed to decode the run-encoded password. If the password
|
|
* is not run-encoded, the upper byte of the String.Length field should
|
|
* be zero.
|
|
*
|
|
*
|
|
* On successful return, LogonToken is a handle to the user's token,
|
|
* the profile buffer contains user profile information.
|
|
*
|
|
* History:
|
|
* 12-05-91 Davidc Created
|
|
\***************************************************************************/
|
|
NTSTATUS
|
|
WinLogonUser(
|
|
IN HANDLE LsaHandle,
|
|
IN ULONG AuthenticationPackage,
|
|
IN SECURITY_LOGON_TYPE LogonType,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING Domain,
|
|
IN PUNICODE_STRING Password,
|
|
IN PSID LogonSid,
|
|
OUT PLUID LogonId,
|
|
OUT PHANDLE LogonToken,
|
|
OUT PQUOTA_LIMITS Quotas,
|
|
OUT PVOID *pProfileBuffer,
|
|
OUT PULONG pProfileBufferLength,
|
|
OUT PNTSTATUS pSubStatus
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
STRING OriginName;
|
|
TOKEN_SOURCE SourceContext;
|
|
PMSV1_0_INTERACTIVE_LOGON MsvAuthInfo;
|
|
PVOID AuthInfoBuf;
|
|
ULONG AuthInfoSize;
|
|
PTOKEN_GROUPS TokenGroups;
|
|
PSECURITY_SEED_AND_LENGTH SeedAndLength;
|
|
UCHAR Seed;
|
|
|
|
DebugLog((DEB_TRACE, " LsaHandle = %x\n", LsaHandle));
|
|
DebugLog((DEB_TRACE, " AuthenticationPackage = %d\n", AuthenticationPackage));
|
|
DebugLog((DEB_TRACE, " UserName = %ws\n", UserName->Buffer));
|
|
DebugLog((DEB_TRACE, " Domain = %ws\n", Domain->Buffer));
|
|
DebugLog((DEB_TRACE, " LogonSid = @%#x\n", LogonSid));
|
|
#if DBG
|
|
if (!RtlValidSid(LogonSid))
|
|
{
|
|
DebugLog((DEB_ERROR, "LogonSid is invalid!\n"));
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Initialize source context structure
|
|
//
|
|
|
|
strncpy(SourceContext.SourceName, "User32 ", sizeof(SourceContext.SourceName)); // LATER from res file
|
|
Status = NtAllocateLocallyUniqueId(&SourceContext.SourceIdentifier);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugLog((DEB_ERROR, "failed to allocate locally unique id, status = 0x%lx", Status));
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// Get any run-encoding information out of the way
|
|
// and decode the password. This creates a window
|
|
// where the cleartext password will be in memory.
|
|
// Keep it short.
|
|
//
|
|
// Save the seed so we can use the same one again.
|
|
//
|
|
|
|
SeedAndLength = (PSECURITY_SEED_AND_LENGTH)(&Password->Length);
|
|
Seed = SeedAndLength->Seed;
|
|
|
|
|
|
//
|
|
// Build the authentication information buffer
|
|
//
|
|
|
|
if (Seed != 0) {
|
|
RevealPassword( Password );
|
|
}
|
|
AuthInfoSize = sizeof(MSV1_0_INTERACTIVE_LOGON) +
|
|
sizeof(TCHAR)*(lstrlen(UserName->Buffer) + 1 +
|
|
lstrlen(Domain->Buffer) + 1 +
|
|
lstrlen(Password->Buffer) + 1 );
|
|
HidePassword( &Seed, Password );
|
|
|
|
|
|
MsvAuthInfo = AuthInfoBuf = Alloc(AuthInfoSize);
|
|
if (MsvAuthInfo == NULL) {
|
|
DebugLog((DEB_ERROR, "failed to allocate memory for authentication buffer\n"));
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
//
|
|
// This authentication buffer will be used for a logon attempt
|
|
//
|
|
|
|
MsvAuthInfo->MessageType = MsV1_0InteractiveLogon;
|
|
|
|
|
|
//
|
|
// Set logon origin
|
|
//
|
|
|
|
RtlInitString(&OriginName, "Winlogon");
|
|
|
|
|
|
//
|
|
// Copy the user name into the authentication buffer
|
|
//
|
|
|
|
MsvAuthInfo->UserName.Length =
|
|
(USHORT)sizeof(TCHAR)*lstrlen(UserName->Buffer);
|
|
MsvAuthInfo->UserName.MaximumLength =
|
|
MsvAuthInfo->UserName.Length + sizeof(TCHAR);
|
|
|
|
MsvAuthInfo->UserName.Buffer = (PWSTR)(MsvAuthInfo+1);
|
|
lstrcpy(MsvAuthInfo->UserName.Buffer, UserName->Buffer);
|
|
|
|
|
|
//
|
|
// Copy the domain name into the authentication buffer
|
|
//
|
|
|
|
MsvAuthInfo->LogonDomainName.Length =
|
|
(USHORT)sizeof(TCHAR)*lstrlen(Domain->Buffer);
|
|
MsvAuthInfo->LogonDomainName.MaximumLength =
|
|
MsvAuthInfo->LogonDomainName.Length + sizeof(TCHAR);
|
|
|
|
MsvAuthInfo->LogonDomainName.Buffer = (PWSTR)
|
|
((PBYTE)(MsvAuthInfo->UserName.Buffer) +
|
|
MsvAuthInfo->UserName.MaximumLength);
|
|
|
|
lstrcpy(MsvAuthInfo->LogonDomainName.Buffer, Domain->Buffer);
|
|
|
|
//
|
|
// Copy the password into the authentication buffer
|
|
// Hide it once we have copied it. Use the same seed value
|
|
// that we used for the original password in pGlobals.
|
|
//
|
|
|
|
RevealPassword( Password );
|
|
MsvAuthInfo->Password.Length =
|
|
(USHORT)sizeof(TCHAR)*lstrlen(Password->Buffer);
|
|
MsvAuthInfo->Password.MaximumLength =
|
|
MsvAuthInfo->Password.Length + sizeof(TCHAR);
|
|
|
|
MsvAuthInfo->Password.Buffer = (PWSTR)
|
|
((PBYTE)(MsvAuthInfo->LogonDomainName.Buffer) +
|
|
MsvAuthInfo->LogonDomainName.MaximumLength);
|
|
lstrcpy(MsvAuthInfo->Password.Buffer, Password->Buffer);
|
|
HidePassword( &Seed, Password);
|
|
HidePassword( &Seed, (PUNICODE_STRING) &MsvAuthInfo->Password);
|
|
|
|
|
|
//
|
|
// Create logon token groups
|
|
//
|
|
|
|
#define TOKEN_GROUP_COUNT 2 // We'll add the local SID and the logon SID
|
|
|
|
TokenGroups = (PTOKEN_GROUPS)Alloc(sizeof(TOKEN_GROUPS) +
|
|
(TOKEN_GROUP_COUNT - ANYSIZE_ARRAY) * sizeof(SID_AND_ATTRIBUTES));
|
|
if (TokenGroups == NULL) {
|
|
DebugLog((DEB_ERROR, "failed to allocate memory for token groups"));
|
|
Free(AuthInfoBuf);
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
//
|
|
// Fill in the logon token group list
|
|
//
|
|
|
|
TokenGroups->GroupCount = TOKEN_GROUP_COUNT;
|
|
TokenGroups->Groups[0].Sid = LogonSid;
|
|
TokenGroups->Groups[0].Attributes =
|
|
SE_GROUP_MANDATORY | SE_GROUP_ENABLED |
|
|
SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_LOGON_ID;
|
|
TokenGroups->Groups[1].Sid = gLocalSid;
|
|
TokenGroups->Groups[1].Attributes =
|
|
SE_GROUP_MANDATORY | SE_GROUP_ENABLED |
|
|
SE_GROUP_ENABLED_BY_DEFAULT;
|
|
|
|
|
|
//
|
|
// Now try to log this sucker on
|
|
//
|
|
|
|
Status = LsaLogonUser (
|
|
LsaHandle,
|
|
&OriginName,
|
|
LogonType,
|
|
AuthenticationPackage,
|
|
AuthInfoBuf,
|
|
AuthInfoSize,
|
|
TokenGroups,
|
|
&SourceContext,
|
|
pProfileBuffer,
|
|
pProfileBufferLength,
|
|
LogonId,
|
|
LogonToken,
|
|
Quotas,
|
|
pSubStatus
|
|
);
|
|
|
|
|
|
|
|
//
|
|
// Discard token group list
|
|
//
|
|
|
|
Free(TokenGroups);
|
|
|
|
//
|
|
// Discard authentication buffer
|
|
//
|
|
|
|
Free(AuthInfoBuf);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* EnablePrivilege
|
|
*
|
|
* Enables/disables the specified well-known privilege in the current thread
|
|
* token if there is one, otherwise the current process token.
|
|
*
|
|
* Returns TRUE on success, FALSE on failure
|
|
*
|
|
* History:
|
|
* 12-05-91 Davidc Created
|
|
\***************************************************************************/
|
|
BOOL
|
|
EnablePrivilege(
|
|
ULONG Privilege,
|
|
BOOL Enable
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN WasEnabled;
|
|
|
|
//
|
|
// Try the thread token first
|
|
//
|
|
|
|
Status = RtlAdjustPrivilege(Privilege,
|
|
(BOOLEAN)Enable,
|
|
TRUE,
|
|
&WasEnabled);
|
|
|
|
if (Status == STATUS_NO_TOKEN) {
|
|
|
|
//
|
|
// No thread token, use the process token
|
|
//
|
|
|
|
Status = RtlAdjustPrivilege(Privilege,
|
|
(BOOLEAN)Enable,
|
|
FALSE,
|
|
&WasEnabled);
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugLog((DEB_ERROR, "Failed to %ws privilege : 0x%lx, status = 0x%lx", Enable ? TEXT("enable") : TEXT("disable"), Privilege, Status));
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* TestTokenForAdmin
|
|
*
|
|
* Returns TRUE if the token passed represents an admin user, otherwise FALSE
|
|
*
|
|
* The token handle passed must have TOKEN_QUERY access.
|
|
*
|
|
* History:
|
|
* 05-06-92 Davidc Created
|
|
\***************************************************************************/
|
|
BOOL
|
|
TestTokenForAdmin(
|
|
HANDLE Token
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG InfoLength;
|
|
PTOKEN_GROUPS TokenGroupList;
|
|
ULONG GroupIndex;
|
|
BOOL FoundAdmin;
|
|
|
|
//
|
|
// Get a list of groups in the token
|
|
//
|
|
|
|
Status = NtQueryInformationToken(
|
|
Token, // Handle
|
|
TokenGroups, // TokenInformationClass
|
|
NULL, // TokenInformation
|
|
0, // TokenInformationLength
|
|
&InfoLength // ReturnLength
|
|
);
|
|
|
|
if ((Status != STATUS_SUCCESS) && (Status != STATUS_BUFFER_TOO_SMALL)) {
|
|
|
|
DebugLog((DEB_ERROR, "failed to get group info for admin token, status = 0x%lx", Status));
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
TokenGroupList = Alloc(InfoLength);
|
|
|
|
if (TokenGroupList == NULL) {
|
|
DebugLog((DEB_ERROR, "unable to allocate memory for token groups"));
|
|
return(FALSE);
|
|
}
|
|
|
|
Status = NtQueryInformationToken(
|
|
Token, // Handle
|
|
TokenGroups, // TokenInformationClass
|
|
TokenGroupList, // TokenInformation
|
|
InfoLength, // TokenInformationLength
|
|
&InfoLength // ReturnLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugLog((DEB_ERROR, "failed to query groups for admin token, status = 0x%lx", Status));
|
|
Free(TokenGroupList);
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
//
|
|
// Search group list for admin alias
|
|
//
|
|
|
|
FoundAdmin = FALSE;
|
|
|
|
for (GroupIndex=0; GroupIndex < TokenGroupList->GroupCount; GroupIndex++ ) {
|
|
|
|
if (RtlEqualSid(TokenGroupList->Groups[GroupIndex].Sid, gAdminSid)) {
|
|
FoundAdmin = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tidy up
|
|
//
|
|
|
|
Free(TokenGroupList);
|
|
|
|
|
|
|
|
return(FoundAdmin);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* TestUserForAdmin
|
|
*
|
|
* Returns TRUE if the named user is an admin. This is done by attempting to
|
|
* log the user on and examining their token.
|
|
*
|
|
* NOTE: The password will be erased upon return to prevent it from being
|
|
* visually identifiable in a pagefile.
|
|
*
|
|
* History:
|
|
* 03-16-92 Davidc Created
|
|
\***************************************************************************/
|
|
BOOL
|
|
TestUserForAdmin(
|
|
PGLOBALS pGlobals,
|
|
IN PWCHAR UserName,
|
|
IN PWCHAR Domain,
|
|
IN PUNICODE_STRING PasswordString
|
|
)
|
|
{
|
|
NTSTATUS Status, SubStatus, IgnoreStatus;
|
|
UNICODE_STRING UserNameString;
|
|
UNICODE_STRING DomainString;
|
|
PVOID ProfileBuffer;
|
|
ULONG ProfileBufferLength;
|
|
QUOTA_LIMITS Quotas;
|
|
HANDLE Token;
|
|
BOOL UserIsAdmin;
|
|
LUID LogonId;
|
|
|
|
RtlInitUnicodeString(&UserNameString, UserName);
|
|
RtlInitUnicodeString(&DomainString, Domain);
|
|
|
|
//
|
|
// Temporarily log this new subject on and see if their groups
|
|
// contain the appropriate admin group
|
|
//
|
|
|
|
Status = WinLogonUser(
|
|
pGlobals->LsaHandle,
|
|
pGlobals->AuthenticationPackage,
|
|
Interactive,
|
|
&UserNameString,
|
|
&DomainString,
|
|
PasswordString,
|
|
pGlobals->LogonSid, // any sid will do
|
|
&LogonId,
|
|
&Token,
|
|
&Quotas,
|
|
&ProfileBuffer,
|
|
&ProfileBufferLength,
|
|
&SubStatus);
|
|
|
|
RtlEraseUnicodeString( PasswordString );
|
|
|
|
//
|
|
// If we couldn't log them on, they're not an admin
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Free up the profile buffer
|
|
//
|
|
|
|
IgnoreStatus = LsaFreeReturnBuffer(ProfileBuffer);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
|
|
|
|
//
|
|
// See if the token represents an admin user
|
|
//
|
|
|
|
UserIsAdmin = TestTokenForAdmin(Token);
|
|
|
|
//
|
|
// We're finished with the token
|
|
//
|
|
|
|
IgnoreStatus = NtClose(Token);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
|
|
|
|
return(UserIsAdmin);
|
|
}
|
|
|
|
BOOL
|
|
UnlockLogon(
|
|
PGLOBALS pGlobals,
|
|
IN PWCHAR UserName,
|
|
IN PWCHAR Domain,
|
|
IN PUNICODE_STRING PasswordString
|
|
)
|
|
{
|
|
NTSTATUS Status, SubStatus, IgnoreStatus;
|
|
UNICODE_STRING UserNameString;
|
|
UNICODE_STRING DomainString;
|
|
PVOID ProfileBuffer;
|
|
ULONG ProfileBufferLength;
|
|
QUOTA_LIMITS Quotas;
|
|
HANDLE Token;
|
|
BOOL UserIsAdmin;
|
|
LUID LogonId;
|
|
|
|
RtlInitUnicodeString(&UserNameString, UserName);
|
|
RtlInitUnicodeString(&DomainString, Domain);
|
|
|
|
//
|
|
// Temporarily log this new subject on and see if their groups
|
|
// contain the appropriate admin group
|
|
//
|
|
|
|
Status = WinLogonUser(
|
|
pGlobals->LsaHandle,
|
|
pGlobals->AuthenticationPackage,
|
|
Unlock,
|
|
&UserNameString,
|
|
&DomainString,
|
|
PasswordString,
|
|
pGlobals->LogonSid, // any sid will do
|
|
&LogonId,
|
|
&Token,
|
|
&Quotas,
|
|
&ProfileBuffer,
|
|
&ProfileBufferLength,
|
|
&SubStatus);
|
|
|
|
RtlEraseUnicodeString( PasswordString );
|
|
|
|
//
|
|
// If we couldn't log them on, they're not an admin
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Free up the profile buffer
|
|
//
|
|
|
|
IgnoreStatus = LsaFreeReturnBuffer(ProfileBuffer);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
|
|
|
|
//
|
|
// We're finished with the token
|
|
//
|
|
|
|
IgnoreStatus = NtClose(Token);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: ImpersonateUser
|
|
*
|
|
* PURPOSE: Impersonates the user by setting the users token
|
|
* on the specified thread. If no thread is specified the token
|
|
* is set on the current thread.
|
|
*
|
|
* RETURNS: Handle to be used on call to StopImpersonating() or NULL on failure
|
|
* If a non-null thread handle was passed in, the handle returned will
|
|
* be the one passed in. (See note)
|
|
*
|
|
* NOTES: Take care when passing in a thread handle and then calling
|
|
* StopImpersonating() with the handle returned by this routine.
|
|
* StopImpersonating() will close any thread handle passed to it -
|
|
* even yours !
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 04-21-92 Davidc Created.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
HANDLE
|
|
ImpersonateUser(
|
|
PUSER_PROCESS_DATA UserProcessData,
|
|
HANDLE ThreadHandle
|
|
)
|
|
{
|
|
NTSTATUS Status, IgnoreStatus;
|
|
HANDLE UserToken = UserProcessData->UserToken;
|
|
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE ImpersonationToken;
|
|
BOOL ThreadHandleOpened = FALSE;
|
|
|
|
if (ThreadHandle == NULL) {
|
|
|
|
//
|
|
// Get a handle to the current thread.
|
|
// Once we have this handle, we can set the user's impersonation
|
|
// token into the thread and remove it later even though we ARE
|
|
// the user for the removal operation. This is because the handle
|
|
// contains the access rights - the access is not re-evaluated
|
|
// at token removal time.
|
|
//
|
|
|
|
Status = NtDuplicateObject( NtCurrentProcess(), // Source process
|
|
NtCurrentThread(), // Source handle
|
|
NtCurrentProcess(), // Target process
|
|
&ThreadHandle, // Target handle
|
|
THREAD_SET_THREAD_TOKEN,// Access
|
|
0L, // Attributes
|
|
DUPLICATE_SAME_ATTRIBUTES
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugLog((DEB_ERROR, "ImpersonateUser : Failed to duplicate thread handle, status = 0x%lx", Status));
|
|
return(NULL);
|
|
}
|
|
|
|
ThreadHandleOpened = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// If the usertoken is NULL, there's nothing to do
|
|
//
|
|
|
|
if (UserToken != NULL) {
|
|
|
|
//
|
|
// UserToken is a primary token - create an impersonation token version
|
|
// of it so we can set it on our thread
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
NULL,
|
|
0L,
|
|
NULL,
|
|
UserProcessData->NewThreadTokenSD);
|
|
|
|
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
|
|
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
SecurityQualityOfService.EffectiveOnly = FALSE;
|
|
|
|
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
|
|
|
|
|
|
Status = NtDuplicateToken( UserToken,
|
|
TOKEN_IMPERSONATE | TOKEN_ADJUST_PRIVILEGES |
|
|
TOKEN_QUERY,
|
|
&ObjectAttributes,
|
|
FALSE,
|
|
TokenImpersonation,
|
|
&ImpersonationToken
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
DebugLog((DEB_ERROR, "Failed to duplicate users token to create impersonation thread, status = 0x%lx", Status));
|
|
|
|
if (ThreadHandleOpened) {
|
|
IgnoreStatus = NtClose(ThreadHandle);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Set the impersonation token on this thread so we 'are' the user
|
|
//
|
|
|
|
Status = NtSetInformationThread( ThreadHandle,
|
|
ThreadImpersonationToken,
|
|
(PVOID)&ImpersonationToken,
|
|
sizeof(ImpersonationToken)
|
|
);
|
|
//
|
|
// We're finished with our handle to the impersonation token
|
|
//
|
|
|
|
IgnoreStatus = NtClose(ImpersonationToken);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
|
|
//
|
|
// Check we set the token on our thread ok
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
DebugLog((DEB_ERROR, "Failed to set user impersonation token on winlogon thread, status = 0x%lx", Status));
|
|
|
|
if (ThreadHandleOpened) {
|
|
IgnoreStatus = NtClose(ThreadHandle);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
|
|
return(ThreadHandle);
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: StopImpersonating
|
|
*
|
|
* PURPOSE: Stops impersonating the client by removing the token on the
|
|
* current thread.
|
|
*
|
|
* PARAMETERS: ThreadHandle - handle returned by ImpersonateUser() call.
|
|
*
|
|
* RETURNS: TRUE on success, FALSE on failure
|
|
*
|
|
* NOTES: If a thread handle was passed in to ImpersonateUser() then the
|
|
* handle returned was one and the same. If this is passed to
|
|
* StopImpersonating() the handle will be closed. Take care !
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 04-21-92 Davidc Created.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL
|
|
StopImpersonating(
|
|
HANDLE ThreadHandle
|
|
)
|
|
{
|
|
NTSTATUS Status, IgnoreStatus;
|
|
HANDLE ImpersonationToken;
|
|
|
|
|
|
//
|
|
// Remove the user's token from our thread so we are 'ourself' again
|
|
//
|
|
|
|
ImpersonationToken = NULL;
|
|
|
|
Status = NtSetInformationThread( ThreadHandle,
|
|
ThreadImpersonationToken,
|
|
(PVOID)&ImpersonationToken,
|
|
sizeof(ImpersonationToken)
|
|
);
|
|
//
|
|
// We're finished with the thread handle
|
|
//
|
|
|
|
IgnoreStatus = NtClose(ThreadHandle);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugLog((DEB_ERROR, "Failed to remove user impersonation token from winlogon thread, status = 0x%lx", Status));
|
|
}
|
|
|
|
return(NT_SUCCESS(Status));
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* TestUserPrivilege
|
|
*
|
|
* Looks at the user token to determine if they have the specified privilege
|
|
*
|
|
* Returns TRUE if the user has the privilege, otherwise FALSE
|
|
*
|
|
* History:
|
|
* 04-21-92 Davidc Created
|
|
\***************************************************************************/
|
|
BOOL
|
|
TestUserPrivilege(
|
|
PGLOBALS pGlobals,
|
|
ULONG Privilege
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
NTSTATUS IgnoreStatus;
|
|
HANDLE UserToken;
|
|
BOOL TokenOpened;
|
|
LUID LuidPrivilege;
|
|
LUID TokenPrivilege;
|
|
PTOKEN_PRIVILEGES Privileges;
|
|
ULONG BytesRequired;
|
|
ULONG i;
|
|
BOOL Found;
|
|
|
|
UserToken = pGlobals->UserToken;
|
|
TokenOpened = FALSE;
|
|
|
|
|
|
//
|
|
// If the token is NULL, get a token for the current process since
|
|
// this is the token that will be inherited by new processes.
|
|
//
|
|
|
|
if (UserToken == NULL) {
|
|
|
|
Status = NtOpenProcessToken(
|
|
NtCurrentProcess(),
|
|
TOKEN_QUERY,
|
|
&UserToken
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugLog((DEB_ERROR, "Can't open own process token for token_query access"));
|
|
return(FALSE);
|
|
}
|
|
|
|
TokenOpened = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Find out how much memory we need to allocate
|
|
//
|
|
|
|
Status = NtQueryInformationToken(
|
|
UserToken, // Handle
|
|
TokenPrivileges, // TokenInformationClass
|
|
NULL, // TokenInformation
|
|
0, // TokenInformationLength
|
|
&BytesRequired // ReturnLength
|
|
);
|
|
|
|
if (Status != STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugLog((DEB_ERROR, "Failed to query privileges from user token, status = 0x%lx", Status));
|
|
}
|
|
|
|
if (TokenOpened) {
|
|
IgnoreStatus = NtClose(UserToken);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate space for the privilege array
|
|
//
|
|
|
|
Privileges = Alloc(BytesRequired);
|
|
if (Privileges == NULL) {
|
|
|
|
DebugLog((DEB_ERROR, "Failed to allocate memory for user privileges"));
|
|
|
|
if (TokenOpened) {
|
|
IgnoreStatus = NtClose(UserToken);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
//
|
|
// Read in the user privileges
|
|
//
|
|
|
|
Status = NtQueryInformationToken(
|
|
UserToken, // Handle
|
|
TokenPrivileges, // TokenInformationClass
|
|
Privileges, // TokenInformation
|
|
BytesRequired, // TokenInformationLength
|
|
&BytesRequired // ReturnLength
|
|
);
|
|
|
|
//
|
|
// We're finished with the token handle
|
|
//
|
|
|
|
if (TokenOpened) {
|
|
IgnoreStatus = NtClose(UserToken);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
}
|
|
|
|
//
|
|
// See if we got the privileges
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
DebugLog((DEB_ERROR, "Failed to query privileges from user token"));
|
|
|
|
Free(Privileges);
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// See if the user has the privilege we're looking for.
|
|
//
|
|
|
|
LuidPrivilege = RtlConvertLongToLuid(Privilege);
|
|
Found = FALSE;
|
|
|
|
for (i=0; i<Privileges->PrivilegeCount; i++) {
|
|
|
|
TokenPrivilege = *((LUID UNALIGNED *) &Privileges->Privileges[i].Luid);
|
|
if (RtlEqualLuid(&TokenPrivilege, &LuidPrivilege))
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
Free(Privileges);
|
|
|
|
return(Found);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: HidePassword
|
|
*
|
|
* PURPOSE: Run-encodes the password so that it is not very visually
|
|
* distinguishable. This is so that if it makes it to a
|
|
* paging file, it wont be obvious.
|
|
*
|
|
* if pGlobals->Seed is zero, then we will allocate and assign
|
|
* a seed value. Otherwise, the existing seed value is used.
|
|
*
|
|
* WARNING - This routine will use the upper portion of the
|
|
* password's length field to store the seed used in encoding
|
|
* password. Be careful you don't pass such a string to
|
|
* a routine that looks at the length (like and RPC routine).
|
|
*
|
|
*
|
|
* RETURNS: (None)
|
|
*
|
|
* NOTES:
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 04-27-93 JimK Created.
|
|
*
|
|
\***************************************************************************/
|
|
VOID
|
|
HidePassword(
|
|
PUCHAR Seed OPTIONAL,
|
|
PUNICODE_STRING Password
|
|
)
|
|
{
|
|
PSECURITY_SEED_AND_LENGTH
|
|
SeedAndLength;
|
|
|
|
UCHAR
|
|
LocalSeed;
|
|
|
|
//
|
|
// If no seed address passed, use our own local seed buffer
|
|
//
|
|
|
|
if (Seed == NULL) {
|
|
Seed = &LocalSeed;
|
|
LocalSeed = 0;
|
|
}
|
|
|
|
SeedAndLength = (PSECURITY_SEED_AND_LENGTH)&Password->Length;
|
|
//ASSERT(*((LPWCH)SeedAndLength+Password->Length) == 0);
|
|
ASSERT((SeedAndLength->Seed) == 0);
|
|
|
|
RtlRunEncodeUnicodeString(
|
|
Seed,
|
|
Password
|
|
);
|
|
|
|
SeedAndLength->Seed = (*Seed);
|
|
return;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: RevealPassword
|
|
*
|
|
* PURPOSE: Reveals a previously hidden password so that it
|
|
* is plain text once again.
|
|
*
|
|
* RETURNS: (None)
|
|
*
|
|
* NOTES:
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 04-27-93 JimK Created.
|
|
*
|
|
\***************************************************************************/
|
|
VOID
|
|
RevealPassword(
|
|
PUNICODE_STRING HiddenPassword
|
|
)
|
|
{
|
|
PSECURITY_SEED_AND_LENGTH
|
|
SeedAndLength;
|
|
|
|
UCHAR
|
|
Seed;
|
|
|
|
SeedAndLength = (PSECURITY_SEED_AND_LENGTH)&HiddenPassword->Length;
|
|
Seed = SeedAndLength->Seed;
|
|
SeedAndLength->Seed = 0;
|
|
|
|
RtlRunDecodeUnicodeString(
|
|
Seed,
|
|
HiddenPassword
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: ErasePassword
|
|
*
|
|
* PURPOSE: zeros a password that is no longer needed.
|
|
*
|
|
* RETURNS: (None)
|
|
*
|
|
* NOTES:
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 04-27-93 JimK Created.
|
|
*
|
|
\***************************************************************************/
|
|
VOID
|
|
ErasePassword(
|
|
PUNICODE_STRING Password
|
|
)
|
|
{
|
|
PSECURITY_SEED_AND_LENGTH
|
|
SeedAndLength;
|
|
|
|
SeedAndLength = (PSECURITY_SEED_AND_LENGTH)&Password->Length;
|
|
SeedAndLength->Seed = 0;
|
|
|
|
RtlEraseUnicodeString(
|
|
Password
|
|
);
|
|
|
|
return;
|
|
|
|
}
|