CernVM-FS  2.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
authz_fetch.cc
Go to the documentation of this file.
1 
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;
34 
35 
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  , next_start_(-1)
50 {
51  InitLock();
52 }
53 
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  , next_start_(-1)
65 {
66  InitLock();
67 }
68 
69 
71  int retval = pthread_mutex_destroy(&lock_);
72  assert(retval == 0);
73 
74  // Allow helper to gracefully terminate
75  if ((fd_send_ >= 0) && !fail_state_) {
76  LogCvmfs(kLogAuthz, kLogDebug, "shutting down authz helper");
77  Send(string("{\"cvmfs_authz_v1\":{") +
78  "\"msgid\":" + StringifyInt(kAuthzMsgQuit) + "," +
79  "\"revision\":0}}");
80  }
81 
82  ReapHelper();
83 }
84 
86  // If we are reaping the helper, we don't try to shut it down again.
87 
88  if (fd_send_ >= 0)
89  close(fd_send_);
90  fd_send_ = -1;
91  if (fd_recv_ >= 0)
92  close(fd_recv_);
93  fd_recv_ = -1;
94 
95  if (pid_ > 0) {
96  int retval;
97  uint64_t now = platform_monotonic_time();
98  int statloc;
99  do {
100  retval = waitpid(pid_, &statloc, WNOHANG);
101  if (platform_monotonic_time() > (now + kChildTimeout)) {
103  "authz helper %s unresponsive, killing", progname_.c_str());
104  retval = kill(pid_, SIGKILL);
105  if (retval == 0) {
106  // Pick up client return status
107  (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  break;
113  }
114  } while (retval == 0);
115  pid_ = -1;
116  }
117 }
118 
119 
122  "authz helper %s enters fail state, no more authorization",
123  progname_.c_str());
124 
125  ReapHelper();
127  fail_state_ = true;
128 }
129 
130 
141  int pipe_send[2];
142  int pipe_recv[2];
143  MakePipe(pipe_send);
144  MakePipe(pipe_recv);
145  char *argv0 = strdupa(progname_.c_str());
146  char *argv[] = {argv0, NULL};
147 
148  const bool strip_prefix = true;
149  vector<string> authz_env =
150  options_manager_->GetEnvironmentSubset("CVMFS_AUTHZ_", strip_prefix);
151  vector<char *> envp;
152  for (unsigned i = 0; i < authz_env.size(); ++i)
153  envp.push_back(strdupa(authz_env[i].c_str()));
154  envp.push_back(strdupa("CVMFS_AUTHZ_HELPER=yes"));
155  envp.push_back(NULL);
156 
157  int max_fd = sysconf(_SC_OPEN_MAX);
158  assert(max_fd > 0);
159  LogCvmfs(kLogAuthz, kLogDebug | kLogSyslog, "starting authz helper %s",
160  argv0);
161 
162  pid_t pid = fork();
163  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  assert(pid > 0);
178  close(pipe_send[0]);
179  close(pipe_recv[1]);
180 
181  // Don't receive a signal if the helper terminates
182  signal(SIGPIPE, SIG_IGN);
183  pid_ = pid;
184  fd_send_ = pipe_send[1];
185  fd_recv_ = pipe_recv[0];
186 }
187 
188 
190  const QueryInfo &query_info,
191  AuthzToken *authz_token,
192  unsigned *ttl)
193 {
194  *ttl = kDefaultTtl;
195 
196  MutexLockGuard lock_guard(lock_);
197  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  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  assert((fd_send_ >= 0) && (fd_recv_ >= 0));
217 
218  string authz_schema;
219  string pure_membership;
220  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  "\"}}";
229  retval = Send(json_msg) && Recv(&json_msg);
230  if (!retval)
231  return kAuthzNoHelper;
232  AuthzExternalMsg binary_msg;
233  retval = ParseMsg(json_msg, kAuthzMsgPermit, &binary_msg);
234  if (!retval)
235  return kAuthzNoHelper;
236 
237  *ttl = binary_msg.permit.ttl;
238  if (binary_msg.permit.status == kAuthzOk) {
239  *authz_token = binary_msg.permit.token;
240  LogCvmfs(kLogAuthz, kLogDebug, "got token of type %d and size %u",
241  binary_msg.permit.token.type, binary_msg.permit.token.size);
242  }
243 
244  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);
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 
272  string debug_log = GetLogDebugFile();
273  string json_debug_log;
274  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  "}}";
285  bool retval = Send(json_msg);
286  if (!retval)
287  return false;
288 
289  retval = Recv(&json_msg);
290  if (!retval)
291  return false;
292  AuthzExternalMsg binary_msg;
293  retval = ParseMsg(json_msg, kAuthzMsgReady, &binary_msg);
294  if (!retval)
295  return false;
296 
297  return true;
298 }
299 
300 
302  int retval = pthread_mutex_init(&lock_, NULL);
303  assert(retval == 0);
304 }
305 
306 
307 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  header.version = kProtocolVersion;
314  header.length = msg.length();
315  unsigned raw_length = sizeof(header) + msg.length();
316  unsigned char *raw_msg = reinterpret_cast<unsigned char *>(
317  alloca(raw_length));
318  memcpy(raw_msg, &header, sizeof(header));
319  memcpy(raw_msg + sizeof(header), msg.data(), header.length);
320 
321  bool retval = SafeWrite(fd_send_, raw_msg, raw_length);
322  if (!retval)
323  EnterFailState();
324  return retval;
325 }
326 
327 
342  const std::string &json_msg,
343  const AuthzExternalMsgIds expected_msgid,
344  AuthzExternalMsg *binary_msg)
345 {
346  assert(binary_msg != NULL);
347 
348  UniquePtr<JsonDocument> json_document(JsonDocument::Create(json_msg));
349  if (!json_document.IsValid()) {
351  "invalid json from authz helper %s: %s",
352  progname_.c_str(), json_msg.c_str());
353  EnterFailState();
354  return false;
355  }
356 
357  JSON *json_authz = JsonDocument::SearchInObject(
358  json_document->root(), "cvmfs_authz_v1", JSON_OBJECT);
359  if (json_authz == NULL) {
361  "\"cvmfs_authz_v1\" not found in json from authz helper %s: %s",
362  progname_.c_str(), json_msg.c_str());
363  EnterFailState();
364  return false;
365  }
366 
367  if (!ParseMsgId(json_authz, binary_msg) ||
368  (binary_msg->msgid != expected_msgid))
369  {
370  EnterFailState();
371  return false;
372  }
373  if (!ParseRevision(json_authz, binary_msg)) {
374  EnterFailState();
375  return false;
376  }
377  if (binary_msg->msgid == kAuthzMsgPermit) {
378  if (!ParsePermit(json_authz, binary_msg)) {
379  EnterFailState();
380  return false;
381  }
382  }
383  return true;
384 }
385 
386 
388  JSON *json_authz,
389  AuthzExternalMsg *binary_msg)
390 {
391  JSON *json_msgid = JsonDocument::SearchInObject(
392  json_authz, "msgid", JSON_INT);
393  if (json_msgid == NULL) {
395  "\"msgid\" not found in json from authz helper %s",
396  progname_.c_str());
397  EnterFailState();
398  return false;
399  }
400 
401  if ((json_msgid->int_value < 0) ||
402  (json_msgid->int_value >= kAuthzMsgInvalid))
403  {
405  "invalid \"msgid\" in json from authz helper %s: %d",
406  progname_.c_str(), json_msgid->int_value);
407  EnterFailState();
408  return false;
409  }
410 
411  binary_msg->msgid = static_cast<AuthzExternalMsgIds>(json_msgid->int_value);
412  return true;
413 }
414 
415 
421  JSON *json_authz,
422  AuthzExternalMsg *binary_msg)
423 {
424  JSON *json_status =
425  JsonDocument::SearchInObject(json_authz, "status", JSON_INT);
426  if (json_status == NULL) {
428  "\"status\" not found in json from authz helper %s",
429  progname_.c_str());
430  EnterFailState();
431  return false;
432  }
433  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  json_status->int_value);
439  }
440 
441  JSON *json_ttl = JsonDocument::SearchInObject(json_authz, "ttl", JSON_INT);
442  if (json_ttl == NULL) {
443  LogCvmfs(kLogAuthz, kLogDebug, "no ttl, using default");
444  binary_msg->permit.ttl = kDefaultTtl;
445  } else {
446  binary_msg->permit.ttl = std::max(kMinTtl, json_ttl->int_value);
447  }
448 
449  JSON *json_token =
450  JsonDocument::SearchInObject(json_authz, "x509_proxy", JSON_STRING);
451  if (json_token != NULL) {
452  binary_msg->permit.token.type = kTokenX509;
453  string token_binary;
454  bool valid_base64 = Debase64(json_token->string_value, &token_binary);
455  if (!valid_base64) {
457  "invalid Base64 in 'x509_proxy' from authz helper %s",
458  progname_.c_str());
459  EnterFailState();
460  return false;
461  }
462  unsigned size = token_binary.size();
463  binary_msg->permit.token.size = size;
464  if (size > 0) {
465  // The token is passed to the AuthzSessionManager, which takes care of
466  // freeing the memory
467  binary_msg->permit.token.data = smalloc(size);
468  memcpy(binary_msg->permit.token.data, token_binary.data(), size);
469  }
470  }
471 
472  json_token = JsonDocument::SearchInObject(json_authz, "bearer_token",
473  JSON_STRING);
474  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 
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
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  if (binary_msg->permit.token.type == kTokenUnknown) {
497  // No auth token returned, so authz should do... what exactly?
498  // Log error message
500  "No auth token found in returned JSON from Authz helper %s",
501  progname_.c_str());
502  }
503 
504  return true;
505 }
506 
507 
509  JSON *json_authz,
510  AuthzExternalMsg *binary_msg)
511 {
512  JSON *json_revision = JsonDocument::SearchInObject(
513  json_authz, "revision", JSON_INT);
514  if (json_revision == NULL) {
516  "\"revision\" not found in json from authz helper %s",
517  progname_.c_str());
518  EnterFailState();
519  return false;
520  }
521 
522  if (json_revision->int_value < 0) {
524  "invalid \"revision\" in json from authz helper %s: %d",
525  progname_.c_str(), json_revision->int_value);
526  EnterFailState();
527  return false;
528  }
529 
530  binary_msg->protocol_revision = json_revision->int_value;
531  return true;
532 }
533 
534 
535 bool AuthzExternalFetcher::Recv(string *msg) {
536  uint32_t version;
537  ssize_t retval = SafeRead(fd_recv_, &version, sizeof(version));
538  if (retval != static_cast<int>(sizeof(version))) {
539  EnterFailState();
540  return false;
541  }
542  if (version != kProtocolVersion) {
544  "authz helper uses unknown protocol version %u", version);
545  EnterFailState();
546  return false;
547  }
548 
549  uint32_t length;
550  retval = SafeRead(fd_recv_, &length, sizeof(length));
551  if (retval != static_cast<int>(sizeof(length))) {
552  EnterFailState();
553  return false;
554  }
555 
556  msg->clear();
557  char buf[kPageSize];
558  unsigned nbytes = 0;
559  while (nbytes < length) {
560  const unsigned remaining = length - nbytes;
561  retval = SafeRead(fd_recv_, buf, std::min(kPageSize, remaining));
562  if (retval < 0) {
564  "read failure from authz helper %s", progname_.c_str());
565  EnterFailState();
566  return false;
567  }
568  nbytes += retval;
569  msg->append(buf, retval);
570  }
571 
572  return true;
573 }
574 
575 
577  const string &membership,
578  string *authz_schema,
579  string *pure_membership)
580 {
581  vector<string> components = SplitString(membership, '%');
582  *authz_schema = components[0];
583  if (components.size() < 2) {
584  LogCvmfs(kLogAuthz, kLogDebug, "invalid membership: %s",
585  membership.c_str());
586  *pure_membership = "";
587  return;
588  }
589 
590  components.erase(components.begin());
591  *pure_membership = JoinStrings(components, "%");
592 }
#define LogCvmfs(source, mask,...)
Definition: logging.h:20
AuthzExternalMsgIds msgid
Definition: authz_fetch.h:84
std::string search_path_
Definition: authz_fetch.h:175
AuthzExternalFetcher(const std::string &fqrn, const std::string &progname, const std::string &search_path, OptionsManager *options_manager)
std::string membership
Definition: authz_fetch.h:29
static JSON * SearchInObject(const JSON *json_object, const std::string &name, const json_type type)
vector< string > SplitString(const string &str, const char delim, const unsigned max_chunks)
Definition: string.cc:288
Definition: authz.h:39
static const unsigned kChildTimeout
Definition: authz_fetch.h:129
string JoinStrings(const vector< string > &strings, const string &joint)
Definition: string.cc:318
Helper: &quot;I verified, cvmfs, here&#39;s the result&quot;.
Definition: authz_fetch.h:73
void * data
Definition: authz.h:33
void StripAuthzSchema(const std::string &membership, std::string *authz_schema, std::string *pure_membership)
Definition: authz_fetch.cc:576
std::vector< std::string > GetEnvironmentSubset(const std::string &key_prefix, bool strip_prefix)
Definition: options.cc:427
pthread_mutex_t lock_
Definition: authz_fetch.h:206
bool SafeWrite(int fd, const void *buf, size_t nbyte)
Definition: posix.cc:1929
assert((mem||(size==0))&&"Out Of Memory")
bool Debase64(const string &data, string *decoded)
Definition: string.cc:534
void MakePipe(int pipe_fd[2])
Definition: posix.cc:525
bool ParseMsgId(JSON *json_authz, AuthzExternalMsg *binary_msg)
Definition: authz_fetch.cc:387
static const uint32_t kProtocolVersion
Definition: authz_fetch.h:112
Helper: &quot;Yes, cvmfs, I&#39;m here&quot;.
Definition: authz_fetch.h:71
std::string progname_
Definition: authz_fetch.h:170
AuthzToken token
Definition: authz_fetch.h:88
OptionsManager * options_manager_
Definition: authz_fetch.h:201
bool IsValid(const std::string &input) const
Definition: sanitizer.cc:114
bool ParsePermit(JSON *json_authz, AuthzExternalMsg *binary_msg)
Definition: authz_fetch.cc:420
bool FileExists(const std::string &path)
Definition: posix.cc:816
pid_t pid_
Definition: cvmfs.cc:146
int GetLogSyslogLevel()
Definition: logging.cc:167
bool Recv(std::string *msg)
Definition: authz_fetch.cc:535
#define strdupa(s)
Definition: platform_osx.h:285
#define GetLogDebugFile()
ssize_t SafeRead(int fd, void *buf, size_t nbyte)
Definition: posix.cc:1988
static JsonDocument * Create(const std::string &text)
AuthzTokenType type
Definition: authz.h:32
std::string FindHelper(const std::string &membership)
Definition: authz_fetch.cc:248
Cvmfs: &quot;Please verify, helper&quot;.
Definition: authz_fetch.h:72
string StringifyInt(const int64_t value)
Definition: string.cc:78
AuthzStatus status
Definition: authz_fetch.h:87
uint64_t platform_monotonic_time()
struct AuthzExternalMsg::@0 permit
bool ParseRevision(JSON *json_authz, AuthzExternalMsg *binary_msg)
Definition: authz_fetch.cc:508
unsigned size
Definition: authz.h:34
virtual ~AuthzExternalFetcher()
Definition: authz_fetch.cc:70
static const int kMinTtl
Definition: authz_fetch.h:134
virtual AuthzStatus Fetch(const QueryInfo &query_info, AuthzToken *authz_token, unsigned *ttl)
Definition: authz_fetch.cc:189
bool ParseMsg(const std::string &json_msg, const AuthzExternalMsgIds expected_msgid, AuthzExternalMsg *binary_msg)
Definition: authz_fetch.cc:341
bool Send(const std::string &msg)
Definition: authz_fetch.cc:307
string Base64(const string &data)
Definition: string.cc:474
BashOptionsManager options_manager_
Definition: mount.cvmfs.cc:34
Cvmfs: &quot;Please shutdown, helper&quot;.
Definition: authz_fetch.h:74
const unsigned kPageSize
Definition: posix.h:29
Definition: mutex.h:42
AuthzExternalMsgIds
Definition: authz_fetch.h:69
static const unsigned kDefaultTtl
Definition: authz_fetch.h:139
First invalid message id.
Definition: authz_fetch.h:75
struct json_value JSON
Definition: helper_allow.cc:11
static void size_t size
Definition: smalloc.h:47
AuthzStatus
Definition: authz.h:38
const JSON * root() const
Definition: json_document.h:25
int GetLogSyslogFacility()
Definition: logging.cc:213