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

Support workspace accounts with basic device management #2296

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

Conversation

fynngodau
Copy link
Contributor

@fynngodau fynngodau commented Apr 3, 2024

  • Contains request code to Cryptauth enrollment that can satisfy the lockscreen requirement as to resolve the DeviceManagementScreenlockRequired error.
  • For manual testing, it is called when opening the account settings webview from the system account settings.
  • Provides true lockscreen status in query.
  • Automatically send query when all dependencies are met, and logs what is missing otherwise.

To do:

  • Show appropriate error messages when user intervention is needed.

Resolves #896. Resolves #1726. Likely resolves #1838.

@fynngodau fynngodau marked this pull request as draft April 3, 2024 23:04
@fynngodau fynngodau changed the title Support workspace accounts Support workspace accounts with basic device management Apr 3, 2024
@aximut
Copy link

aximut commented Apr 4, 2024

Just built your branch on GitHub actions. Using the debug build. Getting the following logcat when clicking on the account settings screen (only relevant parts filtered).
Basically a short spinner appears when clicking and then it returns back to the previous screen. No account settings displayed, and so far could not get the DeviceManagementScreenlockRequired to disappear.

Feels like your code does not get executed (or because the intent returns too fast the deferred part does not get executed), maybe an error on my end.

D AccountSettings: Invoked with com.google.android.gms.accountsettings.SECURITY_SETTINGS and extras Bundle[{account=Supplier{VAL_PARCELABLE@24+228}}]
W ziparchive: Unable to open '/data/app/.../com.google.android.gms-...==/base.dm': No such file or directory
I ActivityTaskManager: Displayed com.google.android.gms/org.microg.gms.accountsettings.ui.MainActivity: +135ms
D GmsHttpFormClient: -- Request --
D GmsHttpFormClient: androidId=...
...
W AccountSettingsWebView: Failed to get weblogin auth.
W AccountSettingsWebView: java.io.IOException: Error=DeviceManagementScreenlockRequired
W AccountSettingsWebView: 	at org.microg.gms.common.HttpFormClient.request(HttpFormClient.java:96)
W AccountSettingsWebView: 	at org.microg.gms.auth.AuthRequest.getResponse(AuthRequest.java:258)
W AccountSettingsWebView: 	at org.microg.gms.auth.AuthManager.requestAuth(AuthManager.java:293)
W AccountSettingsWebView: 	at org.microg.gms.accountsettings.ui.WebViewHelper.openWebWithAccount(WebViewHelper.kt:109)
W AccountSettingsWebView: 	at org.microg.gms.accountsettings.ui.WebViewHelper.access$openWebWithAccount(WebViewHelper.kt:32)
...
D CoreBackPreview: Window{f7e791 u0 Splash Screen com.google.android.gms EXITING}: Setting back callback null

From your code, I expect there should be a debug statement like:

D SyncKeysRequest: -- Request --
...JSON...

@fynngodau
Copy link
Contributor Author

@aximut Unfortunately I haven't been able to find the difference between your builds and my builds. On my end, it is working and outputs (for instance) the request as you expected:

SyncKeysRequest         com.google.android.gms               D  -- Request --
                                                                {"applicationName":"com.google.android.gms","clientVersion":"1.0.0","syncSingleKeyRequests":[{"keyName":"PublicKey","keyHandles":"ZGV2aWNlX2tleQo="}],"clientMetadata":{"invocationReason":"NEW_ACCOUNT"},"clientAppMetadata":[…]}

@aximut
Copy link

aximut commented Apr 6, 2024

Just started further investigation and apparently for me the function returns here:

        if (!it.containsKey(GcmConstants.EXTRA_REGISTRATION_ID)) {
            return null <--
        }

I just rebuilt the main apk (com.google.android.gms, not the com.android.vending one). Is there anything else I need to update? Or might this be a problem with my workspace account?

I added the debug output as in this file and this is the adb log:

D SyncKeysRequest: STARTING GRPC REQUEST PHASE 1
D SyncKeysRequest: STARTING GRPC REQUEST PHASE 2
W MSBackupRestore: Unsupported class loader
W MSBackupRestore: Unsupported class loader
E system_server: Invalid class loader spec: =UnsupportedClassLoaderContext=
E PackageDexUsage: Unsupported context?
W ziparchive: Unable to open '/data/app/~~...==/com.google.android.gms-...==/base.dm': No such file or directory
I ActivityTaskManager: Displayed com.google.android.gms/org.microg.gms.accountsettings.ui.MainActivity: +548ms

Not sure if the class loader issues are related or not.

@fynngodau
Copy link
Contributor Author

@aximut You will need to enable device checkin and Google Cloud Messaging before running the query. I will provide updates to get the functionality in a more use-ready state in the coming days.

@aximut
Copy link

aximut commented Apr 6, 2024

I have device registration and GCM both enabled. For device registration, I can see my Android ID in the microG app. GCM I set to "confirm new apps".

@fynngodau
Copy link
Contributor Author

fynngodau commented Apr 6, 2024

@aximut From the code being called, enabling to "confirm new apps" while querying for an app not yet contained in the database (microG itself) will also yield no result at this moment.

@aximut
Copy link

aximut commented Apr 6, 2024

Thanks! That was it. Now I see the code being fully executed.

D SyncKeysRequest: STARTING GRPC REQUEST PHASE 5
D SyncKeysRequest: STARTING GRPC REQUEST PHASE 6
D SyncKeysRequest: STARTING GRPC REQUEST PHASE 7
D SyncKeysRequest: -- Request --
D SyncKeysRequest: {"applicationName":"com.google.android.gms","clientVersion":"1.0.0","syncSingleKeyRequests":[{"keyName":"PublicKey","keyHandles":"ZGV2aWNlX2tleQo="}],"clientMetadata":{"invocationReason":"NEW_ACCOUNT"},"clientAppMetadata":"..."}

However, once I start a G app afterwards, I still get

D GmsHttpFormClient: -- Request --
D GmsHttpFormClient: androidId=...&app=com.google.android.apps.dynamite&client_sig=...
W GmsAuthManagerSvc: java.io.IOException: Error=DeviceManagementScreenlockRequired

For testing, I commented out all the rest of the web view code though, maybe that's the issue.
Otherwise I'd have to setup the MITM interception again to see more details.

@fynngodau
Copy link
Contributor Author

I've added functionality to automatically send the request if necessary (for instance, when a sync fails with DeviceManagementScreenlockRequired error) and then to re-send the original request if appropriate. If not all dependencies are met, it instead logs what is missing, like this:

GmsAccountErrorResolve  com.google.android.gms  W  User intervention required! You need to ENABLE_GCM, ALLOW_MICROG_GCM, ENABLE_LOCKSCREEN.

ALLOW_MICROG_GCM means what we discussed above (it fails if a user disabled GCM for microG specifically, or requested to manually allow new apps).

The next step will be to add UI to guide users to resolve these problems.


For testing, I commented out all the rest of the web view code though, maybe that's the issue.

@aximut It shouldn't be an issue. After sending the query (and waiting its 500ms additional delay), it should work to sync Google apps and also to load the webview, which would otherwise close immediately. I don't know what the problem could be in your case.

@aximut
Copy link

aximut commented Apr 6, 2024

Ok, then I'll try with the new version soon.

@fynngodau
Copy link
Contributor Author

It seems the instance token + instance ID we are getting from GCM in https://github.com/microg/GmsCore/pull/2296/files#diff-3341dd523d63f6a4b7f5b44b9db7075c9a834e139fdc0fa945aafbb342942cceR48-R75 is somehow bad. The query works with it (which seems to indicate we are going in the right direction, as using a different sender constant for getting the instance ID – or no instance ID at all – would lead to a failing cryptauth request), but DeviceManagementScreenlockRequired keeps occurring even though the answer looks normal.

The same request with an instance ID taken from original GMS from the emulator works and leads to the account fully functioning.

I couldn't find out how original Google Play services generate their instance ID.

@aximut
Copy link

aximut commented Apr 23, 2024

That would explain why the request went through on my device but I'm still facing the errors

@fynngodau
Copy link
Contributor Author

fynngodau commented May 7, 2024

For future investigation regarding the above problem:

  • Instance ID generation should be attempted at client-side, following the pattern in the play-services-iid implementation, specifically:
    byte[] digest = MessageDigest.getInstance("SHA1").digest(keyPair.getPublic().getEncoded());
    digest[0] = (byte) (112 + (0xF & digest[0]) & 0xFF);
    return Base64.encodeToString(digest, 0, 8, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
  • Google Play services sometimes request the scope DeviceKeyRequest with sender/subtype/subscription 121252257541. This sound relevant, but is always rejected by the server on grounds of being an Invalid scope.

@fynngodau
Copy link
Contributor Author

Seemingly good senders / subtypes:

More info:

  • 121252257541 is not even a valid sender
  • 302798585788 leads to a token that leads to the cryptauth request being rejected on the grounds of containing an invalid argument.

@fynngodau
Copy link
Contributor Author

It seems the instance token + instance ID we are getting from GCM in https://github.com/microg/GmsCore/pull/2296/files#diff-3341dd523d63f6a4b7f5b44b9db7075c9a834e139fdc0fa945aafbb342942cceR48-R75 is somehow bad.

The observations were correct, but the conclusion was false. Extensive tests show that:

  • The behavior of the server that we were observing where copying the instance ID from a device with original GMS is likely the result of a server-side bug where it gets confused because instance ID and Android ID are mismatched.
  • We need to complete the cryptauth flow by sending an EnrollKeys query, which constitutes the second half of the flow (as per https://github.com/chromium/chromium/blob/1de4545b5a22b1d290043b3bddd96a4cb3c730dd/chromeos/ash/services/device_sync/proto/cryptauth_enrollment.proto#L5-L17).
  • It is sufficient to send a syntactically correct, but semantically empty EnrollKeys query, as long as it belongs to the SyncKeys query (identified per a random session token), even though this means not following the server's directives (which would tell the device that it should generate a key).

An implementation will follow very soon.

Sync keys request is required for Google Workspace-enabled accounts that
mandate a screenlock to be enabled. Note that with this commit, a
working method is provided, but not yet called, and it also doesn't
contain the correct "screenlock enabled" value yet.
For manual testing only. Remove this commit
Logs which of the dependencies for setting up enterprise accounts are missing:

* Checkin enabled
* GCM enabled
* microG allowed to use GCM
* Lockscreen enabled
@aximut
Copy link

aximut commented Aug 3, 2024

* We need to complete the cryptauth flow by sending an EnrollKeys query, which constitutes the second half of the flow (as per https://github.com/chromium/chromium/blob/1de4545b5a22b1d290043b3bddd96a4cb3c730dd/chromeos/ash/services/device_sync/proto/cryptauth_enrollment.proto#L5-L17).

Ok, this matches with what I observed and wrote in #896 (comment)
Both requests were required when I tried it with the original app.

Interesting to hear that this is a bug on the server.

@fynngodau
Copy link
Contributor Author

Ok, this matches with what I observed and wrote in #896 (comment)
Both requests were required when I tried it with the original app.

Yes, the buggy behavior I described above really did point us in the wrong direction.

I have implemented the EnrollKeys step. @aximut If you want to try again, unlocking the device should now work with the latest changes on our basic-workspace branch.

@fynngodau fynngodau marked this pull request as ready for review August 4, 2024 11:10
@aximut
Copy link

aximut commented Aug 4, 2024

I have implemented the EnrollKeys step. @aximut If you want to try again, unlocking the device should now work with the latest changes on our basic-workspace branch.

@fynngodau You are AMAZING!!! 🎉🎉🎉🎉🎉

It is working. Everything is working. 🥳🥳🥳🥳🥳
I can't believe it.

Wow, I can't count the years for how long I have been dealing with shabby workarounds to this, using everything in browser.
Thank you so much!

So stoked. Made my day week month...

@mar-v-in mar-v-in added this to the 0.3.4 milestone Aug 22, 2024
@fynngodau
Copy link
Contributor Author

It seems the AccountManager already knows the logic of required user interaction, leaving the decision to the caller whether the required interaction should be done in the foreground immediately or whether to postpone it into a notification – which would be a good improvement.

When apps are instead using AuthManagerService, this wouldn't have any effect; however that service itself also seems to know of the same kind of logic. But if done at both places, like the account access permission prompt, we would further duplicate the code. I don't know if all this can be streamlined in some feasible way and don't want to make changes that have potential to break existing functionality.

There's an incomplete and preliminary attempt at moving towards returning an intent for the error case instead of posting to a notification ourselves at https://github.com/e-foundation/android_packages_apps_GmsCore/tree/basic-workspace-am-async. @mar-v-in what do you think?

@fynngodau
Copy link
Contributor Author

For everyone, here's a screencast of the ux flow: https://gitlab.e.foundation/-/project/177/uploads/dbd0dbf91679dfc8610249019c1dc3e7/ux-flow-basic-workspace.mp4 (21.6 MB)

  • I am already signed in to my account [email protected] and attempting to sync Google Calendar, which doesn't work as the account is not fully set up (i.e. the server does not know of any configured screenlock yet).
  • Google Play services would send a notification in this situation as well.
  • The flow guides me to create a screenlock. I choose pattern (creating the pattern itself is not shown on the video because Android prevents this from being visible on screenrecordings)
  • After completing the flow, syncing would work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants