blob: 0753a2b73c255ffc0919f0be23c87a8f3315d2fd [file] [log] [blame]
Vitaly Bukaf08caeb2015-12-02 13:47:48 -08001// 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
Vitaly Buka131b8892015-12-22 17:05:16 -08007#include <algorithm>
8
Vitaly Buka66a01e02015-12-20 18:37:14 -08009#include <base/guid.h>
Vitaly Bukaf08caeb2015-12-02 13:47:48 -080010#include <base/rand_util.h>
11#include <base/strings/string_number_conversions.h>
12
Vitaly Buka229113e2015-12-13 23:12:42 -080013#include "src/config.h"
Vitaly Bukaf08caeb2015-12-02 13:47:48 -080014#include "src/data_encoding.h"
Vitaly Bukac3bc82a2015-12-14 23:24:13 -080015#include "src/privet/constants.h"
Vitaly Bukaf08caeb2015-12-02 13:47:48 -080016#include "src/privet/openssl_utils.h"
17#include "src/string_utils.h"
18
Vitaly Bukaa37056e2015-12-09 14:53:39 -080019extern "C" {
20#include "third_party/libuweave/src/macaroon.h"
21}
22
Vitaly Bukaf08caeb2015-12-02 13:47:48 -080023namespace weave {
24namespace privet {
25
26namespace {
27
Vitaly Bukaa37056e2015-12-09 14:53:39 -080028const size_t kMaxMacaroonSize = 1024;
Vitaly Bukacc77fad2015-12-13 21:04:46 -080029const size_t kMaxPendingClaims = 10;
Vitaly Buka131b8892015-12-22 17:05:16 -080030const char kInvalidTokenError[] = "invalid_token";
Vitaly Bukaf08caeb2015-12-02 13:47:48 -080031
Vitaly Buka483d5972015-12-16 13:45:35 -080032template <class T>
33void AppendToArray(T value, std::vector<uint8_t>* array) {
34 auto begin = reinterpret_cast<const uint8_t*>(&value);
35 array->insert(array->end(), begin, begin + sizeof(value));
36}
37
Vitaly Bukaa37056e2015-12-09 14:53:39 -080038class Caveat {
39 public:
Vitaly Buka131b8892015-12-22 17:05:16 -080040 // TODO(vitalybuka): Use _get_buffer_size_ when available.
41 Caveat(UwMacaroonCaveatType type, uint32_t value) : buffer(8) {
42 CHECK(uw_macaroon_caveat_create_with_uint_(type, value, buffer.data(),
43 buffer.size(), &caveat));
44 }
45
46 // TODO(vitalybuka): Use _get_buffer_size_ when available.
47 Caveat(UwMacaroonCaveatType type, const std::string& value)
48 : buffer(std::max<size_t>(value.size(), 32u) * 2) {
49 CHECK(uw_macaroon_caveat_create_with_str_(
50 type, reinterpret_cast<const uint8_t*>(value.data()), value.size(),
51 buffer.data(), buffer.size(), &caveat));
Vitaly Bukaa37056e2015-12-09 14:53:39 -080052 }
53
54 const UwMacaroonCaveat& GetCaveat() const { return caveat; }
55
56 private:
57 UwMacaroonCaveat caveat;
Vitaly Buka131b8892015-12-22 17:05:16 -080058 std::vector<uint8_t> buffer;
Vitaly Bukaa37056e2015-12-09 14:53:39 -080059
60 DISALLOW_COPY_AND_ASSIGN(Caveat);
61};
62
Vitaly Bukae0df73a2015-12-22 16:50:22 -080063bool CheckCaveatType(const UwMacaroonCaveat& caveat,
64 UwMacaroonCaveatType type,
65 ErrorPtr* error) {
66 UwMacaroonCaveatType caveat_type{};
67 if (!uw_macaroon_caveat_get_type_(&caveat, &caveat_type)) {
Vitaly Buka48a86692016-01-21 17:15:58 -080068 Error::AddTo(error, FROM_HERE, kInvalidTokenError, "Unable to get type");
Vitaly Bukae0df73a2015-12-22 16:50:22 -080069 return false;
70 }
71
72 if (caveat_type != type) {
Vitaly Buka48a86692016-01-21 17:15:58 -080073 Error::AddTo(error, FROM_HERE, kInvalidTokenError,
Vitaly Bukae0df73a2015-12-22 16:50:22 -080074 "Unexpected caveat type");
75 return false;
76 }
77
78 return true;
79}
80
81bool ReadCaveat(const UwMacaroonCaveat& caveat,
82 UwMacaroonCaveatType type,
83 uint32_t* value,
84 ErrorPtr* error) {
85 if (!CheckCaveatType(caveat, type, error))
86 return false;
87
88 if (!uw_macaroon_caveat_get_value_uint_(&caveat, value)) {
Vitaly Buka48a86692016-01-21 17:15:58 -080089 Error::AddTo(error, FROM_HERE, kInvalidTokenError, "Unable to read caveat");
Vitaly Bukae0df73a2015-12-22 16:50:22 -080090 return false;
91 }
92
93 return true;
94}
95
96bool ReadCaveat(const UwMacaroonCaveat& caveat,
97 UwMacaroonCaveatType type,
98 std::string* value,
99 ErrorPtr* error) {
100 if (!CheckCaveatType(caveat, type, error))
101 return false;
102
103 const uint8_t* start{nullptr};
104 size_t size{0};
105 if (!uw_macaroon_caveat_get_value_str_(&caveat, &start, &size)) {
Vitaly Buka48a86692016-01-21 17:15:58 -0800106 Error::AddTo(error, FROM_HERE, kInvalidTokenError, "Unable to read caveat");
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800107 return false;
108 }
109
110 value->assign(reinterpret_cast<const char*>(start), size);
111 return true;
112}
113
Vitaly Buka229113e2015-12-13 23:12:42 -0800114std::vector<uint8_t> CreateSecret() {
115 std::vector<uint8_t> secret(kSha256OutputSize);
116 base::RandBytes(secret.data(), secret.size());
117 return secret;
118}
119
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800120bool IsClaimAllowed(RootClientTokenOwner curret, RootClientTokenOwner claimer) {
121 return claimer > curret || claimer == RootClientTokenOwner::kCloud;
122}
123
Vitaly Buka7a25a3d2015-12-22 15:25:51 -0800124std::vector<uint8_t> CreateMacaroonToken(
125 const std::vector<uint8_t>& secret,
126 const std::vector<UwMacaroonCaveat>& caveats) {
127 CHECK_EQ(kSha256OutputSize, secret.size());
128 UwMacaroon macaroon{};
129 CHECK(uw_macaroon_new_from_root_key_(&macaroon, secret.data(), secret.size(),
130 caveats.data(), caveats.size()));
131
132 std::vector<uint8_t> token(kMaxMacaroonSize);
133 size_t len = 0;
134 CHECK(uw_macaroon_dump_(&macaroon, token.data(), token.size(), &len));
135 token.resize(len);
136
137 return token;
138}
139
Vitaly Buka131b8892015-12-22 17:05:16 -0800140bool LoadMacaroon(const std::vector<uint8_t>& token,
141 std::vector<uint8_t>* buffer,
142 UwMacaroon* macaroon,
143 ErrorPtr* error) {
144 buffer->resize(kMaxMacaroonSize);
145 if (!uw_macaroon_load_(token.data(), token.size(), buffer->data(),
146 buffer->size(), macaroon)) {
Vitaly Buka48a86692016-01-21 17:15:58 -0800147 Error::AddTo(error, FROM_HERE, kInvalidTokenError, "Invalid token format");
Vitaly Buka131b8892015-12-22 17:05:16 -0800148 return false;
149 }
150 return true;
151}
152
153bool VerifyMacaroon(const std::vector<uint8_t>& secret,
154 const UwMacaroon& macaroon,
155 ErrorPtr* error) {
156 CHECK_EQ(kSha256OutputSize, secret.size());
157 if (!uw_macaroon_verify_(&macaroon, secret.data(), secret.size())) {
Vitaly Buka48a86692016-01-21 17:15:58 -0800158 Error::AddTo(error, FROM_HERE, "invalid_signature",
Vitaly Buka131b8892015-12-22 17:05:16 -0800159 "Invalid token signature");
160 return false;
161 }
162 return true;
163}
164
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800165UwMacaroonCaveatScopeType ToMacaroonScope(AuthScope scope) {
166 switch (scope) {
167 case AuthScope::kViewer:
168 return kUwMacaroonCaveatScopeTypeViewer;
169 case AuthScope::kUser:
170 return kUwMacaroonCaveatScopeTypeUser;
171 case AuthScope::kManager:
172 return kUwMacaroonCaveatScopeTypeManager;
173 case AuthScope::kOwner:
174 return kUwMacaroonCaveatScopeTypeOwner;
175 default:
176 NOTREACHED() << EnumToString(scope);
177 }
178 return kUwMacaroonCaveatScopeTypeViewer;
179}
180
181AuthScope FromMacaroonScope(uint32_t scope) {
182 if (scope <= kUwMacaroonCaveatScopeTypeOwner)
183 return AuthScope::kOwner;
184 if (scope <= kUwMacaroonCaveatScopeTypeManager)
185 return AuthScope::kManager;
186 if (scope <= kUwMacaroonCaveatScopeTypeUser)
187 return AuthScope::kUser;
188 if (scope <= kUwMacaroonCaveatScopeTypeViewer)
189 return AuthScope::kViewer;
190 return AuthScope::kNone;
191}
192
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800193} // namespace
194
Vitaly Buka229113e2015-12-13 23:12:42 -0800195AuthManager::AuthManager(Config* config,
196 const std::vector<uint8_t>& certificate_fingerprint)
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800197 : config_{config},
198 certificate_fingerprint_{certificate_fingerprint},
199 access_secret_{CreateSecret()} {
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800200 if (config_) {
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800201 SetAuthSecret(config_->GetSettings().secret,
202 config_->GetSettings().root_client_token_owner);
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800203 } else {
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800204 SetAuthSecret({}, RootClientTokenOwner::kNone);
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800205 }
Vitaly Buka229113e2015-12-13 23:12:42 -0800206}
207
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800208AuthManager::AuthManager(const std::vector<uint8_t>& auth_secret,
Vitaly Buka41aa8092015-12-09 20:04:34 -0800209 const std::vector<uint8_t>& certificate_fingerprint,
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800210 const std::vector<uint8_t>& access_secret,
Vitaly Buka41aa8092015-12-09 20:04:34 -0800211 base::Clock* clock)
Vitaly Buka229113e2015-12-13 23:12:42 -0800212 : AuthManager(nullptr, certificate_fingerprint) {
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800213 access_secret_ = access_secret.size() == kSha256OutputSize ? access_secret
214 : CreateSecret();
215 SetAuthSecret(auth_secret, RootClientTokenOwner::kNone);
Vitaly Buka229113e2015-12-13 23:12:42 -0800216 if (clock)
217 clock_ = clock;
218}
219
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800220void AuthManager::SetAuthSecret(const std::vector<uint8_t>& secret,
221 RootClientTokenOwner owner) {
222 auth_secret_ = secret;
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800223
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800224 if (auth_secret_.size() != kSha256OutputSize) {
225 auth_secret_ = CreateSecret();
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800226 owner = RootClientTokenOwner::kNone;
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800227 }
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800228
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800229 if (!config_ || (config_->GetSettings().secret == auth_secret_ &&
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800230 config_->GetSettings().root_client_token_owner == owner)) {
231 return;
232 }
233
234 Config::Transaction change{config_};
235 change.set_secret(secret);
236 change.set_root_client_token_owner(owner);
237 change.Commit();
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800238}
239
240AuthManager::~AuthManager() {}
241
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800242std::vector<uint8_t> AuthManager::CreateAccessToken(const UserInfo& user_info,
243 base::TimeDelta ttl) const {
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800244 Caveat scope{kUwMacaroonCaveatTypeScope, ToMacaroonScope(user_info.scope())};
245 Caveat user{kUwMacaroonCaveatTypeIdentifier, user_info.user_id()};
246 Caveat issued{kUwMacaroonCaveatTypeExpiration,
247 static_cast<uint32_t>((Now() + ttl).ToTimeT())};
248 return CreateMacaroonToken(
249 access_secret_,
250 {
251 scope.GetCaveat(), user.GetCaveat(), issued.GetCaveat(),
252 });
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800253}
254
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800255bool AuthManager::ParseAccessToken(const std::vector<uint8_t>& token,
256 UserInfo* user_info,
257 ErrorPtr* error) const {
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800258 std::vector<uint8_t> buffer;
259 UwMacaroon macaroon{};
260
261 uint32_t scope{0};
262 std::string user_id;
263 uint32_t expiration{0};
264
265 if (!LoadMacaroon(token, &buffer, &macaroon, error) ||
266 !VerifyMacaroon(access_secret_, macaroon, error) ||
267 macaroon.num_caveats != 3 ||
268 !ReadCaveat(macaroon.caveats[0], kUwMacaroonCaveatTypeScope, &scope,
269 error) ||
270 !ReadCaveat(macaroon.caveats[1], kUwMacaroonCaveatTypeIdentifier,
271 &user_id, error) ||
272 !ReadCaveat(macaroon.caveats[2], kUwMacaroonCaveatTypeExpiration,
273 &expiration, error)) {
Vitaly Buka48a86692016-01-21 17:15:58 -0800274 Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization,
275 "Invalid token");
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800276 return false;
277 }
278
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800279 AuthScope auth_scope{FromMacaroonScope(scope)};
280 if (auth_scope == AuthScope::kNone) {
Vitaly Buka48a86692016-01-21 17:15:58 -0800281 Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization,
282 "Invalid token data");
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800283 return false;
284 }
285
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800286 base::Time time{base::Time::FromTimeT(expiration)};
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800287 if (time < clock_->Now()) {
Vitaly Buka48a86692016-01-21 17:15:58 -0800288 Error::AddTo(error, FROM_HERE, errors::kAuthorizationExpired,
289 "Token is expired");
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800290 return false;
291 }
292
293 if (user_info)
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800294 *user_info = UserInfo{auth_scope, user_id};
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800295
296 return true;
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800297}
298
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800299std::vector<uint8_t> AuthManager::ClaimRootClientAuthToken(
Vitaly Buka4ab50022015-12-14 22:32:24 -0800300 RootClientTokenOwner owner,
301 ErrorPtr* error) {
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800302 CHECK(RootClientTokenOwner::kNone != owner);
303 if (config_) {
304 auto current = config_->GetSettings().root_client_token_owner;
305 if (!IsClaimAllowed(current, owner)) {
Vitaly Buka48a86692016-01-21 17:15:58 -0800306 Error::AddToPrintf(error, FROM_HERE, errors::kAlreadyClaimed,
307 "Device already claimed by '%s'",
308 EnumToString(current).c_str());
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800309 return {};
310 }
311 };
312
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800313 pending_claims_.push_back(std::make_pair(
314 std::unique_ptr<AuthManager>{new AuthManager{nullptr, {}}}, owner));
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800315 if (pending_claims_.size() > kMaxPendingClaims)
316 pending_claims_.pop_front();
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800317 return pending_claims_.back().first->GetRootClientAuthToken();
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800318}
319
Vitaly Buka305ab612015-12-15 12:02:59 -0800320bool AuthManager::ConfirmClientAuthToken(const std::vector<uint8_t>& token,
321 ErrorPtr* error) {
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800322 // Cover case when caller sent confirm twice.
Vitaly Buka66a01e02015-12-20 18:37:14 -0800323 if (pending_claims_.empty())
324 return IsValidAuthToken(token, error);
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800325
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800326 auto claim =
327 std::find_if(pending_claims_.begin(), pending_claims_.end(),
328 [&token](const decltype(pending_claims_)::value_type& auth) {
Vitaly Buka66a01e02015-12-20 18:37:14 -0800329 return auth.first->IsValidAuthToken(token, nullptr);
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800330 });
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800331 if (claim == pending_claims_.end()) {
Vitaly Buka48a86692016-01-21 17:15:58 -0800332 Error::AddTo(error, FROM_HERE, errors::kNotFound, "Unknown claim");
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800333 return false;
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800334 }
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800335
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800336 SetAuthSecret(claim->first->GetAuthSecret(), claim->second);
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800337 pending_claims_.clear();
338 return true;
339}
340
Vitaly Buka47743a32015-12-10 14:59:11 -0800341std::vector<uint8_t> AuthManager::GetRootClientAuthToken() const {
Vitaly Bukaa37056e2015-12-09 14:53:39 -0800342 Caveat scope{kUwMacaroonCaveatTypeScope, kUwMacaroonCaveatScopeTypeOwner};
343 Caveat issued{kUwMacaroonCaveatTypeIssued,
Vitaly Buka41aa8092015-12-09 20:04:34 -0800344 static_cast<uint32_t>(Now().ToTimeT())};
Vitaly Buka7a25a3d2015-12-22 15:25:51 -0800345 return CreateMacaroonToken(auth_secret_,
346 {
347 scope.GetCaveat(), issued.GetCaveat(),
348 });
Vitaly Bukaa37056e2015-12-09 14:53:39 -0800349}
350
Vitaly Buka41aa8092015-12-09 20:04:34 -0800351base::Time AuthManager::Now() const {
352 return clock_->Now();
353}
354
Vitaly Buka66a01e02015-12-20 18:37:14 -0800355bool AuthManager::IsValidAuthToken(const std::vector<uint8_t>& token,
356 ErrorPtr* error) const {
Vitaly Buka131b8892015-12-22 17:05:16 -0800357 std::vector<uint8_t> buffer;
Vitaly Bukae08c7c62015-12-13 20:12:39 -0800358 UwMacaroon macaroon{};
Vitaly Buka131b8892015-12-22 17:05:16 -0800359 if (!LoadMacaroon(token, &buffer, &macaroon, error) ||
360 !VerifyMacaroon(auth_secret_, macaroon, error)) {
Vitaly Buka48a86692016-01-21 17:15:58 -0800361 Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, "Invalid token");
Vitaly Buka66a01e02015-12-20 18:37:14 -0800362 return false;
363 }
364 return true;
365}
366
367bool AuthManager::CreateAccessTokenFromAuth(
368 const std::vector<uint8_t>& auth_token,
369 base::TimeDelta ttl,
370 std::vector<uint8_t>* access_token,
371 AuthScope* access_token_scope,
372 base::TimeDelta* access_token_ttl,
373 ErrorPtr* error) const {
374 // TODO(vitalybuka): implement token validation.
375 if (!IsValidAuthToken(auth_token, error))
376 return false;
377
378 if (!access_token)
379 return true;
380
381 // TODO(vitalybuka): User and scope must be parsed from auth_token.
382 UserInfo info{config_ ? config_->GetSettings().local_anonymous_access_role
383 : AuthScope::kViewer,
384 base::GenerateGUID()};
385
386 // TODO(vitalybuka): TTL also should be reduced in accordance with auth_token.
387 *access_token = CreateAccessToken(info, ttl);
388
389 if (access_token_scope)
390 *access_token_scope = info.scope();
391
392 if (access_token_ttl)
393 *access_token_ttl = ttl;
394 return true;
Vitaly Bukae08c7c62015-12-13 20:12:39 -0800395}
396
Vitaly Buka483d5972015-12-16 13:45:35 -0800397std::vector<uint8_t> AuthManager::CreateSessionId() {
398 std::vector<uint8_t> result;
399 AppendToArray(Now().ToTimeT(), &result);
400 AppendToArray(++session_counter_, &result);
401 return result;
402}
403
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800404} // namespace privet
405} // namespace weave