summaryrefslogtreecommitdiff
path: root/zen/privilege.cpp
blob: c2db4701e4960d70169c30e99e9791ea8da201db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include "privilege.h"
#include <map>
//#include <mutex>
#include "win.h" //includes "windows.h"
#include "thread.h"
#include "zstring.h"
#include "scope_guard.h"
#include "win_ver.h"

using namespace zen;


namespace
{
bool privilegeIsActive(const wchar_t* privilege) //throw FileError
{
    HANDLE hToken = nullptr;
    if (!::OpenProcessToken(::GetCurrentProcess(), //__in   HANDLE ProcessHandle,
                            TOKEN_QUERY,           //__in   DWORD DesiredAccess,
                            &hToken))              //__out  PHANDLE TokenHandle
        throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege +  L"\""), L"OpenProcessToken", getLastError());
    ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken));

    LUID luid = {};
    if (!::LookupPrivilegeValue(nullptr,   //__in_opt  LPCTSTR lpSystemName,
                                privilege, //__in      LPCTSTR lpName,
                                &luid ))   //__out     PLUID lpLuid
        throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege +  L"\""), L"LookupPrivilegeValue", getLastError());

    PRIVILEGE_SET priv  = {};
    priv.PrivilegeCount = 1;
    priv.Control        = PRIVILEGE_SET_ALL_NECESSARY;
    priv.Privilege[0].Luid = luid;
    priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;

    BOOL alreadyGranted = FALSE;
    if (!::PrivilegeCheck(hToken,           //__in     HANDLE ClientToken,
                          &priv,            //__inout  PPRIVILEGE_SET RequiredPrivileges,
                          &alreadyGranted)) //__out    LPBOOL pfResult
        throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege +  L"\""), L"PrivilegeCheck", getLastError());

    return alreadyGranted != FALSE;
}


void setPrivilege(const wchar_t* privilege, bool enable) //throw FileError
{
    HANDLE hToken = nullptr;
    if (!::OpenProcessToken(::GetCurrentProcess(),   //__in   HANDLE ProcessHandle,
                            TOKEN_ADJUST_PRIVILEGES, //__in   DWORD DesiredAccess,
                            &hToken))                //__out  PHANDLE TokenHandle
        throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege +  L"\""), L"OpenProcessToken", getLastError());
    ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken));

    LUID luid = {};
    if (!::LookupPrivilegeValue(nullptr,   //__in_opt  LPCTSTR lpSystemName,
                                privilege, //__in      LPCTSTR lpName,
                                &luid ))   //__out     PLUID lpLuid
        throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege +  L"\""), L"LookupPrivilegeValue", getLastError());

    TOKEN_PRIVILEGES tp = {};
    tp.PrivilegeCount   = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;

    if (!::AdjustTokenPrivileges(hToken,   //__in       HANDLE TokenHandle,
                                 false,    //__in       BOOL DisableAllPrivileges,
                                 &tp,      //__in_opt   PTOKEN_PRIVILEGES NewState,
                                 0,        //__in       DWORD BufferLength,
                                 nullptr,  //__out_opt  PTOKEN_PRIVILEGES PreviousState,
                                 nullptr)) //__out_opt  PDWORD ReturnLength
        throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege +  L"\""), L"AdjustTokenPrivileges", getLastError());

    DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls!
    if (lastError == ERROR_NOT_ALL_ASSIGNED) //check although previous function returned with success!
    {
#ifdef __MINGW32__ //Shobjidl.h
#define ERROR_ELEVATION_REQUIRED         740L
#endif
        if (vistaOrLater()) //replace this useless error code with what it *really* means!
            lastError = ERROR_ELEVATION_REQUIRED;

        throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege +  L"\""), L"AdjustTokenPrivileges", lastError);
    }
}


class Privileges
{
public:
    static Privileges& getInstance()
    {
		        //meyers singleton: avoid static initialization order problem in global namespace!
        static Privileges inst;
        return inst;
    }

    void ensureActive(const wchar_t* privilege) //throw FileError
    {
		    boost::lock_guard<boost::mutex> dummy(lockPrivileges);

        if (activePrivileges.find(privilege) != activePrivileges.end())
            return; //privilege already active

        if (privilegeIsActive(privilege)) //privilege was already active before starting this tool
            activePrivileges.insert(std::make_pair(privilege, false));
        else
        {
            setPrivilege(privilege, true);
            activePrivileges.insert(std::make_pair(privilege, true));
        }
    }

private:
    Privileges() {}
    Privileges           (const Privileges&) = delete;
    Privileges& operator=(const Privileges&) = delete;

    ~Privileges() //clean up: deactivate all privileges that have been activated by this application
    {
        for (const auto& priv : activePrivileges)
            if (priv.second)
            {
                try
                {
                    setPrivilege(priv.first.c_str(), false); //throw FileError
                }
                catch (FileError&) {}
            }
    }

    std::map<Zstring, bool> activePrivileges; //bool: enabled by this application
boost::mutex lockPrivileges;
};

//caveat: function scope static initialization is not thread-safe in VS 2010!
auto& dummy = Privileges::getInstance();
}


void zen::activatePrivilege(const wchar_t* privilege) //throw FileError
{
    Privileges::getInstance().ensureActive(privilege);
}
bgstack15