OSDN Git Service

original
[gb-231r1-is01/GB_2.3_IS01.git] / cts / apps / CtsVerifier / src / com / android / cts / verifier / suid / SuidFilesActivity.java
diff --git a/cts/apps/CtsVerifier/src/com/android/cts/verifier/suid/SuidFilesActivity.java b/cts/apps/CtsVerifier/src/com/android/cts/verifier/suid/SuidFilesActivity.java
new file mode 100644 (file)
index 0000000..4a7224b
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.suid;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestResult;
+import com.android.cts.verifier.os.FileUtils;
+import com.android.cts.verifier.os.FileUtils.FileStatus;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnClickListener;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/** {@link Activity} that tries to find suid files. */
+public class SuidFilesActivity extends ListActivity {
+
+    private static final String TAG = SuidFilesActivity.class.getSimpleName();
+
+    /** These programs are expected suid binaries. */
+    private static final Set<String> WHITELIST = new HashSet<String>(Arrays.asList(
+            "run-as"
+    ));
+
+    private ProgressDialog mProgressDialog;
+
+    private SuidFilesAdapter mAdapter;
+
+    private SuidFilesTask mFindSuidFilesTask;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setResult(RESULT_CANCELED);
+
+        mAdapter = new SuidFilesAdapter();
+        setListAdapter(mAdapter);
+
+        new AlertDialog.Builder(this)
+            .setIcon(android.R.drawable.ic_dialog_info)
+            .setTitle(R.string.suid_files)
+            .setMessage(R.string.suid_files_info)
+            .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int which) {
+                    startScan();
+                }
+            })
+            .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int which) {
+                    finish();
+                }
+            })
+            .setOnCancelListener(new OnCancelListener() {
+                public void onCancel(DialogInterface dialog) {
+                    finish();
+                }
+            })
+            .show();
+    }
+
+    private void startScan() {
+        mProgressDialog = new ProgressDialog(this);
+        mProgressDialog.setTitle(getString(R.string.scanning_directory));
+        mProgressDialog.setOnCancelListener(new OnCancelListener() {
+            public void onCancel(DialogInterface dialog) {
+                // If the scanning dialog is cancelled, then stop the task and finish the activity
+                // to prevent the user from just seeing a blank listview.
+                if (mFindSuidFilesTask != null) {
+                    mFindSuidFilesTask.cancel(true);
+                }
+                finish();
+            }
+        });
+
+        // Start searching for suid files using a background thread.
+        mFindSuidFilesTask = new SuidFilesTask();
+        mFindSuidFilesTask.execute(new File("/"));
+    }
+
+    @Override
+    protected void onListItemClick(ListView listView, View view, int position, long id) {
+        super.onListItemClick(listView, view, position, id);
+        File file = mAdapter.getItem(position);
+        String message = getMessage(file);
+        new AlertDialog.Builder(this)
+                .setTitle(file.getName())
+                .setMessage(message)
+                .show();
+    }
+
+    private String getMessage(File file) {
+        FileStatus status = new FileStatus();
+        if (FileUtils.getFileStatus(file.getAbsolutePath(), status, true)) {
+            return getString(R.string.file_status,
+                    FileUtils.getUserName(status.getUid()),
+                    FileUtils.getGroupName(status.getGid()),
+                    FileUtils.getFormattedPermissions(status.getMode()),
+                    file.getAbsolutePath());
+        } else {
+            return getString(R.string.no_file_status);
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        Log.e("Suid", "onDestroy");
+        super.onDestroy();
+        if (mFindSuidFilesTask != null) {
+            mFindSuidFilesTask.cancel(true);
+        }
+    }
+
+    /** {@link ListView} items display the basenames of the suid files. */
+    class SuidFilesAdapter extends ArrayAdapter<File> {
+
+        SuidFilesAdapter() {
+            super(SuidFilesActivity.this, android.R.layout.simple_list_item_1);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            TextView view = (TextView) super.getView(position, convertView, parent);
+            File file = getItem(position);
+            view.setText(file.getName());
+            view.setBackgroundResource(WHITELIST.contains(file.getName())
+                    ? R.drawable.test_pass_gradient
+                    : R.drawable.test_fail_gradient);
+            return view;
+        }
+    }
+
+    /** {@link AsyncTask} that searches the file system for suid files. */
+    class SuidFilesTask extends AsyncTask<File, File, Set<File>> {
+
+        @Override
+        protected void onPreExecute() {
+            super.onPreExecute();
+            mProgressDialog.show();
+        }
+
+        @Override
+        protected Set<File> doInBackground(File... paths) {
+            Set<File> suidFiles = new HashSet<File>();
+            DirectoryFileFilter dirFilter = new DirectoryFileFilter();
+            SuidFileFilter suidFilter = new SuidFileFilter();
+            for (File path : paths) {
+                findSuidFiles(path, suidFiles, dirFilter, suidFilter);
+            }
+            return suidFiles;
+        }
+
+        private void findSuidFiles(File dir, Set<File> foundSuidFiles,
+                DirectoryFileFilter dirFilter, SuidFileFilter suidFilter) {
+
+            // Recursively traverse sub directories...
+            File[] subDirs = dir.listFiles(dirFilter);
+            if (subDirs != null && subDirs.length > 0) {
+                for (File subDir : subDirs) {
+                    findSuidFiles(subDir, foundSuidFiles, dirFilter, suidFilter);
+                }
+            }
+
+            // / ...then inspect files in directory to find offending binaries.
+            publishProgress(dir);
+            File[] suidFiles = dir.listFiles(suidFilter);
+            if (suidFiles != null && suidFiles.length > 0) {
+                Collections.addAll(foundSuidFiles, suidFiles);
+            }
+        }
+
+        /** {@link FileFilter} that returns only directories that are not symbolic links. */
+        private class DirectoryFileFilter implements FileFilter {
+
+            private final FileStatus status = new FileStatus();
+
+            public boolean accept(File pathname) {
+                // Don't follow symlinks to avoid infinite looping.
+                if (FileUtils.getFileStatus(pathname.getPath(), status, true)) {
+                    return status.isDirectory() && !status.isSymbolicLink();
+                } else {
+                    Log.w(TAG, "Could not stat " + pathname);
+                    return false;
+                }
+            }
+        }
+
+        /** {@link FileFilter} that returns files that have setuid root or setgid root. */
+        private class SuidFileFilter implements FileFilter {
+
+            private final FileStatus status = new FileStatus();
+
+            public boolean accept(File pathname) {
+                if (FileUtils.getFileStatus(pathname.getPath(), status, true)) {
+                    return !status.isDirectory()
+                            && !status.isSymbolicLink()
+                            && status.isSetUid();
+                } else {
+                    Log.w(TAG, "Could not stat " + pathname);
+                    return false;
+                }
+            }
+        }
+
+        @Override
+        protected void onPostExecute(Set<File> results) {
+            super.onPostExecute(results);
+            mProgressDialog.dismiss();
+
+            // Task could be cancelled and results could be null but don't bother doing anything.
+            if (results != null) {
+                boolean passed = true;
+                for (File result : results) {
+                    if (!WHITELIST.contains(result.getName())) {
+                        passed = false;
+                    }
+                    mAdapter.add(result);
+                }
+
+                // Alert the user that nothing was found rather than showing an empty list view.
+                if (passed) {
+                    TestResult.setPassedResult(SuidFilesActivity.this);
+                    new AlertDialog.Builder(SuidFilesActivity.this)
+                            .setTitle(R.string.congratulations)
+                            .setMessage(R.string.no_suid_files)
+                            .setPositiveButton(android.R.string.ok, new OnClickListener() {
+                                public void onClick(DialogInterface dialog, int which) {
+                                    dialog.dismiss();
+                                }
+                            })
+                            .show();
+                } else {
+                    TestResult.setFailedResult(SuidFilesActivity.this);
+                }
+            }
+        }
+
+        @Override
+        protected void onProgressUpdate(File... values) {
+            super.onProgressUpdate(values);
+
+            // Show the current directory being scanned...
+            mProgressDialog.setMessage(values[0].getAbsolutePath());
+        }
+    }
+}