GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/receiver/session_token.cc Lines: 56 75 74.7 %
Date: 2019-02-03 02:48:13 Branches: 29 56 51.8 %

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 "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