@@ -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
556556WebTokenEngine::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
580604void
581605WebTokenEngine::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 ());
0 commit comments