Re: Как скопиpовать пеpмиссии на файл?

From
Alex Fedotov ()
To
Serge Mikhaylov
Date
2002-02-24T11:58:50Z
Area
SU.WINDOWS.NT.PROG
From: "Alex Fedotov" <me@alexfedotov.com>

Serge Mikhaylov wrote:

> Киньте плиз пpимеpчиком. Мне не нужно pазбиpать эту таблицу, нужно только
> пpочитать ее и записать. МОжно и пpо овнеpшип тоже =) Спасибо.

В приложении в конце функция CopyFileSecurity, которая копирует указанные
части дескриптора безопасности одного файла на другой. Использоваться эта
функция может, например, таким образом:

CopyFileSecurity(_T("C:\\config.sys"), _T("C:\\autoexec.bat"),
                 DACL_SECURITY_INFORMATION);  // копирует DACL

CopyFileSecurity(_T("C:\\config.sys"), _T("C:\\autoexec.bat"),
                 OWNER_SECURITY_INFORMATION);  // копирует владельца

Можно указать и несколько флагов одновременно.

Для успешной работы функции нужны некоторые привилегии или права доступа к
файлам (что вполне естественно, в противном случае все бы копировали
разрешения направо и налево), а именно:

1) Чтобы скопировать владельца, нужно:
  a) иметь привилегию SeBackupPrivilege либо право доступа READ_CONTROL к
     исходному файлу, и
  б) иметь привилегию SeRestorePrivilege (в некоторых случаях достаточно
     права доступа WRITE_OWNER к выходному файлу).

2) Чтобы скопировать разрешения (DACL), нужно:
  a) иметь привилегию SeBackupPrivilege либо право доступа READ_CONTROL к
     исходному файлу, и
  б) иметь привилегию SeRestorePrivilege либо право доступа WRITE_DAC к
     выходному файлу.

3) Чтобы скопировать аудит, нужно:
  a) иметь привилегию SeSecurityPrivilege, и
  б) иметь привилегию SeBackupPrivilege либо право доступа READ_CONTROL к
     исходному файлу, и
  в) иметь привилегию SeRestorePrivilege либо право доступа READ_CONTROL к
     выходному файлу.

С практической точки зрения это означает, что администраторы могут
использовать эту функцию без ограничений, так называемые "backup operators"
могут копировать все, кроме SACL, а остальные пользователи могут копировать
только DACL, и то, если позволяют права доступа к конкретным файлам (имеется
в виду стандартное распределение привилегий сразу после установки NT).

Я проверял этот код на XP. Потестируй на остальных версиях, если не будет
проблем, я тогда себе на сайт его в качестве примера выложу.

Стоит также заметить, что существует также альтернативный способ решения
проблемы: с помощью функций BackupRead и BackupWrite можно скопировать поток
дескриптора безопасности из одного файла в другой. Исследованием этого
метода пусть займется кто-нибудь другой, а я лучше пойду что-нибудь полезное
сделаю.

-- Alex Fedotov

Приложение. Исходный код CopyFileSecurity.

// extended version of GetFileSecurity
DWORD ExGetFileSecurity(
    IN PCTSTR pszFileName,
    IN ULONG SecurityInformation,
    IN BOOL bBackupPrivilege,
    OUT PSECURITY_DESCRIPTOR * ppSecDesc
    )
{
    _ASSERTE(pszFileName != NULL);
    _ASSERTE(ppSecDesc != NULL);

    *ppSecDesc = NULL;

    DWORD dwAccess = READ_CONTROL;
    if (SecurityInformation & SACL_SECURITY_INFORMATION)
        dwAccess |= ACCESS_SYSTEM_SECURITY;

    DWORD dwFlags = FILE_ATTRIBUTE_NORMAL;
    if (bBackupPrivilege)
        dwFlags |= FILE_FLAG_BACKUP_SEMANTICS;

    // open file; use backup semantics if SeBackupPrivilege is available
    HANDLE hFile = CreateFile(pszFileName, dwAccess,
                              FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
                              OPEN_EXISTING, dwFlags, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return GetLastError();

    PSECURITY_DESCRIPTOR pSecDesc;
    DWORD cbNeeded;
    DWORD dwError;

    // determine size of the security descriptor
    if (!GetKernelObjectSecurity(hFile, SecurityInformation,
                                 NULL, 0, &cbNeeded))
    {
        dwError = GetLastError();
        if (dwError != ERROR_INSUFFICIENT_BUFFER)
        {
            CloseHandle(hFile);
            return dwError;
        }
    }

    // allocate memory for the security descriptor
    pSecDesc = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, cbNeeded);
    if (pSecDesc == NULL)
    {
        CloseHandle(hFile);
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    // retrieve security descriptor
    if (!GetKernelObjectSecurity(hFile, SecurityInformation, pSecDesc,
                          cbNeeded, &cbNeeded))
    {
        dwError = GetLastError();
        LocalFree((HLOCAL)pSecDesc);
    }
    else
    {
        dwError = ERROR_SUCCESS;
        *ppSecDesc = pSecDesc;
    }

    CloseHandle(hFile);
    return dwError;
}

// extended version of SetFileSecurity
DWORD ExSetFileSecurity(
    IN PCTSTR pszFileName,
    IN ULONG SecurityInformation,
    IN BOOL bRestorePrivilege,
    IN PSECURITY_DESCRIPTOR pSecDesc
    )
{
    _ASSERTE(pszFileName != NULL);
    _ASSERTE(pSecDesc != NULL);

    DWORD dwAccess = 0;
    if (SecurityInformation & OWNER_SECURITY_INFORMATION)
        dwAccess |= WRITE_OWNER;
    if (SecurityInformation & GROUP_SECURITY_INFORMATION)
        dwAccess |= WRITE_OWNER;
    if (SecurityInformation & DACL_SECURITY_INFORMATION)
        dwAccess |= WRITE_DAC;
    if (SecurityInformation & SACL_SECURITY_INFORMATION)
        dwAccess |= ACCESS_SYSTEM_SECURITY;

    DWORD dwFlags = FILE_ATTRIBUTE_NORMAL;
    if (bRestorePrivilege)
        dwFlags |= FILE_FLAG_BACKUP_SEMANTICS;

    // open file; use backup semantics if SeRestorePrivilege is available
    HANDLE hFile = CreateFile(pszFileName, dwAccess,
                              FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
                              OPEN_EXISTING, dwFlags, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return GetLastError();

    DWORD dwError = ERROR_SUCCESS;

    // assign file security
    if (!SetKernelObjectSecurity(hFile, SecurityInformation, pSecDesc))
        dwError = GetLastError();

    CloseHandle(hFile);
    return dwError;
}

// enables or disables a privilege
BOOL EnablePrivilege(
    IN PCTSTR pszPrivilegeName,
    IN BOOL bEnable,
    IN PBOOL pbPreviousState
    )
{
    _ASSERTE(pszPrivilegeName != NULL);

    TOKEN_PRIVILEGES Priv, PrivOld;
    DWORD cbPriv = sizeof(PrivOld);
    HANDLE hToken = NULL;

    if (pbPreviousState != NULL)
        *pbPreviousState = FALSE;

    // obtain token of the current thread
    if (!OpenThreadToken(GetCurrentThread(),
                         TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES,
                         FALSE, &hToken))
    {
        if (GetLastError() != ERROR_NO_TOKEN)
            return FALSE;

        // revert to process token
        if (!OpenProcessToken(GetCurrentProcess(),
                              TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES,
                              &hToken))
            return FALSE;
    }

    Priv.PrivilegeCount = 1;
    if (bEnable)
        Priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        Priv.Privileges[0].Attributes = 0;

    LookupPrivilegeValue(NULL, pszPrivilegeName, &Priv.Privileges[0].Luid);

    // try to enable the privilege
    if (!AdjustTokenPrivileges(hToken, FALSE, &Priv, sizeof(Priv),
                               &PrivOld, &cbPriv))
    {
        DWORD dwError = GetLastError();
        CloseHandle(hToken);
        return SetLastError(dwError), FALSE;
    }

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
    {
        // the privilege is not present in the caller's token
        CloseHandle(hToken);
        return SetLastError(ERROR_PRIVILEGE_NOT_HELD), FALSE;
    }

    CloseHandle(hToken);

    if (pbPreviousState != NULL)
    {
        *pbPreviousState =
             (PrivOld.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED) != 0;
    }

    return TRUE;
}

// copies file security
BOOL CopyFileSecurity(
    IN PCTSTR pszSourceFileName,
    IN PCTSTR pszTargetFileName,
    IN ULONG SecurityInformation
    )
{
    _ASSERTE(pszSourceFileName != NULL);
    _ASSERTE(pszTargetFileName != NULL);

    DWORD dwRes = ERROR_SUCCESS;
    BOOL bPriv, bOldPriv;
    BOOL bSaclPrivilege;

    PSECURITY_DESCRIPTOR pSecDesc;

    // enable SeSecurityPrivilege if we want to manipulate SACL
    if (SecurityInformation & SACL_SECURITY_INFORMATION)
        EnablePrivilege(SE_SECURITY_NAME, TRUE, &bSaclPrivilege);

    // enable SeBackupPrivilege
    bPriv = EnablePrivilege(SE_BACKUP_NAME, TRUE, &bOldPriv);

    // get source file security descriptor
    dwRes = ExGetFileSecurity(pszSourceFileName, SecurityInformation,
                              bPriv, &pSecDesc);

    // restore original state of SeBackupPrivilege
    EnablePrivilege(SE_BACKUP_NAME, bOldPriv, NULL);

    if (dwRes != ERROR_SUCCESS)
    {
        // restore original state of SeSecurityPrivilege
        if (SecurityInformation & SACL_SECURITY_INFORMATION)
            EnablePrivilege(SE_SECURITY_NAME, bSaclPrivilege, NULL);

        return SetLastError(dwRes), FALSE;
    }

    // enable SeRestorePrivilege
    bPriv = EnablePrivilege(SE_RESTORE_NAME, TRUE, &bOldPriv);

    // assign security information to the target file
    dwRes = ExSetFileSecurity(pszTargetFileName, SecurityInformation,
                              bPriv, pSecDesc);

    // restore original state of SeRestorePrivilege
    EnablePrivilege(SE_RESTORE_NAME, bOldPriv, NULL);

    // restore original state of SeSecurityPrivilege
    if (SecurityInformation & SACL_SECURITY_INFORMATION)
        EnablePrivilege(SE_SECURITY_NAME, bSaclPrivilege, NULL);

    // free security descriptor
    LocalFree((HLOCAL)pSecDesc);

    if (dwRes != ERROR_SUCCESS)
        return SetLastError(dwRes), FALSE;

    return TRUE;
}


--- ifmail v.2.15dev5
 * Origin: Demos online service (2:5020/400)