From 25a729fe6f5b2e7d1436eac972949439754a00a3 Mon Sep 17 00:00:00 2001 From: Brian Bockelman Date: Thu, 21 Mar 2019 22:39:08 -0500 Subject: [PATCH] Allow admin to customize default macaroons authz. With this, the admin can set a new `macaroons.onmissing` configuration flag to allow the authorization plugin to always allow in the case when no macaroon exists. This is useful for EOS as EOS does not utilize the XRootD authorization system and will apply its own authorizations downstream of this plugin. --- src/XrdMacaroons/XrdMacaroonsAuthz.cc | 59 +++++++++++++++++------ src/XrdMacaroons/XrdMacaroonsAuthz.hh | 6 +++ src/XrdMacaroons/XrdMacaroonsConfigure.cc | 27 ++++++++++- src/XrdMacaroons/XrdMacaroonsHandler.hh | 12 ++++- 4 files changed, 86 insertions(+), 18 deletions(-) diff --git a/src/XrdMacaroons/XrdMacaroonsAuthz.cc b/src/XrdMacaroons/XrdMacaroonsAuthz.cc index 44625939417..c0d474ffa21 100644 --- a/src/XrdMacaroons/XrdMacaroonsAuthz.cc +++ b/src/XrdMacaroons/XrdMacaroonsAuthz.cc @@ -108,15 +108,34 @@ static XrdAccPrivs AddPriv(Access_Operation op, XrdAccPrivs privs) Authz::Authz(XrdSysLogger *log, char const *config, XrdAccAuthorize *chain) : m_max_duration(86400), m_chain(chain), - m_log(log, "macarons_") + m_log(log, "macarons_"), + m_authz_behavior(static_cast(Handler::AuthzBehavior::PASSTHROUGH)) { - if (!Handler::Config(config, nullptr, &m_log, m_location, m_secret, m_max_duration)) + Handler::AuthzBehavior behavior; + if (!Handler::Config(config, nullptr, &m_log, m_location, m_secret, m_max_duration, behavior)) { throw std::runtime_error("Macaroon authorization config failed."); } + m_authz_behavior = static_cast(behavior); } +XrdAccPrivs +Authz::OnMissing(const XrdSecEntity *Entity, const char *path, + const Access_Operation oper, XrdOucEnv *env) +{ + switch (m_authz_behavior) { + case Handler::AuthzBehavior::PASSTHROUGH: + return m_chain ? m_chain->Access(Entity, path, oper, env) : XrdAccPriv_None; + case Handler::AuthzBehavior::ALLOW: + return AddPriv(oper, XrdAccPriv_None);; + case Handler::AuthzBehavior::DENY: + return XrdAccPriv_None; + } + // Code should be unreachable. + return XrdAccPriv_None; +} + XrdAccPrivs Authz::Access(const XrdSecEntity *Entity, const char *path, const Access_Operation oper, XrdOucEnv *env) @@ -124,13 +143,28 @@ Authz::Access(const XrdSecEntity *Entity, const char *path, const char *authz = env ? env->Get("authz") : nullptr; // We don't allow any testing to occur in this authz module, preventing // a macaroon to be used to receive further macaroons. - if (!authz || strncmp(authz, "Bearer%20", 9) || oper == AOP_Any) + if (oper == AOP_Any) { - //m_log.Emsg("Access", "No bearer token present"); return m_chain ? m_chain->Access(Entity, path, oper, env) : XrdAccPriv_None; } + if (!authz || strncmp(authz, "Bearer%20", 9)) + { + //m_log.Emsg("Access", "No bearer token present"); + return OnMissing(Entity, path, oper, env); + } authz += 9; + macaroon_returncode mac_err = MACAROON_SUCCESS; + struct macaroon* macaroon = macaroon_deserialize( + authz, + &mac_err); + if (!macaroon) + { + // Do not log - might be other token type! + //m_log.Emsg("Access", "Failed to parse the macaroon"); + return OnMissing(Entity, path, oper, env); + } + struct macaroon_verifier *verifier = macaroon_verifier_create(); if (!verifier) { @@ -140,12 +174,12 @@ Authz::Access(const XrdSecEntity *Entity, const char *path, if (!path) { m_log.Emsg("Access", "Request with no provided path."); + macaroon_verifier_destroy(verifier); return XrdAccPriv_None; } AuthzCheck check_helper(path, oper, m_max_duration, m_log); - macaroon_returncode mac_err = MACAROON_SUCCESS; if (macaroon_verifier_satisfy_general(verifier, AuthzCheck::verify_before_s, &check_helper, &mac_err) || macaroon_verifier_satisfy_general(verifier, AuthzCheck::verify_activity_s, &check_helper, &mac_err) || macaroon_verifier_satisfy_general(verifier, AuthzCheck::verify_name_s, &check_helper, &mac_err) || @@ -156,16 +190,6 @@ Authz::Access(const XrdSecEntity *Entity, const char *path, return XrdAccPriv_None; } - struct macaroon* macaroon = macaroon_deserialize( - authz, - &mac_err); - if (!macaroon) - { - m_log.Emsg("Access", "Failed to parse the macaroon"); - macaroon_verifier_destroy(verifier); - return m_chain ? m_chain->Access(Entity, path, oper, env) : XrdAccPriv_None; - } - const unsigned char *macaroon_loc; size_t location_sz; macaroon_location(macaroon, &macaroon_loc, &location_sz); @@ -173,6 +197,7 @@ Authz::Access(const XrdSecEntity *Entity, const char *path, { m_log.Emsg("Access", "Macaroon is for incorrect location", reinterpret_cast(macaroon_loc)); macaroon_verifier_destroy(verifier); + macaroon_destroy(macaroon); return m_chain ? m_chain->Access(Entity, path, oper, env) : XrdAccPriv_None; } @@ -187,11 +212,15 @@ Authz::Access(const XrdSecEntity *Entity, const char *path, macaroon_destroy(macaroon); return m_chain ? m_chain->Access(Entity, path, oper, env) : XrdAccPriv_None; } + macaroon_verifier_destroy(verifier); + const unsigned char *macaroon_id; size_t id_sz; macaroon_identifier(macaroon, &macaroon_id, &id_sz); + std::string macaroon_id_str(reinterpret_cast(macaroon_id), id_sz); m_log.Log(LogMask::Info, "Access", "Macaroon verification successful; ID", macaroon_id_str.c_str()); + macaroon_destroy(macaroon); // Copy the name, if present into the macaroon, into the credential object. if (Entity && check_helper.GetSecName().size()) { diff --git a/src/XrdMacaroons/XrdMacaroonsAuthz.hh b/src/XrdMacaroons/XrdMacaroonsAuthz.hh index 7081f8d8912..4a9ac3d251d 100644 --- a/src/XrdMacaroons/XrdMacaroonsAuthz.hh +++ b/src/XrdMacaroons/XrdMacaroonsAuthz.hh @@ -34,11 +34,17 @@ public: } private: + XrdAccPrivs OnMissing(const XrdSecEntity *Entity, + const char *path, + const Access_Operation oper, + XrdOucEnv *env); + ssize_t m_max_duration; XrdAccAuthorize *m_chain; XrdSysError m_log; std::string m_secret; std::string m_location; + int m_authz_behavior; }; } diff --git a/src/XrdMacaroons/XrdMacaroonsConfigure.cc b/src/XrdMacaroons/XrdMacaroonsConfigure.cc index fccf8d25f9c..746140412e6 100644 --- a/src/XrdMacaroons/XrdMacaroonsConfigure.cc +++ b/src/XrdMacaroons/XrdMacaroonsConfigure.cc @@ -11,8 +11,32 @@ using namespace Macaroons; + +static bool xonmissing(XrdOucStream &config_obj, XrdSysError *log, Handler::AuthzBehavior &behavior) +{ + char *val = config_obj.GetWord(); + if (!val || !val[0]) + { + log->Emsg("Config", "macaroons.onmissing requires a value (valid values: passthrough [default], allow, deny)"); + return false; + } + if (!strcasecmp(val, "passthrough")) { + behavior = Handler::AuthzBehavior::PASSTHROUGH; + } else if (!strcasecmp(val, "allow")) { + behavior = Handler::AuthzBehavior::ALLOW; + } else if (!strcasecmp(val, "deny")) { + behavior = Handler::AuthzBehavior::DENY; + } else + { + log->Emsg("Config", "macaroons.onmissing is invalid (valid values: passthrough [default], allow, deny)! Provided value:", val); + return false; + } + return true; +} + bool Handler::Config(const char *config, XrdOucEnv *env, XrdSysError *log, - std::string &location, std::string &secret, ssize_t &max_duration) + std::string &location, std::string &secret, ssize_t &max_duration, + AuthzBehavior &behavior) { XrdOucStream config_obj(log, getenv("XRDINSTANCE"), env, "=====> "); @@ -47,6 +71,7 @@ bool Handler::Config(const char *config, XrdOucEnv *env, XrdSysError *log, else if (!strcmp("sitename", var)) {success = xsitename(config_obj, log, location);} else if (!strcmp("trace", var)) {success = xtrace(config_obj, log);} else if (!strcmp("maxduration", var)) {success = xmaxduration(config_obj, log, max_duration);} + else if (!strcmp("onmissing", var)) {success = xonmissing(config_obj, log, behavior);} else { log->Say("Config warning: ignoring unknown directive '", orig_var, "'."); config_obj.Echo(); diff --git a/src/XrdMacaroons/XrdMacaroonsHandler.hh b/src/XrdMacaroons/XrdMacaroonsHandler.hh index d75a0a260ce..6a2c95ae87d 100644 --- a/src/XrdMacaroons/XrdMacaroonsHandler.hh +++ b/src/XrdMacaroons/XrdMacaroonsHandler.hh @@ -29,12 +29,19 @@ public: m_chain(chain), m_log(log) { - if (!Config(config, myEnv, m_log, m_location, m_secret, m_max_duration)) + AuthzBehavior behavior; + if (!Config(config, myEnv, m_log, m_location, m_secret, m_max_duration, behavior)) { throw std::runtime_error("Macaroon handler config failed."); } } + enum AuthzBehavior { + PASSTHROUGH, + ALLOW, + DENY + }; + virtual ~Handler(); virtual bool MatchesPath(const char *verb, const char *path) override; @@ -45,7 +52,8 @@ public: // Static configuration method; made static to allow Authz object to reuse // this code. static bool Config(const char *config, XrdOucEnv *env, XrdSysError *log, - std::string &location, std::string &secret, ssize_t &max_duration); + std::string &location, std::string &secret, ssize_t &max_duration, + AuthzBehavior &behavior); private: std::string GenerateID(const std::string &, const XrdSecEntity &, const std::string &, const std::vector &, const std::string &);