diff --git a/app/src/full/AndroidManifest.xml b/app/src/full/AndroidManifest.xml index 6b5b170f3..d07e9c31b 100644 --- a/app/src/full/AndroidManifest.xml +++ b/app/src/full/AndroidManifest.xml @@ -47,12 +47,19 @@ + + diff --git a/app/src/full/java/a/p.java b/app/src/full/java/a/p.java new file mode 100644 index 000000000..479de2a43 --- /dev/null +++ b/app/src/full/java/a/p.java @@ -0,0 +1,7 @@ +package a; + +import com.topjohnwu.magisk.SuRequestActivity; + +public class p extends SuRequestActivity { + /* stub */ +} diff --git a/app/src/full/java/com/topjohnwu/magisk/Const.java b/app/src/full/java/com/topjohnwu/magisk/Const.java index fe49b1d8e..5630f064b 100644 --- a/app/src/full/java/com/topjohnwu/magisk/Const.java +++ b/app/src/full/java/com/topjohnwu/magisk/Const.java @@ -141,9 +141,6 @@ public class Const { public static final int NAMESPACE_MODE_ISOLATE = 2; public static final int NO_NOTIFICATION = 0; public static final int NOTIFICATION_TOAST = 1; - public static final int NOTIFY_NORMAL_LOG = 0; - public static final int NOTIFY_USER_TOASTS = 1; - public static final int NOTIFY_USER_TO_OWNER = 2; public static final int SU_PROMPT = 0; public static final int SU_AUTO_DENY = 1; public static final int SU_AUTO_ALLOW = 2; diff --git a/app/src/full/java/com/topjohnwu/magisk/Data.java b/app/src/full/java/com/topjohnwu/magisk/Data.java index c73e561a0..865b90c05 100644 --- a/app/src/full/java/com/topjohnwu/magisk/Data.java +++ b/app/src/full/java/com/topjohnwu/magisk/Data.java @@ -87,6 +87,7 @@ public class Data { classMap.put(OnBootService.class, a.m.class); classMap.put(UpdateCheckService.class, a.n.class); classMap.put(AboutCardRow.class, a.o.class); + classMap.put(SuRequestActivity.class, a.p.class); } diff --git a/app/src/full/java/com/topjohnwu/magisk/SuRequestActivity.java b/app/src/full/java/com/topjohnwu/magisk/SuRequestActivity.java new file mode 100644 index 000000000..b86c04253 --- /dev/null +++ b/app/src/full/java/com/topjohnwu/magisk/SuRequestActivity.java @@ -0,0 +1,258 @@ +package com.topjohnwu.magisk; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.hardware.fingerprint.FingerprintManager; +import android.net.LocalSocketAddress; +import android.os.Bundle; +import android.os.CountDownTimer; +import android.os.FileObserver; +import android.text.TextUtils; +import android.view.View; +import android.view.Window; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.Spinner; +import android.widget.TextView; + +import com.topjohnwu.magisk.components.BaseActivity; +import com.topjohnwu.magisk.container.Policy; +import com.topjohnwu.magisk.utils.FingerprintHelper; +import com.topjohnwu.magisk.utils.SuConnector; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import androidx.annotation.Nullable; + +public class SuRequestActivity extends BaseActivity { + LinearLayout suPopup; + Spinner timeout; + ImageView appIcon; + TextView appNameView; + TextView packageNameView; + Button grant_btn; + Button deny_btn; + ImageView fingerprintImg; + TextView warning; + + private SuConnector connector; + private Policy policy; + private CountDownTimer timer; + private FingerprintHelper fingerprintHelper; + + class SuConnectorV1 extends SuConnector { + + SuConnectorV1(String name) throws IOException { + socket.connect(new LocalSocketAddress(name, LocalSocketAddress.Namespace.FILESYSTEM)); + new FileObserver(name) { + @Override + public void onEvent(int fileEvent, String path) { + if (fileEvent == FileObserver.DELETE_SELF) { + finish(); + } + } + }.startWatching(); + } + + @Override + public void response() { + try (OutputStream out = getOutputStream()) { + out.write((policy.policy == Policy.ALLOW ? "socket:ALLOW" : "socket:DENY").getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + class SuConnectorV2 extends SuConnector { + + SuConnectorV2(String name) throws IOException { + socket.connect(new LocalSocketAddress(name, LocalSocketAddress.Namespace.ABSTRACT)); + } + + @Override + public void response() { + try (DataOutputStream out = getOutputStream()) { + out.writeInt(policy.policy); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @Override + public int getDarkTheme() { + return R.style.SuRequest_Dark; + } + + @Override + public void finish() { + if (timer != null) + timer.cancel(); + if (fingerprintHelper != null) + fingerprintHelper.cancel(); + super.finish(); + } + + @Override + public void onBackPressed() { + if (policy != null) { + handleAction(Policy.DENY); + } else { + finish(); + } + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + supportRequestWindowFeature(Window.FEATURE_NO_TITLE); + + PackageManager pm = getPackageManager(); + mm.mDB.clearOutdated(); + + // Get policy + Intent intent = getIntent(); + try { + connector = intent.getIntExtra("version", 1) == 1 ? + new SuConnectorV1(intent.getStringExtra("socket")) : + new SuConnectorV2(intent.getStringExtra("socket")); + Bundle bundle = connector.readSocketInput(); + int uid = Integer.parseInt(bundle.getString("uid")); + policy = mm.mDB.getPolicy(uid); + if (policy == null) { + policy = new Policy(uid, pm); + } + } catch (IOException | PackageManager.NameNotFoundException e) { + e.printStackTrace(); + finish(); + return; + } + + // Never allow com.topjohnwu.magisk (could be malware) + if (TextUtils.equals(policy.packageName, Const.ORIG_PKG_NAME)) { + finish(); + return; + } + + switch (Data.suResponseType) { + case Const.Value.SU_AUTO_DENY: + handleAction(Policy.DENY, 0); + return; + case Const.Value.SU_AUTO_ALLOW: + handleAction(Policy.ALLOW, 0); + return; + case Const.Value.SU_PROMPT: + default: + } + + // If not interactive, response directly + if (policy.policy != Policy.INTERACTIVE) { + handleAction(); + return; + } + + setContentView(R.layout.activity_request); + ViewBinder.bind(this); + + appIcon.setImageDrawable(policy.info.loadIcon(pm)); + appNameView.setText(policy.appName); + packageNameView.setText(policy.packageName); + + ArrayAdapter adapter = ArrayAdapter.createFromResource(this, + R.array.allow_timeout, android.R.layout.simple_spinner_item); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + timeout.setAdapter(adapter); + + timer = new CountDownTimer(Data.suRequestTimeout * 1000, 1000) { + @Override + public void onTick(long millisUntilFinished) { + deny_btn.setText(getString(R.string.deny_with_str, "(" + millisUntilFinished / 1000 + ")")); + } + @Override + public void onFinish() { + deny_btn.setText(getString(R.string.deny_with_str, "(0)")); + handleAction(Policy.DENY); + } + }; + + boolean useFingerprint = Data.suFingerprint && FingerprintHelper.canUseFingerprint(); + + if (useFingerprint) { + try { + fingerprintHelper = new FingerprintHelper() { + @Override + public void onAuthenticationError(int errorCode, CharSequence errString) { + warning.setText(errString); + } + + @Override + public void onAuthenticationHelp(int helpCode, CharSequence helpString) { + warning.setText(helpString); + } + + @Override + public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { + handleAction(Policy.ALLOW); + } + + @Override + public void onAuthenticationFailed() { + warning.setText(R.string.auth_fail); + } + }; + fingerprintHelper.authenticate(); + } catch (Exception e) { + e.printStackTrace(); + useFingerprint = false; + } + } + + if (!useFingerprint) { + grant_btn.setOnClickListener(v -> { + handleAction(Policy.ALLOW); + timer.cancel(); + }); + grant_btn.requestFocus(); + } + + grant_btn.setVisibility(useFingerprint ? View.GONE : View.VISIBLE); + fingerprintImg.setVisibility(useFingerprint ? View.VISIBLE : View.GONE); + + deny_btn.setOnClickListener(v -> { + handleAction(Policy.DENY); + timer.cancel(); + }); + suPopup.setOnClickListener(v -> cancelTimeout()); + timeout.setOnTouchListener((v, event) -> cancelTimeout()); + timer.start(); + } + + private boolean cancelTimeout() { + timer.cancel(); + deny_btn.setText(getString(R.string.deny)); + return false; + } + + private void handleAction() { + connector.response(); + finish(); + } + + private void handleAction(int action) { + handleAction(action, Const.Value.timeoutList[timeout.getSelectedItemPosition()]); + } + + private void handleAction(int action, int time) { + policy.policy = action; + if (time >= 0) { + policy.until = (time == 0) ? 0 : (System.currentTimeMillis() / 1000 + time * 60); + mm.mDB.addPolicy(policy); + } + handleAction(); + } +} diff --git a/app/src/full/java/com/topjohnwu/magisk/ViewBinder.java b/app/src/full/java/com/topjohnwu/magisk/ViewBinder.java index 0d5f2c9dd..ecaf39dd1 100644 --- a/app/src/full/java/com/topjohnwu/magisk/ViewBinder.java +++ b/app/src/full/java/com/topjohnwu/magisk/ViewBinder.java @@ -18,7 +18,6 @@ import com.topjohnwu.magisk.fragments.ModulesFragment; import com.topjohnwu.magisk.fragments.ReposFragment; import com.topjohnwu.magisk.fragments.SuLogFragment; import com.topjohnwu.magisk.fragments.SuperuserFragment; -import com.topjohnwu.magisk.superuser.RequestActivity; import androidx.core.content.ContextCompat; @@ -56,8 +55,8 @@ public class ViewBinder { target.findViewById(R.id.no_thanks).setOnClickListener(v -> target.finish()); target.findViewById(R.id.save_logs).setOnClickListener(v -> target.saveLogs()); } - - public static void bind(RequestActivity target) { + + public static void bind(SuRequestActivity target) { target.suPopup = target.findViewById(R.id.su_popup); target.timeout = target.findViewById(R.id.timeout); target.appIcon = target.findViewById(R.id.app_icon); diff --git a/app/src/full/java/com/topjohnwu/magisk/receivers/BootReceiver.java b/app/src/full/java/com/topjohnwu/magisk/receivers/BootReceiver.java index 11bb65b5f..75bf33616 100644 --- a/app/src/full/java/com/topjohnwu/magisk/receivers/BootReceiver.java +++ b/app/src/full/java/com/topjohnwu/magisk/receivers/BootReceiver.java @@ -5,14 +5,31 @@ import android.content.Context; import android.content.Intent; import android.text.TextUtils; -import a.m; +import com.topjohnwu.magisk.Data; +import com.topjohnwu.magisk.SuRequestActivity; +import com.topjohnwu.magisk.services.OnBootService; +import com.topjohnwu.magisk.utils.SuConnector; public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - if (TextUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED)) - m.enqueueWork(context); + if (TextUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED)) { + switch (intent.getExtras().getString("action", "boot")) { + case "request": + Intent i = new Intent(context, Data.classMap.get(SuRequestActivity.class)) + .putExtra("socket", intent.getStringExtra("socket")) + .putExtra("version", 2) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(i); + break; + case "log": + SuConnector.handleLogs(intent, 2); + break; + case "boot": + OnBootService.enqueueWork(context); + break; + } + } } - } diff --git a/app/src/full/java/com/topjohnwu/magisk/superuser/RequestActivity.java b/app/src/full/java/com/topjohnwu/magisk/superuser/RequestActivity.java index 7dbedb57f..3f8a2cbde 100644 --- a/app/src/full/java/com/topjohnwu/magisk/superuser/RequestActivity.java +++ b/app/src/full/java/com/topjohnwu/magisk/superuser/RequestActivity.java @@ -1,295 +1,21 @@ package com.topjohnwu.magisk.superuser; -import android.content.ContentValues; import android.content.Intent; -import android.content.pm.PackageManager; -import android.hardware.fingerprint.FingerprintManager; -import android.net.LocalSocket; -import android.net.LocalSocketAddress; import android.os.Bundle; -import android.os.CountDownTimer; -import android.os.FileObserver; -import android.text.TextUtils; -import android.view.View; -import android.view.Window; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.Spinner; -import android.widget.TextView; -import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Data; -import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.ViewBinder; -import com.topjohnwu.magisk.asyncs.ParallelTask; +import com.topjohnwu.magisk.SuRequestActivity; import com.topjohnwu.magisk.components.BaseActivity; -import com.topjohnwu.magisk.container.Policy; -import com.topjohnwu.magisk.utils.FingerprintHelper; - -import java.io.DataInputStream; -import java.io.IOException; public class RequestActivity extends BaseActivity { - public LinearLayout suPopup; - public Spinner timeout; - public ImageView appIcon; - public TextView appNameView; - public TextView packageNameView; - public Button grant_btn; - public Button deny_btn; - public ImageView fingerprintImg; - public TextView warning; - - private String socketPath; - private LocalSocket socket; - private PackageManager pm; - - private boolean hasTimeout; - private Policy policy; - private CountDownTimer timer; - private FingerprintHelper fingerprintHelper; - - @Override - public int getDarkTheme() { - return R.style.SuRequest_Dark; - } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - supportRequestWindowFeature(Window.FEATURE_NO_TITLE); - - pm = getPackageManager(); - mm.mDB.clearOutdated(); - - Intent intent = getIntent(); - socketPath = intent.getStringExtra("socket"); - hasTimeout = intent.getBooleanExtra("timeout", true); - - new FileObserver(socketPath) { - @Override - public void onEvent(int fileEvent, String path) { - if (fileEvent == FileObserver.DELETE_SELF) { - finish(); - } - } - }.startWatching(); - - new SocketManager(this).exec(); - } - - @Override - public void finish() { - if (timer != null) - timer.cancel(); - if (fingerprintHelper != null) - fingerprintHelper.cancel(); - super.finish(); - } - - private boolean cancelTimeout() { - timer.cancel(); - deny_btn.setText(getString(R.string.deny)); - return false; - } - - private void showRequest() { - switch (Data.suResponseType) { - case Const.Value.SU_AUTO_DENY: - handleAction(Policy.DENY, 0); - return; - case Const.Value.SU_AUTO_ALLOW: - handleAction(Policy.ALLOW, 0); - return; - case Const.Value.SU_PROMPT: - default: - } - - // If not interactive, response directly - if (policy.policy != Policy.INTERACTIVE) { - handleAction(); - return; - } - - setContentView(R.layout.activity_request); - ViewBinder.bind(this); - - appIcon.setImageDrawable(policy.info.loadIcon(pm)); - appNameView.setText(policy.appName); - packageNameView.setText(policy.packageName); - - ArrayAdapter adapter = ArrayAdapter.createFromResource(this, - R.array.allow_timeout, android.R.layout.simple_spinner_item); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - timeout.setAdapter(adapter); - - timer = new CountDownTimer(Data.suRequestTimeout * 1000, 1000) { - @Override - public void onTick(long millisUntilFinished) { - deny_btn.setText(getString(R.string.deny_with_str, "(" + millisUntilFinished / 1000 + ")")); - } - @Override - public void onFinish() { - deny_btn.setText(getString(R.string.deny_with_str, "(0)")); - handleAction(Policy.DENY); - } - }; - - boolean useFingerprint = Data.suFingerprint && FingerprintHelper.canUseFingerprint(); - - if (useFingerprint) { - try { - fingerprintHelper = new FingerprintHelper() { - @Override - public void onAuthenticationError(int errorCode, CharSequence errString) { - warning.setText(errString); - } - - @Override - public void onAuthenticationHelp(int helpCode, CharSequence helpString) { - warning.setText(helpString); - } - - @Override - public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { - handleAction(Policy.ALLOW); - } - - @Override - public void onAuthenticationFailed() { - warning.setText(R.string.auth_fail); - } - }; - fingerprintHelper.authenticate(); - } catch (Exception e) { - e.printStackTrace(); - useFingerprint = false; - } - } - - if (!useFingerprint) { - grant_btn.setOnClickListener(v -> { - handleAction(Policy.ALLOW); - timer.cancel(); - }); - grant_btn.requestFocus(); - } - - grant_btn.setVisibility(useFingerprint ? View.GONE : View.VISIBLE); - fingerprintImg.setVisibility(useFingerprint ? View.VISIBLE : View.GONE); - - deny_btn.setOnClickListener(v -> { - handleAction(Policy.DENY); - timer.cancel(); - }); - suPopup.setOnClickListener(v -> cancelTimeout()); - timeout.setOnTouchListener((v, event) -> cancelTimeout()); - - if (hasTimeout) { - timer.start(); - } else { - cancelTimeout(); - } - } - - @Override - public void onBackPressed() { - if (policy != null) { - handleAction(Policy.DENY); - } else { - finish(); - } - } - - void handleAction() { - String response; - if (policy.policy == Policy.ALLOW) { - response = "socket:ALLOW"; - } else { - response = "socket:DENY"; - } - try { - socket.getOutputStream().write((response).getBytes()); - socket.close(); - } catch (IOException e) { - e.printStackTrace(); - } + Intent intent = new Intent(this, Data.classMap.get(SuRequestActivity.class)) + .putExtra("socket", getIntent().getStringExtra("socket")) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); finish(); } - - void handleAction(int action) { - handleAction(action, Const.Value.timeoutList[timeout.getSelectedItemPosition()]); - } - - void handleAction(int action, int time) { - policy.policy = action; - if (time >= 0) { - policy.until = (time == 0) ? 0 : (System.currentTimeMillis() / 1000 + time * 60); - mm.mDB.addPolicy(policy); - } - handleAction(); - } - - private class SocketManager extends ParallelTask { - - SocketManager(BaseActivity context) { - super(context); - } - - @Override - protected Boolean doInBackground(Void... params) { - try { - socket = new LocalSocket(); - socket.connect(new LocalSocketAddress(socketPath, LocalSocketAddress.Namespace.FILESYSTEM)); - - DataInputStream is = new DataInputStream(socket.getInputStream()); - ContentValues payload = new ContentValues(); - - while (true) { - int nameLen = is.readInt(); - byte[] nameBytes = new byte[nameLen]; - is.readFully(nameBytes); - String name = new String(nameBytes); - if (TextUtils.equals(name, "eof")) - break; - - int dataLen = is.readInt(); - byte[] dataBytes = new byte[dataLen]; - is.readFully(dataBytes); - String data = new String(dataBytes); - payload.put(name, data); - } - - if (payload.getAsInteger("uid") == null) { - return false; - } - - int uid = payload.getAsInteger("uid"); - policy = mm.mDB.getPolicy(uid); - if (policy == null) { - policy = new Policy(uid, pm); - } - - /* Never allow com.topjohnwu.magisk (could be malware) */ - if (TextUtils.equals(policy.packageName, Const.ORIG_PKG_NAME)) - return false; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - return true; - } - - @Override - protected void onPostExecute(Boolean result) { - if (result) { - showRequest(); - } else { - finish(); - } - } - } } diff --git a/app/src/full/java/com/topjohnwu/magisk/superuser/SuReceiver.java b/app/src/full/java/com/topjohnwu/magisk/superuser/SuReceiver.java index 5b055db3d..fe7198e68 100644 --- a/app/src/full/java/com/topjohnwu/magisk/superuser/SuReceiver.java +++ b/app/src/full/java/com/topjohnwu/magisk/superuser/SuReceiver.java @@ -3,88 +3,13 @@ package com.topjohnwu.magisk.superuser; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; -import android.os.Process; -import android.widget.Toast; -import com.topjohnwu.magisk.Const; -import com.topjohnwu.magisk.Data; -import com.topjohnwu.magisk.MagiskManager; -import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.container.Policy; -import com.topjohnwu.magisk.container.SuLogEntry; -import com.topjohnwu.magisk.utils.Utils; - -import java.util.Date; +import com.topjohnwu.magisk.utils.SuConnector; public class SuReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - int fromUid, toUid, pid, mode; - String command, action; - Policy policy; - - MagiskManager mm = Data.MM(); - - if (intent == null) return; - - mode = intent.getIntExtra("mode", -1); - if (mode < 0) return; - - if (mode == Const.Value.NOTIFY_USER_TO_OWNER) { - Utils.toast(R.string.multiuser_hint_owner_request, Toast.LENGTH_LONG); - return; - } - - fromUid = intent.getIntExtra("from.uid", -1); - if (fromUid < 0) return; - if (fromUid == Process.myUid()) return; // Don't show anything if it's Magisk Manager - - action = intent.getStringExtra("action"); - if (action == null) return; - - policy = mm.mDB.getPolicy(fromUid); - if (policy == null) { - try { - policy = new Policy(fromUid, mm.getPackageManager()); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - return; - } - } - - SuLogEntry log = new SuLogEntry(policy); - - String message; - switch (action) { - case "allow": - message = mm.getString(R.string.su_allow_toast, policy.appName); - log.action = true; - break; - case "deny": - message = mm.getString(R.string.su_deny_toast, policy.appName); - log.action = false; - break; - default: - return; - } - - if (policy.notification && Data.suNotificationType == Const.Value.NOTIFICATION_TOAST) - Utils.toast(message, Toast.LENGTH_SHORT); - - if (mode == Const.Value.NOTIFY_NORMAL_LOG && policy.logging) { - toUid = intent.getIntExtra("to.uid", -1); - if (toUid < 0) return; - pid = intent.getIntExtra("pid", -1); - if (pid < 0) return; - command = intent.getStringExtra("command"); - if (command == null) return; - log.toUid = toUid; - log.fromPid = pid; - log.command = command; - log.date = new Date(); - mm.mDB.addLog(log); - } + SuConnector.handleLogs(intent, 1); } } diff --git a/app/src/full/java/com/topjohnwu/magisk/utils/SuConnector.java b/app/src/full/java/com/topjohnwu/magisk/utils/SuConnector.java new file mode 100644 index 000000000..408aa35cf --- /dev/null +++ b/app/src/full/java/com/topjohnwu/magisk/utils/SuConnector.java @@ -0,0 +1,118 @@ +package com.topjohnwu.magisk.utils; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.LocalSocket; +import android.os.Bundle; +import android.os.Process; +import android.text.TextUtils; +import android.widget.Toast; + +import com.topjohnwu.magisk.Const; +import com.topjohnwu.magisk.Data; +import com.topjohnwu.magisk.MagiskManager; +import com.topjohnwu.magisk.R; +import com.topjohnwu.magisk.container.Policy; +import com.topjohnwu.magisk.container.SuLogEntry; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Date; + +public abstract class SuConnector { + + protected LocalSocket socket = new LocalSocket(); + + private String readString(DataInputStream is) throws IOException { + int len = is.readInt(); + byte[] buf = new byte[len]; + is.readFully(buf); + return new String(buf); + } + + public Bundle readSocketInput() throws IOException { + Bundle bundle = new Bundle(); + DataInputStream is = new DataInputStream(socket.getInputStream()); + while (true) { + String name = readString(is); + if (TextUtils.equals(name, "eof")) + break; + bundle.putString(name, readString(is)); + } + return bundle; + } + + protected DataOutputStream getOutputStream() throws IOException { + return new DataOutputStream(socket.getOutputStream()); + } + + public abstract void response(); + + public static void handleLogs(Intent intent, int version) { + MagiskManager mm = Data.MM(); + + if (intent == null) return; + + int fromUid = intent.getIntExtra("from.uid", -1); + if (fromUid < 0) return; + if (fromUid == Process.myUid()) return; + + Policy policy = mm.mDB.getPolicy(fromUid); + if (policy == null) { + try { + policy = new Policy(fromUid, mm.getPackageManager()); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + return; + } + } + + SuLogEntry log = new SuLogEntry(policy); + if (version == 1) { + String action = intent.getStringExtra("action"); + if (action == null) return; + switch (action) { + case "allow": + log.action = true; + break; + case "deny": + log.action = false; + break; + default: + return; + } + } else { + switch (intent.getIntExtra("policy", -1)) { + case Policy.ALLOW: + log.action = true; + break; + case Policy.DENY: + log.action = false; + break; + default: + return; + } + } + + String message = mm.getString(log.action ? + R.string.su_allow_toast : R.string.su_deny_toast, policy.appName); + + if (policy.notification && Data.suNotificationType == Const.Value.NOTIFICATION_TOAST) + Utils.toast(message, Toast.LENGTH_SHORT); + + if (policy.logging) { + int toUid = intent.getIntExtra("to.uid", -1); + if (toUid < 0) return; + int pid = intent.getIntExtra("pid", -1); + if (pid < 0) return; + String command = intent.getStringExtra("command"); + if (command == null) return; + log.toUid = toUid; + log.fromPid = pid; + log.command = command; + log.date = new Date(); + mm.mDB.addLog(log); + } + } +} diff --git a/app/src/full/res/layout/activity_request.xml b/app/src/full/res/layout/activity_request.xml index 10fa98672..d0348dd81 100644 --- a/app/src/full/res/layout/activity_request.xml +++ b/app/src/full/res/layout/activity_request.xml @@ -3,7 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/su_popup" - tools:context=".superuser.RequestActivity" + tools:context=".SuRequestActivity" android:layout_height="wrap_content" android:layout_width="wrap_content" android:minWidth="350dp" diff --git a/native/jni/Android.mk b/native/jni/Android.mk index ef01795f3..263317ed9 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -52,10 +52,9 @@ LOCAL_SRC_FILES := \ resetprop/system_property_api.cpp \ resetprop/system_property_set.cpp \ su/su.c \ - su/activity.c \ + su/connect.c \ su/pts.c \ su/su_daemon.c \ - su/su_socket.c \ utils/img.c \ $(COMMON_UTILS) diff --git a/native/jni/core/daemon.c b/native/jni/core/daemon.c index a42054206..9560b40a6 100644 --- a/native/jni/core/daemon.c +++ b/native/jni/core/daemon.c @@ -111,9 +111,9 @@ void main_daemon() { check_and_start_logger(); struct sockaddr_un sun; - fd = setup_socket(&sun, MAIN_DAEMON); - - if (xbind(fd, (struct sockaddr*) &sun, sizeof(sun.sun_family) + strlen(sun.sun_path + 1) + 1)) + socklen_t len = setup_sockaddr(&sun, MAIN_DAEMON); + fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (xbind(fd, (struct sockaddr*) &sun, len)) exit(1); xlisten(fd, 10); LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n"); @@ -148,8 +148,8 @@ void main_daemon() { /* Connect the daemon, set sockfd, and return if new daemon is spawned */ int connect_daemon2(daemon_t d, int *sockfd) { struct sockaddr_un sun; - *sockfd = setup_socket(&sun, d); - socklen_t len = sizeof(sun.sun_family) + strlen(sun.sun_path + 1) + 1; + socklen_t len = setup_sockaddr(&sun, d); + *sockfd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); if (connect(*sockfd, (struct sockaddr*) &sun, len)) { if (getuid() != UID_ROOT || getgid() != UID_ROOT) { fprintf(stderr, "No daemon is currently running!\n"); diff --git a/native/jni/core/log_daemon.c b/native/jni/core/log_daemon.c index fbfd11a76..bf0594fa4 100644 --- a/native/jni/core/log_daemon.c +++ b/native/jni/core/log_daemon.c @@ -123,8 +123,9 @@ int check_and_start_logger() { void log_daemon() { setsid(); struct sockaddr_un sun; - sockfd = setup_socket(&sun, LOG_DAEMON); - if (xbind(sockfd, (struct sockaddr*) &sun, sizeof(sun.sun_family) + strlen(sun.sun_path + 1) + 1)) + socklen_t len = setup_sockaddr(&sun, LOG_DAEMON); + sockfd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (xbind(sockfd, (struct sockaddr*) &sun, len)) exit(1); xlisten(sockfd, 10); LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") logger started\n"); diff --git a/native/jni/core/socket.c b/native/jni/core/socket.c index 8c93d78b8..23673c334 100644 --- a/native/jni/core/socket.c +++ b/native/jni/core/socket.c @@ -2,18 +2,18 @@ */ #include +#include #include "daemon.h" #include "logging.h" #include "utils.h" #include "magisk.h" -/* Setup the address and return socket fd */ -int setup_socket(struct sockaddr_un *sun, daemon_t d) { - int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); +#define ABS_SOCKET_LEN(sun) (sizeof(sun->sun_family) + strlen(sun->sun_path + 1) + 1) + +socklen_t setup_sockaddr(struct sockaddr_un *sun, daemon_t d) { memset(sun, 0, sizeof(*sun)); sun->sun_family = AF_LOCAL; - sun->sun_path[0] = '\0'; const char *name; switch (d) { case MAIN_DAEMON: @@ -24,9 +24,39 @@ int setup_socket(struct sockaddr_un *sun, daemon_t d) { break; } strcpy(sun->sun_path + 1, name); + return ABS_SOCKET_LEN(sun); +} + +int create_rand_socket(struct sockaddr_un *sun) { + memset(sun, 0, sizeof(*sun)); + sun->sun_family = AF_LOCAL; + gen_rand_str(sun->sun_path + 1, 9); + int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + xbind(fd, (struct sockaddr*) sun, ABS_SOCKET_LEN(sun)); + xlisten(fd, 1); return fd; } +int socket_accept(int serv_fd, int timeout) { + struct timeval tv; + fd_set fds; + int rc; + + tv.tv_sec = timeout; + tv.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(serv_fd, &fds); + do { + rc = select(serv_fd + 1, &fds, NULL, NULL, &tv); + } while (rc < 0 && errno == EINTR); + if (rc < 1) { + PLOGE("select"); + exit(-1); + } + + return xaccept4(serv_fd, NULL, NULL, SOCK_CLOEXEC); +} + /* * Receive a file descriptor from a Unix socket. * Contributed by @mkasick @@ -136,26 +166,59 @@ int read_int(int fd) { return val; } +int read_int_be(int fd) { + uint32_t val; + xxread(fd, &val, sizeof(val)); + return ntohl(val); +} + void write_int(int fd, int val) { if (fd < 0) return; xwrite(fd, &val, sizeof(int)); } -char* read_string(int fd) { - int len = read_int(fd); - if (len > PATH_MAX || len < 0) { - LOGE("invalid string length %d", len); - exit(1); - } +void write_int_be(int fd, int val) { + uint32_t nl = htonl(val); + xwrite(fd, &nl, sizeof(nl)); +} + +static char *rd_str(int fd, int len) { char* val = xmalloc(sizeof(char) * (len + 1)); xxread(fd, val, len); val[len] = '\0'; return val; } -void write_string(int fd, const char* val) { +char* read_string(int fd) { + int len = read_int(fd); + return rd_str(fd, len); +} + +char* read_string_be(int fd) { + int len = read_int_be(fd); + return rd_str(fd, len); +} + +void write_string(int fd, const char *val) { if (fd < 0) return; int len = strlen(val); write_int(fd, len); xwrite(fd, val, len); } + +void write_string_be(int fd, const char *val) { + int len = strlen(val); + write_int_be(fd, len); + xwrite(fd, val, len); +} + +void write_key_value(int fd, const char *key, const char *val) { + write_string_be(fd, key); + write_string_be(fd, val); +} + +void write_key_token(int fd, const char *key, int tok) { + char val[16]; + sprintf(val, "%d", tok); + write_key_value(fd, key, val); +} diff --git a/native/jni/include/daemon.h b/native/jni/include/daemon.h index d93f5f8ad..0d20ef223 100644 --- a/native/jni/include/daemon.h +++ b/native/jni/include/daemon.h @@ -59,13 +59,21 @@ int check_and_start_logger(); // socket.c -int setup_socket(struct sockaddr_un *sun, daemon_t d); +socklen_t setup_sockaddr(struct sockaddr_un *sun, daemon_t d); +int create_rand_socket(struct sockaddr_un *sun); +int socket_accept(int serv_fd, int timeout); int recv_fd(int sockfd); void send_fd(int sockfd, int fd); int read_int(int fd); +int read_int_be(int fd); void write_int(int fd, int val); -char* read_string(int fd); -void write_string(int fd, const char* val); +void write_int_be(int fd, int val); +char *read_string(int fd); +char *read_string_be(int fd); +void write_string(int fd, const char *val); +void write_string_be(int fd, const char *val); +void write_key_value(int fd, const char *key, const char *val); +void write_key_token(int fd, const char *key, int tok); /*************** * Boot Stages * diff --git a/native/jni/su/activity.c b/native/jni/su/activity.c deleted file mode 100644 index 9058fa1b1..000000000 --- a/native/jni/su/activity.c +++ /dev/null @@ -1,141 +0,0 @@ -/* -** Copyright 2017, John Wu (@topjohnwu) -** Copyright 2010, Adam Shanks (@ChainsDD) -** Copyright 2008, Zinx Verituse (@zinxv) -** -*/ - -#include -#include -#include -#include -#include -#include - -#include "magisk.h" -#include "su.h" - -/* intent actions */ -#define ACTION_REQUEST "%s/" REQUESTOR_PREFIX ".RequestActivity" -#define ACTION_RESULT "%s/" REQUESTOR_PREFIX ".SuReceiver" - -#define AM_PATH "/system/bin/app_process", "/system/bin", "com.android.commands.am.Am" - -static char *get_command(const struct su_request *to) { - if (to->command) - return to->command; - if (to->shell) - return to->shell; - return DEFAULT_SHELL; -} - -static void silent_run(char* const args[]) { - set_identity(0); - if (fork()) - return; - int zero = open("/dev/zero", O_RDONLY | O_CLOEXEC); - dup2(zero, 0); - int null = open("/dev/null", O_WRONLY | O_CLOEXEC); - dup2(null, 1); - dup2(null, 2); - setenv("CLASSPATH", "/system/framework/am.jar", 1); - execv(args[0], args); - PLOGE("exec am"); - _exit(EXIT_FAILURE); -} - -static int setup_user(struct su_context *ctx, char* user) { - switch (ctx->info->dbs.v[SU_MULTIUSER_MODE]) { - case MULTIUSER_MODE_OWNER_ONLY: /* Should already be denied if not owner */ - case MULTIUSER_MODE_OWNER_MANAGED: - sprintf(user, "%d", 0); - return ctx->info->uid / 100000; - case MULTIUSER_MODE_USER: - sprintf(user, "%d", ctx->info->uid / 100000); - break; - } - return 0; -} - -void app_send_result(struct su_context *ctx, policy_t policy) { - char fromUid[16]; - if (ctx->info->dbs.v[SU_MULTIUSER_MODE] == MULTIUSER_MODE_OWNER_MANAGED) - sprintf(fromUid, "%d", ctx->info->uid % 100000); - else - sprintf(fromUid, "%d", ctx->info->uid); - - char toUid[16]; - sprintf(toUid, "%d", ctx->to.uid); - - char pid[16]; - sprintf(pid, "%d", ctx->pid); - - char user[16]; - int notify = setup_user(ctx, user); - - char activity[128]; - sprintf(activity, ACTION_RESULT, ctx->info->str.s[SU_MANAGER]); - - // Send notice to manager, enable logging - char *result_command[] = { - AM_PATH, "broadcast", "-n", - activity, - "--user", user, - "--ei", "mode", "0", - "--ei", "from.uid", fromUid, - "--ei", "to.uid", toUid, - "--ei", "pid", pid, - "--es", "command", get_command(&ctx->to), - "--es", "action", policy == ALLOW ? "allow" : "deny", - NULL - }; - silent_run(result_command); - - // Send notice to user (if needed) to create toasts - if (notify) { - sprintf(fromUid, "%d", ctx->info->uid); - sprintf(user, "%d", notify); - char *notify_command[] = { - AM_PATH, "broadcast", "-n", - activity, - "--user", user, - "--ei", "mode", "1", - "--ei", "from.uid", fromUid, - "--es", "action", policy == ALLOW ? "allow" : "deny", - NULL - }; - silent_run(notify_command); - } -} - -void app_send_request(struct su_context *ctx) { - char user[16]; - int notify = setup_user(ctx, user); - - char activity[128]; - sprintf(activity, ACTION_REQUEST, ctx->info->str.s[SU_MANAGER]); - - char *request_command[] = { - AM_PATH, "start", "-n", - activity, - "--user", user, - "--es", "socket", ctx->sock_path, - "--ez", "timeout", notify ? "false" : "true", - NULL - }; - silent_run(request_command); - - // Send notice to user to tell them root is managed by owner - if (notify) { - sprintf(user, "%d", notify); - sprintf(activity, ACTION_RESULT, ctx->info->str.s[SU_MANAGER]); - char *notify_command[] = { - AM_PATH, "broadcast", "-n", - activity, - "--user", user, - "--ei", "mode", "2", - NULL - }; - silent_run(notify_command); - } -} diff --git a/native/jni/su/connect.c b/native/jni/su/connect.c new file mode 100644 index 000000000..8197d1045 --- /dev/null +++ b/native/jni/su/connect.c @@ -0,0 +1,109 @@ +/* +** Copyright 2018, John Wu (@topjohnwu) +** Copyright 2010, Adam Shanks (@ChainsDD) +** Copyright 2008, Zinx Verituse (@zinxv) +** +*/ + +#include +#include +#include +#include +#include +#include + +#include "magisk.h" +#include "daemon.h" +#include "su.h" + +#define AM_PATH "/system/bin/app_process", "/system/bin", "com.android.commands.am.Am" + +static char *get_command(const struct su_request *to) { + if (to->command) + return to->command; + if (to->shell) + return to->shell; + return DEFAULT_SHELL; +} + +static void silent_run(char * const args[]) { + set_identity(0); + if (fork()) + return; + int zero = open("/dev/zero", O_RDONLY | O_CLOEXEC); + dup2(zero, 0); + int null = open("/dev/null", O_WRONLY | O_CLOEXEC); + dup2(null, 1); + dup2(null, 2); + setenv("CLASSPATH", "/system/framework/am.jar", 1); + execv(args[0], args); + PLOGE("exec am"); + _exit(EXIT_FAILURE); +} + +static void setup_user(char *user) { + switch (su_ctx->info->dbs.v[SU_MULTIUSER_MODE]) { + case MULTIUSER_MODE_OWNER_ONLY: + case MULTIUSER_MODE_OWNER_MANAGED: + sprintf(user, "%d", 0); + break; + case MULTIUSER_MODE_USER: + sprintf(user, "%d", su_ctx->info->uid / 100000); + break; + } +} + +void app_log() { + char user[8]; + setup_user(user); + + char fromUid[8]; + sprintf(fromUid, "%d", + su_ctx->info->dbs.v[SU_MULTIUSER_MODE] == MULTIUSER_MODE_OWNER_MANAGED ? + su_ctx->info->uid % 100000 : su_ctx->info->uid); + + char toUid[8]; + sprintf(toUid, "%d", su_ctx->to.uid); + + char pid[8]; + sprintf(pid, "%d", su_ctx->pid); + + char policy[2]; + sprintf(policy, "%d", su_ctx->info->access.policy); + + char *cmd[] = { + AM_PATH, "broadcast", + "-a", "android.intent.action.BOOT_COMPLETED", + "-p", su_ctx->info->str.s[SU_MANAGER], + "--user", user, + "--es", "action", "log", + "--ei", "from.uid", fromUid, + "--ei", "to.uid", toUid, + "--ei", "pid", pid, + "--ei", "policy", policy, + "--es", "command", get_command(&su_ctx->to), + NULL + }; + silent_run(cmd); +} + +void app_connect(const char *socket) { + char user[8]; + setup_user(user); + char *cmd[] = { + AM_PATH, "broadcast", + "-a", "android.intent.action.BOOT_COMPLETED", + "-p", su_ctx->info->str.s[SU_MANAGER], + "--user", user, + "--es", "action", "request", + "--es", "socket", (char *) socket, + NULL + }; + silent_run(cmd); +} + +void socket_send_request(int fd) { + write_key_token(fd, "uid", su_ctx->info->uid); + write_string_be(fd, "eof"); +} + diff --git a/native/jni/su/su.c b/native/jni/su/su.c index 569694223..44439e8a1 100644 --- a/native/jni/su/su.c +++ b/native/jni/su/su.c @@ -25,6 +25,7 @@ #include #include "magisk.h" +#include "daemon.h" #include "utils.h" #include "su.h" @@ -44,11 +45,9 @@ static void usage(int status) { " --preserve-environment preserve the entire environment\n" " -s, --shell SHELL use SHELL instead of the default " DEFAULT_SHELL "\n" " -v, --version display version number and exit\n" - " -V display version code and exit,\n" - " this is used almost exclusively by Superuser.apk\n" + " -V display version code and exit\n" " -mm, -M,\n" - " --mount-master run in the global mount namespace,\n" - " use if you need to publicly apply mounts\n"); + " --mount-master force run in the global mount namespace\n"); exit2(status); } @@ -64,20 +63,20 @@ static char *concat_commands(int argc, char *argv[]) { return strdup(command); } -static void populate_environment(const struct su_context *ctx) { +static void populate_environment() { struct passwd *pw; - if (ctx->to.keepenv) + if (su_ctx->to.keepenv) return; - pw = getpwuid(ctx->to.uid); + pw = getpwuid(su_ctx->to.uid); if (pw) { setenv("HOME", pw->pw_dir, 1); - if (ctx->to.shell) - setenv("SHELL", ctx->to.shell, 1); + if (su_ctx->to.shell) + setenv("SHELL", su_ctx->to.shell, 1); else setenv("SHELL", DEFAULT_SHELL, 1); - if (ctx->to.login || ctx->to.uid) { + if (su_ctx->to.login || su_ctx->to.uid) { setenv("USER", pw->pw_name, 1); setenv("LOGNAME", pw->pw_name, 1); } @@ -103,9 +102,6 @@ void set_identity(unsigned uid) { static __attribute__ ((noreturn)) void allow() { char* argv[] = { NULL, NULL, NULL, NULL }; - if (su_ctx->info->access.notify || su_ctx->info->access.log) - app_send_result(su_ctx, ALLOW); - if (su_ctx->to.login) argv[0] = "-"; else @@ -118,9 +114,12 @@ static __attribute__ ((noreturn)) void allow() { // Setup shell umask(022); - populate_environment(su_ctx); + populate_environment(); set_identity(su_ctx->to.uid); + if (su_ctx->info->access.notify || su_ctx->info->access.log) + app_log(); + execvp(su_ctx->to.shell, argv); fprintf(stderr, "Cannot execute %s: %s\n", su_ctx->to.shell, strerror(errno)); PLOGE("exec"); @@ -129,25 +128,13 @@ static __attribute__ ((noreturn)) void allow() { static __attribute__ ((noreturn)) void deny() { if (su_ctx->info->access.notify || su_ctx->info->access.log) - app_send_result(su_ctx, DENY); + app_log(); LOGW("su: request rejected (%u->%u)", su_ctx->info->uid, su_ctx->to.uid); fprintf(stderr, "%s\n", strerror(EACCES)); exit(EXIT_FAILURE); } -static void socket_cleanup() { - if (su_ctx && su_ctx->sock_path[0]) { - unlink(su_ctx->sock_path); - su_ctx->sock_path[0] = '\0'; - } -} - -static void cleanup_signal(int sig) { - socket_cleanup(); - exit2(EXIT_FAILURE); -} - __attribute__ ((noreturn)) void exit2(int status) { // Handle the pipe, or the daemon will get stuck if (su_ctx->pipefd[0] >= 0) { @@ -159,8 +146,7 @@ __attribute__ ((noreturn)) void exit2(int status) { } int su_daemon_main(int argc, char **argv) { - int c, socket_serv_fd, fd; - char result[64]; + int c; struct option long_opts[] = { { "command", required_argument, NULL, 'c' }, { "help", no_argument, NULL, 'h' }, @@ -247,28 +233,20 @@ int su_daemon_main(int argc, char **argv) { // Change directory to cwd chdir(su_ctx->cwd); - // New request or no db exist, notify user for response if (su_ctx->pipefd[0] >= 0) { - socket_serv_fd = socket_create_temp(su_ctx->sock_path, sizeof(su_ctx->sock_path)); - setup_sighandlers(cleanup_signal); + // Create random socket + struct sockaddr_un addr; + int sockfd = create_rand_socket(&addr); - // Start activity - app_send_request(su_ctx); + // Connect Magisk Manager + app_connect(addr.sun_path + 1); + int fd = socket_accept(sockfd, 60); - atexit(socket_cleanup); - - fd = socket_accept(socket_serv_fd); - socket_send_request(fd, su_ctx); - socket_receive_result(fd, result, sizeof(result)); + socket_send_request(fd); + su_ctx->info->access.policy = read_int_be(fd); close(fd); - close(socket_serv_fd); - socket_cleanup(); - - if (strcmp(result, "socket:ALLOW") == 0) - su_ctx->info->access.policy = ALLOW; - else - su_ctx->info->access.policy = DENY; + close(sockfd); // Report the policy to main daemon xwrite(su_ctx->pipefd[1], &su_ctx->info->access.policy, sizeof(policy_t)); @@ -276,9 +254,6 @@ int su_daemon_main(int argc, char **argv) { close(su_ctx->pipefd[1]); } - if (su_ctx->info->access.policy == ALLOW) - allow(); - else - deny(); + su_ctx->info->access.policy == ALLOW ? allow() : deny(); } diff --git a/native/jni/su/su.h b/native/jni/su/su.h index 43c936c19..28abb67b5 100644 --- a/native/jni/su/su.h +++ b/native/jni/su/su.h @@ -12,11 +12,6 @@ #include "list.h" #define MAGISKSU_VER_STR xstr(MAGISK_VERSION) ":MAGISKSU (topjohnwu)" - -// This is used if wrapping the fragment classes and activities -// with classes in another package. -#define REQUESTOR_PREFIX JAVA_PACKAGE_NAME ".superuser" - #define DEFAULT_SHELL "/system/bin/sh" struct su_info { @@ -49,7 +44,6 @@ struct su_context { struct su_request to; pid_t pid; char cwd[PATH_MAX]; - char sock_path[PATH_MAX]; int pipefd[2]; }; @@ -61,16 +55,11 @@ int su_daemon_main(int argc, char **argv); __attribute__ ((noreturn)) void exit2(int status); void set_identity(unsigned uid); -// su_client.c +// connect.c -int socket_create_temp(char *path, size_t len); -int socket_accept(int serv_fd); -void socket_send_request(int fd, const struct su_context *ctx); -void socket_receive_result(int fd, char *result, ssize_t result_len); +void app_log(); -// activity.c - -void app_send_result(struct su_context *ctx, policy_t policy); -void app_send_request(struct su_context *ctx); +void app_connect(const char *socket); +void socket_send_request(int fd); #endif diff --git a/native/jni/su/su_socket.c b/native/jni/su/su_socket.c deleted file mode 100644 index 28b2e18bd..000000000 --- a/native/jni/su/su_socket.c +++ /dev/null @@ -1,108 +0,0 @@ -/* su_socket.c - Functions for communication to client - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "magisk.h" -#include "utils.h" -#include "su.h" -#include "magiskpolicy.h" - -int socket_create_temp(char *path, size_t len) { - int fd; - struct sockaddr_un sun; - - fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { - PLOGE("fcntl FD_CLOEXEC"); - } - - memset(&sun, 0, sizeof(sun)); - sun.sun_family = AF_LOCAL; - snprintf(path, len, "/dev/.socket%d", getpid()); - strcpy(sun.sun_path, path); - - /* - * Delete the socket to protect from situations when - * something bad occured previously and the kernel reused pid from that process. - * Small probability, isn't it. - */ - unlink(path); - - xbind(fd, (struct sockaddr*) &sun, sizeof(sun)); - xlisten(fd, 1); - - // Set attributes so requester can access it - setfilecon(path, "u:object_r:"SEPOL_FILE_DOMAIN":s0"); - chown(path, su_ctx->info->manager_stat.st_uid, su_ctx->info->manager_stat.st_gid); - - return fd; -} - -int socket_accept(int serv_fd) { - struct timeval tv; - fd_set fds; - int rc; - - /* Wait 60 seconds for a connection, then give up. */ - tv.tv_sec = 60; - tv.tv_usec = 0; - FD_ZERO(&fds); - FD_SET(serv_fd, &fds); - do { - rc = select(serv_fd + 1, &fds, NULL, NULL, &tv); - } while (rc < 0 && errno == EINTR); - if (rc < 1) { - PLOGE("select"); - } - - return xaccept4(serv_fd, NULL, NULL, SOCK_CLOEXEC); -} - -#define write_data(fd, data, data_len) \ -do { \ - uint32_t __len = htonl(data_len); \ - __len = write((fd), &__len, sizeof(__len)); \ - if (__len != sizeof(__len)) { \ - PLOGE("write(" #data ")"); \ - } \ - __len = write((fd), data, data_len); \ - if (__len != data_len) { \ - PLOGE("write(" #data ")"); \ - } \ -} while (0) - -#define write_string_data(fd, name, data) \ -do { \ - write_data(fd, name, strlen(name)); \ - write_data(fd, data, strlen(data)); \ -} while (0) - -// stringify everything. -#define write_token(fd, name, data) \ -do { \ - char buf[16]; \ - snprintf(buf, sizeof(buf), "%d", data); \ - write_string_data(fd, name, buf); \ -} while (0) - -void socket_send_request(int fd, const struct su_context *ctx) { - write_token(fd, "uid", ctx->info->uid); - write_token(fd, "eof", 1); -} - -void socket_receive_result(int fd, char *result, ssize_t result_len) { - ssize_t len; - len = xread(fd, result, result_len - 1); - result[len] = '\0'; -}