blob: 8373a7e4395f8c8f3ec0ecee2e03b0f5cdaf35c9 [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)) {
68 Error::AddTo(error, FROM_HERE, errors::kDomain, kInvalidTokenError,
69 "Unable to get type");
70 return false;
71 }
72
73 if (caveat_type != type) {
74 Error::AddTo(error, FROM_HERE, errors::kDomain, kInvalidTokenError,
75 "Unexpected caveat type");
76 return false;
77 }
78
79 return true;
80}
81
82bool ReadCaveat(const UwMacaroonCaveat& caveat,
83 UwMacaroonCaveatType type,
84 uint32_t* value,
85 ErrorPtr* error) {
86 if (!CheckCaveatType(caveat, type, error))
87 return false;
88
89 if (!uw_macaroon_caveat_get_value_uint_(&caveat, value)) {
90 Error::AddTo(error, FROM_HERE, errors::kDomain, kInvalidTokenError,
91 "Unable to read caveat");
92 return false;
93 }
94
95 return true;
96}
97
98bool ReadCaveat(const UwMacaroonCaveat& caveat,
99 UwMacaroonCaveatType type,
100 std::string* value,
101 ErrorPtr* error) {
102 if (!CheckCaveatType(caveat, type, error))
103 return false;
104
105 const uint8_t* start{nullptr};
106 size_t size{0};
107 if (!uw_macaroon_caveat_get_value_str_(&caveat, &start, &size)) {
108 Error::AddTo(error, FROM_HERE, errors::kDomain, kInvalidTokenError,
109 "Unable to read caveat");
110 return false;
111 }
112
113 value->assign(reinterpret_cast<const char*>(start), size);
114 return true;
115}
116
Vitaly Buka229113e2015-12-13 23:12:42 -0800117std::vector<uint8_t> CreateSecret() {
118 std::vector<uint8_t> secret(kSha256OutputSize);
119 base::RandBytes(secret.data(), secret.size());
120 return secret;
121}
122
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800123bool IsClaimAllowed(RootClientTokenOwner curret, RootClientTokenOwner claimer) {
124 return claimer > curret || claimer == RootClientTokenOwner::kCloud;
125}
126
Vitaly Buka7a25a3d2015-12-22 15:25:51 -0800127std::vector<uint8_t> CreateMacaroonToken(
128 const std::vector<uint8_t>& secret,
129 const std::vector<UwMacaroonCaveat>& caveats) {
130 CHECK_EQ(kSha256OutputSize, secret.size());
131 UwMacaroon macaroon{};
132 CHECK(uw_macaroon_new_from_root_key_(&macaroon, secret.data(), secret.size(),
133 caveats.data(), caveats.size()));
134
135 std::vector<uint8_t> token(kMaxMacaroonSize);
136 size_t len = 0;
137 CHECK(uw_macaroon_dump_(&macaroon, token.data(), token.size(), &len));
138 token.resize(len);
139
140 return token;
141}
142
Vitaly Buka131b8892015-12-22 17:05:16 -0800143bool LoadMacaroon(const std::vector<uint8_t>& token,
144 std::vector<uint8_t>* buffer,
145 UwMacaroon* macaroon,
146 ErrorPtr* error) {
147 buffer->resize(kMaxMacaroonSize);
148 if (!uw_macaroon_load_(token.data(), token.size(), buffer->data(),
149 buffer->size(), macaroon)) {
150 Error::AddTo(error, FROM_HERE, errors::kDomain, kInvalidTokenError,
151 "Invalid token format");
152 return false;
153 }
154 return true;
155}
156
157bool VerifyMacaroon(const std::vector<uint8_t>& secret,
158 const UwMacaroon& macaroon,
159 ErrorPtr* error) {
160 CHECK_EQ(kSha256OutputSize, secret.size());
161 if (!uw_macaroon_verify_(&macaroon, secret.data(), secret.size())) {
162 Error::AddTo(error, FROM_HERE, errors::kDomain, "invalid_signature",
163 "Invalid token signature");
164 return false;
165 }
166 return true;
167}
168
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800169UwMacaroonCaveatScopeType ToMacaroonScope(AuthScope scope) {
170 switch (scope) {
171 case AuthScope::kViewer:
172 return kUwMacaroonCaveatScopeTypeViewer;
173 case AuthScope::kUser:
174 return kUwMacaroonCaveatScopeTypeUser;
175 case AuthScope::kManager:
176 return kUwMacaroonCaveatScopeTypeManager;
177 case AuthScope::kOwner:
178 return kUwMacaroonCaveatScopeTypeOwner;
179 default:
180 NOTREACHED() << EnumToString(scope);
181 }
182 return kUwMacaroonCaveatScopeTypeViewer;
183}
184
185AuthScope FromMacaroonScope(uint32_t scope) {
186 if (scope <= kUwMacaroonCaveatScopeTypeOwner)
187 return AuthScope::kOwner;
188 if (scope <= kUwMacaroonCaveatScopeTypeManager)
189 return AuthScope::kManager;
190 if (scope <= kUwMacaroonCaveatScopeTypeUser)
191 return AuthScope::kUser;
192 if (scope <= kUwMacaroonCaveatScopeTypeViewer)
193 return AuthScope::kViewer;
194 return AuthScope::kNone;
195}
196
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800197} // namespace
198
Vitaly Buka229113e2015-12-13 23:12:42 -0800199AuthManager::AuthManager(Config* config,
200 const std::vector<uint8_t>& certificate_fingerprint)
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800201 : config_{config},
202 certificate_fingerprint_{certificate_fingerprint},
203 access_secret_{CreateSecret()} {
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800204 if (config_) {
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800205 SetAuthSecret(config_->GetSettings().secret,
206 config_->GetSettings().root_client_token_owner);
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800207 } else {
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800208 SetAuthSecret({}, RootClientTokenOwner::kNone);
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800209 }
Vitaly Buka229113e2015-12-13 23:12:42 -0800210}
211
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800212AuthManager::AuthManager(const std::vector<uint8_t>& auth_secret,
Vitaly Buka41aa8092015-12-09 20:04:34 -0800213 const std::vector<uint8_t>& certificate_fingerprint,
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800214 const std::vector<uint8_t>& access_secret,
Vitaly Buka41aa8092015-12-09 20:04:34 -0800215 base::Clock* clock)
Vitaly Buka229113e2015-12-13 23:12:42 -0800216 : AuthManager(nullptr, certificate_fingerprint) {
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800217 access_secret_ = access_secret.size() == kSha256OutputSize ? access_secret
218 : CreateSecret();
219 SetAuthSecret(auth_secret, RootClientTokenOwner::kNone);
Vitaly Buka229113e2015-12-13 23:12:42 -0800220 if (clock)
221 clock_ = clock;
222}
223
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800224void AuthManager::SetAuthSecret(const std::vector<uint8_t>& secret,
225 RootClientTokenOwner owner) {
226 auth_secret_ = secret;
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800227
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800228 if (auth_secret_.size() != kSha256OutputSize) {
229 auth_secret_ = CreateSecret();
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800230 owner = RootClientTokenOwner::kNone;
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800231 }
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800232
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800233 if (!config_ || (config_->GetSettings().secret == auth_secret_ &&
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800234 config_->GetSettings().root_client_token_owner == owner)) {
235 return;
236 }
237
238 Config::Transaction change{config_};
239 change.set_secret(secret);
240 change.set_root_client_token_owner(owner);
241 change.Commit();
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800242}
243
244AuthManager::~AuthManager() {}
245
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800246std::vector<uint8_t> AuthManager::CreateAccessToken(const UserInfo& user_info,
247 base::TimeDelta ttl) const {
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800248 Caveat scope{kUwMacaroonCaveatTypeScope, ToMacaroonScope(user_info.scope())};
249 Caveat user{kUwMacaroonCaveatTypeIdentifier, user_info.user_id()};
250 Caveat issued{kUwMacaroonCaveatTypeExpiration,
251 static_cast<uint32_t>((Now() + ttl).ToTimeT())};
252 return CreateMacaroonToken(
253 access_secret_,
254 {
255 scope.GetCaveat(), user.GetCaveat(), issued.GetCaveat(),
256 });
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800257}
258
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800259bool AuthManager::ParseAccessToken(const std::vector<uint8_t>& token,
260 UserInfo* user_info,
261 ErrorPtr* error) const {
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800262 std::vector<uint8_t> buffer;
263 UwMacaroon macaroon{};
264
265 uint32_t scope{0};
266 std::string user_id;
267 uint32_t expiration{0};
268
269 if (!LoadMacaroon(token, &buffer, &macaroon, error) ||
270 !VerifyMacaroon(access_secret_, macaroon, error) ||
271 macaroon.num_caveats != 3 ||
272 !ReadCaveat(macaroon.caveats[0], kUwMacaroonCaveatTypeScope, &scope,
273 error) ||
274 !ReadCaveat(macaroon.caveats[1], kUwMacaroonCaveatTypeIdentifier,
275 &user_id, error) ||
276 !ReadCaveat(macaroon.caveats[2], kUwMacaroonCaveatTypeExpiration,
277 &expiration, error)) {
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800278 Error::AddTo(error, FROM_HERE, errors::kDomain,
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800279 errors::kInvalidAuthorization, "Invalid token");
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800280 return false;
281 }
282
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800283 AuthScope auth_scope{FromMacaroonScope(scope)};
284 if (auth_scope == AuthScope::kNone) {
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800285 Error::AddTo(error, FROM_HERE, errors::kDomain,
286 errors::kInvalidAuthorization, "Invalid token data");
287 return false;
288 }
289
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800290 base::Time time{base::Time::FromTimeT(expiration)};
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800291 if (time < clock_->Now()) {
292 Error::AddTo(error, FROM_HERE, errors::kDomain,
293 errors::kAuthorizationExpired, "Token is expired");
294 return false;
295 }
296
297 if (user_info)
Vitaly Bukae0df73a2015-12-22 16:50:22 -0800298 *user_info = UserInfo{auth_scope, user_id};
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800299
300 return true;
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800301}
302
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800303std::vector<uint8_t> AuthManager::ClaimRootClientAuthToken(
Vitaly Buka4ab50022015-12-14 22:32:24 -0800304 RootClientTokenOwner owner,
305 ErrorPtr* error) {
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800306 CHECK(RootClientTokenOwner::kNone != owner);
307 if (config_) {
308 auto current = config_->GetSettings().root_client_token_owner;
309 if (!IsClaimAllowed(current, owner)) {
310 Error::AddToPrintf(
311 error, FROM_HERE, errors::kDomain, errors::kAlreadyClaimed,
312 "Device already claimed by '%s'", EnumToString(current).c_str());
313 return {};
314 }
315 };
316
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800317 pending_claims_.push_back(std::make_pair(
318 std::unique_ptr<AuthManager>{new AuthManager{nullptr, {}}}, owner));
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800319 if (pending_claims_.size() > kMaxPendingClaims)
320 pending_claims_.pop_front();
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800321 return pending_claims_.back().first->GetRootClientAuthToken();
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800322}
323
Vitaly Buka305ab612015-12-15 12:02:59 -0800324bool AuthManager::ConfirmClientAuthToken(const std::vector<uint8_t>& token,
325 ErrorPtr* error) {
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800326 // Cover case when caller sent confirm twice.
Vitaly Buka66a01e02015-12-20 18:37:14 -0800327 if (pending_claims_.empty())
328 return IsValidAuthToken(token, error);
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800329
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800330 auto claim =
331 std::find_if(pending_claims_.begin(), pending_claims_.end(),
332 [&token](const decltype(pending_claims_)::value_type& auth) {
Vitaly Buka66a01e02015-12-20 18:37:14 -0800333 return auth.first->IsValidAuthToken(token, nullptr);
Vitaly Bukaa10ab1c2015-12-14 16:28:47 -0800334 });
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800335 if (claim == pending_claims_.end()) {
336 Error::AddTo(error, FROM_HERE, errors::kDomain, errors::kNotFound,
337 "Unknown claim");
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800338 return false;
Vitaly Bukac3bc82a2015-12-14 23:24:13 -0800339 }
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800340
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800341 SetAuthSecret(claim->first->GetAuthSecret(), claim->second);
Vitaly Bukacc77fad2015-12-13 21:04:46 -0800342 pending_claims_.clear();
343 return true;
344}
345
Vitaly Buka47743a32015-12-10 14:59:11 -0800346std::vector<uint8_t> AuthManager::GetRootClientAuthToken() const {
Vitaly Bukaa37056e2015-12-09 14:53:39 -0800347 Caveat scope{kUwMacaroonCaveatTypeScope, kUwMacaroonCaveatScopeTypeOwner};
348 Caveat issued{kUwMacaroonCaveatTypeIssued,
Vitaly Buka41aa8092015-12-09 20:04:34 -0800349 static_cast<uint32_t>(Now().ToTimeT())};
Vitaly Buka7a25a3d2015-12-22 15:25:51 -0800350 return CreateMacaroonToken(auth_secret_,
351 {
352 scope.GetCaveat(), issued.GetCaveat(),
353 });
Vitaly Bukaa37056e2015-12-09 14:53:39 -0800354}
355
Vitaly Buka41aa8092015-12-09 20:04:34 -0800356base::Time AuthManager::Now() const {
357 return clock_->Now();
358}
359
Vitaly Buka66a01e02015-12-20 18:37:14 -0800360bool AuthManager::IsValidAuthToken(const std::vector<uint8_t>& token,
361 ErrorPtr* error) const {
Vitaly Buka131b8892015-12-22 17:05:16 -0800362 std::vector<uint8_t> buffer;
Vitaly Bukae08c7c62015-12-13 20:12:39 -0800363 UwMacaroon macaroon{};
Vitaly Buka131b8892015-12-22 17:05:16 -0800364 if (!LoadMacaroon(token, &buffer, &macaroon, error) ||
365 !VerifyMacaroon(auth_secret_, macaroon, error)) {
Vitaly Buka66a01e02015-12-20 18:37:14 -0800366 Error::AddTo(error, FROM_HERE, errors::kDomain, errors::kInvalidAuthCode,
Vitaly Buka131b8892015-12-22 17:05:16 -0800367 "Invalid token");
Vitaly Buka66a01e02015-12-20 18:37:14 -0800368 return false;
369 }
370 return true;
371}
372
373bool AuthManager::CreateAccessTokenFromAuth(
374 const std::vector<uint8_t>& auth_token,
375 base::TimeDelta ttl,
376 std::vector<uint8_t>* access_token,
377 AuthScope* access_token_scope,
378 base::TimeDelta* access_token_ttl,
379 ErrorPtr* error) const {
380 // TODO(vitalybuka): implement token validation.
381 if (!IsValidAuthToken(auth_token, error))
382 return false;
383
384 if (!access_token)
385 return true;
386
387 // TODO(vitalybuka): User and scope must be parsed from auth_token.
388 UserInfo info{config_ ? config_->GetSettings().local_anonymous_access_role
389 : AuthScope::kViewer,
390 base::GenerateGUID()};
391
392 // TODO(vitalybuka): TTL also should be reduced in accordance with auth_token.
393 *access_token = CreateAccessToken(info, ttl);
394
395 if (access_token_scope)
396 *access_token_scope = info.scope();
397
398 if (access_token_ttl)
399 *access_token_ttl = ttl;
400 return true;
Vitaly Bukae08c7c62015-12-13 20:12:39 -0800401}
402
Vitaly Buka483d5972015-12-16 13:45:35 -0800403std::vector<uint8_t> AuthManager::CreateSessionId() {
404 std::vector<uint8_t> result;
405 AppendToArray(Now().ToTimeT(), &result);
406 AppendToArray(++session_counter_, &result);
407 return result;
408}
409
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800410} // namespace privet
411} // namespace weave