aboutsummaryrefslogtreecommitdiff
path: root/src/dutil/acl2util.cpp
blob: 2261abe3dc0602942b6481c9bcaa0c01c53ddce8 (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
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.

#include "precomp.h"

/********************************************************************
AclCalculateServiceSidString - gets the SID string for the given service name

NOTE: psczSid should be freed with StrFree()
********************************************************************/
extern "C" HRESULT DAPI AclCalculateServiceSidString(
    __in LPCWSTR wzServiceName,
    __in int cchServiceName,
    __deref_out_z LPWSTR* psczSid
    )
{
    // TODO: use undocumented RtlCreateServiceSid function?
    // http://blogs.technet.com/b/voy/archive/2007/03/22/per-service-sid.aspx
    // Assume little endian.
    HRESULT hr = S_OK;
    LPWSTR sczUpperServiceName = NULL;
    DWORD cbHash = SHA1_HASH_LEN;
    BYTE* pbHash = NULL;

    Assert(psczSid);

    if (0 == cchServiceName)
    {
        hr = ::StringCchLengthW(wzServiceName, INT_MAX, reinterpret_cast<size_t*>(&cchServiceName));
        ExitOnFailure(hr, "Failed to get the length of the service name.");
    }

    hr = StrAllocStringToUpperInvariant(&sczUpperServiceName, wzServiceName, cchServiceName);
    ExitOnFailure(hr, "Failed to upper case the service name.");

    pbHash = reinterpret_cast<BYTE*>(MemAlloc(cbHash, TRUE));
    ExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to allocate hash byte array.");

    hr = CrypHashBuffer(reinterpret_cast<BYTE*>(sczUpperServiceName), cchServiceName * 2, PROV_RSA_FULL, CALG_SHA1, pbHash, cbHash);
    ExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to hash the service name.");

    hr = StrAllocFormatted(psczSid, L"S-1-5-80-%u-%u-%u-%u-%u",
                           MAKEDWORD(MAKEWORD(pbHash[0], pbHash[1]), MAKEWORD(pbHash[2], pbHash[3])),
                           MAKEDWORD(MAKEWORD(pbHash[4], pbHash[5]), MAKEWORD(pbHash[6], pbHash[7])),
                           MAKEDWORD(MAKEWORD(pbHash[8], pbHash[9]), MAKEWORD(pbHash[10], pbHash[11])),
                           MAKEDWORD(MAKEWORD(pbHash[12], pbHash[13]), MAKEWORD(pbHash[14], pbHash[15])),
                           MAKEDWORD(MAKEWORD(pbHash[16], pbHash[17]), MAKEWORD(pbHash[18], pbHash[19])));

LExit:
    ReleaseMem(pbHash);
    ReleaseStr(sczUpperServiceName);

    return hr;
}


/********************************************************************
AclGetAccountSidStringEx - gets a string version of the account's SID
                           calculates a service's SID if lookup fails

NOTE: psczSid should be freed with StrFree()
********************************************************************/
extern "C" HRESULT DAPI AclGetAccountSidStringEx(
    __in_z LPCWSTR wzSystem,
    __in_z LPCWSTR wzAccount,
    __deref_out_z LPWSTR* psczSid
    )
{
    HRESULT hr = S_OK;
    int cchAccount = 0;
    PSID psid = NULL;
    LPWSTR pwz = NULL;
    LPWSTR sczSid = NULL;

    Assert(psczSid);

    hr = AclGetAccountSid(wzSystem, wzAccount, &psid);
    if (SUCCEEDED(hr))
    {
        Assert(::IsValidSid(psid));

        if (!::ConvertSidToStringSidW(psid, &pwz))
        {
            ExitWithLastError(hr, "Failed to convert SID to string for Account: %ls", wzAccount);
        }

        hr = StrAllocString(psczSid, pwz, 0);
    }
    else
    {
        if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr)
        {
            HRESULT hrLength = ::StringCchLengthW(wzAccount, INT_MAX, reinterpret_cast<size_t*>(&cchAccount));
            ExitOnFailure(hrLength, "Failed to get the length of the account name.");

            if (11 < cchAccount && CSTR_EQUAL == CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"NT SERVICE\\", 11, wzAccount, 11))
            {
                // If the service is not installed then LookupAccountName doesn't resolve the SID, but we can calculate it.
                LPCWSTR wzServiceName = &wzAccount[11];
                hr = AclCalculateServiceSidString(wzServiceName, cchAccount - 11, &sczSid);
                ExitOnFailure(hr, "Failed to calculate the service SID for %ls", wzServiceName);

                *psczSid = sczSid;
                sczSid = NULL;
            }
        }
        ExitOnFailure(hr, "Failed to get SID for account: %ls", wzAccount);
    }

LExit:
    ReleaseStr(sczSid);
    if (pwz)
    {
        ::LocalFree(pwz);
    }
    if (psid)
    {
        AclFreeSid(psid);
    }

    return hr;
}