Annotation of /trunk/mkinitrd-magellan/busybox/libbb/appletlib.c
Parent Directory | Revision Log
Revision 1123 -
(hide annotations)
(download)
Wed Aug 18 21:56:57 2010 UTC (13 years, 9 months ago) by niro
File MIME type: text/plain
File size: 22165 byte(s)
Wed Aug 18 21:56:57 2010 UTC (13 years, 9 months ago) by niro
File MIME type: text/plain
File size: 22165 byte(s)
-updated to busybox-1.17.1
1 | niro | 816 | /* vi: set sw=4 ts=4: */ |
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) tons of folks. Tracking down who wrote what | ||
6 | * isn't something I'm going to worry about... If you wrote something | ||
7 | * here, please feel free to acknowledge your work. | ||
8 | * | ||
9 | * Based in part on code from sash, Copyright (c) 1999 by David I. Bell | ||
10 | * Permission has been granted to redistribute this code under the GPL. | ||
11 | * | ||
12 | * Licensed under GPLv2 or later, see file License in this tarball for details. | ||
13 | */ | ||
14 | |||
15 | /* We are trying to not use printf, this benefits the case when selected | ||
16 | * applets are really simple. Example: | ||
17 | * | ||
18 | * $ ./busybox | ||
19 | * ... | ||
20 | * Currently defined functions: | ||
21 | * basename, false, true | ||
22 | * | ||
23 | * $ size busybox | ||
24 | * text data bss dec hex filename | ||
25 | * 4473 52 72 4597 11f5 busybox | ||
26 | * | ||
27 | * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :( | ||
28 | */ | ||
29 | niro | 984 | #include "busybox.h" |
30 | niro | 816 | #include <assert.h> |
31 | niro | 984 | #include <malloc.h> |
32 | niro | 1123 | /* Try to pull in PAGE_SIZE */ |
33 | #ifdef __linux__ | ||
34 | # include <sys/user.h> | ||
35 | #endif | ||
36 | #ifdef __GNU__ /* Hurd */ | ||
37 | # include <mach/vm_param.h> | ||
38 | #endif | ||
39 | niro | 816 | |
40 | |||
41 | /* Declare <applet>_main() */ | ||
42 | #define PROTOTYPES | ||
43 | #include "applets.h" | ||
44 | #undef PROTOTYPES | ||
45 | |||
46 | |||
47 | /* Include generated applet names, pointers to <applet>_main, etc */ | ||
48 | #include "applet_tables.h" | ||
49 | /* ...and if applet_tables generator says we have only one applet... */ | ||
50 | #ifdef SINGLE_APPLET_MAIN | ||
51 | niro | 1123 | # undef ENABLE_FEATURE_INDIVIDUAL |
52 | # define ENABLE_FEATURE_INDIVIDUAL 1 | ||
53 | # undef IF_FEATURE_INDIVIDUAL | ||
54 | # define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__ | ||
55 | niro | 816 | #endif |
56 | |||
57 | |||
58 | niro | 1123 | #include "usage_compressed.h" |
59 | |||
60 | #if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE | ||
61 | static const char usage_messages[] ALIGN1 = UNPACKED_USAGE; | ||
62 | #else | ||
63 | # define usage_messages 0 | ||
64 | #endif | ||
65 | |||
66 | niro | 816 | #if ENABLE_FEATURE_COMPRESS_USAGE |
67 | |||
68 | niro | 1123 | static const char packed_usage[] ALIGN1 = { PACKED_USAGE }; |
69 | # include "unarchive.h" | ||
70 | niro | 816 | static const char *unpack_usage_messages(void) |
71 | { | ||
72 | char *outbuf = NULL; | ||
73 | bunzip_data *bd; | ||
74 | int i; | ||
75 | |||
76 | i = start_bunzip(&bd, | ||
77 | /* src_fd: */ -1, | ||
78 | niro | 984 | /* inbuf: */ (void *)packed_usage, |
79 | niro | 816 | /* len: */ sizeof(packed_usage)); |
80 | /* read_bunzip can longjmp to start_bunzip, and ultimately | ||
81 | * end up here with i != 0 on read data errors! Not trivial */ | ||
82 | if (!i) { | ||
83 | /* Cannot use xmalloc: will leak bd in NOFORK case! */ | ||
84 | niro | 1123 | outbuf = malloc_or_warn(sizeof(UNPACKED_USAGE)); |
85 | niro | 816 | if (outbuf) |
86 | niro | 1123 | read_bunzip(bd, outbuf, sizeof(UNPACKED_USAGE)); |
87 | niro | 816 | } |
88 | dealloc_bunzip(bd); | ||
89 | return outbuf; | ||
90 | } | ||
91 | niro | 1123 | # define dealloc_usage_messages(s) free(s) |
92 | niro | 816 | |
93 | #else | ||
94 | |||
95 | niro | 1123 | # define unpack_usage_messages() usage_messages |
96 | # define dealloc_usage_messages(s) ((void)(s)) | ||
97 | niro | 816 | |
98 | #endif /* FEATURE_COMPRESS_USAGE */ | ||
99 | |||
100 | |||
101 | void FAST_FUNC bb_show_usage(void) | ||
102 | { | ||
103 | if (ENABLE_SHOW_USAGE) { | ||
104 | #ifdef SINGLE_APPLET_STR | ||
105 | /* Imagine that this applet is "true". Dont suck in printf! */ | ||
106 | const char *p; | ||
107 | const char *usage_string = p = unpack_usage_messages(); | ||
108 | |||
109 | if (*p == '\b') { | ||
110 | niro | 984 | full_write2_str("No help available.\n\n"); |
111 | niro | 816 | } else { |
112 | niro | 984 | full_write2_str("Usage: "SINGLE_APPLET_STR" "); |
113 | niro | 816 | full_write2_str(p); |
114 | full_write2_str("\n\n"); | ||
115 | } | ||
116 | niro | 984 | if (ENABLE_FEATURE_CLEAN_UP) |
117 | dealloc_usage_messages((char*)usage_string); | ||
118 | niro | 816 | #else |
119 | const char *p; | ||
120 | const char *usage_string = p = unpack_usage_messages(); | ||
121 | int ap = find_applet_by_name(applet_name); | ||
122 | |||
123 | if (ap < 0) /* never happens, paranoia */ | ||
124 | xfunc_die(); | ||
125 | while (ap) { | ||
126 | while (*p++) continue; | ||
127 | ap--; | ||
128 | } | ||
129 | full_write2_str(bb_banner); | ||
130 | niro | 984 | full_write2_str(" multi-call binary.\n"); |
131 | niro | 816 | if (*p == '\b') |
132 | full_write2_str("\nNo help available.\n\n"); | ||
133 | else { | ||
134 | full_write2_str("\nUsage: "); | ||
135 | full_write2_str(applet_name); | ||
136 | full_write2_str(" "); | ||
137 | full_write2_str(p); | ||
138 | full_write2_str("\n\n"); | ||
139 | } | ||
140 | niro | 984 | if (ENABLE_FEATURE_CLEAN_UP) |
141 | dealloc_usage_messages((char*)usage_string); | ||
142 | niro | 816 | #endif |
143 | } | ||
144 | xfunc_die(); | ||
145 | } | ||
146 | |||
147 | #if NUM_APPLETS > 8 | ||
148 | /* NB: any char pointer will work as well, not necessarily applet_names */ | ||
149 | static int applet_name_compare(const void *name, const void *v) | ||
150 | { | ||
151 | int i = (const char *)v - applet_names; | ||
152 | return strcmp(name, APPLET_NAME(i)); | ||
153 | } | ||
154 | #endif | ||
155 | int FAST_FUNC find_applet_by_name(const char *name) | ||
156 | { | ||
157 | #if NUM_APPLETS > 8 | ||
158 | /* Do a binary search to find the applet entry given the name. */ | ||
159 | const char *p; | ||
160 | p = bsearch(name, applet_names, ARRAY_SIZE(applet_main), 1, applet_name_compare); | ||
161 | if (!p) | ||
162 | return -1; | ||
163 | return p - applet_names; | ||
164 | #else | ||
165 | /* A version which does not pull in bsearch */ | ||
166 | int i = 0; | ||
167 | const char *p = applet_names; | ||
168 | while (i < NUM_APPLETS) { | ||
169 | if (strcmp(name, p) == 0) | ||
170 | return i; | ||
171 | p += strlen(p) + 1; | ||
172 | i++; | ||
173 | } | ||
174 | return -1; | ||
175 | #endif | ||
176 | } | ||
177 | |||
178 | |||
179 | void lbb_prepare(const char *applet | ||
180 | niro | 984 | IF_FEATURE_INDIVIDUAL(, char **argv)) |
181 | niro | 816 | MAIN_EXTERNALLY_VISIBLE; |
182 | void lbb_prepare(const char *applet | ||
183 | niro | 984 | IF_FEATURE_INDIVIDUAL(, char **argv)) |
184 | niro | 816 | { |
185 | #ifdef __GLIBC__ | ||
186 | (*(int **)&bb_errno) = __errno_location(); | ||
187 | barrier(); | ||
188 | #endif | ||
189 | applet_name = applet; | ||
190 | |||
191 | /* Set locale for everybody except 'init' */ | ||
192 | if (ENABLE_LOCALE_SUPPORT && getpid() != 1) | ||
193 | setlocale(LC_ALL, ""); | ||
194 | |||
195 | #if ENABLE_FEATURE_INDIVIDUAL | ||
196 | /* Redundant for busybox (run_applet_and_exit covers that case) | ||
197 | * but needed for "individual applet" mode */ | ||
198 | niro | 1123 | if (argv[1] |
199 | && !argv[2] | ||
200 | && strcmp(argv[1], "--help") == 0 | ||
201 | && strncmp(applet, "busybox", 7) != 0 | ||
202 | ) { | ||
203 | niro | 816 | /* Special case. POSIX says "test --help" |
204 | * should be no different from e.g. "test --foo". */ | ||
205 | if (!ENABLE_TEST || strcmp(applet_name, "test") != 0) | ||
206 | bb_show_usage(); | ||
207 | } | ||
208 | #endif | ||
209 | } | ||
210 | |||
211 | /* The code below can well be in applets/applets.c, as it is used only | ||
212 | * for busybox binary, not "individual" binaries. | ||
213 | * However, keeping it here and linking it into libbusybox.so | ||
214 | * (together with remaining tiny applets/applets.o) | ||
215 | * makes it possible to avoid --whole-archive at link time. | ||
216 | * This makes (shared busybox) + libbusybox smaller. | ||
217 | * (--gc-sections would be even better....) | ||
218 | */ | ||
219 | |||
220 | const char *applet_name; | ||
221 | #if !BB_MMU | ||
222 | bool re_execed; | ||
223 | #endif | ||
224 | |||
225 | |||
226 | /* If not built as a single-applet executable... */ | ||
227 | #if !defined(SINGLE_APPLET_MAIN) | ||
228 | |||
229 | niro | 984 | IF_FEATURE_SUID(static uid_t ruid;) /* real uid */ |
230 | niro | 816 | |
231 | #if ENABLE_FEATURE_SUID_CONFIG | ||
232 | |||
233 | /* applets[] is const, so we have to define this "override" structure */ | ||
234 | static struct BB_suid_config { | ||
235 | int m_applet; | ||
236 | uid_t m_uid; | ||
237 | gid_t m_gid; | ||
238 | mode_t m_mode; | ||
239 | struct BB_suid_config *m_next; | ||
240 | } *suid_config; | ||
241 | |||
242 | static bool suid_cfg_readable; | ||
243 | |||
244 | /* check if u is member of group g */ | ||
245 | static int ingroup(uid_t u, gid_t g) | ||
246 | { | ||
247 | struct group *grp = getgrgid(g); | ||
248 | |||
249 | if (grp) { | ||
250 | char **mem; | ||
251 | |||
252 | for (mem = grp->gr_mem; *mem; mem++) { | ||
253 | struct passwd *pwd = getpwnam(*mem); | ||
254 | |||
255 | if (pwd && (pwd->pw_uid == u)) | ||
256 | return 1; | ||
257 | } | ||
258 | } | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | /* This should probably be a libbb routine. In that case, | ||
263 | * I'd probably rename it to something like bb_trimmed_slice. | ||
264 | */ | ||
265 | static char *get_trimmed_slice(char *s, char *e) | ||
266 | { | ||
267 | /* First, consider the value at e to be nul and back up until we | ||
268 | * reach a non-space char. Set the char after that (possibly at | ||
269 | * the original e) to nul. */ | ||
270 | while (e-- > s) { | ||
271 | if (!isspace(*e)) { | ||
272 | break; | ||
273 | } | ||
274 | } | ||
275 | e[1] = '\0'; | ||
276 | |||
277 | /* Next, advance past all leading space and return a ptr to the | ||
278 | * first non-space char; possibly the terminating nul. */ | ||
279 | return skip_whitespace(s); | ||
280 | } | ||
281 | |||
282 | /* Don't depend on the tools to combine strings. */ | ||
283 | static const char config_file[] ALIGN1 = "/etc/busybox.conf"; | ||
284 | |||
285 | /* We don't supply a value for the nul, so an index adjustment is | ||
286 | * necessary below. Also, we use unsigned short here to save some | ||
287 | * space even though these are really mode_t values. */ | ||
288 | static const unsigned short mode_mask[] ALIGN2 = { | ||
289 | /* SST sst xxx --- */ | ||
290 | S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */ | ||
291 | S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */ | ||
292 | 0, S_IXOTH, S_IXOTH, 0 /* other */ | ||
293 | }; | ||
294 | |||
295 | #define parse_error(x) do { errmsg = x; goto pe_label; } while (0) | ||
296 | |||
297 | static void parse_config_file(void) | ||
298 | { | ||
299 | struct BB_suid_config *sct_head; | ||
300 | struct BB_suid_config *sct; | ||
301 | int applet_no; | ||
302 | FILE *f; | ||
303 | const char *errmsg; | ||
304 | char *s; | ||
305 | char *e; | ||
306 | int i; | ||
307 | unsigned lc; | ||
308 | smallint section; | ||
309 | char buffer[256]; | ||
310 | struct stat st; | ||
311 | |||
312 | assert(!suid_config); /* Should be set to NULL by bss init. */ | ||
313 | |||
314 | ruid = getuid(); | ||
315 | if (ruid == 0) /* run by root - don't need to even read config file */ | ||
316 | return; | ||
317 | |||
318 | if ((stat(config_file, &st) != 0) /* No config file? */ | ||
319 | || !S_ISREG(st.st_mode) /* Not a regular file? */ | ||
320 | || (st.st_uid != 0) /* Not owned by root? */ | ||
321 | || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */ | ||
322 | || !(f = fopen_for_read(config_file)) /* Cannot open? */ | ||
323 | ) { | ||
324 | return; | ||
325 | } | ||
326 | |||
327 | suid_cfg_readable = 1; | ||
328 | sct_head = NULL; | ||
329 | section = lc = 0; | ||
330 | |||
331 | while (1) { | ||
332 | s = buffer; | ||
333 | |||
334 | if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */ | ||
335 | // why? | ||
336 | if (ferror(f)) { /* Make sure it wasn't a read error. */ | ||
337 | parse_error("reading"); | ||
338 | } | ||
339 | fclose(f); | ||
340 | suid_config = sct_head; /* Success, so set the pointer. */ | ||
341 | return; | ||
342 | } | ||
343 | |||
344 | lc++; /* Got a (partial) line. */ | ||
345 | |||
346 | /* If a line is too long for our buffer, we consider it an error. | ||
347 | * The following test does mistreat one corner case though. | ||
348 | * If the final line of the file does not end with a newline and | ||
349 | * yet exactly fills the buffer, it will be treated as too long | ||
350 | * even though there isn't really a problem. But it isn't really | ||
351 | * worth adding code to deal with such an unlikely situation, and | ||
352 | * we do err on the side of caution. Besides, the line would be | ||
353 | * too long if it did end with a newline. */ | ||
354 | if (!strchr(s, '\n') && !feof(f)) { | ||
355 | parse_error("line too long"); | ||
356 | } | ||
357 | |||
358 | /* Trim leading and trailing whitespace, ignoring comments, and | ||
359 | * check if the resulting string is empty. */ | ||
360 | s = get_trimmed_slice(s, strchrnul(s, '#')); | ||
361 | if (!*s) { | ||
362 | continue; | ||
363 | } | ||
364 | |||
365 | /* Check for a section header. */ | ||
366 | |||
367 | if (*s == '[') { | ||
368 | /* Unlike the old code, we ignore leading and trailing | ||
369 | * whitespace for the section name. We also require that | ||
370 | * there are no stray characters after the closing bracket. */ | ||
371 | e = strchr(s, ']'); | ||
372 | if (!e /* Missing right bracket? */ | ||
373 | || e[1] /* Trailing characters? */ | ||
374 | || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */ | ||
375 | ) { | ||
376 | parse_error("section header"); | ||
377 | } | ||
378 | /* Right now we only have one section so just check it. | ||
379 | * If more sections are added in the future, please don't | ||
380 | * resort to cascading ifs with multiple strcasecmp calls. | ||
381 | * That kind of bloated code is all too common. A loop | ||
382 | * and a string table would be a better choice unless the | ||
383 | * number of sections is very small. */ | ||
384 | if (strcasecmp(s, "SUID") == 0) { | ||
385 | section = 1; | ||
386 | continue; | ||
387 | } | ||
388 | section = -1; /* Unknown section so set to skip. */ | ||
389 | continue; | ||
390 | } | ||
391 | |||
392 | /* Process sections. */ | ||
393 | |||
394 | if (section == 1) { /* SUID */ | ||
395 | /* Since we trimmed leading and trailing space above, we're | ||
396 | * now looking for strings of the form | ||
397 | * <key>[::space::]*=[::space::]*<value> | ||
398 | * where both key and value could contain inner whitespace. */ | ||
399 | |||
400 | /* First get the key (an applet name in our case). */ | ||
401 | e = strchr(s, '='); | ||
402 | if (e) { | ||
403 | s = get_trimmed_slice(s, e); | ||
404 | } | ||
405 | if (!e || !*s) { /* Missing '=' or empty key. */ | ||
406 | parse_error("keyword"); | ||
407 | } | ||
408 | |||
409 | /* Ok, we have an applet name. Process the rhs if this | ||
410 | * applet is currently built in and ignore it otherwise. | ||
411 | * Note: this can hide config file bugs which only pop | ||
412 | * up when the busybox configuration is changed. */ | ||
413 | applet_no = find_applet_by_name(s); | ||
414 | if (applet_no >= 0) { | ||
415 | /* Note: We currently don't check for duplicates! | ||
416 | * The last config line for each applet will be the | ||
417 | * one used since we insert at the head of the list. | ||
418 | * I suppose this could be considered a feature. */ | ||
419 | sct = xmalloc(sizeof(struct BB_suid_config)); | ||
420 | sct->m_applet = applet_no; | ||
421 | sct->m_mode = 0; | ||
422 | sct->m_next = sct_head; | ||
423 | sct_head = sct; | ||
424 | |||
425 | /* Get the specified mode. */ | ||
426 | |||
427 | e = skip_whitespace(e+1); | ||
428 | |||
429 | for (i = 0; i < 3; i++) { | ||
430 | /* There are 4 chars + 1 nul for each of user/group/other. */ | ||
431 | static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-"; | ||
432 | |||
433 | const char *q; | ||
434 | q = strchrnul(mode_chars + 5*i, *e++); | ||
435 | if (!*q) { | ||
436 | parse_error("mode"); | ||
437 | } | ||
438 | /* Adjust by -i to account for nul. */ | ||
439 | sct->m_mode |= mode_mask[(q - mode_chars) - i]; | ||
440 | } | ||
441 | |||
442 | /* Now get the the user/group info. */ | ||
443 | |||
444 | s = skip_whitespace(e); | ||
445 | |||
446 | /* Note: we require whitespace between the mode and the | ||
447 | * user/group info. */ | ||
448 | if ((s == e) || !(e = strchr(s, '.'))) { | ||
449 | parse_error("<uid>.<gid>"); | ||
450 | } | ||
451 | *e++ = '\0'; | ||
452 | |||
453 | /* We can't use get_ug_id here since it would exit() | ||
454 | * if a uid or gid was not found. Oh well... */ | ||
455 | sct->m_uid = bb_strtoul(s, NULL, 10); | ||
456 | if (errno) { | ||
457 | struct passwd *pwd = getpwnam(s); | ||
458 | if (!pwd) { | ||
459 | parse_error("user"); | ||
460 | } | ||
461 | sct->m_uid = pwd->pw_uid; | ||
462 | } | ||
463 | |||
464 | sct->m_gid = bb_strtoul(e, NULL, 10); | ||
465 | if (errno) { | ||
466 | struct group *grp; | ||
467 | grp = getgrnam(e); | ||
468 | if (!grp) { | ||
469 | parse_error("group"); | ||
470 | } | ||
471 | sct->m_gid = grp->gr_gid; | ||
472 | } | ||
473 | } | ||
474 | continue; | ||
475 | } | ||
476 | |||
477 | /* Unknown sections are ignored. */ | ||
478 | |||
479 | /* Encountering configuration lines prior to seeing a | ||
480 | * section header is treated as an error. This is how | ||
481 | * the old code worked, but it may not be desirable. | ||
482 | * We may want to simply ignore such lines in case they | ||
483 | * are used in some future version of busybox. */ | ||
484 | if (!section) { | ||
485 | parse_error("keyword outside section"); | ||
486 | } | ||
487 | |||
488 | } /* while (1) */ | ||
489 | |||
490 | pe_label: | ||
491 | fprintf(stderr, "Parse error in %s, line %d: %s\n", | ||
492 | config_file, lc, errmsg); | ||
493 | |||
494 | fclose(f); | ||
495 | /* Release any allocated memory before returning. */ | ||
496 | while (sct_head) { | ||
497 | sct = sct_head->m_next; | ||
498 | free(sct_head); | ||
499 | sct_head = sct; | ||
500 | } | ||
501 | } | ||
502 | #else | ||
503 | static inline void parse_config_file(void) | ||
504 | { | ||
505 | niro | 984 | IF_FEATURE_SUID(ruid = getuid();) |
506 | niro | 816 | } |
507 | #endif /* FEATURE_SUID_CONFIG */ | ||
508 | |||
509 | |||
510 | #if ENABLE_FEATURE_SUID | ||
511 | static void check_suid(int applet_no) | ||
512 | { | ||
513 | gid_t rgid; /* real gid */ | ||
514 | |||
515 | if (ruid == 0) /* set by parse_config_file() */ | ||
516 | return; /* run by root - no need to check more */ | ||
517 | rgid = getgid(); | ||
518 | |||
519 | #if ENABLE_FEATURE_SUID_CONFIG | ||
520 | if (suid_cfg_readable) { | ||
521 | uid_t uid; | ||
522 | struct BB_suid_config *sct; | ||
523 | mode_t m; | ||
524 | |||
525 | for (sct = suid_config; sct; sct = sct->m_next) { | ||
526 | if (sct->m_applet == applet_no) | ||
527 | goto found; | ||
528 | } | ||
529 | goto check_need_suid; | ||
530 | found: | ||
531 | m = sct->m_mode; | ||
532 | if (sct->m_uid == ruid) | ||
533 | /* same uid */ | ||
534 | m >>= 6; | ||
535 | else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid)) | ||
536 | /* same group / in group */ | ||
537 | m >>= 3; | ||
538 | |||
539 | if (!(m & S_IXOTH)) /* is x bit not set ? */ | ||
540 | bb_error_msg_and_die("you have no permission to run this applet!"); | ||
541 | |||
542 | /* _both_ sgid and group_exec have to be set for setegid */ | ||
543 | if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) | ||
544 | rgid = sct->m_gid; | ||
545 | /* else (no setegid) we will set egid = rgid */ | ||
546 | |||
547 | /* We set effective AND saved ids. If saved-id is not set | ||
548 | * like we do below, seteiud(0) can still later succeed! */ | ||
549 | if (setresgid(-1, rgid, rgid)) | ||
550 | bb_perror_msg_and_die("setresgid"); | ||
551 | |||
552 | /* do we have to set effective uid? */ | ||
553 | uid = ruid; | ||
554 | if (sct->m_mode & S_ISUID) | ||
555 | uid = sct->m_uid; | ||
556 | /* else (no seteuid) we will set euid = ruid */ | ||
557 | |||
558 | if (setresuid(-1, uid, uid)) | ||
559 | bb_perror_msg_and_die("setresuid"); | ||
560 | return; | ||
561 | } | ||
562 | #if !ENABLE_FEATURE_SUID_CONFIG_QUIET | ||
563 | { | ||
564 | static bool onetime = 0; | ||
565 | |||
566 | if (!onetime) { | ||
567 | onetime = 1; | ||
568 | fprintf(stderr, "Using fallback suid method\n"); | ||
569 | } | ||
570 | } | ||
571 | #endif | ||
572 | check_need_suid: | ||
573 | #endif | ||
574 | niro | 984 | if (APPLET_SUID(applet_no) == _BB_SUID_REQUIRE) { |
575 | niro | 816 | /* Real uid is not 0. If euid isn't 0 too, suid bit |
576 | * is most probably not set on our executable */ | ||
577 | if (geteuid()) | ||
578 | bb_error_msg_and_die("must be suid to work properly"); | ||
579 | niro | 984 | } else if (APPLET_SUID(applet_no) == _BB_SUID_DROP) { |
580 | niro | 816 | xsetgid(rgid); /* drop all privileges */ |
581 | xsetuid(ruid); | ||
582 | } | ||
583 | } | ||
584 | #else | ||
585 | #define check_suid(x) ((void)0) | ||
586 | #endif /* FEATURE_SUID */ | ||
587 | |||
588 | |||
589 | #if ENABLE_FEATURE_INSTALLER | ||
590 | niro | 1123 | static const char usr_bin [] ALIGN1 = "/usr/bin/"; |
591 | static const char usr_sbin[] ALIGN1 = "/usr/sbin/"; | ||
592 | static const char *const install_dir[] = { | ||
593 | &usr_bin [8], /* "/" */ | ||
594 | &usr_bin [4], /* "/bin/" */ | ||
595 | &usr_sbin[4], /* "/sbin/" */ | ||
596 | usr_bin, | ||
597 | usr_sbin | ||
598 | }; | ||
599 | |||
600 | |||
601 | niro | 816 | /* create (sym)links for each applet */ |
602 | niro | 984 | static void install_links(const char *busybox, int use_symbolic_links, |
603 | char *custom_install_dir) | ||
604 | niro | 816 | { |
605 | /* directory table | ||
606 | * this should be consistent w/ the enum, | ||
607 | * busybox.h::bb_install_loc_t, or else... */ | ||
608 | int (*lf)(const char *, const char *); | ||
609 | char *fpc; | ||
610 | unsigned i; | ||
611 | int rc; | ||
612 | |||
613 | lf = link; | ||
614 | if (use_symbolic_links) | ||
615 | lf = symlink; | ||
616 | |||
617 | for (i = 0; i < ARRAY_SIZE(applet_main); i++) { | ||
618 | fpc = concat_path_file( | ||
619 | niro | 984 | custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)], |
620 | niro | 816 | APPLET_NAME(i)); |
621 | // debug: bb_error_msg("%slinking %s to busybox", | ||
622 | // use_symbolic_links ? "sym" : "", fpc); | ||
623 | rc = lf(busybox, fpc); | ||
624 | if (rc != 0 && errno != EEXIST) { | ||
625 | bb_simple_perror_msg(fpc); | ||
626 | } | ||
627 | free(fpc); | ||
628 | } | ||
629 | } | ||
630 | #else | ||
631 | niro | 1123 | # define install_links(x,y,z) ((void)0) |
632 | #endif | ||
633 | niro | 816 | |
634 | /* If we were called as "busybox..." */ | ||
635 | static int busybox_main(char **argv) | ||
636 | { | ||
637 | if (!argv[1]) { | ||
638 | /* Called without arguments */ | ||
639 | const char *a; | ||
640 | niro | 984 | int col; |
641 | unsigned output_width; | ||
642 | niro | 816 | help: |
643 | output_width = 80; | ||
644 | if (ENABLE_FEATURE_AUTOWIDTH) { | ||
645 | /* Obtain the terminal width */ | ||
646 | get_terminal_width_height(0, &output_width, NULL); | ||
647 | } | ||
648 | |||
649 | dup2(1, 2); | ||
650 | niro | 984 | full_write2_str(bb_banner); /* reuse const string */ |
651 | full_write2_str(" multi-call binary.\n"); /* reuse */ | ||
652 | full_write2_str( | ||
653 | niro | 1123 | "Copyright (C) 1998-2009 Erik Andersen, Rob Landley, Denys Vlasenko\n" |
654 | "and others. Licensed under GPLv2.\n" | ||
655 | "See source distribution for full notice.\n" | ||
656 | "\n" | ||
657 | "Usage: busybox [function] [arguments]...\n" | ||
658 | " or: function [arguments]...\n" | ||
659 | "\n" | ||
660 | "\tBusyBox is a multi-call binary that combines many common Unix\n" | ||
661 | "\tutilities into a single executable. Most people will create a\n" | ||
662 | "\tlink to busybox for each function they wish to use and BusyBox\n" | ||
663 | "\twill act like whatever it was invoked as.\n" | ||
664 | "\n" | ||
665 | "Currently defined functions:\n" | ||
666 | ); | ||
667 | niro | 816 | col = 0; |
668 | a = applet_names; | ||
669 | niro | 984 | /* prevent last comma to be in the very last pos */ |
670 | output_width--; | ||
671 | niro | 816 | while (*a) { |
672 | niro | 984 | int len2 = strlen(a) + 2; |
673 | if (col >= (int)output_width - len2) { | ||
674 | niro | 816 | full_write2_str(",\n"); |
675 | col = 0; | ||
676 | } | ||
677 | niro | 984 | if (col == 0) { |
678 | col = 6; | ||
679 | full_write2_str("\t"); | ||
680 | } else { | ||
681 | full_write2_str(", "); | ||
682 | } | ||
683 | niro | 816 | full_write2_str(a); |
684 | niro | 984 | col += len2; |
685 | a += len2 - 1; | ||
686 | niro | 816 | } |
687 | full_write2_str("\n\n"); | ||
688 | return 0; | ||
689 | } | ||
690 | |||
691 | niro | 1123 | if (strncmp(argv[1], "--list", 6) == 0) { |
692 | unsigned i = 0; | ||
693 | const char *a = applet_names; | ||
694 | dup2(1, 2); | ||
695 | while (*a) { | ||
696 | #if ENABLE_FEATURE_INSTALLER | ||
697 | if (argv[1][6]) /* --list-path? */ | ||
698 | full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1); | ||
699 | #endif | ||
700 | full_write2_str(a); | ||
701 | full_write2_str("\n"); | ||
702 | i++; | ||
703 | a += strlen(a) + 1; | ||
704 | } | ||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | niro | 816 | if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) { |
709 | niro | 984 | int use_symbolic_links; |
710 | niro | 816 | const char *busybox; |
711 | busybox = xmalloc_readlink(bb_busybox_exec_path); | ||
712 | if (!busybox) | ||
713 | busybox = bb_busybox_exec_path; | ||
714 | niro | 984 | /* busybox --install [-s] [DIR]: */ |
715 | /* -s: make symlinks */ | ||
716 | /* DIR: directory to install links to */ | ||
717 | use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && argv++); | ||
718 | install_links(busybox, use_symbolic_links, argv[2]); | ||
719 | niro | 816 | return 0; |
720 | } | ||
721 | |||
722 | if (strcmp(argv[1], "--help") == 0) { | ||
723 | /* "busybox --help [<applet>]" */ | ||
724 | if (!argv[2]) | ||
725 | goto help; | ||
726 | /* convert to "<applet> --help" */ | ||
727 | argv[0] = argv[2]; | ||
728 | argv[2] = NULL; | ||
729 | } else { | ||
730 | /* "busybox <applet> arg1 arg2 ..." */ | ||
731 | argv++; | ||
732 | } | ||
733 | /* We support "busybox /a/path/to/applet args..." too. Allows for | ||
734 | * "#!/bin/busybox"-style wrappers */ | ||
735 | applet_name = bb_get_last_path_component_nostrip(argv[0]); | ||
736 | run_applet_and_exit(applet_name, argv); | ||
737 | |||
738 | /*bb_error_msg_and_die("applet not found"); - sucks in printf */ | ||
739 | full_write2_str(applet_name); | ||
740 | full_write2_str(": applet not found\n"); | ||
741 | xfunc_die(); | ||
742 | } | ||
743 | |||
744 | void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) | ||
745 | { | ||
746 | int argc = 1; | ||
747 | |||
748 | while (argv[argc]) | ||
749 | argc++; | ||
750 | |||
751 | /* Reinit some shared global data */ | ||
752 | xfunc_error_retval = EXIT_FAILURE; | ||
753 | |||
754 | applet_name = APPLET_NAME(applet_no); | ||
755 | if (argc == 2 && strcmp(argv[1], "--help") == 0) { | ||
756 | /* Special case. POSIX says "test --help" | ||
757 | * should be no different from e.g. "test --foo". */ | ||
758 | //TODO: just compare applet_no with APPLET_NO_test | ||
759 | if (!ENABLE_TEST || strcmp(applet_name, "test") != 0) | ||
760 | bb_show_usage(); | ||
761 | } | ||
762 | if (ENABLE_FEATURE_SUID) | ||
763 | check_suid(applet_no); | ||
764 | exit(applet_main[applet_no](argc, argv)); | ||
765 | } | ||
766 | |||
767 | void FAST_FUNC run_applet_and_exit(const char *name, char **argv) | ||
768 | { | ||
769 | int applet = find_applet_by_name(name); | ||
770 | if (applet >= 0) | ||
771 | run_applet_no_and_exit(applet, argv); | ||
772 | if (!strncmp(name, "busybox", 7)) | ||
773 | exit(busybox_main(argv)); | ||
774 | } | ||
775 | |||
776 | #endif /* !defined(SINGLE_APPLET_MAIN) */ | ||
777 | |||
778 | |||
779 | |||
780 | #if ENABLE_BUILD_LIBBUSYBOX | ||
781 | int lbb_main(char **argv) | ||
782 | #else | ||
783 | int main(int argc UNUSED_PARAM, char **argv) | ||
784 | #endif | ||
785 | { | ||
786 | niro | 984 | /* Tweak malloc for reduced memory consumption */ |
787 | #ifndef PAGE_SIZE | ||
788 | # define PAGE_SIZE (4*1024) /* guess */ | ||
789 | #endif | ||
790 | #ifdef M_TRIM_THRESHOLD | ||
791 | /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory | ||
792 | * to keep before releasing to the OS | ||
793 | * Default is way too big: 256k | ||
794 | */ | ||
795 | mallopt(M_TRIM_THRESHOLD, 2 * PAGE_SIZE); | ||
796 | #endif | ||
797 | #ifdef M_MMAP_THRESHOLD | ||
798 | /* M_MMAP_THRESHOLD is the request size threshold for using mmap() | ||
799 | * Default is too big: 256k | ||
800 | */ | ||
801 | mallopt(M_MMAP_THRESHOLD, 8 * PAGE_SIZE - 256); | ||
802 | #endif | ||
803 | |||
804 | niro | 816 | #if defined(SINGLE_APPLET_MAIN) |
805 | /* Only one applet is selected by the user! */ | ||
806 | /* applet_names in this case is just "applet\0\0" */ | ||
807 | niro | 984 | lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv)); |
808 | niro | 816 | return SINGLE_APPLET_MAIN(argc, argv); |
809 | #else | ||
810 | niro | 984 | lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv)); |
811 | niro | 816 | |
812 | #if !BB_MMU | ||
813 | /* NOMMU re-exec trick sets high-order bit in first byte of name */ | ||
814 | if (argv[0][0] & 0x80) { | ||
815 | re_execed = 1; | ||
816 | argv[0][0] &= 0x7f; | ||
817 | } | ||
818 | #endif | ||
819 | applet_name = argv[0]; | ||
820 | if (applet_name[0] == '-') | ||
821 | applet_name++; | ||
822 | applet_name = bb_basename(applet_name); | ||
823 | |||
824 | parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */ | ||
825 | |||
826 | run_applet_and_exit(applet_name, argv); | ||
827 | |||
828 | /*bb_error_msg_and_die("applet not found"); - sucks in printf */ | ||
829 | full_write2_str(applet_name); | ||
830 | full_write2_str(": applet not found\n"); | ||
831 | xfunc_die(); | ||
832 | #endif | ||
833 | } |