0

I am facing a problem in Windows 8 where an elevated application/service that impersonates the logged in user does not recognize the mapped drives paths correctly.

I have a windows service that I use to copy files from/to different source paths/destinations including mapped network drives. The Paths are fed to the service through an xml file. The service then reads the source and destination from the xml and copies the file. I never had an issue with mapped drives in Vista and 7 as the service always impersonates the logged user by getting the explorer token and all my CreateFile, ReadFiles and WriteFile worked perfectly.

This is how I impersonate the user

first I get the session token using the following code

DWORD GetActiveSessionId(DWORD& ret)
{
    ret=0;
    DWORD active_session_id = WTSGetActiveConsoleSessionId();
    if (IsSessionActive(active_session_id)) 
    {
        return active_session_id;
    }

    DWORD console_session_ID = active_session_id;
    active_session_id = -2;
    WTS_SESSION_INFO* session_info = NULL;
    DWORD num_sessions = 0;
    if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1,
        &session_info, &num_sessions)) 
    {
            // Pick the first active session we can find
            for (DWORD i = 0 ; i < num_sessions; ++i) 
            {
                if (session_info[i].State == WTSActive) 
                {
                    // There is a user logged on to the WinStation associated with the
                    // session.
                    active_session_id = session_info[i].SessionId;
                    break;
                }
            }
            WTSFreeMemory(session_info);
            return active_session_id;
    }

    ret=::GetLastError();
    return -2;
}


BOOL GetSessionUserToken( HANDLE * phUserToken, DWORD& retCode )
{
    if( NULL == phUserToken )
    {
        return FALSE;
    }

    BOOL bRet = FALSE;
    HANDLE hImpersonationToken = NULL;

    BOOL bWin2K = FALSE;
    OSVERSIONINFOEX osv;
    ZeroMemory( & osv, sizeof( OSVERSIONINFOEX ) );
    osv.dwOSVersionInfoSize  = sizeof( OSVERSIONINFOEX );
    if( GetVersionEx( (OSVERSIONINFO*) & osv ) )
    {
        if( 0 == osv.dwMinorVersion && osv.dwMajorVersion == 5)
        {
            return FALSE;
        }
    }

    DWORD dwActiveSession= CGSSystem::GetActiveSessionId(retCode);

    if (dwActiveSession==GSInvalidSessionId)
        return FALSE;

    if( 0 != WTSQueryUserToken( dwActiveSession, & hImpersonationToken ) )
    {
        bRet = TRUE;
    }
    else
    {

    }


    DWORD neededSize = 0;
    HANDLE *realToken = new HANDLE;
    if(GetTokenInformation(hImpersonationToken, (::TOKEN_INFORMATION_CLASS) TokenLinkedToken, realToken, sizeof(HANDLE), &neededSize))
    {
        CloseHandle(hImpersonationToken);
        hImpersonationToken = *realToken;
    }
    DWORD lastError = GetLastError();
    delete realToken;


    if( TRUE == bRet )
    {
        bRet = DuplicateTokenEx( hImpersonationToken,
                                0,
                                NULL,
                                SecurityImpersonation,
                                TokenPrimary,
                                phUserToken );

        CloseHandle( hImpersonationToken );
    }

    return bRet;
}

Then I have my CopyFile function which is a thread. It is a huge function so I will only mention the important (impersonation/security) parts.

BOOL CopyFile(LPCTSTR source, LPCTSTR destination)
{

    //Some variables initializations
    //...

    HRESULT hrInternal = CoInitializeSecurity(
        NULL,                           //  Allow *all* VSS writers to communicate back!
        -1,                             //  Default COM authentication service
        NULL,                           //  Default COM authorization service
        NULL,                           //  reserved parameter
        RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  //  Strongest COM authentication level
        RPC_C_IMP_LEVEL_IDENTIFY,       //  Minimal impersonation abilities
        NULL,                           //  Default COM authentication settings
        EOAC_NONE,                      //  No special options
        NULL                            //  Reserved parameter
        );

    //Initialize security descriptors
    SECURITY_DESCRIPTOR    SD;
    SECURITY_ATTRIBUTES copyMutexAttrib;
    copyMutexAttrib.nLength = sizeof( SECURITY_ATTRIBUTES );
    copyMutexAttrib.lpSecurityDescriptor = & SD;
    copyMutexAttrib.bInheritHandle = TRUE;

    if(!InitializeSecurityDescriptor( & SD, SECURITY_DESCRIPTOR_REVISION ) )
    {
        //Error handling;           
    }

    // add a NULL disc. ACL to the security descriptor.
    //
    if( ! SetSecurityDescriptorDacl( & SD, TRUE, (PACL) NULL, FALSE ) )
    {
        //Error handling;           
    }

    HRESULT hr=S_OK;
    hr=ModifyThreadPrivilege( SE_BACKUP_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE  );
    if (FAILED(hr))
    {
        //Error Handling and logs
    }

    hr=S_OK;
    hr=ModifyThreadPrivilege( SE_TCB_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE  );
    if (FAILED(hr))
    {
        //Error Handling and logs
    }
    hr=ModifyThreadPrivilege( SE_IMPERSONATE_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE  );
    if (FAILED(hr))
    {
        //Error Handling and logs
    }

    hr=ModifyThreadPrivilege( SE_MANAGE_VOLUME_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE  );
    if (FAILED(hr))
    {
        //Error Handling and logs
    }

    hr=ModifyThreadPrivilege( SE_SYSTEM_PROFILE_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE  );
    if (FAILED(hr))
    {
        //Error Handling and logs
    }

    hr=ModifyThreadPrivilege( SE_DEBUG_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE  );
    if (FAILED(hr))
    {
        //Error Handling and logs
    }


    //Other variable initializations
    //...

    //Create the destination file
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;

    HANDLE hFile = ::CreateFile(destination, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, &sa,
                                CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH|FILE_FLAG_BACKUP_SEMANTICS, NULL);  //---> creates the file in the wrong location


}

and this is my ModifyThreadPrivilage code:

HRESULT ModifyThreadPrivilege(IN LPCTSTR szPrivilege,IN BOOL fEnable,IN BOOL OpenAsSelf)
    {
        HRESULT hr = S_OK;
        TOKEN_PRIVILEGES NewState;
        LUID luid;
        HANDLE hToken = NULL;

        // Open the process token for this process.
        if (!OpenThreadToken(GetCurrentThread(),
            TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
            OpenAsSelf,
            &hToken ))
        {
            int iLast=::GetLastError();
            if (iLast != ERROR_NO_TOKEN) 
            {
                return ERROR_FUNCTION_FAILED;
            }

            /*
            * No access token for the thread so impersonate the security context
            * of the process.
            */
            if (!ImpersonateSelf(SecurityImpersonation)) 
            {
                return ERROR_FUNCTION_FAILED;
            }

            if (!OpenThreadToken(GetCurrentThread(),
                TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
                FALSE,
                &hToken)) 
            {
                return ERROR_FUNCTION_FAILED;
            }   

        }

        // Get the local unique ID for the privilege.
        if ( !LookupPrivilegeValue( NULL,
            szPrivilege,
            &luid ))
        {
            CloseHandle( hToken );
            printf("Failed LookupPrivilegeValue\n");
            return ERROR_FUNCTION_FAILED;
        }

        // Assign values to the TOKEN_PRIVILEGE structure.
        NewState.PrivilegeCount = 1;
        NewState.Privileges[0].Luid = luid;
        NewState.Privileges[0].Attributes = 
            (fEnable ? SE_PRIVILEGE_ENABLED : 0);


        // Adjust the token privilege.
        if (!AdjustTokenPrivileges(hToken,
            FALSE,
            &NewState,
            0,
            NULL,
            NULL))
        {
            hr = ERROR_FUNCTION_FAILED;
        }

        // Close the handle.
        CloseHandle(hToken);

        return hr;
    }

In windows 8 and when the destination is a mapped drive like "Z:\MyFile.txt" it writes the file to the wrong location like so:

I have mapped network drive Z: which is mapped to \\nsa\public\myfolder1\subfolder\ the function writes the file to \\nsa\public\

I have never had such behavior in Windows Vista or 7 but it seems that MS has introduced some new privileges or securities that are causing such behavior.

I have noticed many people complaining about mapped drives in Windows 8, especially for elevated processes but all the solutions suggest to use UNC paths instead of the mapped drive letter.

I also noticed that enabling/disabling UAC has no effect on this.

Can someone explain how can I achieve my goal in copying the file?

Zaid Amir
  • 4,727
  • 6
  • 52
  • 101
  • This is pretty much [a duplicate](http://stackoverflow.com/questions/3098486/cannot-access-files-on-drive-mapped-network-share-from-a-windows-service) and it's surprising it worked on Windows 7. As you say, the solution is to use UNC paths rather than drive letters. Why don't you? – arx Mar 05 '13 at 13:27
  • As I said I never had an issue with this until now when I noticed this behavior on windows 8. I am sure there is a privilege or some tweak that I can apply to make it work on Windows 8. Converting to UNC is the last resort for me ATM as it will require many changes to the code. This issue is just a fraction of my problem – Zaid Amir Mar 05 '13 at 13:35

0 Answers0