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