1239 lines
32 KiB
C
1239 lines
32 KiB
C
//+----------------------------------------------------------------------------
|
|
//
|
|
// Copyright (C) 1996, Microsoft Corporation
|
|
//
|
|
// File: creds.c
|
|
//
|
|
// Contents: Code to handle user-defined credentials
|
|
//
|
|
// Classes: None
|
|
//
|
|
// Functions: DfsCreateCredentials --
|
|
// DfsFreeCredentials --
|
|
// DfsInsertCredentials --
|
|
// DfsDeleteCredentials --
|
|
// DfsLookupCredentials --
|
|
//
|
|
// History: March 18, 1996 Milans Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "dfsprocs.h"
|
|
|
|
#include <align.h>
|
|
#include <ntddnfs.h>
|
|
|
|
#include "dnr.h"
|
|
#include "rpselect.h"
|
|
#include "creds.h"
|
|
|
|
VOID
|
|
DfspFillEa(
|
|
OUT PFILE_FULL_EA_INFORMATION EA,
|
|
IN LPSTR EaName,
|
|
IN PUNICODE_STRING EaValue);
|
|
|
|
NTSTATUS
|
|
DfspTreeConnectToService(
|
|
IN PDFS_SERVICE Service,
|
|
IN PDFS_CREDENTIALS Creds);
|
|
|
|
VOID
|
|
DfspDeleteAllAuthenticatedConnections(
|
|
IN PDFS_CREDENTIALS Creds);
|
|
|
|
NTSTATUS
|
|
DfsCompleteDeleteTreeConnection(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Ctx);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(PAGE,DfsCreateCredentials)
|
|
#pragma alloc_text(PAGE,DfsVerifyCredentials)
|
|
#pragma alloc_text(PAGE,DfspFillEa)
|
|
#pragma alloc_text(PAGE,DfsFreeCredentials)
|
|
#pragma alloc_text(PAGE,DfsInsertCredentials)
|
|
#pragma alloc_text(PAGE,DfsDeleteCredentials)
|
|
#pragma alloc_text(PAGE,DfsLookupCredentials)
|
|
#pragma alloc_text(PAGE,DfsLookupCredentialsByServerShare)
|
|
#pragma alloc_text(PAGE,DfspTreeConnectToService)
|
|
#pragma alloc_text(PAGE,DfspDeleteAllAuthenticatedConnections)
|
|
#pragma alloc_text(PAGE,DfsDeleteTreeConnection)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsCreateCredentials
|
|
//
|
|
// Synopsis: Creates a DFS_CREDENTIALS structure from a
|
|
// FILE_DFS_DEF_ROOT_CREDENTIALS structure.
|
|
//
|
|
// Arguments: [CredDef] -- The input PFILE_DFS_DEF_ROOT_CREDENTIALS.
|
|
// [CredDefSize] -- Size in bytes of *CredDef.
|
|
// [Creds] -- On successful return, contains a pointer to the
|
|
// allocated PDFS_CREDENTIALS structure.
|
|
//
|
|
// Returns: [STATUS_SUCCESS] -- Allocated credentials
|
|
//
|
|
// [STATUS_INVALID_PARAMETER] -- CredDef didn't pass mustard.
|
|
//
|
|
// [STATUS_INSUFFICIENT_RESOURCES] -- Unable to allocate pool.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#define DEF_NAME_TO_UNICODE_STRING(srcLength, dest, srcBuf, destBuf) \
|
|
if ((srcLength)) { \
|
|
(dest)->Length = (dest)->MaximumLength = srcLength; \
|
|
(dest)->Buffer = (destBuf); \
|
|
RtlMoveMemory((dest)->Buffer, (srcBuf), (dest)->Length); \
|
|
srcBuf += ((dest)->Length / sizeof(WCHAR)); \
|
|
destBuf += ((dest)->Length / sizeof(WCHAR)); \
|
|
}
|
|
|
|
#ifdef TERMSRV
|
|
|
|
NTSTATUS
|
|
DfsCreateCredentials(
|
|
IN PFILE_DFS_DEF_ROOT_CREDENTIALS CredDef,
|
|
IN ULONG CredDefSize,
|
|
IN ULONG SessionID,
|
|
IN PLUID LogonID,
|
|
OUT PDFS_CREDENTIALS *Creds
|
|
)
|
|
|
|
#else // TERMSRV
|
|
|
|
NTSTATUS
|
|
DfsCreateCredentials(
|
|
IN PFILE_DFS_DEF_ROOT_CREDENTIALS CredDef,
|
|
IN ULONG CredDefSize,
|
|
IN PLUID LogonID,
|
|
OUT PDFS_CREDENTIALS *Creds)
|
|
|
|
#endif // TERMSRV
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG totalSize;
|
|
PDFS_CREDENTIALS creds;
|
|
PWCHAR nameSrc, nameBuf;
|
|
PFILE_FULL_EA_INFORMATION ea;
|
|
ULONG eaLength;
|
|
|
|
totalSize = CredDef->DomainNameLen +
|
|
CredDef->UserNameLen +
|
|
CredDef->PasswordLen +
|
|
CredDef->ServerNameLen +
|
|
CredDef->ShareNameLen;
|
|
|
|
//
|
|
// Validate the CredDef buffer
|
|
//
|
|
|
|
if ((totalSize + sizeof(FILE_DFS_DEF_ROOT_CREDENTIALS) - sizeof(WCHAR)) >
|
|
CredDefSize)
|
|
status = STATUS_INVALID_PARAMETER;
|
|
else if (CredDef->ServerNameLen == 0)
|
|
status = STATUS_INVALID_PARAMETER;
|
|
else if (CredDef->ShareNameLen == 0)
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// Allocate the new DFS_CREDENTIALS structure
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Add in the size of the DFS_CREDENTIALS_STRUCTURE itself.
|
|
//
|
|
|
|
totalSize += sizeof(DFS_CREDENTIALS);
|
|
|
|
//
|
|
// Add in the size of the EA_BUFFER that we will create. The
|
|
// eaLength has room for 4 FILE_FULL_EA_INFORMATION structures,
|
|
// the names and values of the four EAs we will use, and, since each
|
|
// EA structure has to be long-word aligned, 4 ULONGs.
|
|
//
|
|
|
|
eaLength = 4 * sizeof(FILE_FULL_EA_INFORMATION) +
|
|
sizeof(EA_NAME_DOMAIN) +
|
|
sizeof(EA_NAME_USERNAME) +
|
|
sizeof(EA_NAME_PASSWORD) +
|
|
sizeof(EA_NAME_CSCAGENT) +
|
|
CredDef->DomainNameLen +
|
|
CredDef->UserNameLen +
|
|
CredDef->PasswordLen +
|
|
4 * sizeof(ULONG);
|
|
|
|
if (CredDef->Flags & DFS_USE_NULL_PASSWORD) {
|
|
eaLength += sizeof(UNICODE_NULL);
|
|
totalSize += sizeof(UNICODE_NULL);
|
|
}
|
|
|
|
//
|
|
// The buffers for DomainName, UserName etc. will start right after
|
|
// the EaBuffer of DFS_CREDENTIALS. So, EaLength has to be WCHAR
|
|
// aligned.
|
|
//
|
|
|
|
eaLength = ROUND_UP_COUNT(eaLength, ALIGN_WCHAR);
|
|
|
|
//
|
|
// Now, allocate the pool
|
|
//
|
|
|
|
creds = (PDFS_CREDENTIALS) ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
totalSize + eaLength,
|
|
' puM');
|
|
|
|
if (creds == NULL)
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
//
|
|
// Fill up the DFS_CREDENTIALS structure.
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
nameSrc = CredDef->Buffer;
|
|
|
|
nameBuf =
|
|
(PWCHAR) ((PUCHAR) creds + sizeof(DFS_CREDENTIALS) + eaLength);
|
|
|
|
RtlZeroMemory( creds, sizeof(DFS_CREDENTIALS) );
|
|
|
|
DEF_NAME_TO_UNICODE_STRING(
|
|
CredDef->DomainNameLen,
|
|
&creds->DomainName,
|
|
nameSrc,
|
|
nameBuf);
|
|
|
|
DEF_NAME_TO_UNICODE_STRING(
|
|
CredDef->UserNameLen,
|
|
&creds->UserName,
|
|
nameSrc,
|
|
nameBuf);
|
|
|
|
if (CredDef->Flags & DFS_USE_NULL_PASSWORD) {
|
|
|
|
LPWSTR nullPassword = L"";
|
|
|
|
DEF_NAME_TO_UNICODE_STRING(
|
|
sizeof(UNICODE_NULL),
|
|
&creds->Password,
|
|
nullPassword,
|
|
nameBuf);
|
|
|
|
} else {
|
|
|
|
DEF_NAME_TO_UNICODE_STRING(
|
|
CredDef->PasswordLen,
|
|
&creds->Password,
|
|
nameSrc,
|
|
nameBuf);
|
|
|
|
}
|
|
|
|
DEF_NAME_TO_UNICODE_STRING(
|
|
CredDef->ServerNameLen,
|
|
&creds->ServerName,
|
|
nameSrc,
|
|
nameBuf);
|
|
|
|
DEF_NAME_TO_UNICODE_STRING(
|
|
CredDef->ShareNameLen,
|
|
&creds->ShareName,
|
|
nameSrc,
|
|
nameBuf);
|
|
|
|
creds->RefCount = 0;
|
|
|
|
creds->NetUseCount = 0;
|
|
|
|
eaLength = 0;
|
|
|
|
ea = (PFILE_FULL_EA_INFORMATION) &creds->EaBuffer[0];
|
|
|
|
if (creds->DomainName.Length != 0) {
|
|
|
|
DfspFillEa(ea, EA_NAME_DOMAIN, &creds->DomainName);
|
|
|
|
eaLength += ea->NextEntryOffset;
|
|
|
|
}
|
|
|
|
if (creds->UserName.Length != 0) {
|
|
|
|
ea = (PFILE_FULL_EA_INFORMATION)
|
|
((PUCHAR) ea + ea->NextEntryOffset);
|
|
|
|
DfspFillEa(ea, EA_NAME_USERNAME, &creds->UserName);
|
|
|
|
eaLength += ea->NextEntryOffset;
|
|
|
|
}
|
|
|
|
if (CredDef->Flags & DFS_USE_NULL_PASSWORD) {
|
|
|
|
UNICODE_STRING nullPassword;
|
|
|
|
RtlInitUnicodeString(&nullPassword, L"");
|
|
|
|
ea = (PFILE_FULL_EA_INFORMATION)
|
|
((PUCHAR) ea + ea->NextEntryOffset);
|
|
|
|
DfspFillEa(ea, EA_NAME_PASSWORD, &nullPassword);
|
|
|
|
eaLength += ea->NextEntryOffset;
|
|
|
|
} else if (creds->Password.Length != 0) {
|
|
|
|
ea = (PFILE_FULL_EA_INFORMATION)
|
|
((PUCHAR) ea + ea->NextEntryOffset);
|
|
|
|
DfspFillEa(ea, EA_NAME_PASSWORD, &creds->Password);
|
|
|
|
eaLength += ea->NextEntryOffset;
|
|
|
|
}
|
|
|
|
if (CredDef->CSCAgentCreate == TRUE) {
|
|
|
|
UNICODE_STRING EmptyUniString;
|
|
|
|
RtlInitUnicodeString(&EmptyUniString, NULL);
|
|
|
|
ea = (PFILE_FULL_EA_INFORMATION)
|
|
((PUCHAR) ea + ea->NextEntryOffset);
|
|
|
|
DfspFillEa(ea, EA_NAME_CSCAGENT, &EmptyUniString);
|
|
|
|
eaLength += ea->NextEntryOffset;
|
|
|
|
}
|
|
|
|
ea->NextEntryOffset = 0;
|
|
|
|
creds->EaLength = eaLength;
|
|
|
|
#ifdef TERMSRV
|
|
creds->SessionID = SessionID;
|
|
#endif // TERMSRV
|
|
|
|
RtlCopyLuid(&creds->LogonID, LogonID);
|
|
|
|
*Creds = creds;
|
|
|
|
}
|
|
|
|
//
|
|
// Done...
|
|
//
|
|
|
|
return( status );
|
|
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfspFillEa
|
|
//
|
|
// Synopsis: Helper routine to fill up an EA Buffer
|
|
//
|
|
// Arguments: [EA] -- Pointer to FILE_FULL_EA_INFORMATION to fill
|
|
//
|
|
// [EaName] -- Name of Ea
|
|
//
|
|
// [EaValue] -- Value (UNICODE_STRING) of Ea
|
|
//
|
|
// Returns:
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
DfspFillEa(
|
|
OUT PFILE_FULL_EA_INFORMATION EA,
|
|
IN LPSTR EaName,
|
|
IN PUNICODE_STRING EaValue)
|
|
{
|
|
ULONG nameLen;
|
|
|
|
nameLen = strlen(EaName) + sizeof(CHAR);
|
|
|
|
EA->Flags = 0;
|
|
|
|
EA->EaNameLength =
|
|
(UCHAR) ROUND_UP_COUNT(nameLen, ALIGN_WCHAR) - sizeof(CHAR);
|
|
|
|
EA->EaValueLength = EaValue->Length;
|
|
|
|
//
|
|
// Set the last character of EaName to 0 - the IO subsystem checks for
|
|
// this
|
|
//
|
|
|
|
EA->EaName[ EA->EaNameLength ] = 0;
|
|
|
|
RtlMoveMemory(&EA->EaName[0], EaName, nameLen);
|
|
|
|
if (EaValue->Length > 0) {
|
|
|
|
RtlMoveMemory(
|
|
&EA->EaName[ EA->EaNameLength + 1 ],
|
|
EaValue->Buffer,
|
|
EA->EaValueLength);
|
|
|
|
}
|
|
|
|
EA->NextEntryOffset = ROUND_UP_COUNT(
|
|
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
|
|
EA->EaNameLength +
|
|
EA->EaValueLength,
|
|
ALIGN_DWORD);
|
|
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsFreeCredentials
|
|
//
|
|
// Synopsis: Frees up the resources used by the DFS_CREDENTIALS structure.
|
|
// Dual of DfsCreateCredentials
|
|
//
|
|
// Arguments: [Creds] -- The credentials structure to free
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
DfsFreeCredentials(
|
|
PDFS_CREDENTIALS Creds)
|
|
{
|
|
ExFreePool( Creds );
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsInsertCredentials
|
|
//
|
|
// Synopsis: Inserts a new user credential into DfsData.Credentials queue.
|
|
// Note that if this routine finds an existing credential
|
|
// record, it will free up the passed in one, bump up the ref
|
|
// count on the existing one, return a pointer to the
|
|
// existing one, and return STATUS_OBJECT_NAME_COLLISION.
|
|
//
|
|
// Arguments: [Creds] -- Pointer to DFS_CREDENTIALS structure to insert.
|
|
// [ForDevicelessConnection] -- If TRUE, the creds are being
|
|
// inserted because the caller wants to create a
|
|
// deviceless connection.
|
|
//
|
|
// Returns: [STATUS_SUCCESS] -- Successfully inserted structure
|
|
//
|
|
// [STATUS_NETWORK_CREDENTIAL_CONFLICT] -- There is already
|
|
// another set of credentials for the given server\share.
|
|
//
|
|
// [STATUS_OBJECT_NAME_COLLISION] -- There is already another
|
|
// net use to the same server\share with the same
|
|
// credentials.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
DfsInsertCredentials(
|
|
IN OUT PDFS_CREDENTIALS *Creds,
|
|
IN BOOLEAN ForDevicelessConnection)
|
|
{
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDFS_CREDENTIALS creds, existingCreds;
|
|
|
|
creds = *Creds;
|
|
|
|
ASSERT(creds->ServerName.Length != 0);
|
|
ASSERT(creds->ShareName.Length != 0);
|
|
|
|
ExAcquireResourceExclusiveLite( &DfsData.Resource, TRUE );
|
|
|
|
#ifdef TERMSRV
|
|
|
|
existingCreds =
|
|
DfsLookupCredentialsByServerShare(
|
|
&creds->ServerName,
|
|
&creds->ShareName,
|
|
creds->SessionID,
|
|
&creds->LogonID );
|
|
|
|
#else // TERMSRV
|
|
|
|
existingCreds = DfsLookupCredentialsByServerShare(
|
|
&creds->ServerName,
|
|
&creds->ShareName,
|
|
&creds->LogonID );
|
|
|
|
#endif // TERMSRV
|
|
|
|
if (existingCreds != NULL) {
|
|
|
|
if (
|
|
(creds->DomainName.Length > 0 && !RtlEqualUnicodeString(
|
|
&existingCreds->DomainName,
|
|
&creds->DomainName,
|
|
TRUE))
|
|
||
|
|
(creds->UserName.Length > 0 && !RtlEqualUnicodeString(
|
|
&existingCreds->UserName,
|
|
&creds->UserName,
|
|
TRUE))
|
|
||
|
|
//
|
|
// For compatibility reasons, check for password inconsistency ONLY
|
|
// if we have a previously setup credentials and the previous
|
|
// credentials had explicit password and the current request
|
|
// has explicitly specified password.
|
|
// rdr2\rdbss\rxconnct.c also has a similar check for the rdr.
|
|
//
|
|
|
|
(existingCreds->Password.Length > 0 && creds->Password.Length > 0 && !RtlEqualUnicodeString(
|
|
&existingCreds->Password,
|
|
&creds->Password,
|
|
TRUE))
|
|
) {
|
|
|
|
status = STATUS_NETWORK_CREDENTIAL_CONFLICT;
|
|
|
|
} else {
|
|
//
|
|
// Do this for both deviceless and has device cases.
|
|
// With deep net uses of deviceless, multiple DevlessRoots
|
|
// may point to the same credentials.
|
|
//
|
|
existingCreds->NetUseCount++;
|
|
existingCreds->RefCount++;
|
|
|
|
DfsFreeCredentials( *Creds );
|
|
|
|
*Creds = existingCreds;
|
|
|
|
status = STATUS_OBJECT_NAME_COLLISION;
|
|
}
|
|
|
|
} else {
|
|
|
|
ASSERT(creds->RefCount == 0);
|
|
|
|
ASSERT(creds->NetUseCount == 0);
|
|
|
|
creds->RefCount = 1;
|
|
|
|
creds->NetUseCount = 1;
|
|
|
|
InsertTailList( &DfsData.Credentials, &creds->Link );
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
if (status != STATUS_NETWORK_CREDENTIAL_CONFLICT) {
|
|
|
|
if (ForDevicelessConnection)
|
|
(*Creds)->Flags |= CRED_IS_DEVICELESS;
|
|
else
|
|
(*Creds)->Flags |= CRED_HAS_DEVICE;
|
|
|
|
}
|
|
|
|
ExReleaseResourceLite( &DfsData.Resource );
|
|
|
|
return( status );
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsDeleteCredentials
|
|
//
|
|
// Synopsis: Deletes a user credential record. This is the dual of
|
|
// DfsInsertCredentials, NOT DfsCreateCredentials.
|
|
//
|
|
// Arguments: [Creds] -- Pointer to DFS_CREDENTIALS record to delete.
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
DfsDeleteCredentials(
|
|
IN PDFS_CREDENTIALS Creds)
|
|
{
|
|
ExAcquireResourceExclusiveLite( &DfsData.Resource, TRUE );
|
|
|
|
Creds->NetUseCount--;
|
|
|
|
Creds->RefCount--;
|
|
|
|
if (Creds->NetUseCount == 0) {
|
|
|
|
DfspDeleteAllAuthenticatedConnections( Creds );
|
|
|
|
RemoveEntryList( &Creds->Link );
|
|
|
|
InsertTailList( &DfsData.DeletedCredentials, &Creds->Link );
|
|
|
|
}
|
|
|
|
ExReleaseResourceLite( &DfsData.Resource );
|
|
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsLookupCredentials
|
|
//
|
|
// Synopsis: Looks up a credential, if any, associated with a file name.
|
|
//
|
|
// Arguments: [FileName] -- Name of file. Assumed to have atleast a
|
|
// \server\share part.
|
|
//
|
|
// Returns: Pointer to DFS_CREDENTIALS to use, NULL if not found.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
#ifdef TERMSRV
|
|
|
|
PDFS_CREDENTIALS
|
|
DfsLookupCredentials(
|
|
IN PUNICODE_STRING FileName,
|
|
IN ULONG SessionID,
|
|
IN PLUID LogonID
|
|
)
|
|
|
|
#else // TERMSRV
|
|
|
|
PDFS_CREDENTIALS
|
|
DfsLookupCredentials(
|
|
IN PUNICODE_STRING FileName,
|
|
IN PLUID LogonID
|
|
)
|
|
|
|
#endif // TERMSRV
|
|
{
|
|
UNICODE_STRING server, share;
|
|
USHORT i;
|
|
|
|
//
|
|
// FileName has to be atleast \a\b
|
|
//
|
|
|
|
if (FileName->Length < 4 * sizeof(WCHAR))
|
|
return( NULL );
|
|
|
|
if (FileName->Buffer[0] != UNICODE_PATH_SEP)
|
|
return( NULL );
|
|
|
|
server.Buffer = &FileName->Buffer[1];
|
|
|
|
for (i = 1, server.Length = 0;
|
|
i < FileName->Length/sizeof(WCHAR) &&
|
|
FileName->Buffer[i] != UNICODE_PATH_SEP;
|
|
i++, server.Length += sizeof(WCHAR)) {
|
|
NOTHING;
|
|
}
|
|
|
|
server.MaximumLength = server.Length;
|
|
|
|
i++; // Go past the backslash
|
|
|
|
share.Buffer = &FileName->Buffer[i];
|
|
|
|
for (share.Length = 0;
|
|
i < FileName->Length/sizeof(WCHAR) &&
|
|
FileName->Buffer[i] != UNICODE_PATH_SEP;
|
|
i++, share.Length += sizeof(WCHAR)) {
|
|
NOTHING;
|
|
}
|
|
|
|
share.MaximumLength = share.Length;
|
|
|
|
if ((server.Length == 0) || (share.Length == 0))
|
|
return( NULL );
|
|
|
|
|
|
#ifdef TERMSRV
|
|
|
|
return DfsLookupCredentialsByServerShare( &server, &share, SessionID, LogonID );
|
|
|
|
#else // TERMSRV
|
|
|
|
return DfsLookupCredentialsByServerShare( &server, &share, LogonID );
|
|
|
|
#endif // TERMSRV
|
|
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsLookupCredentialsByServerShare
|
|
//
|
|
// Synopsis: Searches DfsData.Credentials for credentials given a server
|
|
// and share name.
|
|
//
|
|
// Arguments: [ServerName] -- Name of server to match.
|
|
// [ShareName] -- Name of share to match.
|
|
//
|
|
// Returns: Pointer to DFS_CREDENTIALS, NULL if not found.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifdef TERMSRV
|
|
|
|
PDFS_CREDENTIALS
|
|
DfsLookupCredentialsByServerShare(
|
|
IN PUNICODE_STRING ServerName,
|
|
IN PUNICODE_STRING ShareName,
|
|
IN ULONG SessionID,
|
|
IN PLUID LogonID
|
|
)
|
|
|
|
#else // TERMSRV
|
|
|
|
PDFS_CREDENTIALS
|
|
DfsLookupCredentialsByServerShare(
|
|
IN PUNICODE_STRING ServerName,
|
|
IN PUNICODE_STRING ShareName,
|
|
IN PLUID LogonID
|
|
)
|
|
|
|
#endif // TERMSRV
|
|
{
|
|
PLIST_ENTRY link;
|
|
PDFS_CREDENTIALS matchedCreds = NULL;
|
|
|
|
for (link = DfsData.Credentials.Flink;
|
|
link != &DfsData.Credentials && matchedCreds == NULL;
|
|
link = link->Flink) {
|
|
|
|
PDFS_CREDENTIALS creds;
|
|
|
|
creds = CONTAINING_RECORD(link, DFS_CREDENTIALS, Link);
|
|
|
|
if (RtlEqualUnicodeString(ServerName, &creds->ServerName, TRUE) &&
|
|
RtlEqualUnicodeString(ShareName, &creds->ShareName, TRUE)) {
|
|
#ifdef TERMSRV
|
|
|
|
if( (creds->SessionID == SessionID) &&
|
|
RtlEqualLuid(&creds->LogonID, LogonID) ) {
|
|
matchedCreds = creds;
|
|
}
|
|
|
|
#else // TERMSRV
|
|
if( RtlEqualLuid(&creds->LogonID, LogonID) ) {
|
|
|
|
matchedCreds = creds;
|
|
}
|
|
|
|
#endif // TERMSRV
|
|
}
|
|
|
|
|
|
}
|
|
|
|
return( matchedCreds );
|
|
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsVerifyCredentials
|
|
//
|
|
// Synopsis: Returns the result of trying to connect to a Dfs share using
|
|
// the supplied credentials
|
|
//
|
|
// Arguments: [Prefix] -- The Dfs Prefix to connect to.
|
|
// [Creds] -- The DFS_CREDENTIALS record to use for connecting.
|
|
//
|
|
// Returns: [STATUS_SUCCESS] -- Successfully connected.
|
|
//
|
|
// [STATUS_BAD_NETWORK_PATH] -- Unable to find Prefix
|
|
// in Pkt or a server for prefix could not be found.
|
|
//
|
|
// NT Status of Tree Connect attempt
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
DfsVerifyCredentials(
|
|
IN PUNICODE_STRING Prefix,
|
|
IN PDFS_CREDENTIALS Creds)
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING remPath, shareName;
|
|
PDFS_PKT pkt;
|
|
PDFS_PKT_ENTRY pktEntry;
|
|
PDFS_SERVICE service;
|
|
ULONG i, USN;
|
|
BOOLEAN pktLocked, fRetry;
|
|
UNICODE_STRING UsePrefix;
|
|
|
|
DfsGetServerShare( &UsePrefix,
|
|
Prefix );
|
|
|
|
pkt = _GetPkt();
|
|
|
|
|
|
//
|
|
// We acquire Pkt exclusive because we might tear down the IPC$ connection
|
|
// to a server while trying to establish a connection with supplied
|
|
// credentials.
|
|
//
|
|
|
|
PktAcquireExclusive( TRUE, &pktLocked );
|
|
|
|
do {
|
|
|
|
fRetry = FALSE;
|
|
|
|
pktEntry = PktLookupEntryByPrefix( pkt, &UsePrefix, &remPath );
|
|
|
|
|
|
if (pktEntry != NULL) {
|
|
|
|
InterlockedIncrement(&pktEntry->UseCount);
|
|
|
|
USN = pktEntry->USN;
|
|
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
|
|
for (i = 0; i < pktEntry->Info.ServiceCount; i++) {
|
|
|
|
service = &pktEntry->Info.ServiceList[i];
|
|
|
|
status = DfspTreeConnectToService(service, Creds);
|
|
|
|
//
|
|
// If tree connect succeeded, we are done.
|
|
//
|
|
|
|
if (NT_SUCCESS(status))
|
|
break;
|
|
|
|
//
|
|
// If tree connect failed with an "interesting error" like
|
|
// STATUS_ACCESS_DENIED, we are done.
|
|
//
|
|
|
|
if (!ReplIsRecoverableError(status))
|
|
break;
|
|
|
|
//
|
|
// Tree connect failed because of an error like host not
|
|
// reachable. In that case, we want to go on to the next
|
|
// server in the list. But before we do that, we have to see
|
|
// if the pkt changed on us while we were off doing the tree
|
|
// connect.
|
|
//
|
|
|
|
if (USN != pktEntry->USN) {
|
|
|
|
fRetry = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
InterlockedDecrement(&pktEntry->UseCount);
|
|
|
|
} else {
|
|
|
|
status = STATUS_BAD_NETWORK_PATH;
|
|
|
|
}
|
|
|
|
} while ( fRetry );
|
|
|
|
PktRelease();
|
|
|
|
return( status );
|
|
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfspTreeConnectToService
|
|
//
|
|
// Synopsis: Helper routine to tree connect to a DFS_SERVICE with supplied
|
|
// credentials.
|
|
//
|
|
// Arguments: [Service] -- The service to connect to
|
|
// [Creds] -- The credentials to use to tree connect
|
|
//
|
|
// Returns: NT Status of tree connect
|
|
//
|
|
// Notes: This routine assumes that the Pkt has been acquired before
|
|
// being called. This routine will release and reacquire the Pkt
|
|
// so the caller should be prepared for the event that the Pkt
|
|
// has changed after a call to this routine.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
DfspTreeConnectToService(
|
|
IN PDFS_SERVICE Service,
|
|
IN PDFS_CREDENTIALS Creds)
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING shareName;
|
|
HANDLE treeHandle;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
BOOLEAN pktLocked;
|
|
USHORT i, k;
|
|
|
|
ASSERT( PKT_LOCKED_FOR_SHARED_ACCESS() );
|
|
|
|
//
|
|
// Compute the share name...
|
|
//
|
|
|
|
if (Service->pProvider != NULL &&
|
|
Service->pProvider->DeviceName.Buffer != NULL &&
|
|
Service->pProvider->DeviceName.Length > 0) {
|
|
|
|
//
|
|
// We have a provider already - use it
|
|
//
|
|
|
|
shareName.MaximumLength =
|
|
Service->pProvider->DeviceName.Length +
|
|
Service->Address.Length;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We don't have a provider yet - give it to the mup to find one
|
|
//
|
|
|
|
shareName.MaximumLength =
|
|
sizeof(DD_NFS_DEVICE_NAME_U) +
|
|
Service->Address.Length;
|
|
|
|
}
|
|
|
|
shareName.Buffer = ExAllocatePoolWithTag(PagedPool, shareName.MaximumLength, ' puM');
|
|
|
|
if (shareName.Buffer != NULL) {
|
|
|
|
//
|
|
// If we have a cached connection to the IPC$ share of this server,
|
|
// close it or it might conflict with the credentials supplied here.
|
|
//
|
|
|
|
if (Service->ConnFile != NULL) {
|
|
|
|
ExAcquireResourceExclusiveLite(&DfsData.Resource, TRUE);
|
|
|
|
if (Service->ConnFile != NULL)
|
|
DfsCloseConnection(Service);
|
|
|
|
ExReleaseResourceLite(&DfsData.Resource);
|
|
|
|
}
|
|
|
|
//
|
|
// Now, build the share name to tree connect to.
|
|
//
|
|
|
|
shareName.Length = 0;
|
|
|
|
if (Service->pProvider != NULL &&
|
|
Service->pProvider->DeviceName.Buffer != NULL &&
|
|
Service->pProvider->DeviceName.Length > 0) {
|
|
|
|
//
|
|
// We have a provider already - use it
|
|
//
|
|
|
|
RtlAppendUnicodeToString(
|
|
&shareName,
|
|
Service->pProvider->DeviceName.Buffer);
|
|
|
|
} else {
|
|
|
|
//
|
|
// We don't have a provider yet - give it to the mup to find one
|
|
//
|
|
|
|
RtlAppendUnicodeToString(
|
|
&shareName,
|
|
DD_NFS_DEVICE_NAME_U);
|
|
|
|
}
|
|
|
|
RtlAppendUnicodeStringToString(&shareName, &Service->Address);
|
|
|
|
//
|
|
// One can only do tree connects to server\share. So, in case
|
|
// pService->Address refers to something deeper than the share,
|
|
// make sure we setup a tree-conn only to server\share. Note that
|
|
// by now, shareName is of the form
|
|
// \Device\LanmanRedirector\server\share<\path>. So, count up to
|
|
// 4 slashes and terminate the share name there.
|
|
//
|
|
|
|
for (i = 0, k = 0;
|
|
i < shareName.Length/sizeof(WCHAR) && k < 5;
|
|
i++) {
|
|
|
|
if (shareName.Buffer[i] == UNICODE_PATH_SEP)
|
|
k++;
|
|
}
|
|
|
|
shareName.Length = i * sizeof(WCHAR);
|
|
if (k == 5)
|
|
shareName.Length -= sizeof(WCHAR);
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&shareName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
//
|
|
// Release the Pkt before going over the net...
|
|
//
|
|
|
|
PktRelease();
|
|
|
|
status = ZwCreateFile(
|
|
&treeHandle,
|
|
SYNCHRONIZE,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ |
|
|
FILE_SHARE_WRITE |
|
|
FILE_SHARE_DELETE,
|
|
FILE_OPEN_IF,
|
|
FILE_CREATE_TREE_CONNECTION |
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
(PVOID) Creds->EaBuffer,
|
|
Creds->EaLength);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
PFILE_OBJECT fileObject;
|
|
|
|
//
|
|
// 426184, need to check return code for errors.
|
|
//
|
|
status = ObReferenceObjectByHandle(
|
|
treeHandle,
|
|
0,
|
|
NULL,
|
|
KernelMode,
|
|
&fileObject,
|
|
NULL);
|
|
|
|
ZwClose( treeHandle );
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
DfsDeleteTreeConnection( fileObject, USE_FORCE );
|
|
}
|
|
}
|
|
|
|
|
|
ExFreePool( shareName.Buffer );
|
|
|
|
PktAcquireShared( TRUE, &pktLocked );
|
|
|
|
} else {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
return( status );
|
|
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfspDeleteAllAuthenticatedConnections
|
|
//
|
|
// Synopsis: Deletes all authenticated connections made using a particular
|
|
// set of credentials that we might have cached. Useful to
|
|
// implement net use /d
|
|
//
|
|
// Arguments: [Creds] -- The Credentials to match against authenticated
|
|
// connection
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
// Notes: Pkt and DfsData must have been acquired before calling!
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
DfspDeleteAllAuthenticatedConnections(
|
|
IN PDFS_CREDENTIALS Creds)
|
|
{
|
|
PDFS_PKT_ENTRY pktEntry;
|
|
ULONG i;
|
|
PDFS_MACHINE_ENTRY machine;
|
|
|
|
ASSERT( PKT_LOCKED_FOR_SHARED_ACCESS() ||
|
|
PKT_LOCKED_FOR_EXCLUSIVE_ACCESS() );
|
|
|
|
ASSERT( ExIsResourceAcquiredExclusiveLite( &DfsData.Resource ) );
|
|
|
|
for (pktEntry = PktFirstEntry(&DfsData.Pkt);
|
|
pktEntry != NULL;
|
|
pktEntry = PktNextEntry(&DfsData.Pkt, pktEntry)) {
|
|
|
|
for (i = 0; i < pktEntry->Info.ServiceCount; i++) {
|
|
|
|
//
|
|
// Tear down connection to IPC$ if we have one...
|
|
//
|
|
|
|
if (pktEntry->Info.ServiceList[i].ConnFile != NULL)
|
|
DfsCloseConnection( &pktEntry->Info.ServiceList[i] );
|
|
|
|
|
|
machine = pktEntry->Info.ServiceList[i].pMachEntry;
|
|
|
|
if (machine->Credentials == Creds) {
|
|
|
|
DfsDeleteTreeConnection(machine->AuthConn, USE_LOTS_OF_FORCE);
|
|
|
|
machine->AuthConn = NULL;
|
|
|
|
machine->Credentials->RefCount--;
|
|
|
|
machine->Credentials = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsDeleteTreeConnection, public
|
|
//
|
|
// Synopsis: Tears down tree connections given the file object representing
|
|
// the tree connection.
|
|
//
|
|
// Arguments: [TreeConnFileObj] -- The tree connection to tear down.
|
|
// [ForceFilesClosed] -- If TRUE, the tree connection will be
|
|
// torn down even if files are open on the server
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
DfsDeleteTreeConnection(
|
|
IN PFILE_OBJECT TreeConnFileObj,
|
|
IN ULONG Level)
|
|
{
|
|
PIRP irp;
|
|
KEVENT event;
|
|
static LMR_REQUEST_PACKET req;
|
|
|
|
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
|
|
|
|
req.Version = REQUEST_PACKET_VERSION;
|
|
|
|
req.Level = Level;
|
|
|
|
irp = DnrBuildFsControlRequest(
|
|
TreeConnFileObj,
|
|
&event,
|
|
FSCTL_LMR_DELETE_CONNECTION,
|
|
&req,
|
|
sizeof(req),
|
|
NULL,
|
|
0,
|
|
DfsCompleteDeleteTreeConnection);
|
|
|
|
if (irp != NULL) {
|
|
|
|
IoCallDriver(
|
|
IoGetRelatedDeviceObject( TreeConnFileObj ),
|
|
irp);
|
|
|
|
KeWaitForSingleObject(
|
|
&event,
|
|
UserRequest,
|
|
KernelMode,
|
|
FALSE, // Alertable
|
|
NULL); // Timeout
|
|
|
|
IoFreeIrp( irp );
|
|
|
|
ObDereferenceObject(TreeConnFileObj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
DfsCompleteDeleteTreeConnection(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Ctx)
|
|
{
|
|
|
|
KeSetEvent( (PKEVENT) Ctx, EVENT_INCREMENT, FALSE );
|
|
|
|
return( STATUS_MORE_PROCESSING_REQUIRED );
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
DfsGetServerShare(
|
|
PUNICODE_STRING pDest,
|
|
PUNICODE_STRING pSrc)
|
|
{
|
|
ULONG i;
|
|
|
|
*pDest = *pSrc;
|
|
|
|
|
|
for (i = 0; ((i < pDest->Length/sizeof(WCHAR)) && (pDest->Buffer[i] == UNICODE_PATH_SEP)); i++)
|
|
{
|
|
NOTHING;
|
|
}
|
|
for (; ((i < pDest->Length/sizeof(WCHAR)) && (pDest->Buffer[i] != UNICODE_PATH_SEP)); i++)
|
|
{
|
|
NOTHING;
|
|
}
|
|
for (i = i + 1; ((i < pDest->Length/sizeof(WCHAR)) && (pDest->Buffer[i] != UNICODE_PATH_SEP)); i++)
|
|
{
|
|
NOTHING;
|
|
}
|
|
|
|
if (i <= pDest->Length/sizeof(WCHAR))
|
|
{
|
|
pDest->Length = (USHORT)i * sizeof(WCHAR);
|
|
}
|
|
}
|
|
|