From c0aff7dda13bf6056db914c3f451c8fcb7cb4e46 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 23 Jan 2017 02:38:56 +0800 Subject: [PATCH] Add signature checking to plugin platform --- mobile/src/main/res/values/strings.xml | 1 + .../shadowsocks/ProfileConfigFragment.scala | 2 + .../github/shadowsocks/plugin/Plugin.scala | 1 + .../shadowsocks/plugin/PluginManager.scala | 37 ++++++++++++++++++- .../shadowsocks/plugin/ResolvedPlugin.scala | 2 + 5 files changed, 41 insertions(+), 2 deletions(-) diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index 8e78ddf6..a8e902f9 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -142,5 +142,6 @@ Configure… Disabled Unknown plugin %s + Warning: This plugin does not seem to come from a known trusted source. Plugin: %s diff --git a/mobile/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala b/mobile/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala index d338ddbb..4e8f1aa6 100644 --- a/mobile/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala +++ b/mobile/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala @@ -81,6 +81,8 @@ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListe app.editor.putString(Key.plugin, pluginConfiguration.toString).putBoolean(Key.dirty, true).apply() pluginConfigure.setEnabled(!TextUtils.isEmpty(selected)) pluginConfigure.setText(pluginConfiguration.selectedOptions.toString) + if (!PluginManager.fetchPlugins()(selected).trusted) + Snackbar.make(getView, R.string.plugin_untrusted, Snackbar.LENGTH_LONG).show() true }) pluginConfigure.setOnPreferenceChangeListener(onPluginConfigureChanged) diff --git a/mobile/src/main/scala/com/github/shadowsocks/plugin/Plugin.scala b/mobile/src/main/scala/com/github/shadowsocks/plugin/Plugin.scala index 2017eb5c..f206034e 100644 --- a/mobile/src/main/scala/com/github/shadowsocks/plugin/Plugin.scala +++ b/mobile/src/main/scala/com/github/shadowsocks/plugin/Plugin.scala @@ -11,4 +11,5 @@ abstract class Plugin { def icon: Drawable = null def defaultConfig: String = null def packageName: String = null + def trusted: Boolean = true } diff --git a/mobile/src/main/scala/com/github/shadowsocks/plugin/PluginManager.scala b/mobile/src/main/scala/com/github/shadowsocks/plugin/PluginManager.scala index 00a66e7d..935a0006 100644 --- a/mobile/src/main/scala/com/github/shadowsocks/plugin/PluginManager.scala +++ b/mobile/src/main/scala/com/github/shadowsocks/plugin/PluginManager.scala @@ -2,11 +2,11 @@ package com.github.shadowsocks.plugin import java.io.{File, FileNotFoundException, FileOutputStream, IOException} -import android.content.pm.PackageManager +import android.content.pm.{PackageManager, Signature} import android.content.{BroadcastReceiver, ContentResolver, Intent} import android.net.Uri import android.os.Bundle -import android.util.Log +import android.util.{Base64, Log} import com.github.shadowsocks.ShadowsocksApplication.app import com.github.shadowsocks.utils.CloseUtils.autoClose import com.github.shadowsocks.utils.{Commandline, IOUtils} @@ -19,6 +19,39 @@ import scala.collection.mutable.ListBuffer * @author Mygod */ object PluginManager { + /** + * Trusted signatures by the app. Third-party fork should add their public key to their fork if the developer wishes + * to publish or has published plugins for this app. You can obtain your public key by executing: + * + * $ keytool -export -alias key-alias -keystore /path/to/keystore.jks -rfc + * + * If you don't plan to publish any plugin but is developing/has developed some, it's not necessary to add your + * public key yet since it will also automatically trust packages signed by the same signatures, e.g. debug keys. + */ + lazy val trustedSignatures: Set[Signature] = + app.getPackageManager.getPackageInfo(app.getPackageName, PackageManager.GET_SIGNATURES).signatures.toSet + + new Signature(Base64.decode( // @Mygod + """ + |MIIDWzCCAkOgAwIBAgIEUzfv8DANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJD + |TjEOMAwGA1UECBMFTXlnb2QxDjAMBgNVBAcTBU15Z29kMQ4wDAYDVQQKEwVNeWdv + |ZDEOMAwGA1UECxMFTXlnb2QxDjAMBgNVBAMTBU15Z29kMCAXDTE0MDUwMjA5MjQx + |OVoYDzMwMTMwOTAyMDkyNDE5WjBdMQswCQYDVQQGEwJDTjEOMAwGA1UECBMFTXln + |b2QxDjAMBgNVBAcTBU15Z29kMQ4wDAYDVQQKEwVNeWdvZDEOMAwGA1UECxMFTXln + |b2QxDjAMBgNVBAMTBU15Z29kMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC + |AQEAjm5ikHoP3w6zavvZU5bRo6Birz41JL/nZidpdww21q/G9APA+IiJMUeeocy0 + |L7/QY8MQZABVwNq79LXYWJBcmmFXM9xBPgDqQP4uh9JsvazCI9bvDiMn92mz9HiS + |Sg9V4KGg0AcY0r230KIFo7hz+2QBp1gwAAE97myBfA3pi3IzJM2kWsh4LWkKQMfL + |M6KDhpb4mdDQnHlgi4JWe3SYbLtpB6whnTqjHaOzvyiLspx1tmrb0KVxssry9KoX + |YQzl56scfE/QJX0jJ5qYmNAYRCb4PibMuNSGB2NObDabSOMAdT4JLueOcHZ/x9tw + |agGQ9UdymVZYzf8uqc+29ppKdQIDAQABoyEwHzAdBgNVHQ4EFgQUBK4uJ0cqmnho + |6I72VmOVQMvVCXowDQYJKoZIhvcNAQELBQADggEBABZQ3yNESQdgNJg+NRIcpF9l + |YSKZvrBZ51gyrC7/2ZKMpRIyXruUOIrjuTR5eaONs1E4HI/uA3xG1eeW2pjPxDnO + |zgM4t7EPH6QbzibihoHw1MAB/mzECzY8r11PBhDQlst0a2hp+zUNR8CLbpmPPqTY + |RSo6EooQ7+NBejOXysqIF1q0BJs8Y5s/CaTOmgbL7uPCkzArB6SS/hzXgDk5gw6v + |wkGeOtzcj1DlbUTvt1s5GlnwBTGUmkbLx+YUje+n+IBgMbohLUDYBtUHylRVgMsc + |1WS67kDqeJiiQZvrxvyW6CZZ/MIGI+uAkkj3DqJpaZirkwPgvpcOIrjZy0uFvQM= + """, Base64.DEFAULT)) + private var receiver: BroadcastReceiver = _ private var cachedPlugins: Map[String, Plugin] = _ def fetchPlugins(): Map[String, Plugin] = { diff --git a/mobile/src/main/scala/com/github/shadowsocks/plugin/ResolvedPlugin.scala b/mobile/src/main/scala/com/github/shadowsocks/plugin/ResolvedPlugin.scala index 21a3d0a5..ac23b390 100644 --- a/mobile/src/main/scala/com/github/shadowsocks/plugin/ResolvedPlugin.scala +++ b/mobile/src/main/scala/com/github/shadowsocks/plugin/ResolvedPlugin.scala @@ -19,4 +19,6 @@ abstract class ResolvedPlugin(resolveInfo: ResolveInfo, packageManager: PackageM override final lazy val icon: Drawable = resolveInfo.loadIcon(packageManager) override final lazy val defaultConfig: String = metaData.getString(PluginContract.METADATA_KEY_DEFAULT_CONFIG) override def packageName: String = resolveInfo.resolvePackageName + override final lazy val trusted: Boolean = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES) + .signatures.exists(PluginManager.trustedSignatures.contains) }