XRootD
XrdAccSciTokens Class Reference
+ Inheritance diagram for XrdAccSciTokens:
+ Collaboration diagram for XrdAccSciTokens:

Public Member Functions

 XrdAccSciTokens (XrdSysLogger *lp, const char *parms, XrdAccAuthorize *chain, XrdOucEnv *envP)
 
virtual ~XrdAccSciTokens ()
 
virtual XrdAccPrivs Access (const XrdSecEntity *Entity, const char *path, const Access_Operation oper, XrdOucEnv *env) override
 
virtual int Audit (const int accok, const XrdSecEntity *Entity, const char *path, const Access_Operation oper, XrdOucEnv *Env=0) override
 
std::string GetConfigFile ()
 
virtual Issuers IssuerList () override
 
virtual int Test (const XrdAccPrivs priv, const Access_Operation oper) override
 
virtual bool Validate (const char *token, std::string &emsg, long long *expT, XrdSecEntity *Entity) override
 
- Public Member Functions inherited from XrdAccAuthorize
 XrdAccAuthorize ()
 Constructor. More...
 
virtual ~XrdAccAuthorize ()
 Destructor. More...
 
- Public Member Functions inherited from XrdSciTokensHelper
 XrdSciTokensHelper ()
 Constructor and Destructor. More...
 
virtual ~XrdSciTokensHelper ()
 
- Public Member Functions inherited from XrdSciTokensMon
 XrdSciTokensMon ()
 
 ~XrdSciTokensMon ()
 
bool Mon_isIO (const Access_Operation oper)
 
void Mon_Report (const XrdSecEntity &Entity, const std::string &subject, const std::string &username)
 

Additional Inherited Members

- Public Types inherited from XrdSciTokensHelper
typedef std::vector< ValidIssuerIssuers
 

Detailed Description

Definition at line 467 of file XrdSciTokensAccess.cc.

Constructor & Destructor Documentation

◆ XrdAccSciTokens()

XrdAccSciTokens::XrdAccSciTokens ( XrdSysLogger lp,
const char *  parms,
XrdAccAuthorize chain,
XrdOucEnv envP 
)
inline

Definition at line 478 of file XrdSciTokensAccess.cc.

478  :
479  m_chain(chain),
480  m_parms(parms ? parms : ""),
481  m_next_clean(monotonic_time() + m_expiry_secs),
482  m_log(lp, "scitokens_")
483  {
484  pthread_rwlock_init(&m_config_lock, nullptr);
485  m_config_lock_initialized = true;
486  m_log.Say("++++++ XrdAccSciTokens: Initialized SciTokens-based authorization.");
487  if (!Config(envP)) {
488  throw std::runtime_error("Failed to configure SciTokens authorization.");
489  }
490  }
void Say(const char *text1, const char *text2=0, const char *txt3=0, const char *text4=0, const char *text5=0, const char *txt6=0)
Definition: XrdSysError.cc:141
XrdOucEnv * envP
Definition: XrdPss.cc:108

References XrdProxy::envP, and XrdSysError::Say().

+ Here is the call graph for this function:

◆ ~XrdAccSciTokens()

virtual XrdAccSciTokens::~XrdAccSciTokens ( )
inlinevirtual

Definition at line 492 of file XrdSciTokensAccess.cc.

492  {
493  if (m_config_lock_initialized) {
494  pthread_rwlock_destroy(&m_config_lock);
495  }
496  }

Member Function Documentation

◆ Access()

virtual XrdAccPrivs XrdAccSciTokens::Access ( const XrdSecEntity Entity,
const char *  path,
const Access_Operation  oper,
XrdOucEnv Env 
)
inlineoverridevirtual

Check whether or not the client is permitted specified access to a path.

Parameters
Entity-> Authentication information
path-> The logical path which is the target of oper
oper-> The operation being attempted (see the enum above). If the oper is AOP_Any, then the actual privileges are returned and the caller may make subsequent tests using Test().
Env-> Environmental information at the time of the operation as supplied by the path CGI string. This is optional and the pointer may be zero.
Returns
Permit: a non-zero value (access is permitted) Deny: zero (access is denied)

Implements XrdAccAuthorize.

Definition at line 498 of file XrdSciTokensAccess.cc.

502  {
503  const char *authz = env ? env->Get("authz") : nullptr;
504  // Note: this is more permissive than the plugin was previously.
505  // The prefix 'Bearer%20' used to be required as that's what HTTP
506  // required. However, to make this more pleasant for XRootD protocol
507  // users, we now simply "handle" the prefix insterad of requiring it.
508  if (authz && !strncmp(authz, "Bearer%20", 9)) {
509  authz += 9;
510  }
511  // If there's no request-specific token, then see if the ZTN authorization
512  // has provided us with a session token.
513  if (!authz && Entity && !strcmp("ztn", Entity->prot) && Entity->creds &&
514  Entity->credslen && Entity->creds[Entity->credslen] == '\0')
515  {
516  authz = Entity->creds;
517  }
518  if (authz == nullptr) {
519  return OnMissing(Entity, path, oper, env);
520  }
521  m_log.Log(LogMask::Debug, "Access", "Trying token-based access control");
522  std::shared_ptr<XrdAccRules> access_rules;
523  uint64_t now = monotonic_time();
524  Check(now);
525  {
526  std::lock_guard<std::mutex> guard(m_mutex);
527  const auto iter = m_map.find(authz);
528  if (iter != m_map.end() && !iter->second->expired()) {
529  access_rules = iter->second;
530  }
531  }
532  if (!access_rules) {
533  m_log.Log(LogMask::Debug, "Access", "Token not found in recent cache; parsing.");
534  try {
535  uint64_t cache_expiry;
536  AccessRulesRaw rules;
537  std::string username;
538  std::string token_subject;
539  std::string issuer;
540  std::vector<MapRule> map_rules;
541  std::vector<std::string> groups;
542  uint32_t authz_strategy;
543  if (GenerateAcls(authz, cache_expiry, rules, username, token_subject, issuer, map_rules, groups, authz_strategy)) {
544  access_rules.reset(new XrdAccRules(now + cache_expiry, username, token_subject, issuer, map_rules, groups, authz_strategy));
545  access_rules->parse(rules);
546  } else {
547  m_log.Log(LogMask::Warning, "Access", "Failed to generate ACLs for token");
548  return OnMissing(Entity, path, oper, env);
549  }
550  if (m_log.getMsgMask() & LogMask::Debug) {
551  m_log.Log(LogMask::Debug, "Access", "New valid token", access_rules->str().c_str());
552  }
553  } catch (std::exception &exc) {
554  m_log.Log(LogMask::Warning, "Access", "Error generating ACLs for authorization", exc.what());
555  return OnMissing(Entity, path, oper, env);
556  }
557  std::lock_guard<std::mutex> guard(m_mutex);
558  m_map[authz] = access_rules;
559  } else if (m_log.getMsgMask() & LogMask::Debug) {
560  m_log.Log(LogMask::Debug, "Access", "Cached token", access_rules->str().c_str());
561  }
562 
563  // Strategy: assuming the corresponding strategy is enabled, we populate the name in
564  // the XrdSecEntity if:
565  // 1. There are scopes present in the token that authorize the request,
566  // 2. The token is mapped by some rule in the mapfile (group or subject-based mapping).
567  // The default username for the issuer is only used in (1).
568  // If the scope-based mapping is successful, authorize immediately. Otherwise, if the
569  // mapping is successful, we potentially chain to another plugin.
570  //
571  // We always populate the issuer and the groups, if present.
572 
573  // Access may be authorized; populate XrdSecEntity
574  XrdSecEntity new_secentity;
575  new_secentity.vorg = nullptr;
576  new_secentity.grps = nullptr;
577  new_secentity.role = nullptr;
578  new_secentity.secMon = Entity->secMon;
579  new_secentity.addrInfo = Entity->addrInfo;
580  const auto &issuer = access_rules->get_issuer();
581  if (!issuer.empty()) {
582  new_secentity.vorg = strdup(issuer.c_str());
583  }
584  bool group_success = false;
585  if ((access_rules->get_authz_strategy() & IssuerAuthz::Group) && access_rules->groups().size()) {
586  std::stringstream ss;
587  for (const auto &grp : access_rules->groups()) {
588  ss << grp << " ";
589  }
590  const auto &groups_str = ss.str();
591  new_secentity.grps = static_cast<char*>(malloc(groups_str.size() + 1));
592  if (new_secentity.grps) {
593  memcpy(new_secentity.grps, groups_str.c_str(), groups_str.size());
594  new_secentity.grps[groups_str.size()] = '\0';
595  }
596  group_success = true;
597  }
598 
599  std::string username;
600  bool mapping_success = false;
601  bool scope_success = false;
602  username = access_rules->get_username(path);
603 
604  mapping_success = (access_rules->get_authz_strategy() & IssuerAuthz::Mapping) && !username.empty();
605  scope_success = (access_rules->get_authz_strategy() & IssuerAuthz::Capability) && access_rules->apply(oper, path);
606  if (scope_success && (m_log.getMsgMask() & LogMask::Debug)) {
607  std::stringstream ss;
608  ss << "Grant authorization based on scopes for operation=" << OpToName(oper) << ", path=" << path;
609  m_log.Log(LogMask::Debug, "Access", ss.str().c_str());
610  }
611 
612  if (!scope_success && !mapping_success && !group_success) {
613  auto returned_accs = OnMissing(&new_secentity, path, oper, env);
614  // Clean up the new_secentity
615  if (new_secentity.vorg != nullptr) free(new_secentity.vorg);
616  if (new_secentity.grps != nullptr) free(new_secentity.grps);
617  if (new_secentity.role != nullptr) free(new_secentity.role);
618 
619  return returned_accs;
620  }
621 
622  // Default user only applies to scope-based mappings.
623  if (scope_success && username.empty()) {
624  username = access_rules->get_default_username();
625  }
626 
627  // Setting the request.name will pass the username to the next plugin.
628  // Ensure we do that only if map-based or scope-based authorization worked.
629  if (scope_success || mapping_success) {
630  // Set scitokens.name in the extra attribute
631  Entity->eaAPI->Add("request.name", username, true);
632  new_secentity.eaAPI->Add("request.name", username, true);
633  m_log.Log(LogMask::Debug, "Access", "Request username", username.c_str());
634  }
635 
636  // Make the token subject available. Even though it's a reasonably bad idea
637  // to use for *authorization* for file access, there may be other use cases.
638  // For example, the combination of (vorg, token.subject) is a reasonable
639  // approximation of a unique 'entity' (either person or a robot) and is
640  // more reasonable to use for resource fairshare in XrdThrottle.
641  const auto &token_subject = access_rules->get_token_subject();
642  if (!token_subject.empty()) {
643  Entity->eaAPI->Add("token.subject", token_subject, true);
644  }
645 
646  // When the scope authorized this access, allow immediately. Otherwise, chain
647  XrdAccPrivs returned_op = scope_success ? AddPriv(oper, XrdAccPriv_None) : OnMissing(&new_secentity, path, oper, env);
648 
649  // Since we are doing an early return, insert token info into the
650  // monitoring stream if monitoring is in effect and access granted
651  //
652  if (Entity->secMon && scope_success && returned_op && Mon_isIO(oper))
653  Mon_Report(new_secentity, token_subject, username);
654 
655  // Cleanup the new_secentry
656  if (new_secentity.vorg != nullptr) free(new_secentity.vorg);
657  if (new_secentity.grps != nullptr) free(new_secentity.grps);
658  if (new_secentity.role != nullptr) free(new_secentity.role);
659 
660  return returned_op;
661  }
XrdAccPrivs
Definition: XrdAccPrivs.hh:39
@ XrdAccPriv_None
Definition: XrdAccPrivs.hh:53
@ Warning
bool Mon_isIO(const Access_Operation oper)
void Mon_Report(const XrdSecEntity &Entity, const std::string &subject, const std::string &username)
bool Add(XrdSecAttr &attr)
char * vorg
Entity's virtual organization(s)
Definition: XrdSecEntity.hh:71
int credslen
Length of the 'creds' data.
Definition: XrdSecEntity.hh:78
XrdNetAddrInfo * addrInfo
Entity's connection details.
Definition: XrdSecEntity.hh:80
XrdSecEntityAttr * eaAPI
non-const API to attributes
Definition: XrdSecEntity.hh:92
char prot[XrdSecPROTOIDSIZE]
Auth protocol used (e.g. krb5)
Definition: XrdSecEntity.hh:67
char * creds
Raw entity credentials or cert.
Definition: XrdSecEntity.hh:77
XrdSecMonitor * secMon
If !0 security monitoring enabled.
Definition: XrdSecEntity.hh:89
char * grps
Entity's group name(s)
Definition: XrdSecEntity.hh:73
char * role
Entity's role(s)
Definition: XrdSecEntity.hh:72
int getMsgMask()
Definition: XrdSysError.hh:156
void Log(int mask, const char *esfx, const char *text1, const char *text2=0, const char *text3=0)
Definition: XrdSysError.hh:133

References XrdSecEntityAttr::Add(), XrdSecEntity::addrInfo, XrdSecEntity::creds, XrdSecEntity::credslen, Macaroons::Debug, XrdSecEntity::eaAPI, XrdOucEnv::Get(), XrdSysError::getMsgMask(), XrdSecEntity::grps, XrdSysError::Log(), XrdSciTokensMon::Mon_isIO(), XrdSciTokensMon::Mon_Report(), XrdSecEntity::prot, XrdSecEntity::role, XrdSecEntity::secMon, XrdSecEntity::vorg, Warning, and XrdAccPriv_None.

+ Here is the call graph for this function:

◆ Audit()

virtual int XrdAccSciTokens::Audit ( const int  accok,
const XrdSecEntity Entity,
const char *  path,
const Access_Operation  oper,
XrdOucEnv Env = 0 
)
inlineoverridevirtual

Route an audit message to the appropriate audit exit routine. See XrdAccAudit.h for more information on how the default implementation works. Currently, this method is not called by the ofs but should be used by the implementation to record denials or grants, as warranted.

Parameters
accok-> True is access was grated; false otherwise.
Entity-> Authentication information
path-> The logical path which is the target of oper
oper-> The operation being attempted (see above)
Env-> Environmental information at the time of the operation as supplied by the path CGI string. This is optional and the pointer may be zero.
Returns
Success: !0 information recorded. Failure: 0 information could not be recorded.

Implements XrdAccAuthorize.

Definition at line 730 of file XrdSciTokensAccess.cc.

735  {
736  return 0;
737  }

◆ GetConfigFile()

std::string XrdAccSciTokens::GetConfigFile ( )
inline

Definition at line 745 of file XrdSciTokensAccess.cc.

745  {
746  return m_cfg_file;
747  }

◆ IssuerList()

virtual Issuers XrdAccSciTokens::IssuerList ( )
inlineoverridevirtual

Implements XrdSciTokensHelper.

Definition at line 663 of file XrdSciTokensAccess.cc.

664  {
665  /*
666  Convert the m_issuers into the data structure:
667  struct ValidIssuer
668  {std::string issuer_name;
669  std::string issuer_url;
670  };
671  typedef std::vector<ValidIssuer> Issuers;
672  */
673  Issuers issuers;
674  for (auto it: m_issuers) {
675  ValidIssuer issuer_info;
676  issuer_info.issuer_name = it.first;
677  issuer_info.issuer_url = it.second.m_url;
678  issuers.push_back(issuer_info);
679  }
680  return issuers;
681 
682  }
std::vector< ValidIssuer > Issuers

References XrdSciTokensHelper::ValidIssuer::issuer_name, and XrdSciTokensHelper::ValidIssuer::issuer_url.

◆ Test()

virtual int XrdAccSciTokens::Test ( const XrdAccPrivs  priv,
const Access_Operation  oper 
)
inlineoverridevirtual

Check whether the specified operation is permitted.

Parameters
priv-> the privileges as returned by Access().
oper-> The operation being attempted (see above)
Returns
Permit: a non-zero value (access is permitted) Deny: zero (access is denied)

Implements XrdAccAuthorize.

Definition at line 739 of file XrdSciTokensAccess.cc.

741  {
742  return (m_chain ? m_chain->Test(priv, oper) : 0);
743  }
virtual int Test(const XrdAccPrivs priv, const Access_Operation oper)=0

References XrdAccAuthorize::Test().

+ Here is the call graph for this function:

◆ Validate()

virtual bool XrdAccSciTokens::Validate ( const char *  token,
std::string &  emsg,
long long *  expT,
XrdSecEntity entP 
)
inlineoverridevirtual

Validate a scitoken.

Parameters
token- Pointer to the token to validate.
emsg- Reference to a string to hold the reason for rejection
expT- Pointer to where the expiry value is to be placed. If nill, the value is not returned.
entP- Pointer to the SecEntity object and when not nil requests that it be filled with any identifying information in the token. The caller assumes that all supplied fields may be released by calling free().
Returns
Return true if the token is valid; false otherwise with emsg set.

Implements XrdSciTokensHelper.

Definition at line 684 of file XrdSciTokensAccess.cc.

686  {
687  // Just check if the token is valid, no scope checking
688 
689  // Deserialize the token
690  SciToken scitoken;
691  char *err_msg;
692  if (!strncmp(token, "Bearer%20", 9)) token += 9;
693  pthread_rwlock_rdlock(&m_config_lock);
694  auto retval = scitoken_deserialize(token, &scitoken, &m_valid_issuers_array[0], &err_msg);
695  pthread_rwlock_unlock(&m_config_lock);
696  if (retval) {
697  // This originally looked like a JWT so log the failure.
698  m_log.Log(LogMask::Warning, "Validate", "Failed to deserialize SciToken:", err_msg);
699  emsg = err_msg;
700  free(err_msg);
701  return false;
702  }
703 
704  // If an entity was passed then we will fill it in with the subject
705  // name, should it exist. Note that we are gauranteed that all the
706  // settable entity fields are null so no need to worry setting them.
707  //
708  if (Entity)
709  {char *value = nullptr;
710  if (!scitoken_get_claim_string(scitoken, "sub", &value, &err_msg))
711  Entity->name = strdup(value);
712  }
713 
714  // Return the expiration time of this token if so wanted.
715  //
716  if (expT && scitoken_get_expiration(scitoken, expT, &err_msg)) {
717  emsg = err_msg;
718  free(err_msg);
719  return false;
720  }
721 
722 
723  // Delete the scitokens
724  scitoken_destroy(scitoken);
725 
726  // Deserialize checks the key, so we're good now.
727  return true;
728  }
int emsg(int rc, char *msg)

References emsg(), XrdSysError::Log(), XrdSecEntity::name, and Warning.

+ Here is the call graph for this function:

The documentation for this class was generated from the following file: