1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <windows.h>
6#include <Sddl.h>  // For ConvertSidToStringSidW.
7#include <string>
8
9#include "base/memory/scoped_ptr.h"
10#include "base/strings/string16.h"
11#include "rlz/lib/assert.h"
12
13namespace rlz_lib {
14
15namespace {
16
17bool GetSystemVolumeSerialNumber(int* number) {
18  if (!number)
19    return false;
20
21  *number = 0;
22
23  // Find the system root path (e.g: C:\).
24  wchar_t system_path[MAX_PATH + 1];
25  if (!GetSystemDirectoryW(system_path, MAX_PATH))
26    return false;
27
28  wchar_t* first_slash = wcspbrk(system_path, L"\\/");
29  if (first_slash != NULL)
30    *(first_slash + 1) = 0;
31
32  DWORD number_local = 0;
33  if (!GetVolumeInformationW(system_path, NULL, 0, &number_local, NULL, NULL,
34                             NULL, 0))
35    return false;
36
37  *number = number_local;
38  return true;
39}
40
41bool GetComputerSid(const wchar_t* account_name, SID* sid, DWORD sid_size) {
42  static const DWORD kStartDomainLength = 128;  // reasonable to start with
43
44  scoped_ptr<wchar_t[]> domain_buffer(new wchar_t[kStartDomainLength]);
45  DWORD domain_size = kStartDomainLength;
46  DWORD sid_dword_size = sid_size;
47  SID_NAME_USE sid_name_use;
48
49  BOOL success = ::LookupAccountNameW(NULL, account_name, sid,
50                                      &sid_dword_size, domain_buffer.get(),
51                                      &domain_size, &sid_name_use);
52  if (!success && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
53    // We could have gotten the insufficient buffer error because
54    // one or both of sid and szDomain was too small. Check for that
55    // here.
56    if (sid_dword_size > sid_size)
57      return false;
58
59    if (domain_size > kStartDomainLength)
60      domain_buffer.reset(new wchar_t[domain_size]);
61
62    success = ::LookupAccountNameW(NULL, account_name, sid, &sid_dword_size,
63                                   domain_buffer.get(), &domain_size,
64                                   &sid_name_use);
65  }
66
67  return success != FALSE;
68}
69
70std::wstring ConvertSidToString(SID* sid) {
71  std::wstring sid_string;
72#if _WIN32_WINNT >= 0x500
73  wchar_t* sid_buffer = NULL;
74  if (ConvertSidToStringSidW(sid, &sid_buffer)) {
75    sid_string = sid_buffer;
76    LocalFree(sid_buffer);
77  }
78#else
79  SID_IDENTIFIER_AUTHORITY* sia = ::GetSidIdentifierAuthority(sid);
80
81  if(sia->Value[0] || sia->Value[1]) {
82    base::SStringPrintf(
83        &sid_string, L"S-%d-0x%02hx%02hx%02hx%02hx%02hx%02hx",
84        SID_REVISION, (USHORT)sia->Value[0], (USHORT)sia->Value[1],
85        (USHORT)sia->Value[2], (USHORT)sia->Value[3], (USHORT)sia->Value[4],
86        (USHORT)sia->Value[5]);
87  } else {
88    ULONG authority = 0;
89    for (int i = 2; i < 6; ++i) {
90      authority <<= 8;
91      authority |= sia->Value[i];
92    }
93    base::SStringPrintf(&sid_string, L"S-%d-%lu", SID_REVISION, authority);
94  }
95
96  int sub_auth_count = *::GetSidSubAuthorityCount(sid);
97  for(int i = 0; i < sub_auth_count; ++i)
98    base::StringAppendF(&sid_string, L"-%lu", *::GetSidSubAuthority(sid, i));
99#endif
100
101  return sid_string;
102}
103
104}  // namespace
105
106bool GetRawMachineId(base::string16* sid_string, int* volume_id) {
107  // Calculate the Windows SID.
108
109  wchar_t computer_name[MAX_COMPUTERNAME_LENGTH + 1] = {0};
110  DWORD size = arraysize(computer_name);
111
112  if (GetComputerNameW(computer_name, &size)) {
113    char sid_buffer[SECURITY_MAX_SID_SIZE];
114    SID* sid = reinterpret_cast<SID*>(sid_buffer);
115    if (GetComputerSid(computer_name, sid, SECURITY_MAX_SID_SIZE)) {
116      *sid_string = ConvertSidToString(sid);
117    }
118  }
119
120  // Get the system drive volume serial number.
121  *volume_id = 0;
122  if (!GetSystemVolumeSerialNumber(volume_id)) {
123    ASSERT_STRING("GetMachineId: Failed to retrieve volume serial number");
124    *volume_id = 0;
125  }
126
127  return true;
128}
129
130}  // namespace rlz_lib
131