@@ -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,48 @@ 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::verify_oidc_thumbprint (const DoutPrefixProvider* dpp, const std::string& cert_url,
581+ const std::vector<std::string>& thumbprints) const
582+ {
583+ if (!cct->_conf .get_val <bool >(" rgw_enable_jwks_url_verification" )) {
584+ ldpp_dout (dpp, 5 ) << " Verification of JWKS endpoint is turned off." << dendl;
585+ return true ;
586+ }
587+
588+ // Fetch and verify cert according to https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html
589+ const auto hostname = get_top_level_domain_from_host (dpp, cert_url);
590+ ldpp_dout (dpp, 20 ) << " Validating hostname: " << hostname << dendl;
591+ const auto cert_chain = connect_to_host_get_cert_chain (dpp, hostname, 443 );
592+ std::string cert;
593+ try {
594+ cert = extract_last_certificate (dpp, cert_chain);
595+ ldpp_dout (dpp, 20 ) << " last cert: " << cert << dendl;
596+ } catch (const std::exception& e) {
597+ ldpp_dout (dpp, 20 ) << " Extracting last cert of jwks uri failed with: " << e.what () << dendl;
598+ return false ;
599+ }
600+
601+ if (!is_cert_valid (thumbprints, cert)) {
602+ ldpp_dout (dpp, 20 ) << " Cert doesn't match that with the thumbprints registered with oidc provider: " << cert.c_str () << dendl;
603+ return false ;
604+ }
605+
606+ return true ;
578607}
579608
580609void
581610WebTokenEngine::validate_signature (const DoutPrefixProvider* dpp, const jwt::decoded_jwt& decoded, const string& algorithm, const string& iss, const vector<string>& thumbprints, optional_yield y) const
582611{
583612 if (algorithm != " HS256" && algorithm != " HS384" && algorithm != " HS512" ) {
584- string cert_url = get_cert_url (iss, dpp, y);
585- if (cert_url.empty ()) {
613+ const auto cert_url = get_cert_url (iss, dpp, y);
614+ if (cert_url.empty () || !verify_oidc_thumbprint (dpp, cert_url, thumbprints)) {
615+ ldpp_dout (dpp, 5 ) << " Not able to validate JWKS url with registered thumbprints" << dendl;
586616 throw std::system_error (EINVAL, std::system_category ());
587617 }
588618
@@ -609,33 +639,30 @@ WebTokenEngine::validate_signature(const DoutPrefixProvider* dpp, const jwt::dec
609639 for (auto &key : keys) {
610640 JSONParser k_parser;
611641 vector<string> x5c;
612- std::string use;
613- bool skip{false };
642+ std::string use, kid;
614643 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- }
644+ if (JSONDecoder::decode_json (" kid" , kid, &k_parser)) {
645+ ldpp_dout (dpp, 20 ) << " Checking key id: " << kid << dendl;
621646 }
622- if (JSONDecoder::decode_json (" x5c" , x5c, &k_parser)) {
623- if (skip == true ) {
647+ if (JSONDecoder::decode_json (" use" , use, &k_parser) && use != " sig" ) {
624648 continue ;
625- }
649+ }
650+
651+ if (JSONDecoder::decode_json (" x5c" , x5c, &k_parser)) {
626652 string cert;
627653 bool found_valid_cert = false ;
654+ bool skip_thumbprint_verification = cct->_conf .get_val <bool >(" rgw_enable_jwks_url_verification" );
628655 for (auto & it : x5c) {
629656 cert = " -----BEGIN CERTIFICATE-----\n " + it + " \n -----END CERTIFICATE-----" ;
630657 ldpp_dout (dpp, 20 ) << " Certificate is: " << cert.c_str () << dendl;
631- if (is_cert_valid (thumbprints, cert)) {
658+ if (skip_thumbprint_verification || is_cert_valid (thumbprints, cert)) {
632659 found_valid_cert = true ;
633660 break ;
634661 }
635662 }
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 ()) ;
663+ if (!found_valid_cert) {
664+ ldpp_dout (dpp, 10 ) << " Cert doesn't match that with the thumbprints registered with oidc provider: " << cert.c_str () << dendl;
665+ continue ;
639666 }
640667 try {
641668 // verify method takes care of expired tokens also
@@ -694,53 +721,35 @@ WebTokenEngine::validate_signature(const DoutPrefixProvider* dpp, const jwt::dec
694721 verifier.verify (decoded);
695722 return ;
696723 } else {
697- ldpp_dout (dpp, 0 ) << " Unsupported algorithm: " << algorithm << dendl;
698- throw std::system_error (EINVAL, std::system_category ());
724+ ldpp_dout (dpp, 5 ) << " Unsupported algorithm: " << algorithm << dendl;
699725 }
700726 }
701727 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 ());
728+ ldpp_dout (dpp, 10 ) << " Signature validation using x5c failed" << e.what () << dendl;
704729 }
705730 } else {
731+ // Try bare key validation
732+ ldpp_dout (dpp, 20 ) << " Trying bare key validation" << dendl;
733+ std::string kty;
734+ if (JSONDecoder::decode_json (" kty" , kty, &k_parser) && kty != " RSA" ) {
735+ ldpp_dout (dpp, 10 ) << " Only RSA bare key validation is currently supported" << dendl;
736+ continue ;
737+ }
738+
706739 if (algorithm == " RS256" || algorithm == " RS384" || algorithm == " RS512" ) {
707- string n, e; // modulus and exponent
740+ std:: string n, e; // modulus and exponent
708741 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 ());
742+ if (validate_signature_using_n_e (dpp, decoded, algorithm, n, e)) {
743+ return ;
726744 }
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 ;
733745 }
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);
746+ ldpp_dout (dpp, 10 ) << " Bare key parameters (n&e) are not present for key" << dendl;
738747 }
739748 }
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 ());
742749 } // end k_parser.parse
743- }// end for iterate through keys
750+ } // end for iterate through keys
751+ ldpp_dout (dpp, 0 ) << " Signature can not be validated with the JWKS present." << dendl;
752+ throw std::system_error (EINVAL, std::system_category ());
744753 } else { // end val->is_array
745754 ldpp_dout (dpp, 0 ) << " keys not present in JSON" << dendl;
746755 throw std::system_error (EINVAL, std::system_category ());
0 commit comments