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// A library to manage RLZ information for access-points shared
6// across different client applications.
7
8#include "rlz/lib/rlz_lib.h"
9
10#include <windows.h>
11#include <aclapi.h>
12#include <winerror.h>
13
14#include "base/basictypes.h"
15#include "base/win/registry.h"
16#include "rlz/lib/assert.h"
17#include "rlz/lib/rlz_value_store.h"
18#include "rlz/win/lib/machine_deal.h"
19#include "rlz/win/lib/rlz_value_store_registry.h"
20
21namespace rlz_lib {
22
23// OEM Deal confirmation storage functions.
24
25template<class T>
26class typed_buffer_ptr {
27  scoped_ptr<char[]> buffer_;
28
29 public:
30  typed_buffer_ptr() {
31  }
32
33  explicit typed_buffer_ptr(size_t size) : buffer_(new char[size]) {
34  }
35
36  void reset(size_t size) {
37    buffer_.reset(new char[size]);
38  }
39
40  operator T*() {
41    return reinterpret_cast<T*>(buffer_.get());
42  }
43};
44
45// Check if this SID has the desired access by scanning the ACEs in the DACL.
46// This function is part of the rlz_lib namespace so that it can be called from
47// unit tests.  Non-unit test code should not call this function.
48bool HasAccess(PSID sid, ACCESS_MASK access_mask, ACL* dacl) {
49  if (dacl == NULL)
50    return false;
51
52  ACL_SIZE_INFORMATION info;
53  if (!GetAclInformation(dacl, &info, sizeof(info), AclSizeInformation))
54    return false;
55
56  GENERIC_MAPPING generic_mapping = {KEY_READ, KEY_WRITE, KEY_EXECUTE,
57                                     KEY_ALL_ACCESS};
58  MapGenericMask(&access_mask, &generic_mapping);
59
60  for (DWORD i = 0; i < info.AceCount; ++i) {
61    ACCESS_ALLOWED_ACE* ace;
62    if (GetAce(dacl, i, reinterpret_cast<void**>(&ace))) {
63      if ((ace->Header.AceFlags & INHERIT_ONLY_ACE) == INHERIT_ONLY_ACE)
64        continue;
65
66      PSID existing_sid = reinterpret_cast<PSID>(&ace->SidStart);
67      DWORD mask = ace->Mask;
68      MapGenericMask(&mask, &generic_mapping);
69
70      if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE &&
71         (mask & access_mask) == access_mask && EqualSid(existing_sid, sid))
72        return true;
73
74      if (ace->Header.AceType == ACCESS_DENIED_ACE_TYPE &&
75         (mask & access_mask) != 0 && EqualSid(existing_sid, sid))
76        return false;
77    }
78  }
79
80  return false;
81}
82
83bool CreateMachineState() {
84  LibMutex lock;
85  if (lock.failed())
86    return false;
87
88  base::win::RegKey hklm_key;
89  if (hklm_key.Create(HKEY_LOCAL_MACHINE,
90                      RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
91                      KEY_ALL_ACCESS | KEY_WOW64_32KEY) != ERROR_SUCCESS) {
92    ASSERT_STRING("rlz_lib::CreateMachineState: "
93                  "Unable to create / open machine key.");
94    return false;
95  }
96
97  // Create a SID that represents ALL USERS.
98  DWORD users_sid_size = SECURITY_MAX_SID_SIZE;
99  typed_buffer_ptr<SID> users_sid(users_sid_size);
100  CreateWellKnownSid(WinBuiltinUsersSid, NULL, users_sid, &users_sid_size);
101
102  // Get the security descriptor for the registry key.
103  DWORD original_sd_size = 0;
104  ::RegGetKeySecurity(hklm_key.Handle(), DACL_SECURITY_INFORMATION, NULL,
105      &original_sd_size);
106  typed_buffer_ptr<SECURITY_DESCRIPTOR> original_sd(original_sd_size);
107
108  LONG result = ::RegGetKeySecurity(hklm_key.Handle(),
109      DACL_SECURITY_INFORMATION, original_sd, &original_sd_size);
110  if (result != ERROR_SUCCESS) {
111    ASSERT_STRING("rlz_lib::CreateMachineState: "
112                  "Unable to create / open machine key.");
113    return false;
114  }
115
116  // Make a copy of the security descriptor so we can modify it.  The one
117  // returned by RegGetKeySecurity() is self-relative, so we need to make it
118  // absolute.
119  DWORD new_sd_size = 0;
120  DWORD dacl_size = 0;
121  DWORD sacl_size = 0;
122  DWORD owner_size = 0;
123  DWORD group_size = 0;
124  ::MakeAbsoluteSD(original_sd, NULL, &new_sd_size, NULL, &dacl_size,
125                        NULL, &sacl_size, NULL, &owner_size,
126                        NULL, &group_size);
127
128  typed_buffer_ptr<SECURITY_DESCRIPTOR> new_sd(new_sd_size);
129  // Make sure the DACL is big enough to add one more ACE.
130  typed_buffer_ptr<ACL> dacl(dacl_size + SECURITY_MAX_SID_SIZE);
131  typed_buffer_ptr<ACL> sacl(sacl_size);
132  typed_buffer_ptr<SID> owner(owner_size);
133  typed_buffer_ptr<SID> group(group_size);
134
135  if (!::MakeAbsoluteSD(original_sd, new_sd, &new_sd_size, dacl, &dacl_size,
136                        sacl, &sacl_size, owner, &owner_size,
137                        group, &group_size)) {
138    ASSERT_STRING("rlz_lib::CreateMachineState: MakeAbsoluteSD failed");
139    return false;
140  }
141
142  // If all users already have read/write access to the registry key, then
143  // nothing to do.  Otherwise change the security descriptor of the key to
144  // give everyone access.
145  if (HasAccess(users_sid, KEY_ALL_ACCESS, dacl)) {
146    return false;
147  }
148
149  // Add ALL-USERS ALL-ACCESS ACL.
150  EXPLICIT_ACCESS ea;
151  ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
152  ea.grfAccessPermissions = GENERIC_ALL | KEY_ALL_ACCESS;
153  ea.grfAccessMode = GRANT_ACCESS;
154  ea.grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
155  ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
156  ea.Trustee.ptstrName = L"Everyone";
157
158  ACL* new_dacl = NULL;
159  result = SetEntriesInAcl(1, &ea, dacl, &new_dacl);
160  if (result != ERROR_SUCCESS) {
161    ASSERT_STRING("rlz_lib::CreateMachineState: SetEntriesInAcl failed");
162    return false;
163  }
164
165  BOOL ok = SetSecurityDescriptorDacl(new_sd, TRUE, new_dacl, FALSE);
166  if (!ok) {
167    ASSERT_STRING("rlz_lib::CreateMachineState: "
168                  "SetSecurityDescriptorOwner failed");
169    LocalFree(new_dacl);
170    return false;
171  }
172
173  result = ::RegSetKeySecurity(hklm_key.Handle(),
174                               DACL_SECURITY_INFORMATION,
175                               new_sd);
176  // Note that the new DACL cannot be freed until after the call to
177  // RegSetKeySecurity().
178  LocalFree(new_dacl);
179
180  bool success = true;
181  if (result != ERROR_SUCCESS) {
182    ASSERT_STRING("rlz_lib::CreateMachineState: "
183                  "Unable to create / open machine key.");
184    success = false;
185  }
186
187
188  return success;
189}
190
191bool SetMachineDealCode(const char* dcc) {
192  return MachineDealCode::Set(dcc);
193}
194
195bool GetMachineDealCodeAsCgi(char* cgi, size_t cgi_size) {
196  return MachineDealCode::GetAsCgi(cgi, cgi_size);
197}
198
199bool GetMachineDealCode(char* dcc, size_t dcc_size) {
200  return MachineDealCode::Get(dcc, dcc_size);
201}
202
203// Combined functions.
204
205bool SetMachineDealCodeFromPingResponse(const char* response) {
206  return MachineDealCode::SetFromPingResponse(response);
207}
208
209}  // namespace rlz_lib
210