Сопоставление маски доступа в DACL для ключей CNG

(Примечание: ИМО вопрос в основном касается WinAPI и DACL, а не CNG, поэтому, пожалуйста, читайте дальше!)

В настоящее время я пытаюсь модифицировать пример поставщика хранилища ключей CNG из пакета Microsoft Cryptographic Provider Development Kit таким образом, чтобы он не сохранял ключи в отдельных файлах. Однако у меня проблемы с дескрипторами безопасности, которые могут быть назначены закрытым ключам.

В оснастке «Сертификаты» консоли управления Windows Server можно управлять закрытыми ключами сертификатов, т. е. можно изменить владельца, DACL и SACL ключа, что приводит к вызову NCryptSetProperty с дескриптором безопасности в качестве параметра. Для DACL оснастка позволяет разрешать/запрещать только «полный доступ» или «чтение», что приводит к установке бита GENERIC_ALL или GENERIC_READ в маске доступа ACE.

Как я понял, эти общие биты должны быть сопоставлены с правами конкретного приложения, иначе AccessCheck не будет работать. Но мне действительно нужно сделать это вручную???

CreatePrivateObjectSecurity+SetPrivateObjectSecurity не всегда работает, так как CreatePrivateObjectSecurity очень разборчив в отношении владельца и группы во входном дескрипторе безопасности. Более того, при применении сопоставления общие биты в маске доступа сбрасываются, в результате чего оснастка показывает неверные настройки (как я уже сказал, оснастка учитывает только биты GA и GR при отображении текущих разрешений).

Кажется, мне не хватает некоторых частей здесь...


person dannyM    schedule 05.12.2016    source источник


Ответы (1)


в вашем CPSetProvParam реализации для PP_KEYSET_SEC_DESCR вы получили адрес SECURITY_DESCRIPTOR, который вам нужно каким-то образом применить к вашему хранилищу закрытых ключей. если ваше хранилище основано на файле(ах) или ключе(ах) реестра (в принципе любой тип объекта ядра, но что еще можно здесь использовать?) вам нужно вызвать SetKernelObjectSecurity с файлом или ключом HANDLE (который должен иметь доступ WRITE_DAC) (может быть несколько раз если вы говорите, что у вас есть несколько файлов для хранения одного ключа). в ядре ОБЩИЙ доступ к объекту будет автоматически преобразован в права, специфичные для объекта.

если ваша реализация хранилища не прямая на основе какого-то объекта ядра, а кастомная - вам нужно самому в этот момент преобразовать ОБЩИЙ доступ (маска 0xF0000000) в конкретные права доступа (маска 0x0000FFFF)

__________________ РЕДАКТИРОВАТЬ ____________________

после дополнительной проверки я обнаружил, что провайдер должен не только преобразовывать общий доступ в конкретный в CPSetProvParam, но также преобразовывать конкретные в общие в CPGetProvParam несмотря на то, что это прямо не указано в документации.

вот как MS_ENHANCED_PROV (реализовано в rsaenh.dll) примерно так:

void CheckAndChangeAccessMask(PSECURITY_DESCRIPTOR SecurityDescriptor)
{
    BOOL bDaclPresent, bDaclDefaulted;
    PACL Dacl;
    ACL_SIZE_INFORMATION asi;

    if (
        GetSecurityDescriptorDacl(SecurityDescriptor, &bDaclPresent, &Dacl, &bDaclDefaulted) 
        &&
        bDaclPresent
        &&
        Dacl
        &&
        GetAclInformation(Dacl, &asi, sizeof(asi), AclSizeInformation)
        &&
        asi.AceCount
        )
    {

        union{
            PVOID pAce;
            PACE_HEADER pah;
            PACCESS_ALLOWED_ACE paa;
        };

        do 
        {
            if (GetAce(Dacl, --asi.AceCount, &pAce))
            {
                switch (pah->AceType)
                {
                case ACCESS_ALLOWED_ACE_TYPE:
                case ACCESS_DENIED_ACE_TYPE:
                    ACCESS_MASK Mask = paa->Mask, Gen_Mask = 0;

                    if (Mask & FILE_READ_DATA)
                    {
                        Gen_Mask |= GENERIC_READ;
                    }

                    if (Mask & FILE_WRITE_DATA)
                    {
                        Gen_Mask |= GENERIC_ALL;
                    }

                    paa->Mask = Gen_Mask;
                    break;
                }
            }
        } while (asi.AceCount);
    }
}

поэтому FILE_READ_DATA преобразуется в GENERIC_READ, а FILE_WRITE_DATA в GENERIC_ALL (это именно алгоритм) - однако вы можете сами посмотреть код rsaenh.CheckAndChangeAccessMask (имя из символов pdb)

rsaenh сначала получите SD из файла с помощью GetNamedSecurityInfoW (SE_FILE_OBJECT), а затем преобразовать его в общий доступ. здесь график вызовов и измененный DACL (в правом верхнем углу измененный ACCESS_MASK выделен красным цветом) введите здесь описание изображения

person RbMm    schedule 06.12.2016
comment
Как я писал выше: я не применяю полученный дескриптор безопасности к файлу или любому другому объекту ядра, а должен хранить его внутри. На самом деле, я стараюсь хранить все данные ключа (открытый и закрытый ключ, а также свойства!) в базе данных. Следовательно, возможны только функции *PrivateObjectSecurity, но они не работают (s.a.). - person dannyM; 07.12.2016
comment
Сопоставление общих прав с определенными правами только также неверно, поскольку, когда CPGetProvParam/NCryptGetPropertyFn должен вернуть SD, мне нужно будет снова преобразовать его. Причина в том, что консоль управления Windows Server только распознает ОБЩИЕ права. Кажется, здесь не хватает какой-то функциональности... - person dannyM; 07.12.2016
comment
@dannyM - извините, я был неправ - отредактируйте ответ самостоятельно. CP действительно нужно преобразовать в общую маску в CPGetProvParam - я описываю это в отредактированном ответе - person RbMm; 07.12.2016