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 470 of file XrdSciTokensAccess.cc.

Constructor & Destructor Documentation

◆ XrdAccSciTokens()

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

Definition at line 481 of file XrdSciTokensAccess.cc.

481  :
482  m_chain(chain),
483  m_parms(parms ? parms : ""),
484  m_next_clean(monotonic_time() + m_expiry_secs),
485  m_log(lp, "scitokens_")
486  {
487  pthread_rwlock_init(&m_config_lock, nullptr);
488  m_config_lock_initialized = true;
489  m_log.Say("++++++ XrdAccSciTokens: Initialized SciTokens-based authorization.");
490  if (!Config(envP)) {
491  throw std::runtime_error("Failed to configure SciTokens authorization.");
492  }
493  }
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:109

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

+ Here is the call graph for this function:

◆ ~XrdAccSciTokens()

virtual XrdAccSciTokens::~XrdAccSciTokens ( )
inlinevirtual

Definition at line 495 of file XrdSciTokensAccess.cc.

495  {
496  if (m_config_lock_initialized) {
497  pthread_rwlock_destroy(&m_config_lock);
498  }
499  }

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 501 of file XrdSciTokensAccess.cc.

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

References XrdSecEntityAttr::Add(), XrdSecEntity::addrInfo, XrdSecEntity::creds, XrdSecEntity::credslen, 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, TPC::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 742 of file XrdSciTokensAccess.cc.

747  {
748  return 0;
749  }

◆ GetConfigFile()

std::string XrdAccSciTokens::GetConfigFile ( )
inline

Definition at line 757 of file XrdSciTokensAccess.cc.

757  {
758  return m_cfg_file;
759  }

◆ IssuerList()

virtual Issuers XrdAccSciTokens::IssuerList ( )
inlineoverridevirtual

Implements XrdSciTokensHelper.

Definition at line 666 of file XrdSciTokensAccess.cc.

667  {
668  /*
669  Convert the m_issuers into the data structure:
670  struct ValidIssuer
671  {std::string issuer_name;
672  std::string issuer_url;
673  };
674  typedef std::vector<ValidIssuer> Issuers;
675  */
676  Issuers issuers;
677  pthread_rwlock_rdlock(&m_config_lock);
678  try {
679  for (const auto &it: m_issuers) {
680  issuers.push_back({it.first, it.second.m_url});
681  }
682  } catch (...) {
683  pthread_rwlock_unlock(&m_config_lock);
684  throw;
685  }
686  pthread_rwlock_unlock(&m_config_lock);
687  return issuers;
688 
689  }
std::vector< ValidIssuer > Issuers

◆ 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 751 of file XrdSciTokensAccess.cc.

753  {
754  return (m_chain ? m_chain->Test(priv, oper) : 0);
755  }
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 691 of file XrdSciTokensAccess.cc.

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

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

+ Here is the call graph for this function:

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