diff options
author | David 'Digit' Turner | 2010-03-02 20:05:23 -0600 |
---|---|---|
committer | David 'Digit' Turner | 2010-03-17 13:02:08 -0500 |
commit | 1f4d95296acf34a93128332441782a80c10845b4 (patch) | |
tree | 2b8cf38a4716b2c622c564d3a7da82022cdaf9c9 | |
parent | 5fc070be8593f39f5140ab63fb6f5eccceb1dc83 (diff) | |
download | platform-system-core-1f4d95296acf34a93128332441782a80c10845b4.tar.gz platform-system-core-1f4d95296acf34a93128332441782a80c10845b4.tar.xz platform-system-core-1f4d95296acf34a93128332441782a80c10845b4.zip |
Add 'run-as' command implementation as set-uid program.
Typical usage is 'run-as <package-name> <command>' to run <command>
in the data directory, and the user id, of <package-name> if, and only
if <package-name> is the name of an installed and debuggable application.
This relies on the /data/system/packages.list file generated by the
PackageManager service.
BEWARE: This is intended to be available on production devices !
-rw-r--r-- | include/private/android_filesystem_config.h | 5 | ||||
-rw-r--r-- | run-as/Android.mk | 12 | ||||
-rw-r--r-- | run-as/NOTICE | 190 | ||||
-rw-r--r-- | run-as/package.c | 471 | ||||
-rw-r--r-- | run-as/package.h | 41 | ||||
-rw-r--r-- | run-as/run-as.c | 178 |
6 files changed, 896 insertions, 1 deletions
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index f2a5fe1af..4cab96a40 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h | |||
@@ -169,7 +169,7 @@ static struct fs_path_config android_files[] = { | |||
169 | * Do not change. */ | 169 | * Do not change. */ |
170 | { 02755, AID_ROOT, AID_NET_RAW, "system/bin/ping" }, | 170 | { 02755, AID_ROOT, AID_NET_RAW, "system/bin/ping" }, |
171 | { 02750, AID_ROOT, AID_INET, "system/bin/netcfg" }, | 171 | { 02750, AID_ROOT, AID_INET, "system/bin/netcfg" }, |
172 | /* the following four files are INTENTIONALLY set-uid, but they | 172 | /* the following five files are INTENTIONALLY set-uid, but they |
173 | * are NOT included on user builds. */ | 173 | * are NOT included on user builds. */ |
174 | { 06755, AID_ROOT, AID_ROOT, "system/xbin/su" }, | 174 | { 06755, AID_ROOT, AID_ROOT, "system/xbin/su" }, |
175 | { 06755, AID_ROOT, AID_ROOT, "system/xbin/librank" }, | 175 | { 06755, AID_ROOT, AID_ROOT, "system/xbin/librank" }, |
@@ -177,6 +177,9 @@ static struct fs_path_config android_files[] = { | |||
177 | { 06755, AID_ROOT, AID_ROOT, "system/xbin/procmem" }, | 177 | { 06755, AID_ROOT, AID_ROOT, "system/xbin/procmem" }, |
178 | { 06755, AID_ROOT, AID_ROOT, "system/xbin/tcpdump" }, | 178 | { 06755, AID_ROOT, AID_ROOT, "system/xbin/tcpdump" }, |
179 | { 04770, AID_ROOT, AID_RADIO, "system/bin/pppd-ril" }, | 179 | { 04770, AID_ROOT, AID_RADIO, "system/bin/pppd-ril" }, |
180 | /* the following file is INTENTIONALLY set-uid, and IS included | ||
181 | * in user builds. */ | ||
182 | { 06750, AID_ROOT, AID_SHELL, "system/bin/run-as" }, | ||
180 | { 00755, AID_ROOT, AID_SHELL, "system/bin/*" }, | 183 | { 00755, AID_ROOT, AID_SHELL, "system/bin/*" }, |
181 | { 00755, AID_ROOT, AID_SHELL, "system/xbin/*" }, | 184 | { 00755, AID_ROOT, AID_SHELL, "system/xbin/*" }, |
182 | { 00750, AID_ROOT, AID_SHELL, "sbin/*" }, | 185 | { 00750, AID_ROOT, AID_SHELL, "sbin/*" }, |
diff --git a/run-as/Android.mk b/run-as/Android.mk new file mode 100644 index 000000000..326f5afeb --- /dev/null +++ b/run-as/Android.mk | |||
@@ -0,0 +1,12 @@ | |||
1 | LOCAL_PATH:= $(call my-dir) | ||
2 | include $(CLEAR_VARS) | ||
3 | |||
4 | LOCAL_SRC_FILES:= run-as.c package.c | ||
5 | |||
6 | LOCAL_MODULE:= run-as | ||
7 | |||
8 | LOCAL_FORCE_STATIC_EXECUTABLE := true | ||
9 | |||
10 | LOCAL_STATIC_LIBRARIES := libc | ||
11 | |||
12 | include $(BUILD_EXECUTABLE) | ||
diff --git a/run-as/NOTICE b/run-as/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/run-as/NOTICE | |||
@@ -0,0 +1,190 @@ | |||
1 | |||
2 | Copyright (c) 2005-2008, 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 | |||
7 | Unless required by applicable law or agreed to in writing, software | ||
8 | distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
10 | See the License for the specific language governing permissions and | ||
11 | limitations under the License. | ||
12 | |||
13 | |||
14 | Apache License | ||
15 | Version 2.0, January 2004 | ||
16 | http://www.apache.org/licenses/ | ||
17 | |||
18 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||
19 | |||
20 | 1. Definitions. | ||
21 | |||
22 | "License" shall mean the terms and conditions for use, reproduction, | ||
23 | and distribution as defined by Sections 1 through 9 of this document. | ||
24 | |||
25 | "Licensor" shall mean the copyright owner or entity authorized by | ||
26 | the copyright owner that is granting the License. | ||
27 | |||
28 | "Legal Entity" shall mean the union of the acting entity and all | ||
29 | other entities that control, are controlled by, or are under common | ||
30 | control with that entity. For the purposes of this definition, | ||
31 | "control" means (i) the power, direct or indirect, to cause the | ||
32 | direction or management of such entity, whether by contract or | ||
33 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||
34 | outstanding shares, or (iii) beneficial ownership of such entity. | ||
35 | |||
36 | "You" (or "Your") shall mean an individual or Legal Entity | ||
37 | exercising permissions granted by this License. | ||
38 | |||
39 | "Source" form shall mean the preferred form for making modifications, | ||
40 | including but not limited to software source code, documentation | ||
41 | source, and configuration files. | ||
42 | |||
43 | "Object" form shall mean any form resulting from mechanical | ||
44 | transformation or translation of a Source form, including but | ||
45 | not limited to compiled object code, generated documentation, | ||
46 | and conversions to other media types. | ||
47 | |||
48 | "Work" shall mean the work of authorship, whether in Source or | ||
49 | Object form, made available under the License, as indicated by a | ||
50 | copyright notice that is included in or attached to the work | ||
51 | (an example is provided in the Appendix below). | ||
52 | |||
53 | "Derivative Works" shall mean any work, whether in Source or Object | ||
54 | form, that is based on (or derived from) the Work and for which the | ||
55 | editorial revisions, annotations, elaborations, or other modifications | ||
56 | represent, as a whole, an original work of authorship. For the purposes | ||
57 | of this License, Derivative Works shall not include works that remain | ||
58 | separable from, or merely link (or bind by name) to the interfaces of, | ||
59 | the Work and Derivative Works thereof. | ||
60 | |||
61 | "Contribution" shall mean any work of authorship, including | ||
62 | the original version of the Work and any modifications or additions | ||
63 | to that Work or Derivative Works thereof, that is intentionally | ||
64 | submitted to Licensor for inclusion in the Work by the copyright owner | ||
65 | or by an individual or Legal Entity authorized to submit on behalf of | ||
66 | the copyright owner. For the purposes of this definition, "submitted" | ||
67 | means any form of electronic, verbal, or written communication sent | ||
68 | to the Licensor or its representatives, including but not limited to | ||
69 | communication on electronic mailing lists, source code control systems, | ||
70 | and issue tracking systems that are managed by, or on behalf of, the | ||
71 | Licensor for the purpose of discussing and improving the Work, but | ||
72 | excluding communication that is conspicuously marked or otherwise | ||
73 | designated in writing by the copyright owner as "Not a Contribution." | ||
74 | |||
75 | "Contributor" shall mean Licensor and any individual or Legal Entity | ||
76 | on behalf of whom a Contribution has been received by Licensor and | ||
77 | subsequently incorporated within the Work. | ||
78 | |||
79 | 2. Grant of Copyright License. Subject to the terms and conditions of | ||
80 | this License, each Contributor hereby grants to You a perpetual, | ||
81 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||
82 | copyright license to reproduce, prepare Derivative Works of, | ||
83 | publicly display, publicly perform, sublicense, and distribute the | ||
84 | Work and such Derivative Works in Source or Object form. | ||
85 | |||
86 | 3. Grant of Patent License. Subject to the terms and conditions of | ||
87 | this License, each Contributor hereby grants to You a perpetual, | ||
88 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||
89 | (except as stated in this section) patent license to make, have made, | ||
90 | use, offer to sell, sell, import, and otherwise transfer the Work, | ||
91 | where such license applies only to those patent claims licensable | ||
92 | by such Contributor that are necessarily infringed by their | ||
93 | Contribution(s) alone or by combination of their Contribution(s) | ||
94 | with the Work to which such Contribution(s) was submitted. If You | ||
95 | institute patent litigation against any entity (including a | ||
96 | cross-claim or counterclaim in a lawsuit) alleging that the Work | ||
97 | or a Contribution incorporated within the Work constitutes direct | ||
98 | or contributory patent infringement, then any patent licenses | ||
99 | granted to You under this License for that Work shall terminate | ||
100 | as of the date such litigation is filed. | ||
101 | |||
102 | 4. Redistribution. You may reproduce and distribute copies of the | ||
103 | Work or Derivative Works thereof in any medium, with or without | ||
104 | modifications, and in Source or Object form, provided that You | ||
105 | meet the following conditions: | ||
106 | |||
107 | (a) You must give any other recipients of the Work or | ||
108 | Derivative Works a copy of this License; and | ||
109 | |||
110 | (b) You must cause any modified files to carry prominent notices | ||
111 | stating that You changed the files; and | ||
112 | |||
113 | (c) You must retain, in the Source form of any Derivative Works | ||
114 | that You distribute, all copyright, patent, trademark, and | ||
115 | attribution notices from the Source form of the Work, | ||
116 | excluding those notices that do not pertain to any part of | ||
117 | the Derivative Works; and | ||
118 | |||
119 | (d) If the Work includes a "NOTICE" text file as part of its | ||
120 | distribution, then any Derivative Works that You distribute must | ||
121 | include a readable copy of the attribution notices contained | ||
122 | within such NOTICE file, excluding those notices that do not | ||
123 | pertain to any part of the Derivative Works, in at least one | ||
124 | of the following places: within a NOTICE text file distributed | ||
125 | as part of the Derivative Works; within the Source form or | ||
126 | documentation, if provided along with the Derivative Works; or, | ||
127 | within a display generated by the Derivative Works, if and | ||
128 | wherever such third-party notices normally appear. The contents | ||
129 | of the NOTICE file are for informational purposes only and | ||
130 | do not modify the License. You may add Your own attribution | ||
131 | notices within Derivative Works that You distribute, alongside | ||
132 | or as an addendum to the NOTICE text from the Work, provided | ||
133 | that such additional attribution notices cannot be construed | ||
134 | as modifying the License. | ||
135 | |||
136 | You may add Your own copyright statement to Your modifications and | ||
137 | may provide additional or different license terms and conditions | ||
138 | for use, reproduction, or distribution of Your modifications, or | ||
139 | for any such Derivative Works as a whole, provided Your use, | ||
140 | reproduction, and distribution of the Work otherwise complies with | ||
141 | the conditions stated in this License. | ||
142 | |||
143 | 5. Submission of Contributions. Unless You explicitly state otherwise, | ||
144 | any Contribution intentionally submitted for inclusion in the Work | ||
145 | by You to the Licensor shall be under the terms and conditions of | ||
146 | this License, without any additional terms or conditions. | ||
147 | Notwithstanding the above, nothing herein shall supersede or modify | ||
148 | the terms of any separate license agreement you may have executed | ||
149 | with Licensor regarding such Contributions. | ||
150 | |||
151 | 6. Trademarks. This License does not grant permission to use the trade | ||
152 | names, trademarks, service marks, or product names of the Licensor, | ||
153 | except as required for reasonable and customary use in describing the | ||
154 | origin of the Work and reproducing the content of the NOTICE file. | ||
155 | |||
156 | 7. Disclaimer of Warranty. Unless required by applicable law or | ||
157 | agreed to in writing, Licensor provides the Work (and each | ||
158 | Contributor provides its Contributions) on an "AS IS" BASIS, | ||
159 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
160 | implied, including, without limitation, any warranties or conditions | ||
161 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||
162 | PARTICULAR PURPOSE. You are solely responsible for determining the | ||
163 | appropriateness of using or redistributing the Work and assume any | ||
164 | risks associated with Your exercise of permissions under this License. | ||
165 | |||
166 | 8. Limitation of Liability. In no event and under no legal theory, | ||
167 | whether in tort (including negligence), contract, or otherwise, | ||
168 | unless required by applicable law (such as deliberate and grossly | ||
169 | negligent acts) or agreed to in writing, shall any Contributor be | ||
170 | liable to You for damages, including any direct, indirect, special, | ||
171 | incidental, or consequential damages of any character arising as a | ||
172 | result of this License or out of the use or inability to use the | ||
173 | Work (including but not limited to damages for loss of goodwill, | ||
174 | work stoppage, computer failure or malfunction, or any and all | ||
175 | other commercial damages or losses), even if such Contributor | ||
176 | has been advised of the possibility of such damages. | ||
177 | |||
178 | 9. Accepting Warranty or Additional Liability. While redistributing | ||
179 | the Work or Derivative Works thereof, You may choose to offer, | ||
180 | and charge a fee for, acceptance of support, warranty, indemnity, | ||
181 | or other liability obligations and/or rights consistent with this | ||
182 | License. However, in accepting such obligations, You may act only | ||
183 | on Your own behalf and on Your sole responsibility, not on behalf | ||
184 | of any other Contributor, and only if You agree to indemnify, | ||
185 | defend, and hold each Contributor harmless for any liability | ||
186 | incurred by, or claims asserted against, such Contributor by reason | ||
187 | of your accepting any such warranty or additional liability. | ||
188 | |||
189 | END OF TERMS AND CONDITIONS | ||
190 | |||
diff --git a/run-as/package.c b/run-as/package.c new file mode 100644 index 000000000..46f8239c3 --- /dev/null +++ b/run-as/package.c | |||
@@ -0,0 +1,471 @@ | |||
1 | /* | ||
2 | ** | ||
3 | ** Copyright 2010, The Android Open Source Project | ||
4 | ** | ||
5 | ** Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | ** you may not use this file except in compliance with the License. | ||
7 | ** You may obtain a copy of the License at | ||
8 | ** | ||
9 | ** http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | ** | ||
11 | ** Unless required by applicable law or agreed to in writing, software | ||
12 | ** distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | ** See the License for the specific language governing permissions and | ||
15 | ** limitations under the License. | ||
16 | */ | ||
17 | #include <errno.h> | ||
18 | #include <fcntl.h> | ||
19 | #include <unistd.h> | ||
20 | #include <sys/stat.h> | ||
21 | #include <private/android_filesystem_config.h> | ||
22 | #include "package.h" | ||
23 | |||
24 | /* | ||
25 | * WARNING WARNING WARNING WARNING | ||
26 | * | ||
27 | * The following code runs as root on production devices, before | ||
28 | * the run-as command has dropped the uid/gid. Hence be very | ||
29 | * conservative and keep in mind the following: | ||
30 | * | ||
31 | * - Performance does not matter here, clarity and safety of the code | ||
32 | * does however. Documentation is a must. | ||
33 | * | ||
34 | * - Avoid calling C library functions with complex implementations | ||
35 | * like malloc() and printf(). You want to depend on simple system | ||
36 | * calls instead, which behaviour is not going to be altered in | ||
37 | * unpredictible ways by environment variables or system properties. | ||
38 | * | ||
39 | * - Do not trust user input and/or the filesystem whenever possible. | ||
40 | * | ||
41 | */ | ||
42 | |||
43 | /* The file containing the list of installed packages on the system */ | ||
44 | #define PACKAGES_LIST_FILE "/data/system/packages.list" | ||
45 | |||
46 | /* This should be large enough to hold the content of the package database file */ | ||
47 | #define PACKAGES_LIST_BUFFER_SIZE 8192 | ||
48 | |||
49 | /* Copy 'srclen' string bytes from 'src' into buffer 'dst' of size 'dstlen' | ||
50 | * This function always zero-terminate the destination buffer unless | ||
51 | * 'dstlen' is 0, even in case of overflow. | ||
52 | */ | ||
53 | static void | ||
54 | string_copy(char* dst, size_t dstlen, const char* src, size_t srclen) | ||
55 | { | ||
56 | const char* srcend = src + srclen; | ||
57 | const char* dstend = dst + dstlen; | ||
58 | |||
59 | if (dstlen == 0) | ||
60 | return; | ||
61 | |||
62 | dstend--; /* make room for terminating zero */ | ||
63 | |||
64 | while (dst < dstend && src < srcend && *src != '\0') | ||
65 | *dst++ = *src++; | ||
66 | |||
67 | *dst = '\0'; /* zero-terminate result */ | ||
68 | } | ||
69 | |||
70 | /* Read up to 'buffsize' bytes into 'buff' from the file | ||
71 | * named 'filename'. Return byte length on success, or -1 | ||
72 | * on error. | ||
73 | */ | ||
74 | static int | ||
75 | read_file(const char* filename, char* buff, size_t buffsize) | ||
76 | { | ||
77 | int fd, len, old_errno; | ||
78 | |||
79 | /* check the input buffer size */ | ||
80 | if (buffsize >= INT_MAX) { | ||
81 | errno = EINVAL; | ||
82 | return -1; | ||
83 | } | ||
84 | |||
85 | /* open the file for reading */ | ||
86 | do { | ||
87 | fd = open(filename, O_RDONLY); | ||
88 | } while (fd < 0 && errno == EINTR); | ||
89 | |||
90 | if (fd < 0) | ||
91 | return -1; | ||
92 | |||
93 | /* read the content */ | ||
94 | do { | ||
95 | len = read(fd, buff, buffsize); | ||
96 | } while (len < 0 && errno == EINTR); | ||
97 | |||
98 | /* close the file, preserve old errno for better diagnostics */ | ||
99 | old_errno = errno; | ||
100 | close(fd); | ||
101 | errno = old_errno; | ||
102 | |||
103 | return len; | ||
104 | } | ||
105 | |||
106 | /* Check that a given directory: | ||
107 | * - exists | ||
108 | * - is owned by a given uid/gid | ||
109 | * - is a real directory, not a symlink | ||
110 | * - isn't readable or writable by others | ||
111 | * | ||
112 | * Return 0 on success, or -1 on error. | ||
113 | * errno is set to EINVAL in case of failed check. | ||
114 | */ | ||
115 | static int | ||
116 | check_directory_ownership(const char* path, uid_t uid) | ||
117 | { | ||
118 | int ret; | ||
119 | struct stat st; | ||
120 | |||
121 | do { | ||
122 | ret = lstat(path, &st); | ||
123 | } while (ret < 0 && errno == EINTR); | ||
124 | |||
125 | if (ret < 0) | ||
126 | return -1; | ||
127 | |||
128 | /* must be a real directory, not a symlink */ | ||
129 | if (!S_ISDIR(st.st_mode)) | ||
130 | goto BAD; | ||
131 | |||
132 | /* must be owned by specific uid/gid */ | ||
133 | if (st.st_uid != uid || st.st_gid != uid) | ||
134 | goto BAD; | ||
135 | |||
136 | /* must not be readable or writable by others */ | ||
137 | if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0) | ||
138 | goto BAD; | ||
139 | |||
140 | /* everything ok */ | ||
141 | return 0; | ||
142 | |||
143 | BAD: | ||
144 | errno = EINVAL; | ||
145 | return -1; | ||
146 | } | ||
147 | |||
148 | /* This function is used to check the data directory path for safety. | ||
149 | * We check that every sub-directory is owned by the 'system' user | ||
150 | * and exists and is not a symlink. We also check that the full directory | ||
151 | * path is properly owned by the user ID. | ||
152 | * | ||
153 | * Return 0 on success, -1 on error. | ||
154 | */ | ||
155 | int | ||
156 | check_data_path(const char* dataPath, uid_t uid) | ||
157 | { | ||
158 | int nn; | ||
159 | |||
160 | /* the path should be absolute */ | ||
161 | if (dataPath[0] != '/') { | ||
162 | errno = EINVAL; | ||
163 | return -1; | ||
164 | } | ||
165 | |||
166 | /* look for all sub-paths, we do that by finding | ||
167 | * directory separators in the input path and | ||
168 | * checking each sub-path independently | ||
169 | */ | ||
170 | for (nn = 1; dataPath[nn] != '\0'; nn++) | ||
171 | { | ||
172 | char subpath[PATH_MAX]; | ||
173 | |||
174 | /* skip non-separator characters */ | ||
175 | if (dataPath[nn] != '/') | ||
176 | continue; | ||
177 | |||
178 | /* handle trailing separator case */ | ||
179 | if (dataPath[nn+1] == '\0') { | ||
180 | break; | ||
181 | } | ||
182 | |||
183 | /* found a separator, check that dataPath is not too long. */ | ||
184 | if (nn >= (int)(sizeof subpath)) { | ||
185 | errno = EINVAL; | ||
186 | return -1; | ||
187 | } | ||
188 | |||
189 | /* reject any '..' subpath */ | ||
190 | if (nn >= 3 && | ||
191 | dataPath[nn-3] == '/' && | ||
192 | dataPath[nn-2] == '.' && | ||
193 | dataPath[nn-1] == '.') { | ||
194 | errno = EINVAL; | ||
195 | return -1; | ||
196 | } | ||
197 | |||
198 | /* copy to 'subpath', then check ownership */ | ||
199 | memcpy(subpath, dataPath, nn); | ||
200 | subpath[nn] = '\0'; | ||
201 | |||
202 | if (check_directory_ownership(subpath, AID_SYSTEM) < 0) | ||
203 | return -1; | ||
204 | } | ||
205 | |||
206 | /* All sub-paths were checked, now verify that the full data | ||
207 | * directory is owned by the application uid | ||
208 | */ | ||
209 | if (check_directory_ownership(dataPath, uid) < 0) | ||
210 | return -1; | ||
211 | |||
212 | /* all clear */ | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | /* Return TRUE iff a character is a space or tab */ | ||
217 | static inline int | ||
218 | is_space(char c) | ||
219 | { | ||
220 | return (c == ' ' || c == '\t'); | ||
221 | } | ||
222 | |||
223 | /* Skip any space or tab character from 'p' until 'end' is reached. | ||
224 | * Return new position. | ||
225 | */ | ||
226 | static const char* | ||
227 | skip_spaces(const char* p, const char* end) | ||
228 | { | ||
229 | while (p < end && is_space(*p)) | ||
230 | p++; | ||
231 | |||
232 | return p; | ||
233 | } | ||
234 | |||
235 | /* Skip any non-space and non-tab character from 'p' until 'end'. | ||
236 | * Return new position. | ||
237 | */ | ||
238 | static const char* | ||
239 | skip_non_spaces(const char* p, const char* end) | ||
240 | { | ||
241 | while (p < end && !is_space(*p)) | ||
242 | p++; | ||
243 | |||
244 | return p; | ||
245 | } | ||
246 | |||
247 | /* Find the first occurence of 'ch' between 'p' and 'end' | ||
248 | * Return its position, or 'end' if none is found. | ||
249 | */ | ||
250 | static const char* | ||
251 | find_first(const char* p, const char* end, char ch) | ||
252 | { | ||
253 | while (p < end && *p != ch) | ||
254 | p++; | ||
255 | |||
256 | return p; | ||
257 | } | ||
258 | |||
259 | /* Check that the non-space string starting at 'p' and eventually | ||
260 | * ending at 'end' equals 'name'. Return new position (after name) | ||
261 | * on success, or NULL on failure. | ||
262 | * | ||
263 | * This function fails is 'name' is NULL, empty or contains any space. | ||
264 | */ | ||
265 | static const char* | ||
266 | compare_name(const char* p, const char* end, const char* name) | ||
267 | { | ||
268 | /* 'name' must not be NULL or empty */ | ||
269 | if (name == NULL || name[0] == '\0' || p == end) | ||
270 | return NULL; | ||
271 | |||
272 | /* compare characters to those in 'name', excluding spaces */ | ||
273 | while (*name) { | ||
274 | /* note, we don't check for *p == '\0' since | ||
275 | * it will be caught in the next conditional. | ||
276 | */ | ||
277 | if (p >= end || is_space(*p)) | ||
278 | goto BAD; | ||
279 | |||
280 | if (*p != *name) | ||
281 | goto BAD; | ||
282 | |||
283 | p++; | ||
284 | name++; | ||
285 | } | ||
286 | |||
287 | /* must be followed by end of line or space */ | ||
288 | if (p < end && !is_space(*p)) | ||
289 | goto BAD; | ||
290 | |||
291 | return p; | ||
292 | |||
293 | BAD: | ||
294 | return NULL; | ||
295 | } | ||
296 | |||
297 | /* Parse one or more whitespace characters starting from '*pp' | ||
298 | * until 'end' is reached. Updates '*pp' on exit. | ||
299 | * | ||
300 | * Return 0 on success, -1 on failure. | ||
301 | */ | ||
302 | static int | ||
303 | parse_spaces(const char** pp, const char* end) | ||
304 | { | ||
305 | const char* p = *pp; | ||
306 | |||
307 | if (p >= end || !is_space(*p)) { | ||
308 | errno = EINVAL; | ||
309 | return -1; | ||
310 | } | ||
311 | p = skip_spaces(p, end); | ||
312 | *pp = p; | ||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | /* Parse a positive decimal number starting from '*pp' until 'end' | ||
317 | * is reached. Adjust '*pp' on exit. Return decimal value or -1 | ||
318 | * in case of error. | ||
319 | * | ||
320 | * If the value is larger than INT_MAX, -1 will be returned, | ||
321 | * and errno set to EOVERFLOW. | ||
322 | * | ||
323 | * If '*pp' does not start with a decimal digit, -1 is returned | ||
324 | * and errno set to EINVAL. | ||
325 | */ | ||
326 | static int | ||
327 | parse_positive_decimal(const char** pp, const char* end) | ||
328 | { | ||
329 | const char* p = *pp; | ||
330 | int value = 0; | ||
331 | int overflow = 0; | ||
332 | |||
333 | if (p >= end || *p < '0' || *p > '9') { | ||
334 | errno = EINVAL; | ||
335 | return -1; | ||
336 | } | ||
337 | |||
338 | while (p < end) { | ||
339 | int ch = *p; | ||
340 | unsigned d = (unsigned)(ch - '0'); | ||
341 | int val2; | ||
342 | |||
343 | if (d >= 10U) /* d is unsigned, no lower bound check */ | ||
344 | break; | ||
345 | |||
346 | val2 = value*10 + (int)d; | ||
347 | if (val2 < value) | ||
348 | overflow = 1; | ||
349 | value = val2; | ||
350 | p++; | ||
351 | } | ||
352 | *pp = p; | ||
353 | |||
354 | if (overflow) { | ||
355 | errno = EOVERFLOW; | ||
356 | value = -1; | ||
357 | } | ||
358 | return value; | ||
359 | |||
360 | BAD: | ||
361 | *pp = p; | ||
362 | return -1; | ||
363 | } | ||
364 | |||
365 | /* Read the system's package database and extract information about | ||
366 | * 'pkgname'. Return 0 in case of success, or -1 in case of error. | ||
367 | * | ||
368 | * If the package is unknown, return -1 and set errno to ENOENT | ||
369 | * If the package database is corrupted, return -1 and set errno to EINVAL | ||
370 | */ | ||
371 | int | ||
372 | get_package_info(const char* pkgName, PackageInfo *info) | ||
373 | { | ||
374 | static char buffer[PACKAGES_LIST_BUFFER_SIZE]; | ||
375 | int buffer_len; | ||
376 | const char* p; | ||
377 | const char* buffer_end; | ||
378 | int result; | ||
379 | |||
380 | info->uid = 0; | ||
381 | info->isDebuggable = 0; | ||
382 | info->dataDir[0] = '\0'; | ||
383 | |||
384 | buffer_len = read_file(PACKAGES_LIST_FILE, buffer, sizeof buffer); | ||
385 | if (buffer_len < 0) | ||
386 | return -1; | ||
387 | |||
388 | p = buffer; | ||
389 | buffer_end = buffer + buffer_len; | ||
390 | |||
391 | /* expect the following format on each line of the control file: | ||
392 | * | ||
393 | * <pkgName> <uid> <debugFlag> <dataDir> | ||
394 | * | ||
395 | * where: | ||
396 | * <pkgName> is the package's name | ||
397 | * <uid> is the application-specific user Id (decimal) | ||
398 | * <debugFlag> is 1 if the package is debuggable, or 0 otherwise | ||
399 | * <dataDir> is the path to the package's data directory (e.g. /data/data/com.example.foo) | ||
400 | * | ||
401 | * The file is generated in com.android.server.PackageManagerService.Settings.writeLP() | ||
402 | */ | ||
403 | |||
404 | while (p < buffer_end) { | ||
405 | /* find end of current line and start of next one */ | ||
406 | const char* end = find_first(p, buffer_end, '\n'); | ||
407 | const char* next = (end < buffer_end) ? end + 1 : buffer_end; | ||
408 | const char* q; | ||
409 | int uid, debugFlag; | ||
410 | |||
411 | /* first field is the package name */ | ||
412 | p = compare_name(p, end, pkgName); | ||
413 | if (p == NULL) | ||
414 | goto NEXT_LINE; | ||
415 | |||
416 | /* skip spaces */ | ||
417 | if (parse_spaces(&p, end) < 0) | ||
418 | goto BAD_FORMAT; | ||
419 | |||
420 | /* second field is the pid */ | ||
421 | uid = parse_positive_decimal(&p, end); | ||
422 | if (uid < 0) | ||
423 | return -1; | ||
424 | |||
425 | info->uid = (uid_t) uid; | ||
426 | |||
427 | /* skip spaces */ | ||
428 | if (parse_spaces(&p, end) < 0) | ||
429 | goto BAD_FORMAT; | ||
430 | |||
431 | /* third field is debug flag (0 or 1) */ | ||
432 | debugFlag = parse_positive_decimal(&p, end); | ||
433 | switch (debugFlag) { | ||
434 | case 0: | ||
435 | info->isDebuggable = 0; | ||
436 | break; | ||
437 | case 1: | ||
438 | info->isDebuggable = 1; | ||
439 | break; | ||
440 | default: | ||
441 | goto BAD_FORMAT; | ||
442 | } | ||
443 | |||
444 | /* skip spaces */ | ||
445 | if (parse_spaces(&p, end) < 0) | ||
446 | goto BAD_FORMAT; | ||
447 | |||
448 | /* fourth field is data directory path and must not contain | ||
449 | * spaces. | ||
450 | */ | ||
451 | q = skip_non_spaces(p, end); | ||
452 | if (q == p) | ||
453 | goto BAD_FORMAT; | ||
454 | |||
455 | string_copy(info->dataDir, sizeof info->dataDir, p, q - p); | ||
456 | |||
457 | /* Ignore the rest */ | ||
458 | return 0; | ||
459 | |||
460 | NEXT_LINE: | ||
461 | p = next; | ||
462 | } | ||
463 | |||
464 | /* the package is unknown */ | ||
465 | errno = ENOENT; | ||
466 | return -1; | ||
467 | |||
468 | BAD_FORMAT: | ||
469 | errno = EINVAL; | ||
470 | return -1; | ||
471 | } | ||
diff --git a/run-as/package.h b/run-as/package.h new file mode 100644 index 000000000..852af0632 --- /dev/null +++ b/run-as/package.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | ** | ||
3 | ** Copyright 2010, The Android Open Source Project | ||
4 | ** | ||
5 | ** Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | ** you may not use this file except in compliance with the License. | ||
7 | ** You may obtain a copy of the License at | ||
8 | ** | ||
9 | ** http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | ** | ||
11 | ** Unless required by applicable law or agreed to in writing, software | ||
12 | ** distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | ** See the License for the specific language governing permissions and | ||
15 | ** limitations under the License. | ||
16 | */ | ||
17 | #ifndef RUN_AS_PACKAGE_H | ||
18 | #define RUN_AS_PACKAGE_H | ||
19 | |||
20 | #include <limits.h> | ||
21 | #include <sys/types.h> | ||
22 | |||
23 | typedef enum { | ||
24 | PACKAGE_IS_DEBUGGABLE = 0, | ||
25 | PACKAGE_IS_NOT_DEBUGGABLE, | ||
26 | PACKAGE_IS_UNKNOWN, | ||
27 | } PackageStatus; | ||
28 | |||
29 | typedef struct { | ||
30 | uid_t uid; | ||
31 | char isDebuggable; | ||
32 | char dataDir[PATH_MAX]; | ||
33 | } PackageInfo; | ||
34 | |||
35 | /* see documentation in package.c for these functiosn */ | ||
36 | |||
37 | extern int get_package_info(const char* packageName, PackageInfo* info); | ||
38 | |||
39 | extern int check_data_path(const char* dataDir, uid_t uid); | ||
40 | |||
41 | #endif /* RUN_AS_PACKAGE_H */ | ||
diff --git a/run-as/run-as.c b/run-as/run-as.c new file mode 100644 index 000000000..d2a44e1b5 --- /dev/null +++ b/run-as/run-as.c | |||
@@ -0,0 +1,178 @@ | |||
1 | /* | ||
2 | ** | ||
3 | ** Copyright 2010, The Android Open Source Project | ||
4 | ** | ||
5 | ** Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | ** you may not use this file except in compliance with the License. | ||
7 | ** You may obtain a copy of the License at | ||
8 | ** | ||
9 | ** http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | ** | ||
11 | ** Unless required by applicable law or agreed to in writing, software | ||
12 | ** distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | ** See the License for the specific language governing permissions and | ||
15 | ** limitations under the License. | ||
16 | */ | ||
17 | |||
18 | #define PROGNAME "run-as" | ||
19 | #define LOG_TAG PROGNAME | ||
20 | |||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <string.h> | ||
24 | #include <sys/types.h> | ||
25 | #include <sys/stat.h> | ||
26 | #include <dirent.h> | ||
27 | #include <errno.h> | ||
28 | #include <unistd.h> | ||
29 | #include <time.h> | ||
30 | #include <stdarg.h> | ||
31 | |||
32 | #include <private/android_filesystem_config.h> | ||
33 | #include "package.h" | ||
34 | |||
35 | /* | ||
36 | * WARNING WARNING WARNING WARNING | ||
37 | * | ||
38 | * This program runs as set-uid root on Android production devices. | ||
39 | * Be very conservative when modifying it to avoid any serious | ||
40 | * security issue. Keep in mind the following: | ||
41 | * | ||
42 | * - This program should only run for the 'root' or 'shell' users | ||
43 | * | ||
44 | * - Statically link against the C library, and avoid anything that | ||
45 | * is more complex than simple system calls until the uid/gid has | ||
46 | * been dropped to that of a normal user or you are sure to exit. | ||
47 | * | ||
48 | * This avoids depending on environment variables, system properties | ||
49 | * and other external factors that may affect the C library in | ||
50 | * unpredictable ways. | ||
51 | * | ||
52 | * - Do not trust user input and/or the filesystem whenever possible. | ||
53 | * | ||
54 | * Read README.TXT for more details. | ||
55 | * | ||
56 | * | ||
57 | * | ||
58 | * The purpose of this program is to run a command as a specific | ||
59 | * application user-id. Typical usage is: | ||
60 | * | ||
61 | * run-as <package-name> <command> <args> | ||
62 | * | ||
63 | * The 'run-as' binary is setuid, but will check the following: | ||
64 | * | ||
65 | * - that it is invoked from the 'shell' or 'root' user (abort otherwise) | ||
66 | * - that '<package-name>' is the name of an installed and debuggable package | ||
67 | * - that the package's data directory is well-formed (see package.c) | ||
68 | * | ||
69 | * If so, it will cd to the package's data directory, drop to the application's | ||
70 | * user id / group id then run the command there. | ||
71 | * | ||
72 | * This can be useful for a number of different things on production devices: | ||
73 | * | ||
74 | * - Allow application developers to look at their own applicative data | ||
75 | * during development. | ||
76 | * | ||
77 | * - Run the 'gdbserver' binary executable to allow native debugging | ||
78 | */ | ||
79 | |||
80 | static void | ||
81 | usage(void) | ||
82 | { | ||
83 | const char* str = "Usage: " PROGNAME " <package-name> <command> [<args>]\n\n"; | ||
84 | write(1, str, strlen(str)); | ||
85 | exit(1); | ||
86 | } | ||
87 | |||
88 | |||
89 | static void | ||
90 | panic(const char* format, ...) | ||
91 | { | ||
92 | va_list args; | ||
93 | |||
94 | fprintf(stderr, "%s: ", PROGNAME); | ||
95 | va_start(args, format); | ||
96 | vfprintf(stderr, format, args); | ||
97 | va_end(args); | ||
98 | exit(1); | ||
99 | } | ||
100 | |||
101 | |||
102 | int main(int argc, char **argv) | ||
103 | { | ||
104 | const char* pkgname; | ||
105 | int myuid, uid, gid; | ||
106 | PackageInfo info; | ||
107 | |||
108 | /* check arguments */ | ||
109 | if (argc < 2) | ||
110 | usage(); | ||
111 | |||
112 | /* check userid of caller - must be 'shell' or 'root' */ | ||
113 | myuid = getuid(); | ||
114 | if (myuid != AID_SHELL && myuid != AID_ROOT) { | ||
115 | panic("only 'shell' or 'root' users can run this program\n"); | ||
116 | } | ||
117 | |||
118 | /* retrieve package information from system */ | ||
119 | pkgname = argv[1]; | ||
120 | if (get_package_info(pkgname, &info) < 0) { | ||
121 | panic("Package '%s' is unknown\n", pkgname); | ||
122 | return 1; | ||
123 | } | ||
124 | |||
125 | /* reject system packages */ | ||
126 | if (info.uid < AID_APP) { | ||
127 | panic("Package '%s' is not an application\n", pkgname); | ||
128 | return 1; | ||
129 | } | ||
130 | |||
131 | /* reject any non-debuggable package */ | ||
132 | if (!info.isDebuggable) { | ||
133 | panic("Package '%s' is not debuggable\n", pkgname); | ||
134 | return 1; | ||
135 | } | ||
136 | |||
137 | /* check that the data directory path is valid */ | ||
138 | if (check_data_path(info.dataDir, info.uid) < 0) { | ||
139 | panic("Package '%s' has corrupt installation\n", pkgname); | ||
140 | return 1; | ||
141 | } | ||
142 | |||
143 | /* then move to it */ | ||
144 | { | ||
145 | int ret; | ||
146 | do { | ||
147 | ret = chdir(info.dataDir); | ||
148 | } while (ret < 0 && errno == EINTR); | ||
149 | |||
150 | if (ret < 0) { | ||
151 | panic("Could not cd to package's data directory: %s\n", strerror(errno)); | ||
152 | return 1; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | /* Ensure that we change all real/effective/saved IDs at the | ||
157 | * same time to avoid nasty surprises. | ||
158 | */ | ||
159 | uid = gid = info.uid; | ||
160 | if(setresgid(gid,gid,gid) || setresuid(uid,uid,uid)) { | ||
161 | panic("Permission denied\n"); | ||
162 | return 1; | ||
163 | } | ||
164 | |||
165 | /* User specified command for exec. */ | ||
166 | if (argc >= 3 ) { | ||
167 | if (execvp(argv[2], argv+2) < 0) { | ||
168 | panic("exec failed for %s Error:%s\n", argv[2], strerror(errno)); | ||
169 | return -errno; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | /* Default exec shell. */ | ||
174 | execlp("/system/bin/sh", "sh", NULL); | ||
175 | |||
176 | panic("exec failed\n"); | ||
177 | return 1; | ||
178 | } | ||