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// Information about the current process.
6
7#include "rlz/win/lib/process_info.h"
8
9#include <windows.h>
10#include <Sddl.h>  // For ConvertSidToStringSid.
11#include <LMCons.h>  // For UNLEN
12
13#include "base/logging.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/process/process_handle.h"
16#include "base/win/scoped_handle.h"
17#include "base/win/windows_version.h"
18#include "rlz/lib/assert.h"
19
20namespace {
21
22HRESULT GetCurrentUser(std::wstring* name,
23                       std::wstring* domain,
24                       std::wstring* sid) {
25  DWORD err;
26
27  // Get the current username & domain the hard way.  (GetUserNameEx would be
28  // nice, but unfortunately requires connectivity to a domain controller.
29  // Useless.)
30
31  // (Following call doesn't work if running as a Service - because a Service
32  // runs under special accounts like LOCAL_SYSTEM, not as the logged in user.
33  // In which case, search for and use the process handle of a running
34  // Explorer.exe.)
35  HANDLE token;
36  if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
37    return E_FAIL;
38
39  base::win::ScopedHandle scoped_process_token(token);
40
41  // (Following call will fail with ERROR_INSUFFICIENT_BUFFER and give us the
42  // required size.)
43  scoped_ptr<char[]> token_user_bytes;
44  DWORD token_user_size;
45  DWORD token_user_size2;
46  BOOL result = ::GetTokenInformation(token, TokenUser, NULL, 0,
47                                      &token_user_size);
48  err = ::GetLastError();
49  CHECK(!result && err == ERROR_INSUFFICIENT_BUFFER);
50
51  token_user_bytes.reset(new char[token_user_size]);
52  if (!token_user_bytes.get())
53    return E_OUTOFMEMORY;
54
55  if (!::GetTokenInformation(token, TokenUser, token_user_bytes.get(),
56                             token_user_size, &token_user_size2)) {
57    return E_FAIL;
58  }
59
60  WCHAR user_name[UNLEN + 1];  // max username length
61  WCHAR domain_name[UNLEN + 1];
62  DWORD user_name_size = UNLEN + 1;
63  DWORD domain_name_size = UNLEN + 1;
64  SID_NAME_USE sid_type;
65  TOKEN_USER* token_user =
66      reinterpret_cast<TOKEN_USER*>(token_user_bytes.get());
67  if (!token_user)
68    return E_FAIL;
69  PSID user_sid = token_user->User.Sid;
70  if (!::LookupAccountSidW(NULL, user_sid, user_name, &user_name_size,
71                           domain_name, &domain_name_size, &sid_type)) {
72    return E_FAIL;
73  }
74
75  if (name != NULL) {
76    *name = user_name;
77  }
78  if (domain != NULL) {
79    *domain = domain_name;
80  }
81  if (sid != NULL) {
82    LPWSTR string_sid;
83    ConvertSidToStringSidW(user_sid, &string_sid);
84    *sid = string_sid;  // copy out to cstring
85    // free memory, as documented for ConvertSidToStringSid
86    LocalFree(string_sid);
87  }
88
89  return S_OK;
90}
91
92HRESULT GetElevationType(PTOKEN_ELEVATION_TYPE elevation) {
93  if (!elevation)
94    return E_POINTER;
95
96  *elevation = TokenElevationTypeDefault;
97
98  if (base::win::GetVersion() < base::win::VERSION_VISTA)
99    return E_FAIL;
100
101  HANDLE process_token;
102  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token))
103    return HRESULT_FROM_WIN32(GetLastError());
104
105  base::win::ScopedHandle scoped_process_token(process_token);
106
107  DWORD size;
108  TOKEN_ELEVATION_TYPE elevation_type;
109  if (!GetTokenInformation(process_token, TokenElevationType, &elevation_type,
110                           sizeof(elevation_type), &size)) {
111    return HRESULT_FROM_WIN32(GetLastError());
112  }
113
114  *elevation = elevation_type;
115  return S_OK;
116}
117
118// based on http://msdn2.microsoft.com/en-us/library/aa376389.aspx
119bool GetUserGroup(long* group) {
120  if (!group)
121    return false;
122
123  *group = 0;
124
125  // groups are listed in DECREASING order of importance
126  // (eg. If a user is a member of both the admin group and
127  // the power user group, it is more useful to list the user
128  // as an admin)
129  DWORD user_groups[] =  {DOMAIN_ALIAS_RID_ADMINS,
130                          DOMAIN_ALIAS_RID_POWER_USERS};
131  SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
132
133  for (int i = 0; i < arraysize(user_groups) && *group == 0; ++i) {
134    PSID current_group;
135    if (AllocateAndInitializeSid(&nt_authority, 2,
136                                 SECURITY_BUILTIN_DOMAIN_RID,
137                                 user_groups[i], 0, 0, 0, 0,
138                                 0, 0, &current_group)) {
139      BOOL current_level;
140      if (CheckTokenMembership(NULL, current_group, &current_level) &&
141          current_level) {
142        *group = user_groups[i];
143      }
144
145      FreeSid(current_group);
146    }
147  }
148
149  return group != 0;
150}
151}  //anonymous
152
153
154namespace rlz_lib {
155
156bool ProcessInfo::IsRunningAsSystem() {
157  static std::wstring name;
158  static std::wstring domain;
159  static std::wstring sid;
160  if (name.empty())
161    CHECK(SUCCEEDED(GetCurrentUser(&name, &domain, &sid)));
162
163  return (name == L"SYSTEM");
164}
165
166bool ProcessInfo::HasAdminRights() {
167  static bool evaluated = false;
168  static bool has_rights = false;
169
170  if (!evaluated) {
171    if (IsRunningAsSystem()) {
172      has_rights = true;
173    } else if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
174      TOKEN_ELEVATION_TYPE elevation;
175      base::IntegrityLevel level;
176
177      if (SUCCEEDED(GetElevationType(&elevation)) &&
178        base::GetProcessIntegrityLevel(base::GetCurrentProcessHandle(), &level))
179        has_rights = (elevation == TokenElevationTypeFull) ||
180                    (level == HIGH_INTEGRITY);
181    } else {
182      long group = 0;
183      if (GetUserGroup(&group))
184        has_rights = (group == DOMAIN_ALIAS_RID_ADMINS);
185    }
186  }
187
188  evaluated = true;
189  if (!has_rights)
190    ASSERT_STRING("ProcessInfo::HasAdminRights: Does not have admin rights.");
191
192  return has_rights;
193}
194
195};  // namespace
196