1

The following code fails when trying to logically extend my Windows 8.1 file with SetFileVaildData().

The returned Windows error code and message is:

ERROR_PRIVILEGE_NOT_HELD 1314 (0x522) A required privilege is not held by the client.

I'm running the code as Administrator and I have asserted that the process indeed has the SE_MANAGE_VOLUME_NAME privilege using OpenProcessToken() and GetTokenInformation().

// SetFileValidData_test.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <Windows.h>

bool ProcessHasSeManageVolumePrivilege();

int _tmain(int argc, _TCHAR* argv[])
{
    // Set access methods
    DWORD accessMethods = GENERIC_READ | GENERIC_WRITE;

    // Set share modes
    DWORD shareModes = 0;

    // Set security attributes
    LPSECURITY_ATTRIBUTES secAttr = NULL;

    // Set creation disposition
    DWORD creationDispositions = CREATE_ALWAYS;

    // Set file flags
    DWORD fileFlags = 0;

    // Set template
    HANDLE templateFile = NULL;

    if (!ProcessHasSeManageVolumePrivilege())
    {
        // Missing privilege to continue
        std::cerr << "Process is missing the required SE_MANAGE_VOLUME_NAME (\"SeManageVolumePrivilege\") privilege." << std::endl;
        return -1;
    }

    // Create the file
    HANDLE filehandle = CreateFile(
        L"testfile.tmp",
        accessMethods,
        shareModes,
        secAttr,
        creationDispositions,
        fileFlags,
        templateFile);
    if (filehandle == INVALID_HANDLE_VALUE)
    {
        // Error
        std::cerr << "CreateFile() failed with error #" << GetLastError() << "." << std::endl;
        return -1;
    }

    // Extend the file to 1 MB
    if (!SetFileValidData(filehandle, 1024*1024))
    {
        // Error
        std::cerr << "SetFileValidData() failed with error #" << GetLastError() << "." << std::endl;
        return -1;
    }

    std::cout << "File was logically extended successfully!" << std::endl 
        << "Press a key to quit..." << std::endl;
    getchar();

    return 0;
}

bool ProcessHasSeManageVolumePrivilege()
{
    HANDLE token;
    void* tpv;
    TOKEN_PRIVILEGES* tp;
    DWORD rl;
    bool hasPrivilege = false;

    std::cout << "Asserting process has the \"SeManageVolumePrivilege\" privilege:" << std::endl;

    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token))
    {
        std::cerr << "OpenProcessToken() failed with error #" << GetLastError() << "." << std::endl;
        return false;
    }

    if (!GetTokenInformation(token, TokenPrivileges, NULL, 0, &rl))
    {
        DWORD dw = GetLastError();
        if (dw != ERROR_INSUFFICIENT_BUFFER)
        {
            std::cerr << "GetTokenInformation() failed with error #" << GetLastError() << "." << std::endl;
            return false;
        }
    }

    tpv = malloc(rl + 10);
    if (!tpv) throw std::bad_alloc();

    ZeroMemory(tpv, rl + 10);

    if (!GetTokenInformation(token, TokenPrivileges, tpv, rl, &rl))
    {
        std::cerr << "GetTokenInformation() failed with error #" << GetLastError() << "." << std::endl;
        return false;
    }


    tp = (TOKEN_PRIVILEGES*)tpv;
    for (DWORD i = 0; i < tp->PrivilegeCount; i++)
    {
        const int nLen = 100;
        TCHAR bufname[nLen];
        DWORD bufsize = nLen;

        try
        {
            LookupPrivilegeName(NULL, &tp->Privileges[i].Luid, bufname, &bufsize);
            bufname[nLen - 1] = '\0';
            std::wcout << "\t" << bufname;
            if (wcscmp(L"SeManageVolumePrivilege", bufname) == 0)
            {
                std::cout << " ... YES! Found it!" << std::endl;
                hasPrivilege = true;
                break;
            }
            else std::cout << " ... no" << std::endl;
        }
        catch (...)
        {
            // Clean up before re-throwing exception        
            free(tpv);
            CloseHandle(token);
            throw;
        }
    }

    free(tpv);
    CloseHandle(token);
    token = NULL;

    return hasPrivilege;
}

Result:

C:\dev\SetFileValidData_test\Debug> SetFileValidData_test.exe
Asserting process has the "SeManageVolumePrivilege" privilege:
        SeIncreaseQuotaPrivilege ... no
        SeSecurityPrivilege ... no
        SeTakeOwnershipPrivilege ... no
        SeLoadDriverPrivilege ... no
        SeSystemProfilePrivilege ... no
        SeSystemtimePrivilege ... no
        SeProfileSingleProcessPrivilege ... no
        SeIncreaseBasePriorityPrivilege ... no
        SeCreatePagefilePrivilege ... no
        SeBackupPrivilege ... no
        SeRestorePrivilege ... no
        SeShutdownPrivilege ... no
        SeDebugPrivilege ... no
        SeSystemEnvironmentPrivilege ... no
        SeChangeNotifyPrivilege ... no
        SeRemoteShutdownPrivilege ... no
        SeUndockPrivilege ... no
        SeManageVolumePrivilege ... YES! Found it!
SetFileValidData() failed with error #1314.

C:\dev\SetFileValidData_test\Debug>
  • 2
    So you are surely not actually running with admin privileges. You never mentioned taking care of the UAC manifest – Hans Passant Mar 10 '14 at 08:58
  • @HansPassant I'm positive, I've updated the post and added my `ProcessHasSeManageVolumePrivilege()` method that asserts that the running process has the `SE_MANAGE_VOLUME_NAME (\"SeManageVolumePrivilege\")` privilege. When it comes to the mentioned "UAC manifest" I have no idea what you mean; do I have to take care of some UAC issues also? –  Mar 10 '14 at 09:28
  • @HansPassant I just remembered that my Windows 8.1 is hardened, this probably has something to do with my quandary... But what? –  Mar 10 '14 at 10:17
  • Do you actually **need** to do this? There's a reason why you need privilegues for this operation (contents not initialized, so you might see stale secret data), and there exist alternative ways of achieving the same. If `SetEndOfFile` is not acceptable because it can block for a while, create a memory mapping that exceeds the file's size. Always works reliably for me, and takes "zero time", not exactly sure _how it works_, presumably Windows marks parts of the file as "zeroed" and provides pages from the zero pool as you map them? Either way, it _does_ work, and without privilegues :-) – Damon Mar 10 '14 at 13:09
  • @Damon Good question. Supposedly there is a significant performance gain in database system (like ours) when _logically_ extending the database files as they grow instead of _physically_ extending the files which requires additional I/O operations like zero padding. I haven't tested the claims yet but it sounds reasonable. We don't use sparse files, so I believe they have to be zero padded to avoid the security hole of potentially showing "old" data. –  Mar 10 '14 at 13:20
  • @Damon Also, we use `FILE_FLAG_NO_BUFFERING` so the OS is very little involved in the management of the file. –  Mar 10 '14 at 13:28

1 Answers1

1

Privileges need to be enabled before you can use them. Here's some code from one of my projects that enables SE_INCREASE_QUOTE_NAME and SE_ASSIGNPRIMARYTOKEN_NAME, it can be easily modified to enable other privileges.

BOOL enable_privs(void)
{
    HANDLE token;

    struct {
        DWORD count;
        LUID_AND_ATTRIBUTES privilege[2];
    } token_privileges;

    token_privileges.count = 2;
    token_privileges.privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
    token_privileges.privilege[1].Attributes = SE_PRIVILEGE_ENABLED;

    if (!LookupPrivilegeValue(0, SE_INCREASE_QUOTA_NAME, &token_privileges.privilege[0].Luid)) return FALSE;
    if (!LookupPrivilegeValue(0, SE_ASSIGNPRIMARYTOKEN_NAME, &token_privileges.privilege[1].Luid)) return FALSE;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)) return FALSE;
    if (!AdjustTokenPrivileges(token, 0, (PTOKEN_PRIVILEGES)&token_privileges, 0, 0, 0)) return FALSE;
    if (GetLastError() != ERROR_SUCCESS) return FALSE;

    return TRUE;
}
Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • That did something as `SetFileValidData()` fails with error #87 _"The parameter is incorrect"_ now. –  Mar 10 '14 at 21:35
  • Got it to work now. Error #87 was because had not called `SetFilePointerEx()` and `SetEndOfFile()` before `SetFileValidData()` . –  Mar 10 '14 at 22:51