blob: 66d04c4bf56785ee67e4a96e1891aa23821e9005 [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 Buka0dbbf602016-01-22 11:38:37 -080068 return Error::AddTo(error, FROM_HERE, kInvalidTokenError,
69 "Unable to get type");
Vitaly Bukae0df73a2015-12-22 16:50:22 -080070 }
71
72 if (caveat_type != type) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -080073 return Error::AddTo(error, FROM_HERE, kInvalidTokenError,
74 "Unexpected caveat type");
Vitaly Bukae0df73a2015-12-22 16:50:22 -080075 }
76
77 return true;
78}
79
80bool ReadCaveat(const UwMacaroonCaveat& caveat,
81 UwMacaroonCaveatType type,
82 uint32_t* value,
83 ErrorPtr* error) {
84 if (!CheckCaveatType(caveat, type, error))
85 return false;
86
87 if (!uw_macaroon_caveat_get_value_uint_(&caveat, value)) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -080088 return Error::AddTo(error, FROM_HERE, kInvalidTokenError,
89 "Unable to read caveat");
Vitaly Bukae0df73a2015-12-22 16:50:22 -080090 }
91
92 return true;
93}
94
95bool ReadCaveat(const UwMacaroonCaveat& caveat,
96 UwMacaroonCaveatType type,
97 std::string* value,
98 ErrorPtr* error) {
99 if (!CheckCaveatType(caveat, type, error))
100 return false;
101
102 const uint8_t* start{nullptr};
103 size_t size{0};
104 if (!uw_macaroon_caveat_get_value_str_(&caveat, &start, &size)) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800105 return Error::AddTo(error, FROM_HERE, kInvalidTokenError,
106 "Unable to read caveat");
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800107 }
108
109 value->assign(reinterpret_cast<const char*>(start), size);
110 return true;
111}
112
Vitaly Buka229113e2015-12-13 23:12:42 -0800113std::vector<uint8_t> CreateSecret() {
114 std::vector<uint8_t> secret(kSha256OutputSize);
115 base::RandBytes(secret.data(), secret.size());
116 return secret;
117}
118
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800119bool IsClaimAllowed(RootClientTokenOwner curret, RootClientTokenOwner claimer) {
120 return claimer > curret || claimer == RootClientTokenOwner::kCloud;
121}
122
Vitaly Buka7a25a3d2015-12-22 15:25:51 -0800123std::vector<uint8_t> CreateMacaroonToken(
124 const std::vector<uint8_t>& secret,
125 const std::vector<UwMacaroonCaveat>& caveats) {
126 CHECK_EQ(kSha256OutputSize, secret.size());
127 UwMacaroon macaroon{};
128 CHECK(uw_macaroon_new_from_root_key_(&macaroon, secret.data(), secret.size(),
129 caveats.data(), caveats.size()));
130
131 std::vector<uint8_t> token(kMaxMacaroonSize);
132 size_t len = 0;
133 CHECK(uw_macaroon_dump_(&macaroon, token.data(), token.size(), &len));
134 token.resize(len);
135
136 return token;
137}
138
Vitaly Buka131b8892015-12-22 17:05:16 -0800139bool LoadMacaroon(const std::vector<uint8_t>& token,
140 std::vector<uint8_t>* buffer,
141 UwMacaroon* macaroon,
142 ErrorPtr* error) {
143 buffer->resize(kMaxMacaroonSize);
144 if (!uw_macaroon_load_(token.data(), token.size(), buffer->data(),
145 buffer->size(), macaroon)) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800146 return Error::AddTo(error, FROM_HERE, kInvalidTokenError,
147 "Invalid token format");
Vitaly Buka131b8892015-12-22 17:05:16 -0800148 }
149 return true;
150}
151
152bool VerifyMacaroon(const std::vector<uint8_t>& secret,
153 const UwMacaroon& macaroon,
154 ErrorPtr* error) {
155 CHECK_EQ(kSha256OutputSize, secret.size());
156 if (!uw_macaroon_verify_(&macaroon, secret.data(), secret.size())) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800157 return Error::AddTo(error, FROM_HERE, "invalid_signature",
158 "Invalid token signature");
Vitaly Buka131b8892015-12-22 17:05:16 -0800159 }
160 return true;
161}
162
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800163UwMacaroonCaveatScopeType ToMacaroonScope(AuthScope scope) {
164 switch (scope) {
165 case AuthScope::kViewer:
166 return kUwMacaroonCaveatScopeTypeViewer;
167 case AuthScope::kUser:
168 return kUwMacaroonCaveatScopeTypeUser;
169 case AuthScope::kManager:
170 return kUwMacaroonCaveatScopeTypeManager;
171 case AuthScope::kOwner:
172 return kUwMacaroonCaveatScopeTypeOwner;
173 default:
174 NOTREACHED() << EnumToString(scope);
175 }
176 return kUwMacaroonCaveatScopeTypeViewer;
177}
178
179AuthScope FromMacaroonScope(uint32_t scope) {
180 if (scope <= kUwMacaroonCaveatScopeTypeOwner)
181 return AuthScope::kOwner;
182 if (scope <= kUwMacaroonCaveatScopeTypeManager)
183 return AuthScope::kManager;
184 if (scope <= kUwMacaroonCaveatScopeTypeUser)
185 return AuthScope::kUser;
186 if (scope <= kUwMacaroonCaveatScopeTypeViewer)
187 return AuthScope::kViewer;
188 return AuthScope::kNone;
189}
190
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800191} // namespace
192
Vitaly Buka229113e2015-12-13 23:12:42 -0800193AuthManager::AuthManager(Config* config,
194 const std::vector<uint8_t>& certificate_fingerprint)
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800195 : config_{config},
196 certificate_fingerprint_{certificate_fingerprint},
197 access_secret_{CreateSecret()} {
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800198 if (config_) {
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800199 SetAuthSecret(config_->GetSettings().secret,
200 config_->GetSettings().root_client_token_owner);
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800201 } else {
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800202 SetAuthSecret({}, RootClientTokenOwner::kNone);
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800203 }
Vitaly Buka229113e2015-12-13 23:12:42 -0800204}
205
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800206AuthManager::AuthManager(const std::vector<uint8_t>& auth_secret,
Vitaly Buka41aa8092015-12-09 20:04:34 -0800207 const std::vector<uint8_t>& certificate_fingerprint,
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800208 const std::vector<uint8_t>& access_secret,
Vitaly Buka41aa8092015-12-09 20:04:34 -0800209 base::Clock* clock)
Vitaly Buka229113e2015-12-13 23:12:42 -0800210 : AuthManager(nullptr, certificate_fingerprint) {
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800211 access_secret_ = access_secret.size() == kSha256OutputSize ? access_secret
212 : CreateSecret();
213 SetAuthSecret(auth_secret, RootClientTokenOwner::kNone);
Vitaly Buka229113e2015-12-13 23:12:42 -0800214 if (clock)
215 clock_ = clock;
216}
217
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800218void AuthManager::SetAuthSecret(const std::vector<uint8_t>& secret,
219 RootClientTokenOwner owner) {
220 auth_secret_ = secret;
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800221
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800222 if (auth_secret_.size() != kSha256OutputSize) {
223 auth_secret_ = CreateSecret();
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800224 owner = RootClientTokenOwner::kNone;
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800225 }
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800226
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800227 if (!config_ || (config_->GetSettings().secret == auth_secret_ &&
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800228 config_->GetSettings().root_client_token_owner == owner)) {
229 return;
230 }
231
232 Config::Transaction change{config_};
233 change.set_secret(secret);
234 change.set_root_client_token_owner(owner);
235 change.Commit();
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800236}
237
238AuthManager::~AuthManager() {}
239
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800240std::vector<uint8_t> AuthManager::CreateAccessToken(const UserInfo& user_info,
241 base::TimeDelta ttl) const {
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800242 Caveat scope{kUwMacaroonCaveatTypeScope, ToMacaroonScope(user_info.scope())};
243 Caveat user{kUwMacaroonCaveatTypeIdentifier, user_info.user_id()};
244 Caveat issued{kUwMacaroonCaveatTypeExpiration,
245 static_cast<uint32_t>((Now() + ttl).ToTimeT())};
246 return CreateMacaroonToken(
247 access_secret_,
248 {
249 scope.GetCaveat(), user.GetCaveat(), issued.GetCaveat(),
250 });
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800251}
252
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800253bool AuthManager::ParseAccessToken(const std::vector<uint8_t>& token,
254 UserInfo* user_info,
255 ErrorPtr* error) const {
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800256 std::vector<uint8_t> buffer;
257 UwMacaroon macaroon{};
258
259 uint32_t scope{0};
260 std::string user_id;
261 uint32_t expiration{0};
262
263 if (!LoadMacaroon(token, &buffer, &macaroon, error) ||
264 !VerifyMacaroon(access_secret_, macaroon, error) ||
265 macaroon.num_caveats != 3 ||
266 !ReadCaveat(macaroon.caveats[0], kUwMacaroonCaveatTypeScope, &scope,
267 error) ||
268 !ReadCaveat(macaroon.caveats[1], kUwMacaroonCaveatTypeIdentifier,
269 &user_id, error) ||
270 !ReadCaveat(macaroon.caveats[2], kUwMacaroonCaveatTypeExpiration,
271 &expiration, error)) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800272 return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization,
273 "Invalid token");
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800274 }
275
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800276 AuthScope auth_scope{FromMacaroonScope(scope)};
277 if (auth_scope == AuthScope::kNone) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800278 return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization,
279 "Invalid token data");
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800280 }
281
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800282 base::Time time{base::Time::FromTimeT(expiration)};
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800283 if (time < clock_->Now()) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800284 return Error::AddTo(error, FROM_HERE, errors::kAuthorizationExpired,
285 "Token is expired");
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800286 }
287
288 if (user_info)
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800289 *user_info = UserInfo{auth_scope, user_id};
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800290
291 return true;
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800292}
293
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800294std::vector<uint8_t> AuthManager::ClaimRootClientAuthToken(
Vitaly Buka4ab50022015-12-14 22:32:24 -0800295 RootClientTokenOwner owner,
296 ErrorPtr* error) {
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800297 CHECK(RootClientTokenOwner::kNone != owner);
298 if (config_) {
299 auto current = config_->GetSettings().root_client_token_owner;
300 if (!IsClaimAllowed(current, owner)) {
Vitaly Buka48a86692016-01-21 17:15:58 -0800301 Error::AddToPrintf(error, FROM_HERE, errors::kAlreadyClaimed,
302 "Device already claimed by '%s'",
303 EnumToString(current).c_str());
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800304 return {};
305 }
306 };
307
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800308 pending_claims_.push_back(std::make_pair(
309 std::unique_ptr<AuthManager>{new AuthManager{nullptr, {}}}, owner));
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800310 if (pending_claims_.size() > kMaxPendingClaims)
311 pending_claims_.pop_front();
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800312 return pending_claims_.back().first->GetRootClientAuthToken();
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800313}
314
Vitaly Buka305ab612015-12-15 12:02:59 -0800315bool AuthManager::ConfirmClientAuthToken(const std::vector<uint8_t>& token,
316 ErrorPtr* error) {
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800317 // Cover case when caller sent confirm twice.
Vitaly Buka66a01e02015-12-20 18:37:14 -0800318 if (pending_claims_.empty())
319 return IsValidAuthToken(token, error);
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800320
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800321 auto claim =
322 std::find_if(pending_claims_.begin(), pending_claims_.end(),
323 [&token](const decltype(pending_claims_)::value_type& auth) {
Vitaly Buka66a01e02015-12-20 18:37:14 -0800324 return auth.first->IsValidAuthToken(token, nullptr);
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800325 });
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800326 if (claim == pending_claims_.end()) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800327 return Error::AddTo(error, FROM_HERE, errors::kNotFound, "Unknown claim");
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800328 }
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800329
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800330 SetAuthSecret(claim->first->GetAuthSecret(), claim->second);
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800331 pending_claims_.clear();
332 return true;
333}
334
Vitaly Buka47743a32015-12-10 14:59:11 -0800335std::vector<uint8_t> AuthManager::GetRootClientAuthToken() const {
Vitaly Bukaa37056e2015-12-09 14:53:39 -0800336 Caveat scope{kUwMacaroonCaveatTypeScope, kUwMacaroonCaveatScopeTypeOwner};
337 Caveat issued{kUwMacaroonCaveatTypeIssued,
Vitaly Buka41aa8092015-12-09 20:04:34 -0800338 static_cast<uint32_t>(Now().ToTimeT())};
Vitaly Buka7a25a3d2015-12-22 15:25:51 -0800339 return CreateMacaroonToken(auth_secret_,
340 {
341 scope.GetCaveat(), issued.GetCaveat(),
342 });
Vitaly Bukaa37056e2015-12-09 14:53:39 -0800343}
344
Vitaly Buka41aa8092015-12-09 20:04:34 -0800345base::Time AuthManager::Now() const {
346 return clock_->Now();
347}
348
Vitaly Buka66a01e02015-12-20 18:37:14 -0800349bool AuthManager::IsValidAuthToken(const std::vector<uint8_t>& token,
350 ErrorPtr* error) const {
Vitaly Buka131b8892015-12-22 17:05:16 -0800351 std::vector<uint8_t> buffer;
Vitaly Bukae08c7c62015-12-13 20:12:39 -0800352 UwMacaroon macaroon{};
Vitaly Buka131b8892015-12-22 17:05:16 -0800353 if (!LoadMacaroon(token, &buffer, &macaroon, error) ||
354 !VerifyMacaroon(auth_secret_, macaroon, error)) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800355 return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode,
356 "Invalid token");
Vitaly Buka66a01e02015-12-20 18:37:14 -0800357 }
358 return true;
359}
360
361bool AuthManager::CreateAccessTokenFromAuth(
362 const std::vector<uint8_t>& auth_token,
363 base::TimeDelta ttl,
364 std::vector<uint8_t>* access_token,
365 AuthScope* access_token_scope,
366 base::TimeDelta* access_token_ttl,
367 ErrorPtr* error) const {
368 // TODO(vitalybuka): implement token validation.
369 if (!IsValidAuthToken(auth_token, error))
370 return false;
371
372 if (!access_token)
373 return true;
374
375 // TODO(vitalybuka): User and scope must be parsed from auth_token.
376 UserInfo info{config_ ? config_->GetSettings().local_anonymous_access_role
377 : AuthScope::kViewer,
378 base::GenerateGUID()};
379
380 // TODO(vitalybuka): TTL also should be reduced in accordance with auth_token.
381 *access_token = CreateAccessToken(info, ttl);
382
383 if (access_token_scope)
384 *access_token_scope = info.scope();
385
386 if (access_token_ttl)
387 *access_token_ttl = ttl;
388 return true;
Vitaly Bukae08c7c62015-12-13 20:12:39 -0800389}
390
Vitaly Buka483d5972015-12-16 13:45:35 -0800391std::vector<uint8_t> AuthManager::CreateSessionId() {
392 std::vector<uint8_t> result;
393 AppendToArray(Now().ToTimeT(), &result);
394 AppendToArray(++session_counter_, &result);
395 return result;
396}
397
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800398} // namespace privet
399} // namespace weave