Commit e38ffa37 authored by kelvinp@chromium.org's avatar kelvinp@chromium.org

Verify the host-supplied URL matches the domain's allowed URL patterns.

1. Modify HostInfo.java to parse the tokenPatternUrl from the host list.
2. Modify ThirdPartyTokenFetcher to check against the domain's allowed URL before directing the user to the login page requested by the host.

Review URL: https://codereview.chromium.org/348433002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278337 0039d316-1c4b-4281-b951-d872f2087c98
parent 2d4801ef
...@@ -281,9 +281,12 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe ...@@ -281,9 +281,12 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
@Override @Override
public void onCancel(DialogInterface dialog) { public void onCancel(DialogInterface dialog) {
JniInterface.disconnectFromHost(); JniInterface.disconnectFromHost();
mTokenFetcher = null;
} }
}); });
SessionConnector connector = new SessionConnector(this, this, mHostListLoader); SessionConnector connector = new SessionConnector(this, this, mHostListLoader);
assert mTokenFetcher == null;
mTokenFetcher = createTokenFetcher(host);
connector.connectToHost(mAccount.name, mToken, host); connector.connectToHost(mAccount.name, mToken, host);
} }
...@@ -459,9 +462,7 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe ...@@ -459,9 +462,7 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
} }
} }
public void fetchThirdPartyToken(String tokenUrl, String clientId, String scope) { private ThirdPartyTokenFetcher createTokenFetcher(HostInfo host) {
assert mTokenFetcher == null;
ThirdPartyTokenFetcher.Callback callback = new ThirdPartyTokenFetcher.Callback() { ThirdPartyTokenFetcher.Callback callback = new ThirdPartyTokenFetcher.Callback() {
public void onTokenFetched(String code, String accessToken) { public void onTokenFetched(String code, String accessToken) {
// The native client sends the OAuth authorization code to the host as the token so // The native client sends the OAuth authorization code to the host as the token so
...@@ -476,10 +477,11 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe ...@@ -476,10 +477,11 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
JniInterface.nativeOnThirdPartyTokenFetched(token, sharedSecret); JniInterface.nativeOnThirdPartyTokenFetched(token, sharedSecret);
} }
}; };
return new ThirdPartyTokenFetcher(this, host.getTokenUrlPatterns(), callback);
mTokenFetcher = new ThirdPartyTokenFetcher(this, tokenUrl, clientId, scope, callback);
mTokenFetcher.fetchToken();
} }
public void fetchThirdPartyToken(String tokenUrl, String clientId, String scope) {
assert mTokenFetcher != null;
mTokenFetcher.fetchToken(tokenUrl, clientId, scope);
}
} }
...@@ -4,6 +4,12 @@ ...@@ -4,6 +4,12 @@
package org.chromium.chromoting; package org.chromium.chromoting;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
/** Class to represent a Host returned by {@link HostListLoader}. */ /** Class to represent a Host returned by {@link HostListLoader}. */
public class HostInfo { public class HostInfo {
public final String name; public final String name;
...@@ -11,12 +17,46 @@ public class HostInfo { ...@@ -11,12 +17,46 @@ public class HostInfo {
public final String jabberId; public final String jabberId;
public final String publicKey; public final String publicKey;
public final boolean isOnline; public final boolean isOnline;
private final ArrayList<String> mTokenUrlPatterns;
public HostInfo(String name, String id, String jabberId, String publicKey, boolean isOnline) { public HostInfo(String name,
String id,
String jabberId,
String publicKey,
ArrayList<String> tokenUrlPatterns,
boolean isOnline) {
this.name = name; this.name = name;
this.id = id; this.id = id;
this.jabberId = jabberId; this.jabberId = jabberId;
this.publicKey = publicKey; this.publicKey = publicKey;
this.mTokenUrlPatterns = tokenUrlPatterns;
this.isOnline = isOnline; this.isOnline = isOnline;
} }
public ArrayList<String> getTokenUrlPatterns() {
return new ArrayList<String>(mTokenUrlPatterns);
}
public static HostInfo create(JSONObject json) throws JSONException {
assert json != null;
ArrayList<String> tokenUrlPatterns = new ArrayList<String>();
JSONArray jsonPatterns = json.optJSONArray("tokenUrlPatterns");
if (jsonPatterns != null) {
for (int i = 0; i < jsonPatterns.length(); i++) {
String pattern = jsonPatterns.getString(i);
if (pattern != null && !pattern.isEmpty()) {
tokenUrlPatterns.add(pattern);
}
}
}
return new HostInfo(
json.getString("hostName"),
json.getString("hostId"),
json.optString("jabberId"),
json.optString("publicKey"),
tokenUrlPatterns,
json.optString("status").equals("ONLINE"));
}
} }
...@@ -145,12 +145,7 @@ public class HostListLoader { ...@@ -145,12 +145,7 @@ public class HostListLoader {
// attempt will fail because of the missing keys. The failed attempt will // attempt will fail because of the missing keys. The failed attempt will
// trigger reloading of the host-list, by which time the keys will hopefully be // trigger reloading of the host-list, by which time the keys will hopefully be
// present, and the retried connection can succeed. // present, and the retried connection can succeed.
HostInfo host = new HostInfo( HostInfo host = HostInfo.create(hostJson);
hostJson.getString("hostName"),
hostJson.getString("hostId"),
hostJson.optString("jabberId"),
hostJson.optString("publicKey"),
hostJson.optString("status").equals("ONLINE"));
hostList.add(host); hostList.add(host);
++index; ++index;
} }
......
...@@ -10,10 +10,12 @@ import android.content.ComponentName; ...@@ -10,10 +10,12 @@ import android.content.ComponentName;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils;
import android.util.Base64; import android.util.Base64;
import android.util.Log; import android.util.Log;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
/** /**
...@@ -51,46 +53,42 @@ public class ThirdPartyTokenFetcher { ...@@ -51,46 +53,42 @@ public class ThirdPartyTokenFetcher {
*/ */
private final String mState; private final String mState;
/** URL of the third party login page. */
private final String mTokenUrl;
/** The client identifier. See http://tools.ietf.org/html/rfc6749#section-2.2. */
private final String mClientId;
/** The scope of access request. See http://tools.ietf.org/html/rfc6749#section-3.3. */
private final String mScope;
private final Callback mCallback; private final Callback mCallback;
/** The list of TokenUrls allowed by the domain. */
private final ArrayList<String> mTokenUrlPatterns;
private final String mRedirectUriScheme; private final String mRedirectUriScheme;
private final String mRedirectUri; private final String mRedirectUri;
public ThirdPartyTokenFetcher(Activity context, public ThirdPartyTokenFetcher(Activity context,
String tokenUrl, ArrayList<String> tokenUrlPatterns,
String clientId,
String scope,
Callback callback) { Callback callback) {
this.mContext = context; this.mContext = context;
this.mTokenUrl = tokenUrl;
this.mClientId = clientId;
this.mState = generateXsrfToken(); this.mState = generateXsrfToken();
this.mScope = scope;
this.mCallback = callback; this.mCallback = callback;
this.mTokenUrlPatterns = tokenUrlPatterns;
this.mRedirectUriScheme = context.getApplicationContext().getPackageName(); this.mRedirectUriScheme = context.getApplicationContext().getPackageName();
this.mRedirectUri = mRedirectUriScheme + "://" + REDIRECT_URI_HOST; this.mRedirectUri = mRedirectUriScheme + "://" + REDIRECT_URI_HOST;
} }
public void fetchToken() { /**
Uri.Builder uriBuilder = Uri.parse(mTokenUrl).buildUpon(); * @param tokenUrl URL of the third party login page.
uriBuilder.appendQueryParameter("redirect_uri", this.mRedirectUri); * @param clientId The client identifier. See http://tools.ietf.org/html/rfc6749#section-2.2.
uriBuilder.appendQueryParameter("scope", mScope); * @param scope The scope of access request. See http://tools.ietf.org/html/rfc6749#section-3.3.
uriBuilder.appendQueryParameter("client_id", mClientId); */
uriBuilder.appendQueryParameter("state", mState); public void fetchToken(String tokenUrl, String clientId, String scope) {
uriBuilder.appendQueryParameter("response_type", RESPONSE_TYPE); if (!isValidTokenUrl(tokenUrl)) {
failFetchToken(
"Token URL does not match the domain\'s allowed URL patterns." +
" URL: " + tokenUrl +
", patterns: " + TextUtils.join(",", this.mTokenUrlPatterns));
return;
}
Uri uri = uriBuilder.build(); Uri uri = buildRequestUri(tokenUrl, clientId, scope);
Intent intent = new Intent(Intent.ACTION_VIEW, uri); Intent intent = new Intent(Intent.ACTION_VIEW, uri);
Log.i("ThirdPartyAuth", "fetchToken() url:" + uri); Log.i("ThirdPartyAuth", "fetchToken() url:" + uri);
OAuthRedirectActivity.setEnabled(mContext, true); OAuthRedirectActivity.setEnabled(mContext, true);
...@@ -102,6 +100,27 @@ public class ThirdPartyTokenFetcher { ...@@ -102,6 +100,27 @@ public class ThirdPartyTokenFetcher {
} }
} }
private Uri buildRequestUri(String tokenUrl, String clientId, String scope) {
Uri.Builder uriBuilder = Uri.parse(tokenUrl).buildUpon();
uriBuilder.appendQueryParameter("redirect_uri", this.mRedirectUri);
uriBuilder.appendQueryParameter("scope", scope);
uriBuilder.appendQueryParameter("client_id", clientId);
uriBuilder.appendQueryParameter("state", mState);
uriBuilder.appendQueryParameter("response_type", RESPONSE_TYPE);
return uriBuilder.build();
}
/** Verifies the host-supplied URL matches the domain's allowed URL patterns. */
private boolean isValidTokenUrl(String tokenUrl) {
for (String pattern : mTokenUrlPatterns) {
if (tokenUrl.matches(pattern)) {
return true;
}
}
return false;
}
private boolean isValidIntent(Intent intent) { private boolean isValidIntent(Intent intent) {
assert intent != null; assert intent != null;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment