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

Constructor & Destructor Documentation

◆ XrdAccSciTokens()

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

Definition at line 469 of file XrdSciTokensAccess.cc.

469  :
470  m_chain(chain),
471  m_parms(parms ? parms : ""),
472  m_next_clean(monotonic_time() + m_expiry_secs),
473  m_log(lp, "scitokens_")
474  {
475  pthread_rwlock_init(&m_config_lock, nullptr);
476  m_config_lock_initialized = true;
477  m_log.Say("++++++ XrdAccSciTokens: Initialized SciTokens-based authorization.");
478  if (!Config(envP)) {
479  throw std::runtime_error("Failed to configure SciTokens authorization.");
480  }
481  }
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 483 of file XrdSciTokensAccess.cc.

483  {
484  if (m_config_lock_initialized) {
485  pthread_rwlock_destroy(&m_config_lock);
486  }
487  }

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

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

726  {
727  return 0;
728  }

◆ GetConfigFile()

std::string XrdAccSciTokens::GetConfigFile ( )
inline

Definition at line 736 of file XrdSciTokensAccess.cc.

736  {
737  return m_cfg_file;
738  }

◆ IssuerList()

virtual Issuers XrdAccSciTokens::IssuerList ( )
inlineoverridevirtual

Implements XrdSciTokensHelper.

Definition at line 654 of file XrdSciTokensAccess.cc.

655  {
656  /*
657  Convert the m_issuers into the data structure:
658  struct ValidIssuer
659  {std::string issuer_name;
660  std::string issuer_url;
661  };
662  typedef std::vector<ValidIssuer> Issuers;
663  */
664  Issuers issuers;
665  for (auto it: m_issuers) {
666  ValidIssuer issuer_info;
667  issuer_info.issuer_name = it.first;
668  issuer_info.issuer_url = it.second.m_url;
669  issuers.push_back(issuer_info);
670  }
671  return issuers;
672 
673  }
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 730 of file XrdSciTokensAccess.cc.

732  {
733  return (m_chain ? m_chain->Test(priv, oper) : 0);
734  }
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 675 of file XrdSciTokensAccess.cc.

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