windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/wkssvc/server/useutil.c
2020-09-26 16:20:57 +08:00

1903 lines
49 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991-1992 Microsoft Corporation
Module Name:
useutil.c
Abstract:
This module contains the common utility routines for needed to
implement the NetUse APIs.
Author:
Rita Wong (ritaw) 10-Mar-1991
Revision History:
--*/
#include "wsutil.h"
#include "wsdevice.h"
#include "wsuse.h"
#include "wsmain.h"
#include <names.h>
#include <winbasep.h>
//-------------------------------------------------------------------//
// //
// Local function prototypes //
// //
//-------------------------------------------------------------------//
STATIC
NET_API_STATUS
WsGrowUseTable(
VOID
);
STATIC
VOID
WsFindLocal(
IN PUSE_ENTRY UseList,
IN LPTSTR Local,
OUT PUSE_ENTRY *MatchedPointer,
OUT PUSE_ENTRY *BackPointer
);
LPTSTR
WsReturnSessionPath(
IN LPTSTR LocalDeviceName
);
//-------------------------------------------------------------------//
// //
// Global variables //
// //
//-------------------------------------------------------------------//
//
// Redirector name in NT string format
//
UNICODE_STRING RedirectorDeviceName;
//
// Use Table
//
USERS_OBJECT Use;
NET_API_STATUS
WsInitUseStructures(
VOID
)
/*++
Routine Description:
This function creates the Use Table, and initialize the NT-style string
of the redirector device name.
Arguments:
None
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
//
// Initialize NT-style redirector device name string.
//
RtlInitUnicodeString(&RedirectorDeviceName, DD_NFS_DEVICE_NAME_U);
//
// Allocate and initialize the Use Table which is an array of logged
// on user entries, with a linked list of use entries for each user.
//
return WsInitializeUsersObject(&Use);
}
VOID
WsDestroyUseStructures(
VOID
)
/*++
Routine Description:
This function destroys the Use Table.
Arguments:
None
Return Value:
None.
--*/
{
DWORD i;
PUSE_ENTRY UseEntry;
PUSE_ENTRY PreviousEntry;
//
// Lock Use Table
//
if (! RtlAcquireResourceExclusive(&Use.TableResource, TRUE)) {
return;
}
//
// Close handles for every use entry that still exist and free the memory
// allocated for the use entry.
//
for (i = 0; i < Use.TableSize; i++) {
UseEntry = Use.Table[i].List;
while (UseEntry != NULL) {
(void) WsDeleteConnection(
&Use.Table[i].LogonId,
UseEntry->TreeConnection,
USE_NOFORCE
);
WsDeleteSymbolicLink(
UseEntry->Local,
UseEntry->TreeConnectStr,
NULL
);
UseEntry->Remote->TotalUseCount -= UseEntry->UseCount;
if (UseEntry->Remote->TotalUseCount == 0) {
(void) LocalFree((HLOCAL) UseEntry->Remote);
}
PreviousEntry = UseEntry;
UseEntry = UseEntry->Next;
(void) LocalFree((HLOCAL) PreviousEntry);
}
}
RtlReleaseResource(&Use.TableResource);
//
// Free the array of logged on user entries, and delete the resource
// created to serialize access to the array.
//
WsDestroyUsersObject(&Use);
}
NET_API_STATUS
WsFindUse(
IN PLUID LogonId,
IN PUSE_ENTRY UseList,
IN LPTSTR UseName,
OUT PHANDLE TreeConnection,
OUT PUSE_ENTRY *MatchedPointer,
OUT PUSE_ENTRY *BackPointer OPTIONAL
)
/*++
Routine Description:
This function searches the Use Table for the specified tree connection.
If the connection is found, NERR_Success is returned.
If the UseName is found in the Use Table (explicit connection), a
pointer to the matching use entry is returned. Otherwise, MatchedPointer
is set to NULL.
WARNING: This function assumes that the Use.TableResource is claimed.
Arguments:
LogonId - Supplies a pointer to the user's Logon Id.
UseList - Supplies the use list of the user.
UseName - Supplies the name of the tree connection, this is either a
local device name or a UNC name.
TreeConnection - Returns a handle to the found tree connection.
MatchedPointer - Returns the pointer to the matching use entry. This
pointer is set to NULL if the specified use is an implicit
connection.
BackPointer - Returns the pointer to the entry previous to the matching
use entry if MatchedPointer is not NULL.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
PUSE_ENTRY Back;
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] WsFindUse: Usename is %ws\n", UseName));
}
//
// Look for use entry depending on whether the local device name or
// UNC name is specified.
//
if (UseName[1] != TCHAR_BACKSLASH) {
//
// Local device name is specified.
//
WsFindLocal(
UseList,
UseName,
MatchedPointer,
&Back
);
if (*MatchedPointer == NULL) {
return NERR_UseNotFound;
}
else {
*TreeConnection = (*MatchedPointer)->TreeConnection;
if (ARGUMENT_PRESENT(BackPointer)) {
*BackPointer = Back;
}
return NERR_Success;
}
}
else {
//
// UNC name is specified, need to find matching shared resource
// in use list.
//
WsFindUncName(
UseList,
UseName,
MatchedPointer,
&Back
);
if (*MatchedPointer == NULL) {
NET_API_STATUS status;
DWORD EnumConnectionHint = 0; // Hint size from redirector
LMR_REQUEST_PACKET Rrp; // Redirector request packet
PLMR_CONNECTION_INFO_0 UncList; // List of information on UNC
// connections
PLMR_CONNECTION_INFO_0 SavePtr;
DWORD i;
BOOL FoundImplicitEntry = FALSE;
DWORD UseNameLength = STRLEN(UseName);
UNICODE_STRING TreeConnectStr;
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] WsFindUse: No explicit entry\n"));
}
//
// Did not find an explicit connection, see if there is an
// implicit connection by enumerating all implicit connections
//
Rrp.Type = GetConnectionInfo;
Rrp.Version = REQUEST_PACKET_VERSION;
RtlCopyLuid(&Rrp.LogonId, LogonId);
Rrp.Level = 0;
Rrp.Parameters.Get.ResumeHandle = 0;
if ((status = WsDeviceControlGetInfo(
Redirector,
WsRedirDeviceHandle,
FSCTL_LMR_ENUMERATE_CONNECTIONS,
(PVOID) &Rrp,
sizeof(LMR_REQUEST_PACKET),
(LPBYTE *) &UncList,
MAXULONG,
EnumConnectionHint,
NULL
)) != NERR_Success) {
return status;
}
SavePtr = UncList;
for (i = 0; i < Rrp.Parameters.Get.EntriesRead &&
FoundImplicitEntry == FALSE; i++, UncList++) {
if (WsCompareStringU(
UncList->UNCName.Buffer,
UncList->UNCName.Length / sizeof(WCHAR),
UseName,
UseNameLength
) == 0) {
FoundImplicitEntry = TRUE;
}
}
MIDL_user_free((PVOID) SavePtr);
//
// Fail if no such connection.
//
if (! FoundImplicitEntry) {
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] WsFindUse: No implicit entry\n"));
}
return NERR_UseNotFound;
}
//
// Otherwise open the connection and return the handle
//
//
// Replace \\ with \Device\LanmanRedirector\ in UseName
//
if ((status = WsCreateTreeConnectName(
UseName,
STRLEN(UseName),
NULL,
0,
&TreeConnectStr
)) != NERR_Success) {
return status;
}
//
// Redirector will pick up the logon username and password
// from the LSA if the authentication package is loaded.
//
status = WsOpenCreateConnection(
&TreeConnectStr,
NULL,
NULL,
NULL,
0, // no special flags
FILE_OPEN,
USE_WILDCARD,
TreeConnection,
NULL
);
(void) LocalFree(TreeConnectStr.Buffer);
return status;
}
else {
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] WsFindUse: Found an explicit entry\n"));
}
//
// Found an explicit UNC connection (NULL local device name).
//
NetpAssert((*MatchedPointer)->Local == NULL);
*TreeConnection = (*MatchedPointer)->TreeConnection;
if (ARGUMENT_PRESENT(BackPointer)) {
*BackPointer = Back;
}
return NERR_Success;
}
}
}
VOID
WsFindInsertLocation(
IN PUSE_ENTRY UseList,
IN LPTSTR UncName,
OUT PUSE_ENTRY *MatchedPointer,
OUT PUSE_ENTRY *InsertPointer
)
/*++
Routine Description:
This function searches the use list for the location to insert a new use
entry. The use entry is inserted to the end of the use list so the
pointer to the last node in the use list is returned via InsertPointer.
We also have to save a pointer to the node with the same UNC name so that
the new use entry can be set to point to the same remote node (where the
UNC name is stored). This pointer is returned as MatchedPointer.
WARNING: This function assumes that the Use.TableResource has been claimed.
Arguments:
UseList - Supplies the pointer to the use list.
UncName - Supplies the pointer to the shared resource (UNC name).
MatchedPointer - Returns a pointer to the node that holds the matching
UncName. If no matching UncName is found, this pointer is set to
NULL. If there are more than one node that has the same UNC name,
this pointer will point to the node with the NULL local device name,
if any; otherwise, if all nodes with matching UNC names have non-null
local device names, the pointer to the last matching node will be
returned.
InsertPointer - Returns a pointer to the last use entry, after which the
new entry is to be inserted.
Return Value:
None.
--*/
{
BOOL IsMatchWithNullDevice = FALSE;
*MatchedPointer = NULL;
while (UseList != NULL) {
//
// Do the string comparison only if we haven't found a matching UNC
// name with a NULL local device name.
//
if (! IsMatchWithNullDevice &&
(STRICMP((LPWSTR) UseList->Remote->UncName, UncName) == 0)) {
//
// Found matching entry
//
*MatchedPointer = UseList;
IsMatchWithNullDevice = (UseList->Local == NULL);
}
*InsertPointer = UseList;
UseList = UseList->Next;
}
}
VOID
WsFindUncName(
IN PUSE_ENTRY UseList,
IN LPTSTR UncName,
OUT PUSE_ENTRY *MatchedPointer,
OUT PUSE_ENTRY *BackPointer
)
/*++
Routine Description:
This function searches the use list for the use entry with the specified
UNC name with a NULL local device name.
WARNING: This function assumes that the Use.TableResource has been claimed.
Arguments:
UseList - Supplies the pointer to the use list.
UncName - Supplies the pointer to the shared resource (UNC name).
MatchedPointer - Returns a pointer to the node that holds the matching
UncName. If no matching UncName is found, this pointer is set to
NULL.
BackPointer - Returns a pointer to the entry previous to the found entry.
If UncName is not found, this pointer is set to NULL.
Return Value:
None.
--*/
{
*BackPointer = UseList;
while (UseList != NULL) {
if ((UseList->Local == NULL) &&
(STRICMP((LPWSTR) UseList->Remote->UncName, UncName) == 0)) {
//
// Found matching entry
//
*MatchedPointer = UseList;
return;
}
else {
*BackPointer = UseList;
UseList = UseList->Next;
}
}
//
// Did not find matching UNC name with a NULL local device name in the
// entire list.
//
*MatchedPointer = NULL;
*BackPointer = NULL;
}
STATIC
VOID
WsFindLocal(
IN PUSE_ENTRY UseList,
IN LPTSTR Local,
OUT PUSE_ENTRY *MatchedPointer,
OUT PUSE_ENTRY *BackPointer
)
/*++
Routine Description:
This function searches the use list for the specified local device name.
WARNING: This function assumes that the Use.TableResource has been claimed.
Arguments:
UseList - Supplies the pointer to the use list.
Local - Supplies the local device name.
MatchedPointer - Returns a pointer to the use entry that holds the matching
local device name. If no matching local device name is found, this
pointer is set to NULL.
BackPointer - Returns a pointer to the entry previous to the found entry.
If the local device name is not found, this pointer is set to NULL.
Return Value:
None.
--*/
{
*BackPointer = UseList;
while (UseList != NULL) {
if ((UseList->Local != NULL) &&
(STRICMP(UseList->Local, Local) == 0)) {
//
// Found matching entry
//
*MatchedPointer = UseList;
return;
}
else {
*BackPointer = UseList;
UseList = UseList->Next;
}
}
//
// Did not find matching local device name in the entire list.
//
*MatchedPointer = NULL;
*BackPointer = NULL;
}
NET_API_STATUS
WsCreateTreeConnectName(
IN LPTSTR UncName,
IN DWORD UncNameLength,
IN LPTSTR LocalName OPTIONAL,
IN DWORD SessionId,
OUT PUNICODE_STRING TreeConnectStr
)
/*++
Routine Description:
This function replaces \\ with \Device\LanmanRedirector\DEVICE: in the
UncName to form the NT-style tree connection name. A buffer is allocated
by this function and returned as the output string.
Arguments:
UncName - Supplies the UNC name of the shared resource.
UncNameLength - Supplies the length of the UNC name.
LocalName - Supplies the local device name for the redirection.
SessionId - Id that uniquely identifies a Hydra session. This value is always
0 for non-hydra NT and console hydra session
TreeConnectStr - Returns a string with a newly allocated buffer that
contains the NT-style tree connection name.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
BOOLEAN IsDeviceName = FALSE;
WCHAR IdBuffer[16]; // Value from RtlIntegerToUnicodeString
UNICODE_STRING IdString;
LUID LogonId;
WCHAR LUIDBuffer[32]; // Value from _snwprintf
UNICODE_STRING LUIDString;
NET_API_STATUS status;
IdString.Length = 0;
IdString.MaximumLength = sizeof(IdBuffer);
IdString.Buffer = IdBuffer;
RtlIntegerToUnicodeString( SessionId, 10, &IdString );
if (WsLUIDDeviceMapsEnabled == TRUE) {
//
// Get LogonID of the user
//
if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
return status;
}
_snwprintf( LUIDBuffer,
sizeof(LUIDBuffer)/sizeof(WCHAR),
L"%08x%08x",
LogonId.HighPart,
LogonId.LowPart );
RtlInitUnicodeString( &LUIDString, LUIDBuffer );
}
if (ARGUMENT_PRESENT(LocalName)) {
IsDeviceName = ((STRNICMP(LocalName, TEXT("LPT"), 3) == 0) ||
(STRNICMP(LocalName, TEXT("COM"), 3) == 0));
}
//
// Initialize tree connect string maximum length to hold
// \Device\LanmanRedirector\DEVICE:\SERVER\SHARE
//
// The new redirector requires an additional character for name
// canonicalization.
if (!LoadedMRxSmbInsteadOfRdr) {
// The old redirector
TreeConnectStr->MaximumLength = (USHORT)(RedirectorDeviceName.Length +
(USHORT) (UncNameLength * sizeof(WCHAR)) +
(ARGUMENT_PRESENT(LocalName) ? (STRLEN(LocalName)*sizeof(WCHAR)) : 0) +
sizeof(WCHAR) + // For "\"
(IsDeviceName ? sizeof(WCHAR) : 0));
} else {
// The new redirector
TreeConnectStr->MaximumLength = (USHORT)(RedirectorDeviceName.Length +
(USHORT) (UncNameLength * sizeof(WCHAR)) +
(ARGUMENT_PRESENT(LocalName) ? ((STRLEN(LocalName)+1)*sizeof(WCHAR)) //+1 for ';'
: 0) +
sizeof(WCHAR) + // For "\"
((WsLUIDDeviceMapsEnabled == TRUE) ?
(LUIDString.Length * sizeof(WCHAR)) :
(IdString.Length * sizeof(WCHAR))) +
(IsDeviceName ? sizeof(WCHAR) : 0));
}
if ((TreeConnectStr->Buffer = (PWSTR) LocalAlloc(
LMEM_ZEROINIT,
(UINT) TreeConnectStr->MaximumLength
)) == NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Copy \Device\LanmanRedirector
//
RtlCopyUnicodeString(TreeConnectStr, &RedirectorDeviceName);
//
// Concatenate \DEVICE:
//
if (ARGUMENT_PRESENT(LocalName)) {
wcscat(TreeConnectStr->Buffer, L"\\");
TreeConnectStr->Length += sizeof(WCHAR);
// Concatenate the ; required by the new redirector for canonicalization
if (LoadedMRxSmbInsteadOfRdr) {
wcscat(TreeConnectStr->Buffer, L";");
TreeConnectStr->Length += sizeof(WCHAR);
}
wcscat(TreeConnectStr->Buffer, LocalName);
TreeConnectStr->Length += (USHORT)(STRLEN(LocalName)*sizeof(WCHAR));
if (IsDeviceName) {
wcscat(TreeConnectStr->Buffer, L":");
TreeConnectStr->Length += sizeof(WCHAR);
}
if (LoadedMRxSmbInsteadOfRdr) {
if (WsLUIDDeviceMapsEnabled == TRUE) {
// Add the Logon Id
RtlAppendUnicodeStringToString( TreeConnectStr, &LUIDString );
}
else {
// Add the session id
RtlAppendUnicodeStringToString( TreeConnectStr, &IdString );
}
}
}
//
// Concatenate \SERVER\SHARE
//
wcscat(TreeConnectStr->Buffer, &UncName[1]);
TreeConnectStr->Length += (USHORT)((UncNameLength - 1) * sizeof(WCHAR));
return NERR_Success;
}
NET_API_STATUS
WsOpenCreateConnection(
IN PUNICODE_STRING TreeConnectionName,
IN LPTSTR UserName OPTIONAL,
IN LPTSTR DomainName OPTIONAL,
IN LPTSTR Password OPTIONAL,
IN ULONG CreateFlags,
IN ULONG CreateDisposition,
IN ULONG ConnectionType,
OUT PHANDLE TreeConnectionHandle,
OUT PULONG_PTR Information OPTIONAL
)
/*++
Routine Description:
This function asks the redirector to either open an existing tree
connection (CreateDisposition == FILE_OPEN), or create a new tree
connection if one does not exist (CreateDisposition == FILE_OPEN_IF).
The password and user name passed to the redirector via the EA buffer
in the NtCreateFile call. The EA buffer is NULL if neither password
or user name is specified.
The redirector expects the EA descriptor string to be in Unicode
but the password and username strings to be in ANSI.
Arguments:
TreeConnectionName - Supplies the name of the tree connection in NT-style
file name format: \Device\LanmanRedirector\SERVER\SHARE
UserName - Supplies the user name to create the tree connection with.
DomainName - Supplies the name of the domain to get user credentials from.
Password - Supplies the password to create the tree connection with.
CreateDisposition - Supplies the create disposition value to either
open or create the tree connection.
ConnectionType - Supplies the type of the connection (USE_xxx)
TreeConnectionHandle - Returns the handle to the tree connection
created/opened by the redirector.
Information - Returns the information field of the I/O status block.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS status;
NTSTATUS ntstatus;
OBJECT_ATTRIBUTES UncNameAttributes;
IO_STATUS_BLOCK IoStatusBlock;
PFILE_FULL_EA_INFORMATION EaBuffer = NULL;
PFILE_FULL_EA_INFORMATION Ea;
ULONG EaBufferSize = 0;
UCHAR EaNamePasswordSize = (UCHAR) (ROUND_UP_COUNT(
strlen(EA_NAME_PASSWORD) + sizeof(CHAR),
ALIGN_WCHAR
) - sizeof(CHAR));
UCHAR EaNameUserNameSize = (UCHAR) (ROUND_UP_COUNT(
strlen(EA_NAME_USERNAME) + sizeof(CHAR),
ALIGN_WCHAR
) - sizeof(CHAR));
UCHAR EaNameDomainNameSize = (UCHAR) (ROUND_UP_COUNT(
strlen(EA_NAME_DOMAIN) + sizeof(CHAR),
ALIGN_WCHAR
) - sizeof(CHAR));
UCHAR EaNameTypeSize = (UCHAR) (ROUND_UP_COUNT(
strlen(EA_NAME_TYPE) + sizeof(CHAR),
ALIGN_DWORD
) - sizeof(CHAR));
UCHAR EaNameConnectSize = (UCHAR) (ROUND_UP_COUNT(
strlen(EA_NAME_CONNECT) + sizeof(CHAR),
ALIGN_DWORD
) - sizeof(CHAR));
UCHAR EaNameCSCAgentSize = (UCHAR) (ROUND_UP_COUNT(
strlen(EA_NAME_CSCAGENT) + sizeof(CHAR),
ALIGN_DWORD
) - sizeof(CHAR));
USHORT PasswordSize = 0;
USHORT UserNameSize = 0;
USHORT DomainNameSize = 0;
USHORT TypeSize = sizeof(ULONG);
InitializeObjectAttributes(
&UncNameAttributes,
TreeConnectionName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
//
// Calculate the number of bytes needed for the EA buffer to put the
// password or user name.
//
if (ARGUMENT_PRESENT(Password)) {
PasswordSize = (USHORT) (wcslen(Password) * sizeof(WCHAR));
EaBufferSize = ROUND_UP_COUNT(
FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNamePasswordSize + sizeof(CHAR) +
PasswordSize,
ALIGN_DWORD
);
}
if (ARGUMENT_PRESENT(UserName)) {
UserNameSize = (USHORT) (wcslen(UserName) * sizeof(WCHAR));
EaBufferSize += ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameUserNameSize + sizeof(CHAR) +
UserNameSize,
ALIGN_DWORD
);
}
if (ARGUMENT_PRESENT(DomainName)) {
DomainNameSize = (USHORT) (wcslen(DomainName) * sizeof(WCHAR));
EaBufferSize += ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameDomainNameSize + sizeof(CHAR) +
DomainNameSize,
ALIGN_DWORD
);
}
if(CreateFlags & CREATE_NO_CONNECT)
{
EaBufferSize += ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameConnectSize + sizeof(CHAR),
ALIGN_DWORD
);
}
if(CreateFlags & CREATE_BYPASS_CSC)
{
EaBufferSize += ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameCSCAgentSize + sizeof(CHAR),
ALIGN_DWORD
);
}
EaBufferSize += FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameTypeSize + sizeof(CHAR) +
TypeSize;
//
// Allocate the EA buffer
//
if ((EaBuffer = (PFILE_FULL_EA_INFORMATION) LocalAlloc(
LMEM_ZEROINIT,
(UINT) EaBufferSize
)) == NULL) {
status = GetLastError();
goto FreeMemory;
}
Ea = EaBuffer;
if(CreateFlags & CREATE_NO_CONNECT)
{
//
// Copy the EA name into EA buffer. EA name length does not
// include the zero terminator.
//
strcpy((LPSTR) Ea->EaName, EA_NAME_CONNECT);
Ea->EaNameLength = EaNameConnectSize;
Ea->EaValueLength = 0;
Ea->NextEntryOffset = ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameConnectSize + sizeof(CHAR) +
0,
ALIGN_DWORD
);
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] OpenCreate: After round, NextEntryOffset=%lu\n",
Ea->NextEntryOffset));
}
Ea->Flags = 0;
(ULONG_PTR) Ea += Ea->NextEntryOffset;
}
if( CreateFlags & CREATE_BYPASS_CSC ) {
strcpy((LPSTR)Ea->EaName, EA_NAME_CSCAGENT);
Ea->EaNameLength = EaNameCSCAgentSize;
Ea->EaValueLength = 0;
Ea->NextEntryOffset = ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameCSCAgentSize + sizeof(CHAR) +
0,
ALIGN_DWORD
);
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] OpenCreate: After round, NextEntryOffset=%lu\n",
Ea->NextEntryOffset));
}
Ea->Flags = 0;
(ULONG_PTR) Ea += Ea->NextEntryOffset;
}
if (ARGUMENT_PRESENT(Password)) {
//
// Copy the EA name into EA buffer. EA name length does not
// include the zero terminator.
//
strcpy((LPSTR) Ea->EaName, EA_NAME_PASSWORD);
Ea->EaNameLength = EaNamePasswordSize;
//
// Copy the EA value into EA buffer. EA value length does not
// include the zero terminator.
//
wcscpy(
(LPWSTR) &(Ea->EaName[EaNamePasswordSize + sizeof(CHAR)]),
Password
);
Ea->EaValueLength = PasswordSize;
Ea->NextEntryOffset = ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNamePasswordSize + sizeof(CHAR) +
PasswordSize,
ALIGN_DWORD
);
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] OpenCreate: After round, NextEntryOffset=%lu\n",
Ea->NextEntryOffset));
}
Ea->Flags = 0;
(ULONG_PTR) Ea += Ea->NextEntryOffset;
}
if (ARGUMENT_PRESENT(UserName)) {
//
// Copy the EA name into EA buffer. EA name length does not
// include the zero terminator.
//
strcpy((LPSTR) Ea->EaName, EA_NAME_USERNAME);
Ea->EaNameLength = EaNameUserNameSize;
//
// Copy the EA value into EA buffer. EA value length does not
// include the zero terminator.
//
wcscpy(
(LPWSTR) &(Ea->EaName[EaNameUserNameSize + sizeof(CHAR)]),
UserName
);
Ea->EaValueLength = UserNameSize;
Ea->NextEntryOffset = ROUND_UP_COUNT(
FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameUserNameSize + sizeof(CHAR) +
UserNameSize,
ALIGN_DWORD
);
Ea->Flags = 0;
(ULONG_PTR) Ea += Ea->NextEntryOffset;
}
if (ARGUMENT_PRESENT(DomainName)) {
//
// Copy the EA name into EA buffer. EA name length does not
// include the zero terminator.
//
strcpy((LPSTR) Ea->EaName, EA_NAME_DOMAIN);
Ea->EaNameLength = EaNameDomainNameSize;
//
// Copy the EA value into EA buffer. EA value length does not
// include the zero terminator.
//
wcscpy(
(LPWSTR) &(Ea->EaName[EaNameDomainNameSize + sizeof(CHAR)]),
DomainName
);
Ea->EaValueLength = DomainNameSize;
Ea->NextEntryOffset = ROUND_UP_COUNT(
FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameDomainNameSize + sizeof(CHAR) +
DomainNameSize,
ALIGN_DWORD
);
Ea->Flags = 0;
(ULONG_PTR) Ea += Ea->NextEntryOffset;
}
//
// Copy the EA for the connection type name into EA buffer. EA name length
// does not include the zero terminator.
//
strcpy((LPSTR) Ea->EaName, EA_NAME_TYPE);
Ea->EaNameLength = EaNameTypeSize;
*((PULONG) &(Ea->EaName[EaNameTypeSize + sizeof(CHAR)])) = ConnectionType;
Ea->EaValueLength = TypeSize;
Ea->NextEntryOffset = 0;
Ea->Flags = 0;
if ((status = WsImpersonateClient()) != NERR_Success) {
goto FreeMemory;
}
//
// Create or open a tree connection
//
ntstatus = NtCreateFile(
TreeConnectionHandle,
SYNCHRONIZE,
&UncNameAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
CreateDisposition,
FILE_CREATE_TREE_CONNECTION
| FILE_SYNCHRONOUS_IO_NONALERT,
(PVOID) EaBuffer,
EaBufferSize
);
WsRevertToSelf();
if (NT_SUCCESS(ntstatus)) {
ntstatus = IoStatusBlock.Status;
}
if (ARGUMENT_PRESENT(Information)) {
*Information = IoStatusBlock.Information;
}
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] NtCreateFile returns %lx\n", ntstatus));
}
status = WsMapStatus(ntstatus);
FreeMemory:
if (EaBuffer != NULL) {
// Prevent password from making it to pagefile.
RtlZeroMemory( EaBuffer, EaBufferSize );
(void) LocalFree((HLOCAL) EaBuffer);
}
return status;
}
NET_API_STATUS
WsDeleteConnection(
IN PLUID LogonId,
IN HANDLE TreeConnection,
IN DWORD ForceLevel
)
/*++
Routine Description:
This function asks the redirector to delete the tree connection
associated with the tree connection handle, and closes the handle.
Arguments:
LogonId - Supplies a pointer to the user's Logon Id.
TreeConnection - Supplies the handle to the tree connection created.
ForceLevel - Supplies the level of force to delete the tree connection.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS status;
LMR_REQUEST_PACKET Rrp; // Redirector request packet
//
// Map force level to values the redirector understand
//
switch (ForceLevel) {
case USE_NOFORCE:
case USE_LOTS_OF_FORCE:
Rrp.Level = ForceLevel;
break;
case USE_FORCE:
Rrp.Level = USE_NOFORCE;
break;
default:
NetpKdPrint(("[Wksta] Invalid force level %lu should never happen!\n",
ForceLevel));
NetpAssert(FALSE);
}
//
// Tell the redirector to delete the tree connection
//
Rrp.Version = REQUEST_PACKET_VERSION;
RtlCopyLuid(&Rrp.LogonId, LogonId);
status = WsRedirFsControl(
TreeConnection,
FSCTL_LMR_DELETE_CONNECTION,
&Rrp,
sizeof(LMR_REQUEST_PACKET),
NULL,
0,
NULL
);
//
// Close the connection handle
//
if(status == NERR_Success)
{
(void) NtClose(TreeConnection);
}
return status;
}
BOOL
WsRedirectionPaused(
IN LPTSTR LocalDeviceName
)
/*++
Routine Description:
This function checks to see if the redirection for the print and comm
devices are paused for the system. Since we are only checking a global
flag, there's no reason to protect it with a RESOURCE.
Arguments:
LocalDeviceName - Supplies the name of the local device.
Return Value:
Returns TRUE redirection is paused; FALSE otherwise
--*/
{
if ((STRNICMP(LocalDeviceName, TEXT("LPT"), 3) == 0) ||
(STRNICMP(LocalDeviceName, TEXT("COM"), 3) == 0)) {
//
// Redirection of print and comm devices are paused if
// workstation service is paused.
//
return (WsGlobalData.Status.dwCurrentState == SERVICE_PAUSED);
} else {
//
// Redirection of disk devices cannot be paused.
//
return FALSE;
}
}
VOID
WsPauseOrContinueRedirection(
IN REDIR_OPERATION OperationType
)
/*++
Routine Description:
This function pauses or unpauses (based on OperationType) the redirection
of print or comm devices.
Arguments:
OperationType - Supplies a value that causes redirection to be paused or
continued.
Return Value:
None.
--*/
{
DWORD Index; // Index to user entry in Use Table
PUSE_ENTRY UseEntry;
//
// Lock Use Table
//
if (! RtlAcquireResourceExclusive(&Use.TableResource, TRUE)) {
return;
}
//
// If we want to pause and we are already paused, or if we want to
// continue and we have not paused, just return.
//
if ((OperationType == PauseRedirection &&
WsGlobalData.Status.dwCurrentState == SERVICE_PAUSED) ||
(OperationType == ContinueRedirection &&
WsGlobalData.Status.dwCurrentState == SERVICE_RUNNING)) {
RtlReleaseResource(&Use.TableResource);
return;
}
//
// Pause or continue for all users
//
for (Index = 0; Index < Use.TableSize; Index++) {
UseEntry = Use.Table[Index].List;
while (UseEntry != NULL) {
if ((UseEntry->Local != NULL) &&
((STRNICMP(TEXT("LPT"), UseEntry->Local, 3) == 0) ||
(STRNICMP(TEXT("COM"), UseEntry->Local, 3) == 0))) {
if (OperationType == PauseRedirection) {
//
// Pause the redirection
//
//
// Delete the symbolic link
//
WsDeleteSymbolicLink(
UseEntry->Local,
UseEntry->TreeConnectStr,
NULL
);
}
else {
LPWSTR Session = NULL;
//
// Continue the redirection
//
if (WsCreateSymbolicLink(
UseEntry->Local,
USE_SPOOLDEV, // USE_CHARDEV is just as good
UseEntry->TreeConnectStr,
NULL,
&Session
) != NERR_Success) {
PUSE_ENTRY RestoredEntry = Use.Table[Index].List;
//
// Could not continue completely. Delete all
// symbolic links restored so far
//
while (RestoredEntry != UseEntry) {
if ((UseEntry->Local != NULL) &&
((STRNICMP(TEXT("LPT"), UseEntry->Local, 3) == 0) ||
(STRNICMP(TEXT("COM"), UseEntry->Local, 3) == 0))) {
WsDeleteSymbolicLink(
RestoredEntry->Local,
RestoredEntry->TreeConnectStr,
Session
);
}
RestoredEntry = RestoredEntry->Next;
}
RtlReleaseResource(&Use.TableResource);
LocalFree(Session);
return;
}
LocalFree(Session);
}
}
UseEntry = UseEntry->Next;
}
} // for all users
if (OperationType == PauseRedirection) {
WsGlobalData.Status.dwCurrentState = SERVICE_PAUSED;
}
else {
WsGlobalData.Status.dwCurrentState = SERVICE_RUNNING;
}
//
// Use the same resource to protect access to the RedirectionPaused flag
// in WsGlobalData
//
RtlReleaseResource(&Use.TableResource);
}
NET_API_STATUS
WsCreateSymbolicLink(
IN LPWSTR Local,
IN DWORD DeviceType,
IN LPWSTR TreeConnectStr,
IN PUSE_ENTRY UseList,
IN OUT LPWSTR *Session
)
/*++
Routine Description:
This function creates a symbolic link object for the specified local
device name which is linked to the tree connection name that has a
format of \Device\LanmanRedirector\Device:\Server\Share.
NOTE: when LUID Device maps are enabled,
Must perform the creation outside of exclusively holding the
Use.TableResource.
Otherwise, when the shell tries to update the current status of
a drive letter change, the explorer.exe thread will block while
trying to acquire the Use.TableResource
Arguments:
Local - Supplies the local device name.
DeviceType - Supplies the shared resource device type.
TreeConnectStr - Supplies the tree connection name string which is
the link target of the symbolick link object.
UseList - Supplies the pointer to the use list.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS status = NERR_Success;
WCHAR TempBuf[64];
DWORD dddFlags;
//
// Multiple session support
//
*Session = WsReturnSessionPath(Local);
if( *Session == NULL ) {
return( GetLastError() );
}
if (WsLUIDDeviceMapsEnabled == TRUE) {
if ((status = WsImpersonateClient()) != NERR_Success) {
return status;
}
}
//
// To redirect a comm or print device, we need to see if we have
// redirected it once before by searching through all existing
// redirections.
//
if ((DeviceType == USE_CHARDEV) || (DeviceType == USE_SPOOLDEV)) {
PUSE_ENTRY MatchedPointer;
PUSE_ENTRY BackPointer;
WsFindLocal(
UseList,
Local,
&MatchedPointer,
&BackPointer
);
if (MatchedPointer != NULL) {
//
// Already redirected
//
return ERROR_ALREADY_ASSIGNED;
}
}
else {
if (! QueryDosDeviceW(
*Session,
TempBuf,
64
)) {
if (GetLastError() != ERROR_FILE_NOT_FOUND) {
//
// Most likely failure occurred because our output
// buffer is too small. It still means someone already
// has an existing symbolic link for this device.
//
return ERROR_ALREADY_ASSIGNED;
}
//
// ERROR_FILE_NOT_FOUND (translated from OBJECT_NAME_NOT_FOUND)
// means it does not exist and we can redirect this device.
//
}
else {
//
// QueryDosDevice successfully an existing symbolic link--
// somebody is already using this device.
//
return ERROR_ALREADY_ASSIGNED;
}
}
//
// Create a symbolic link object to the device we are redirecting
//
dddFlags = DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM;
if (!DefineDosDeviceW(
dddFlags,
*Session,
TreeConnectStr
)) {
DWORD dwError = GetLastError();
if (WsLUIDDeviceMapsEnabled == TRUE) {
WsRevertToSelf();
}
return dwError;
}
else {
if (WsLUIDDeviceMapsEnabled == TRUE) {
WsRevertToSelf();
}
return NERR_Success;
}
}
VOID
WsDeleteSymbolicLink(
IN LPWSTR LocalDeviceName,
IN LPWSTR TreeConnectStr,
IN LPWSTR SessionDeviceName
)
/*++
Routine Description:
This function deletes the symbolic link we had created earlier for
the device.
NOTE: when LUID Device maps are enabled,
Must perform the deletion outside of exclusively holding the
Use.TableResource.
Otherwise, when the shell tries to update the current status of
a drive letter change, the explorer.exe thread will block while
trying to acquire the Use.TableResource
Arguments:
LocalDeviceName - Supplies the local device name string of which the
symbolic link object is created.
TreeConnectStr - Supplies a pointer to the Unicode string which
contains the link target string we want to match and delete.
Return Value:
None.
--*/
{
BOOLEAN DeleteSession = FALSE;
DWORD dddFlags;
if (LocalDeviceName != NULL ||
SessionDeviceName != NULL) {
if (SessionDeviceName == NULL) {
SessionDeviceName = WsReturnSessionPath(LocalDeviceName);
if( SessionDeviceName == NULL ) return;
DeleteSession = TRUE;
}
dddFlags = DDD_REMOVE_DEFINITION |
DDD_RAW_TARGET_PATH |
DDD_EXACT_MATCH_ON_REMOVE |
DDD_NO_BROADCAST_SYSTEM;
if (WsLUIDDeviceMapsEnabled == TRUE) {
if (WsImpersonateClient() != NERR_Success) {
return;
}
}
if (! DefineDosDeviceW(
dddFlags,
SessionDeviceName,
TreeConnectStr
)) {
#if DBG
NetpKdPrint(("DefineDosDevice DEL of %ws %ws returned %ld\n",
LocalDeviceName, TreeConnectStr, GetLastError()));
#endif
}
if (WsLUIDDeviceMapsEnabled == TRUE) {
WsRevertToSelf();
}
}
if( SessionDeviceName && DeleteSession) {
LocalFree( SessionDeviceName );
}
}
NET_API_STATUS
WsUseCheckRemote(
IN LPTSTR RemoteResource,
OUT LPTSTR UncName,
OUT LPDWORD UncNameLength
)
/*++
Routine Description:
This function checks the validity of the remote resource name
specified to NetUseAdd.
Arguments:
RemoteResource - Supplies the remote resource name specified by the API
caller.
UncName - Returns the canonicalized remote resource name.
UncNameLength - Returns the length of the canonicalized name.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS status;
DWORD PathType = 0;
LPTSTR Ptr;
if ((status = I_NetPathType(
NULL,
RemoteResource,
&PathType,
0)) == NERR_Success) {
//
// Check for UNC type
//
if (PathType != ITYPE_UNC) {
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] WsUseCheckRemote not UNC type\n"));
}
return ERROR_INVALID_PARAMETER;
}
//
// Canonicalize the name
//
status = I_NetPathCanonicalize(
NULL,
RemoteResource,
UncName,
(MAX_PATH) * sizeof(TCHAR),
NULL,
&PathType,
0
);
if (status != NERR_Success) {
IF_DEBUG(USE) {
NetpKdPrint((
"[Wksta] WsUseCheckRemote: I_NetPathCanonicalize return %lu\n",
status
));
}
return status;
}
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] WsUseCheckRemote: %ws\n", UncName));
}
}
else {
NetpKdPrint(("[Wksta] WsUseCheckRemote: I_NetPathType return %lu\n",
status));
return status;
}
//
// Detect illegal remote name in the form of \\XXX\YYY\zzz. We assume
// that the UNC name begins with exactly two leading backslashes.
//
if ((Ptr = STRCHR(UncName + 2, TCHAR_BACKSLASH)) == NULL) {
return ERROR_INVALID_PARAMETER;
}
if (!LoadedMRxSmbInsteadOfRdr && STRCHR(Ptr + 1, TCHAR_BACKSLASH) != NULL) {
//
// There should not be anymore backslashes
//
return ERROR_INVALID_PARAMETER;
}
*UncNameLength = STRLEN(UncName);
return NERR_Success;
}
NET_API_STATUS
WsUseCheckLocal(
IN LPTSTR LocalDevice,
OUT LPTSTR Local,
OUT LPDWORD LocalLength
)
/*++
Routine Description:
This function checks the validity of the local device name
specified to NetUseAdd.
Arguments:
LocalDevice - Supplies the local device name specified by the API
caller.
Local - Returns the canonicalized local device name.
LocalLength - Returns the length of the canonicalized name.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS status;
DWORD PathType = 0;
if ((status = I_NetPathType(
NULL,
LocalDevice,
&PathType,
0)) == NERR_Success) {
//
// Check for DEVICE type
//
if ((PathType != (ITYPE_DEVICE | ITYPE_DISK)) &&
(PathType != (ITYPE_DEVICE | ITYPE_LPT)) &&
(PathType != (ITYPE_DEVICE | ITYPE_COM))) {
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] WsUseCheckLocal not DISK, LPT, or COM type\n"));
}
return ERROR_INVALID_PARAMETER;
}
//
// Canonicalize the name
//
status = I_NetPathCanonicalize(
NULL,
LocalDevice,
Local,
(DEVLEN + 1) * sizeof(TCHAR),
NULL,
&PathType,
0
);
if (status != NERR_Success) {
IF_DEBUG(USE) {
NetpKdPrint((
"[Wksta] WsUseCheckLocal: I_NetPathCanonicalize return %lu\n",
status
));
}
return status;
}
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] WsUseCheckLocal: %ws\n", Local));
}
}
else {
NetpKdPrint(("[Wksta] WsUseCheckLocal: I_NetPathType return %lu\n",
status));
return status;
}
*LocalLength = STRLEN(Local);
return NERR_Success;
}
LPTSTR
WsReturnSessionPath(
IN LPTSTR LocalDeviceName
)
/*++
Routine Description:
This function returns the per session path to access the
specific dos device for multiple session support.
Arguments:
LocalDeviceName - Supplies the local device name specified by the API
caller.
Return Value:
LPTSTR - Pointer to per session path in newly allocated memory
by LocalAlloc().
--*/
{
BOOL rc;
DWORD SessionId;
CLIENT_ID ClientId;
LPTSTR SessionDeviceName;
NET_API_STATUS status;
if ((status = WsImpersonateAndGetSessionId(&SessionId)) != NERR_Success) {
return NULL;
}
rc = DosPathToSessionPath(
SessionId,
LocalDeviceName,
&SessionDeviceName
);
if( !rc ) {
return NULL;
}
return SessionDeviceName;
}