From 6a00880f61757829708b06dcdd4fc7c1d8cc1701 Mon Sep 17 00:00:00 2001 From: kari-ts <135075563+kari-ts@users.noreply.github.com> Date: Wed, 10 Apr 2024 13:56:31 -0700 Subject: [PATCH] ui: port syspolicy handler code to new app (#304) * ui: port syspolicy handler code to new app port over https://github.com/tailscale/tailscale-android/pull/199 from cmd/tailscale and legacy_android to libtailscale and android/ Updates tailscale/corp#18202 Signed-off-by: kari-ts * android: PR suggestions for syspolicyHandler (#308) Updates tailscale/corp#18202 Signed-off-by: Percy Wegmann --------- Signed-off-by: kari-ts Signed-off-by: Percy Wegmann Co-authored-by: Percy Wegmann --- .../src/main/java/com/tailscale/ipn/App.kt | 15 ++++++ .../java/com/tailscale/ipn/mdm/MDMSettings.kt | 2 + libtailscale/interfaces.go | 6 +++ libtailscale/syspolicy_handler.go | 47 +++++++++++++++++++ libtailscale/tailscale.go | 2 + 5 files changed, 72 insertions(+) create mode 100644 libtailscale/syspolicy_handler.go diff --git a/android/src/main/java/com/tailscale/ipn/App.kt b/android/src/main/java/com/tailscale/ipn/App.kt index ae6ec07..ce20f33 100644 --- a/android/src/main/java/com/tailscale/ipn/App.kt +++ b/android/src/main/java/com/tailscale/ipn/App.kt @@ -37,6 +37,7 @@ import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey +import com.tailscale.ipn.mdm.MDMSettings import com.tailscale.ipn.ui.localapi.Client import com.tailscale.ipn.ui.localapi.Request import com.tailscale.ipn.ui.model.Ipn @@ -451,4 +452,18 @@ class App : Application(), libtailscale.AppContext { return downloads } + + @Throws(IOException::class, GeneralSecurityException::class) + override fun getSyspolicyBooleanValue(key: String): Boolean { + return getSyspolicyStringValue(key) == "true" + } + + @Throws(IOException::class, GeneralSecurityException::class) + override fun getSyspolicyStringValue(key: String): String { + return MDMSettings.allSettingsByKey[key]?.flow?.value?.toString() + ?: run { + Log.d("MDM", "$key is not defined on Android. Returning empty.") + "" + } + } } diff --git a/android/src/main/java/com/tailscale/ipn/mdm/MDMSettings.kt b/android/src/main/java/com/tailscale/ipn/mdm/MDMSettings.kt index 924eae4..24364c0 100644 --- a/android/src/main/java/com/tailscale/ipn/mdm/MDMSettings.kt +++ b/android/src/main/java/com/tailscale/ipn/mdm/MDMSettings.kt @@ -57,6 +57,8 @@ object MDMSettings { .map { it.call(MDMSettings) as MDMSetting<*> } } + val allSettingsByKey by lazy { allSettings.associateBy { it.key } } + fun update(app: App, restrictionsManager: RestrictionsManager?) { val bundle = restrictionsManager?.applicationRestrictions allSettings.forEach { it.setFrom(bundle, app) } diff --git a/libtailscale/interfaces.go b/libtailscale/interfaces.go index 2aa7bb3..cc17561 100644 --- a/libtailscale/interfaces.go +++ b/libtailscale/interfaces.go @@ -45,6 +45,12 @@ type AppContext interface { // GetPlatformDNSConfig gets a string representation of the current DNS // configuration. GetPlatformDNSConfig() string + + // GetSyspolicyStringValue returns the current string value for the given system policy. + GetSyspolicyStringValue(key string) (string, error) + + // GetSyspolicyBooleanValue returns whether the given system policy is enabled. + GetSyspolicyBooleanValue(key string) (bool, error) } // IPNService corresponds to our IPNService in Java. diff --git a/libtailscale/syspolicy_handler.go b/libtailscale/syspolicy_handler.go new file mode 100644 index 0000000..b7261b1 --- /dev/null +++ b/libtailscale/syspolicy_handler.go @@ -0,0 +1,47 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +package libtailscale + +import ( + "log" + + "tailscale.com/util/syspolicy" +) + +// syspolicyHandler is a syspolicy handler for the Android version of the Tailscale client, +// which lets the main networking code read values set via the Android RestrictionsManager. +type syspolicyHandler struct { + a *App +} + +func (h syspolicyHandler) ReadString(key string) (string, error) { + if key == "" { + return "", syspolicy.ErrNoSuchKey + } + retVal, err := h.a.appCtx.GetSyspolicyStringValue(key) + if err != nil { + log.Printf("syspolicy: failed to get string value via gomobile: %v", err) + } + return retVal, err +} + +func (h syspolicyHandler) ReadBoolean(key string) (bool, error) { + if key == "" { + return false, syspolicy.ErrNoSuchKey + } + retVal, err := h.a.appCtx.GetSyspolicyBooleanValue(key) + if err != nil { + log.Printf("syspolicy: failed to get bool value via gomobile: %v", err) + } + return retVal, err +} + +func (h syspolicyHandler) ReadUInt64(key string) (uint64, error) { + if key == "" { + return 0, syspolicy.ErrNoSuchKey + } + // TODO(angott): drop ReadUInt64 everywhere. We are not using it. + log.Fatalf("ReadUInt64 is not implemented on Android") + return 0, nil +} diff --git a/libtailscale/tailscale.go b/libtailscale/tailscale.go index 93d7a58..84af1a1 100644 --- a/libtailscale/tailscale.go +++ b/libtailscale/tailscale.go @@ -18,6 +18,7 @@ import ( "tailscale.com/types/logger" "tailscale.com/types/logid" "tailscale.com/util/clientmetric" + "tailscale.com/util/syspolicy" ) const defaultMTU = 1280 // minimalMTU from wgengine/userspace.go @@ -38,6 +39,7 @@ func newApp(dataDir, directFileRoot string, appCtx AppContext) Application { a.store = newStateStore(a.appCtx) interfaces.RegisterInterfaceGetter(a.getInterfaces) + syspolicy.RegisterHandler(syspolicyHandler{a: a}) go func() { defer func() { if p := recover(); p != nil {