1 |
|
|
/** |
2 |
|
|
* This file is part of the CernVM File System. |
3 |
|
|
*/ |
4 |
|
|
|
5 |
|
|
#define __STDC_FORMAT_MACROS |
6 |
|
|
#include "authz_fetch.h" |
7 |
|
|
|
8 |
|
|
#include <errno.h> |
9 |
|
|
#include <signal.h> |
10 |
|
|
#include <sys/wait.h> |
11 |
|
|
#include <syslog.h> |
12 |
|
|
#include <unistd.h> |
13 |
|
|
|
14 |
|
|
#include <algorithm> |
15 |
|
|
#include <cassert> |
16 |
|
|
#include <cstring> |
17 |
|
|
#include <vector> |
18 |
|
|
|
19 |
|
|
#include "logging.h" |
20 |
|
|
#include "options.h" |
21 |
|
|
#include "platform.h" |
22 |
|
|
#include "sanitizer.h" |
23 |
|
|
#include "smalloc.h" |
24 |
|
|
#include "util/pointer.h" |
25 |
|
|
#include "util/posix.h" |
26 |
|
|
#include "util/string.h" |
27 |
|
|
#include "util_concurrency.h" |
28 |
|
|
|
29 |
|
|
using namespace std; // NOLINT |
30 |
|
|
|
31 |
|
|
|
32 |
|
|
const int AuthzExternalFetcher::kMinTtl = 0; |
33 |
|
|
const uint32_t AuthzExternalFetcher::kProtocolVersion = 1; |
34 |
|
|
|
35 |
|
|
|
36 |
|
136 |
AuthzExternalFetcher::AuthzExternalFetcher( |
37 |
|
|
const string &fqrn, |
38 |
|
|
const string &progname, |
39 |
|
|
const string &search_path, |
40 |
|
|
OptionsManager *options_manager) |
41 |
|
|
: fqrn_(fqrn) |
42 |
|
|
, progname_(progname) |
43 |
|
|
, search_path_(search_path) |
44 |
|
|
, fd_send_(-1) |
45 |
|
|
, fd_recv_(-1) |
46 |
|
|
, pid_(-1) |
47 |
|
|
, fail_state_(false) |
48 |
|
|
, options_manager_(options_manager) |
49 |
|
136 |
, next_start_(-1) |
50 |
|
|
{ |
51 |
|
136 |
InitLock(); |
52 |
|
136 |
} |
53 |
|
|
|
54 |
|
75 |
AuthzExternalFetcher::AuthzExternalFetcher( |
55 |
|
|
const string &fqrn, |
56 |
|
|
int fd_send, |
57 |
|
|
int fd_recv) |
58 |
|
|
: fqrn_(fqrn) |
59 |
|
|
, fd_send_(fd_send) |
60 |
|
|
, fd_recv_(fd_recv) |
61 |
|
|
, pid_(-1) |
62 |
|
|
, fail_state_(false) |
63 |
|
|
, options_manager_(NULL) |
64 |
|
75 |
, next_start_(-1) |
65 |
|
|
{ |
66 |
|
75 |
InitLock(); |
67 |
|
75 |
} |
68 |
|
|
|
69 |
|
|
|
70 |
|
422 |
AuthzExternalFetcher::~AuthzExternalFetcher() { |
71 |
|
211 |
int retval = pthread_mutex_destroy(&lock_); |
72 |
✗✓ |
211 |
assert(retval == 0); |
73 |
|
|
|
74 |
|
|
// Allow helper to gracefully terminate |
75 |
✓✓✓✗
|
211 |
if ((fd_send_ >= 0) && !fail_state_) { |
76 |
|
60 |
LogCvmfs(kLogAuthz, kLogDebug, "shutting down authz helper"); |
77 |
|
|
Send(string("{\"cvmfs_authz_v1\":{") + |
78 |
|
|
"\"msgid\":" + StringifyInt(kAuthzMsgQuit) + "," + |
79 |
|
60 |
"\"revision\":0}}"); |
80 |
|
|
} |
81 |
|
|
|
82 |
|
211 |
ReapHelper(); |
83 |
✗✓ |
211 |
} |
84 |
|
|
|
85 |
|
526 |
void AuthzExternalFetcher::ReapHelper() { |
86 |
|
|
// If we are reaping the helper, we don't try to shut it down again. |
87 |
|
|
|
88 |
✓✓ |
526 |
if (fd_send_ >= 0) |
89 |
|
120 |
close(fd_send_); |
90 |
|
526 |
fd_send_ = -1; |
91 |
✓✓ |
526 |
if (fd_recv_ >= 0) |
92 |
|
120 |
close(fd_recv_); |
93 |
|
526 |
fd_recv_ = -1; |
94 |
|
|
|
95 |
✓✓ |
526 |
if (pid_ > 0) { |
96 |
|
|
int retval; |
97 |
|
45 |
uint64_t now = platform_monotonic_time(); |
98 |
|
|
int statloc; |
99 |
✓✓ |
390591330 |
do { |
100 |
|
390591345 |
retval = waitpid(pid_, &statloc, WNOHANG); |
101 |
✓✓ |
390591345 |
if (platform_monotonic_time() > (now + kChildTimeout)) { |
102 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogWarn | kLogDebug, |
103 |
|
15 |
"authz helper %s unresponsive, killing", progname_.c_str()); |
104 |
|
15 |
retval = kill(pid_, SIGKILL); |
105 |
✓✗ |
15 |
if (retval == 0) { |
106 |
|
|
// Pick up client return status |
107 |
|
15 |
(void) waitpid(pid_, &statloc, 0); |
108 |
|
|
} else { |
109 |
|
|
// Process might have been terminated just before the kill() call |
110 |
|
|
(void) waitpid(pid_, &statloc, WNOHANG); |
111 |
|
|
} |
112 |
|
15 |
break; |
113 |
|
|
} |
114 |
|
|
} while (retval == 0); |
115 |
|
45 |
pid_ = -1; |
116 |
|
|
} |
117 |
|
526 |
} |
118 |
|
|
|
119 |
|
|
|
120 |
|
315 |
void AuthzExternalFetcher::EnterFailState() { |
121 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
122 |
|
|
"authz helper %s enters fail state, no more authorization", |
123 |
|
315 |
progname_.c_str()); |
124 |
|
|
|
125 |
|
315 |
ReapHelper(); |
126 |
|
315 |
next_start_ = platform_monotonic_time() + kChildTimeout; |
127 |
|
315 |
fail_state_ = true; |
128 |
|
315 |
} |
129 |
|
|
|
130 |
|
|
|
131 |
|
|
/** |
132 |
|
|
* Uses execve to start progname_. The started program has stdin and stdout |
133 |
|
|
* connected to fd_send_ and fd_recv_ and the CVMFS_... environment variables |
134 |
|
|
* set. Special care must be taken when we call fork here in an unknown state |
135 |
|
|
* of the client. Therefore we can't use ManagedExec (we can't use malloc). |
136 |
|
|
* |
137 |
|
|
* A failed execve is not caught by this routine. It will be caught in the |
138 |
|
|
* next step, when mother and child start talking. |
139 |
|
|
*/ |
140 |
|
60 |
void AuthzExternalFetcher::ExecHelper() { |
141 |
|
|
int pipe_send[2]; |
142 |
|
|
int pipe_recv[2]; |
143 |
|
60 |
MakePipe(pipe_send); |
144 |
|
60 |
MakePipe(pipe_recv); |
145 |
|
60 |
char *argv0 = strdupa(progname_.c_str()); |
146 |
|
60 |
char *argv[] = {argv0, NULL}; |
147 |
|
|
|
148 |
|
60 |
const bool strip_prefix = true; |
149 |
|
|
vector<string> authz_env = |
150 |
|
60 |
options_manager_->GetEnvironmentSubset("CVMFS_AUTHZ_", strip_prefix); |
151 |
|
60 |
vector<char *> envp; |
152 |
✗✓ |
60 |
for (unsigned i = 0; i < authz_env.size(); ++i) |
153 |
|
|
envp.push_back(strdupa(authz_env[i].c_str())); |
154 |
|
60 |
envp.push_back(strdupa("CVMFS_AUTHZ_HELPER=yes")); |
155 |
|
60 |
envp.push_back(NULL); |
156 |
|
|
|
157 |
|
60 |
int max_fd = sysconf(_SC_OPEN_MAX); |
158 |
✗✓ |
60 |
assert(max_fd > 0); |
159 |
|
|
LogCvmfs(kLogAuthz, kLogDebug | kLogSyslog, "starting authz helper %s", |
160 |
|
60 |
argv0); |
161 |
|
|
|
162 |
|
60 |
pid_t pid = fork(); |
163 |
✗✓ |
60 |
if (pid == 0) { |
164 |
|
|
// Child process, close file descriptors and run the helper |
165 |
|
|
int retval = dup2(pipe_send[0], 0); |
166 |
|
|
assert(retval == 0); |
167 |
|
|
retval = dup2(pipe_recv[1], 1); |
168 |
|
|
assert(retval == 1); |
169 |
|
|
for (int fd = 2; fd < max_fd; fd++) |
170 |
|
|
close(fd); |
171 |
|
|
|
172 |
|
|
execve(argv0, argv, &envp[0]); |
173 |
|
|
syslog(LOG_USER | LOG_ERR, "failed to start authz helper %s (%d)", |
174 |
|
|
argv0, errno); |
175 |
|
|
abort(); |
176 |
|
|
} |
177 |
✗✓ |
60 |
assert(pid > 0); |
178 |
|
60 |
close(pipe_send[0]); |
179 |
|
60 |
close(pipe_recv[1]); |
180 |
|
|
|
181 |
|
|
// Don't receive a signal if the helper terminates |
182 |
|
60 |
signal(SIGPIPE, SIG_IGN); |
183 |
|
60 |
pid_ = pid; |
184 |
|
60 |
fd_send_ = pipe_send[1]; |
185 |
|
60 |
fd_recv_ = pipe_recv[0]; |
186 |
|
60 |
} |
187 |
|
|
|
188 |
|
|
|
189 |
|
15 |
AuthzStatus AuthzExternalFetcher::Fetch( |
190 |
|
|
const QueryInfo &query_info, |
191 |
|
|
AuthzToken *authz_token, |
192 |
|
|
unsigned *ttl) |
193 |
|
|
{ |
194 |
|
15 |
*ttl = kDefaultTtl; |
195 |
|
|
|
196 |
|
15 |
MutexLockGuard lock_guard(lock_); |
197 |
✗✓ |
15 |
if (fail_state_) { |
198 |
|
|
uint64_t now = platform_monotonic_time(); |
199 |
|
|
if (now > next_start_) { |
200 |
|
|
fail_state_ = false; |
201 |
|
|
} else { |
202 |
|
|
return kAuthzNoHelper; |
203 |
|
|
} |
204 |
|
|
} |
205 |
|
|
|
206 |
|
|
bool retval; |
207 |
|
|
|
208 |
✗✓ |
15 |
if (fd_send_ < 0) { |
209 |
|
|
if (progname_.empty()) |
210 |
|
|
progname_ = FindHelper(query_info.membership); |
211 |
|
|
ExecHelper(); |
212 |
|
|
retval = Handshake(); |
213 |
|
|
if (!retval) |
214 |
|
|
return kAuthzNoHelper; |
215 |
|
|
} |
216 |
✓✗✗✓
|
15 |
assert((fd_send_ >= 0) && (fd_recv_ >= 0)); |
217 |
|
|
|
218 |
|
15 |
string authz_schema; |
219 |
|
15 |
string pure_membership; |
220 |
|
15 |
StripAuthzSchema(query_info.membership, &authz_schema, &pure_membership); |
221 |
|
|
string json_msg = string("{\"cvmfs_authz_v1\":{") + |
222 |
|
|
"\"msgid\":" + StringifyInt(kAuthzMsgVerify) + "," + |
223 |
|
|
"\"revision\":0," + |
224 |
|
|
"\"uid\":" + StringifyInt(query_info.uid) + "," + |
225 |
|
|
"\"gid\":" + StringifyInt(query_info.gid) + "," + |
226 |
|
|
"\"pid\":" + StringifyInt(query_info.pid) + "," + |
227 |
|
|
"\"membership\":\"" + Base64(pure_membership) + |
228 |
|
15 |
"\"}}"; |
229 |
✓✗✓✗
|
15 |
retval = Send(json_msg) && Recv(&json_msg); |
230 |
✗✓ |
15 |
if (!retval) |
231 |
|
|
return kAuthzNoHelper; |
232 |
|
15 |
AuthzExternalMsg binary_msg; |
233 |
|
15 |
retval = ParseMsg(json_msg, kAuthzMsgPermit, &binary_msg); |
234 |
✗✓ |
15 |
if (!retval) |
235 |
|
|
return kAuthzNoHelper; |
236 |
|
|
|
237 |
|
15 |
*ttl = binary_msg.permit.ttl; |
238 |
✓✗ |
15 |
if (binary_msg.permit.status == kAuthzOk) { |
239 |
✗✓ |
15 |
*authz_token = binary_msg.permit.token; |
240 |
|
|
LogCvmfs(kLogAuthz, kLogDebug, "got token of type %d and size %u", |
241 |
|
15 |
binary_msg.permit.token.type, binary_msg.permit.token.size); |
242 |
|
|
} |
243 |
|
|
|
244 |
|
15 |
return binary_msg.permit.status; |
245 |
|
|
} |
246 |
|
|
|
247 |
|
|
|
248 |
|
|
string AuthzExternalFetcher::FindHelper(const string &membership) { |
249 |
|
|
string authz_schema; |
250 |
|
|
string pure_membership; |
251 |
|
|
StripAuthzSchema(membership, &authz_schema, &pure_membership); |
252 |
|
|
sanitizer::AuthzSchemaSanitizer sanitizer; |
253 |
|
|
if (!sanitizer.IsValid(authz_schema)) { |
254 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, "invalid authz schema: %s", |
255 |
|
|
authz_schema.c_str()); |
256 |
|
|
return ""; |
257 |
|
|
} |
258 |
|
|
|
259 |
|
|
string exe_path = search_path_ + "/cvmfs_" + authz_schema + "_helper"; |
260 |
|
|
if (!FileExists(exe_path)) { |
261 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, "authz helper %s missing", |
262 |
|
|
exe_path.c_str()); |
263 |
|
|
} |
264 |
|
|
return exe_path; |
265 |
|
|
} |
266 |
|
|
|
267 |
|
|
|
268 |
|
|
/** |
269 |
|
|
* Establish communication link with a forked authz helper. |
270 |
|
|
*/ |
271 |
|
30 |
bool AuthzExternalFetcher::Handshake() { |
272 |
|
30 |
string debug_log = GetLogDebugFile(); |
273 |
|
30 |
string json_debug_log; |
274 |
✗✓ |
30 |
if (debug_log != "") |
275 |
|
|
json_debug_log = ",\"debug_log\":\"" + debug_log + "\""; |
276 |
|
|
string json_msg = string("{") + |
277 |
|
|
"\"cvmfs_authz_v1\":{" + |
278 |
|
|
"\"msgid\":" + StringifyInt(0) + "," + |
279 |
|
|
"\"revision\":0," + |
280 |
|
|
"\"fqrn\":\"" + fqrn_ + "\"," + |
281 |
|
|
"\"syslog_facility\":" + StringifyInt(GetLogSyslogFacility()) + "," + |
282 |
|
|
"\"syslog_level\":" + StringifyInt(GetLogSyslogLevel()) + |
283 |
|
|
json_debug_log + |
284 |
|
30 |
"}}"; |
285 |
|
30 |
bool retval = Send(json_msg); |
286 |
✓✓ |
30 |
if (!retval) |
287 |
|
15 |
return false; |
288 |
|
|
|
289 |
|
15 |
retval = Recv(&json_msg); |
290 |
✗✓ |
15 |
if (!retval) |
291 |
|
|
return false; |
292 |
|
15 |
AuthzExternalMsg binary_msg; |
293 |
|
15 |
retval = ParseMsg(json_msg, kAuthzMsgReady, &binary_msg); |
294 |
✗✓ |
15 |
if (!retval) |
295 |
|
|
return false; |
296 |
|
|
|
297 |
|
15 |
return true; |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
|
301 |
|
211 |
void AuthzExternalFetcher::InitLock() { |
302 |
|
211 |
int retval = pthread_mutex_init(&lock_, NULL); |
303 |
✗✓ |
211 |
assert(retval == 0); |
304 |
|
211 |
} |
305 |
|
|
|
306 |
|
|
|
307 |
|
150 |
bool AuthzExternalFetcher::Send(const string &msg) { |
308 |
|
|
// Line format: 4 byte protocol version, 4 byte length, message |
309 |
|
|
struct { |
310 |
|
|
uint32_t version; |
311 |
|
|
uint32_t length; |
312 |
|
|
} header; |
313 |
|
150 |
header.version = kProtocolVersion; |
314 |
|
150 |
header.length = msg.length(); |
315 |
|
150 |
unsigned raw_length = sizeof(header) + msg.length(); |
316 |
|
|
unsigned char *raw_msg = reinterpret_cast<unsigned char *>( |
317 |
|
150 |
alloca(raw_length)); |
318 |
|
150 |
memcpy(raw_msg, &header, sizeof(header)); |
319 |
|
150 |
memcpy(raw_msg + sizeof(header), msg.data(), header.length); |
320 |
|
|
|
321 |
|
150 |
bool retval = SafeWrite(fd_send_, raw_msg, raw_length); |
322 |
✓✓ |
150 |
if (!retval) |
323 |
|
30 |
EnterFailState(); |
324 |
|
150 |
return retval; |
325 |
|
|
} |
326 |
|
|
|
327 |
|
|
|
328 |
|
|
/** |
329 |
|
|
* We want to see valid JSON in the form |
330 |
|
|
* { "cvmfs_authz_v1" : { |
331 |
|
|
* "msgid": |
332 |
|
|
* "revision": |
333 |
|
|
* ... |
334 |
|
|
* } |
335 |
|
|
* ... |
336 |
|
|
* } |
337 |
|
|
* |
338 |
|
|
* The contents of "cvmfs_authz_v1" depends on the msgid. Additional fields |
339 |
|
|
* are ignored. The protocol revision should indicate changes in the fields. |
340 |
|
|
*/ |
341 |
|
285 |
bool AuthzExternalFetcher::ParseMsg( |
342 |
|
|
const std::string &json_msg, |
343 |
|
|
const AuthzExternalMsgIds expected_msgid, |
344 |
|
|
AuthzExternalMsg *binary_msg) |
345 |
|
|
{ |
346 |
✗✓ |
285 |
assert(binary_msg != NULL); |
347 |
|
|
|
348 |
|
285 |
UniquePtr<JsonDocument> json_document(JsonDocument::Create(json_msg)); |
349 |
✓✓ |
285 |
if (!json_document.IsValid()) { |
350 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
351 |
|
|
"invalid json from authz helper %s: %s", |
352 |
|
30 |
progname_.c_str(), json_msg.c_str()); |
353 |
|
30 |
EnterFailState(); |
354 |
|
30 |
return false; |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
JSON *json_authz = JsonDocument::SearchInObject( |
358 |
|
255 |
json_document->root(), "cvmfs_authz_v1", JSON_OBJECT); |
359 |
✓✓ |
255 |
if (json_authz == NULL) { |
360 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
361 |
|
|
"\"cvmfs_authz_v1\" not found in json from authz helper %s: %s", |
362 |
|
15 |
progname_.c_str(), json_msg.c_str()); |
363 |
|
15 |
EnterFailState(); |
364 |
|
15 |
return false; |
365 |
|
|
} |
366 |
|
|
|
367 |
✓✓✓✓ ✓✓ |
240 |
if (!ParseMsgId(json_authz, binary_msg) || |
368 |
|
|
(binary_msg->msgid != expected_msgid)) |
369 |
|
|
{ |
370 |
|
90 |
EnterFailState(); |
371 |
|
90 |
return false; |
372 |
|
|
} |
373 |
✓✓ |
150 |
if (!ParseRevision(json_authz, binary_msg)) { |
374 |
|
15 |
EnterFailState(); |
375 |
|
15 |
return false; |
376 |
|
|
} |
377 |
✓✓ |
135 |
if (binary_msg->msgid == kAuthzMsgPermit) { |
378 |
✓✓ |
90 |
if (!ParsePermit(json_authz, binary_msg)) { |
379 |
|
15 |
EnterFailState(); |
380 |
|
15 |
return false; |
381 |
|
|
} |
382 |
|
|
} |
383 |
|
120 |
return true; |
384 |
|
|
} |
385 |
|
|
|
386 |
|
|
|
387 |
|
240 |
bool AuthzExternalFetcher::ParseMsgId( |
388 |
|
|
JSON *json_authz, |
389 |
|
|
AuthzExternalMsg *binary_msg) |
390 |
|
|
{ |
391 |
|
|
JSON *json_msgid = JsonDocument::SearchInObject( |
392 |
|
240 |
json_authz, "msgid", JSON_INT); |
393 |
✓✓ |
240 |
if (json_msgid == NULL) { |
394 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
395 |
|
|
"\"msgid\" not found in json from authz helper %s", |
396 |
|
30 |
progname_.c_str()); |
397 |
|
30 |
EnterFailState(); |
398 |
|
30 |
return false; |
399 |
|
|
} |
400 |
|
|
|
401 |
✓✓✓✓
|
210 |
if ((json_msgid->int_value < 0) || |
402 |
|
|
(json_msgid->int_value >= kAuthzMsgInvalid)) |
403 |
|
|
{ |
404 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
405 |
|
|
"invalid \"msgid\" in json from authz helper %s: %d", |
406 |
|
30 |
progname_.c_str(), json_msgid->int_value); |
407 |
|
30 |
EnterFailState(); |
408 |
|
30 |
return false; |
409 |
|
|
} |
410 |
|
|
|
411 |
|
180 |
binary_msg->msgid = static_cast<AuthzExternalMsgIds>(json_msgid->int_value); |
412 |
|
180 |
return true; |
413 |
|
|
} |
414 |
|
|
|
415 |
|
|
|
416 |
|
|
/** |
417 |
|
|
* A permit must contain the authorization status. Optionally it can come with |
418 |
|
|
* a "time to live" of the answer and a token (e.g. X.509 proxy certificate). |
419 |
|
|
*/ |
420 |
|
90 |
bool AuthzExternalFetcher::ParsePermit( |
421 |
|
|
JSON *json_authz, |
422 |
|
|
AuthzExternalMsg *binary_msg) |
423 |
|
|
{ |
424 |
|
|
JSON *json_status = |
425 |
|
90 |
JsonDocument::SearchInObject(json_authz, "status", JSON_INT); |
426 |
✓✓ |
90 |
if (json_status == NULL) { |
427 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
428 |
|
|
"\"status\" not found in json from authz helper %s", |
429 |
|
15 |
progname_.c_str()); |
430 |
|
15 |
EnterFailState(); |
431 |
|
15 |
return false; |
432 |
|
|
} |
433 |
✓✗✗✓
|
75 |
if ((json_status->int_value < 0) || (json_status->int_value > kAuthzUnknown)) |
434 |
|
|
{ |
435 |
|
|
binary_msg->permit.status = kAuthzUnknown; |
436 |
|
|
} else { |
437 |
|
|
binary_msg->permit.status = static_cast<AuthzStatus>( |
438 |
|
75 |
json_status->int_value); |
439 |
|
|
} |
440 |
|
|
|
441 |
|
75 |
JSON *json_ttl = JsonDocument::SearchInObject(json_authz, "ttl", JSON_INT); |
442 |
✓✓ |
75 |
if (json_ttl == NULL) { |
443 |
|
15 |
LogCvmfs(kLogAuthz, kLogDebug, "no ttl, using default"); |
444 |
|
15 |
binary_msg->permit.ttl = kDefaultTtl; |
445 |
|
|
} else { |
446 |
|
60 |
binary_msg->permit.ttl = std::max(kMinTtl, json_ttl->int_value); |
447 |
|
|
} |
448 |
|
|
|
449 |
|
|
JSON *json_token = |
450 |
|
75 |
JsonDocument::SearchInObject(json_authz, "x509_proxy", JSON_STRING); |
451 |
✓✓ |
75 |
if (json_token != NULL) { |
452 |
|
30 |
binary_msg->permit.token.type = kTokenX509; |
453 |
|
30 |
string token_binary; |
454 |
|
30 |
bool valid_base64 = Debase64(json_token->string_value, &token_binary); |
455 |
✗✓ |
30 |
if (!valid_base64) { |
456 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
457 |
|
|
"invalid Base64 in 'x509_proxy' from authz helper %s", |
458 |
|
|
progname_.c_str()); |
459 |
|
|
EnterFailState(); |
460 |
|
|
return false; |
461 |
|
|
} |
462 |
|
30 |
unsigned size = token_binary.size(); |
463 |
|
30 |
binary_msg->permit.token.size = size; |
464 |
✓✗ |
30 |
if (size > 0) { |
465 |
|
|
// The token is passed to the AuthzSessionManager, which takes care of |
466 |
|
|
// freeing the memory |
467 |
|
30 |
binary_msg->permit.token.data = smalloc(size); |
468 |
|
30 |
memcpy(binary_msg->permit.token.data, token_binary.data(), size); |
469 |
|
|
} |
470 |
|
|
} |
471 |
|
|
|
472 |
|
|
json_token = JsonDocument::SearchInObject(json_authz, "bearer_token", |
473 |
|
75 |
JSON_STRING); |
474 |
✗✓ |
75 |
if (json_token != NULL) { |
475 |
|
|
binary_msg->permit.token.type = kTokenBearer; |
476 |
|
|
unsigned size = strlen(json_token->string_value); |
477 |
|
|
binary_msg->permit.token.size = size; |
478 |
|
|
if (size > 0) { |
479 |
|
|
// The token is passed to the AuthzSessionManager, which takes care of |
480 |
|
|
// freeing the memory |
481 |
|
|
binary_msg->permit.token.data = smalloc(size); |
482 |
|
|
memcpy(binary_msg->permit.token.data, json_token->string_value, size); |
483 |
|
|
|
484 |
|
|
LogCvmfs(kLogAuthz, kLogDebug, |
485 |
|
|
"Got a bearer_token from authz_helper. " |
486 |
|
|
"Setting token type to kTokenBearer"); |
487 |
|
|
} else { |
488 |
|
|
// We got a bearer_token, but a size 0 (or negative?) string |
489 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
490 |
|
|
"bearer_token was in returned JSON from Authz helper," |
491 |
|
|
" but of size 0 from authz helper %s", |
492 |
|
|
progname_.c_str()); |
493 |
|
|
} |
494 |
|
|
} |
495 |
|
|
|
496 |
✓✓ |
75 |
if (binary_msg->permit.token.type == kTokenUnknown) { |
497 |
|
|
// No auth token returned, so authz should do... what exactly? |
498 |
|
|
// Log error message |
499 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
500 |
|
|
"No auth token found in returned JSON from Authz helper %s", |
501 |
|
30 |
progname_.c_str()); |
502 |
|
|
} |
503 |
|
|
|
504 |
|
75 |
return true; |
505 |
|
|
} |
506 |
|
|
|
507 |
|
|
|
508 |
|
150 |
bool AuthzExternalFetcher::ParseRevision( |
509 |
|
|
JSON *json_authz, |
510 |
|
|
AuthzExternalMsg *binary_msg) |
511 |
|
|
{ |
512 |
|
|
JSON *json_revision = JsonDocument::SearchInObject( |
513 |
|
150 |
json_authz, "revision", JSON_INT); |
514 |
✗✓ |
150 |
if (json_revision == NULL) { |
515 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
516 |
|
|
"\"revision\" not found in json from authz helper %s", |
517 |
|
|
progname_.c_str()); |
518 |
|
|
EnterFailState(); |
519 |
|
|
return false; |
520 |
|
|
} |
521 |
|
|
|
522 |
✓✓ |
150 |
if (json_revision->int_value < 0) { |
523 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
524 |
|
|
"invalid \"revision\" in json from authz helper %s: %d", |
525 |
|
15 |
progname_.c_str(), json_revision->int_value); |
526 |
|
15 |
EnterFailState(); |
527 |
|
15 |
return false; |
528 |
|
|
} |
529 |
|
|
|
530 |
|
135 |
binary_msg->protocol_revision = json_revision->int_value; |
531 |
|
135 |
return true; |
532 |
|
|
} |
533 |
|
|
|
534 |
|
|
|
535 |
|
60 |
bool AuthzExternalFetcher::Recv(string *msg) { |
536 |
|
|
uint32_t version; |
537 |
|
60 |
ssize_t retval = SafeRead(fd_recv_, &version, sizeof(version)); |
538 |
✓✓ |
60 |
if (retval != static_cast<int>(sizeof(version))) { |
539 |
|
15 |
EnterFailState(); |
540 |
|
15 |
return false; |
541 |
|
|
} |
542 |
✓✓ |
45 |
if (version != kProtocolVersion) { |
543 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
544 |
|
15 |
"authz helper uses unknown protocol version %u", version); |
545 |
|
15 |
EnterFailState(); |
546 |
|
15 |
return false; |
547 |
|
|
} |
548 |
|
|
|
549 |
|
|
uint32_t length; |
550 |
|
30 |
retval = SafeRead(fd_recv_, &length, sizeof(length)); |
551 |
✗✓ |
30 |
if (retval != static_cast<int>(sizeof(length))) { |
552 |
|
|
EnterFailState(); |
553 |
|
|
return false; |
554 |
|
|
} |
555 |
|
|
|
556 |
|
30 |
msg->clear(); |
557 |
|
|
char buf[kPageSize]; |
558 |
|
30 |
unsigned nbytes = 0; |
559 |
✓✓ |
120 |
while (nbytes < length) { |
560 |
|
60 |
const unsigned remaining = length - nbytes; |
561 |
|
60 |
retval = SafeRead(fd_recv_, buf, std::min(kPageSize, remaining)); |
562 |
✗✓ |
60 |
if (retval < 0) { |
563 |
|
|
LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
564 |
|
|
"read failure from authz helper %s", progname_.c_str()); |
565 |
|
|
EnterFailState(); |
566 |
|
|
return false; |
567 |
|
|
} |
568 |
|
60 |
nbytes += retval; |
569 |
|
60 |
msg->append(buf, retval); |
570 |
|
|
} |
571 |
|
|
|
572 |
|
30 |
return true; |
573 |
|
|
} |
574 |
|
|
|
575 |
|
|
|
576 |
|
15 |
void AuthzExternalFetcher::StripAuthzSchema( |
577 |
|
|
const string &membership, |
578 |
|
|
string *authz_schema, |
579 |
|
|
string *pure_membership) |
580 |
|
|
{ |
581 |
|
15 |
vector<string> components = SplitString(membership, '%'); |
582 |
|
15 |
*authz_schema = components[0]; |
583 |
✓✗ |
15 |
if (components.size() < 2) { |
584 |
|
|
LogCvmfs(kLogAuthz, kLogDebug, "invalid membership: %s", |
585 |
|
15 |
membership.c_str()); |
586 |
|
15 |
*pure_membership = ""; |
587 |
|
|
return; |
588 |
|
|
} |
589 |
|
|
|
590 |
|
|
components.erase(components.begin()); |
591 |
|
|
*pure_membership = JoinStrings(components, "%"); |
592 |
|
|
} |