From 9555ae3bc1287daa27151456a1f266bdc1640e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sun, 23 Mar 2014 01:26:49 +0100 Subject: Move classes for OpenPGP API into remote package --- .../keychain/remote/RemoteService.java | 233 +++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java new file mode 100644 index 000000000..434fe84c1 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2013-2014 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.remote; + +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; +import android.net.Uri; +import android.os.Binder; +import org.openintents.openpgp.OpenPgpError; +import org.openintents.openpgp.util.OpenPgpApi; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Abstract service class for remote APIs that handle app registration and user input. + */ +public abstract class RemoteService extends Service { + Context mContext; + + private static final int PRIVATE_REQUEST_CODE_REGISTER = 651; + private static final int PRIVATE_REQUEST_CODE_ERROR = 652; + + + public Context getContext() { + return mContext; + } + + protected Intent isAllowed(Intent data) { + try { + if (isCallerAllowed(false)) { + + return null; + } else { + String[] callingPackages = getPackageManager().getPackagesForUid( + Binder.getCallingUid()); + // TODO: currently simply uses first entry + String packageName = callingPackages[0]; + + byte[] packageSignature; + try { + packageSignature = getPackageSignature(packageName); + } catch (NameNotFoundException e) { + Log.e(Constants.TAG, "Should not happen, returning!", e); + // return error + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); + result.putExtra(OpenPgpApi.RESULT_ERROR, + new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); + return result; + } + Log.e(Constants.TAG, "Not allowed to use service! return PendingIntent for registration!"); + + Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class); + intent.setAction(RemoteServiceActivity.ACTION_REGISTER); + intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName); + intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature); + intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); + + PendingIntent pi = PendingIntent.getActivity(getBaseContext(), + PRIVATE_REQUEST_CODE_REGISTER, intent, 0); + + // return PendingIntent to be executed by client + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED); + result.putExtra(OpenPgpApi.RESULT_INTENT, pi); + + return result; + } + } catch (WrongPackageSignatureException e) { + Log.e(Constants.TAG, "wrong signature!", e); + + Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class); + intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE); + intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, + getString(R.string.api_error_wrong_signature)); + intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); + + PendingIntent pi = PendingIntent.getActivity(getBaseContext(), + PRIVATE_REQUEST_CODE_ERROR, intent, 0); + + // return PendingIntent to be executed by client + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED); + result.putExtra(OpenPgpApi.RESULT_INTENT, pi); + + return result; + } + } + + private byte[] getPackageSignature(String packageName) throws NameNotFoundException { + PackageInfo pkgInfo = getPackageManager().getPackageInfo(packageName, + PackageManager.GET_SIGNATURES); + Signature[] signatures = pkgInfo.signatures; + // TODO: Only first signature?! + byte[] packageSignature = signatures[0].toByteArray(); + + return packageSignature; + } + + /** + * Retrieves AppSettings from database for the application calling this remote service + * + * @return + */ + protected AppSettings getAppSettings() { + String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); + + // get app settings for this package + for (int i = 0; i < callingPackages.length; i++) { + String currentPkg = callingPackages[i]; + + Uri uri = KeychainContract.ApiApps.buildByPackageNameUri(currentPkg); + + AppSettings settings = ProviderHelper.getApiAppSettings(this, uri); + + if (settings != null) { + return settings; + } + } + + return null; + } + + /** + * Checks if process that binds to this service (i.e. the package name corresponding to the + * process) is in the list of allowed package names. + * + * @param allowOnlySelf allow only Keychain app itself + * @return true if process is allowed to use this service + * @throws WrongPackageSignatureException + */ + private boolean isCallerAllowed(boolean allowOnlySelf) throws WrongPackageSignatureException { + return isUidAllowed(Binder.getCallingUid(), allowOnlySelf); + } + + private boolean isUidAllowed(int uid, boolean allowOnlySelf) + throws WrongPackageSignatureException { + if (android.os.Process.myUid() == uid) { + return true; + } + if (allowOnlySelf) { // barrier + return false; + } + + String[] callingPackages = getPackageManager().getPackagesForUid(uid); + + // is calling package allowed to use this service? + for (int i = 0; i < callingPackages.length; i++) { + String currentPkg = callingPackages[i]; + + if (isPackageAllowed(currentPkg)) { + return true; + } + } + + Log.d(Constants.TAG, "Caller is NOT allowed!"); + return false; + } + + /** + * Checks if packageName is a registered app for the API. Does not return true for own package! + * + * @param packageName + * @return + * @throws WrongPackageSignatureException + */ + private boolean isPackageAllowed(String packageName) throws WrongPackageSignatureException { + Log.d(Constants.TAG, "packageName: " + packageName); + + ArrayList allowedPkgs = ProviderHelper.getRegisteredApiApps(this); + Log.d(Constants.TAG, "allowed: " + allowedPkgs); + + // check if package is allowed to use our service + if (allowedPkgs.contains(packageName)) { + Log.d(Constants.TAG, "Package is allowed! packageName: " + packageName); + + // check package signature + byte[] currentSig; + try { + currentSig = getPackageSignature(packageName); + } catch (NameNotFoundException e) { + throw new WrongPackageSignatureException(e.getMessage()); + } + + byte[] storedSig = ProviderHelper.getApiAppSignature(this, packageName); + if (Arrays.equals(currentSig, storedSig)) { + Log.d(Constants.TAG, + "Package signature is correct! (equals signature from database)"); + return true; + } else { + throw new WrongPackageSignatureException( + "PACKAGE NOT ALLOWED! Signature wrong! (Signature not equals signature from database)"); + } + } + + return false; + } + + @Override + public void onCreate() { + super.onCreate(); + mContext = this; + } + +} -- cgit v1.2.3 From aa35b1f4b5a5198482c9c5a659d357b3ac9a101b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 25 Mar 2014 19:11:20 +0100 Subject: More experimental work on API accounts --- .../org/sufficientlysecure/keychain/remote/RemoteService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java index 434fe84c1..7b66a0b5c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java @@ -130,16 +130,16 @@ public abstract class RemoteService extends Service { * * @return */ - protected AppSettings getAppSettings() { + protected AccountSettings getAccSettings(String accountName) { String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); // get app settings for this package for (int i = 0; i < callingPackages.length; i++) { String currentPkg = callingPackages[i]; - Uri uri = KeychainContract.ApiApps.buildByPackageNameUri(currentPkg); + Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName); - AppSettings settings = ProviderHelper.getApiAppSettings(this, uri); + AccountSettings settings = ProviderHelper.getApiAccountSettings(this, uri); if (settings != null) { return settings; @@ -210,7 +210,7 @@ public abstract class RemoteService extends Service { throw new WrongPackageSignatureException(e.getMessage()); } - byte[] storedSig = ProviderHelper.getApiAppSignature(this, packageName); + byte[] storedSig = ProviderHelper.getApiSignature(this, packageName); if (Arrays.equals(currentSig, storedSig)) { Log.d(Constants.TAG, "Package signature is correct! (equals signature from database)"); -- cgit v1.2.3 From dc9fd1221387a4f31ec2ceba615b376b0796ff10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 25 Mar 2014 23:09:57 +0100 Subject: Return account creation intent if account does not exists in db --- .../keychain/remote/RemoteService.java | 69 +++++++++++++++------- 1 file changed, 47 insertions(+), 22 deletions(-) (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java index 7b66a0b5c..0fd9ee7df 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java @@ -27,6 +27,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.net.Uri; import android.os.Binder; + import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.Constants; @@ -48,7 +49,6 @@ public abstract class RemoteService extends Service { private static final int PRIVATE_REQUEST_CODE_REGISTER = 651; private static final int PRIVATE_REQUEST_CODE_ERROR = 652; - public Context getContext() { return mContext; } @@ -56,13 +56,9 @@ public abstract class RemoteService extends Service { protected Intent isAllowed(Intent data) { try { if (isCallerAllowed(false)) { - return null; } else { - String[] callingPackages = getPackageManager().getPackagesForUid( - Binder.getCallingUid()); - // TODO: currently simply uses first entry - String packageName = callingPackages[0]; + String packageName = getCurrentCallingPackage(); byte[] packageSignature; try { @@ -85,7 +81,7 @@ public abstract class RemoteService extends Service { intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); PendingIntent pi = PendingIntent.getActivity(getBaseContext(), - PRIVATE_REQUEST_CODE_REGISTER, intent, 0); + PRIVATE_REQUEST_CODE_REGISTER, intent, 0); // return PendingIntent to be executed by client Intent result = new Intent(); @@ -100,11 +96,11 @@ public abstract class RemoteService extends Service { Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class); intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE); intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, - getString(R.string.api_error_wrong_signature)); + getString(R.string.api_error_wrong_signature)); intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); PendingIntent pi = PendingIntent.getActivity(getBaseContext(), - PRIVATE_REQUEST_CODE_ERROR, intent, 0); + PRIVATE_REQUEST_CODE_ERROR, intent, 0); // return PendingIntent to be executed by client Intent result = new Intent(); @@ -126,27 +122,56 @@ public abstract class RemoteService extends Service { } /** - * Retrieves AppSettings from database for the application calling this remote service + * Returns package name associated with the UID, which is assigned to the process that sent you the + * current transaction that is being processed :) + * + * @return package name + */ + private String getCurrentCallingPackage() { + // TODO: + // callingPackages contains more than one entry when sharedUserId has been used... + String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); + String currentPkg = callingPackages[0]; + Log.d(Constants.TAG, "currentPkg: " + currentPkg); + + return currentPkg; + } + + /** + * Retrieves AccountSettings from database for the application calling this remote service * * @return */ protected AccountSettings getAccSettings(String accountName) { - String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); + String currentPkg = getCurrentCallingPackage(); + Log.d(Constants.TAG, "accountName: " + accountName); - // get app settings for this package - for (int i = 0; i < callingPackages.length; i++) { - String currentPkg = callingPackages[i]; + Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName); - Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName); + AccountSettings settings = ProviderHelper.getApiAccountSettings(this, uri); - AccountSettings settings = ProviderHelper.getApiAccountSettings(this, uri); + return settings; // can be null! + } - if (settings != null) { - return settings; - } - } + protected Intent getCreateAccountIntent(Intent data, String accountName) { + String packageName = getCurrentCallingPackage(); + Log.d(Constants.TAG, "accountName: " + accountName); + + Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class); + intent.setAction(RemoteServiceActivity.ACTION_CREATE_ACCOUNT); + intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName); + intent.putExtra(RemoteServiceActivity.EXTRA_ACC_NAME, accountName); + intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); + + PendingIntent pi = PendingIntent.getActivity(getBaseContext(), + PRIVATE_REQUEST_CODE_REGISTER, intent, 0); + + // return PendingIntent to be executed by client + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED); + result.putExtra(OpenPgpApi.RESULT_INTENT, pi); - return null; + return result; } /** @@ -217,7 +242,7 @@ public abstract class RemoteService extends Service { return true; } else { throw new WrongPackageSignatureException( - "PACKAGE NOT ALLOWED! Signature wrong! (Signature not equals signature from database)"); + "PACKAGE NOT ALLOWED! Signature wrong! (Signature not equals signature from database)"); } } -- cgit v1.2.3 From 028af0c1195b2d6a747874af8cae07dde542dc64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 00:11:49 +0100 Subject: Fix constraints and insert --- .../main/java/org/sufficientlysecure/keychain/remote/RemoteService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java index 0fd9ee7df..7e935d317 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java @@ -235,7 +235,7 @@ public abstract class RemoteService extends Service { throw new WrongPackageSignatureException(e.getMessage()); } - byte[] storedSig = ProviderHelper.getApiSignature(this, packageName); + byte[] storedSig = ProviderHelper.getApiAppSignature(this, packageName); if (Arrays.equals(currentSig, storedSig)) { Log.d(Constants.TAG, "Package signature is correct! (equals signature from database)"); -- cgit v1.2.3 From ec3459733180a5d83805b6fe9ddf5571f7b0380f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 14:11:28 +0100 Subject: Make PendingIntents with FLAG_CANCEL_CURRENT and FLAG_ONE_SHOT --- .../keychain/remote/RemoteService.java | 24 ++++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java index 7e935d317..8cea3cd21 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java @@ -46,9 +46,6 @@ import java.util.Arrays; public abstract class RemoteService extends Service { Context mContext; - private static final int PRIVATE_REQUEST_CODE_REGISTER = 651; - private static final int PRIVATE_REQUEST_CODE_ERROR = 652; - public Context getContext() { return mContext; } @@ -59,6 +56,7 @@ public abstract class RemoteService extends Service { return null; } else { String packageName = getCurrentCallingPackage(); + Log.d(Constants.TAG, "isAllowed packageName: " + packageName); byte[] packageSignature; try { @@ -80,8 +78,9 @@ public abstract class RemoteService extends Service { intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature); intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); - PendingIntent pi = PendingIntent.getActivity(getBaseContext(), - PRIVATE_REQUEST_CODE_REGISTER, intent, 0); + PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, + intent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); // return PendingIntent to be executed by client Intent result = new Intent(); @@ -99,8 +98,9 @@ public abstract class RemoteService extends Service { getString(R.string.api_error_wrong_signature)); intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); - PendingIntent pi = PendingIntent.getActivity(getBaseContext(), - PRIVATE_REQUEST_CODE_ERROR, intent, 0); + PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, + intent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); // return PendingIntent to be executed by client Intent result = new Intent(); @@ -163,8 +163,9 @@ public abstract class RemoteService extends Service { intent.putExtra(RemoteServiceActivity.EXTRA_ACC_NAME, accountName); intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); - PendingIntent pi = PendingIntent.getActivity(getBaseContext(), - PRIVATE_REQUEST_CODE_REGISTER, intent, 0); + PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, + intent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); // return PendingIntent to be executed by client Intent result = new Intent(); @@ -206,7 +207,7 @@ public abstract class RemoteService extends Service { } } - Log.d(Constants.TAG, "Caller is NOT allowed!"); + Log.d(Constants.TAG, "Uid is NOT allowed!"); return false; } @@ -218,7 +219,7 @@ public abstract class RemoteService extends Service { * @throws WrongPackageSignatureException */ private boolean isPackageAllowed(String packageName) throws WrongPackageSignatureException { - Log.d(Constants.TAG, "packageName: " + packageName); + Log.d(Constants.TAG, "isPackageAllowed packageName: " + packageName); ArrayList allowedPkgs = ProviderHelper.getRegisteredApiApps(this); Log.d(Constants.TAG, "allowed: " + allowedPkgs); @@ -246,6 +247,7 @@ public abstract class RemoteService extends Service { } } + Log.d(Constants.TAG, "Package is NOT allowed! packageName: " + packageName); return false; } -- cgit v1.2.3