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 "sandbox/win/src/restricted_token.h"
6
7#include <vector>
8
9#include "base/logging.h"
10#include "sandbox/win/src/acl.h"
11#include "sandbox/win/src/win_utils.h"
12
13
14namespace sandbox {
15
16unsigned RestrictedToken::Init(const HANDLE effective_token) {
17  if (init_)
18    return ERROR_ALREADY_INITIALIZED;
19
20  if (effective_token) {
21    // We duplicate the handle to be able to use it even if the original handle
22    // is closed.
23    HANDLE effective_token_dup;
24    if (::DuplicateHandle(::GetCurrentProcess(),
25                          effective_token,
26                          ::GetCurrentProcess(),
27                          &effective_token_dup,
28                          0,
29                          FALSE,
30                          DUPLICATE_SAME_ACCESS)) {
31      effective_token_ = effective_token_dup;
32    } else {
33      return ::GetLastError();
34    }
35  } else {
36    if (!::OpenProcessToken(::GetCurrentProcess(),
37                            TOKEN_ALL_ACCESS,
38                            &effective_token_))
39      return ::GetLastError();
40  }
41
42  init_ = true;
43  return ERROR_SUCCESS;
44}
45
46unsigned RestrictedToken::GetRestrictedTokenHandle(HANDLE *token_handle) const {
47  DCHECK(init_);
48  if (!init_)
49    return ERROR_NO_TOKEN;
50
51  size_t deny_size = sids_for_deny_only_.size();
52  size_t restrict_size = sids_to_restrict_.size();
53  size_t privileges_size = privileges_to_disable_.size();
54
55  SID_AND_ATTRIBUTES *deny_only_array = NULL;
56  if (deny_size) {
57    deny_only_array = new SID_AND_ATTRIBUTES[deny_size];
58
59    for (unsigned int i = 0; i < sids_for_deny_only_.size() ; ++i) {
60      deny_only_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY;
61      deny_only_array[i].Sid =
62          const_cast<SID*>(sids_for_deny_only_[i].GetPSID());
63    }
64  }
65
66  SID_AND_ATTRIBUTES *sids_to_restrict_array = NULL;
67  if (restrict_size) {
68    sids_to_restrict_array = new SID_AND_ATTRIBUTES[restrict_size];
69
70    for (unsigned int i = 0; i < restrict_size; ++i) {
71      sids_to_restrict_array[i].Attributes = 0;
72      sids_to_restrict_array[i].Sid =
73          const_cast<SID*>(sids_to_restrict_[i].GetPSID());
74    }
75  }
76
77  LUID_AND_ATTRIBUTES *privileges_to_disable_array = NULL;
78  if (privileges_size) {
79    privileges_to_disable_array = new LUID_AND_ATTRIBUTES[privileges_size];
80
81    for (unsigned int i = 0; i < privileges_size; ++i) {
82      privileges_to_disable_array[i].Attributes = 0;
83      privileges_to_disable_array[i].Luid = privileges_to_disable_[i];
84    }
85  }
86
87  BOOL result = TRUE;
88  HANDLE new_token = NULL;
89  // The SANDBOX_INERT flag did nothing in XP and it was just a way to tell
90  // if a token has ben restricted given the limiations of IsTokenRestricted()
91  // but it appears that in Windows 7 it hints the AppLocker subsystem to
92  // leave us alone.
93  if (deny_size || restrict_size || privileges_size) {
94    result = ::CreateRestrictedToken(effective_token_,
95                                     SANDBOX_INERT,
96                                     static_cast<DWORD>(deny_size),
97                                     deny_only_array,
98                                     static_cast<DWORD>(privileges_size),
99                                     privileges_to_disable_array,
100                                     static_cast<DWORD>(restrict_size),
101                                     sids_to_restrict_array,
102                                     &new_token);
103  } else {
104    // Duplicate the token even if it's not modified at this point
105    // because any subsequent changes to this token would also affect the
106    // current process.
107    result = ::DuplicateTokenEx(effective_token_, TOKEN_ALL_ACCESS, NULL,
108                                SecurityIdentification, TokenPrimary,
109                                &new_token);
110  }
111
112  if (deny_only_array)
113    delete[] deny_only_array;
114
115  if (sids_to_restrict_array)
116    delete[] sids_to_restrict_array;
117
118  if (privileges_to_disable_array)
119    delete[] privileges_to_disable_array;
120
121  if (!result)
122    return ::GetLastError();
123
124  // Modify the default dacl on the token to contain Restricted and the user.
125  if (!AddSidToDefaultDacl(new_token, WinRestrictedCodeSid, GENERIC_ALL))
126    return ::GetLastError();
127
128  if (!AddUserSidToDefaultDacl(new_token, GENERIC_ALL))
129    return ::GetLastError();
130
131  DWORD error = SetTokenIntegrityLevel(new_token, integrity_level_);
132  if (ERROR_SUCCESS != error)
133    return error;
134
135  BOOL status = ::DuplicateHandle(::GetCurrentProcess(),
136                                  new_token,
137                                  ::GetCurrentProcess(),
138                                  token_handle,
139                                  TOKEN_ALL_ACCESS,
140                                  FALSE,  // Don't inherit.
141                                  0);
142
143  if (new_token != effective_token_)
144    ::CloseHandle(new_token);
145
146  if (!status)
147    return ::GetLastError();
148
149  return ERROR_SUCCESS;
150}
151
152unsigned RestrictedToken::GetRestrictedTokenHandleForImpersonation(
153    HANDLE *token_handle) const {
154  DCHECK(init_);
155  if (!init_)
156    return ERROR_NO_TOKEN;
157
158  HANDLE restricted_token_handle;
159  unsigned err_code = GetRestrictedTokenHandle(&restricted_token_handle);
160  if (ERROR_SUCCESS != err_code)
161    return err_code;
162
163  HANDLE impersonation_token;
164  if (!::DuplicateToken(restricted_token_handle,
165                        SecurityImpersonation,
166                        &impersonation_token)) {
167    ::CloseHandle(restricted_token_handle);
168    return ::GetLastError();
169  }
170
171  ::CloseHandle(restricted_token_handle);
172
173  BOOL status = ::DuplicateHandle(::GetCurrentProcess(),
174                                  impersonation_token,
175                                  ::GetCurrentProcess(),
176                                  token_handle,
177                                  TOKEN_ALL_ACCESS,
178                                  FALSE,  // Don't inherit.
179                                  0);
180
181  ::CloseHandle(impersonation_token);
182
183  if (!status)
184    return ::GetLastError();
185
186  return ERROR_SUCCESS;
187}
188
189unsigned RestrictedToken::AddAllSidsForDenyOnly(std::vector<Sid> *exceptions) {
190  DCHECK(init_);
191  if (!init_)
192    return ERROR_NO_TOKEN;
193
194  TOKEN_GROUPS *token_groups = NULL;
195  DWORD size = 0;
196
197  BOOL result = ::GetTokenInformation(effective_token_,
198                                      TokenGroups,
199                                      NULL,  // No buffer.
200                                      0,  // Size is 0.
201                                      &size);
202  if (!size)
203    return ::GetLastError();
204
205  token_groups = reinterpret_cast<TOKEN_GROUPS*>(new BYTE[size]);
206  result = ::GetTokenInformation(effective_token_,
207                                 TokenGroups,
208                                 token_groups,
209                                 size,
210                                 &size);
211  if (!result) {
212    delete[] reinterpret_cast<BYTE*>(token_groups);
213    return ::GetLastError();
214  }
215
216  // Build the list of the deny only group SIDs
217  for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) {
218    if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0 &&
219        (token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 0) {
220      bool should_ignore = false;
221      if (exceptions) {
222        for (unsigned int j = 0; j < exceptions->size(); ++j) {
223          if (::EqualSid(const_cast<SID*>((*exceptions)[j].GetPSID()),
224                          token_groups->Groups[i].Sid)) {
225            should_ignore = true;
226            break;
227          }
228        }
229      }
230      if (!should_ignore) {
231        sids_for_deny_only_.push_back(
232            reinterpret_cast<SID*>(token_groups->Groups[i].Sid));
233      }
234    }
235  }
236
237  delete[] reinterpret_cast<BYTE*>(token_groups);
238
239  return ERROR_SUCCESS;
240}
241
242unsigned RestrictedToken::AddSidForDenyOnly(const Sid &sid) {
243  DCHECK(init_);
244  if (!init_)
245    return ERROR_NO_TOKEN;
246
247  sids_for_deny_only_.push_back(sid);
248  return ERROR_SUCCESS;
249}
250
251unsigned RestrictedToken::AddUserSidForDenyOnly() {
252  DCHECK(init_);
253  if (!init_)
254    return ERROR_NO_TOKEN;
255
256  DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
257  TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(new BYTE[size]);
258
259  BOOL result = ::GetTokenInformation(effective_token_,
260                                      TokenUser,
261                                      token_user,
262                                      size,
263                                      &size);
264
265  if (!result) {
266    delete[] reinterpret_cast<BYTE*>(token_user);
267    return ::GetLastError();
268  }
269
270  Sid user = reinterpret_cast<SID*>(token_user->User.Sid);
271  sids_for_deny_only_.push_back(user);
272
273  delete[] reinterpret_cast<BYTE*>(token_user);
274
275  return ERROR_SUCCESS;
276}
277
278unsigned RestrictedToken::DeleteAllPrivileges(
279    const std::vector<base::string16> *exceptions) {
280  DCHECK(init_);
281  if (!init_)
282    return ERROR_NO_TOKEN;
283
284  // Get the list of privileges in the token
285  TOKEN_PRIVILEGES *token_privileges = NULL;
286  DWORD size = 0;
287
288  BOOL result = ::GetTokenInformation(effective_token_,
289                                      TokenPrivileges,
290                                      NULL,  // No buffer.
291                                      0,  // Size is 0.
292                                      &size);
293  if (!size)
294    return ::GetLastError();
295
296  token_privileges = reinterpret_cast<TOKEN_PRIVILEGES*>(new BYTE[size]);
297  result = ::GetTokenInformation(effective_token_,
298                                 TokenPrivileges,
299                                 token_privileges,
300                                 size,
301                                 &size);
302  if (!result) {
303    delete[] reinterpret_cast<BYTE *>(token_privileges);
304    return ::GetLastError();
305  }
306
307
308  // Build the list of privileges to disable
309  for (unsigned int i = 0; i < token_privileges->PrivilegeCount; ++i) {
310    bool should_ignore = false;
311    if (exceptions) {
312      for (unsigned int j = 0; j < exceptions->size(); ++j) {
313        LUID luid = {0};
314        ::LookupPrivilegeValue(NULL, (*exceptions)[j].c_str(), &luid);
315        if (token_privileges->Privileges[i].Luid.HighPart == luid.HighPart &&
316            token_privileges->Privileges[i].Luid.LowPart == luid.LowPart) {
317          should_ignore = true;
318          break;
319        }
320      }
321    }
322    if (!should_ignore) {
323        privileges_to_disable_.push_back(token_privileges->Privileges[i].Luid);
324    }
325  }
326
327  delete[] reinterpret_cast<BYTE *>(token_privileges);
328
329  return ERROR_SUCCESS;
330}
331
332unsigned RestrictedToken::DeletePrivilege(const wchar_t *privilege) {
333  DCHECK(init_);
334  if (!init_)
335    return ERROR_NO_TOKEN;
336
337  LUID luid = {0};
338  if (LookupPrivilegeValue(NULL, privilege, &luid))
339    privileges_to_disable_.push_back(luid);
340  else
341    return ::GetLastError();
342
343  return ERROR_SUCCESS;
344}
345
346unsigned RestrictedToken::AddRestrictingSid(const Sid &sid) {
347  DCHECK(init_);
348  if (!init_)
349    return ERROR_NO_TOKEN;
350
351  sids_to_restrict_.push_back(sid);  // No attributes
352  return ERROR_SUCCESS;
353}
354
355unsigned RestrictedToken::AddRestrictingSidLogonSession() {
356  DCHECK(init_);
357  if (!init_)
358    return ERROR_NO_TOKEN;
359
360  TOKEN_GROUPS *token_groups = NULL;
361  DWORD size = 0;
362
363  BOOL result = ::GetTokenInformation(effective_token_,
364                                      TokenGroups,
365                                      NULL,  // No buffer.
366                                      0,  // Size is 0.
367                                      &size);
368  if (!size)
369    return ::GetLastError();
370
371  token_groups = reinterpret_cast<TOKEN_GROUPS*>(new BYTE[size]);
372  result = ::GetTokenInformation(effective_token_,
373                                 TokenGroups,
374                                 token_groups,
375                                 size,
376                                 &size);
377  if (!result) {
378    delete[] reinterpret_cast<BYTE*>(token_groups);
379    return ::GetLastError();
380  }
381
382  SID *logon_sid = NULL;
383  for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) {
384    if ((token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) {
385        logon_sid = static_cast<SID*>(token_groups->Groups[i].Sid);
386        break;
387    }
388  }
389
390  if (logon_sid)
391    sids_to_restrict_.push_back(logon_sid);
392
393  delete[] reinterpret_cast<BYTE*>(token_groups);
394
395  return ERROR_SUCCESS;
396}
397
398unsigned RestrictedToken::AddRestrictingSidCurrentUser() {
399  DCHECK(init_);
400  if (!init_)
401    return ERROR_NO_TOKEN;
402
403  DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
404  TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(new BYTE[size]);
405
406  BOOL result = ::GetTokenInformation(effective_token_,
407                                      TokenUser,
408                                      token_user,
409                                      size,
410                                      &size);
411
412  if (!result) {
413    delete[] reinterpret_cast<BYTE*>(token_user);
414    return ::GetLastError();
415  }
416
417  Sid user = reinterpret_cast<SID*>(token_user->User.Sid);
418  sids_to_restrict_.push_back(user);
419
420  delete[] reinterpret_cast<BYTE*>(token_user);
421
422  return ERROR_SUCCESS;
423}
424
425unsigned RestrictedToken::AddRestrictingSidAllSids() {
426  DCHECK(init_);
427  if (!init_)
428    return ERROR_NO_TOKEN;
429
430  // Add the current user to the list.
431  unsigned error = AddRestrictingSidCurrentUser();
432  if (ERROR_SUCCESS != error)
433    return error;
434
435  TOKEN_GROUPS *token_groups = NULL;
436  DWORD size = 0;
437
438  // Get the buffer size required.
439  BOOL result = ::GetTokenInformation(effective_token_, TokenGroups, NULL, 0,
440                                      &size);
441  if (!size)
442    return ::GetLastError();
443
444  token_groups = reinterpret_cast<TOKEN_GROUPS*>(new BYTE[size]);
445  result = ::GetTokenInformation(effective_token_,
446                                 TokenGroups,
447                                 token_groups,
448                                 size,
449                                 &size);
450  if (!result) {
451    delete[] reinterpret_cast<BYTE*>(token_groups);
452    return ::GetLastError();
453  }
454
455  // Build the list of restricting sids from all groups.
456  for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) {
457    if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0)
458      AddRestrictingSid(reinterpret_cast<SID*>(token_groups->Groups[i].Sid));
459  }
460
461  delete[] reinterpret_cast<BYTE*>(token_groups);
462
463  return ERROR_SUCCESS;
464}
465
466unsigned RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) {
467  integrity_level_ = integrity_level;
468  return ERROR_SUCCESS;
469}
470
471}  // namespace sandbox
472