diff options
author | Vitaly Buka <vitalybuka@google.com> | 2016-01-27 18:25:14 -0800 |
---|---|---|
committer | Vitaly Buka <vitalybuka@google.com> | 2016-01-28 18:52:12 +0000 |
commit | d5f7aabc2a7e414c63dc5a20a294c71a54111910 (patch) | |
tree | c5ff24d4768972a6a098f5e5917b977ba06dc18c | |
parent | d7c6deb0576805c0e043686e220a7a27e17b50d4 (diff) | |
download | libweave-d5f7aabc2a7e414c63dc5a20a294c71a54111910.tar.gz |
Add session ID validation
BUG: 26292014
Change-Id: I2a71dbf3dbc4b422c8f9bedd806f459d2bc35333
Reviewed-on: https://weave-review.googlesource.com/2380
Reviewed-by: Alex Vakulenko <avakulenko@google.com>
-rw-r--r-- | src/privet/auth_manager.cc | 64 | ||||
-rw-r--r-- | src/privet/auth_manager.h | 10 | ||||
-rw-r--r-- | src/privet/auth_manager_unittest.cc | 116 | ||||
-rw-r--r-- | src/privet/security_manager.cc | 2 |
4 files changed, 153 insertions, 39 deletions
diff --git a/src/privet/auth_manager.cc b/src/privet/auth_manager.cc index fd15c21..0a2b75a 100644 --- a/src/privet/auth_manager.cc +++ b/src/privet/auth_manager.cc @@ -30,6 +30,7 @@ const time_t kJ2000ToTimeT = 946684800; const size_t kMaxMacaroonSize = 1024; const size_t kMaxPendingClaims = 10; const char kInvalidTokenError[] = "invalid_token"; +const int kSessionIdTtlMinutes = 1; uint32_t ToJ2000Time(const base::Time& time) { return std::max(time.ToTimeT(), kJ2000ToTimeT) - kJ2000ToTimeT; @@ -95,11 +96,11 @@ class ExpirationCaveat : public Caveat { class UserIdCaveat : public Caveat { public: - explicit UserIdCaveat(const std::string& user_id) - : Caveat(kUwMacaroonCaveatTypeDelegateeUser, user_id.size()) { + explicit UserIdCaveat(const std::string& id) + : Caveat(kUwMacaroonCaveatTypeDelegateeUser, id.size()) { CHECK(uw_macaroon_caveat_create_delegatee_user_( - reinterpret_cast<const uint8_t*>(user_id.data()), user_id.size(), - buffer_.data(), buffer_.size(), &caveat_)); + reinterpret_cast<const uint8_t*>(id.data()), id.size(), buffer_.data(), + buffer_.size(), &caveat_)); } DISALLOW_COPY_AND_ASSIGN(UserIdCaveat); @@ -117,6 +118,18 @@ class UserIdCaveat : public Caveat { // DISALLOW_COPY_AND_ASSIGN(ServiceCaveat); // }; +class SessionIdCaveat : public Caveat { + public: + explicit SessionIdCaveat(const std::string& id) + : Caveat(kUwMacaroonCaveatTypeLanSessionID, id.size()) { + CHECK(uw_macaroon_caveat_create_lan_session_id_( + reinterpret_cast<const uint8_t*>(id.data()), id.size(), buffer_.data(), + buffer_.size(), &caveat_)); + } + + DISALLOW_COPY_AND_ASSIGN(SessionIdCaveat); +}; + class ClientAuthTokenCaveat : public Caveat { public: ClientAuthTokenCaveat() @@ -439,12 +452,11 @@ bool AuthManager::CreateAccessTokenFromAuth( AuthScope auth_scope{FromMacaroonScope(result.granted_scope)}; if (auth_scope == AuthScope::kNone) { - return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization, + return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, "Invalid token data"); } // TODO: Integrate black list checks. - // TODO: Check session id. auto delegates_rbegin = std::reverse_iterator<const UwMacaroonDelegateeInfo*>( result.delegatees + result.num_delegatees); auto delegates_rend = @@ -456,10 +468,16 @@ bool AuthManager::CreateAccessTokenFromAuth( }); if (last_user_id == delegates_rend || !last_user_id->id_len) { - return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization, + return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, "User ID is missing"); } + const char* session_id = reinterpret_cast<const char*>(result.lan_session_id); + if (!IsValidSessionId({session_id, session_id + result.lan_session_id_len})) { + return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, + "Invalid session id"); + } + CHECK_GE(FromJ2000Time(result.expiration_time), now); if (!access_token) @@ -480,27 +498,39 @@ bool AuthManager::CreateAccessTokenFromAuth( return true; } -std::vector<uint8_t> AuthManager::CreateSessionId() { - std::vector<uint8_t> result; - AppendToArray(Now().ToTimeT(), &result); - AppendToArray(++session_counter_, &result); - return result; +std::string AuthManager::CreateSessionId() const { + return std::to_string(ToJ2000Time(Now())) + ":" + + std::to_string(++session_counter_); +} + +bool AuthManager::IsValidSessionId(const std::string& session_id) const { + base::Time ssid_time = FromJ2000Time(std::atoi(session_id.c_str())); + return Now() - base::TimeDelta::FromMinutes(kSessionIdTtlMinutes) <= + ssid_time && + ssid_time <= Now(); } std::vector<uint8_t> AuthManager::DelegateToUser( const std::vector<uint8_t>& token, + base::TimeDelta ttl, const UserInfo& user_info) const { std::vector<uint8_t> buffer; UwMacaroon macaroon{}; CHECK(LoadMacaroon(token, &buffer, &macaroon, nullptr)); + const base::Time now = Now(); + TimestampCaveat issued{now}; + ExpirationCaveat expiration{now + ttl}; ScopeCaveat scope{ToMacaroonScope(user_info.scope())}; - UserIdCaveat user_caveat{user_info.user_id()}; + UserIdCaveat user{user_info.user_id()}; + SessionIdCaveat session{CreateSessionId()}; - return ExtendMacaroonToken(macaroon, Now(), - { - &scope.GetCaveat(), &user_caveat.GetCaveat(), - }); + return ExtendMacaroonToken( + macaroon, now, + { + &issued.GetCaveat(), &expiration.GetCaveat(), &scope.GetCaveat(), + &user.GetCaveat(), &session.GetCaveat(), + }); } } // namespace privet diff --git a/src/privet/auth_manager.h b/src/privet/auth_manager.h index 0fa90a7..f0a5761 100644 --- a/src/privet/auth_manager.h +++ b/src/privet/auth_manager.h @@ -68,19 +68,21 @@ class AuthManager { void SetAuthSecret(const std::vector<uint8_t>& secret, RootClientTokenOwner owner); - std::vector<uint8_t> CreateSessionId(); + std::string CreateSessionId() const; + bool IsValidSessionId(const std::string& session_id) const; private: - FRIEND_TEST_ALL_PREFIXES(AuthManagerClaimTest, CreateAccessTokenFromAuth); + friend class AuthManagerTest; - // Test helper. + // Test helpers. Device does not need to implement delegation. std::vector<uint8_t> DelegateToUser(const std::vector<uint8_t>& token, + base::TimeDelta ttl, const UserInfo& user_info) const; Config* config_{nullptr}; // Can be nullptr for tests. base::DefaultClock default_clock_; base::Clock* clock_{&default_clock_}; - uint32_t session_counter_{0}; + mutable uint32_t session_counter_{0}; std::vector<uint8_t> auth_secret_; // Persistent. std::vector<uint8_t> certificate_fingerprint_; diff --git a/src/privet/auth_manager_unittest.cc b/src/privet/auth_manager_unittest.cc index c5fba3c..d74abd8 100644 --- a/src/privet/auth_manager_unittest.cc +++ b/src/privet/auth_manager_unittest.cc @@ -29,6 +29,11 @@ class AuthManagerTest : public testing::Test { } protected: + std::vector<uint8_t> DelegateToUser(const std::vector<uint8_t>& token, + base::TimeDelta ttl, + const UserInfo& user_info) const { + return auth_.DelegateToUser(token, ttl, user_info); + } const std::vector<uint8_t> kSecret1{ 78, 40, 39, 68, 29, 19, 70, 86, 38, 61, 13, 55, 33, 32, 51, 52, 34, 43, 97, 48, 8, 56, 11, 99, 50, 59, 24, 26, 31, 71, 76, 28}; @@ -129,6 +134,10 @@ TEST_F(AuthManagerTest, ParseAccessToken) { .WillRepeatedly(Return(kStartTime + base::TimeDelta::FromSeconds(i))); EXPECT_TRUE(auth.ParseAccessToken(token, &user_info, nullptr)); + auto extended = DelegateToUser(token, base::TimeDelta::FromSeconds(1000), + UserInfo{AuthScope::kUser, "234"}); + EXPECT_FALSE(auth.ParseAccessToken(extended, &user_info, nullptr)); + EXPECT_CALL(clock_, Now()) .WillRepeatedly( Return(kStartTime + base::TimeDelta::FromSeconds(i + 1))); @@ -176,6 +185,96 @@ TEST_F(AuthManagerTest, IsValidAuthToken) { } } +TEST_F(AuthManagerTest, CreateSessionId) { + EXPECT_EQ("463315200:1", auth_.CreateSessionId()); +} + +TEST_F(AuthManagerTest, IsValidSessionId) { + EXPECT_TRUE(auth_.IsValidSessionId("463315200:1")); + EXPECT_TRUE(auth_.IsValidSessionId("463315200:2")); + EXPECT_TRUE(auth_.IsValidSessionId("463315150")); + + // Future + EXPECT_FALSE(auth_.IsValidSessionId("463315230:1")); + + // Expired + EXPECT_FALSE(auth_.IsValidSessionId("463315100:1")); +} + +TEST_F(AuthManagerTest, CreateAccessTokenFromAuth) { + std::vector<uint8_t> access_token; + AuthScope scope; + base::TimeDelta ttl; + auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); + auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(1000), + UserInfo{AuthScope::kUser, "234"}); + EXPECT_EQ( + "WEWIQxkgAUYIGhudoQBCCUBGCBobnaEARgUaG52k6EIBDkUJQzIzNE0RSzQ2MzMxNTIwMDox" + "UHN8Lm+CUQo7s84Sh+grpAE=", + Base64Encode(extended)); + EXPECT_TRUE( + auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1), + &access_token, &scope, &ttl, nullptr)); + UserInfo user_info; + EXPECT_TRUE(auth_.ParseAccessToken(access_token, &user_info, nullptr)); + EXPECT_EQ(scope, user_info.scope()); + EXPECT_EQ(AuthScope::kUser, user_info.scope()); + + EXPECT_EQ("234", user_info.user_id()); +} + +TEST_F(AuthManagerTest, CreateAccessTokenFromAuthNotMinted) { + std::vector<uint8_t> access_token; + auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); + ErrorPtr error; + EXPECT_FALSE(auth_.CreateAccessTokenFromAuth( + root, base::TimeDelta::FromDays(1), nullptr, nullptr, nullptr, &error)); + EXPECT_TRUE(error->HasError("invalidAuthCode")); +} + +TEST_F(AuthManagerTest, CreateAccessTokenFromAuthValidateAfterSomeTime) { + auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); + auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(1000), + UserInfo{AuthScope::kUser, "234"}); + + // new_time < session_id_expiration < token_expiration. + auto new_time = clock_.Now() + base::TimeDelta::FromSeconds(15); + EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); + EXPECT_TRUE( + auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1), + nullptr, nullptr, nullptr, nullptr)); +} + +TEST_F(AuthManagerTest, CreateAccessTokenFromAuthExpired) { + auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); + auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(10), + UserInfo{AuthScope::kUser, "234"}); + ErrorPtr error; + + // token_expiration < new_time < session_id_expiration. + auto new_time = clock_.Now() + base::TimeDelta::FromSeconds(15); + EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); + EXPECT_FALSE( + auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1), + nullptr, nullptr, nullptr, &error)); + EXPECT_TRUE(error->HasError("invalidAuthCode")); +} + +TEST_F(AuthManagerTest, CreateAccessTokenFromAuthExpiredSessionid) { + auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); + auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(1000), + UserInfo{AuthScope::kUser, "234"}); + ErrorPtr error; + + // session_id_expiration < new_time < token_expiration. + auto new_time = clock_.Now() + base::TimeDelta::FromSeconds(200); + EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); + EXPECT_FALSE( + auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1), + nullptr, nullptr, nullptr, &error)); + EXPECT_TRUE(error->HasError("invalidAuthCode")); +} + class AuthManagerClaimTest : public testing::Test { public: void SetUp() override { EXPECT_EQ(auth_.GetAuthSecret().size(), 32u); } @@ -251,22 +350,5 @@ TEST_F(AuthManagerClaimTest, TokenOverflow) { EXPECT_FALSE(auth_.ConfirmClientAuthToken(token, nullptr)); } -TEST_F(AuthManagerClaimTest, CreateAccessTokenFromAuth) { - std::vector<uint8_t> access_token; - AuthScope scope; - base::TimeDelta ttl; - auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); - auto extended = auth_.DelegateToUser(root, UserInfo{AuthScope::kUser, "234"}); - EXPECT_TRUE( - auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1), - &access_token, &scope, &ttl, nullptr)); - UserInfo user_info; - EXPECT_TRUE(auth_.ParseAccessToken(access_token, &user_info, nullptr)); - EXPECT_EQ(scope, user_info.scope()); - EXPECT_EQ(AuthScope::kUser, user_info.scope()); - - EXPECT_EQ("234", user_info.user_id()); -} - } // namespace privet } // namespace weave diff --git a/src/privet/security_manager.cc b/src/privet/security_manager.cc index 0f00699..04164b3 100644 --- a/src/privet/security_manager.cc +++ b/src/privet/security_manager.cc @@ -388,7 +388,7 @@ bool SecurityManager::CancelPairing(const std::string& session_id, } std::string SecurityManager::CreateSessionId() { - return Base64Encode(auth_manager_->CreateSessionId()); + return auth_manager_->CreateSessionId(); } void SecurityManager::RegisterPairingListeners( |