Any audit logs from dmesg, logcat, or /data/misc/audit ?
Yes. Please check the post again. I've added the audit logs recently.
Yes. Please check the post again. I've added the audit logs recently.
Try "attradd { uhid_device input_device } { mlstrustedsubject mlstrustedobject }". Not quite sure about this one.
Note of course that a better solution would be running whatever handles this input as root, and have it communicate with your main app over sockets and pipes, rather than adjusting security settings for all appdomain/untrusted_app.
I also noticed something new in the audit logs in Marshmallow. Something like this " untrusted_app:s0:c512,c768 " . Does this signify anything new?
This way the library is loaded by the app and uses the app's context. Which is untrusted:s0. Even with Root access my Application's context doesn't change to init:s0. Only the supersu shell has init:s0 context.
Is there anyway we could change the context of my App to init:s0 or maybe system_app:s0? This might solve my issue I believe. I would really appreciate if you could help me with this, the one last time.
Yes, it's called MLS/MCS. This is why I asked you try mlstrustedobject / mlstrustedsubject. It's a tricky subject and annoying to work with.
http://selinuxproject.org/page/NB_MLS
Unfortunately that is not currently possible.
Tasks.executeInBackground(MainActivity.this, new BackgroundWork<Boolean>() {
@Override
public Boolean doInBackground() throws Exception {
return Shell.SU.available();
}
}, new Completion<Boolean>() {
@Override
public void onSuccess(Context context, Boolean result) {
isSuAvailable = result;
Log.i(TAG, "SU available: " + Boolean.toString(result));
if (isSuAvailable) {
// Perform stuff
}
} else {
Log.i(TAG, "SU permission denied or not available");
// Show error dialog
}
}
I have some of users report me that (1) they didn't see the SU authorisation prompt AND the app received root permission or (2) The app didn't make SU show the authorisation prompt, so they couldn't approve the app
Which su are they running?
On various CM-based firmwares you do not get a confirmation dialog, you need to manually enable root access before starting the app.
I'm not sure, I haven't asked them, although some of them said they're on the latest SuperSU stable or KingRoot. I'm on SuperSU beta (May 10) on a Galaxy S7 edge, and I see a prompt.
Is there a way to handle external authorisation? A way to detect if the SU authorisation dialog will show up or not, so I can tell the user to manually grant access via their SU app.
Well, then it seems you have altogether too little info...
Nope.
The best you can do is check the su version and compare it with known strings like supersu or cm-su.
So how does one handle other SU apps or SU binaries located in a different path? It seems like bruteforcing is the only way. I wish there was a consistent way (maybe a method in libsuperuser) to handle different scenarios.
@Chainfire I'm trying to start an interactive shell at the startup of app but am having some problems with the callbacks.
So, in my onCreate I start an interactive shell using Shell.Builder.open method. Then in one of my Asynctasks I need to run the su command.
I read the Shell.Interactive documentation and you clearly stated that callbacks will be executed on the thread in which the instance was created (unless I call #setAutoHandler with false). But I'm already in my asynctask and I need the callback to be executed on that same thread. Is there any way I can do that? The inline documentation in java docs is a bit conflicting in this regard, as in one line you say not to pass commands in main thread, but then later on you say that only main thread have a Looper and only it can handle the callback, and that while the callback (onLineListener for eg.) is being executed, we need to take into consideration the multithreading point stand, and make calls thread safe.
I think I'm missing some really foolish. Please point me in the right path.
HandlerThread ht = new HandlerThread("threads must have a name");
ht.start();
Handler h = new Handler(ht.getLooper());
.... Shell.Builder#setHandler(h) ...
public void onStart() {
(new Shell.Builder())
.addCommand("echo test", 0, new Shell.OnCommandResultListener() {
public void onCommandResult(int commandCode, int exitCode, List<String> output) {
// point A
}
})
.open()
// point B
.waitForIdle();
// point C
}
private HandlerThread backgroundHandlerThread;
private Handler backgroundHandler;
private Shell.Interactive shell;
private void ensureShell() {
if (shell != null) return;
backgroundHandlerThread = new HandlerThread("BackgroundHandlerThread");
backgroundHandlerThread.start();
backgroundHandler = new Handler(backgroundHandlerThread.getLooper());
shell = (new Shell.Builder()).useSU().useHandler(backgroundHandler).open();
}
@Override
public void onStart() {
ensureShell();
(new MyTask()).execute();
}
private class MyTask extends AsyncTask<Void, Void, Boolean> {
@Override
protected Boolean doInBackground(Void... params) {
final Boolean[] result = new Boolean[] { false };
shell.addCommand("echo foobar", 0, new Shell.OnCommandResultListener() {
public void onCommandResult(int commandCode, int exitCode, List<String> output) {
for (String line : output) {
if (output.contains("foobar")) {
result[0] = true;
break;
}
}
}
});
shell.waitForIdle();
return result[0];
}
}
In other words: if you want to use results from a shell call, you want the callbacks to run on a different thread than the calling code!
Something like this works:
Code:private HandlerThread backgroundHandlerThread; private Handler backgroundHandler; private Shell.Interactive shell; private void ensureShell() { if (shell != null) return; backgroundHandlerThread = new HandlerThread("BackgroundHandlerThread"); backgroundHandlerThread.start(); backgroundHandler = new Handler(backgroundHandlerThread.getLooper()); shell = (new Shell.Builder()).useSU().useHandler(backgroundHandler).open(); } @Override public void onStart() { ensureShell(); (new MyTask()).execute(); } private class MyTask extends AsyncTask<Void, Void, Boolean> { @Override protected Boolean doInBackground(Void... params) { final Boolean[] result = new Boolean[] { false }; shell.addCommand("echo foobar", 0, new Shell.OnCommandResultListener() { public void onCommandResult(int commandCode, int exitCode, List<String> output) { for (String line : output) { if (output.contains("foobar")) { result[0] = true; break; } } } }); shell.waitForIdle(); return result[0]; } }
E: Can't run /tmp/update_binary (No such file or directory)
E: Error in sideload/package.zip
(status 255)
new BackgroundThread(command).execute();
class BackgroundThread extends AsyncTask<String, Void, Void> {
private String s;
public BackgroundThread(String command) {
s = command;
}
@Override
protected Void doInBackground(String... command) {
Process process = null;
DataOutputStream os = null;
try {
process = Runtime.getRuntime().exec("su");
os = new DataOutputStream(process.getOutputStream());
os.writeBytes(s+"\n");
os.writeBytes("exit\n");
os.flush();
process.waitFor();
} catch (Exception e) {
}
finally {
try {
if (os != null)
os.close();
else
process.destroy();
} catch (Exception e) {
}
}
return null;
}
}
public void run(String command) {
new BackgroundThread(command).doInBackground();
}
No it isn't acceptable, but the reason is not what you might think.
Using a generic AsyncTask derived class to run su calls can be a solution. As long as your code ends up running in the background, all is good. However, in my experience what usually happens is that you have to do a lot of things in the background.
For example, imagine that during your app's startup, you need to check some files, perform some su calls, check some more files, do some computations, etc. Those should all go in a single AsyncTask's doInBackground method. That way you can use onPreExecute and onPostExecute to show and dismiss a dialog that tells the user you are busy.
Furthermore, if you had 10 su commands to run, would you just call new BackgroundThread ... 10 times? As the call should return before the su command is finished executing, the order of the executions of those commands becomes semi-random, you can't depend on them being run in any specific order, it even differs between Android versions (some versions run AsyncTasks serially, others run X of them in parallel). Not to mention that you shouldn't create 10 separate su processes to run 10 commands unless you have a good reason. Batching commands together == much higher performance.
The above does depend on which commands you need to execute, the needs of your application, and how you use it. If you only have to execute a single su command, it can be done that way, but I think in general this is not an ideal solution.
Now, the reason why your solution is truly unacceptable is because you are calling doInBackground. This does not make the code run in the background. You override doInBackground and insert your code, but you call the execute method. You should call your code like this:
Code:new BackgroundThread(command).execute();
Else you're still calling on the main thread !
If you really want to keep using this method, keep in mind that you can actually pass parameters to the execute() function that in turn will be passed to doInBackground. Look in the AsyncTask documentation ...