CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
authz_session.cc
Go to the documentation of this file.
1 
5 #define __STDC_FORMAT_MACROS
6 #include "authz_session.h"
7 
8 #include <errno.h>
9 #include <inttypes.h>
10 #ifdef __APPLE__
11 #include <sys/sysctl.h>
12 #endif
13 
14 #include <cassert>
15 #include <cstdio>
16 #include <cstring>
17 #include <vector>
18 
19 #include "authz/authz_fetch.h"
20 #include "statistics.h"
21 #include "util/concurrency.h"
22 #include "util/logging.h"
23 #include "util/platform.h"
24 #include "util/posix.h"
25 
26 using namespace std; // NOLINT
27 
28 
30  : deadline_sweep_pids_(0)
31  , deadline_sweep_creds_(0)
32  , authz_fetcher_(NULL)
33  , no_pid_(NULL)
34  , no_session_(NULL)
35  , n_fetch_(NULL)
36  , n_grant_(NULL)
37  , n_deny_(NULL) {
38  int retval = pthread_mutex_init(&lock_pid2session_, NULL);
39  assert(retval == 0);
40  retval = pthread_mutex_init(&lock_session2cred_, NULL);
41  assert(retval == 0);
42 
44  pid2session_.Init(16, PidKey(), HashPidKey);
45 }
46 
47 
49  int retval = pthread_mutex_destroy(&lock_pid2session_);
50  assert(retval == 0);
51  retval = pthread_mutex_destroy(&lock_session2cred_);
52  assert(retval == 0);
53 
54  SessionKey empty_key;
55  for (unsigned i = 0; i < session2cred_.capacity(); ++i) {
56  if (session2cred_.keys()[i] != empty_key) {
57  if ((session2cred_.values() + i)->token.data != NULL)
58  free((session2cred_.values() + i)->token.data);
59  }
60  }
61 }
62 
63 
66  session2cred_.Clear();
67  no_session_->Set(0);
68 }
69 
70 
72  perf::Statistics *statistics) {
73  AuthzSessionManager *authz_mgr = new AuthzSessionManager();
74  authz_mgr->authz_fetcher_ = authz_fetcher;
75 
76  authz_mgr->no_pid_ = statistics->Register("authz.no_pid", "cached pids");
77  authz_mgr->no_session_ = statistics->Register("authz.no_session",
78  "cached sessions");
79  authz_mgr->n_fetch_ = statistics->Register(
80  "authz.n_fetch", "overall number of authz helper invocations");
81  authz_mgr->n_grant_ = statistics->Register(
82  "authz.n_grant", "overall number of granted membership queries");
83  authz_mgr->n_deny_ = statistics->Register(
84  "authz.n_deny", "overall number of denied membership queries");
85 
86  return authz_mgr;
87 }
88 
89 
93 bool AuthzSessionManager::GetPidInfo(pid_t pid, PidKey *pid_key) {
94  int retval;
95 
96  // TODO(jblomer): better in platform.h? Maybe a bit too bulky for that?
97 #ifdef __APPLE__
98  pid_key->sid = getsid(pid);
99  if (pid_key->sid == static_cast<pid_t>(-1)) {
100  LogCvmfs(kLogAuthz, kLogDebug, "failed to get sid (%s)", strerror(errno));
101  return false;
102  }
103 
104  int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
105  struct kinfo_proc kp;
106  size_t len = sizeof(kp);
107  retval = sysctl(mib, 4, &kp, &len, NULL, 0);
108  if (retval == -1) {
109  LogCvmfs(kLogAuthz, kLogDebug, "failed to get pid info (%s)",
110  strerror(errno));
111  return false;
112  }
113  pid_key->uid = kp.kp_eproc.e_pcred.p_ruid;
114  pid_key->gid = kp.kp_eproc.e_pcred.p_rgid;
115  int64_t usec = static_cast<int64_t>(kp.kp_proc.p_un.__p_starttime.tv_sec)
116  * 1000000;
117  usec += static_cast<int64_t>(kp.kp_proc.p_un.__p_starttime.tv_usec);
118  pid_key->pid_bday = usec;
119  pid_key->pid = pid;
120  return true;
121 #endif
122 
123  const int kMaxProcPath = 64; // Enough to store /proc/PID/stat
124  char pid_path[kMaxProcPath];
125  if (snprintf(pid_path, kMaxProcPath, "/proc/%d/stat", pid) >= kMaxProcPath) {
126  return false;
127  }
128 
129  FILE *fp_stat = fopen(pid_path, "r");
130  if (fp_stat == NULL) {
132  "Failed to open status file /proc/%d/stat: (errno=%d) %s", pid,
133  errno, strerror(errno));
135  "Authorization for session %d disappeared", pid);
136  return false;
137  }
138 
139  // The uid and gid can be gathered from /proc/$PID/stat file ownership
140  int fd_stat = fileno(fp_stat);
141  platform_stat64 info;
142  retval = platform_fstat(fd_stat, &info);
143  if (retval != 0) {
144  fclose(fp_stat);
146  "Failed to get stat information of running process.");
147  return false;
148  }
149  pid_key->uid = info.st_uid;
150  pid_key->gid = info.st_gid;
151 
152  // TODO(bbockelm): EINTR handling
153  retval = fscanf(fp_stat,
154  "%*d %*s %*c %*d %*d %d %*d %*d %*u %*u %*u %*u "
155  "%*u %*u %*u %*d %*d %*d %*d %*d %*d %" SCNu64,
156  &(pid_key->sid), &(pid_key->pid_bday));
157  fclose(fp_stat);
158  if (retval != 2) {
159  if (errno == 0) {
160  errno = EINVAL;
161  }
163  "Failed to parse status file for "
164  "pid %d: (errno=%d) %s, fscanf result %d",
165  pid, errno, strerror(errno), retval);
166  return false;
167  }
168 
169  pid_key->pid = pid;
170  return true;
171 }
172 
173 
178  const std::string &membership) {
179  SessionKey session_key;
180  PidKey pid_key;
181  bool retval = LookupSessionKey(pid, &pid_key, &session_key);
182  if (!retval)
183  return NULL;
184 
185  AuthzData authz_data;
186  const bool granted = LookupAuthzData(pid_key, session_key, membership,
187  &authz_data);
188  if (!granted)
189  return NULL;
190  return authz_data.token.DeepCopy();
191 }
192 
193 
194 bool AuthzSessionManager::IsMemberOf(const pid_t pid,
195  const std::string &membership) {
196  SessionKey session_key;
197  PidKey pid_key;
198  bool retval = LookupSessionKey(pid, &pid_key, &session_key);
199  if (!retval)
200  return false;
201 
202  AuthzData authz_data;
203  return LookupAuthzData(pid_key, session_key, membership, &authz_data);
204 }
205 
206 
212  const SessionKey &session_key,
213  const std::string &membership,
214  AuthzData *authz_data) {
215  assert(authz_data != NULL);
216 
217  bool found;
218  {
220  MaySweepCreds();
221  found = session2cred_.Lookup(session_key, authz_data);
222  }
223  if (found) {
225  "cached authz data for sid %d, membership %s, status %d",
226  session_key.sid, authz_data->membership.c_str(),
227  authz_data->status);
228  const bool granted = authz_data->IsGranted(membership);
229  if (granted)
231  else
233  return granted;
234  }
235 
236  // Not found in cache, ask for help
238  unsigned ttl;
239  authz_data->status = authz_fetcher_->Fetch(
240  AuthzFetcher::QueryInfo(pid_key.pid, pid_key.uid, pid_key.gid,
241  membership),
242  &(authz_data->token), &ttl);
243  authz_data->deadline = platform_monotonic_time() + ttl;
244  if (authz_data->status == kAuthzOk)
245  authz_data->membership = membership;
247  "fetched authz data for sid %d (pid %d), membership %s, status %d, "
248  "ttl %u",
249  session_key.sid, pid_key.pid, authz_data->membership.c_str(),
250  authz_data->status, ttl);
251 
252  {
254  if (!session2cred_.Contains(session_key))
256  session2cred_.Insert(session_key, *authz_data);
257  }
258  const bool granted = authz_data->status == kAuthzOk;
259  if (granted)
261  else
263  return granted;
264 }
265 
266 
273  PidKey *pid_key,
274  SessionKey *session_key) {
275  assert(pid_key != NULL);
276  assert(session_key != NULL);
277  if (!GetPidInfo(pid, pid_key))
278  return false;
279 
280  bool found;
281  {
283  found = pid2session_.Lookup(*pid_key, session_key);
284  MaySweepPids();
285  }
286  if (found) {
288  "Session key %d/%" PRIu64 " in cache; sid=%d, bday=%" PRIu64,
289  pid_key->pid, pid_key->pid_bday, session_key->sid,
290  session_key->sid_bday);
291  return true;
292  }
293 
295  "Session key not found in cache, getting information from OS");
296  PidKey sid_key;
297  pid_t sid = pid_key->sid;
298  if (sid == 0) {
299  // This can happen inside process namespaces such as those used by
300  // singularity and cvmfsexec. Use init process id instead.
301  sid = 1;
302  }
303  if (!GetPidInfo(sid, &sid_key))
304  return false;
305 
306  session_key->sid = sid_key.pid;
307  session_key->sid_bday = sid_key.pid_bday;
308  {
311  if (!pid2session_.Contains(*pid_key))
313  pid2session_.Insert(*pid_key, *session_key);
314  }
315 
316  LogCvmfs(kLogAuthz, kLogDebug, "Lookup key %d/%" PRIu64 "; sid=%d, bday=%lu",
317  pid_key->pid, pid_key->pid_bday, session_key->sid,
318  session_key->sid_bday);
319  return true;
320 }
321 
322 
327  uint64_t now = platform_monotonic_time();
328  if (now >= deadline_sweep_creds_) {
329  SweepCreds(now);
331  }
332 }
333 
334 
339  uint64_t now = platform_monotonic_time();
340  if (now >= deadline_sweep_pids_) {
341  SweepPids(now);
343  }
344 }
345 
346 
352  SessionKey empty_key;
353  vector<SessionKey> trash_bin;
354  for (unsigned i = 0; i < session2cred_.capacity(); ++i) {
355  SessionKey this_key = session2cred_.keys()[i];
356  if (this_key != empty_key) {
357  if (now >= (session2cred_.values() + i)->deadline)
358  trash_bin.push_back(this_key);
359  }
360  }
361 
362  for (unsigned i = 0; i < trash_bin.size(); ++i) {
363  session2cred_.Erase(trash_bin[i]);
365  }
366 }
367 
368 
373 void AuthzSessionManager::SweepPids(uint64_t now) {
374  PidKey empty_key;
375  vector<PidKey> trash_bin;
376  for (unsigned i = 0; i < pid2session_.capacity(); ++i) {
377  PidKey this_key = pid2session_.keys()[i];
378  if (this_key != empty_key) {
379  if (now >= this_key.deadline)
380  trash_bin.push_back(this_key);
381  }
382  }
383 
384  for (unsigned i = 0; i < trash_bin.size(); ++i) {
385  pid2session_.Erase(trash_bin[i]);
387  }
388 }
void Dec(class Counter *counter)
Definition: statistics.h:49
Counter * Register(const std::string &name, const std::string &desc)
Definition: statistics.cc:163
SmallHashDynamic< PidKey, SessionKey > pid2session_
struct stat64 platform_stat64
pthread_mutex_t lock_pid2session_
perf::Counter * n_deny_
bool LookupAuthzData(const PidKey &pid_key, const SessionKey &session_key, const std::string &membership, AuthzData *authz_data)
bool LookupSessionKey(pid_t pid, PidKey *pid_key, SessionKey *session_key)
Definition: authz.h:39
static const unsigned kPidLifetime
Definition: authz_session.h:67
static uint32_t HashSessionKey(const SessionKey &key)
void SweepCreds(uint64_t now)
virtual AuthzStatus Fetch(const QueryInfo &query_info, AuthzToken *authz_token, unsigned *ttl)=0
SmallHashDynamic< SessionKey, AuthzData > session2cred_
bool GetPidInfo(pid_t pid, PidKey *pid_key)
AuthzToken token
Definition: authz.h:61
AuthzFetcher * authz_fetcher_
assert((mem||(size==0))&&"Out Of Memory")
AuthzToken * GetTokenCopy(const pid_t pid, const std::string &membership)
AuthzToken * DeepCopy()
Definition: authz.cc:11
void SweepPids(uint64_t now)
void Set(const int64_t val)
Definition: statistics.h:33
uint64_t deadline_sweep_creds_
perf::Counter * no_session_
static AuthzSessionManager * Create(AuthzFetcher *authz_fetcher, perf::Statistics *statistics)
std::string membership
Definition: authz.h:63
static uint32_t HashPidKey(const PidKey &key)
bool IsMemberOf(const pid_t pid, const std::string &membership)
pthread_mutex_t lock_session2cred_
perf::Counter * no_pid_
uint64_t platform_monotonic_time()
void Inc(class Counter *counter)
Definition: statistics.h:50
perf::Counter * n_grant_
perf::Counter * n_fetch_
static const unsigned kSweepInterval
Definition: authz_session.h:62
uint64_t deadline_sweep_pids_
AuthzStatus status
Definition: authz.h:64
Definition: mutex.h:42
bool IsGranted(const std::string &expected_membership) const
Definition: authz.h:58
int platform_fstat(int filedes, platform_stat64 *buf)
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545
uint64_t deadline
Definition: authz.h:62