1 // Copyright 2015 The Weave 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 "src/privet/auth_manager.h" 6 7 #include <algorithm> 8 9 #include <base/guid.h> 10 #include <base/rand_util.h> 11 #include <base/strings/string_number_conversions.h> 12 13 #include "src/config.h" 14 #include "src/data_encoding.h" 15 #include "src/privet/constants.h" 16 #include "src/privet/openssl_utils.h" 17 #include "src/string_utils.h" 18 19 extern "C" { 20 #include "third_party/libuweave/src/macaroon.h" 21 #include "third_party/libuweave/src/macaroon_caveat_internal.h" 22 } 23 24 namespace weave { 25 namespace privet { 26 27 namespace { 28 29 const time_t kJ2000ToTimeT = 946684800; 30 const size_t kMaxMacaroonSize = 1024; 31 const size_t kMaxPendingClaims = 10; 32 const char kInvalidTokenError[] = "invalid_token"; 33 const int kSessionIdTtlMinutes = 1; 34 35 uint32_t ToJ2000Time(const base::Time& time) { 36 return std::max(time.ToTimeT(), kJ2000ToTimeT) - kJ2000ToTimeT; 37 } 38 39 base::Time FromJ2000Time(uint32_t time) { 40 return base::Time::FromTimeT(time + kJ2000ToTimeT); 41 } 42 43 template <class T> 44 void AppendToArray(T value, std::vector<uint8_t>* array) { 45 auto begin = reinterpret_cast<const uint8_t*>(&value); 46 array->insert(array->end(), begin, begin + sizeof(value)); 47 } 48 49 class Caveat { 50 public: 51 Caveat(UwMacaroonCaveatType type, size_t str_len) 52 : buffer_(uw_macaroon_caveat_creation_get_buffsize_(type, str_len)) { 53 CHECK(!buffer_.empty()); 54 } 55 const UwMacaroonCaveat& GetCaveat() const { return caveat_; } 56 57 protected: 58 UwMacaroonCaveat caveat_{}; 59 std::vector<uint8_t> buffer_; 60 61 DISALLOW_COPY_AND_ASSIGN(Caveat); 62 }; 63 64 class ScopeCaveat : public Caveat { 65 public: 66 explicit ScopeCaveat(UwMacaroonCaveatScopeType scope) 67 : Caveat(kUwMacaroonCaveatTypeScope, 0) { 68 CHECK(uw_macaroon_caveat_create_scope_(scope, buffer_.data(), 69 buffer_.size(), &caveat_)); 70 } 71 72 DISALLOW_COPY_AND_ASSIGN(ScopeCaveat); 73 }; 74 75 class TimestampCaveat : public Caveat { 76 public: 77 explicit TimestampCaveat(const base::Time& timestamp) 78 : Caveat(kUwMacaroonCaveatTypeDelegationTimestamp, 0) { 79 CHECK(uw_macaroon_caveat_create_delegation_timestamp_( 80 ToJ2000Time(timestamp), buffer_.data(), buffer_.size(), &caveat_)); 81 } 82 83 DISALLOW_COPY_AND_ASSIGN(TimestampCaveat); 84 }; 85 86 class ExpirationCaveat : public Caveat { 87 public: 88 explicit ExpirationCaveat(const base::Time& timestamp) 89 : Caveat(kUwMacaroonCaveatTypeExpirationAbsolute, 0) { 90 CHECK(uw_macaroon_caveat_create_expiration_absolute_( 91 ToJ2000Time(timestamp), buffer_.data(), buffer_.size(), &caveat_)); 92 } 93 94 DISALLOW_COPY_AND_ASSIGN(ExpirationCaveat); 95 }; 96 97 class UserIdCaveat : public Caveat { 98 public: 99 explicit UserIdCaveat(const std::vector<uint8_t>& id) 100 : Caveat(kUwMacaroonCaveatTypeDelegateeUser, id.size()) { 101 CHECK(uw_macaroon_caveat_create_delegatee_user_( 102 id.data(), id.size(), buffer_.data(), buffer_.size(), &caveat_)); 103 } 104 105 DISALLOW_COPY_AND_ASSIGN(UserIdCaveat); 106 }; 107 108 class AppIdCaveat : public Caveat { 109 public: 110 explicit AppIdCaveat(const std::vector<uint8_t>& id) 111 : Caveat(kUwMacaroonCaveatTypeDelegateeApp, id.size()) { 112 CHECK(uw_macaroon_caveat_create_delegatee_app_( 113 id.data(), id.size(), buffer_.data(), buffer_.size(), &caveat_)); 114 } 115 116 DISALLOW_COPY_AND_ASSIGN(AppIdCaveat); 117 }; 118 119 class ServiceCaveat : public Caveat { 120 public: 121 explicit ServiceCaveat(const std::string& id) 122 : Caveat(kUwMacaroonCaveatTypeDelegateeService, id.size()) { 123 CHECK(uw_macaroon_caveat_create_delegatee_service_( 124 reinterpret_cast<const uint8_t*>(id.data()), id.size(), buffer_.data(), 125 buffer_.size(), &caveat_)); 126 } 127 128 DISALLOW_COPY_AND_ASSIGN(ServiceCaveat); 129 }; 130 131 class SessionIdCaveat : public Caveat { 132 public: 133 explicit SessionIdCaveat(const std::string& id) 134 : Caveat(kUwMacaroonCaveatTypeLanSessionID, id.size()) { 135 CHECK(uw_macaroon_caveat_create_lan_session_id_( 136 reinterpret_cast<const uint8_t*>(id.data()), id.size(), buffer_.data(), 137 buffer_.size(), &caveat_)); 138 } 139 140 DISALLOW_COPY_AND_ASSIGN(SessionIdCaveat); 141 }; 142 143 class ClientAuthTokenCaveat : public Caveat { 144 public: 145 ClientAuthTokenCaveat() 146 : Caveat(kUwMacaroonCaveatTypeClientAuthorizationTokenV1, 0) { 147 CHECK(uw_macaroon_caveat_create_client_authorization_token_( 148 nullptr, 0, buffer_.data(), buffer_.size(), &caveat_)); 149 } 150 151 DISALLOW_COPY_AND_ASSIGN(ClientAuthTokenCaveat); 152 }; 153 154 std::vector<uint8_t> CreateSecret() { 155 std::vector<uint8_t> secret(kSha256OutputSize); 156 base::RandBytes(secret.data(), secret.size()); 157 return secret; 158 } 159 160 bool IsClaimAllowed(RootClientTokenOwner curret, RootClientTokenOwner claimer) { 161 return claimer > curret || claimer == RootClientTokenOwner::kCloud; 162 } 163 164 std::vector<uint8_t> CreateMacaroonToken( 165 const std::vector<uint8_t>& secret, 166 const base::Time& time, 167 const std::vector<const UwMacaroonCaveat*>& caveats) { 168 CHECK_EQ(kSha256OutputSize, secret.size()); 169 170 UwMacaroonContext context{}; 171 CHECK(uw_macaroon_context_create_(ToJ2000Time(time), nullptr, 0, &context)); 172 173 UwMacaroon macaroon{}; 174 CHECK(uw_macaroon_create_from_root_key_(&macaroon, secret.data(), 175 secret.size(), &context, 176 caveats.data(), caveats.size())); 177 178 std::vector<uint8_t> serialized_token(kMaxMacaroonSize); 179 size_t len = 0; 180 CHECK(uw_macaroon_serialize_(&macaroon, serialized_token.data(), 181 serialized_token.size(), &len)); 182 serialized_token.resize(len); 183 184 return serialized_token; 185 } 186 187 std::vector<uint8_t> ExtendMacaroonToken( 188 const UwMacaroon& macaroon, 189 const base::Time& time, 190 const std::vector<const UwMacaroonCaveat*>& caveats) { 191 UwMacaroonContext context{}; 192 CHECK(uw_macaroon_context_create_(ToJ2000Time(time), nullptr, 0, &context)); 193 194 UwMacaroon prev_macaroon = macaroon; 195 std::vector<uint8_t> prev_buffer(kMaxMacaroonSize); 196 std::vector<uint8_t> new_buffer(kMaxMacaroonSize); 197 198 for (auto caveat : caveats) { 199 UwMacaroon new_macaroon{}; 200 CHECK(uw_macaroon_extend_(&prev_macaroon, &new_macaroon, &context, caveat, 201 new_buffer.data(), new_buffer.size())); 202 new_buffer.swap(prev_buffer); 203 prev_macaroon = new_macaroon; 204 } 205 206 std::vector<uint8_t> serialized_token(kMaxMacaroonSize); 207 size_t len = 0; 208 CHECK(uw_macaroon_serialize_(&prev_macaroon, serialized_token.data(), 209 serialized_token.size(), &len)); 210 serialized_token.resize(len); 211 212 return serialized_token; 213 } 214 215 bool LoadMacaroon(const std::vector<uint8_t>& token, 216 std::vector<uint8_t>* buffer, 217 UwMacaroon* macaroon, 218 ErrorPtr* error) { 219 buffer->resize(kMaxMacaroonSize); 220 if (!uw_macaroon_deserialize_(token.data(), token.size(), buffer->data(), 221 buffer->size(), macaroon)) { 222 return Error::AddTo(error, FROM_HERE, kInvalidTokenError, 223 "Invalid token format"); 224 } 225 return true; 226 } 227 228 bool VerifyMacaroon(const std::vector<uint8_t>& secret, 229 const UwMacaroon& macaroon, 230 const base::Time& time, 231 UwMacaroonValidationResult* result, 232 ErrorPtr* error) { 233 CHECK_EQ(kSha256OutputSize, secret.size()); 234 UwMacaroonContext context = {}; 235 CHECK(uw_macaroon_context_create_(ToJ2000Time(time), nullptr, 0, &context)); 236 237 if (!uw_macaroon_validate_(&macaroon, secret.data(), secret.size(), &context, 238 result)) { 239 return Error::AddTo(error, FROM_HERE, "invalid_token", 240 "Invalid token signature"); 241 } 242 return true; 243 } 244 245 UwMacaroonCaveatScopeType ToMacaroonScope(AuthScope scope) { 246 switch (scope) { 247 case AuthScope::kViewer: 248 return kUwMacaroonCaveatScopeTypeViewer; 249 case AuthScope::kUser: 250 return kUwMacaroonCaveatScopeTypeUser; 251 case AuthScope::kManager: 252 return kUwMacaroonCaveatScopeTypeManager; 253 case AuthScope::kOwner: 254 return kUwMacaroonCaveatScopeTypeOwner; 255 default: 256 NOTREACHED() << EnumToString(scope); 257 } 258 return kUwMacaroonCaveatScopeTypeViewer; 259 } 260 261 AuthScope FromMacaroonScope(uint32_t scope) { 262 if (scope <= kUwMacaroonCaveatScopeTypeOwner) 263 return AuthScope::kOwner; 264 if (scope <= kUwMacaroonCaveatScopeTypeManager) 265 return AuthScope::kManager; 266 if (scope <= kUwMacaroonCaveatScopeTypeUser) 267 return AuthScope::kUser; 268 if (scope <= kUwMacaroonCaveatScopeTypeViewer) 269 return AuthScope::kViewer; 270 return AuthScope::kNone; 271 } 272 273 } // namespace 274 275 AuthManager::AuthManager(Config* config, 276 const std::vector<uint8_t>& certificate_fingerprint) 277 : config_{config}, 278 certificate_fingerprint_{certificate_fingerprint}, 279 access_secret_{CreateSecret()} { 280 if (config_) { 281 SetAuthSecret(config_->GetSettings().secret, 282 config_->GetSettings().root_client_token_owner); 283 } else { 284 SetAuthSecret({}, RootClientTokenOwner::kNone); 285 } 286 } 287 288 AuthManager::AuthManager(const std::vector<uint8_t>& auth_secret, 289 const std::vector<uint8_t>& certificate_fingerprint, 290 const std::vector<uint8_t>& access_secret, 291 base::Clock* clock) 292 : AuthManager(nullptr, certificate_fingerprint) { 293 access_secret_ = access_secret.size() == kSha256OutputSize ? access_secret 294 : CreateSecret(); 295 SetAuthSecret(auth_secret, RootClientTokenOwner::kNone); 296 if (clock) 297 clock_ = clock; 298 } 299 300 void AuthManager::SetAuthSecret(const std::vector<uint8_t>& secret, 301 RootClientTokenOwner owner) { 302 auth_secret_ = secret; 303 304 if (auth_secret_.size() != kSha256OutputSize) { 305 auth_secret_ = CreateSecret(); 306 owner = RootClientTokenOwner::kNone; 307 } 308 309 if (!config_ || (config_->GetSettings().secret == auth_secret_ && 310 config_->GetSettings().root_client_token_owner == owner)) { 311 return; 312 } 313 314 Config::Transaction change{config_}; 315 change.set_secret(secret); 316 change.set_root_client_token_owner(owner); 317 change.Commit(); 318 } 319 320 AuthManager::~AuthManager() {} 321 322 std::vector<uint8_t> AuthManager::CreateAccessToken(const UserInfo& user_info, 323 base::TimeDelta ttl) const { 324 const base::Time now = Now(); 325 TimestampCaveat issued{now}; 326 ScopeCaveat scope{ToMacaroonScope(user_info.scope())}; 327 // Macaroons have no caveats for auth type. So we just append the type to the 328 // user ID. 329 std::vector<uint8_t> id_with_type{user_info.id().user}; 330 id_with_type.push_back(static_cast<uint8_t>(user_info.id().type)); 331 UserIdCaveat user{id_with_type}; 332 AppIdCaveat app{user_info.id().app}; 333 ExpirationCaveat expiration{now + ttl}; 334 return CreateMacaroonToken( 335 access_secret_, now, 336 { 337 338 &issued.GetCaveat(), &scope.GetCaveat(), &user.GetCaveat(), 339 &app.GetCaveat(), &expiration.GetCaveat(), 340 }); 341 } 342 343 bool AuthManager::ParseAccessToken(const std::vector<uint8_t>& token, 344 UserInfo* user_info, 345 ErrorPtr* error) const { 346 std::vector<uint8_t> buffer; 347 UwMacaroon macaroon{}; 348 349 UwMacaroonValidationResult result{}; 350 const base::Time now = Now(); 351 if (!LoadMacaroon(token, &buffer, &macaroon, error) || 352 macaroon.num_caveats != 5 || 353 !VerifyMacaroon(access_secret_, macaroon, now, &result, error)) { 354 return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization, 355 "Invalid token"); 356 } 357 358 AuthScope auth_scope{FromMacaroonScope(result.granted_scope)}; 359 if (auth_scope == AuthScope::kNone) { 360 return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization, 361 "Invalid token data"); 362 } 363 364 // If token is valid and token was not extended, it should has precisely this 365 // values. 366 CHECK_GE(FromJ2000Time(result.expiration_time), now); 367 CHECK_EQ(2u, result.num_delegatees); 368 CHECK_EQ(kUwMacaroonDelegateeTypeUser, result.delegatees[0].type); 369 CHECK_EQ(kUwMacaroonDelegateeTypeApp, result.delegatees[1].type); 370 CHECK_GT(result.delegatees[0].id_len, 1u); 371 std::vector<uint8_t> user_id{ 372 result.delegatees[0].id, 373 result.delegatees[0].id + result.delegatees[0].id_len}; 374 // Last byte is used for type. See |CreateAccessToken|. 375 AuthType type = static_cast<AuthType>(user_id.back()); 376 user_id.pop_back(); 377 378 std::vector<uint8_t> app_id{ 379 result.delegatees[1].id, 380 result.delegatees[1].id + result.delegatees[1].id_len}; 381 if (user_info) 382 *user_info = UserInfo{auth_scope, UserAppId{type, user_id, app_id}}; 383 384 return true; 385 } 386 387 std::vector<uint8_t> AuthManager::ClaimRootClientAuthToken( 388 RootClientTokenOwner owner, 389 ErrorPtr* error) { 390 CHECK(RootClientTokenOwner::kNone != owner); 391 if (config_) { 392 auto current = config_->GetSettings().root_client_token_owner; 393 if (!IsClaimAllowed(current, owner)) { 394 Error::AddToPrintf(error, FROM_HERE, errors::kAlreadyClaimed, 395 "Device already claimed by '%s'", 396 EnumToString(current).c_str()); 397 return {}; 398 } 399 }; 400 401 pending_claims_.push_back(std::make_pair( 402 std::unique_ptr<AuthManager>{new AuthManager{nullptr, {}}}, owner)); 403 if (pending_claims_.size() > kMaxPendingClaims) 404 pending_claims_.pop_front(); 405 return pending_claims_.back().first->GetRootClientAuthToken(owner); 406 } 407 408 bool AuthManager::ConfirmClientAuthToken(const std::vector<uint8_t>& token, 409 ErrorPtr* error) { 410 // Cover case when caller sent confirm twice. 411 if (pending_claims_.empty()) 412 return IsValidAuthToken(token, error); 413 414 auto claim = 415 std::find_if(pending_claims_.begin(), pending_claims_.end(), 416 [&token](const decltype(pending_claims_)::value_type& auth) { 417 return auth.first->IsValidAuthToken(token, nullptr); 418 }); 419 if (claim == pending_claims_.end()) { 420 return Error::AddTo(error, FROM_HERE, errors::kNotFound, "Unknown claim"); 421 } 422 423 SetAuthSecret(claim->first->GetAuthSecret(), claim->second); 424 pending_claims_.clear(); 425 return true; 426 } 427 428 std::vector<uint8_t> AuthManager::GetRootClientAuthToken( 429 RootClientTokenOwner owner) const { 430 CHECK(RootClientTokenOwner::kNone != owner); 431 ClientAuthTokenCaveat auth_token; 432 const base::Time now = Now(); 433 TimestampCaveat issued{now}; 434 435 ServiceCaveat client{owner == RootClientTokenOwner::kCloud ? "google.com" 436 : ""}; 437 return CreateMacaroonToken( 438 auth_secret_, now, 439 { 440 &auth_token.GetCaveat(), &issued.GetCaveat(), &client.GetCaveat(), 441 }); 442 } 443 444 base::Time AuthManager::Now() const { 445 return clock_->Now(); 446 } 447 448 bool AuthManager::IsValidAuthToken(const std::vector<uint8_t>& token, 449 ErrorPtr* error) const { 450 std::vector<uint8_t> buffer; 451 UwMacaroon macaroon{}; 452 UwMacaroonValidationResult result{}; 453 if (!LoadMacaroon(token, &buffer, &macaroon, error) || 454 !VerifyMacaroon(auth_secret_, macaroon, Now(), &result, error)) { 455 return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, 456 "Invalid token"); 457 } 458 return true; 459 } 460 461 bool AuthManager::CreateAccessTokenFromAuth( 462 const std::vector<uint8_t>& auth_token, 463 base::TimeDelta ttl, 464 std::vector<uint8_t>* access_token, 465 AuthScope* access_token_scope, 466 base::TimeDelta* access_token_ttl, 467 ErrorPtr* error) const { 468 std::vector<uint8_t> buffer; 469 UwMacaroon macaroon{}; 470 UwMacaroonValidationResult result{}; 471 const base::Time now = Now(); 472 if (!LoadMacaroon(auth_token, &buffer, &macaroon, error) || 473 !VerifyMacaroon(auth_secret_, macaroon, now, &result, error)) { 474 return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, 475 "Invalid token"); 476 } 477 478 AuthScope auth_scope{FromMacaroonScope(result.granted_scope)}; 479 if (auth_scope == AuthScope::kNone) { 480 return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, 481 "Invalid token data"); 482 } 483 484 // TODO: Integrate black list checks. 485 auto delegates_rbegin = std::reverse_iterator<const UwMacaroonDelegateeInfo*>( 486 result.delegatees + result.num_delegatees); 487 auto delegates_rend = 488 std::reverse_iterator<const UwMacaroonDelegateeInfo*>(result.delegatees); 489 auto last_user_id = 490 std::find_if(delegates_rbegin, delegates_rend, 491 [](const UwMacaroonDelegateeInfo& delegatee) { 492 return delegatee.type == kUwMacaroonDelegateeTypeUser; 493 }); 494 auto last_app_id = 495 std::find_if(delegates_rbegin, delegates_rend, 496 [](const UwMacaroonDelegateeInfo& delegatee) { 497 return delegatee.type == kUwMacaroonDelegateeTypeApp; 498 }); 499 500 if (last_user_id == delegates_rend || !last_user_id->id_len) { 501 return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, 502 "User ID is missing"); 503 } 504 505 const char* session_id = reinterpret_cast<const char*>(result.lan_session_id); 506 if (!IsValidSessionId({session_id, session_id + result.lan_session_id_len})) { 507 return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, 508 "Invalid session id"); 509 } 510 511 CHECK_GE(FromJ2000Time(result.expiration_time), now); 512 513 if (!access_token) 514 return true; 515 516 std::vector<uint8_t> user_id{last_user_id->id, 517 last_user_id->id + last_user_id->id_len}; 518 std::vector<uint8_t> app_id; 519 if (last_app_id != delegates_rend) 520 app_id.assign(last_app_id->id, last_app_id->id + last_app_id->id_len); 521 522 UserInfo info{auth_scope, {AuthType::kLocal, user_id, app_id}}; 523 524 ttl = std::min(ttl, FromJ2000Time(result.expiration_time) - now); 525 *access_token = CreateAccessToken(info, ttl); 526 527 if (access_token_scope) 528 *access_token_scope = info.scope(); 529 530 if (access_token_ttl) 531 *access_token_ttl = ttl; 532 return true; 533 } 534 535 std::string AuthManager::CreateSessionId() const { 536 return std::to_string(ToJ2000Time(Now())) + ":" + 537 std::to_string(++session_counter_); 538 } 539 540 bool AuthManager::IsValidSessionId(const std::string& session_id) const { 541 base::Time ssid_time = FromJ2000Time(std::atoi(session_id.c_str())); 542 return Now() - base::TimeDelta::FromMinutes(kSessionIdTtlMinutes) <= 543 ssid_time && 544 ssid_time <= Now(); 545 } 546 547 std::vector<uint8_t> AuthManager::DelegateToUser( 548 const std::vector<uint8_t>& token, 549 base::TimeDelta ttl, 550 const UserInfo& user_info) const { 551 std::vector<uint8_t> buffer; 552 UwMacaroon macaroon{}; 553 CHECK(LoadMacaroon(token, &buffer, &macaroon, nullptr)); 554 555 const base::Time now = Now(); 556 TimestampCaveat issued{now}; 557 ExpirationCaveat expiration{now + ttl}; 558 ScopeCaveat scope{ToMacaroonScope(user_info.scope())}; 559 UserIdCaveat user{user_info.id().user}; 560 AppIdCaveat app{user_info.id().app}; 561 SessionIdCaveat session{CreateSessionId()}; 562 563 std::vector<const UwMacaroonCaveat*> caveats{ 564 &issued.GetCaveat(), &expiration.GetCaveat(), &scope.GetCaveat(), 565 &user.GetCaveat(), 566 }; 567 568 if (!user_info.id().app.empty()) 569 caveats.push_back(&app.GetCaveat()); 570 571 caveats.push_back(&session.GetCaveat()); 572 573 return ExtendMacaroonToken(macaroon, now, caveats); 574 } 575 576 } // namespace privet 577 } // namespace weave 578