Home | History | Annotate | Download | only in lib
      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 // Library functions related to the OEM Deal Confirmation Code.
      6 
      7 #include "rlz/win/lib/machine_deal.h"
      8 
      9 #include <windows.h>
     10 #include <vector>
     11 
     12 #include "base/basictypes.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/strings/string_split.h"
     15 #include "base/strings/string_util.h"
     16 #include "base/strings/stringprintf.h"
     17 #include "base/win/registry.h"
     18 #include "rlz/lib/assert.h"
     19 #include "rlz/lib/lib_values.h"
     20 #include "rlz/win/lib/lib_mutex.h"
     21 #include "rlz/win/lib/registry_util.h"
     22 #include "rlz/win/lib/rlz_value_store_registry.h"
     23 
     24 const wchar_t kDccValueName[]             = L"DCC";
     25 
     26 namespace {
     27 
     28 // Current DCC can only uses [a-zA-Z0-9_-!@$*();.<>,:]
     29 // We will be more liberal and allow some additional chars, but not url meta
     30 // chars.
     31 bool IsGoodDccChar(char ch) {
     32   if (IsAsciiAlpha(ch) || IsAsciiDigit(ch))
     33     return true;
     34 
     35   switch (ch) {
     36     case '_':
     37     case '-':
     38     case '!':
     39     case '@':
     40     case '$':
     41     case '*':
     42     case '(':
     43     case ')':
     44     case ';':
     45     case '.':
     46     case '<':
     47     case '>':
     48     case ',':
     49     case ':':
     50       return true;
     51   }
     52 
     53   return false;
     54 }
     55 
     56 // This function will remove bad rlz chars and also limit the max rlz to some
     57 // reasonable size. It also assumes that normalized_dcc is at least
     58 // kMaxDccLength+1 long.
     59 void NormalizeDcc(const char* raw_dcc, char* normalized_dcc) {
     60   int index = 0;
     61   for (; raw_dcc[index] != 0 && index < rlz_lib::kMaxDccLength; ++index) {
     62     char current = raw_dcc[index];
     63     if (IsGoodDccChar(current)) {
     64       normalized_dcc[index] = current;
     65     } else {
     66       normalized_dcc[index] = '.';
     67     }
     68   }
     69 
     70   normalized_dcc[index] = 0;
     71 }
     72 
     73 bool GetResponseLine(const char* response_text, int response_length,
     74                      int* search_index, std::string* response_line) {
     75   if (!response_line || !search_index || *search_index > response_length)
     76     return false;
     77 
     78   response_line->clear();
     79 
     80   if (*search_index < 0)
     81     return false;
     82 
     83   int line_begin = *search_index;
     84   const char* line_end = strchr(response_text + line_begin, '\n');
     85 
     86   if (line_end == NULL || line_end - response_text > response_length) {
     87     line_end = response_text + response_length;
     88     *search_index = -1;
     89   } else {
     90     *search_index = line_end - response_text + 1;
     91   }
     92 
     93   response_line->assign(response_text + line_begin,
     94                         line_end - response_text - line_begin);
     95   return true;
     96 }
     97 
     98 bool GetResponseValue(const std::string& response_line,
     99                       const std::string& response_key,
    100                       std::string* value) {
    101   if (!value)
    102     return false;
    103 
    104   value->clear();
    105 
    106   if (!StartsWithASCII(response_line, response_key, true))
    107     return false;
    108 
    109   std::vector<std::string> tokens;
    110   base::SplitString(response_line, ':', &tokens);
    111   if (tokens.size() != 2)
    112     return false;
    113 
    114   // The first token is the key, the second is the value.  The value is already
    115   // trimmed for whitespace.
    116   *value = tokens[1];
    117   return true;
    118 }
    119 
    120 }  // namespace anonymous
    121 
    122 namespace rlz_lib {
    123 
    124 bool MachineDealCode::Set(const char* dcc) {
    125   LibMutex lock;
    126   if (lock.failed())
    127     return false;
    128 
    129   // TODO: if (!ProcessInfo::CanWriteMachineKey()) return false;
    130 
    131   // Validate the new dcc value.
    132   size_t length = strlen(dcc);
    133   if (length >  kMaxDccLength) {
    134     ASSERT_STRING("MachineDealCode::Set: DCC length is exceeds max allowed.");
    135     return false;
    136   }
    137 
    138   base::win::RegKey hklm_key(HKEY_LOCAL_MACHINE,
    139                              RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
    140                              KEY_READ | KEY_WRITE | KEY_WOW64_32KEY);
    141   if (!hklm_key.Valid()) {
    142     ASSERT_STRING("MachineDealCode::Set: Unable to create / open machine key."
    143                   " Did you call rlz_lib::CreateMachineState()?");
    144     return false;
    145   }
    146 
    147   char normalized_dcc[kMaxDccLength + 1];
    148   NormalizeDcc(dcc, normalized_dcc);
    149   VERIFY(length == strlen(normalized_dcc));
    150 
    151   // Write the DCC to HKLM.  Note that we need to include the null character
    152   // when writing the string.
    153   if (!RegKeyWriteValue(hklm_key, kDccValueName, normalized_dcc)) {
    154     ASSERT_STRING("MachineDealCode::Set: Could not write the DCC value");
    155     return false;
    156   }
    157 
    158   return true;
    159 }
    160 
    161 bool MachineDealCode::GetNewCodeFromPingResponse(const char* response,
    162     bool* has_new_dcc, char* new_dcc, int new_dcc_size) {
    163   if (!has_new_dcc || !new_dcc || !new_dcc_size)
    164     return false;
    165 
    166   *has_new_dcc = false;
    167   new_dcc[0] = 0;
    168 
    169   int response_length = -1;
    170   if (!IsPingResponseValid(response, &response_length))
    171     return false;
    172 
    173   // Get the current DCC value to compare to later)
    174   char stored_dcc[kMaxDccLength + 1];
    175   if (!Get(stored_dcc, arraysize(stored_dcc)))
    176     stored_dcc[0] = 0;
    177 
    178   int search_index = 0;
    179   std::string response_line;
    180   std::string new_dcc_value;
    181   bool old_dcc_confirmed = false;
    182   const std::string dcc_cgi(kDccCgiVariable);
    183   const std::string dcc_cgi_response(kSetDccResponseVariable);
    184   while (GetResponseLine(response, response_length, &search_index,
    185                          &response_line)) {
    186     std::string value;
    187 
    188     if (!old_dcc_confirmed &&
    189         GetResponseValue(response_line, dcc_cgi, &value)) {
    190       // This is the old DCC confirmation - should match value in registry.
    191       if (value != stored_dcc)
    192         return false;  // Corrupted DCC - ignore this response.
    193       else
    194         old_dcc_confirmed = true;
    195       continue;
    196     }
    197 
    198     if (!(*has_new_dcc) &&
    199         GetResponseValue(response_line, dcc_cgi_response, &value)) {
    200       // This is the new DCC.
    201       if (value.size() > kMaxDccLength) continue;  // Too long
    202       *has_new_dcc = true;
    203       new_dcc_value = value;
    204     }
    205   }
    206 
    207   old_dcc_confirmed |= (NULL == stored_dcc[0]);
    208 
    209   base::strlcpy(new_dcc, new_dcc_value.c_str(), new_dcc_size);
    210   return old_dcc_confirmed;
    211 }
    212 
    213 bool MachineDealCode::SetFromPingResponse(const char* response) {
    214   bool has_new_dcc = false;
    215   char new_dcc[kMaxDccLength + 1];
    216 
    217   bool response_valid = GetNewCodeFromPingResponse(
    218       response, &has_new_dcc, new_dcc, arraysize(new_dcc));
    219 
    220   if (response_valid && has_new_dcc)
    221     return Set(new_dcc);
    222 
    223   return response_valid;
    224 }
    225 
    226 bool MachineDealCode::GetAsCgi(char* cgi, int cgi_size) {
    227   if (!cgi || cgi_size <= 0) {
    228     ASSERT_STRING("MachineDealCode::GetAsCgi: Invalid buffer");
    229     return false;
    230   }
    231 
    232   cgi[0] = 0;
    233 
    234   std::string cgi_arg;
    235   base::StringAppendF(&cgi_arg, "%s=", kDccCgiVariable);
    236   int cgi_arg_length = cgi_arg.size();
    237 
    238   if (cgi_arg_length >= cgi_size) {
    239     ASSERT_STRING("MachineDealCode::GetAsCgi: Insufficient buffer size");
    240     return false;
    241   }
    242 
    243   base::strlcpy(cgi, cgi_arg.c_str(), cgi_size);
    244 
    245   if (!Get(cgi + cgi_arg_length, cgi_size - cgi_arg_length)) {
    246     cgi[0] = 0;
    247     return false;
    248   }
    249   return true;
    250 }
    251 
    252 bool MachineDealCode::Get(char* dcc, int dcc_size) {
    253   LibMutex lock;
    254   if (lock.failed())
    255     return false;
    256 
    257   if (!dcc || dcc_size <= 0) {
    258     ASSERT_STRING("MachineDealCode::Get: Invalid buffer");
    259     return false;
    260   }
    261 
    262   dcc[0] = 0;
    263 
    264   base::win::RegKey dcc_key(HKEY_LOCAL_MACHINE,
    265                             RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
    266                             KEY_READ | KEY_WOW64_32KEY);
    267   if (!dcc_key.Valid())
    268     return false;  // no DCC key.
    269 
    270   size_t size = dcc_size;
    271   if (!RegKeyReadValue(dcc_key, kDccValueName, dcc, &size)) {
    272     ASSERT_STRING("MachineDealCode::Get: Insufficient buffer size");
    273     dcc[0] = 0;
    274     return false;
    275   }
    276 
    277   return true;
    278 }
    279 
    280 bool MachineDealCode::Clear() {
    281   base::win::RegKey dcc_key(HKEY_LOCAL_MACHINE,
    282                             RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
    283                             KEY_READ | KEY_WRITE | KEY_WOW64_32KEY);
    284   if (!dcc_key.Valid())
    285     return false;  // no DCC key.
    286 
    287   dcc_key.DeleteValue(kDccValueName);
    288 
    289   // Verify deletion.
    290   wchar_t dcc[kMaxDccLength + 1];
    291   DWORD dcc_size = arraysize(dcc);
    292   if (dcc_key.ReadValue(kDccValueName, dcc, &dcc_size, NULL) == ERROR_SUCCESS) {
    293     ASSERT_STRING("MachineDealCode::Clear: Could not delete the DCC value.");
    294     return false;
    295   }
    296 
    297   return true;
    298 }
    299 
    300 }  // namespace rlz_lib
    301