aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVitaly Buka <vitalybuka@google.com>2016-01-27 18:25:14 -0800
committerVitaly Buka <vitalybuka@google.com>2016-01-28 18:52:12 +0000
commitd5f7aabc2a7e414c63dc5a20a294c71a54111910 (patch)
treec5ff24d4768972a6a098f5e5917b977ba06dc18c
parentd7c6deb0576805c0e043686e220a7a27e17b50d4 (diff)
downloadlibweave-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.cc64
-rw-r--r--src/privet/auth_manager.h10
-rw-r--r--src/privet/auth_manager_unittest.cc116
-rw-r--r--src/privet/security_manager.cc2
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(