windows-nt/Source/XPSP1/NT/ds/nw/svcdlls/nwwks/server/connect.c
2020-09-26 16:20:57 +08:00

2161 lines
54 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) 1993 Microsoft Corporation
Module Name:
connect.c
Abstract:
This module contains tree connections routines supported by
NetWare Workstation service.
Author:
Rita Wong (ritaw) 15-Feb-1993
Revision History:
--*/
#include <nw.h>
#include <handle.h>
#include <nwauth.h>
#include <nwcanon.h>
#include <nwreg.h>
#include <winbasep.h>
#define NW_ENUM_EXTRA_BYTES 256
extern BOOL NwLUIDDeviceMapsEnabled;
//-------------------------------------------------------------------//
// //
// Local Function Prototypes //
// //
//-------------------------------------------------------------------//
DWORD
NwAllocAndGetUncName(
IN LPWSTR LocalName,
IN DWORD LocalNameLength,
OUT LPWSTR *UncName
);
DWORD
NwDeleteAllInRegistry(
VOID
);
DWORD
NwDeleteUidSymLinks(
IN LUID Uid,
IN ULONG WinStationId
);
LPTSTR
NwReturnSessionPath(
IN LPTSTR LocalDeviceName
);
//-------------------------------------------------------------------//
DWORD
NwrCreateConnection(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR LocalName OPTIONAL,
IN LPWSTR RemoteName,
IN DWORD Type,
IN LPWSTR Password OPTIONAL,
IN LPWSTR UserName OPTIONAL
)
/*++
Routine Description:
This function creates a tree connection to the specified RemoteName
(UNC name) and maps it to the LocalName (local device name), if
it is specified. The password and user name are the credentials
used to create the connection, if specified; otherwise, the
interactive logged on user's credentials are used by default.
NOTE: This code now calls a helper routine to do the work, this helper
routine (NwCreateConnection) is identical to the code that used to be
here with the exception that the helper does call ImpersonateClient().
We now do the client impersonation outside of the helper routine.
Arguments:
Reserved - Must be NULL.
LocalName - Supplies the local device name to map to the created tree
connection. Only drive letter device names are accepted. (No
LPT or COM).
RemoteName - Supplies the UNC name of the remote resource in the format
of Server\Volume\Directory. It must be a disk resource.
Type - Supplies the connection type.
Password - Supplies the password to use to make the connection to the
server.
UserName - Supplies the user name to use to make the connection.
Return Value:
NO_ERROR - Operation was successful.
ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating internal work buffers.
WN_BAD_NETNAME - Remote resource name is invalid.
WN_BAD_LOCALNAME - Local DOS device name is invalid.
ERROR_BAD_NETPATH - The UNC name does not exist on the network.
ERROR_INVALID_PARAMETER - LPT or COM LocalName was specified.
Other errors from the redirector.
--*/
{
DWORD status;
BOOL Impersonate = FALSE ;
UNREFERENCED_PARAMETER(Reserved);
//
// Impersonate the client
//
if ((status = NwImpersonateClient()) != NO_ERROR)
{
goto CleanExit;
}
Impersonate = TRUE ;
status = NwCreateConnection( LocalName,
RemoteName,
Type,
Password,
UserName );
CleanExit:
if (Impersonate) {
(void) NwRevertToSelf();
}
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("NWWORKSTATION: NwrCreateConnection returns %lu\n", status));
}
#endif
return status;
}
DWORD
NwrDeleteConnection(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR ConnectionName,
IN DWORD UseForce
)
/*++
Routine Description:
This function deletes an existing connection.
Arguments:
Reserved - Must be NULL.
ConnectionName - Supplies the local device name or UNC name which
specifies the connection to delete. If UNC name is specified,
the UNC connection must exist.
UseForce - Supplies a flag which if TRUE specifies to tear down
the connection eventhough files are opened. If FALSE, the
connection is deleted only if there are no opened files.
Return Value:
NO_ERROR - Operation was successful.
ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating internal work buffers.
WN_BAD_NETNAME - ConnectionName is invalid.
ERROR_BAD_NETPATH - The UNC name does not exist on the network.
ERROR_INVALID_PARAMETER - LPT or COM LocalName was specified.
Other errors from the redirector.
--*/
{
DWORD status;
LPWSTR ConnectName = NULL;
DWORD ConnectLength;
LPWSTR LocalName;
LPWSTR UncName = NULL;
BOOL Impersonate = FALSE ;
UNREFERENCED_PARAMETER(Reserved);
if (*ConnectionName == 0) {
return ERROR_INVALID_PARAMETER;
}
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("\nNWWORKSTATION: NwrDeleteConnection: ConnectionName %ws, Force %lu\n",
ConnectionName, UseForce));
}
#endif
if ((status = NwLibCanonLocalName(
ConnectionName,
&ConnectName,
&ConnectLength
)) == NO_ERROR) {
//
// Get the UNC name mapped to this drive letter so that we can
// open a handle to it for deletion.
//
// ----Multi-user---------
// Need to impersonate the client
if ((status = NwImpersonateClient()) != NO_ERROR) {
goto CleanExit;
}
Impersonate = TRUE ;
if ((status = NwAllocAndGetUncName(
ConnectName,
ConnectLength,
&UncName
)) != NO_ERROR) {
if (status == WN_NOT_CONNECTED &&
NwGetGatewayResource(ConnectName,
NULL,
0,
NULL) == WN_MORE_DATA)
{
status = ERROR_DEVICE_IN_USE ;
}
(void) LocalFree((HLOCAL) ConnectName);
if (Impersonate) {
(void) NwRevertToSelf();
}
return status;
}
LocalName = ConnectName;
}
else {
//
// Not a device name. See if it is a UNC name.
//
if ((status = NwLibCanonRemoteName(
NULL,
ConnectionName,
&ConnectName,
NULL
)) != NO_ERROR) {
return status;
}
UncName = ConnectName;
LocalName = NULL;
}
if ( !Impersonate ) {
if ((status = NwImpersonateClient()) != NO_ERROR) {
goto CleanExit;
}
Impersonate = TRUE ;
}
//
// To delete a connection, a tree connection handle must be opened to
// it so that the handle can be specified to the redirector to delete
// the connection.
//
status = NwOpenHandleToDeleteConn(
UncName,
LocalName,
UseForce,
FALSE,
TRUE
);
if ( status == ERROR_FILE_NOT_FOUND )
status = ERROR_BAD_NETPATH;
CleanExit:
if (Impersonate) {
(void) NwRevertToSelf();
}
if (UncName != NULL && UncName != ConnectName) {
(void) LocalFree((HLOCAL) UncName);
}
if (ConnectName != NULL) {
(void) LocalFree((HLOCAL) ConnectName);
}
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("NWWORKSTATION: NwrDeleteConnection returns %lu\n", status));
}
#endif
return status;
}
DWORD
NwrQueryServerResource(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR LocalName,
OUT LPWSTR RemoteName,
IN DWORD RemoteNameLen,
OUT LPDWORD CharsRequired
)
/*++
Routine Description:
This function looks up the UNC name associated with the given DOS
device name.
Arguments:
Reserved - Must be NULL.
LocalName - Supplies the local device name to look up.
RemoteName - Receives the UNC name mapped to the LocalName.
RemoteNameLen - Supplies the length of the RemoteName buffer.
CharsRequired - Receives the length required of the RemoteName buffer
to get the UNC name. This value is only returned if the return
code is ERROR_MORE_DATA.
Return Value:
NO_ERROR - Operation was successful.
WN_BAD_LOCALNAME - LocalName was invalid.
ERROR_INVALID_PARAMETER - LPT or COM LocalName was specified.
ERROR_MORE_DATA - RemoteName buffer was too small.
ERROR_NOT_CONNECTED - LocalName does not map to any server resource.
--*/
{
DWORD status;
LPWSTR Local;
DWORD LocalLength;
BOOL Impersonate = FALSE ;
UNREFERENCED_PARAMETER(Reserved);
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("\nNWWORKSTATION: NwrQueryServerResource: LocalName %ws, RemoteNameLen %lu\n",
LocalName, RemoteNameLen));
}
#endif
//
// Canonicalize the LocalName
//
if ((status = NwLibCanonLocalName(
LocalName,
&Local,
&LocalLength
)) != NO_ERROR) {
return WN_BAD_LOCALNAME;
}
if ((status = NwImpersonateClient()) != NO_ERROR)
{
goto CleanExit;
}
Impersonate = TRUE ;
status = NwGetServerResource(
Local,
LocalLength,
RemoteName,
RemoteNameLen,
CharsRequired
);
if (status == WN_NOT_CONNECTED)
{
status = NwGetGatewayResource(
Local,
RemoteName,
RemoteNameLen,
CharsRequired
);
}
CleanExit:
if (Impersonate) {
(void) NwRevertToSelf();
}
(void) LocalFree((HLOCAL) Local);
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("NWWORKSTATION: NwrQueryServerResource returns %lu\n", status));
if (status == NO_ERROR) {
KdPrint((" RemoteName is %ws\n", RemoteName));
}
else if (status == ERROR_MORE_DATA) {
KdPrint((" RemoteNameLen %lu too small. Need %lu\n",
RemoteNameLen, *CharsRequired));
}
}
#endif
return status;
}
DWORD
NwrOpenEnumConnections(
IN LPWSTR Reserved OPTIONAL,
IN DWORD ConnectionType,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function creates a new context handle and initializes it
for enumerating the connections.
Arguments:
Reserved - Unused.
EnumHandle - Receives the newly created context handle.
Return Value:
ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could
not be allocated.
NO_ERROR - Call was successful.
--*/
{
LPNW_ENUM_CONTEXT ContextHandle;
UNREFERENCED_PARAMETER(Reserved);
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("\nNWWORKSTATION: NwrOpenEnumConnections\n"));
}
#endif
//
// Allocate memory for the context handle structure.
//
ContextHandle = (PVOID) LocalAlloc(
LMEM_ZEROINIT,
sizeof(NW_ENUM_CONTEXT)
);
if (ContextHandle == NULL) {
KdPrint(("NWWORKSTATION: NwrOpenEnumConnections LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Initialize contents of the context handle structure.
//
ContextHandle->Signature = NW_HANDLE_SIGNATURE;
ContextHandle->HandleType = NwsHandleListConnections;
ContextHandle->ResumeId = 0;
ContextHandle->ConnectionType = 0;
if ( ConnectionType == RESOURCETYPE_ANY ) {
ContextHandle->ConnectionType = CONNTYPE_ANY;
}
else {
if ( ConnectionType & RESOURCETYPE_DISK )
ContextHandle->ConnectionType |= CONNTYPE_DISK;
if ( ConnectionType & RESOURCETYPE_PRINT )
ContextHandle->ConnectionType |= CONNTYPE_PRINT;
}
//
// Return the newly created context.
//
*EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle;
return NO_ERROR;
}
DWORD
NwrGetConnectionPerformance(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR lpRemoteName,
OUT LPBYTE lpNetConnectInfo,
IN DWORD dwBufferSize
)
/*++
Routine Description:
This function returns information about the expected performance of a
connection used to access a network resource. The request can only be
for a network resource to which there is currently a connection.
Arguments:
Reserved - Unused.
lpRemoteName - Contains the local name or remote name for a resource
for which a connection exists.
lpNetConnectInfo - This is a pointer to a NETCONNECTINFOSTRUCT structure
which is to be filled if the connection performance
of connection lpRemoteName can be determined.
Return Value:
NO_ERROR - Successful.
WN_NOT_CONNECTED - Connection could not be found.
WN_NONETWORK - Network is not present.
Other network errors.
--*/
{
DWORD status = NO_ERROR;
LPNETCONNECTINFOSTRUCT lpNetConnInfo =
(LPNETCONNECTINFOSTRUCT) lpNetConnectInfo;
NTSTATUS ntstatus;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
ACCESS_MASK DesiredAccess = SYNCHRONIZE | FILE_LIST_DIRECTORY;
//
// dfergus 19 Apr 2001 - #333280
// Init hRdr so test for null is valid
HANDLE hRdr = NULL;
WCHAR OpenString[] = L"\\Device\\Nwrdr\\*";
UNICODE_STRING OpenName;
UNICODE_STRING ConnectionName;
PNWR_REQUEST_PACKET Request = NULL;
ULONG BufferSize = 0;
ULONG RequestSize;
BOOL Impersonate = FALSE ;
UNREFERENCED_PARAMETER(Reserved);
UNREFERENCED_PARAMETER(dwBufferSize);
if (lpRemoteName == NULL)
{
return ERROR_INVALID_PARAMETER;
}
BufferSize = sizeof(NWR_REQUEST_PACKET) +
( ( wcslen(lpRemoteName) + 1 ) * sizeof(WCHAR) );
//
// Impersonate the client
//
if ((status = NwImpersonateClient()) != NO_ERROR)
{
goto ExitWithClose;
}
Impersonate = TRUE;
//
// Allocate buffer space.
//
Request = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT, BufferSize );
if ( Request == NULL )
{
KdPrint(("NWWORKSTATION: NwrGetConnectionPerformance LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
RtlInitUnicodeString( &OpenName, OpenString );
InitializeObjectAttributes( &ObjectAttributes,
&OpenName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
ntstatus = NtOpenFile( &hRdr,
DesiredAccess,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT );
if ( !NT_SUCCESS(ntstatus) )
{
status = RtlNtStatusToDosError(ntstatus);
goto ExitWithClose;
}
//
// Fill out the request packet for FSCTL_NWR_GET_CONN_PERFORMANCE.
//
RtlInitUnicodeString( &ConnectionName, lpRemoteName );
Request->Parameters.GetConnPerformance.RemoteNameLength =
ConnectionName.Length;
RtlCopyMemory( Request->Parameters.GetConnPerformance.RemoteName,
ConnectionName.Buffer,
ConnectionName.Length );
RequestSize = sizeof( NWR_REQUEST_PACKET ) + ConnectionName.Length;
ntstatus = NtFsControlFile( hRdr,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_NWR_GET_CONN_PERFORMANCE,
(PVOID) Request,
RequestSize,
NULL,
0 );
if ( !NT_SUCCESS( ntstatus ) )
{
status = RtlNtStatusToDosError(ntstatus);
goto ExitWithClose;
}
lpNetConnInfo->cbStructure = sizeof(NETCONNECTINFOSTRUCT);
lpNetConnInfo->dwFlags = Request->Parameters.GetConnPerformance.dwFlags;
lpNetConnInfo->dwSpeed = Request->Parameters.GetConnPerformance.dwSpeed;
lpNetConnInfo->dwDelay = Request->Parameters.GetConnPerformance.dwDelay;
lpNetConnInfo->dwOptDataSize =
Request->Parameters.GetConnPerformance.dwOptDataSize;
ExitWithClose:
if ( Request )
LocalFree( Request );
if ( Impersonate )
{
(void) NwRevertToSelf();
}
if ( hRdr )
NtClose( hRdr );
return status;
}
DWORD
NwAllocAndGetUncName(
IN LPWSTR LocalName,
IN DWORD LocalNameLength,
OUT LPWSTR *UncName
)
/*++
Routine Description:
This function calls an internal routine to ask the redirector for the
UNC name of a given DOS device name. It also allocates the output
buffer to hold the UNC name.
Arguments:
LocalName - Supplies the DOS device name.
LocalNameLength - Supplies the length of the DOS device name (chars).
UncName - Receives a pointer to the output buffer allocated by this
routine which contains the UNC name of the DOS device.
Return Value:
NO_ERROR - Operation was successful.
ERROR_NOT_ENOUGH_MEMORY - Could not allocate output buffer.
Other errors from the redirector.
--*/
{
DWORD status;
DWORD UncNameLength;
*UncName = (PVOID) LocalAlloc(
LMEM_ZEROINIT,
(MAX_PATH + 1) * sizeof(WCHAR)
);
if (*UncName == NULL) {
KdPrint(("NWWORKSTATION: NwAllocAndGetUncName LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
status = NwGetServerResource(
LocalName,
LocalNameLength,
*UncName,
MAX_PATH + 1,
&UncNameLength
);
if ((status == ERROR_MORE_DATA) || (status == ERROR_INSUFFICIENT_BUFFER)) {
//
// Our output buffer was too small. Try again.
//
(void) LocalFree((HLOCAL) *UncName);
*UncName = (PVOID) LocalAlloc(
LMEM_ZEROINIT,
UncNameLength * sizeof(WCHAR)
);
if (*UncName == NULL) {
KdPrint(("NWWORKSTATION: NwAllocAndGetUncName LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
status = NwGetServerResource(
LocalName,
LocalNameLength,
*UncName,
UncNameLength,
&UncNameLength
);
}
//
// callers will only free this if success.
//
if (status != NO_ERROR)
{
(void) LocalFree((HLOCAL) *UncName);
*UncName = NULL ;
}
return status;
}
DWORD
NwOpenHandleToDeleteConn(
IN LPWSTR UncName,
IN LPWSTR LocalName OPTIONAL,
IN DWORD UseForce,
IN BOOL IsStopWksta,
IN BOOL ImpersonatingClient
)
/*++
Routine Description:
This function deletes an active connection by opening a tree connection
handle to the connection first, and specifying this handle to the
redirector to delete. This is because the workstation service does
not keep any connection information.
Arguments:
UncName - Supplies the UNC name of the connection to delete.
LocalName - Supplies the DOS device name of the connection, if any.
UseForce - Supplies a flag which if TRUE specifies to tear down
the connection eventhough files are opened. If FALSE, the
connection is deleted only if there are no opened files.
IsStopWksta - Supplies a flag which if TRUE indicates that we must
delete the symbolic link, even when we have failed to delete the
connection in the redirector. As much as possible must be cleaned
up because the workstation service is stopping. A value of FALSE,
indicates that the delete is aborted if we cannot delete it in
the redirector.
ImpersonatingClient - Flag that indicates whether the thread has
called NwImpersonateClient. The gateway service functions don't
impersonate, where as the client service operations do.
Return Value:
NO_ERROR - Operation was successful.
ERROR_NOT_ENOUGH_MEMORY - Could not allocate output buffer.
Other errors from the redirector.
--*/
{
DWORD status;
NTSTATUS ntstatus ;
UNICODE_STRING TreeConnectStr;
HANDLE TreeConnection = NULL;
TreeConnectStr.Buffer = NULL;
//
// Create an NT-style tree connection name, either: \Device\Nwrdr\Server\Vol
// or \Device\Nwrdr\X:\Server\Vol, if LocalName is specified.
//
if ((status = NwCreateTreeConnectName(
UncName,
LocalName,
&TreeConnectStr
)) != NO_ERROR) {
return status;
}
ntstatus = NwCallNtOpenFile( &TreeConnection,
SYNCHRONIZE | DELETE,
&TreeConnectStr,
FILE_CREATE_TREE_CONNECTION
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DELETE_ON_CLOSE
);
//
// treat the 2 as the same in order to return nicer error to user
//
if (ntstatus == STATUS_OBJECT_NAME_INVALID)
ntstatus = STATUS_OBJECT_NAME_NOT_FOUND ;
status = NwMapStatus(ntstatus) ;
if (status == NO_ERROR) {
//
// Ask the redirector to delete the tree connection.
//
status = NwNukeConnection(
TreeConnection,
UseForce
);
(void) CloseHandle(TreeConnection);
}
if (ARGUMENT_PRESENT(LocalName) &&
(status == NO_ERROR || IsStopWksta))
{
//
// Delete the symbolic link we created.
//
NwDeleteSymbolicLink(
LocalName,
TreeConnectStr.Buffer,
NULL,
ImpersonatingClient
);
}
if (TreeConnectStr.Buffer != NULL) {
(void) LocalFree((HLOCAL) TreeConnectStr.Buffer);
}
return status;
}
VOID
DeleteAllConnections(
VOID
)
/*++
Routine Description:
This function deletes all active connections returned by the
redirector ENUMERATE_CONNECTIONS fsctl on workstation termination.
Arguments:
None.
Return Value:
None.
--*/
{
DWORD status;
NWWKSTA_CONTEXT_HANDLE EnumHandle;
LPNETRESOURCEW NetR = NULL;
DWORD BytesNeeded = 256;
DWORD EntriesRead;
status = NwrOpenEnumConnections(NULL, RESOURCETYPE_ANY, &EnumHandle);
if ( status != NO_ERROR )
return;
//
// Allocate buffer to get connection list.
//
if ((NetR = (LPVOID) LocalAlloc(
0,
BytesNeeded
)) == NULL) {
status = ERROR_NOT_ENOUGH_MEMORY;
goto CleanExit;
}
do {
status = NwEnumerateConnections(
&((LPNW_ENUM_CONTEXT) EnumHandle)->ResumeId,
(DWORD) -1,
(LPBYTE) NetR,
BytesNeeded,
&BytesNeeded,
&EntriesRead,
CONNTYPE_ANY,
NULL
);
if (status == NO_ERROR) {
DWORD i;
LPNETRESOURCEW SavePtr = NetR;
LPWSTR Local;
for (i = 0; i < EntriesRead; i++, NetR++) {
Local = NetR->lpLocalName;
if (NetR->lpLocalName && *(NetR->lpLocalName) == 0) {
Local = NULL;
}
(void) NwOpenHandleToDeleteConn(
NetR->lpRemoteName,
Local,
TRUE,
TRUE,
FALSE
);
}
NetR = SavePtr;
}
else if (status == WN_MORE_DATA) {
//
// Original buffer was too small. Free it and allocate
// the recommended size and then some to get as many
// entries as possible.
//
(void) LocalFree((HLOCAL) NetR);
BytesNeeded += NW_ENUM_EXTRA_BYTES;
if ((NetR = (LPVOID) LocalAlloc(
0,
BytesNeeded
)) == NULL) {
status = ERROR_NOT_ENOUGH_MEMORY;
goto CleanExit;
}
}
else {
// give up if see any other return code
break ;
}
} while (status != WN_NO_MORE_ENTRIES);
CleanExit:
(void) NwrCloseEnum(&EnumHandle);
if (NetR != NULL) {
(void) LocalFree((HLOCAL) NetR);
}
(void) NwDeleteAllInRegistry();
}
DWORD
NwCreateSymbolicLink(
IN LPWSTR Local,
IN LPWSTR TreeConnectStr,
IN BOOL bGateway,
IN BOOL ImpersonatingClient
)
/*++
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\NwRdr\Device:\Server\Volume\Directory.
Arguments:
Local - Supplies the local device name.
TreeConnectStr - Supplies the tree connection name string which is
the link target of the symbolick link object.
ImpersonatingClient - Flag that indicates whether the thread has
called NwImpersonateClient. The gateway service functions don't
impersonate, where as the client service operations do.
Return Value:
NO_ERROR or reason for failure.
--*/
{
WCHAR TempBuf[64];
LPWSTR Session = NULL; //Terminal Server Addition
NTSTATUS Status = NO_ERROR;
BOOL ResetToClient = FALSE;
DWORD LocalLength = wcslen(Local);
//
// Multiple session support
//
if (bGateway)
{
//Because this is Gateway connect, force sessionID 0
if (!DosPathToSessionPath( 0,
Local,
&Session ))
{
Status = GetLastError();
goto Exit;
}
}
else
{
Session = NwReturnSessionPath(Local);
if (Session == 0)
{
Status = GetLastError();
goto Exit;
}
}
if ( (NwLUIDDeviceMapsEnabled == FALSE) && ImpersonatingClient )
{
(void) NwRevertToSelf();
ResetToClient = TRUE;
}
if (LocalLength > 2)
{
LPWSTR UncName;
//
// Local device is LPTn:
//
//
// Check to see if we already have this UNC name mapped.
//
if (NwAllocAndGetUncName(
Local,
LocalLength,
&UncName
) == NO_ERROR)
{
LocalFree((HLOCAL) UncName);
Status = ERROR_ALREADY_ASSIGNED;
goto Exit;
}
}
else
{
//
// Local device is X:
//
if (! QueryDosDeviceW( Session,
TempBuf,
sizeof(TempBuf) / sizeof(WCHAR) ))
{
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.
//
Status = ERROR_ALREADY_ASSIGNED;
goto Exit;
}
}
else
{
//
// QueryDosDevice successfully an existing symbolic link--
// somebody is already using this device.
//
Status = ERROR_ALREADY_ASSIGNED;
goto Exit;
}
}
//
// Create a symbolic link object to the device we are redirecting
//
if (! DefineDosDeviceW(
DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM,
Session,
TreeConnectStr
))
{
Status = GetLastError();
goto Exit;
}
Exit:
if ( ResetToClient )
{
(void) NwImpersonateClient();
}
if (Session)
{
LocalFree(Session);
}
return Status;
}
VOID
NwDeleteSymbolicLink(
IN LPWSTR LocalDeviceName,
IN LPWSTR TreeConnectStr,
IN LPWSTR SessionDeviceName, //Terminal Server Addition
// This parameter is required because
// the device created is per session
IN BOOL ImpersonatingClient
)
/*++
Routine Description:
This function deletes the symbolic link we had created earlier for
the device.
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.
ImpersonatingClient - Flag that indicates whether the thread has
called NwImpersonateClient. The gateway service functions don't
impersonate, where as the client service operations do.
Return Value:
None.
--*/
{
BOOLEAN DeleteSession = FALSE;
BOOL ResetToClient = FALSE;
if (LocalDeviceName != NULL ||
SessionDeviceName != NULL) {
if (SessionDeviceName == NULL) {
SessionDeviceName = NwReturnSessionPath(LocalDeviceName);
if ( SessionDeviceName == NULL ) return;
DeleteSession = TRUE;
}
if ( (NwLUIDDeviceMapsEnabled == FALSE) && ImpersonatingClient )
{
(void) NwRevertToSelf();
ResetToClient = TRUE;
}
if (! DefineDosDeviceW(
DDD_REMOVE_DEFINITION |
DDD_RAW_TARGET_PATH |
DDD_EXACT_MATCH_ON_REMOVE |
DDD_NO_BROADCAST_SYSTEM,
// LocalDeviceName,
SessionDeviceName,
TreeConnectStr
))
{
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("NWWORKSTATION: DefineDosDevice DEL of %ws %ws returned %lu\n",
LocalDeviceName, TreeConnectStr, GetLastError()));
}
#endif
}
#if DBG
else {
IF_DEBUG(CONNECT) {
KdPrint(("NWWORKSTATION: DefineDosDevice DEL of %ws %ws returned successful\n",
LocalDeviceName, TreeConnectStr));
}
}
#endif
}
if ( SessionDeviceName && DeleteSession) {
LocalFree( SessionDeviceName );
}
if ( ResetToClient )
{
(void) NwImpersonateClient();
}
}
DWORD
NwCreateGWConnection(
IN LPWSTR RemoteName,
IN LPWSTR UserName,
IN LPWSTR Password,
IN BOOL KeepConnection
)
/*++
Routine Description:
This function creates a tree connection to the specified RemoteName
(UNC name). It is only used by the Gateway and DOES NOT impersonate.
Arguments:
RemoteName - Supplies the UNC name of the remote resource in the format
of Server\Volume\Directory. It must be a disk resource.
Return Value:
NO_ERROR - Operation was successful.
ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating internal work buffers.
WN_BAD_NETNAME - Remote resource name is invalid.
WN_BAD_LOCALNAME - Local DOS device name is invalid.
ERROR_BAD_NETPATH - The UNC name does not exist on the network.
ERROR_INVALID_PARAMETER - LPT or COM LocalName was specified.
Other errors from the redirector.
--*/
{
DWORD status;
LPWSTR Unc = NULL;
LPWSTR User = NULL;
UNICODE_STRING TreeConnectStr;
HANDLE TreeConnection;
TreeConnectStr.Buffer = NULL;
//
// Canonicalize the remote name, if it is not \\Server.
//
if ((status = NwLibCanonRemoteName(
NULL,
RemoteName,
&Unc, // Must be freed with LocalFree when done.
NULL
)) != NO_ERROR)
{
status = WN_BAD_NETNAME;
goto CleanExit;
}
if (UserName != NULL) {
//
// Canonicalize username
//
if ((status = NwLibCanonUserName(
UserName,
&User, // Must be freed with LocalFree when done.
NULL
)) != NO_ERROR) {
status = WN_BAD_VALUE;
goto CleanExit;
}
}
//
// Create an NT-style tree connection name
//
if ((status = NwCreateTreeConnectName(
Unc,
NULL,
&TreeConnectStr
)) != NO_ERROR)
{
goto CleanExit;
}
//
// Create the tree connection while impersonating the client so
// that redirector can get to caller's logon id.
//
status = NwOpenCreateConnection(
&TreeConnectStr,
User,
Password,
Unc,
SYNCHRONIZE | GENERIC_WRITE,
FILE_CREATE, // Fail if already exist
FILE_CREATE_TREE_CONNECTION |
FILE_SYNCHRONOUS_IO_NONALERT,
RESOURCETYPE_DISK,
&TreeConnection,
NULL
);
if (status != NO_ERROR) {
if ( (status == ERROR_NOT_CONNECTED)
|| (status == ERROR_FILE_NOT_FOUND )
)
{
status = ERROR_BAD_NETPATH;
}
}
else
{
//
// Just close the connection handle.
//
(void) NtClose(TreeConnection);
//
// delete the connect we just created. ignore this error.
//
if (!KeepConnection)
{
(void) NwOpenHandleToDeleteConn(
RemoteName,
NULL,
FALSE,
FALSE,
FALSE
);
}
}
CleanExit:
if (User != NULL) {
(void) LocalFree((HLOCAL) User);
}
if (Unc != NULL) {
(void) LocalFree((HLOCAL) Unc);
}
if (TreeConnectStr.Buffer != NULL) {
(void) LocalFree((HLOCAL) TreeConnectStr.Buffer);
}
return status;
}
DWORD
NwDeleteGWConnection(
IN LPWSTR ConnectionName
)
/*++
Routine Description:
This function deletes an existing connection.
Arguments:
ConnectionName - Supplies the local device name or UNC name which
specifies the connection to delete. If UNC name is specified,
the UNC connection must exist.
Return Value:
NO_ERROR - Operation was successful.
ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating internal work buffers.
WN_BAD_NETNAME - ConnectionName is invalid.
ERROR_BAD_NETPATH - The UNC name does not exist on the network.
ERROR_INVALID_PARAMETER - LPT or COM LocalName was specified.
Other errors from the redirector.
--*/
{
DWORD status;
LPWSTR ConnectName = NULL;
DWORD ConnectLength;
if (!ConnectionName || *ConnectionName == 0) {
return ERROR_INVALID_PARAMETER;
}
//
// See if it is a UNC name.
//
if ((status = NwLibCanonRemoteName(
NULL,
ConnectionName,
&ConnectName,
NULL
)) != NO_ERROR) {
return status;
}
//
// To delete a connection, a tree connection handle must be opened to
// it so that the handle can be specified to the redirector to delete
// the connection.
//
status = NwOpenHandleToDeleteConn(
ConnectName,
NULL,
TRUE,
FALSE,
FALSE
);
if ( status == ERROR_FILE_NOT_FOUND )
status = ERROR_BAD_NETPATH;
if (ConnectName != NULL) {
(void) LocalFree((HLOCAL) ConnectName);
}
return status;
}
DWORD
NwCreateConnection(
IN LPWSTR LocalName OPTIONAL,
IN LPWSTR RemoteName,
IN DWORD Type,
IN LPWSTR Password OPTIONAL,
IN LPWSTR UserName OPTIONAL
)
/*++
Routine Description:
This function creates a tree connection to the specified RemoteName
(UNC name) and maps it to the LocalName (local device name), if
it is specified. The password and user name are the credentials
used to create the connection, if specified; otherwise, the
interactive logged on user's credentials are used by default.
NOTE: This code used to be NwrCreateConnection, except that it used
to have the ImpersonateClient() call in it. Now this code is here, and
NwrCreateConnection calls this function and handles the client
impersonation there. The reason for this is to allow the print spooler
code to call this helper routine without calling Impersonate client a
second time, thus reverting the credentials to that of services.exe.
4/15/99 - GlennC - Assumption is that this routine is currently only
called while impersonating the client (NwImpersonateClient == TRUE)!!!
Arguments:
LocalName - Supplies the local device name to map to the created tree
connection. Only drive letter device names are accepted. (No
LPT or COM).
RemoteName - Supplies the UNC name of the remote resource in the format
of Server\Volume\Directory. It must be a disk resource.
Type - Supplies the connection type.
Password - Supplies the password to use to make the connection to the
server.
UserName - Supplies the user name to use to make the connection.
Return Value:
NO_ERROR - Operation was successful.
ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating internal work buffers.
WN_BAD_NETNAME - Remote resource name is invalid.
WN_BAD_LOCALNAME - Local DOS device name is invalid.
ERROR_BAD_NETPATH - The UNC name does not exist on the network.
ERROR_INVALID_PARAMETER - LPT or COM LocalName was specified.
Other errors from the redirector.
--*/
{
DWORD status;
DWORD LocalLength;
LPWSTR Local = NULL;
LPWSTR Unc = NULL;
LPWSTR User = NULL;
UNICODE_STRING TreeConnectStr;
UNICODE_STRING EncodedPassword;
HANDLE TreeConnection;
TreeConnectStr.Buffer = NULL;
EncodedPassword.Length = 0;
//
// If local device is an empty string, it will be treated as a pointer to
// NULL.
//
if (LocalName != NULL && *LocalName != 0) {
//
// Local device name is not NULL, canonicalize it
//
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("\nNWWORKSTATION: NwCreateConnection: LocalName %ws\n", LocalName));
}
#endif
if ((status = NwLibCanonLocalName(
LocalName,
&Local, // Must be freed with LocalFree when done.
&LocalLength
)) != NO_ERROR) {
return WN_BAD_LOCALNAME;
}
}
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("NWWORKSTATION: NwCreateConnection: RemoteName %ws\n", RemoteName));
}
#endif
//
// Canonicalize the remote name, if it is not \\Server.
//
status = NwLibCanonRemoteName(
Local,
RemoteName,
&Unc, // Must be freed with LocalFree when done.
NULL
);
if (status != NO_ERROR)
{
status = WN_BAD_NETNAME;
goto CleanExit;
}
//
// Canonicalize user name.
//
if (UserName != NULL) {
//
// Canonicalize username
//
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("NWWORKSTATION: NwCreateConnection: UserName %ws\n",
UserName));
}
#endif
if ((status = NwLibCanonUserName(
UserName,
&User, // Must be freed with LocalFree when done.
NULL
)) != NO_ERROR) {
#ifdef QFE_BUILD
//
// if not valid, just ignore the username. this works
// around MPR bug where if you pass say domain\user to NWRDR
// as first provider, and he throws it out, then the next one
// doesnt get a chance.
//
// TRACKING - this should be removed when MPR bug #4051 is fixed
// and all platforms we ship NWRDR have that fix.
//
UserName = NULL ;
status = NO_ERROR;
#else
status = WN_BAD_VALUE;
goto CleanExit;
#endif
}
}
//
// For password any syntax or length is accepted.
//
if (Password != NULL) {
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("NWWORKSTATION: NwCreateConnection: Password %ws\n",
Password));
}
#endif
//
// Decode the password
//
RtlInitUnicodeString(&EncodedPassword, Password);
RtlRunDecodeUnicodeString(NW_ENCODE_SEED3, &EncodedPassword);
}
//
// Create an NT-style tree connection name
//
if ((status = NwCreateTreeConnectName(
Unc,
Local,
&TreeConnectStr
)) != NO_ERROR) {
goto CleanExit;
}
if (Local != NULL) {
//
// Create symbolic link for local device name.
//
if ((status = NwCreateSymbolicLink(
Local,
TreeConnectStr.Buffer,
FALSE, //Not a gateway
TRUE // We are impersonating the client!
)) != NO_ERROR)
{
goto CleanExit;
}
}
//
// Create the tree connection while impersonating the client so
// that redirector can get to caller's logon id.
//
status = NwOpenCreateConnection(
&TreeConnectStr,
User,
Password,
Unc,
SYNCHRONIZE | GENERIC_WRITE,
FILE_CREATE, // Fail if already exist
FILE_CREATE_TREE_CONNECTION |
FILE_SYNCHRONOUS_IO_NONALERT,
Type,
&TreeConnection,
NULL
);
//
// If there's a problem creating the tree connection, remove symbolic
// link if any.
//
if (status != NO_ERROR) {
if ( (status == ERROR_NOT_CONNECTED) ||
(status == ERROR_FILE_NOT_FOUND) ||
(status == ERROR_INVALID_NAME) )
{
status = ERROR_BAD_NETPATH;
}
if ( status == ERROR_CONNECTION_INVALID )
{
status = WN_BAD_NETNAME;
}
//
// Delete the symbolic link we created.
//
NwDeleteSymbolicLink(
Local,
TreeConnectStr.Buffer,
NULL,
TRUE // We are impersonating the client!
);
}
else {
//
// Just close the connection handle.
//
(void) NtClose(TreeConnection);
}
CleanExit:
if (Local != NULL) {
(void) LocalFree((HLOCAL) Local);
}
if (Unc != NULL) {
(void) LocalFree((HLOCAL) Unc);
}
if (User != NULL) {
(void) LocalFree((HLOCAL) User);
}
if (TreeConnectStr.Buffer != NULL) {
(void) LocalFree((HLOCAL) TreeConnectStr.Buffer);
}
//
// Put the password back the way we found it.
//
if (EncodedPassword.Length != 0) {
UCHAR Seed = NW_ENCODE_SEED3;
RtlRunEncodeUnicodeString(&Seed, &EncodedPassword);
}
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("NWWORKSTATION: NwCreateConnection returns %lu\n", status));
}
#endif
return status;
}
//Terminal Server
DWORD
NwDeleteAllInRegistry(
VOID
)
/*++
Routine Description:
This function spins through the registry deleting the symbolic
links and closing all connections for all logons.
This is neccessary since the the users are not neccessarily in the
system context.
Arguments:
none
Return Value:
NO_ERROR or reason for failure.
--*/
{
LONG RegError;
HKEY InteractiveLogonKey;
DWORD Index = 0;
WCHAR LogonIdName[NW_MAX_LOGON_ID_LEN];
LUID LogonId;
HKEY OneLogonKey;
ULONG WinStationId = 0L;
PULONG pWinId = NULL;
RegError = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
NW_INTERACTIVE_LOGON_REGKEY,
REG_OPTION_NON_VOLATILE,
KEY_READ,
&InteractiveLogonKey
);
if (RegError == ERROR_SUCCESS) {
do {
RegError = RegEnumKeyW(
InteractiveLogonKey,
Index,
LogonIdName,
sizeof(LogonIdName) / sizeof(WCHAR)
);
if (RegError == ERROR_SUCCESS) {
//
// Got a logon id key.
//
NwWStrToLuid(LogonIdName, &LogonId);
//
// Open the <LogonIdName> key under Logon
//
RegError = RegOpenKeyExW(
InteractiveLogonKey,
LogonIdName,
REG_OPTION_NON_VOLATILE,
KEY_READ,
&OneLogonKey
);
if ( RegError != ERROR_SUCCESS ) {
KdPrint(("NWWORKSTATION: NwDeleteAllInRegistry: RegOpenKeyExW failed, Not interactive Logon: Error %d\n", GetLastError()));
} else {
//
// Read the WinStation value.
//
RegError = NwReadRegValue(
OneLogonKey,
NW_WINSTATION_VALUENAME,
(LPWSTR *) &pWinId
);
(void) RegCloseKey(OneLogonKey);
if ( RegError != NO_ERROR ) {
KdPrint(("NWWORKSTATION: NwDeleteAllInRegistry: Could not read SID from reg %lu\n", RegError));
continue;
} else {
if (pWinId != NULL) {
WinStationId = *pWinId;
(void) LocalFree((HLOCAL) pWinId);
}
NwDeleteUidSymLinks( LogonId, WinStationId );
}
}
} else if (RegError != ERROR_NO_MORE_ITEMS) {
KdPrint(("NWWORKSTATION: NwDeleteAllInRegistry failed to enum logon IDs RegError=%lu\n",
RegError));
}
Index++;
} while (RegError == ERROR_SUCCESS);
(void) RegCloseKey(InteractiveLogonKey);
}
NwCloseAllConnections();
return NO_ERROR;
}
DWORD
NwDeleteUidSymLinks(
IN LUID Uid,
IN ULONG WinStationId
)
/*++
Routine Description:
This function deletes all symbolic links for a given UID/Winstation.
Arguments:
None.
Return Value:
NO_ERROR
--*/
{
DWORD status= NO_ERROR;
NWWKSTA_CONTEXT_HANDLE EnumHandle;
LPNETRESOURCEW NetR = NULL;
DWORD BytesNeeded = 256;
DWORD EntriesRead;
WCHAR LocalUidCombo[256];
UNICODE_STRING TreeConnectStr;
status = NwrOpenEnumConnections(NULL, RESOURCETYPE_ANY, &EnumHandle);
if ( status != NO_ERROR )
return status;
//
// Allocate buffer to get connection list.
//
if ((NetR = (LPVOID) LocalAlloc(
0,
BytesNeeded
)) == NULL) {
status = ERROR_NOT_ENOUGH_MEMORY;
goto CleanExit;
}
do {
status = NwEnumerateConnections(
&((LPNW_ENUM_CONTEXT) EnumHandle)->ResumeId,
0xFFFFFFFF,
(LPBYTE) NetR,
BytesNeeded,
&BytesNeeded,
&EntriesRead,
CONNTYPE_ANY | CONNTYPE_UID,
&Uid
);
if (status == NO_ERROR) {
DWORD i;
LPNETRESOURCEW SavePtr = NetR;
LPWSTR Local;
for (i = 0; i < EntriesRead; i++, NetR++) {
Local = NetR->lpLocalName;
TreeConnectStr.Buffer = NULL;
if (NetR->lpLocalName && *(NetR->lpLocalName) == 0) {
Local = NULL;
} else if ((status = NwCreateTreeConnectName(
NetR->lpRemoteName,
Local,
&TreeConnectStr
)) != NO_ERROR) {
Local = NULL;
}
if ( Local != NULL ) {
swprintf(LocalUidCombo, L"%ws:%x", Local, WinStationId);
//
// Delete the symbolic link we created.
//
if (! DefineDosDeviceW(
DDD_REMOVE_DEFINITION |
DDD_RAW_TARGET_PATH |
DDD_EXACT_MATCH_ON_REMOVE |
0x80000000,
LocalUidCombo,
TreeConnectStr.Buffer
)) {
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("NWWORKSTATION: DefineDosDevice DEL of %ws %ws returned %lu\n",
LocalUidCombo, TreeConnectStr.Buffer, GetLastError()));
}
#endif
}
#if DBG
else {
IF_DEBUG(CONNECT) {
KdPrint(("NWWORKSTATION: DefineDosDevice DEL of %ws %ws returned successful\n",
LocalUidCombo, TreeConnectStr.Buffer));
}
}
#endif
if (TreeConnectStr.Buffer != NULL) {
(void) LocalFree((HLOCAL) TreeConnectStr.Buffer);
TreeConnectStr.Buffer = NULL;
}
}
}
NetR = SavePtr;
} else if (status == WN_MORE_DATA) {
//
// Original buffer was too small. Free it and allocate
// the recommended size and then some to get as many
// entries as possible.
//
(void) LocalFree((HLOCAL) NetR);
BytesNeeded += NW_ENUM_EXTRA_BYTES;
if ((NetR = (LPVOID) LocalAlloc(
0,
BytesNeeded
)) == NULL) {
status = ERROR_NOT_ENOUGH_MEMORY;
goto CleanExit;
}
} else {
// give up if see any other return code
break ;
}
} while (status != WN_NO_MORE_ENTRIES);
CleanExit:
(void) NwrCloseEnum(&EnumHandle);
if (NetR != NULL) {
(void) LocalFree((HLOCAL) NetR);
}
return NO_ERROR;
}
//
// Terminal Server Addition
//
LPTSTR
NwReturnSessionPath(
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 = NULL;
NTSTATUS status;
if ((status = NwGetSessionId(&SessionId)) != NO_ERROR) {
return NULL;
}
rc = DosPathToSessionPath(
SessionId,
LocalDeviceName,
&SessionDeviceName
);
if ( !rc ) {
return NULL;
}
return SessionDeviceName;
}