• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cts.verifier.suid;
18 
19 import com.android.cts.verifier.PassFailButtons;
20 import com.android.cts.verifier.R;
21 import com.android.cts.verifier.TestResult;
22 import com.android.cts.verifier.os.FileUtils;
23 import com.android.cts.verifier.os.FileUtils.FileStatus;
24 
25 import android.app.Activity;
26 import android.app.AlertDialog;
27 import android.app.ProgressDialog;
28 import android.content.DialogInterface;
29 import android.content.DialogInterface.OnCancelListener;
30 import android.content.DialogInterface.OnClickListener;
31 import android.os.AsyncTask;
32 import android.os.Bundle;
33 import android.util.Log;
34 import android.view.View;
35 import android.view.ViewGroup;
36 import android.widget.ArrayAdapter;
37 import android.widget.ListView;
38 import android.widget.TextView;
39 
40 import java.io.File;
41 import java.io.FileFilter;
42 import java.util.Arrays;
43 import java.util.Collections;
44 import java.util.HashSet;
45 import java.util.Set;
46 
47 /** {@link Activity} that tries to find suid files. */
48 public class SuidFilesActivity extends PassFailButtons.ListActivity {
49 
50     private static final String TAG = SuidFilesActivity.class.getSimpleName();
51 
52     /** These programs are expected suid binaries. */
53     private static final Set<String> WHITELIST = new HashSet<String>(Arrays.asList(
54             "run-as"
55     ));
56 
57     private ProgressDialog mProgressDialog;
58 
59     private SuidFilesAdapter mAdapter;
60 
61     private SuidFilesTask mFindSuidFilesTask;
62 
63     @Override
onCreate(Bundle savedInstanceState)64     protected void onCreate(Bundle savedInstanceState) {
65         super.onCreate(savedInstanceState);
66         setContentView(R.layout.pass_fail_list);
67         setPassFailButtonClickListeners();
68         getPassButton().setEnabled(false);
69 
70         mAdapter = new SuidFilesAdapter();
71         setListAdapter(mAdapter);
72 
73         new AlertDialog.Builder(this)
74             .setIcon(android.R.drawable.ic_dialog_info)
75             .setTitle(R.string.suid_files)
76             .setMessage(R.string.suid_files_info)
77             .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
78                 @Override
79                 public void onClick(DialogInterface dialog, int which) {
80                     startScan();
81                 }
82             })
83             .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
84                 @Override
85                 public void onClick(DialogInterface dialog, int which) {
86                     finish();
87                 }
88             })
89             .setOnCancelListener(new OnCancelListener() {
90                 @Override
91                 public void onCancel(DialogInterface dialog) {
92                     finish();
93                 }
94             })
95             .show();
96     }
97 
startScan()98     private void startScan() {
99         mProgressDialog = new ProgressDialog(this);
100         mProgressDialog.setTitle(getString(R.string.scanning_directory));
101         mProgressDialog.setOnCancelListener(new OnCancelListener() {
102             @Override
103             public void onCancel(DialogInterface dialog) {
104                 // If the scanning dialog is cancelled, then stop the task and finish the activity
105                 // to prevent the user from just seeing a blank listview.
106                 if (mFindSuidFilesTask != null) {
107                     mFindSuidFilesTask.cancel(true);
108                 }
109                 finish();
110             }
111         });
112 
113         // Start searching for suid files using a background thread.
114         mFindSuidFilesTask = new SuidFilesTask();
115         mFindSuidFilesTask.execute(new File("/"));
116     }
117 
118     @Override
onListItemClick(ListView listView, View view, int position, long id)119     protected void onListItemClick(ListView listView, View view, int position, long id) {
120         super.onListItemClick(listView, view, position, id);
121         File file = mAdapter.getItem(position);
122         String message = getMessage(file);
123         new AlertDialog.Builder(this)
124                 .setTitle(file.getName())
125                 .setMessage(message)
126                 .show();
127     }
128 
getMessage(File file)129     private String getMessage(File file) {
130         FileStatus status = new FileStatus();
131         if (FileUtils.getFileStatus(file.getAbsolutePath(), status, true)) {
132             return getString(R.string.file_status,
133                     FileUtils.getUserName(status.getUid()),
134                     FileUtils.getGroupName(status.getGid()),
135                     FileUtils.getFormattedPermissions(status.getMode()),
136                     file.getAbsolutePath());
137         } else {
138             return getString(R.string.no_file_status);
139         }
140     }
141 
142     @Override
onDestroy()143     protected void onDestroy() {
144         Log.e("Suid", "onDestroy");
145         super.onDestroy();
146         if (mFindSuidFilesTask != null) {
147             mFindSuidFilesTask.cancel(true);
148         }
149     }
150 
151     /** {@link ListView} items display the basenames of the suid files. */
152     class SuidFilesAdapter extends ArrayAdapter<File> {
153 
SuidFilesAdapter()154         SuidFilesAdapter() {
155             super(SuidFilesActivity.this, android.R.layout.simple_list_item_1);
156         }
157 
158         @Override
getView(int position, View convertView, ViewGroup parent)159         public View getView(int position, View convertView, ViewGroup parent) {
160             TextView view = (TextView) super.getView(position, convertView, parent);
161             File file = getItem(position);
162             view.setText(file.getName());
163             view.setBackgroundResource(WHITELIST.contains(file.getName())
164                     ? R.drawable.test_pass_gradient
165                     : R.drawable.test_fail_gradient);
166             return view;
167         }
168     }
169 
170     /** {@link AsyncTask} that searches the file system for suid files. */
171     class SuidFilesTask extends AsyncTask<File, File, Set<File>> {
172 
173         @Override
onPreExecute()174         protected void onPreExecute() {
175             super.onPreExecute();
176             mProgressDialog.show();
177         }
178 
179         @Override
doInBackground(File... paths)180         protected Set<File> doInBackground(File... paths) {
181             Set<File> suidFiles = new HashSet<File>();
182             DirectoryFileFilter dirFilter = new DirectoryFileFilter();
183             SuidFileFilter suidFilter = new SuidFileFilter();
184             for (File path : paths) {
185                 findSuidFiles(path, suidFiles, dirFilter, suidFilter);
186             }
187             return suidFiles;
188         }
189 
findSuidFiles(File dir, Set<File> foundSuidFiles, DirectoryFileFilter dirFilter, SuidFileFilter suidFilter)190         private void findSuidFiles(File dir, Set<File> foundSuidFiles,
191                 DirectoryFileFilter dirFilter, SuidFileFilter suidFilter) {
192 
193             // Recursively traverse sub directories...
194             File[] subDirs = dir.listFiles(dirFilter);
195             if (subDirs != null && subDirs.length > 0) {
196                 for (File subDir : subDirs) {
197                     findSuidFiles(subDir, foundSuidFiles, dirFilter, suidFilter);
198                 }
199             }
200 
201             // / ...then inspect files in directory to find offending binaries.
202             publishProgress(dir);
203             File[] suidFiles = dir.listFiles(suidFilter);
204             if (suidFiles != null && suidFiles.length > 0) {
205                 Collections.addAll(foundSuidFiles, suidFiles);
206             }
207         }
208 
209         /** {@link FileFilter} that returns only directories that are not symbolic links. */
210         private class DirectoryFileFilter implements FileFilter {
211 
212             private final FileStatus status = new FileStatus();
213 
214             @Override
accept(File pathname)215             public boolean accept(File pathname) {
216                 // Don't follow symlinks to avoid infinite looping.
217                 if (FileUtils.getFileStatus(pathname.getPath(), status, true)) {
218                     return status.isDirectory() && !status.isSymbolicLink();
219                 } else {
220                     Log.w(TAG, "Could not stat " + pathname);
221                     return false;
222                 }
223             }
224         }
225 
226         /** {@link FileFilter} that returns files that have setuid root or setgid root. */
227         private class SuidFileFilter implements FileFilter {
228 
229             private final FileStatus status = new FileStatus();
230 
231             @Override
accept(File pathname)232             public boolean accept(File pathname) {
233                 if (FileUtils.getFileStatus(pathname.getPath(), status, true)) {
234                     // only files with setUid which can be executable by CTS are reported.
235                     return !status.isDirectory()
236                             && !status.isSymbolicLink()
237                             && status.isSetUid()
238                             && status.isExecutableByCTS();
239                 } else {
240                     Log.w(TAG, "Could not stat " + pathname);
241                     return false;
242                 }
243             }
244         }
245 
246         @Override
onPostExecute(Set<File> results)247         protected void onPostExecute(Set<File> results) {
248             super.onPostExecute(results);
249             mProgressDialog.dismiss();
250 
251             // Task could be cancelled and results could be null but don't bother doing anything.
252             if (results != null) {
253                 boolean passed = true;
254                 for (File result : results) {
255                     if (!WHITELIST.contains(result.getName())) {
256                         passed = false;
257                     }
258                     mAdapter.add(result);
259                 }
260 
261                 // Alert the user that nothing was found rather than showing an empty list view.
262                 if (passed) {
263                     getPassButton().setEnabled(true);
264                     new AlertDialog.Builder(SuidFilesActivity.this)
265                             .setTitle(R.string.congratulations)
266                             .setMessage(R.string.no_suid_files)
267                             .setPositiveButton(android.R.string.ok, new OnClickListener() {
268                                 @Override
269                                 public void onClick(DialogInterface dialog, int which) {
270                                     dialog.dismiss();
271                                 }
272                             })
273                             .show();
274                 }
275             }
276         }
277 
278         @Override
onProgressUpdate(File... values)279         protected void onProgressUpdate(File... values) {
280             super.onProgressUpdate(values);
281 
282             // Show the current directory being scanned...
283             mProgressDialog.setMessage(values[0].getAbsolutePath());
284         }
285     }
286 }
287