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   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 
     46 unsigned 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 
    152 unsigned 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 
    189 unsigned 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 
    242 unsigned 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 
    251 unsigned 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 
    278 unsigned 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 
    332 unsigned 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 
    346 unsigned 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 
    355 unsigned 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 
    398 unsigned 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 
    425 unsigned 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 
    466 unsigned RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) {
    467   integrity_level_ = integrity_level;
    468   return ERROR_SUCCESS;
    469 }
    470 
    471 }  // namespace sandbox
    472