keycloak-uncached

Details

diff --git a/core/src/main/java/org/keycloak/util/UriUtils.java b/core/src/main/java/org/keycloak/util/UriUtils.java
index 873283f..8532c5b 100644
--- a/core/src/main/java/org/keycloak/util/UriUtils.java
+++ b/core/src/main/java/org/keycloak/util/UriUtils.java
@@ -1,12 +1,15 @@
 package org.keycloak.util;
 
 import java.net.URI;
+import java.util.regex.Pattern;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
 public class UriUtils {
 
+    private static final Pattern originPattern = Pattern.compile("(http://|https://)[\\w]+(\\.[\\w]+)*(:[\\d]{2,5})?");
+
     public static String getOrigin(URI uri) {
         return getOrigin(uri.toString());
     }
@@ -16,4 +19,8 @@ public class UriUtils {
         return u.substring(0, u.indexOf('/', 8));
     }
 
+    public static boolean isOrigin(String url) {
+        return originPattern.matcher(url).matches();
+    }
+
 }
diff --git a/core/src/test/java/org/keycloak/util/UriUtilsTest.java b/core/src/test/java/org/keycloak/util/UriUtilsTest.java
new file mode 100644
index 0000000..52d484d
--- /dev/null
+++ b/core/src/test/java/org/keycloak/util/UriUtilsTest.java
@@ -0,0 +1,44 @@
+package org.keycloak.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UriUtilsTest {
+
+    @Test
+    public void testOrigins() {
+        assertValid("http://test");
+        assertValid("http://test:8080");
+        assertValid("https://test");
+        assertValid("http://test.com");
+        assertValid("https://test.com");
+        assertValid("https://test.com:8080");
+        assertValid("http://sub.test.com");
+        assertValid("https://sub.test.com");
+        assertValid("https://sub.test.com:8080");
+        assertValid("http://192.168.123.123");
+        assertValid("https://192.168.123.123");
+        assertValid("https://192.168.123.123:8080");
+
+        assertInvalid("https://test/");
+        assertInvalid("{");
+        assertInvalid("https://{}");
+        assertInvalid("https://)");
+        assertInvalid("http://test:test");
+        assertInvalid("http://test:8080:8080");
+    }
+
+    public void assertValid(String origin) {
+        assertTrue(UriUtils.isOrigin(origin));
+    }
+
+    public void assertInvalid(String origin) {
+        assertFalse(UriUtils.isOrigin(origin));
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 4ceb9f3..7a479ef 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -81,6 +81,7 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.core.Variant;
+import java.lang.reflect.Method;
 import java.net.URI;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -96,6 +97,16 @@ public class AccountService {
 
     private static final Logger logger = Logger.getLogger(AccountService.class);
 
+    private static Set<String> VALID_PATHS = new HashSet<String>();
+    static {
+        for (Method m : AccountService.class.getMethods()) {
+            Path p = m.getAnnotation(Path.class);
+            if (p != null) {
+                VALID_PATHS.add(p.value());
+            }
+        }
+    }
+
     private static final EventType[] LOG_EVENTS = {EventType.LOGIN, EventType.LOGOUT, EventType.REGISTER, EventType.REMOVE_SOCIAL_LINK, EventType.REMOVE_TOTP, EventType.SEND_RESET_PASSWORD,
             EventType.SEND_VERIFY_EMAIL, EventType.SOCIAL_LINK, EventType.UPDATE_EMAIL, EventType.UPDATE_PASSWORD, EventType.UPDATE_PROFILE, EventType.UPDATE_TOTP, EventType.VERIFY_EMAIL};
 
@@ -706,6 +717,9 @@ public class AccountService {
                 logger.debug("error from oauth");
                 throw new ForbiddenException("error");
             }
+            if (path != null && !VALID_PATHS.contains(path)) {
+                throw new BadRequestException("Invalid path");
+            }
             if (!realm.isEnabled()) {
                 logger.debug("realm not enabled");
                 throw new ForbiddenException();
diff --git a/services/src/main/java/org/keycloak/services/resources/QRCodeResource.java b/services/src/main/java/org/keycloak/services/resources/QRCodeResource.java
index 6cc66ee..c846df2 100755
--- a/services/src/main/java/org/keycloak/services/resources/QRCodeResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/QRCodeResource.java
@@ -43,14 +43,22 @@ public class QRCodeResource {
 
         if (size != null) {
             String[] s = size.split("x");
-            width = Integer.parseInt(s[0]);
-            height = Integer.parseInt(s[1]);
+            try {
+                width = Integer.parseInt(s[0]);
+                height = Integer.parseInt(s[1]);
+            } catch (Throwable t) {
+                return Response.status(Response.Status.BAD_REQUEST).build();
+            }
         }
 
         if (contents == null) {
             return Response.status(Response.Status.BAD_REQUEST).build();
         }
 
+        if (width > 1000 || height > 1000 || contents.length() > 1000) {
+            return Response.status(Response.Status.BAD_REQUEST).build();
+        }
+
         QRCodeWriter writer = new QRCodeWriter();
         final BitMatrix bitMatrix = writer.encode(contents, BarcodeFormat.QR_CODE, width, height);
 
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index e618c9e..9bb5db3 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -19,6 +19,7 @@ import org.keycloak.services.managers.BruteForceProtector;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.TokenManager;
 import org.keycloak.util.StreamUtil;
+import org.keycloak.util.UriUtils;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
@@ -94,6 +95,10 @@ public class RealmsResource {
     public Response getLoginStatusIframe(final @PathParam("realm") String name,
                                        @QueryParam("client_id") String client_id,
                                        @QueryParam("origin") String origin) {
+        if (!UriUtils.isOrigin(origin)) {
+            throw new BadRequestException("Invalid origin");
+        }
+
         AuthenticationManager auth = new AuthenticationManager();
 
         RealmManager realmManager = new RealmManager(session);
diff --git a/services/src/main/java/org/keycloak/services/resources/SocialResource.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
index 572b74d..7e8122b 100755
--- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
@@ -31,6 +31,7 @@ import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventType;
 import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.crypto.RSAProvider;
 import org.keycloak.models.AccountRoles;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.Constants;
@@ -106,9 +107,11 @@ public class SocialResource {
     @GET
     @Path("callback")
     public Response callback(@QueryParam("state") String encodedState) throws URISyntaxException, IOException {
+        JWSInput jwsInput;
         State initialRequest;
         try {
-            initialRequest = new JWSInput(encodedState).readJsonContent(State.class);
+            jwsInput = new JWSInput(encodedState);
+            initialRequest = jwsInput.readJsonContent(State.class);
         } catch (Throwable t) {
             logger.error("Invalid social callback", t);
             return Flows.forms(session, null, null, uriInfo).setError("Unexpected callback").createErrorPage();
@@ -127,6 +130,11 @@ public class SocialResource {
                 .detail(Details.RESPONSE_TYPE, initialRequest.get(OAuth2Constants.RESPONSE_TYPE))
                 .detail(Details.AUTH_METHOD, authMethod);
 
+        if (!RSAProvider.verify(jwsInput, realm.getPublicKey())) {
+            logger.error("Invalid social callback");
+            return Flows.forms(session, null, null, uriInfo).setError("Unexpected callback").createErrorPage();
+        }
+
         AuthenticationManager authManager = new AuthenticationManager();
         OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);