Windows 8 картографирани пътища на дискове не са разпознати правилно от услугата

Изправен съм пред проблем в Windows 8, при който приложение/услуга с повишени права, които се представят за влезлия потребител, не разпознават правилно картографираните пътища на устройства.

Имам услуга за Windows, която използвам за копиране на файлове от/до различни изходни пътища/дестинации, включително картографирани мрежови устройства. Пътищата се подават към услугата чрез xml файл. След това услугата чете източника и дестинацията от xml и копира файла. Никога не съм имал проблем с картографираните устройства във Vista и 7, тъй като услугата винаги се представя за влезлия потребител, като получава токена на изследователя и всичките ми CreateFile, ReadFiles и WriteFile работеха перфектно.

Ето как се представям за потребителя

първо получавам токена на сесията, използвайки следния код

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;
}

След това имам моята функция CopyFile, която е нишка. Това е огромна функция, така че ще спомена само важните (имитиране/сигурност) части.

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


}

и това е моят код ModifyThreadPrivilage:

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;
    }

В Windows 8 и когато местоназначението е картографирано устройство като "Z:\MyFile.txt", той записва файла на грешно място така:

Имам картографиран мрежов диск Z:, който е картографиран към \\nsa\public\myfolder1\subfolder\, функцията записва файла в \\nsa\public\

Никога не съм имал подобно поведение в Windows Vista или 7, но изглежда, че MS е въвела някои нови привилегии или сигурност, които причиняват подобно поведение.

Забелязах, че много хора се оплакват от картографирани устройства в Windows 8, особено за повишени процеси, но всички решения предлагат да се използват UNC пътеки вместо картографираната буква на устройството.

Също така забелязах, че активирането/деактивирането на UAC няма ефект върху това.

Може ли някой да обясни как мога да постигна целта си да копирам файла?


person Zaid Amir    schedule 05.03.2013    source източник
comment
Това до голяма степен е дубликат и е изненадващо, че работи на Windows 7. Както казвате, решението е да се използват UNC пътища, а не букви на устройства. Защо не го правиш?   -  person arx    schedule 05.03.2013
comment
Както казах, никога не съм имал проблем с това до сега, когато забелязах това поведение на Windows 8. Сигурен съм, че има привилегия или някаква настройка, която мога да приложа, за да работи на Windows 8. Конвертирането в UNC е последната възможност за мен банкомат, тъй като ще изисква много промени в кода. Този проблем е само част от моя проблем   -  person Zaid Amir    schedule 05.03.2013