1903 lines
49 KiB
C
1903 lines
49 KiB
C
/*++
|
||
|
||
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;
|
||
}
|
||
|
||
|