Skip to content

Commit b5e0142

Browse files
rgw: check all JWKS for STS
Signed-off-by: Alex Wojno <[email protected]>
1 parent efde751 commit b5e0142

File tree

2 files changed

+60
-55
lines changed

2 files changed

+60
-55
lines changed

src/rgw/rgw_rest_sts.cc

Lines changed: 57 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ WebTokenEngine::connect_to_host_get_cert_chain(const DoutPrefixProvider* dpp, co
552552
return chain_pem;
553553
}
554554

555-
void
555+
bool
556556
WebTokenEngine::validate_signature_using_n_e(const DoutPrefixProvider* dpp, const jwt::decoded_jwt& decoded, const std::string &algorithm, const std::string& n, const std::string& e) const
557557
{
558558
try {
@@ -571,18 +571,43 @@ WebTokenEngine::validate_signature_using_n_e(const DoutPrefixProvider* dpp, cons
571571
}
572572
} catch (const std::exception& e) {
573573
ldpp_dout(dpp, 10) << std::string("Signature validation using n, e failed: ") + e.what() << dendl;
574-
throw std::system_error(EACCES, std::system_category(), std::string("Signature validation using n, e failed: ") + e.what());
574+
return false;
575575
}
576576
ldpp_dout(dpp, 10) << "Verified signature using n and e"<< dendl;
577-
return;
577+
return true;
578+
}
579+
580+
bool WebTokenEngine::validate_cert_url(const DoutPrefixProvider* dpp, const std::string& cert_url,
581+
const std::vector<std::string>& thumbprints) const
582+
{
583+
// Fetch and verify cert according to https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html
584+
const auto hostname = get_top_level_domain_from_host(dpp, cert_url);
585+
ldpp_dout(dpp, 20) << "Validating hostname: " << hostname << dendl;
586+
const auto cert_chain = connect_to_host_get_cert_chain(dpp, hostname, 443);
587+
std::string cert;
588+
try {
589+
cert = extract_last_certificate(dpp, cert_chain);
590+
ldpp_dout(dpp, 20) << "last cert: " << cert << dendl;
591+
} catch(const std::exception& e) {
592+
ldpp_dout(dpp, 20) << "Extracting last cert of jwks uri failed with: " << e.what() << dendl;
593+
return false;
594+
}
595+
596+
if (!is_cert_valid(thumbprints, cert)) {
597+
ldpp_dout(dpp, 20) << "Cert doesn't match that with the thumbprints registered with oidc provider: " << cert.c_str() << dendl;
598+
return false;
599+
}
600+
601+
return true;
578602
}
579603

580604
void
581605
WebTokenEngine::validate_signature(const DoutPrefixProvider* dpp, const jwt::decoded_jwt& decoded, const string& algorithm, const string& iss, const vector<string>& thumbprints, optional_yield y) const
582606
{
583607
if (algorithm != "HS256" && algorithm != "HS384" && algorithm != "HS512") {
584-
string cert_url = get_cert_url(iss, dpp, y);
585-
if (cert_url.empty()) {
608+
const auto cert_url = get_cert_url(iss, dpp, y);
609+
if (cert_url.empty() || !validate_cert_url(dpp, cert_url, thumbprints)) {
610+
ldpp_dout(dpp, 5) << "Not able to validate JWKS url with registered thumbprints" << dendl;
586611
throw std::system_error(EINVAL, std::system_category());
587612
}
588613

@@ -609,20 +634,16 @@ WebTokenEngine::validate_signature(const DoutPrefixProvider* dpp, const jwt::dec
609634
for (auto &key : keys) {
610635
JSONParser k_parser;
611636
vector<string> x5c;
612-
std::string use;
613-
bool skip{false};
637+
std::string use, kid;
614638
if (k_parser.parse(key.c_str(), key.size())) {
615-
if (JSONDecoder::decode_json("use", use, &k_parser)) {
616-
if (use == "enc") { //if key is for encryption then don't use x5c or n and e for signature validation
617-
skip = true;
618-
} else if (use == "sig") {
619-
skip = false;
620-
}
639+
if (JSONDecoder::decode_json("kid", kid, &k_parser)) {
640+
ldpp_dout(dpp, 20) << "Checking key id: " << kid << dendl;
621641
}
622-
if (JSONDecoder::decode_json("x5c", x5c, &k_parser)) {
623-
if (skip == true) {
642+
if (JSONDecoder::decode_json("use", use, &k_parser) && use != "sig") {
624643
continue;
625-
}
644+
}
645+
646+
if (JSONDecoder::decode_json("x5c", x5c, &k_parser)) {
626647
string cert;
627648
bool found_valid_cert = false;
628649
for (auto& it : x5c) {
@@ -633,9 +654,9 @@ WebTokenEngine::validate_signature(const DoutPrefixProvider* dpp, const jwt::dec
633654
break;
634655
}
635656
}
636-
if (! found_valid_cert) {
637-
ldpp_dout(dpp, 0) << "Cert doesn't match that with the thumbprints registered with oidc provider: " << cert.c_str() << dendl;
638-
throw std::system_error(EINVAL, std::system_category());
657+
if (!found_valid_cert) {
658+
ldpp_dout(dpp, 10) << "Cert doesn't match that with the thumbprints registered with oidc provider: " << cert.c_str() << dendl;
659+
continue;
639660
}
640661
try {
641662
//verify method takes care of expired tokens also
@@ -694,53 +715,35 @@ WebTokenEngine::validate_signature(const DoutPrefixProvider* dpp, const jwt::dec
694715
verifier.verify(decoded);
695716
return;
696717
} else {
697-
ldpp_dout(dpp, 0) << "Unsupported algorithm: " << algorithm << dendl;
698-
throw std::system_error(EINVAL, std::system_category());
718+
ldpp_dout(dpp, 5) << "Unsupported algorithm: " << algorithm << dendl;
699719
}
700720
}
701721
catch (const std::exception& e) {
702-
ldpp_dout(dpp, 0) << "Signature validation using x5c failed" << e.what() << dendl;
703-
throw std::system_error(EACCES, std::system_category());
722+
ldpp_dout(dpp, 10) << "Signature validation using x5c failed" << e.what() << dendl;
704723
}
705724
} else {
725+
// Try bare key validation
726+
ldpp_dout(dpp, 20) << "Trying bare key validation" << dendl;
727+
std::string kty;
728+
if (JSONDecoder::decode_json("kty", kty, &k_parser) && kty != "RSA") {
729+
ldpp_dout(dpp, 10) << "Only RSA bare key validation is currently supported" << dendl;
730+
continue;
731+
}
732+
706733
if (algorithm == "RS256" || algorithm == "RS384" || algorithm == "RS512") {
707-
string n, e; //modulus and exponent
734+
std::string n, e; //modulus and exponent
708735
if (JSONDecoder::decode_json("n", n, &k_parser) && JSONDecoder::decode_json("e", e, &k_parser)) {
709-
if (skip == true) {
710-
continue;
711-
}
712-
//Fetch and verify cert according to https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html
713-
//and the same must be installed as part of create oidc provider in rgw
714-
//this can be made common to all types of keys(x5c, n&e), making thumbprint validation similar to
715-
//AWS
716-
std::string hostname = get_top_level_domain_from_host(dpp, cert_url);
717-
//connect to host and get back cert chain from it
718-
std::string cert_chain = connect_to_host_get_cert_chain(dpp, hostname, 443);
719-
std::string cert;
720-
try {
721-
cert = extract_last_certificate(dpp, cert_chain);
722-
ldpp_dout(dpp, 20) << "last cert: " << cert << dendl;
723-
} catch(const std::exception& e) {
724-
ldpp_dout(dpp, 20) << "Extracting last cert of jwks uri failed with: " << e.what() << dendl;
725-
throw std::system_error(EINVAL, std::system_category());
736+
if (validate_signature_using_n_e(dpp, decoded, algorithm, n, e)) {
737+
return;
726738
}
727-
if (!is_cert_valid(thumbprints, cert)) {
728-
ldpp_dout(dpp, 20) << "Cert doesn't match that with the thumbprints registered with oidc provider: " << cert.c_str() << dendl;
729-
throw std::system_error(EINVAL, std::system_category());
730-
}
731-
validate_signature_using_n_e(dpp, decoded, algorithm, n, e);
732-
return;
733739
}
734-
ldpp_dout(dpp, 0) << "x5c not present or n, e not present" << dendl;
735-
throw std::system_error(EINVAL, std::system_category());
736-
} else {
737-
throw std::system_error(EINVAL, std::system_category(), "Invalid algorithm: " + algorithm);
740+
ldpp_dout(dpp, 10) << "Bare key parameters are not present for key" << dendl;
738741
}
739742
}
740-
ldpp_dout(dpp, 0) << "Signature can not be validated with the input given in keys: "<< dendl;
741-
throw std::system_error(EINVAL, std::system_category());
742743
} //end k_parser.parse
743-
}//end for iterate through keys
744+
} //end for iterate through keys
745+
ldpp_dout(dpp, 0) << "Signature can not be validated with the JWKS present." << dendl;
746+
throw std::system_error(EINVAL, std::system_category());
744747
} else { //end val->is_array
745748
ldpp_dout(dpp, 0) << "keys not present in JSON" << dendl;
746749
throw std::system_error(EINVAL, std::system_category());

src/rgw/rgw_rest_sts.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class WebTokenEngine : public rgw::auth::Engine {
5353
std::tuple<boost::optional<WebTokenEngine::token_t>, boost::optional<WebTokenEngine::principal_tags_t>>
5454
get_from_jwt(const DoutPrefixProvider* dpp, const std::string& token, const req_state* const s, optional_yield y) const;
5555

56-
void validate_signature_using_n_e(const DoutPrefixProvider* dpp, const jwt::decoded_jwt& decoded, const std::string &algorithm, const std::string& n, const std::string& e) const;
56+
bool validate_signature_using_n_e(const DoutPrefixProvider* dpp, const jwt::decoded_jwt& decoded, const std::string &algorithm, const std::string& n, const std::string& e) const;
5757

5858
void validate_signature (const DoutPrefixProvider* dpp, const jwt::decoded_jwt& decoded, const std::string& algorithm, const std::string& iss, const std::vector<std::string>& thumbprints, optional_yield y) const;
5959

@@ -69,6 +69,8 @@ class WebTokenEngine : public rgw::auth::Engine {
6969
std::string connect_to_host_get_cert_chain(const DoutPrefixProvider* dpp, const std::string& hostname, int port = 443) const;
7070
std::string get_top_level_domain_from_host(const DoutPrefixProvider* dpp, const std::string& hostname) const;
7171
std::string extract_last_certificate(const DoutPrefixProvider* dpp, const std::string& pem_chain) const;
72+
bool validate_cert_url(const DoutPrefixProvider* dpp, const std::string& cert_url,
73+
const std::vector<std::string>& thumbprints) const;
7274
void shutdown_ssl(const DoutPrefixProvider* dpp, SSL* ssl, SSL_CTX* ctx) const;
7375

7476
public:

0 commit comments

Comments
 (0)