Home | History | Annotate | Download | only in chrome_frame
      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 "chrome_frame/chrome_launcher.h"
      6 
      7 #include <windows.h>
      8 #include <shellapi.h>
      9 #include <shlwapi.h>
     10 
     11 #include "policy/policy_constants.h"
     12 
     13 // Herein lies stuff selectively stolen from Chrome. We don't pull it in
     14 // directly because all of it results in many things we don't want being
     15 // included as well.
     16 namespace {
     17 
     18 // These are the switches we will allow (along with their values) in the
     19 // safe-for-Low-Integrity version of the Chrome command line.
     20 // Including the chrome switch files pulls in a bunch of dependencies sadly, so
     21 // we redefine things here:
     22 const wchar_t* kAllowedSwitches[] = {
     23   L"automation-channel",
     24   L"chrome-frame",
     25   L"chrome-version",
     26   L"disable-background-mode",
     27   L"disable-popup-blocking",
     28   L"disable-print-preview",
     29   L"disable-renderer-accessibility",
     30   L"enable-experimental-extension-apis",
     31   L"force-renderer-accessibility",
     32   L"full-memory-crash-report",
     33   L"lang",
     34   L"no-default-browser-check",
     35   L"no-first-run",
     36   L"noerrdialogs",
     37   L"user-data-dir",
     38 };
     39 
     40 const wchar_t kWhitespaceChars[] = {
     41   0x0009, /* <control-0009> to <control-000D> */
     42   0x000A,
     43   0x000B,
     44   0x000C,
     45   0x000D,
     46   0x0020, /* Space */
     47   0x0085, /* <control-0085> */
     48   0x00A0, /* No-Break Space */
     49   0x1680, /* Ogham Space Mark */
     50   0x180E, /* Mongolian Vowel Separator */
     51   0x2000, /* En Quad to Hair Space */
     52   0x2001,
     53   0x2002,
     54   0x2003,
     55   0x2004,
     56   0x2005,
     57   0x2006,
     58   0x2007,
     59   0x2008,
     60   0x2009,
     61   0x200A,
     62   0x200C, /* Zero Width Non-Joiner */
     63   0x2028, /* Line Separator */
     64   0x2029, /* Paragraph Separator */
     65   0x202F, /* Narrow No-Break Space */
     66   0x205F, /* Medium Mathematical Space */
     67   0x3000, /* Ideographic Space */
     68   0
     69 };
     70 
     71 const wchar_t kLauncherExeBaseName[] = L"chrome_launcher.exe";
     72 const wchar_t kBrowserProcessExecutableName[] = L"chrome.exe";
     73 
     74 }  // end namespace
     75 
     76 
     77 namespace chrome_launcher {
     78 
     79 std::wstring TrimWhiteSpace(const wchar_t* input_str) {
     80   std::wstring output;
     81   if (input_str != NULL) {
     82     std::wstring str(input_str);
     83 
     84     const std::wstring::size_type first_good_char =
     85         str.find_first_not_of(kWhitespaceChars);
     86     const std::wstring::size_type last_good_char =
     87         str.find_last_not_of(kWhitespaceChars);
     88 
     89     if (first_good_char != std::wstring::npos &&
     90         last_good_char != std::wstring::npos &&
     91         last_good_char >= first_good_char) {
     92       // + 1 because find_last_not_of returns the index, and we want the count
     93       output = str.substr(first_good_char,
     94                           last_good_char - first_good_char + 1);
     95     }
     96   }
     97 
     98   return output;
     99 }
    100 
    101 bool IsValidArgument(const std::wstring& arg) {
    102   if (arg.length() < 2) {
    103     return false;
    104   }
    105 
    106   for (int i = 0; i < arraysize(kAllowedSwitches); ++i) {
    107     size_t arg_length = lstrlenW(kAllowedSwitches[i]);
    108     if (arg.find(kAllowedSwitches[i], 2) == 2) {
    109       // The argument starts off right, now it must either end here, or be
    110       // followed by an equals sign.
    111       if (arg.length() == (arg_length + 2) ||
    112           (arg.length() > (arg_length + 2) && arg[arg_length+2] == L'=')) {
    113         return true;
    114       }
    115     }
    116   }
    117 
    118   return false;
    119 }
    120 
    121 bool IsValidCommandLine(const wchar_t* command_line) {
    122   if (command_line == NULL) {
    123     return false;
    124   }
    125 
    126   int num_args = 0;
    127   wchar_t** args = NULL;
    128   args = CommandLineToArgvW(command_line, &num_args);
    129 
    130   bool success = true;
    131   // Note that we skip args[0] since that is just our executable name and
    132   // doesn't get passed through to Chrome.
    133   for (int i = 1; i < num_args; ++i) {
    134     std::wstring trimmed_arg = TrimWhiteSpace(args[i]);
    135     if (!IsValidArgument(trimmed_arg.c_str())) {
    136       success = false;
    137       break;
    138     }
    139   }
    140 
    141   return success;
    142 }
    143 
    144 // Looks up optionally configured launch parameters for Chrome that may have
    145 // been set via group policy.
    146 void AppendAdditionalLaunchParameters(std::wstring* command_line) {
    147   static const HKEY kRootKeys[] = {
    148     HKEY_LOCAL_MACHINE,
    149     HKEY_CURRENT_USER
    150   };
    151 
    152   std::wstring launch_params_value_name(
    153       &policy::key::kAdditionalLaunchParameters[0],
    154       &policy::key::kAdditionalLaunchParameters[
    155           lstrlenA(policy::key::kAdditionalLaunchParameters)]);
    156 
    157   // Used for basic checks since CreateProcess doesn't support command lines
    158   // longer than 0x8000 characters. If we surpass that length, we do not add the
    159   // additional parameters.  Because we need to add a space before the
    160   // extra parameters, we use 0x7fff and not 0x8000.
    161   const size_t kMaxChars = 0x7FFF - command_line->size();
    162   HKEY key;
    163   LONG result;
    164   bool found = false;
    165   for (int i = 0; !found && i < arraysize(kRootKeys); ++i) {
    166     result = ::RegOpenKeyExW(kRootKeys[i], policy::kRegistryChromePolicyKey, 0,
    167                              KEY_QUERY_VALUE, &key);
    168     if (result == ERROR_SUCCESS) {
    169       DWORD size = 0;
    170       DWORD type = 0;
    171       result = RegQueryValueExW(key, launch_params_value_name.c_str(),
    172                                 0, &type, NULL, &size);
    173       if (result == ERROR_SUCCESS && type == REG_SZ && size > 0 &&
    174           (size / sizeof(wchar_t)) < kMaxChars) {
    175         // This size includes any terminating null character or characters
    176         // unless the data was stored without them, so for safety we allocate
    177         // one extra char and zero out the buffer.
    178         wchar_t* value = new wchar_t[(size / sizeof(wchar_t)) + 1];
    179         memset(value, 0, size + sizeof(wchar_t));
    180         result = RegQueryValueExW(key, launch_params_value_name.c_str(), 0,
    181                                   &type, reinterpret_cast<BYTE*>(&value[0]),
    182                                   &size);
    183         if (result == ERROR_SUCCESS) {
    184           *command_line += L' ';
    185           *command_line += value;
    186           found = true;
    187         }
    188         delete [] value;
    189       }
    190       ::RegCloseKey(key);
    191     }
    192   }
    193 }
    194 
    195 bool SanitizeAndLaunchChrome(const wchar_t* command_line) {
    196   bool success = false;
    197   if (IsValidCommandLine(command_line)) {
    198     std::wstring chrome_path;
    199     if (GetChromeExecutablePath(&chrome_path)) {
    200       const wchar_t* args = PathGetArgs(command_line);
    201 
    202       // Build the command line string with the quoted path to chrome.exe.
    203       std::wstring command_line;
    204       command_line.reserve(chrome_path.size() + 2);
    205       command_line.append(1, L'\"').append(chrome_path).append(1, L'\"');
    206 
    207       if (args != NULL) {
    208         command_line += L' ';
    209         command_line += args;
    210       }
    211 
    212       // Append parameters that might be set by group policy.
    213       AppendAdditionalLaunchParameters(&command_line);
    214 
    215       STARTUPINFO startup_info = {0};
    216       startup_info.cb = sizeof(startup_info);
    217       startup_info.dwFlags = STARTF_USESHOWWINDOW;
    218       startup_info.wShowWindow = SW_SHOW;
    219       PROCESS_INFORMATION process_info = {0};
    220       if (CreateProcess(&chrome_path[0], &command_line[0],
    221                         NULL, NULL, FALSE, 0, NULL, NULL,
    222                         &startup_info, &process_info)) {
    223         // Close handles.
    224         CloseHandle(process_info.hThread);
    225         CloseHandle(process_info.hProcess);
    226         success = true;
    227       } else {
    228         _ASSERT(FALSE);
    229       }
    230     }
    231   }
    232 
    233   return success;
    234 }
    235 
    236 bool GetChromeExecutablePath(std::wstring* chrome_path) {
    237   _ASSERT(chrome_path);
    238 
    239   wchar_t cur_path[MAX_PATH * 4] = {0};
    240   // Assume that we are always built into an exe.
    241   GetModuleFileName(NULL, cur_path, arraysize(cur_path) / 2);
    242 
    243   PathRemoveFileSpec(cur_path);
    244 
    245   bool success = false;
    246   if (PathAppend(cur_path, kBrowserProcessExecutableName)) {
    247     if (!PathFileExists(cur_path)) {
    248       // The installation model for Chrome places the DLLs in a versioned
    249       // sub-folder one down from the Chrome executable. If we fail to find
    250       // chrome.exe in the current path, try looking one up and launching that
    251       // instead. In practice, that means we back up two and append the
    252       // executable name again.
    253       PathRemoveFileSpec(cur_path);
    254       PathRemoveFileSpec(cur_path);
    255       PathAppend(cur_path, kBrowserProcessExecutableName);
    256     }
    257 
    258     if (PathFileExists(cur_path)) {
    259       *chrome_path = cur_path;
    260       success = true;
    261     }
    262   }
    263 
    264   return success;
    265 }
    266 
    267 }  // namespace chrome_launcher
    268