| | |
| | | * limitations under the License. |
| | | */ |
| | | |
| | | package org.keycloak.protocol.oidc.utils; |
| | | package org.keycloak.protocol.cas.utils; |
| | | |
| | | import java.net.URI; |
| | | import java.util.Arrays; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Locale; |
| | | import java.util.Set; |
| | | import java.util.TreeSet; |
| | | import java.util.regex.Pattern; |
| | | |
| | | import org.keycloak.common.util.KeycloakUriBuilder; |
| | | import org.keycloak.common.util.MultivaluedHashMap; |
| | | import org.keycloak.common.util.UriUtils; |
| | | import org.keycloak.models.ClientModel; |
| | | import org.keycloak.models.Constants; |
| | | import org.keycloak.models.KeycloakSession; |
| | | import org.keycloak.models.KeycloakUriInfo; |
| | | import org.keycloak.models.RealmModel; |
| | | import org.keycloak.protocol.cas.CASLoginProtocol; |
| | | import org.keycloak.services.Urls; |
| | | import org.keycloak.services.util.ResolveRelative; |
| | | |
| | |
| | | |
| | | public static final Set<String> LOOPBACK_INTERFACES = new HashSet<>(Arrays.asList("localhost", "127.0.0.1", "[::1]")); |
| | | |
| | | private static final Set<String> FORBIDDEN_OIDC_PARAMS = Set.of( |
| | | CASLoginProtocol.SAMLART_RESPONSE_PARAM.toLowerCase(Locale.ROOT), |
| | | CASLoginProtocol.TICKET_RESPONSE_PARAM.toLowerCase(Locale.ROOT) |
| | | ); |
| | | |
| | | private static final Logger logger = Logger.getLogger(RedirectUtils.class); |
| | | |
| | | public static String verifyRedirectUri(KeycloakSession session, String redirectUri, ClientModel client) { |
| | |
| | | return null; |
| | | } |
| | | |
| | | // Check for HTTP Parameter Pollution - forbidden OIDC response parameters in redirect URI |
| | | if (containsForbiddenOidcParameters(originalRedirect)){ |
| | | return null; |
| | | } |
| | | |
| | | // check if the passed URI allows wildcards |
| | | boolean allowWildcards = areWildcardsAllowed(originalRedirect); |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | private static boolean containsForbiddenOidcParameters(URI originalRedirect) { |
| | | String query = originalRedirect.getRawQuery(); |
| | | if (query != null && !query.isEmpty()) { |
| | | MultivaluedHashMap<String, String> params =UriUtils.decodeQueryString(query); |
| | | for (String paramName : params.keySet()) { |
| | | if (FORBIDDEN_OIDC_PARAMS.contains(paramName.toLowerCase(Locale.ROOT))) { |
| | | logger.warnf("Redirect URI rejected: contains forbidden OIDC parameter '%s' in query string: scheme=%s, host=%s, path=%s", |
| | | paramName, |
| | | originalRedirect.getScheme(), |
| | | originalRedirect.getHost(), |
| | | originalRedirect.getPath()); |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private static URI toUri(String redirectUri) { |
| | | URI uri = null; |
| | | if (redirectUri != null) { |
| | |
| | | } |
| | | return redirectUri; |
| | | } |
| | | |
| | | public static Set<String> resolveUrlsWithRedirects(KeycloakSession session, List<String> origUrls, |
| | | String rootUrl, List<String> redirectUris, boolean returnAsOrigins) { |
| | | |
| | | Set<String> refactoredUrls = (origUrls != null) ? new HashSet<>(origUrls) : new HashSet<>(); |
| | | if (refactoredUrls.contains(Constants.INCLUDE_REDIRECTS)) { |
| | | refactoredUrls.remove(Constants.INCLUDE_REDIRECTS); |
| | | |
| | | Set<String> redirectsToProcess = (redirectUris != null) ? new HashSet<>(redirectUris) : Collections.emptySet(); |
| | | for (String redirectUri : resolveValidRedirects(session, rootUrl, redirectsToProcess)) { |
| | | if (isValidScheme(redirectUri)) { |
| | | if (returnAsOrigins) { |
| | | refactoredUrls.add(UriUtils.getOrigin(redirectUri)); |
| | | } else { |
| | | refactoredUrls.add(redirectUri); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return refactoredUrls; |
| | | } |
| | | |
| | | private static boolean isValidScheme(String url) { |
| | | return url != null && (url.startsWith("http://") || url.startsWith("https://")); |
| | | } |
| | | } |