Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add user-install microG User Services #2188

Open
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

p1gp1g
Copy link

@p1gp1g p1gp1g commented Feb 19, 2024

Following discussion on #2183 (which may be closed ? Superseded by this one)

@mar-v-in @ale5000-git

I have tested, and org.microg.gms can't be installed on a system with com.google.android.gms (Google or microG) because of duplicate permission.

I have used the dimension 'target' for the user flavor, I think that's the one to use.

@ale5000-git
Copy link
Member

@p1gp1g
If I'm not wrong, permissions can be defined more than one time if both apps are signed with the same signature.

@@ -36,6 +36,7 @@
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
import static org.microg.gms.common.Constants.MICROG_GMS_PACKAGE_NAME;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't be better something like USER_GMS_PACKAGE_NAME?
Both are still microG.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@ale5000-git
Copy link
Member

Now it fails build:

/home/runner/work/GmsCore/GmsCore/play-services-core/src/main/AndroidManifest.xml:137:9-36 Error:

	Attribute application@label at AndroidManifest.xml:137:9-36 requires a placeholder substitution but no value for <appLabel> is provided.
/home/runner/work/GmsCore/GmsCore/play-services-core/src/huawei/AndroidManifest.xml Error:
	Validation failed, exiting
> Task :play-services-core:processHmsHuaweiDebugMainManifest FAILED

@p1gp1g
Copy link
Author

p1gp1g commented Feb 20, 2024

I've not tested locally, but it should build now. I'll rebase to fix the commit into the previous one later.


@p1gp1g If I'm not wrong, permissions can be defined more than one time if both apps are signed with the same signature.

IIRC signature spoofing works only for system app, so microG (system) should have a different signature. I think we should test this case.


A few things should be done on the UI for the user services:

  • a dialog on first use, explaining that:
    • this service is experimental (?)
    • microG Services use signature spoofing to work with applications using the google libraries, this build does not use this technique and works only with application which use the microG libraries replacement.
    • Help is welcome to word this :)
  • Remove the signature spoofing check for the user services
  • Maybe remove some other things ?

@mar-v-in
Copy link
Member

mar-v-in commented Feb 20, 2024

microG Services can be installed as a user / non-system app already. (If that's not possible on GrapheneOS that might be a GrapheneOS specific issue and would be interesting to analyze that.) As a matter of fact, I personally run microG as a user app myself (using my homebrowed OS that's just very few modifications on top of AOSP).

Many systems would not allow signature spoofing when installing microG as a user app. While signature spoofing might be restricted to system apps on systems that come with microG, that doesn't mean it needs to be. DivestOS for example supports signature spoofing for microG running as a user app.

Signature spoofing does not have an impact on any (security) feature of the OS that are signature specific (like signature shared permission declarations). Signature spoofing only affects apps that ask the OS for the signing certificate of an app - which essentially is only the play services client library, because use of that OS feature is discouraged by Google for being insecure as a security mechanism (it's possible to run race attacks against it) and unsuitable for anything else.

Some OS include modified or resigned versions of microG Services. Those obviously have a different signature and can't be upgraded from the original microG package. Otherwise, I would nornally expect both variants to be signed using the same signing key, so that it's easy for users to verify.

To prevent users from installing both, we can and probably should just display appropriate messages when the user does so. Reworking the self-check has been on my list for some time already, so this could become part of it.

Given what I wrote I think the branding as "microG User Services" is very much misleading. We should come up with another name before publishing this. Something along the lines of "microG Services without Google Play Services compatibility", which would be more descriptive but not a nice name ;)

@p1gp1g
Copy link
Author

p1gp1g commented Feb 20, 2024

I'll use CGAG for services with id com.google.android.gms and OMG to talk about services with id org.microg.gms.


Many systems would not allow signature spoofing when installing microG as a user app. While signature spoofing might be restricted to system apps on systems that come with microG, that doesn't mean it needs to be. DivestOS for example supports signature spoofing for microG running as a user app.

TIL about DivestOS doing it. Still, you shouldn't be able to install OGM + CGAG because of signature spoofing for the later. Nevertheless, some user without signature spoofing at all may try to install both on their system (which wouldn't work with CGAG). [Edit: OGM and CGAG can be installed on the same system.]

A user installing OMG when CGAG is already installed should not be an issue since CGAG has always the priority.

The main issue is :

  1. If a user install CGAG after OMG, then CGAG will take the priority, and some data may be lost (like things needed in shared pref)
  2. If a user install CGAG or OMG after using an application embedding a -core (like the Corona tracing app - CCTG). Then, CGAG or OMG will take the priority, and some data may be lost (like things needed in shared pref)

Note that the 2nd issue is already there.

So. Maybe we should add a dialog to CGAG:

  • if it has been installed after the system install, that some application may use it instead of internal mechanism and the user may need to login again or something like this (to make it understandable)

To prevent users from installing both, we can and probably should just display appropriate messages when the user does so. Reworking the self-check has been on my list for some time already, so this could become part of it.

It isn't important if both is installed, CGAG takes the priority.

Given what I wrote I think the branding as "microG User Services" is very much misleading. We should come up with another name before publishing this. Something along the lines of "microG Services without Google Play Services compatibility", which would be more descriptive but not a nice name ;)

I know, that's a temporary name :)

microG Minus Services ?

@mar-v-in
Copy link
Member

Still, you shouldn't be able to install OGM + CGAG because of signature spoofing for the later.

Signature spoofing has no impact here.

Signature spoofing does not have an impact on any (security) feature of the OS that are signature specific (like signature shared permission declarations). Signature spoofing only affects apps that ask the OS for the signing certificate of an app

@p1gp1g
Copy link
Author

p1gp1g commented Feb 20, 2024

Sorry, I missed that point, I've edited the previous comment

@p1gp1g p1gp1g force-pushed the feat/user_services branch 2 times, most recently from d304a7d to af11563 Compare February 23, 2024 11:52
@p1gp1g
Copy link
Author

p1gp1g commented Feb 24, 2024

The last commit introduced a function to remember the service used by the application. That way, if I use the embedded core, then the app won't use an external one after OMG/CGAG install. For instance, CCTG won't lose its data.

Also, I adds a check to be sure microG/Play services is legit to reduce risks of malicious app branded as another. It assures the service

  • Has an activity (so it will be listed in the installed app)
  • Has a legit name (Note: This is note a space 0x20 between Google and Play for CGAG)

I think this is important because: if a user doesn't have play services/microG with id com.google.android.gms (big if), a malicious app installed with id com.google.android.gms, branded has another service (a cooking app for instance), may use the FIDO service to get access to some services:
1 When the user wants to do a FIDO authent, a request is send to the FIDO service
2. The malicious app uses the key activation to register to another service (the user think it is a legit login)
3. The malicious app sends the response to a remote server and it displays a screen saying there is an error and to retry
4. Do the user's legit login

PS: I don't think the CI Out Of Memory error is related to this PR, isn't it ? Could it be restarted ?

@ale5000-git
Copy link
Member

@p1gp1g

Quote:

If you use the gradle-android-plugin to build your app, then you can use
BuildConfig.APPLICATION_ID
to retrieve the package name from any scope, incl. a static one.

Isn't this simpler?

@p1gp1g
Copy link
Author

p1gp1g commented Feb 24, 2024

Instead of the resValue package_id ? I've done that because I don't know if that's possible to use it in the activity's xml.

@ale5000-git
Copy link
Member

ale5000-git commented Feb 24, 2024

Yes, for the microG self check it should be the simplest way (it also avoid passing context), for other cases I don't know; I don't have time to try currently.

@mar-v-in
Copy link
Member

In AndroidManifest.xml you can use ${applicationId} to get the current application id, never tried if that works for other resource files

@p1gp1g
Copy link
Author

p1gp1g commented Feb 24, 2024

Yes, for the microG self check it should be the simplest way (it also avoid passing context), for other cases I don't know; I don't have time to try currently.

You mean the check in function MultiConnectionKeeper.getTargetPackageWithoutPref ? This is intended to be a library to service check (eg. Browser requests microG)

@p1gp1g
Copy link
Author

p1gp1g commented Feb 25, 2024

The packageId must be shared with a resValue if it used in resource files (eg. https://stackoverflow.com/questions/57074999/get-application-id-in-xml-dynamically)

@p1gp1g
Copy link
Author

p1gp1g commented Feb 25, 2024

I have renamed the services microG Minus Services, this will be less confusing. Feel free to change the name

I have also added a dialog on first run to inform the user that this flavor is only compatible with microG libraries, I hope it will reduce users misunderstanding.

@ArchangeGabriel
Copy link
Contributor

(JFTR, since it was discussed earlier, there is now a major ROM allowing signature spoofing as user app: https://review.lineageos.org/c/LineageOS/android_frameworks_base/+/383573)

@p1gp1g
Copy link
Author

p1gp1g commented Feb 25, 2024

@ArchangeGabriel

This patch enables signature spoofing when the following conditions are
met:

  • Build is debuggable (userdebug/eng)
  • Package name is com.android.vending or com.google.android.gms
  • Package is signed with microG release keys
  • Fake signature is correct

I'm OK to check the microG signature (or Google Play signature) instead of controlling the application name: https://github.com/microg/GmsCore/pull/2188/files#diff-4fe7d2cf47e0e66eec592e3286e77a70323b66a82f5d43bfe7f903623230525dR77 . I've done that because some other project may want to fork microG/reimplement (I've seen one exists). I can change it if you want

@ale5000-git
Copy link
Member

ale5000-git commented Feb 25, 2024

@ArchangeGabriel
This PR is about an alternative user build of microG; it has a different internal name so it won't be used by apps compiled with Google libraries but only by apps compiled with microG libraries so signatute spoofing isn't needed at all.

@p1gp1g
Copy link
Author

p1gp1g commented Feb 25, 2024

But this PR introduces 2 things too:

  • the application using the library saves the targetted packageId, so it won't lose data if a service is installed after the first use
  • a check here (posted on the previous post) to ensure another app is not trying to spoof microG. This checks if the app 1. has an activity (to be listed in user app) 2. use a name with Google Play or microG so it won't be branded as something else. This second point could be changed into a signature check.

@ale5000-git
Copy link
Member

Many ROMs ship a custom microG with a different signature, so a signature check isn't a good thing.

@mar-v-in
Copy link
Member

mar-v-in commented Feb 25, 2024

I don't think this name check is really necessary or a good idea. I would prefer if forks of microG would not call themselves microG (to not confuse users), but this reinforces them to actually have microG in their name.

I personally don't think apps taking over the package name is a huge risk as there is a ton of other ways that malware could affect your system if you install apps blindly from untrusted source. But anyway, I'd propose to do at most the following:
For the package name com.google.android.gms, check if it's a system app or signed using Google or microG certificate and if not, introduce some user interaction, asking the user to confirm they want to use services from that app (displaying its name, app icon and maybe even a signature fingerprint). Same for the org.microg.gms package name, except that Google certificate is not accepted for that. The confirmation is obviously to be persisted.
This way, most users will have everything work silently, but those that decide to use a custom microG will have to confirm once (per app) to use it.

Doesn't need to be part of this pull request though.

@ArchangeGabriel
Copy link
Contributor

@ArchangeGabriel This PR is about an alternative user build of microG; it has a different internal name so it won't be used by apps compiled with Google libraries but only by apps compiled with microG libraries so signatute spoofing isn't needed at all.

I know, I was just replying to:

IIRC signature spoofing works only for system app, so microG (system) should have a different signature. I think we should test this case.

from @p1gp1g, even though @mar-v-in already addressed that with:

Many systems would not allow signature spoofing when installing microG as a user app. While signature spoofing might be restricted to system apps on systems that come with microG, that doesn't mean it needs to be. DivestOS for example supports signature spoofing for microG running as a user app.

I was just wanting to point out that allowing signature spoofing as user app just become suddenly much more common. ;)

@p1gp1g
Copy link
Author

p1gp1g commented Mar 5, 2024

@mar-v-in

I don't think this name check is really necessary or a good idea. I would prefer if forks of microG would not call themselves microG (to not confuse users), but this reinforces them to actually have microG in their name.

I personally don't think apps taking over the package name is a huge risk as there is a ton of other ways that malware could affect your system if you install apps blindly from untrusted source. But anyway, I'd propose to do at most the following: For the package name com.google.android.gms, check if it's a system app or signed using Google or microG certificate and if not, introduce some user interaction, asking the user to confirm they want to use services from that app (displaying its name, app icon and maybe even a signature fingerprint). Same for the org.microg.gms package name, except that Google certificate is not accepted for that. The confirmation is obviously to be persisted. This way, most users will have everything work silently, but those that decide to use a custom microG will have to confirm once (per app) to use it.

I'm OK with that. I've set a signature check, tested with Google Play, if someone can test it with microG.

I've started to look into the user confirmation when the signature doesn't match, it introduces 2 main changes:

  • Setting gmsPackage will be async
  • It needs an activity to open an alert box or something.

I think the user confirmation can fit another PR, but I need to know what's your way to deal with those 2 points :)

@p1gp1g
Copy link
Author

p1gp1g commented Mar 7, 2024

I've changed the new app name to microG Limited Services. There is already a dialog explaining how it is different from the regular one. I think this should be clear enough for the users

microG lib will now connect to the PlayServices if:

  • CGAG is installed as a system app
  • CGAG is installed with Google signatures (can be spoofed)
  • CGAG is installed with microG signatures
  • OMG is installed with microG signatures

I think this is enough for any fork to work. In addition, a user interaction to use CGAG or OMG with other signatures (user installed) is still good to implement

@p1gp1g
Copy link
Author

p1gp1g commented Mar 15, 2024

@mar-v-in Is it possible to add it to the next milestone ?

@mar-v-in mar-v-in added this to the 0.3.2 milestone Mar 21, 2024
@mar-v-in mar-v-in modified the milestones: 0.3.2, 0.3.3 Apr 30, 2024
@p1gp1g
Copy link
Author

p1gp1g commented May 9, 2024

@mar-v-in What is missing for this PR to be merged ? I've seen you have changed the milestone but the PR is ready and nothing seems blocking.

Copy link
Member

@mar-v-in mar-v-in left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for not replying yet. I didn't have a lot of time to do testing yet, so this round of review is only on the code. While it looks as if it should work fine, I want to at least run this once before merging.


public class MultiConnectionKeeper {
private static final String TAG = "GmsMultiConKeeper";
private static final String PREF_MASTER = "GmsMultiConKeeper";
Copy link
Member

@mar-v-in mar-v-in May 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preference file name should be properly scoped to ensure there won't be conflicts with the app using the library. Use a name like org.microg.gms_connection instead. Also would be preferably to call the constant PREFERENCES_FILE_NAME or similar.


public class MultiConnectionKeeper {
private static final String TAG = "GmsMultiConKeeper";
private static final String PREF_MASTER = "GmsMultiConKeeper";
private static final String PREF_TARGET = "GmsMultiConKeeper_target";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preference name is already scoped through the file name (PREF_MASTER), so for readability I think it would be preferably to not prefix the preference name with the file name.

@@ -21,9 +21,11 @@
public class Constants {
public static final int GMS_VERSION_CODE = (BuildConfig.VERSION_CODE / 1000) * 1000;
public static final String GMS_PACKAGE_NAME = "com.google.android.gms";
public static final String MICROG_PACKAGE_NAME = "org.microg.gms";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to USER_MICROG_PACKAGE_NAME or similar.

SharedPreferences prefs = getSharedPreferences(FirstRunMaster, MODE_PRIVATE);
if (BuildConfig.APPLICATION_ID == Constants.MICROG_PACKAGE_NAME &&
prefs.getBoolean(FirstRunPref, true)) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use buildAlertDialog() from org.microg.gms.ui.buildAlertDialog (which will use Material design when applicable)

Comment on lines 28 to 29
private static final String FirstRunMaster = "FirstRun";
private static final String FirstRunPref = "FirstRun_pref";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constants are all caps

Comment on lines 28 to 29
private static final String FirstRunMaster = "FirstRun";
private static final String FirstRunPref = "FirstRun_pref";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, don't prefix the preference name with the file name

private static MultiConnectionKeeper INSTANCE;

private final Context context;

private final String gmsPackage;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

targetPackage would be a better name IMO.


private String getTargetPackage() {
SharedPreferences prefs = context.getSharedPreferences(PREF_MASTER, Context.MODE_PRIVATE);
final String SELF = "SELF";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of special handling for SELF / null, you can just use context.getPackageName() (both in the preference and returned from getTargetPackage() and extend isMicrog() to return true if the service's package name is the current package name (because the service won't be found inside the same package if it's not microG)

@p1gp1g p1gp1g force-pushed the feat/user_services branch 3 times, most recently from 29e662f to 0d911b5 Compare May 20, 2024 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants