CernVM-FS  2.9.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 "logging.h"
21 #include "platform.h"
22 #include "statistics.h"
23 #include "util/posix.h"
24 #include "util_concurrency.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 {
39  int retval = pthread_mutex_init(&lock_pid2session_, NULL);
40  assert(retval == 0);
41  retval = pthread_mutex_init(&lock_session2cred_, NULL);
42  assert(retval == 0);
43 
45  pid2session_.Init(16, PidKey(), HashPidKey);
46 }
47 
48 
50  int retval = pthread_mutex_destroy(&lock_pid2session_);
51  assert(retval == 0);
52  retval = pthread_mutex_destroy(&lock_session2cred_);
53  assert(retval == 0);
54 
55  SessionKey empty_key;
56  for (unsigned i = 0; i < session2cred_.capacity(); ++i) {
57  if (session2cred_.keys()[i] != empty_key) {
58  if ((session2cred_.values() + i)->token.data != NULL)
59  free((session2cred_.values() + i)->token.data);
60  }
61  }
62 }
63 
64 
67  session2cred_.Clear();
68  no_session_->Set(0);
69 }
70 
71 
73  AuthzFetcher *authz_fetcher,
74  perf::Statistics *statistics)
75 {
76  AuthzSessionManager *authz_mgr = new AuthzSessionManager();
77  authz_mgr->authz_fetcher_ = authz_fetcher;
78 
79  authz_mgr->no_pid_ = statistics->Register("authz.no_pid", "cached pids");
80  authz_mgr->no_session_ = statistics->Register(
81  "authz.no_session", "cached sessions");
82  authz_mgr->n_fetch_ = statistics->Register(
83  "authz.n_fetch", "overall number of authz helper invocations");
84  authz_mgr->n_grant_ = statistics->Register(
85  "authz.n_grant", "overall number of granted membership queries");
86  authz_mgr->n_deny_ = statistics->Register(
87  "authz.n_deny", "overall number of denied membership queries");
88 
89  return authz_mgr;
90 }
91 
92 
96 bool AuthzSessionManager::GetPidInfo(pid_t pid, PidKey *pid_key) {
97  int retval;
98 
99  // TODO(jblomer): better in platform.h? Maybe a bit too bulky for that?
100 #ifdef __APPLE__
101  pid_key->sid = getsid(pid);
102  if (pid_key->sid == static_cast<pid_t>(-1)) {
103  LogCvmfs(kLogAuthz, kLogDebug, "failed to get sid (%s)", strerror(errno));
104  return false;
105  }
106 
107  int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid };
108  struct kinfo_proc kp;
109  size_t len = sizeof(kp);
110  retval = sysctl(mib, 4, &kp, &len, NULL, 0);
111  if (retval == -1) {
112  LogCvmfs(kLogAuthz, kLogDebug, "failed to get pid info (%s)",
113  strerror(errno));
114  return false;
115  }
116  pid_key->uid = kp.kp_eproc.e_pcred.p_ruid;
117  pid_key->gid = kp.kp_eproc.e_pcred.p_rgid;
118  int64_t usec =
119  static_cast<int64_t>(kp.kp_proc.p_un.__p_starttime.tv_sec) * 1000000;
120  usec += static_cast<int64_t>(kp.kp_proc.p_un.__p_starttime.tv_usec);
121  pid_key->pid_bday = usec;
122  pid_key->pid = pid;
123  return true;
124 #endif
125 
126  const int kMaxProcPath = 64; // Enough to store /proc/PID/stat
127  char pid_path[kMaxProcPath];
128  if (snprintf(pid_path, kMaxProcPath, "/proc/%d/stat", pid) >= kMaxProcPath) {
129  return false;
130  }
131 
132  FILE *fp_stat = fopen(pid_path, "r");
133  if (fp_stat == NULL) {
135  "Failed to open status file /proc/%d/stat: (errno=%d) %s",
136  pid, errno, strerror(errno));
138  "Authorization for session %d disappeared", pid);
139  return false;
140  }
141 
142  // The uid and gid can be gathered from /proc/$PID/stat file ownership
143  int fd_stat = fileno(fp_stat);
144  platform_stat64 info;
145  retval = platform_fstat(fd_stat, &info);
146  if (retval != 0) {
147  fclose(fp_stat);
149  "Failed to get stat information of running process.");
150  return false;
151  }
152  pid_key->uid = info.st_uid;
153  pid_key->gid = info.st_gid;
154 
155  // TODO(bbockelm): EINTR handling
156  retval = fscanf(fp_stat, "%*d %*s %*c %*d %*d %d %*d %*d %*u %*u %*u %*u "
157  "%*u %*u %*u %*d %*d %*d %*d %*d %*d %" SCNu64,
158  &(pid_key->sid), &(pid_key->pid_bday));
159  fclose(fp_stat);
160  if (retval != 2) {
161  if (errno == 0) {
162  errno = EINVAL;
163  }
164  LogCvmfs(kLogAuthz, kLogDebug, "Failed to parse status file for "
165  "pid %d: (errno=%d) %s, fscanf result %d", pid, errno,
166  strerror(errno), retval);
167  return false;
168  }
169 
170  pid_key->pid = pid;
171  return true;
172 }
173 
174 
179  const pid_t pid,
180  const std::string &membership)
181 {
182  SessionKey session_key;
183  PidKey pid_key;
184  bool retval = LookupSessionKey(pid, &pid_key, &session_key);
185  if (!retval)
186  return NULL;
187 
188  AuthzData authz_data;
189  const bool granted =
190  LookupAuthzData(pid_key, session_key, membership, &authz_data);
191  if (!granted)
192  return NULL;
193  return authz_data.token.DeepCopy();
194 }
195 
196 
198  const pid_t pid,
199  const std::string &membership)
200 {
201  SessionKey session_key;
202  PidKey pid_key;
203  bool retval = LookupSessionKey(pid, &pid_key, &session_key);
204  if (!retval)
205  return false;
206 
207  AuthzData authz_data;
208  return LookupAuthzData(pid_key, session_key, membership, &authz_data);
209 }
210 
211 
217  const PidKey &pid_key,
218  const SessionKey &session_key,
219  const std::string &membership,
220  AuthzData *authz_data)
221 {
222  assert(authz_data != NULL);
223 
224  bool found;
225  {
227  MaySweepCreds();
228  found = session2cred_.Lookup(session_key, authz_data);
229  }
230  if (found) {
232  "cached authz data for sid %d, membership %s, status %d",
233  session_key.sid, authz_data->membership.c_str(),
234  authz_data->status);
235  const bool granted = authz_data->IsGranted(membership);
236  if (granted)
238  else
240  return granted;
241  }
242 
243  // Not found in cache, ask for help
245  unsigned ttl;
246  authz_data->status = authz_fetcher_->Fetch(
247  AuthzFetcher::QueryInfo(pid_key.pid, pid_key.uid, pid_key.gid, membership),
248  &(authz_data->token), &ttl);
249  authz_data->deadline = platform_monotonic_time() + ttl;
250  if (authz_data->status == kAuthzOk)
251  authz_data->membership = membership;
253  "fetched authz data for sid %d (pid %d), membership %s, status %d, "
254  "ttl %u", session_key.sid, pid_key.pid,
255  authz_data->membership.c_str(), authz_data->status, ttl);
256 
257  {
259  if (!session2cred_.Contains(session_key)) perf::Inc(no_session_);
260  session2cred_.Insert(session_key, *authz_data);
261  }
262  const bool granted = authz_data->status == kAuthzOk;
263  if (granted)
265  else
267  return granted;
268 }
269 
270 
277  pid_t pid,
278  PidKey *pid_key,
279  SessionKey *session_key)
280 {
281  assert(pid_key != NULL);
282  assert(session_key != NULL);
283  if (!GetPidInfo(pid, pid_key))
284  return false;
285 
286  bool found;
287  {
289  found = pid2session_.Lookup(*pid_key, session_key);
290  MaySweepPids();
291  }
292  if (found) {
294  "Session key %d/%" PRIu64 " in cache; sid=%d, bday=%" PRIu64,
295  pid_key->pid, pid_key->pid_bday,
296  session_key->sid, session_key->sid_bday);
297  return true;
298  }
299 
301  "Session key not found in cache, getting information from OS");
302  PidKey sid_key;
303  pid_t sid = pid_key->sid;
304  if (sid == 0) {
305  // This can happen inside process namespaces such as those used by
306  // singularity and cvmfsexec. Use init process id instead.
307  sid = 1;
308  }
309  if (!GetPidInfo(sid, &sid_key))
310  return false;
311 
312  session_key->sid = sid_key.pid;
313  session_key->sid_bday = sid_key.pid_bday;
314  {
317  if (!pid2session_.Contains(*pid_key)) perf::Inc(no_pid_);
318  pid2session_.Insert(*pid_key, *session_key);
319  }
320 
321  LogCvmfs(kLogAuthz, kLogDebug, "Lookup key %d/%" PRIu64 "; sid=%d, bday=%llu",
322  pid_key->pid, pid_key->pid_bday,
323  session_key->sid, session_key->sid_bday);
324  return true;
325 }
326 
327 
332  uint64_t now = platform_monotonic_time();
333  if (now >= deadline_sweep_creds_) {
334  SweepCreds(now);
336  }
337 }
338 
339 
344  uint64_t now = platform_monotonic_time();
345  if (now >= deadline_sweep_pids_) {
346  SweepPids(now);
348  }
349 }
350 
351 
357  SessionKey empty_key;
358  vector<SessionKey> trash_bin;
359  for (unsigned i = 0; i < session2cred_.capacity(); ++i) {
360  SessionKey this_key = session2cred_.keys()[i];
361  if (this_key != empty_key) {
362  if (now >= (session2cred_.values() + i)->deadline)
363  trash_bin.push_back(this_key);
364  }
365  }
366 
367  for (unsigned i = 0; i < trash_bin.size(); ++i) {
368  session2cred_.Erase(trash_bin[i]);
370  }
371 }
372 
373 
378 void AuthzSessionManager::SweepPids(uint64_t now) {
379  PidKey empty_key;
380  vector<PidKey> trash_bin;
381  for (unsigned i = 0; i < pid2session_.capacity(); ++i) {
382  PidKey this_key = pid2session_.keys()[i];
383  if (this_key != empty_key) {
384  if (now >= this_key.deadline)
385  trash_bin.push_back(this_key);
386  }
387  }
388 
389  for (unsigned i = 0; i < trash_bin.size(); ++i) {
390  pid2session_.Erase(trash_bin[i]);
392  }
393 }
#define LogCvmfs(source, mask,...)
Definition: logging.h:20
void Dec(class Counter *counter)
Definition: statistics.h:49
Counter * Register(const std::string &name, const std::string &desc)
Definition: statistics.cc:139
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
bool IsGranted(const std::string &expected_membership) const
Definition: authz.h:58
int platform_fstat(int filedes, platform_stat64 *buf)
uint64_t deadline
Definition: authz.h:62