1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30#include <assert.h>
31#include "busybox.h"
32
33
34
35#define PROTOTYPES
36#include "applets.h"
37#undef PROTOTYPES
38
39#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
40
41static const char usage_messages[] ALIGN1 = ""
42#define MAKE_USAGE
43#include "usage.h"
44#include "applets.h"
45;
46#undef MAKE_USAGE
47#else
48#define usage_messages 0
49#endif
50
51
52
53#include "applet_tables.h"
54
55#ifdef SINGLE_APPLET_MAIN
56#undef ENABLE_FEATURE_INDIVIDUAL
57#define ENABLE_FEATURE_INDIVIDUAL 1
58#undef USE_FEATURE_INDIVIDUAL
59#define USE_FEATURE_INDIVIDUAL(...) __VA_ARGS__
60#endif
61
62
63#if ENABLE_FEATURE_COMPRESS_USAGE
64
65#include "usage_compressed.h"
66#include "unarchive.h"
67
68static const char *unpack_usage_messages(void)
69{
70 char *outbuf = NULL;
71 bunzip_data *bd;
72 int i;
73
74 i = start_bunzip(&bd,
75 -1,
76
77 (void *)packed_usage + 2,
78 sizeof(packed_usage));
79
80
81 if (!i) {
82
83 outbuf = malloc_or_warn(SIZEOF_usage_messages);
84 if (outbuf)
85 read_bunzip(bd, outbuf, SIZEOF_usage_messages);
86 }
87 dealloc_bunzip(bd);
88 return outbuf;
89}
90#define dealloc_usage_messages(s) free(s)
91
92#else
93
94#define unpack_usage_messages() usage_messages
95#define dealloc_usage_messages(s) ((void)(s))
96
97#endif
98
99
100static void full_write2_str(const char *str)
101{
102 full_write(STDERR_FILENO, str, strlen(str));
103}
104
105void FAST_FUNC bb_show_usage(void)
106{
107 if (ENABLE_SHOW_USAGE) {
108#ifdef SINGLE_APPLET_STR
109
110 const char *p;
111 const char *usage_string = p = unpack_usage_messages();
112
113 if (*p == '\b') {
114 full_write2_str("\nNo help available.\n\n");
115 } else {
116 full_write2_str("\nUsage: "SINGLE_APPLET_STR" ");
117 full_write2_str(p);
118 full_write2_str("\n\n");
119 }
120 dealloc_usage_messages((char*)usage_string);
121#else
122 const char *p;
123 const char *usage_string = p = unpack_usage_messages();
124 int ap = find_applet_by_name(applet_name);
125
126 if (ap < 0)
127 xfunc_die();
128 while (ap) {
129 while (*p++) continue;
130 ap--;
131 }
132 full_write2_str(bb_banner);
133 full_write2_str(" multi-call binary\n");
134 if (*p == '\b')
135 full_write2_str("\nNo help available.\n\n");
136 else {
137 full_write2_str("\nUsage: ");
138 full_write2_str(applet_name);
139 full_write2_str(" ");
140 full_write2_str(p);
141 full_write2_str("\n\n");
142 }
143 dealloc_usage_messages((char*)usage_string);
144#endif
145 }
146 xfunc_die();
147}
148
149#if NUM_APPLETS > 8
150
151static int applet_name_compare(const void *name, const void *v)
152{
153 int i = (const char *)v - applet_names;
154 return strcmp(name, APPLET_NAME(i));
155}
156#endif
157int FAST_FUNC find_applet_by_name(const char *name)
158{
159#if NUM_APPLETS > 8
160
161 const char *p;
162 p = bsearch(name, applet_names, ARRAY_SIZE(applet_main), 1, applet_name_compare);
163 if (!p)
164 return -1;
165 return p - applet_names;
166#else
167
168 int i = 0;
169 const char *p = applet_names;
170 while (i < NUM_APPLETS) {
171 if (strcmp(name, p) == 0)
172 return i;
173 p += strlen(p) + 1;
174 i++;
175 }
176 return -1;
177#endif
178}
179
180
181void lbb_prepare(const char *applet
182 USE_FEATURE_INDIVIDUAL(, char **argv))
183 MAIN_EXTERNALLY_VISIBLE;
184void lbb_prepare(const char *applet
185 USE_FEATURE_INDIVIDUAL(, char **argv))
186{
187#ifdef __GLIBC__
188 (*(int **)&bb_errno) = __errno_location();
189 barrier();
190#endif
191 applet_name = applet;
192
193
194 if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
195 setlocale(LC_ALL, "");
196
197#if ENABLE_FEATURE_INDIVIDUAL
198
199
200 if (argv[1] && !argv[2] && strcmp(argv[1], "--help") == 0) {
201
202
203 if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
204 bb_show_usage();
205 }
206#endif
207}
208
209
210
211
212
213
214
215
216
217
218const char *applet_name;
219#if !BB_MMU
220bool re_execed;
221#endif
222
223
224
225#if !defined(SINGLE_APPLET_MAIN)
226
227USE_FEATURE_SUID(static uid_t ruid;)
228
229#if ENABLE_FEATURE_SUID_CONFIG
230
231
232static struct BB_suid_config {
233 int m_applet;
234 uid_t m_uid;
235 gid_t m_gid;
236 mode_t m_mode;
237 struct BB_suid_config *m_next;
238} *suid_config;
239
240static bool suid_cfg_readable;
241
242
243static int ingroup(uid_t u, gid_t g)
244{
245 struct group *grp = getgrgid(g);
246
247 if (grp) {
248 char **mem;
249
250 for (mem = grp->gr_mem; *mem; mem++) {
251 struct passwd *pwd = getpwnam(*mem);
252
253 if (pwd && (pwd->pw_uid == u))
254 return 1;
255 }
256 }
257 return 0;
258}
259
260
261
262
263static char *get_trimmed_slice(char *s, char *e)
264{
265
266
267
268 while (e-- > s) {
269 if (!isspace(*e)) {
270 break;
271 }
272 }
273 e[1] = '\0';
274
275
276
277 return skip_whitespace(s);
278}
279
280
281static const char config_file[] ALIGN1 = "/etc/busybox.conf";
282
283
284
285
286static const unsigned short mode_mask[] ALIGN2 = {
287
288 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0,
289 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0,
290 0, S_IXOTH, S_IXOTH, 0
291};
292
293#define parse_error(x) do { errmsg = x; goto pe_label; } while (0)
294
295static void parse_config_file(void)
296{
297 struct BB_suid_config *sct_head;
298 struct BB_suid_config *sct;
299 int applet_no;
300 FILE *f;
301 const char *errmsg;
302 char *s;
303 char *e;
304 int i;
305 unsigned lc;
306 smallint section;
307 char buffer[256];
308 struct stat st;
309
310 assert(!suid_config);
311
312 ruid = getuid();
313 if (ruid == 0)
314 return;
315
316 if ((stat(config_file, &st) != 0)
317 || !S_ISREG(st.st_mode)
318 || (st.st_uid != 0)
319 || (st.st_mode & (S_IWGRP | S_IWOTH))
320 || !(f = fopen_for_read(config_file))
321 ) {
322 return;
323 }
324
325 suid_cfg_readable = 1;
326 sct_head = NULL;
327 section = lc = 0;
328
329 while (1) {
330 s = buffer;
331
332 if (!fgets(s, sizeof(buffer), f)) {
333
334 if (ferror(f)) {
335 parse_error("reading");
336 }
337 fclose(f);
338 suid_config = sct_head;
339 return;
340 }
341
342 lc++;
343
344
345
346
347
348
349
350
351
352 if (!strchr(s, '\n') && !feof(f)) {
353 parse_error("line too long");
354 }
355
356
357
358 s = get_trimmed_slice(s, strchrnul(s, '#'));
359 if (!*s) {
360 continue;
361 }
362
363
364
365 if (*s == '[') {
366
367
368
369 e = strchr(s, ']');
370 if (!e
371 || e[1]
372 || !*(s = get_trimmed_slice(s+1, e))
373 ) {
374 parse_error("section header");
375 }
376
377
378
379
380
381
382 if (strcasecmp(s, "SUID") == 0) {
383 section = 1;
384 continue;
385 }
386 section = -1;
387 continue;
388 }
389
390
391
392 if (section == 1) {
393
394
395
396
397
398
399 e = strchr(s, '=');
400 if (e) {
401 s = get_trimmed_slice(s, e);
402 }
403 if (!e || !*s) {
404 parse_error("keyword");
405 }
406
407
408
409
410
411 applet_no = find_applet_by_name(s);
412 if (applet_no >= 0) {
413
414
415
416
417 sct = xmalloc(sizeof(struct BB_suid_config));
418 sct->m_applet = applet_no;
419 sct->m_mode = 0;
420 sct->m_next = sct_head;
421 sct_head = sct;
422
423
424
425 e = skip_whitespace(e+1);
426
427 for (i = 0; i < 3; i++) {
428
429 static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-";
430
431 const char *q;
432 q = strchrnul(mode_chars + 5*i, *e++);
433 if (!*q) {
434 parse_error("mode");
435 }
436
437 sct->m_mode |= mode_mask[(q - mode_chars) - i];
438 }
439
440
441
442 s = skip_whitespace(e);
443
444
445
446 if ((s == e) || !(e = strchr(s, '.'))) {
447 parse_error("<uid>.<gid>");
448 }
449 *e++ = '\0';
450
451
452
453 sct->m_uid = bb_strtoul(s, NULL, 10);
454 if (errno) {
455 struct passwd *pwd = getpwnam(s);
456 if (!pwd) {
457 parse_error("user");
458 }
459 sct->m_uid = pwd->pw_uid;
460 }
461
462 sct->m_gid = bb_strtoul(e, NULL, 10);
463 if (errno) {
464 struct group *grp;
465 grp = getgrnam(e);
466 if (!grp) {
467 parse_error("group");
468 }
469 sct->m_gid = grp->gr_gid;
470 }
471 }
472 continue;
473 }
474
475
476
477
478
479
480
481
482 if (!section) {
483 parse_error("keyword outside section");
484 }
485
486 }
487
488 pe_label:
489 fprintf(stderr, "Parse error in %s, line %d: %s\n",
490 config_file, lc, errmsg);
491
492 fclose(f);
493
494 while (sct_head) {
495 sct = sct_head->m_next;
496 free(sct_head);
497 sct_head = sct;
498 }
499}
500#else
501static inline void parse_config_file(void)
502{
503 USE_FEATURE_SUID(ruid = getuid();)
504}
505#endif
506
507
508#if ENABLE_FEATURE_SUID
509static void check_suid(int applet_no)
510{
511 gid_t rgid;
512
513 if (ruid == 0)
514 return;
515 rgid = getgid();
516
517#if ENABLE_FEATURE_SUID_CONFIG
518 if (suid_cfg_readable) {
519 uid_t uid;
520 struct BB_suid_config *sct;
521 mode_t m;
522
523 for (sct = suid_config; sct; sct = sct->m_next) {
524 if (sct->m_applet == applet_no)
525 goto found;
526 }
527 goto check_need_suid;
528 found:
529 m = sct->m_mode;
530 if (sct->m_uid == ruid)
531
532 m >>= 6;
533 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
534
535 m >>= 3;
536
537 if (!(m & S_IXOTH))
538 bb_error_msg_and_die("you have no permission to run this applet!");
539
540
541 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
542 rgid = sct->m_gid;
543
544
545
546
547 if (setresgid(-1, rgid, rgid))
548 bb_perror_msg_and_die("setresgid");
549
550
551 uid = ruid;
552 if (sct->m_mode & S_ISUID)
553 uid = sct->m_uid;
554
555
556 if (setresuid(-1, uid, uid))
557 bb_perror_msg_and_die("setresuid");
558 return;
559 }
560#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
561 {
562 static bool onetime = 0;
563
564 if (!onetime) {
565 onetime = 1;
566 fprintf(stderr, "Using fallback suid method\n");
567 }
568 }
569#endif
570 check_need_suid:
571#endif
572 if (APPLET_SUID(applet_no) == _BB_SUID_ALWAYS) {
573
574
575 if (geteuid())
576 bb_error_msg_and_die("must be suid to work properly");
577 } else if (APPLET_SUID(applet_no) == _BB_SUID_NEVER) {
578 xsetgid(rgid);
579 xsetuid(ruid);
580 }
581}
582#else
583#define check_suid(x) ((void)0)
584#endif
585
586
587#if ENABLE_FEATURE_INSTALLER
588
589static void install_links(const char *busybox, int use_symbolic_links)
590{
591
592
593
594 static const char usr_bin [] ALIGN1 = "/usr/bin";
595 static const char usr_sbin[] ALIGN1 = "/usr/sbin";
596 static const char *const install_dir[] = {
597 &usr_bin [8],
598 &usr_bin [4],
599 &usr_sbin[4],
600 usr_bin,
601 usr_sbin
602 };
603
604 int (*lf)(const char *, const char *);
605 char *fpc;
606 unsigned i;
607 int rc;
608
609 lf = link;
610 if (use_symbolic_links)
611 lf = symlink;
612
613 for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
614 fpc = concat_path_file(
615 install_dir[APPLET_INSTALL_LOC(i)],
616 APPLET_NAME(i));
617
618
619 rc = lf(busybox, fpc);
620 if (rc != 0 && errno != EEXIST) {
621 bb_simple_perror_msg(fpc);
622 }
623 free(fpc);
624 }
625}
626#else
627#define install_links(x,y) ((void)0)
628#endif
629
630
631static int busybox_main(char **argv)
632{
633 if (!argv[1]) {
634
635 const char *a;
636 unsigned col, output_width;
637 help:
638 output_width = 80;
639 if (ENABLE_FEATURE_AUTOWIDTH) {
640
641 get_terminal_width_height(0, &output_width, NULL);
642 }
643
644 output_width -= MAX_APPLET_NAME_LEN + 8;
645
646 dup2(1, 2);
647 full_write2_str(bb_banner);
648 full_write2_str(" multi-call binary\n"
649 "Copyright (C) 1998-2008 Erik Andersen, Rob Landley, Denys Vlasenko\n"
650 "and others. Licensed under GPLv2.\n"
651 "See source distribution for full notice.\n"
652 "\n"
653 "Usage: busybox [function] [arguments]...\n"
654 " or: function [arguments]...\n"
655 "\n"
656 "\tBusyBox is a multi-call binary that combines many common Unix\n"
657 "\tutilities into a single executable. Most people will create a\n"
658 "\tlink to busybox for each function they wish to use and BusyBox\n"
659 "\twill act like whatever it was invoked as!\n"
660 "\n"
661 "Currently defined functions:\n");
662 col = 0;
663 a = applet_names;
664 while (*a) {
665 int len;
666 if (col > output_width) {
667 full_write2_str(",\n");
668 col = 0;
669 }
670 full_write2_str(col ? ", " : "\t");
671 full_write2_str(a);
672 len = strlen(a);
673 col += len + 2;
674 a += len + 1;
675 }
676 full_write2_str("\n\n");
677 return 0;
678 }
679
680 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
681 const char *busybox;
682 busybox = xmalloc_readlink(bb_busybox_exec_path);
683 if (!busybox)
684 busybox = bb_busybox_exec_path;
685
686 install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0);
687 return 0;
688 }
689
690 if (strcmp(argv[1], "--help") == 0) {
691
692 if (!argv[2])
693 goto help;
694
695 argv[0] = argv[2];
696 argv[2] = NULL;
697 } else {
698
699 argv++;
700 }
701
702
703 applet_name = bb_get_last_path_component_nostrip(argv[0]);
704 run_applet_and_exit(applet_name, argv);
705
706
707 full_write2_str(applet_name);
708 full_write2_str(": applet not found\n");
709 xfunc_die();
710}
711
712void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
713{
714 int argc = 1;
715
716 while (argv[argc])
717 argc++;
718
719
720 xfunc_error_retval = EXIT_FAILURE;
721
722 applet_name = APPLET_NAME(applet_no);
723 if (argc == 2 && strcmp(argv[1], "--help") == 0) {
724
725
726
727 if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
728 bb_show_usage();
729 }
730 if (ENABLE_FEATURE_SUID)
731 check_suid(applet_no);
732 exit(applet_main[applet_no](argc, argv));
733}
734
735void FAST_FUNC run_applet_and_exit(const char *name, char **argv)
736{
737 int applet = find_applet_by_name(name);
738 if (applet >= 0)
739 run_applet_no_and_exit(applet, argv);
740 if (!strncmp(name, "busybox", 7))
741 exit(busybox_main(argv));
742}
743
744#endif
745
746
747
748#if ENABLE_BUILD_LIBBUSYBOX
749int lbb_main(char **argv)
750#else
751int main(int argc UNUSED_PARAM, char **argv)
752#endif
753{
754#if defined(SINGLE_APPLET_MAIN)
755
756
757 lbb_prepare(applet_names USE_FEATURE_INDIVIDUAL(, argv));
758 return SINGLE_APPLET_MAIN(argc, argv);
759#else
760 lbb_prepare("busybox" USE_FEATURE_INDIVIDUAL(, argv));
761
762#if !BB_MMU
763
764 if (argv[0][0] & 0x80) {
765 re_execed = 1;
766 argv[0][0] &= 0x7f;
767 }
768#endif
769 applet_name = argv[0];
770 if (applet_name[0] == '-')
771 applet_name++;
772 applet_name = bb_basename(applet_name);
773
774 parse_config_file();
775
776 run_applet_and_exit(applet_name, argv);
777
778
779 full_write2_str(applet_name);
780 full_write2_str(": applet not found\n");
781 xfunc_die();
782#endif
783}
784