Home | History | Annotate | Download | only in src
      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 
     14 namespace sandbox {
     15 
     16 unsigned 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 
     47 unsigned 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 
    153 unsigned 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 
    190 unsigned 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 
    243 unsigned 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 
    252 unsigned 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 
    279 unsigned 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 
    333 unsigned 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 
    347 unsigned 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 
    356 unsigned 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 
    399 unsigned 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 
    426 unsigned 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 
    467 unsigned RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) {
    468   integrity_level_ = integrity_level;
    469   return ERROR_SUCCESS;
    470 }
    471 
    472 }  // namespace sandbox
    473