1

I'm trying to modify Windows access rights to a file in a way that only the owner (not even other Administrators) can access the file. Somewhat the equivalent of unix chmod 700 file.

I've played with denying rights to the general group (EVERYONE, ADMINISTRATORS) and granting them to the current user, but the current user always also loses the rights.

I tried to change the order (eas[0], eas[1]) and stuff, but without success.

Ideas anyone?

    EXPLICIT_ACCESSA ea= { 0, }, eas[5]= { { 0, }, };
    PACL pacl= 0;

    ea.grfAccessPermissions = GENERIC_ALL;
    ea.grfAccessMode = DENY_ACCESS ;
    ea.grfInheritance = NO_INHERITANCE;
    ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
    ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
    ea.Trustee.ptstrName = "EVERYONE";
    eas[0]= ea;


    ea.grfAccessPermissions = GENERIC_ALL;
    ea.grfAccessMode = GRANT_ACCESS ;
    ea.grfInheritance = NO_INHERITANCE;
    ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
    ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
    ea.Trustee.ptstrName = "CURRENT_USER";
    eas[1]= ea;

    rc= SetEntriesInAcl(2, &eas[0], NULL, &pacl);

    rc= SetNamedSecurityInfoA((LPSTR)filename, SE_FILE_OBJECT,
                                    DACL_SECURITY_INFORMATION,
                                    NULL, NULL, pacl, NULL);
4
  • Can't the admin just take ownership? Commented Feb 9, 2017 at 19:28
  • any who have SE_TAKE_OWNERSHIP_PRIVILEGE can do this. admin usually (by default) have this. but from UI this (take ownership) need do explicity Commented Feb 9, 2017 at 19:33
  • 2
    Deny overrides permit, so if you have a "deny all" then no one can access it at all. Commented Feb 9, 2017 at 20:44
  • @all. Yes, admin can take ownership. But I started doing this, and then I couldn't get it to work the way I wanted and it sort of turned into a research project, even if it has little practical additional security. Commented Feb 10, 2017 at 9:41

3 Answers 3

4

In most cases, deny entries take precedent over allow entries. Since access is denied by default, you don't need the deny entry, however you will need to disable inherited permissions. You can do this by using the PROTECTED_DACL_SECURITY_INFORMATION flag.

#include <Windows.h>
#include <Aclapi.h>

#include <stdio.h>

int main(int argc, char ** argv)
{
    EXPLICIT_ACCESS eas[1];
    PACL pacl = 0;
    DWORD rc;

    eas[0].grfAccessPermissions = GENERIC_ALL;
    eas[0].grfAccessMode = GRANT_ACCESS;
    eas[0].grfInheritance = NO_INHERITANCE;
    eas[0].Trustee.TrusteeForm = TRUSTEE_IS_NAME;
    eas[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
    eas[0].Trustee.ptstrName = L"CURRENT_USER";

    rc = SetEntriesInAcl(1, &eas[0], NULL, &pacl);
    if (rc != ERROR_SUCCESS)
    {
        printf("SetEntriesInAcl: %u\n", rc);
        return 1;
    }

    rc = SetNamedSecurityInfo(L"C:\\working\\test.txt", SE_FILE_OBJECT, 
             DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, 
             NULL, NULL, pacl, NULL);
    if (rc != ERROR_SUCCESS)
    {
        printf("SetNamedSecurityInfo: %u\n", rc);
        return 1;
    }

    printf("OK!\n");
    return 0;
}

Note that an administrator can always reset the permissions in order to gain access to the file. If you really need to protect the data against other administrators you'll have to encrypt it. (And hope nobody installs a keylogger to steal your encryption password.)

Sign up to request clarification or add additional context in comments.

2 Comments

I will try your code but I think I've tried this already. The problem was, that without the deny, the inhertited access, administrators get access through inheritance from the enclosing folder. (I know that admins can reset the flags by taking ownership.)
Ah, I see this is doing what I expected (probably via the extra flag). Missed your initial comment about disabling "inheritance", which was my essential problem I guess. Thanks +1 and check.
1

While technically possible, it doesn't buy you much in the way of securing an object. A local administrator may not immediately be able to access a file, given a DACL with appropriate entries, but they can always take ownership of any object in the system. This grants them full control over the object, and they can manipulate its DACL and SACL.

3 Comments

I know but it still a small extra layer of security (and at some point it turned into a research project, because I couldn't get it to work the way I wanted, even if there's little practical benefit).
@Anachronism: There is no extra layer of security, however small. It may take an additional step to access an object for a local administrator, but that's it. In case you are looking for a good introduction into this topic, see Permissions: A Primer, or: DACL, SACL, Owner, SID and ACE Explained. It will teach you everything you need to know to understand, why Harry Johnston's answer works.
@ll. Acutally I think there is. If a malware isn't aware of the extra necessary steps (taking ownership), it might just fail. Or if another person hasn't enough time. It's slim, marginal, but it's not nothing. And as I said, it turned in to a bit of a research project anyway.
1

formally you need next code

DWORD demo(PCWSTR filename)
{
    EXPLICIT_ACCESS ea= { 
        GENERIC_ALL, 
        GRANT_ACCESS, 
        NO_INHERITANCE, 
        { 0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME, TRUSTEE_IS_USER, L"CURRENT_USER"}
    };
    PACL pacl;

    DWORD err = SetEntriesInAcl(1, &ea, NULL, &pacl);

    if (!err)
    {
        err = ERROR_GEN_FAILURE;

        if (pacl->AceCount == 1)
        {
            union {
                PVOID pvAce;
                PACE_HEADER Header;
                PACCESS_ALLOWED_ACE pAce;
            };

            if (GetAce(pacl, 0, &pvAce) && Header->AceType == ACCESS_ALLOWED_ACE_TYPE)
            {
                HANDLE hFile = CreateFile(filename, WRITE_DAC|WRITE_OWNER, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

                if (hFile != INVALID_HANDLE_VALUE)
                {
                    SECURITY_DESCRIPTOR sd;
                    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
                    SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE);
                    SetSecurityDescriptorOwner(&sd, &pAce->SidStart, FALSE);
                    SetSecurityDescriptorControl(&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED);

                    err = SetKernelObjectSecurity(hFile, DACL_SECURITY_INFORMATION|OWNER_SECURITY_INFORMATION, &sd)
                        ? NOERROR : GetLastError();

                    CloseHandle(hFile);
                }
                else
                {
                    err = GetLastError();
                }
            }
        }

        LocalFree(pacl);
    }

    return err;
}

note line

SetSecurityDescriptorControl(&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED);

with this code DACL for file will be have only one entry - GENERIC_ALL for current user. and all what explicitly not allowed in DACL - denied. but of course if user have SE_TAKE_OWNERSHIP_PRIVILEGE privilege - you can open file with WRITE_OWNER access and set self as owner. after this you can open file with WRITE_DAC and change DACL


as noted @Harry Johnston code can be and shorter if use SetNamedSecurityInfo

DWORD demo(PCWSTR filename)
{
    EXPLICIT_ACCESS ea= { 
        GENERIC_ALL, 
        GRANT_ACCESS, 
        NO_INHERITANCE, 
        { 0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME, TRUSTEE_IS_USER, L"CURRENT_USER"}
    };
    PACL pacl;

    DWORD err = SetEntriesInAcl(1, &ea, NULL, &pacl);

    if (!err)
    {
        err = ERROR_GEN_FAILURE;

        if (pacl->AceCount == 1)
        {
            union {
                PVOID pvAce;
                PACE_HEADER Header;
                PACCESS_ALLOWED_ACE pAce;
            };

            if (GetAce(pacl, 0, &pvAce) && Header->AceType == ACCESS_ALLOWED_ACE_TYPE)
            {
                err = SetNamedSecurityInfo((PWSTR)filename, SE_FILE_OBJECT, 
                    DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION |PROTECTED_DACL_SECURITY_INFORMATION, 
                    &pAce->SidStart, NULL, pacl, NULL);
            }
        }

        LocalFree(pacl);
    }

    return err;
}

4 Comments

SetNamedSecurityInfo accepts the PROTECTED_DACL_SECURITY_INFORMATION flag.
@HarryJohnston yes, my mistake. I missed this
Thanks +1. (prefer Harry's version, suits my case better, the file exists already, it's written via fopen() ...)
@Anachronism - agree. only one different - i change not only DACL but owner too. it make +1 step for change permission (need first owner change)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.