Пути подключенных дисков Windows 8 неправильно распознаются службой

Я столкнулся с проблемой в Windows 8, когда приложение / служба с повышенными привилегиями, олицетворяющая вошедшего в систему пользователя, не распознает правильно пути подключенных дисков.

У меня есть служба Windows, которую я использую для копирования файлов из / в разные исходные пути / места назначения, включая подключенные сетевые диски. Пути передаются в службу через файл xml. Затем служба считывает источник и назначение из xml и копирует файл. У меня никогда не было проблем с подключенными дисками в Vista и 7, поскольку служба всегда олицетворяет зарегистрированного пользователя, получая токен проводника, и все мои CreateFile, ReadFile и 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