summaryrefslogtreecommitdiffstats
path: root/private/nw/svcdlls/nwwks/server/connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/svcdlls/nwwks/server/connect.c')
-rw-r--r--private/nw/svcdlls/nwwks/server/connect.c1688
1 files changed, 1688 insertions, 0 deletions
diff --git a/private/nw/svcdlls/nwwks/server/connect.c b/private/nw/svcdlls/nwwks/server/connect.c
new file mode 100644
index 000000000..546a745f2
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/connect.c
@@ -0,0 +1,1688 @@
+/*++
+
+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>
+
+#define NW_ENUM_EXTRA_BYTES 256
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+DWORD
+NwAllocAndGetUncName(
+ IN LPWSTR LocalName,
+ IN DWORD LocalNameLength,
+ OUT LPWSTR *UncName
+ );
+
+//-------------------------------------------------------------------//
+
+
+
+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.
+ //
+ 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);
+
+ 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 ((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
+ );
+
+ 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;
+ HANDLE hRdr;
+
+ WCHAR OpenString[] = L"\\Device\\Nwrdr\\*";
+ UNICODE_STRING OpenName;
+ UNICODE_STRING ConnectionName;
+
+ PNWR_REQUEST_PACKET Request;
+ ULONG BufferSize = sizeof(NWR_REQUEST_PACKET) +
+ ( ( wcslen(lpRemoteName) + 1 ) * sizeof(WCHAR) );
+ ULONG RequestSize;
+ BOOL Impersonate = FALSE ;
+
+ UNREFERENCED_PARAMETER(Reserved);
+ UNREFERENCED_PARAMETER(dwBufferSize);
+
+ //
+ // 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
+ )
+/*++
+
+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.
+
+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
+ );
+ }
+
+ 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,
+ 0xFFFFFFFF,
+ (LPBYTE) NetR,
+ BytesNeeded,
+ &BytesNeeded,
+ &EntriesRead,
+ CONNTYPE_ANY
+ );
+
+ 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
+ );
+ }
+
+ 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);
+ }
+}
+
+
+
+DWORD
+NwCreateSymbolicLink(
+ IN LPWSTR Local,
+ IN LPWSTR TreeConnectStr
+ )
+/*++
+
+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.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ WCHAR TempBuf[64];
+
+ DWORD LocalLength = wcslen(Local);
+
+
+ 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);
+ return ERROR_ALREADY_ASSIGNED;
+ }
+
+ }
+ else {
+
+ //
+ // Local device is X:
+ //
+
+ if (! QueryDosDeviceW(
+ Local,
+ 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
+ //
+ if (! DefineDosDeviceW(
+ DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM,
+ Local,
+ TreeConnectStr
+ )) {
+
+ return GetLastError();
+ }
+
+ return NO_ERROR;
+}
+
+
+
+VOID
+NwDeleteSymbolicLink(
+ IN LPWSTR LocalDeviceName,
+ IN LPWSTR TreeConnectStr
+ )
+/*++
+
+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.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ if (LocalDeviceName != NULL) {
+
+ if (! DefineDosDeviceW(
+ DDD_REMOVE_DEFINITION | DDD_RAW_TARGET_PATH |
+ DDD_EXACT_MATCH_ON_REMOVE | DDD_NO_BROADCAST_SYSTEM,
+ LocalDeviceName,
+ 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
+
+ }
+}
+
+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
+ );
+ }
+ }
+
+
+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
+ );
+
+ 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.
+
+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.
+ //
+ // BUGBUG - 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
+ )) != 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
+ );
+ }
+ 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;
+}
+
+