XRootD
Loading...
Searching...
No Matches
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.
 
virtual ~XrdAccAuthorize ()
 Destructor.
 
- Public Member Functions inherited from XrdSciTokensHelper
 XrdSciTokensHelper ()
 Constructor and Destructor.
 
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 437 of file XrdSciTokensAccess.cc.

Constructor & Destructor Documentation

◆ XrdAccSciTokens()

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

Definition at line 448 of file XrdSciTokensAccess.cc.

448 :
449 m_chain(chain),
450 m_parms(parms ? parms : ""),
451 m_next_clean(monotonic_time() + m_expiry_secs),
452 m_log(lp, "scitokens_")
453 {
454 pthread_rwlock_init(&m_config_lock, nullptr);
455 m_config_lock_initialized = true;
456 m_log.Say("++++++ XrdAccSciTokens: Initialized SciTokens-based authorization.");
457 if (!Config(envP)) {
458 throw std::runtime_error("Failed to configure SciTokens authorization.");
459 }
460 }

References XrdAccAuthorize::XrdAccAuthorize().

+ Here is the call graph for this function:

◆ ~XrdAccSciTokens()

virtual XrdAccSciTokens::~XrdAccSciTokens ( )
inlinevirtual

Definition at line 462 of file XrdSciTokensAccess.cc.

462 {
463 if (m_config_lock_initialized) {
464 pthread_rwlock_destroy(&m_config_lock);
465 }
466 }

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

472 {
473 std::vector<std::string_view> authz_list;
474 authz_list.reserve(1);
475
476 // Parse the authz environment entry as a comma-separated list of tokens.
477 // Traditionally, `authz` has been used as the parameter for XRootD; however,
478 // RFC 6750 Section 2.3 ("URI Query Parameter") specifies that access_token
479 // is correct. We support both.
480 ParseTokenString("authz", env, authz_list);
481 ParseTokenString("access_token", env, authz_list);
482
483 if (Entity && !strcmp("ztn", Entity->prot) && Entity->creds &&
484 Entity->credslen && Entity->creds[Entity->credslen] == '\0')
485 {
486 authz_list.push_back(Entity->creds);
487 }
488
489 if (authz_list.empty()) {
490 return OnMissing(Entity, path, oper, env);
491 }
492
493 // A potential DoS would be providing a large number of tokens to consider for ACLs.
494 // Have a hardcoded assumption of <10 tokens per request.
495 if (authz_list.size() > 10) {
496 m_log.Log(LogMask::Warning, "Access", "Request had more than 10 tokens attached; ignoring");
497 return OnMissing(Entity, path, oper, env);
498 }
499
500 m_log.Log(LogMask::Debug, "Access", "Trying token-based access control");
501 std::vector<std::shared_ptr<XrdAccRules>> access_rules_list;
502 uint64_t now = monotonic_time();
503 Check(now);
504 for (const auto &authz : authz_list) {
505 std::shared_ptr<XrdAccRules> access_rules;
506 {
507 std::lock_guard<std::mutex> guard(m_mutex);
508 const auto iter = m_map.find(authz);
509 if (iter != m_map.end() && !iter->second->expired()) {
510 access_rules = iter->second;
511 }
512 }
513 if (!access_rules) {
514 m_log.Log(LogMask::Debug, "Access", "Token not found in recent cache; parsing.");
515 try {
516 uint64_t cache_expiry;
517 AccessRulesRaw rules;
518 std::string username;
519 std::string token_subject;
520 std::string issuer;
521 std::vector<MapRule> map_rules;
522 std::vector<std::string> groups;
523 uint32_t authz_strategy;
524 AuthzSetting acceptable_authz;
525 if (GenerateAcls(authz, cache_expiry, rules, username, token_subject, issuer, map_rules, groups, authz_strategy, acceptable_authz)) {
526 access_rules.reset(new XrdAccRules(now + cache_expiry, username, token_subject, issuer, map_rules, groups, authz_strategy, acceptable_authz));
527 access_rules->parse(rules);
528 } else {
529 m_log.Log(LogMask::Warning, "Access", "Failed to generate ACLs for token");
530 continue;
531 }
532 if (m_log.getMsgMask() & LogMask::Debug) {
533 m_log.Log(LogMask::Debug, "Access", "New valid token", access_rules->str().c_str());
534 }
535 } catch (std::exception &exc) {
536 m_log.Log(LogMask::Warning, "Access", "Error generating ACLs for authorization", exc.what());
537 continue;
538 }
539 std::lock_guard<std::mutex> guard(m_mutex);
540 m_map[std::string(authz)] = access_rules;
541 } else if (m_log.getMsgMask() & LogMask::Debug) {
542 m_log.Log(LogMask::Debug, "Access", "Cached token", access_rules->str().c_str());
543 }
544 access_rules_list.push_back(access_rules);
545 }
546 if (access_rules_list.empty()) {
547 return OnMissing(Entity, path, oper, env);
548 }
549 std::string_view path_view(path, strlen(path));
550
551 // Apply the logic for the required issuers.
552 if (!AuthorizesRequiredIssuers(oper, path_view, m_required_issuers, access_rules_list)) {
553 return OnMissing(Entity, path, oper, env);
554 }
555
556 // Strategy: assuming the corresponding strategy is enabled, we populate the name in
557 // the XrdSecEntity if:
558 // 1. There are scopes present in the token that authorize the request,
559 // 2. The token is mapped by some rule in the mapfile (group or subject-based mapping).
560 // The default username for the issuer is only used in (1).
561 // If the scope-based mapping is successful, authorize immediately. Otherwise, if the
562 // mapping is successful, we potentially chain to another plugin.
563 //
564 // We always populate the issuer and the groups, if present.
565
566 // Access may be authorized; populate XrdSecEntity
567 for (const auto &access_rules : access_rules_list) {
568 // Make sure this issuer is acceptable for the given operation.
569 if (!access_rules->acceptable_authz(oper)) {
570 m_log.Log(LogMask::Debug, "Access", "Issuer is not acceptable for given operation:", access_rules->get_issuer().c_str());
571 continue;
572 }
573
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_view);
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_view);
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 return returned_op;
660 }
661
662 // We iterated through all available credentials and none provided authorization; fall back
663 return OnMissing(Entity, path, oper, env);
664 }
XrdAccPrivs
@ XrdAccPriv_None
bool AuthorizesRequiredIssuers(Access_Operation client_oper, const std::string_view &path, const std::vector< std::pair< std::unique_ptr< SubpathMatch >, std::string > > &required_issuers, const std::vector< std::shared_ptr< XrdAccRules > > &access_rules_list)
std::vector< std::pair< Access_Operation, std::string > > AccessRulesRaw
@ Capability
if(ec< 0) ec
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)
int credslen
Length of the 'creds' data.
XrdNetAddrInfo * addrInfo
Entity's connection details.
XrdSecEntityAttr * eaAPI
non-const API to attributes
char prot[XrdSecPROTOIDSIZE]
Auth protocol used (e.g. krb5)
char * creds
Raw entity credentials or cert.
XrdSecMonitor * secMon
If !0 security monitoring enabled.
char * grps
Entity's group name(s)
char * role
Entity's role(s)

References XrdSecEntityAttr::Add(), XrdSecEntity::addrInfo, AuthorizesRequiredIssuers(), Capability, XrdSecEntity::creds, XrdSecEntity::credslen, XrdSecEntity::eaAPI, Group, XrdSecEntity::grps, Mapping, XrdSciTokensMon::Mon_isIO(), XrdSciTokensMon::Mon_Report(), XrdSecEntity::prot, XrdSecEntity::role, XrdSecEntity::secMon, XrdSecEntity::vorg, 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 733 of file XrdSciTokensAccess.cc.

738 {
739 return 0;
740 }

◆ GetConfigFile()

std::string XrdAccSciTokens::GetConfigFile ( )
inline

Definition at line 748 of file XrdSciTokensAccess.cc.

748 {
749 return m_cfg_file;
750 }

◆ 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 for (auto it: m_issuers) {
678 ValidIssuer issuer_info;
679 issuer_info.issuer_name = it.first;
680 issuer_info.issuer_url = it.second.m_url;
681 issuers.push_back(issuer_info);
682 }
683 return issuers;
684
685 }
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 742 of file XrdSciTokensAccess.cc.

744 {
745 return (m_chain ? m_chain->Test(priv, oper) : 0);
746 }

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

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

References emsg(), and XrdSecEntity::name.

+ Here is the call graph for this function:

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