/*++ 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 #include #include #include #include #include #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 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; }