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/browser/signin/signin_internals_util.h" 6 7 #include <sstream> 8 9 #include "base/logging.h" 10 #include "base/strings/string_number_conversions.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "chrome/common/chrome_version_info.h" 14 #include "chrome/common/url_constants.h" 15 #include "content/public/browser/web_contents.h" 16 #include "crypto/sha2.h" 17 #include "google_apis/gaia/gaia_constants.h" 18 19 namespace signin_internals_util { 20 21 const char kSigninPrefPrefix[] = "google.services.signin."; 22 const char kTokenPrefPrefix[] = "google.services.signin.tokens."; 23 24 TokenInfo::TokenInfo(const std::string& truncated_token, 25 const std::string& status, 26 const std::string& time, 27 const int64& time_internal, 28 const std::string& service) 29 : truncated_token(truncated_token), 30 status(status), 31 time(time), 32 time_internal(time_internal), 33 service(service) { 34 // This should be a truncated and hashed token. 35 DCHECK_LE(truncated_token.length(), kTruncateTokenStringLength); 36 } 37 38 TokenInfo::TokenInfo() { 39 } 40 41 TokenInfo::~TokenInfo() { 42 } 43 44 DictionaryValue* TokenInfo::ToValue() { 45 scoped_ptr<DictionaryValue> token_info(new DictionaryValue()); 46 token_info->SetString("service", service); 47 token_info->SetString("token", truncated_token); 48 token_info->SetString("status", status); 49 token_info->SetString("time", time); 50 51 return token_info.release(); 52 } 53 54 #define ENUM_CASE(x) case x: return (std::string(kSigninPrefPrefix) + #x) 55 std::string SigninStatusFieldToString(UntimedSigninStatusField field) { 56 switch (field) { 57 ENUM_CASE(USERNAME); 58 ENUM_CASE(SID); 59 ENUM_CASE(LSID); 60 case UNTIMED_FIELDS_END: 61 NOTREACHED(); 62 return std::string(); 63 } 64 65 NOTREACHED(); 66 return std::string(); 67 } 68 69 std::string SigninStatusFieldToString(TimedSigninStatusField field) { 70 switch (field) { 71 ENUM_CASE(SIGNIN_TYPE); 72 ENUM_CASE(CLIENT_LOGIN_STATUS); 73 ENUM_CASE(OAUTH_LOGIN_STATUS); 74 ENUM_CASE(GET_USER_INFO_STATUS); 75 ENUM_CASE(UBER_TOKEN_STATUS); 76 ENUM_CASE(MERGE_SESSION_STATUS); 77 case TIMED_FIELDS_END: 78 NOTREACHED(); 79 return std::string(); 80 } 81 82 NOTREACHED(); 83 return std::string(); 84 } 85 86 SigninStatus::SigninStatus() 87 :untimed_signin_fields(UNTIMED_FIELDS_COUNT), 88 timed_signin_fields(TIMED_FIELDS_COUNT) { 89 } 90 91 SigninStatus::~SigninStatus() { 92 } 93 94 std::string TokenPrefPath(const std::string& token_name) { 95 return std::string(kTokenPrefPrefix) + token_name; 96 } 97 98 namespace { 99 100 ListValue* AddSection(ListValue* parent_list, const std::string& title) { 101 scoped_ptr<DictionaryValue> section(new DictionaryValue()); 102 ListValue* section_contents = new ListValue(); 103 104 section->SetString("title", title); 105 section->Set("data", section_contents); 106 parent_list->Append(section.release()); 107 return section_contents; 108 } 109 110 void AddSectionEntry(ListValue* section_list, 111 const std::string& field_name, 112 const std::string& field_val) { 113 scoped_ptr<DictionaryValue> entry(new DictionaryValue()); 114 entry->SetString("label", field_name); 115 entry->SetString("value", field_val); 116 section_list->Append(entry.release()); 117 } 118 119 120 // Returns a string describing the chrome version environment. Version format: 121 // <Build Info> <OS> <Version number> (<Last change>)<channel or "-devel"> 122 // If version information is unavailable, returns "invalid." 123 std::string GetVersionString() { 124 // Build a version string that matches MakeUserAgentForSyncApi with the 125 // addition of channel info and proper OS names. 126 chrome::VersionInfo chrome_version; 127 if (!chrome_version.is_valid()) 128 return "invalid"; 129 // GetVersionStringModifier returns empty string for stable channel or 130 // unofficial builds, the channel string otherwise. We want to have "-devel" 131 // for unofficial builds only. 132 std::string version_modifier = 133 chrome::VersionInfo::GetVersionStringModifier(); 134 if (version_modifier.empty()) { 135 if (chrome::VersionInfo::GetChannel() != 136 chrome::VersionInfo::CHANNEL_STABLE) { 137 version_modifier = "-devel"; 138 } 139 } else { 140 version_modifier = " " + version_modifier; 141 } 142 return chrome_version.Name() + " " + chrome_version.OSType() + " " + 143 chrome_version.Version() + " (" + chrome_version.LastChange() + ")" + 144 version_modifier; 145 } 146 147 148 std::string SigninStatusFieldToLabel(UntimedSigninStatusField field) { 149 switch (field) { 150 case USERNAME: 151 return "User Id"; 152 case LSID: 153 return "Lsid (Hash)"; 154 case SID: 155 return "Sid (Hash)"; 156 case UNTIMED_FIELDS_END: 157 NOTREACHED(); 158 return std::string(); 159 } 160 NOTREACHED(); 161 return std::string(); 162 } 163 164 TimedSigninStatusValue SigninStatusFieldToLabel( 165 TimedSigninStatusField field) { 166 switch (field) { 167 case SIGNIN_TYPE: 168 return TimedSigninStatusValue("Type", "Time"); 169 case CLIENT_LOGIN_STATUS: 170 return TimedSigninStatusValue("Last OnClientLogin Status", 171 "Last OnClientLogin Time"); 172 case OAUTH_LOGIN_STATUS: 173 return TimedSigninStatusValue("Last OnOAuthLogin Status", 174 "Last OnOAuthLogin Time"); 175 176 case GET_USER_INFO_STATUS: 177 return TimedSigninStatusValue("Last OnGetUserInfo Status", 178 "Last OnGetUserInfo Time"); 179 case UBER_TOKEN_STATUS: 180 return TimedSigninStatusValue("Last OnUberToken Status", 181 "Last OnUberToken Time"); 182 case MERGE_SESSION_STATUS: 183 return TimedSigninStatusValue("Last OnMergeSession Status", 184 "Last OnMergeSession Time"); 185 case TIMED_FIELDS_END: 186 NOTREACHED(); 187 return TimedSigninStatusValue("Error", std::string()); 188 } 189 NOTREACHED(); 190 return TimedSigninStatusValue("Error", std::string()); 191 } 192 193 } // namespace 194 195 scoped_ptr<DictionaryValue> SigninStatus::ToValue() { 196 scoped_ptr<DictionaryValue> signin_status(new DictionaryValue()); 197 ListValue* signin_info = new ListValue(); 198 signin_status->Set("signin_info", signin_info); 199 200 // A summary of signin related info first. 201 ListValue* basic_info = AddSection(signin_info, "Basic Information"); 202 const std::string signin_status_string = 203 untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN].empty() ? 204 "Not Signed In" : "Signed In"; 205 AddSectionEntry(basic_info, "Chrome Version", GetVersionString()); 206 AddSectionEntry(basic_info, "Signin Status", signin_status_string); 207 208 // Only add username. SID and LSID have moved to tokens section. 209 const std::string field = 210 SigninStatusFieldToLabel(static_cast<UntimedSigninStatusField>(USERNAME)); 211 AddSectionEntry( 212 basic_info, 213 field, 214 untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN]); 215 216 // Time and status information of the possible sign in types. 217 ListValue* detailed_info = AddSection(signin_info, "Last Signin Details"); 218 for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) { 219 const std::string value_field = 220 SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i)).first; 221 const std::string time_field = 222 SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i)).second; 223 224 AddSectionEntry(detailed_info, value_field, 225 timed_signin_fields[i - TIMED_FIELDS_BEGIN].first); 226 AddSectionEntry(detailed_info, time_field, 227 timed_signin_fields[i - TIMED_FIELDS_BEGIN].second); 228 } 229 230 // Token information for all services. 231 ListValue* token_info = new ListValue(); 232 ListValue* token_details = AddSection(token_info, "Token Details"); 233 signin_status->Set("token_info", token_info); 234 for (std::map<std::string, TokenInfo>::iterator it = token_info_map.begin(); 235 it != token_info_map.end(); ++it) { 236 DictionaryValue* token_info = it->second.ToValue(); 237 token_details->Append(token_info); 238 } 239 240 return signin_status.Pass(); 241 } 242 243 // Gets the first few hex characters of the SHA256 hash of the passed in string. 244 // These are enough to perform equality checks across a single users tokens, 245 // while preventing outsiders from reverse-engineering the actual token from 246 // the displayed value. 247 // Note that for readability (in about:signin-internals), an empty string 248 // is not hashed, but simply returned as an empty string. 249 std::string GetTruncatedHash(const std::string& str) { 250 if (str.empty()) 251 return str; 252 253 // Since each character in the hash string generates two hex charaters 254 // we only need half as many charaters in |hash_val| as hex characters 255 // returned. 256 const int kTruncateSize = kTruncateTokenStringLength / 2; 257 char hash_val[kTruncateSize]; 258 crypto::SHA256HashString(str, &hash_val[0], kTruncateSize); 259 return StringToLowerASCII(base::HexEncode(&hash_val[0], kTruncateSize)); 260 } 261 262 } // namespace signin_internals_util 263