Skip to content

Commit

Permalink
add some additional debug logging around logging out and migrate sess…
Browse files Browse the repository at this point in the history
…ion cookies to hardware-backed secure storage on newer versions of Android
  • Loading branch information
c99koder committed Dec 19, 2022
1 parent ed8d17e commit 6bcea2f
Show file tree
Hide file tree
Showing 13 changed files with 126 additions and 48 deletions.
5 changes: 3 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ def getRevision = { ->
}

android {
compileSdkVersion 32
compileSdkVersion 33
testBuildType "mockdata"

defaultConfig {
versionCode 339
versionCode 340
versionName "4.29"
minSdkVersion 22
targetSdkVersion 31
Expand Down Expand Up @@ -359,6 +359,7 @@ dependencies {
implementation "androidx.browser:browser:1.4.0"
implementation "androidx.exifinterface:exifinterface:1.3.5"
implementation "androidx.room:room-runtime:2.4.3"
implementation "androidx.security:security-crypto:1.1.0-alpha04"
annotationProcessor "androidx.room:room-compiler:2.4.3"
implementation "com.google.android.gms:play-services-base:18.1.0"
implementation "com.google.android.gms:play-services-auth:20.3.0"
Expand Down
2 changes: 1 addition & 1 deletion irccloud-android.iml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/out" />
</content>
<orderEntry type="jdk" jdkName="Android API 32 Platform" jdkType="Android SDK" />
<orderEntry type="jdk" jdkName="Android API 33, extension level 3 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
7 changes: 0 additions & 7 deletions src/com/irccloud/android/IRCCloudApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,6 @@ public synchronized void onReceive(Context context, Intent intent) {
conn = NetworkConnection.getInstance();
ColorFormatter.init();

prefs = getSharedPreferences("prefs", 0);
NetworkConnection.IRCCLOUD_HOST = prefs.getString("host", BuildConfig.HOST);
NetworkConnection.IRCCLOUD_PATH = prefs.getString("path", "/");

if(!NetworkConnection.IRCCLOUD_HOST.endsWith(".irccloud.com"))
NetworkConnection.IRCCLOUD_HOST = BuildConfig.HOST;

/*try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE))
Expand Down
2 changes: 1 addition & 1 deletion src/com/irccloud/android/MarkAsReadBroadcastReceiver.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void onReceive(Context ctx, Intent i) {
}
}
IRCCloudLog.Log(Log.INFO, "IRCCloud", "Mark as read bid" + bid);
NetworkConnection.getInstance().postHeartbeat(cid, bid, highestEid, IRCCloudApplication.getInstance().getApplicationContext().getSharedPreferences("prefs", 0).getString("session_key", ""));
NetworkConnection.getInstance().postHeartbeat(cid, bid, highestEid, NetworkConnection.getInstance().session);
IRCCloudLog.Log(Log.INFO, "IRCCloud", "Cancel bid" + bid);
NotificationManagerCompat.from(IRCCloudApplication.getInstance().getApplicationContext()).cancel(bid);
}
Expand Down
115 changes: 103 additions & 12 deletions src/com/irccloud/android/NetworkConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.WindowManager;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.widget.Toast;

import androidx.core.content.FileProvider;
import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKey;

import com.codebutler.android_websockets.WebSocketClient;
import com.datatheorem.android.trustkit.TrustKit;
Expand Down Expand Up @@ -91,6 +94,7 @@
import java.net.URL;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -254,6 +258,7 @@ public interface IRCResultCallback {

private HashMap<Integer, OOBFetcher> oobTasks = new HashMap<Integer, OOBFetcher>();
private ArrayList<IRCCloudJSONObject> pendingEdits = new ArrayList<>();
private final SparseArray<String> reqids = new SparseArray<>();

public synchronized static NetworkConnection getInstance() {
if (instance == null) {
Expand Down Expand Up @@ -336,7 +341,36 @@ public void onReceive(Context context, Intent intent) {

@SuppressWarnings("deprecation")
public NetworkConnection() {
session = IRCCloudApplication.getInstance().getApplicationContext().getSharedPreferences("prefs", 0).getString("session_key", "");
SharedPreferences prefs = IRCCloudApplication.getInstance().getApplicationContext().getSharedPreferences("prefs", 0);
session = prefs.getString("session_key", "");
IRCCloudLog.Log(Log.INFO, "IRCCloud", "Session: " + session);
if(session.length() > 0) {
IRCCloudLog.Log(Log.INFO, "IRCCloud", "Migrating session key");
NetworkConnection.IRCCLOUD_HOST = prefs.getString("host", BuildConfig.HOST);
NetworkConnection.IRCCLOUD_PATH = prefs.getString("path", "/");

if(!NetworkConnection.IRCCLOUD_HOST.endsWith(".irccloud.com"))
NetworkConnection.IRCCLOUD_HOST = BuildConfig.HOST;

set_api_host(NetworkConnection.IRCCLOUD_HOST);
set_api_path(NetworkConnection.IRCCLOUD_PATH);
set_session(session);

SharedPreferences.Editor editor = prefs.edit();
editor.remove("session_key");
editor.remove("host");
editor.remove("path");
editor.apply();
} else {
try {
prefs = getEncryptedSharedPrefs();
session = prefs.getString("session", "");
NetworkConnection.IRCCLOUD_HOST = prefs.getString("host", BuildConfig.HOST);
NetworkConnection.IRCCLOUD_PATH = prefs.getString("path", "/");
} catch (Exception e) {
IRCCloudLog.LogException(e);
}
}
String version;
String network_type = null;
try {
Expand Down Expand Up @@ -706,11 +740,38 @@ public static void set_api_host(String host) {
if (host.endsWith("/"))
host = host.substring(0, host.length() - 1);

SharedPreferences.Editor editor = IRCCloudApplication.getInstance().getApplicationContext().getSharedPreferences("prefs", 0).edit();
editor.putString("host", host);
editor.apply();
NetworkConnection.IRCCLOUD_HOST = host;
IRCCloudLog.Log(Log.INFO, TAG, "API host: " + NetworkConnection.IRCCLOUD_HOST);
try {
SharedPreferences.Editor editor = getEncryptedSharedPrefs().edit();
editor.putString("host", host);
editor.apply();
NetworkConnection.IRCCLOUD_HOST = host;
IRCCloudLog.Log(Log.INFO, TAG, "API host: " + NetworkConnection.IRCCLOUD_HOST);
} catch (Exception e) {
IRCCloudLog.LogException(e);
}
}

public static void set_api_path(String path) {
try {
SharedPreferences.Editor editor = getEncryptedSharedPrefs().edit();
editor.putString("path", path);
editor.apply();
NetworkConnection.IRCCLOUD_PATH = path;
IRCCloudLog.Log(Log.INFO, TAG, "API path: " + NetworkConnection.IRCCLOUD_PATH);
} catch (Exception e) {
IRCCloudLog.LogException(e);
}
}

public void set_session(String session) {
try {
SharedPreferences.Editor editor = getEncryptedSharedPrefs().edit();
editor.putString("session", session);
editor.apply();
this.session = session;
} catch (Exception e) {
IRCCloudLog.LogException(e);
}
}

public void set_pastebin_cookie() {
Expand Down Expand Up @@ -946,15 +1007,34 @@ public boolean hasResult(BaseTransaction<List<Model>> baseTransaction, List<Mode
saveTimer.schedule(saveTimerTask, delay);*/
}

private static SharedPreferences getEncryptedSharedPrefs() throws GeneralSecurityException, IOException {
MasterKey masterKey = new MasterKey.Builder(IRCCloudApplication.getInstance().getApplicationContext())
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();

return EncryptedSharedPreferences.create(
IRCCloudApplication.getInstance().getApplicationContext(),
"secret_shared_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
}

public void connect() {
connect(false);
}

@TargetApi(24)
public synchronized void connect(boolean ignoreNetworkState) {
IRCCloudLog.Log(Log.DEBUG, TAG, "connect()");
reqids.clear();
Context ctx = IRCCloudApplication.getInstance().getApplicationContext();
session = ctx.getSharedPreferences("prefs", 0).getString("session_key", "");
try {
session = getEncryptedSharedPrefs().getString("session", "");
} catch (Exception e) {
IRCCloudLog.LogException(e);
}
int limit = 100;

if (session.length() == 0) {
Expand Down Expand Up @@ -1307,6 +1387,13 @@ public void logout() {
SharedPreferences.Editor editor = IRCCloudApplication.getInstance().getApplicationContext().getSharedPreferences("prefs", 0).edit();
editor.clear();
editor.apply();
try {
editor = getEncryptedSharedPrefs().edit();
editor.clear();
editor.apply();
} catch (Exception e) {
IRCCloudLog.LogException(e);
}
mServers.clear();
mBuffers.clear();
mChannels.clear();
Expand Down Expand Up @@ -1357,6 +1444,9 @@ private int send(String method, JSONObject params, IRCResultCallback callback) {
params.put("_method", method);
//Log.d(TAG, "Reqid: " + last_reqid + " Method: " + method + " Params: " + params.toString());
client.send(params.toString());
reqids.put(last_reqid, method);
if(reqids.size() > 25)
reqids.clear();
return last_reqid;
} catch (Exception e) {
printStackTraceToCrashlytics(e);
Expand Down Expand Up @@ -3268,18 +3358,19 @@ public synchronized void parse_object(IRCCloudJSONObject object, boolean backlog
if (object.has("success") && !object.getBoolean("success") && object.has("message")) {
IRCCloudLog.Log(Log.ERROR, TAG, "Error: " + object);
if(object.getString("message").equals("auth")) {
int session_length = session.length();
String old_host = IRCCLOUD_HOST;
String old_path = IRCCLOUD_PATH;
logout();
notifyHandlers(EVENT_AUTH_FAILED, object);
IRCCloudLog.Log("LOGOUT: Auth error: " + object + " method: " + reqids.get(object.getInt("_reqid")) + " host: " + old_host + " path: " + old_path +" session length: " + session_length);
} else if(object.getString("message").equals("set_shard")) {
disconnect();
ready = false;
SharedPreferences.Editor editor = IRCCloudApplication.getInstance().getSharedPreferences("prefs", 0).edit();
editor.putString("session_key", object.getString("cookie"));
set_session(object.getString("cookie"));
if (object.has("websocket_path")) {
IRCCLOUD_PATH = object.getString("websocket_path");
set_api_path(object.getString("websocket_path"));
}
editor.putString("path", NetworkConnection.IRCCLOUD_PATH);
editor.apply();
if (object.has("api_host")) {
set_api_host(object.getString("api_host"));
}
Expand Down
2 changes: 1 addition & 1 deletion src/com/irccloud/android/RemoteInputService.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public RemoteInputService() {
@Override
protected void onHandleIntent(Intent intent) {
boolean success = false;
String sk = getSharedPreferences("prefs", 0).getString("session_key", "");
String sk = NetworkConnection.getInstance().session;
if (intent != null && sk != null && sk.length() > 0) {
if(intent.hasExtra("eid"))
NotificationManagerCompat.from(IRCCloudApplication.getInstance().getApplicationContext()).cancel((int)(intent.getLongExtra("eid", -1) / 1000));
Expand Down
4 changes: 2 additions & 2 deletions src/com/irccloud/android/activity/BaseActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,7 @@ public void onResume() {
f.delete();
}
new ImageListPruneTask().execute((Void)null);
String session = getSharedPreferences("prefs", 0).getString("session_key", "");
if (session.length() > 0 || BuildConfig.MOCK_DATA) {
if ((NetworkConnection.getInstance().session != null && NetworkConnection.getInstance().session.length() > 0) || BuildConfig.MOCK_DATA) {
if(conn.notifier) {
IRCCloudLog.Log(Log.INFO, "IRCCloud", "Upgrading notifier websocket");
conn.upgrade();
Expand Down Expand Up @@ -647,6 +646,7 @@ public void onClick(DialogInterface dialog, int which) {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
conn.logout();
IRCCloudLog.Log("LOGOUT: Logout menu item selected");
if(mGoogleApiClient.isConnected()) {
Auth.CredentialsApi.disableAutoSignIn(mGoogleApiClient).setResultCallback(new ResultCallback<Status>() {
@Override
Expand Down
2 changes: 2 additions & 0 deletions src/com/irccloud/android/activity/EditConnectionActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import com.irccloud.android.ColorScheme;
import com.irccloud.android.IRCCloudApplication;
import com.irccloud.android.IRCCloudJSONObject;
import com.irccloud.android.IRCCloudLog;
import com.irccloud.android.NetworkConnection;
import com.irccloud.android.R;
import com.irccloud.android.data.collection.ServersList;
Expand Down Expand Up @@ -83,6 +84,7 @@ public void onCreate(Bundle savedInstanceState) {
public void onClick(View v) {
if (ServersList.getInstance().count() < 1) {
NetworkConnection.getInstance().logout();
IRCCloudLog.Log("LOGOUT: EditConnectionActivity cancel button pressed");
Intent i = new Intent(EditConnectionActivity.this, LoginActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
Expand Down
25 changes: 7 additions & 18 deletions src/com/irccloud/android/activity/LoginActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -481,9 +481,10 @@ public void onResume() {
private void login_or_connect() {
if (NetworkConnection.IRCCLOUD_HOST != null && NetworkConnection.IRCCLOUD_HOST.length() > 0 && getIntent() != null && getIntent().getData() != null && getIntent().getData().getPath().endsWith("/access-link")) {
NetworkConnection.getInstance().logout();
IRCCloudLog.Log("LOGOUT: Access Link launched");
new AccessLinkTask().execute("https://" + NetworkConnection.IRCCLOUD_HOST + "/chat/access-link?" + getIntent().getData().getEncodedQuery().replace("&mobile=1", "") + "&format=json");
setIntent(new Intent(this, LoginActivity.class));
} else if (getSharedPreferences("prefs", 0).getString("session_key","").length() > 0) {
} else if (NetworkConnection.getInstance().session != null && NetworkConnection.getInstance().session.length() > 0) {
Intent i = new Intent(LoginActivity.this, MainActivity.class);
i.putExtra("nosplash", true);
if (getIntent() != null) {
Expand All @@ -505,12 +506,6 @@ private void login_or_connect() {
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SAML) {
if (resultCode == RESULT_OK) {
SharedPreferences.Editor editor = getSharedPreferences("prefs", 0).edit();
editor.putString("session_key", NetworkConnection.getInstance().session);
editor.putString("host", NetworkConnection.IRCCLOUD_HOST);
editor.putString("path", NetworkConnection.IRCCLOUD_PATH);
editor.apply();

final Intent i = new Intent(LoginActivity.this, MainActivity.class);
i.putExtra("nosplash", true);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
Expand Down Expand Up @@ -620,14 +615,10 @@ protected JSONObject doInBackground(Void... arg0) {
public void onPostExecute(JSONObject result) {
if (result != null && result.has("session")) {
try {
NetworkConnection.getInstance().session = result.getString("session");
SharedPreferences.Editor editor = getSharedPreferences("prefs", 0).edit();
editor.putString("session_key", result.getString("session"));
NetworkConnection.getInstance().set_session(result.getString("session"));
if (result.has("websocket_path")) {
NetworkConnection.IRCCLOUD_PATH = result.getString("websocket_path");
NetworkConnection.set_api_path(result.getString("websocket_path"));
}
editor.putString("path", NetworkConnection.IRCCLOUD_PATH);
editor.apply();

if (result.has("api_host")) {
NetworkConnection.set_api_host(result.getString("api_host"));
Expand Down Expand Up @@ -808,13 +799,10 @@ protected JSONObject doInBackground(String... arg0) {
public void onPostExecute(JSONObject result) {
if (result != null && result.has("session")) {
try {
SharedPreferences.Editor editor = getSharedPreferences("prefs", 0).edit();
editor.putString("session_key", result.getString("session"));
NetworkConnection.getInstance().set_session(result.getString("session"));
if (result.has("websocket_path")) {
NetworkConnection.IRCCLOUD_PATH = result.getString("websocket_path");
NetworkConnection.set_api_path(result.getString("websocket_path"));
}
editor.putString("path", NetworkConnection.IRCCLOUD_PATH);
editor.apply();

if (result.has("api_host")) {
NetworkConnection.set_api_host(result.getString("api_host"));
Expand Down Expand Up @@ -997,6 +985,7 @@ public void onPostExecute(JSONObject result) {

if (NetworkConnection.IRCCLOUD_HOST != null && NetworkConnection.IRCCLOUD_HOST.length() > 0 && getIntent() != null && getIntent().getData() != null && getIntent().getData().getPath().endsWith("/access-link")) {
NetworkConnection.getInstance().logout();
IRCCloudLog.Log("LOGOUT: Access Link launched");
new AccessLinkTask().execute("https://" + NetworkConnection.IRCCLOUD_HOST + "/chat/access-link?" + getIntent().getData().getEncodedQuery().replace("&mobile=1", "") + "&format=json");
setIntent(new Intent(LoginActivity.this, LoginActivity.class));
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ public void onClick(View view) {
mWebView.addJavascriptInterface(new JSInterface(), "Android");
mWebView.getSettings().setLoadWithOverviewMode(false);
mWebView.getSettings().setUseWideViewPort(false);
mWebView.getSettings().setAppCacheEnabled(false);
mWebView.getSettings().setAllowFileAccess(false);
mWebView.setWebViewClient(new WebViewClient() {
@Override
Expand Down
Loading

0 comments on commit 6bcea2f

Please sign in to comment.