windows-nt/Source/XPSP1/NT/ds/security/protocols/common/common.cxx
2020-09-26 16:20:57 +08:00

702 lines
18 KiB
C++

//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 2000
//
// File: common.cxx
//
// Contents: Shared SSPI code
//
//
// History: 11-March-2000 Created Todds
//
//------------------------------------------------------------------------
//+-------------------------------------------------------------------------
//
// Function: SspAllocate
//
// Synopsis: Copies a string from the client and if necessary converts
// from ansi to unicode
//
// Effects: allocates output with either KerbAllocate (unicode)
// or RtlAnsiStringToUnicodeString
//
// Arguments: StringPointer - address of string in client process
// StringLength - Lenght (in characters) of string
// AnsiString - if TRUE, string is ansi
// LocalString - receives allocated string
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
PVOID
SspAllocate(
IN ULONG BufferSize
)
{
PVOID pBuffer = NULL;
if (*pSspState == SspLsaMode)
{
pBuffer = LsaFunctions->AllocateLsaHeap(BufferSize);
if (pBuffer != NULL)
{
RtlZeroMemory(Buffer, BufferSize);
}
}
else
{
ASSERT((*pSspState) == SspUserMode);
pBuffer = LocalAlloc(LPTR, BufferSize);
}
return pBuffer;
}
//+-------------------------------------------------------------------------
//
// Function: SspFree
//
// Synopsis: Copies a string from the client and if necessary converts
// from ansi to unicode
//
// Effects: allocates output with either KerbAllocate (unicode)
// or RtlAnsiStringToUnicodeString
//
// Arguments: StringPointer - address of string in client process
// StringLength - Lenght (in characters) of string
// AnsiString - if TRUE, string is ansi
// LocalString - receives allocated string
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
SspFree(
IN PVOID pBuffer
)
{
if (ARGUMENT_PRESENT(Buffer))
{
if ((*pSspState) == SspLsaMode)
{
LsaFunctions->FreeLsaHeap(Buffer);
}
else
{
ASSERT((*pSspState) == SspUserMode);
LocalFree(Buffer);
}
}
}
//+-------------------------------------------------------------------------
//
// Function: SspCopyClientString
//
// Synopsis: Copies a string from the client and if necessary converts
// from ansi to unicode
//
// Effects: allocates output with SspAllocate, free w/ SspFree
//
// Arguments: StringPointer - address of string in client process
// StringLength - Lenght (in characters) of string
// UnicodeString - if TRUE, string is ansi
// MaxLength - Maximum length of string (useful for pwds)
// LocalString - receives allocated string
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
SspCopyClientString(
IN PVOID StringPointer,
IN ULONG StringLength,
IN BOOLEAN UnicodeString,
OUT PUNICODE_STRING LocalString,
IN ULONG MaxLength = 0xFFFF
)
{
NTSTATUS Status = STATUS_SUCCESS;
STRING TempString = {0};
ULONG SourceSize = 0;
ULONG CharacterSize = (DoUnicode ? sizeof(WCHAR) : sizeof(CHAR));
// init outputs
LocalString->Length = LocalString->MaximumLength = 0;
LocalString->Buffer = NULL;
if (NULL != StringPointer)
{
// For 0 length strings, allocate 2 bytes
if (StringLength == 0)
{
LocalString->Buffer = (LPWSTR) SspAllocate(sizeof(WCHAR));
if (NULL == LocalString->Buffer)
{
DebugLog((DEB_ERROR,"SspCopyClientString allocation failure!\n");
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
LocalString->MaximumLength = sizeof(WCHAR);
*LocalString->Buffer = L"\0";
}
else
{
//
// Ensure no overflow against UNICODE_STRING, or desired string
//
SourceSize = (StringLength + 1) * CharacterSize;
if ((StringLength > MaxLength) || (SourceSize > 0xFFFF))
{
Status = STATUS_INVALID_PARAMETER;
DebugLog((DEB_WARN, "SspCopyClientString, String is too big for UNICODE_STRING\n"));
goto Cleanup;
}
TempString.Buffer = (LPSTR) SspAllocate(SourceSize);
if (NULL == TempString.Buffer)
{
DebugLog((DEB_ERROR,"SspCopyClientString allocation failure!\n");
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
TempString.Length = (USHORT) (SourceSize - CharacterSize);
TempString.MaximumLength = (USHORT) SourceSize;
Status = LsaFunctions->CopyFromClientBuffer(
NULL,
SourceSize - CharacterSize,
TempString.Buffer,
StringPointer
);
if (!NT_SUCCESS(Status))
{
DebugLog((
DEB_ERROR,
"SspCopyClientString:LsaFn->CopyFromClientBuffer Failed - 0x%x\n",
Status));
goto Cleanup;
}
// We've put info into a STRING structure. Now do
// translation to UNICODE_STRING.
if (UnicodeString)
{
LocalString->Buffer = (LPWSTR) TemporaryString.Buffer;
LocalString->Length = TempString.Length;
LocalString->MaximumLength = TempString.MaximumLength;
}
else
{
NTSTATUS Status1;
Status1 = RtlOemStringToUnicodeString(
LocalString,
&TemporaryString,
TRUE
); // allocate destination
if (!NT_SUCCESS(Status1))
{
Status = STATUS_NO_MEMORY;
DebugLog((
DEB_ERROR,
"SspCopyClientString, Error from RtlOemStringToUnicodeString is 0x%lx\n",
Status
));
goto Cleanup;
}
}
}
}
Cleanup:
if (TempString.Buffer != NULL)
{
//
// Free this if we failed and were doing unicode or if we weren't
// doing unicode
//
if ((UnicodeString && !NT_SUCCESS(Status)) || !UnicodeString)
{
NtLmFree(TemporaryString.Buffer);
}
}
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: SspAllocate
//
// Synopsis: Copies a string from the client and if necessary converts
// from ansi to unicode
//
// Effects: allocates output with either KerbAllocate (unicode)
// or RtlAnsiStringToUnicodeString
//
// Arguments: StringPointer - address of string in client process
// StringLength - Lenght (in characters) of string
// AnsiString - if TRUE, string is ansi
// LocalString - receives allocated string
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
SspCopyAuthorizationData(IN PVOID AuthorizationData,
IN OUT PBOOLEAN NullSession)
{
PSEC_WINNT_AUTH_IDENTITY_EXW pAuthIdentityEx = NULL;
BOOLEAN UnicodeString = TRUE;
// Init out parameters
*NullSession = FALSE;
if (NULL == AuthorizationData)
{
return STATUS_SUCCESS;
}
pAuthIdentity = (PSEC_WINNT_AUTH_IDENTITY_EXW)
SspAllocate(sizeof(SEC_WINNT_AUTH_IDENTITY_EXW));
if (NULL != pAuthIdentity)
{
Status = LsaFunctions->CopyFromClientBuffer(
NULL,
sizeof(SEC_WINNT_AUTH_IDENTITY),
pAuthIdentityEx,
AuthorizationData
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Fail: LsaFunctions->CopyFromClientBuffer is 0x%lx\n", Status));
goto Cleanup;
}
} else {
Status = STATUS_NO_MEMORY;
DebugLog((DEB_ERROR, "Fail: Alloc in SspCopyAuthData\n");
goto Cleanup;
}
//
// Do we have an EX version?
//
if (pAuthIdentityEx->Version == SEC_WINNT_AUTH_IDENTITY_VERSION)
{
Status = LsaFunctions->CopyFromClientBuffer(
NULL,
sizeof(SEC_WINNT_AUTH_IDENTITY_EXW),
pAuthIdentityEx,
AuthorizationData
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Fail: Error from LsaFunctions->CopyFromClientBuffer 0x%lx\n", Status));
goto Cleanup;
}
pAuthIdentity = (PSEC_WINNT_AUTH_IDENTITY) &pAuthIdentityEx->User;
CredSize = pAuthIdentityEx->Length;
Offset = FIELD_OFFSET(SEC_WINNT_AUTH_IDENTITY_EXW, User);
}
else
{
pAuthIdentity = (PSEC_WINNT_AUTH_IDENTITY_W) pAuthIdentityEx;
CredSize = sizeof(SEC_WINNT_AUTH_IDENTITY_W);
}
if ((pAuthIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI) != 0)
{
DoUnicode = FALSE;
//
// Turn off the marshalled flag because we don't support marshalling
// with ansi.
//
pAuthIdentity->Flags &= ~SEC_WINNT_AUTH_IDENTITY_MARSHALLED;
}
else if ((pAuthIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) == 0)
{
Status = SEC_E_INVALID_TOKEN;
SspPrint((SSP_CRITICAL,"SpAcquireCredentialsHandle, Error from pAuthIdentity->Flags is 0x%lx\n", pAuthIdentity->Flags));
goto Cleanup;
}
//
// For NTLM, we've got to verify that this is indeed a NULL session
//
if ((pAuthIdentity->UserLength == 0) &&
(pAuthIdentity->DomainLength == 0) &&
(pAuthIdentity->PasswordLength == 0) &&
(pAuthIdentity->User != NULL) &&
(pAuthIdentity->Domain != NULL) &&
(pAuthIdentity->Password != NULL))
{
*NullSession = TRUE;
}
//
// Copy over marshalled data
//
if((pAuthIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_MARSHALLED) != 0 )
{
ULONG TmpCredentialSize;
ULONG_PTR EndOfCreds;
ULONG_PTR TmpUser;
ULONG_PTR TmpDomain;
ULONG_PTR TmpPassword;
if( pAuthIdentity->UserLength > UNLEN ||
pAuthIdentity->PasswordLength > PWLEN ||
pAuthIdentity->DomainLength > DNS_MAX_NAME_LENGTH ) {
SspPrint((SSP_CRITICAL,"Supplied credentials illegal length.\n"));
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
//
// The callers can set the length of field to n chars, but they
// will really occupy n+1 chars (null-terminator).
//
TmpCredentialSize = CredSize +
( pAuthIdentity->UserLength +
pAuthIdentity->DomainLength +
pAuthIdentity->PasswordLength +
(((pAuthIdentity->User != NULL) ? 1 : 0) +
((pAuthIdentity->Domain != NULL) ? 1 : 0) +
((pAuthIdentity->Password != NULL) ? 1 : 0)) ) * sizeof(WCHAR);
EndOfCreds = (ULONG_PTR) AuthorizationData + TmpCredentialSize;
//
// Verify that all the offsets are valid and no overflow will happen
//
TmpUser = (ULONG_PTR) pAuthIdentity->User;
if ((TmpUser != NULL) &&
( (TmpUser < (ULONG_PTR) AuthorizationData) ||
(TmpUser > EndOfCreds) ||
((TmpUser + (pAuthIdentity->UserLength) * sizeof(WCHAR)) > EndOfCreds ) ||
((TmpUser + (pAuthIdentity->UserLength * sizeof(WCHAR))) < TmpUser)))
{
SspPrint((SSP_CRITICAL,"Username in supplied credentials has invalid pointer or length.\n"));
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
TmpDomain = (ULONG_PTR) pAuthIdentity->Domain;
if ((TmpDomain != NULL) &&
( (TmpDomain < (ULONG_PTR) AuthorizationData) ||
(TmpDomain > EndOfCreds) ||
((TmpDomain + (pAuthIdentity->DomainLength) * sizeof(WCHAR)) > EndOfCreds ) ||
((TmpDomain + (pAuthIdentity->DomainLength * sizeof(WCHAR))) < TmpDomain)))
{
SspPrint((SSP_CRITICAL,"Domainname in supplied credentials has invalid pointer or length.\n"));
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
TmpPassword = (ULONG_PTR) pAuthIdentity->Password;
if ((TmpPassword != NULL) &&
( (TmpPassword < (ULONG_PTR) AuthorizationData) ||
(TmpPassword > EndOfCreds) ||
((TmpPassword + (pAuthIdentity->PasswordLength) * sizeof(WCHAR)) > EndOfCreds ) ||
((TmpPassword + (pAuthIdentity->PasswordLength * sizeof(WCHAR))) < TmpPassword)))
{
SspPrint((SSP_CRITICAL,"Password in supplied credentials has invalid pointer or length.\n"));
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
//
// Allocate a chunk of memory for the credentials
//
TmpCredentials = (PSEC_WINNT_AUTH_IDENTITY_W) NtLmAllocate(TmpCredentialSize - Offset);
if (TmpCredentials == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
//
// Copy the credentials from the client
//
Status = LsaFunctions->CopyFromClientBuffer(
NULL,
TmpCredentialSize - Offset,
TmpCredentials,
(PUCHAR) AuthorizationData + Offset
);
if (!NT_SUCCESS(Status))
{
SspPrint((SSP_CRITICAL,"Failed to copy whole auth identity\n"));
goto Cleanup;
}
//
// Now convert all the offsets to pointers.
//
if (TmpCredentials->User != NULL)
{
USHORT cbUser;
TmpCredentials->User = (LPWSTR) RtlOffsetToPointer(
TmpCredentials->User,
(PUCHAR) TmpCredentials - (PUCHAR) AuthorizationData - Offset
);
ASSERT( (TmpCredentials->UserLength*sizeof(WCHAR)) <= 0xFFFF );
cbUser = (USHORT)(TmpCredentials->UserLength * sizeof(WCHAR));
UserName.Buffer = (PWSTR)NtLmAllocate( cbUser );
if (UserName.Buffer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
CopyMemory( UserName.Buffer, TmpCredentials->User, cbUser );
UserName.Length = cbUser;
UserName.MaximumLength = cbUser;
}
if (TmpCredentials->Domain != NULL)
{
USHORT cbDomain;
TmpCredentials->Domain = (LPWSTR) RtlOffsetToPointer(
TmpCredentials->Domain,
(PUCHAR) TmpCredentials - (PUCHAR) AuthorizationData - Offset
);
ASSERT( (TmpCredentials->DomainLength*sizeof(WCHAR)) <= 0xFFFF );
cbDomain = (USHORT)(TmpCredentials->DomainLength * sizeof(WCHAR));
DomainName.Buffer = (PWSTR)NtLmAllocate( cbDomain );
if (DomainName.Buffer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
CopyMemory( DomainName.Buffer, TmpCredentials->Domain, cbDomain );
DomainName.Length = cbDomain;
DomainName.MaximumLength = cbDomain;
}
if (TmpCredentials->Password != NULL)
{
USHORT cbPassword;
TmpCredentials->Password = (LPWSTR) RtlOffsetToPointer(
TmpCredentials->Password,
(PUCHAR) TmpCredentials - (PUCHAR) AuthorizationData - Offset
);
ASSERT( (TmpCredentials->PasswordLength*sizeof(WCHAR)) <= 0xFFFF );
cbPassword = (USHORT)(TmpCredentials->PasswordLength * sizeof(WCHAR));
Password.Buffer = (PWSTR)NtLmAllocate( cbPassword );
if (Password.Buffer == NULL ) {
ZeroMemory( TmpCredentials->Password, cbPassword );
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
CopyMemory( Password.Buffer, TmpCredentials->Password, cbPassword );
Password.Length = cbPassword;
Password.MaximumLength = cbPassword;
ZeroMemory( TmpCredentials->Password, cbPassword );
}
}
//
// Data was *not* marshalled, copy strings individually
//
else
{
if (pAuthIdentity->Password != NULL)
{
Status = SspCopyClientString(
pAuthIdentity->Password,
pAuthIdentity->PasswordLength,
UnicodeString,
&Password
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"SpAcquireCredentialsHandle, Error from CopyClientString is 0x%lx\n", Status));
goto Cleanup;
}
}
if (pAuthIdentity->User != NULL)
{
Status = SspCopyClientString(
pAuthIdentity->User,
pAuthIdentity->UserLength,
UnicodeString,
&UserName
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "SpAcquireCredentialsHandle, Error from CopyClientString is 0x%lx\n", Status));
goto Cleanup;
}
}
if (pAuthIdentity->Domain != NULL)
{
Status = SspCopyClientString(
pAuthIdentity->Domain,
pAuthIdentity->DomainLength,
UnicodeString,
&DomainName
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "SpAcquireCredentialsHandle, Error from CopyClientString is 0x%lx\n", Status));
goto Cleanup;
}
//
// Make sure that the domain name length is not greater
// than the allowed dns domain name
//
if (DomainName.Length > DNS_MAX_NAME_LENGTH * sizeof(WCHAR))
{
DebugLog((DEB_ERROR, "SpAcquireCredentialsHandle: Invalid supplied domain name %wZ\n",
&DomainName ));
Status = SEC_E_UNKNOWN_CREDENTIALS;
goto Cleanup;
}
}
}
}
}