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