1 |
|
|
/** |
2 |
|
|
* This file is part of the CernVM File System. |
3 |
|
|
* |
4 |
|
|
* This file contains a very simple implementation of session tokens. |
5 |
|
|
*/ |
6 |
|
|
|
7 |
|
|
#include "session_token.h" |
8 |
|
|
|
9 |
|
|
#include <limits> |
10 |
|
|
|
11 |
|
|
#include "encrypt.h" |
12 |
|
|
#include "json.h" |
13 |
|
|
#include "json_document.h" |
14 |
|
|
#include "logging.h" |
15 |
|
|
#include "platform.h" |
16 |
|
|
#include "util/pointer.h" |
17 |
|
|
#include "util/string.h" |
18 |
|
|
|
19 |
|
|
namespace receiver { |
20 |
|
|
|
21 |
|
|
/** |
22 |
|
|
* Generate a session token from a public key_id, a path argument and a max |
23 |
|
|
* lease time (given in seconds). |
24 |
|
|
* |
25 |
|
|
* The session token encodes a lease valid for "path" until now() + |
26 |
|
|
* max_lease_time. |
27 |
|
|
* |
28 |
|
|
* Returns the session token, the (public) token_id and the token secret. |
29 |
|
|
*/ |
30 |
|
7 |
bool GenerateSessionToken(const std::string& key_id, const std::string& path, |
31 |
|
|
uint64_t max_lease_time, std::string* session_token, |
32 |
|
|
std::string* public_token_id, |
33 |
|
|
std::string* token_secret) { |
34 |
✓✓✓✗ ✗✓ |
7 |
if (session_token == NULL || public_token_id == NULL || |
35 |
|
|
token_secret == NULL) { |
36 |
|
1 |
return false; |
37 |
|
|
} |
38 |
|
|
|
39 |
✗✓✗✗ ✗✓ |
6 |
if (key_id.empty() && path.empty()) { |
40 |
|
|
return false; |
41 |
|
|
} |
42 |
|
|
|
43 |
|
6 |
UniquePtr<cipher::Key> secret(cipher::Key::CreateRandomly(32)); |
44 |
✗✓ |
6 |
if (!secret.IsValid()) { |
45 |
|
|
return false; |
46 |
|
|
} |
47 |
|
|
|
48 |
|
6 |
UniquePtr<cipher::Cipher> cipher(cipher::Cipher::Create(cipher::kAes256Cbc)); |
49 |
✗✓ |
6 |
if (!cipher.IsValid()) { |
50 |
|
|
return false; |
51 |
|
|
} |
52 |
|
|
|
53 |
|
6 |
*public_token_id = key_id + path; |
54 |
|
6 |
*token_secret = secret->ToBase64(); |
55 |
|
|
|
56 |
|
6 |
const uint64_t current_time = platform_monotonic_time(); |
57 |
✗✓ |
6 |
if (std::numeric_limits<uint64_t>::max() - max_lease_time < current_time) { |
58 |
|
|
return false; |
59 |
|
|
} |
60 |
|
|
|
61 |
|
6 |
const std::string expiry(StringifyUint(current_time + max_lease_time)); |
62 |
|
|
|
63 |
|
6 |
std::string encrypted_body; |
64 |
✓✗ |
6 |
if (!cipher->Encrypt( |
65 |
|
|
"{\"path\" : \"" + path + "\", \"expiry\" : \"" + expiry + "\"}", |
66 |
|
|
*secret, &encrypted_body)) { |
67 |
|
|
return false; |
68 |
|
|
} |
69 |
|
|
|
70 |
|
|
*session_token = Base64("{\"token_id\" : \"" + *public_token_id + |
71 |
|
6 |
"\", \"blob\" : \"" + Base64(encrypted_body) + "\"}"); |
72 |
|
|
|
73 |
|
6 |
return true; |
74 |
|
|
} |
75 |
|
|
|
76 |
|
|
/** |
77 |
|
|
* Obtain the public_id from a session token |
78 |
|
|
*/ |
79 |
|
|
|
80 |
|
2 |
bool GetTokenPublicId(const std::string& token, std::string* public_id) { |
81 |
✗✓ |
2 |
if (public_id == NULL) { |
82 |
|
|
return false; |
83 |
|
|
} |
84 |
|
|
|
85 |
|
2 |
std::string debased64_token; |
86 |
✗✓ |
2 |
if (!Debase64(token, &debased64_token)) { |
87 |
|
|
return false; |
88 |
|
|
} |
89 |
|
|
|
90 |
|
2 |
UniquePtr<JsonDocument> token_json(JsonDocument::Create(debased64_token)); |
91 |
✗✓ |
2 |
if (!token_json.IsValid()) { |
92 |
|
|
return false; |
93 |
|
|
} |
94 |
|
|
|
95 |
|
|
const JSON* token_id = |
96 |
|
2 |
JsonDocument::SearchInObject(token_json->root(), "token_id", JSON_STRING); |
97 |
|
|
const JSON* blob = |
98 |
|
2 |
JsonDocument::SearchInObject(token_json->root(), "blob", JSON_STRING); |
99 |
|
|
|
100 |
✓✗✗✓
|
2 |
if (token_id == NULL || blob == NULL) { |
101 |
|
|
return false; |
102 |
|
|
} |
103 |
|
|
|
104 |
|
2 |
*public_id = token_id->string_value; |
105 |
|
|
|
106 |
|
2 |
return true; |
107 |
|
|
} |
108 |
|
|
|
109 |
|
|
/* |
110 |
|
|
* Check the validity of a session token using the associated secret |
111 |
|
|
*/ |
112 |
|
3 |
TokenCheckResult CheckToken(const std::string& token, const std::string& secret, |
113 |
|
|
std::string* lease_path) { |
114 |
✗✓ |
3 |
if (!lease_path) { |
115 |
|
|
return kInvalid; |
116 |
|
|
} |
117 |
|
|
|
118 |
|
3 |
std::string debased64_token; |
119 |
✗✓ |
3 |
if (!Debase64(token, &debased64_token)) { |
120 |
|
|
return kInvalid; |
121 |
|
|
} |
122 |
|
|
|
123 |
|
3 |
UniquePtr<JsonDocument> token_json(JsonDocument::Create(debased64_token)); |
124 |
✗✓ |
3 |
if (!token_json.IsValid()) { |
125 |
|
|
return kInvalid; |
126 |
|
|
} |
127 |
|
|
|
128 |
|
|
const JSON* token_id = |
129 |
|
3 |
JsonDocument::SearchInObject(token_json->root(), "token_id", JSON_STRING); |
130 |
|
|
const JSON* blob = |
131 |
|
3 |
JsonDocument::SearchInObject(token_json->root(), "blob", JSON_STRING); |
132 |
✓✗✗✓
|
3 |
if (token_id == NULL || blob == NULL) { |
133 |
|
|
return kInvalid; |
134 |
|
|
} |
135 |
|
|
|
136 |
|
3 |
std::string debased64_secret; |
137 |
✗✓ |
3 |
if (!Debase64(secret, &debased64_secret)) { |
138 |
|
|
return kInvalid; |
139 |
|
|
} |
140 |
|
3 |
UniquePtr<cipher::Key> key(cipher::Key::CreateFromString(debased64_secret)); |
141 |
✗✓ |
3 |
if (!key.IsValid()) { |
142 |
|
|
return kInvalid; |
143 |
|
|
} |
144 |
|
|
|
145 |
|
3 |
std::string encrypted_body; |
146 |
✓✗ |
3 |
if (!Debase64(blob->string_value, &encrypted_body)) { |
147 |
|
|
return kInvalid; |
148 |
|
|
} |
149 |
|
|
|
150 |
|
3 |
std::string body; |
151 |
✗✓ |
3 |
if (!cipher::Cipher::Decrypt(encrypted_body, *key, &body)) { |
152 |
|
|
return kInvalid; |
153 |
|
|
} |
154 |
|
|
|
155 |
|
3 |
UniquePtr<JsonDocument> body_json(JsonDocument::Create(body)); |
156 |
✗✓ |
3 |
if (!token_json.IsValid()) { |
157 |
|
|
return kInvalid; |
158 |
|
|
} |
159 |
|
|
|
160 |
|
|
const JSON* path = |
161 |
|
3 |
JsonDocument::SearchInObject(body_json->root(), "path", JSON_STRING); |
162 |
|
|
const JSON* expiry = |
163 |
|
3 |
JsonDocument::SearchInObject(body_json->root(), "expiry", JSON_STRING); |
164 |
✓✗✗✓
|
3 |
if (path == NULL || expiry == NULL) { |
165 |
|
|
return kInvalid; |
166 |
|
|
} |
167 |
|
|
|
168 |
|
|
// TODO(radu): can we still use monotonic time if the process restarts? |
169 |
|
3 |
uint64_t expiry_time = String2Uint64(expiry->string_value); |
170 |
|
3 |
const uint64_t current_time = platform_monotonic_time(); |
171 |
✓✓ |
3 |
if (current_time > expiry_time) { |
172 |
|
1 |
return kExpired; |
173 |
|
|
} |
174 |
|
|
|
175 |
|
2 |
*lease_path = path->string_value; |
176 |
|
|
|
177 |
|
2 |
return kValid; |
178 |
|
|
} |
179 |
|
|
|
180 |
|
|
} // namespace receiver |