Skip to content

Commit

Permalink
Add utility function create shell command from uid and SELinux context
Browse files Browse the repository at this point in the history
The shell command is normally just "sh" or "su", but sometimes you need
to switch to a specific uid or SELinux context. The Shell.SU.shell(...)
method will construct a string in this format: "su[ --context
<context>][ <uid>]". 

Currently only SuperSU v1.90 supports the context parameter, so the code
auto-detects if that is the su binary currently being used. Update the
command format for other su binaries if/when that feature becomes
available. 

To reduce the chance of issues, the uid is only passed if not 0 (root,
this is the default case), and the SELinux context is only passed if
SELinux is detected to be in enforcing mode (otherwise it may be useful
for development/logging purposes only, but not end-user)
  • Loading branch information
Chainfire committed Jan 18, 2014
1 parent cfe75ff commit 3f8be11
Showing 1 changed file with 69 additions and 0 deletions.
69 changes: 69 additions & 0 deletions libsuperuser/src/eu/chainfire/libsuperuser/Shell.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
package eu.chainfire.libsuperuser;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
Expand Down Expand Up @@ -337,6 +340,72 @@ public static boolean isSU(String shell) {

return shell.equals("su");
}

/**
* Constructs a shell command to start a su shell using the supplied
* uid and SELinux context. This is can be an expensive operation,
* consider caching the result.
*
* @param uid Uid to use (0 == root)
* @param context (SELinux) context name to use or null
* @return Shell command
*/
public static String shell(int uid, String context) {
// su[ --context <context>][ <uid>]
String shell = "su";

// First known firmware with SELinux built-in was a 4.2 (17) leak
if ((context != null) && (android.os.Build.VERSION.SDK_INT >= 17)) {
Boolean enforcing = null;

// Detect enforcing through sysfs, not always present
if (enforcing == null) {
File f = new File("/sys/fs/selinux/enforce");
if (f.exists()) {
try {
InputStream is = new FileInputStream("/sys/fs/selinux/enforce");
try {
enforcing = (is.read() == '1');
} finally {
is.close();
}
} catch (Exception e) {
}
}
}

// 4.4+ builds are enforcing by default, take the gamble
if (enforcing == null) {
enforcing = (android.os.Build.VERSION.SDK_INT >= 19);
}

// Switching to a context in permissive mode is not generally
// useful aside from audit testing, but we want to avoid
// switching the context due to the increased chance of issues.
if (enforcing) {
String display = version(false);
String internal = version(true);

// We only know the format for SuperSU v1.90+ right now
if (
(display != null) &&
(internal != null) &&
(display.endsWith("SUPERSU")) &&
(Integer.valueOf(internal) >= 190)
) {
shell = String.format(Locale.ENGLISH, "%s --context %s", shell, context);
}
}
}

// Most su binaries support the "su <uid>" format, but in case
// they don't, lets skip it for the default 0 (root) case
if (uid > 0) {
shell = String.format(Locale.ENGLISH, "%s %d", shell, uid);
}

return shell;
}
}

/**
Expand Down

0 comments on commit 3f8be11

Please sign in to comment.