GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/receiver/session_token.cc
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 62 81 76.5%
Branches: 77 152 50.7%

Line Branch Exec Source
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 "crypto/encrypt.h"
12 #include "json.h"
13 #include "json_document.h"
14 #include "util/logging.h"
15 #include "util/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
4/6
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
7 if (session_token == NULL || public_token_id == NULL ||
35 token_secret == NULL) {
36 1 return false;
37 }
38
39
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
6 if (key_id.empty() && path.empty()) {
40 return false;
41 }
42
43
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 UniquePtr<cipher::Key> secret(cipher::Key::CreateRandomly(32));
44
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (!secret.IsValid()) {
45 return false;
46 }
47
48
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 UniquePtr<cipher::Cipher> cipher(cipher::Cipher::Create(cipher::kAes256Cbc));
49
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (!cipher.IsValid()) {
50 return false;
51 }
52
53
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 *public_token_id = key_id + path;
54
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 *token_secret = secret->ToBase64();
55
56 6 const uint64_t current_time = platform_monotonic_time();
57
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (std::numeric_limits<uint64_t>::max() - max_lease_time < current_time) {
58 return false;
59 }
60
61
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 const std::string expiry(StringifyUint(current_time + max_lease_time));
62
63 6 std::string encrypted_body;
64
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
12 if (!cipher->Encrypt(
65
5/10
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 6 times.
✗ Branch 11 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 6 times.
12 "{\"path\" : \"" + path + "\", \"expiry\" : \"" + expiry + "\"}",
66 6 *secret, &encrypted_body)) {
67 return false;
68 }
69
70
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
24 *session_token = Base64("{\"token_id\" : \"" + *public_token_id +
71
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
30 "\", \"blob\" : \"" + Base64(encrypted_body) + "\"}");
72
73 6 return true;
74 6 }
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
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (public_id == NULL) {
82 return false;
83 }
84
85 2 std::string debased64_token;
86
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (!Debase64(token, &debased64_token)) {
87 return false;
88 }
89
90
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 UniquePtr<JsonDocument> token_json(JsonDocument::Create(debased64_token));
91
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!token_json.IsValid()) {
92 return false;
93 }
94
95 const JSON* token_id =
96
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
2 JsonDocument::SearchInObject(token_json->root(), "token_id", JSON_STRING);
97 const JSON* blob =
98
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
2 JsonDocument::SearchInObject(token_json->root(), "blob", JSON_STRING);
99
100
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (token_id == NULL || blob == NULL) {
101 return false;
102 }
103
104
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 *public_id = token_id->string_value;
105
106 2 return true;
107 2 }
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
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!lease_path) {
115 return kInvalid;
116 }
117
118 3 std::string debased64_token;
119
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if (!Debase64(token, &debased64_token)) {
120 return kInvalid;
121 }
122
123
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 UniquePtr<JsonDocument> token_json(JsonDocument::Create(debased64_token));
124
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (!token_json.IsValid()) {
125 return kInvalid;
126 }
127
128 const JSON* token_id =
129
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
3 JsonDocument::SearchInObject(token_json->root(), "token_id", JSON_STRING);
130 const JSON* blob =
131
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
3 JsonDocument::SearchInObject(token_json->root(), "blob", JSON_STRING);
132
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (token_id == NULL || blob == NULL) {
133 return kInvalid;
134 }
135
136 3 std::string debased64_secret;
137
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if (!Debase64(secret, &debased64_secret)) {
138 return kInvalid;
139 }
140
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 UniquePtr<cipher::Key> key(cipher::Key::CreateFromString(debased64_secret));
141
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (!key.IsValid()) {
142 return kInvalid;
143 }
144
145 3 std::string encrypted_body;
146
3/6
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
3 if (!Debase64(blob->string_value, &encrypted_body)) {
147 return kInvalid;
148 }
149
150 3 std::string body;
151
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 if (!cipher::Cipher::Decrypt(encrypted_body, *key, &body)) {
152 return kInvalid;
153 }
154
155
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 UniquePtr<JsonDocument> body_json(JsonDocument::Create(body));
156
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (!token_json.IsValid()) {
157 return kInvalid;
158 }
159
160 const JSON* path =
161
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
3 JsonDocument::SearchInObject(body_json->root(), "path", JSON_STRING);
162 const JSON* expiry =
163
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
3 JsonDocument::SearchInObject(body_json->root(), "expiry", JSON_STRING);
164
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
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
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 uint64_t expiry_time = String2Uint64(expiry->string_value);
170 3 const uint64_t current_time = platform_monotonic_time();
171
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (current_time > expiry_time) {
172 1 return kExpired;
173 }
174
175
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 *lease_path = path->string_value;
176
177 2 return kValid;
178 3 }
179
180 } // namespace receiver
181