aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-10-10 14:41:07 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-10-10 14:41:07 +0000
commit724d196c75e097535d475528002518d5322868c6 (patch)
tree94e4345625688d6071eacecf2bd1e87a9980e90f
parent82d38dab917754c9c37aaa7e414a47318b5082fe (diff)
downloadbusybox-w32-724d196c75e097535d475528002518d5322868c6.tar.gz
busybox-w32-724d196c75e097535d475528002518d5322868c6.tar.bz2
busybox-w32-724d196c75e097535d475528002518d5322868c6.zip
Move applets/applet.c into libbb, allows to get rid of --whole-archive
(smaller code). Tested in static and shared mode.
-rw-r--r--applets/applets.c541
-rw-r--r--include/busybox.h3
-rw-r--r--libbb/appletlib.c553
-rwxr-xr-xscripts/trylink17
4 files changed, 566 insertions, 548 deletions
diff --git a/applets/applets.c b/applets/applets.c
index 3b4f395c9..bbf150c79 100644
--- a/applets/applets.c
+++ b/applets/applets.c
@@ -1,21 +1,15 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2/* 2/*
3 * Utility routines. 3 * Stub for linking busybox binary against libbusybox.
4 * 4 *
5 * Copyright (C) tons of folks. Tracking down who wrote what 5 * Copyright (C) 2007 Denis Vlasenko
6 * isn't something I'm going to worry about... If you wrote something
7 * here, please feel free to acknowledge your work.
8 * 6 *
9 * Based in part on code from sash, Copyright (c) 1999 by David I. Bell 7 * Licensed under GPLv2, see file License in this tarball for details.
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 */ 8 */
14 9
15#include <assert.h> 10#include <assert.h>
16#include "busybox.h" 11#include "busybox.h"
17 12
18
19/* Apparently uclibc defines __GLIBC__ (compat trick?). Oh well. */ 13/* Apparently uclibc defines __GLIBC__ (compat trick?). Oh well. */
20#if ENABLE_STATIC && defined(__GLIBC__) && !defined(__UCLIBC__) 14#if ENABLE_STATIC && defined(__GLIBC__) && !defined(__UCLIBC__)
21#warning Static linking against glibc produces buggy executables 15#warning Static linking against glibc produces buggy executables
@@ -27,532 +21,9 @@
27#error Aborting compilation. 21#error Aborting compilation.
28#endif 22#endif
29 23
30 24#if ENABLE_BUILD_LIBBUSYBOX
31const char *applet_name;
32#if !BB_MMU
33bool re_execed;
34#endif
35
36USE_FEATURE_SUID(static uid_t ruid;) /* real uid */
37
38#if ENABLE_FEATURE_SUID_CONFIG
39
40/* applets[] is const, so we have to define this "override" structure */
41static struct BB_suid_config {
42 const struct bb_applet *m_applet;
43 uid_t m_uid;
44 gid_t m_gid;
45 mode_t m_mode;
46 struct BB_suid_config *m_next;
47} *suid_config;
48
49static bool suid_cfg_readable;
50
51/* check if u is member of group g */
52static int ingroup(uid_t u, gid_t g)
53{
54 struct group *grp = getgrgid(g);
55
56 if (grp) {
57 char **mem;
58
59 for (mem = grp->gr_mem; *mem; mem++) {
60 struct passwd *pwd = getpwnam(*mem);
61
62 if (pwd && (pwd->pw_uid == u))
63 return 1;
64 }
65 }
66 return 0;
67}
68
69/* This should probably be a libbb routine. In that case,
70 * I'd probably rename it to something like bb_trimmed_slice.
71 */
72static char *get_trimmed_slice(char *s, char *e)
73{
74 /* First, consider the value at e to be nul and back up until we
75 * reach a non-space char. Set the char after that (possibly at
76 * the original e) to nul. */
77 while (e-- > s) {
78 if (!isspace(*e)) {
79 break;
80 }
81 }
82 e[1] = '\0';
83
84 /* Next, advance past all leading space and return a ptr to the
85 * first non-space char; possibly the terminating nul. */
86 return skip_whitespace(s);
87}
88
89/* Don't depend on the tools to combine strings. */
90static const char config_file[] ALIGN1 = "/etc/busybox.conf";
91
92/* We don't supply a value for the nul, so an index adjustment is
93 * necessary below. Also, we use unsigned short here to save some
94 * space even though these are really mode_t values. */
95static const unsigned short mode_mask[] ALIGN2 = {
96 /* SST sst xxx --- */
97 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
98 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
99 0, S_IXOTH, S_IXOTH, 0 /* other */
100};
101
102#define parse_error(x) do { errmsg = x; goto pe_label; } while (0)
103
104static void parse_config_file(void)
105{
106 struct BB_suid_config *sct_head;
107 struct BB_suid_config *sct;
108 const struct bb_applet *applet;
109 FILE *f;
110 const char *errmsg;
111 char *s;
112 char *e;
113 int i;
114 unsigned lc;
115 smallint section;
116 char buffer[256];
117 struct stat st;
118
119 assert(!suid_config); /* Should be set to NULL by bss init. */
120
121 ruid = getuid();
122 if (ruid == 0) /* run by root - don't need to even read config file */
123 return;
124
125 if ((stat(config_file, &st) != 0) /* No config file? */
126 || !S_ISREG(st.st_mode) /* Not a regular file? */
127 || (st.st_uid != 0) /* Not owned by root? */
128 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
129 || !(f = fopen(config_file, "r")) /* Cannot open? */
130 ) {
131 return;
132 }
133
134 suid_cfg_readable = 1;
135 sct_head = NULL;
136 section = lc = 0;
137
138 while (1) {
139 s = buffer;
140
141 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
142 if (ferror(f)) { /* Make sure it wasn't a read error. */
143 parse_error("reading");
144 }
145 fclose(f);
146 suid_config = sct_head; /* Success, so set the pointer. */
147 return;
148 }
149
150 lc++; /* Got a (partial) line. */
151
152 /* If a line is too long for our buffer, we consider it an error.
153 * The following test does mistreat one corner case though.
154 * If the final line of the file does not end with a newline and
155 * yet exactly fills the buffer, it will be treated as too long
156 * even though there isn't really a problem. But it isn't really
157 * worth adding code to deal with such an unlikely situation, and
158 * we do err on the side of caution. Besides, the line would be
159 * too long if it did end with a newline. */
160 if (!strchr(s, '\n') && !feof(f)) {
161 parse_error("line too long");
162 }
163
164 /* Trim leading and trailing whitespace, ignoring comments, and
165 * check if the resulting string is empty. */
166 s = get_trimmed_slice(s, strchrnul(s, '#'));
167 if (!*s) {
168 continue;
169 }
170
171 /* Check for a section header. */
172
173 if (*s == '[') {
174 /* Unlike the old code, we ignore leading and trailing
175 * whitespace for the section name. We also require that
176 * there are no stray characters after the closing bracket. */
177 e = strchr(s, ']');
178 if (!e /* Missing right bracket? */
179 || e[1] /* Trailing characters? */
180 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
181 ) {
182 parse_error("section header");
183 }
184 /* Right now we only have one section so just check it.
185 * If more sections are added in the future, please don't
186 * resort to cascading ifs with multiple strcasecmp calls.
187 * That kind of bloated code is all too common. A loop
188 * and a string table would be a better choice unless the
189 * number of sections is very small. */
190 if (strcasecmp(s, "SUID") == 0) {
191 section = 1;
192 continue;
193 }
194 section = -1; /* Unknown section so set to skip. */
195 continue;
196 }
197
198 /* Process sections. */
199
200 if (section == 1) { /* SUID */
201 /* Since we trimmed leading and trailing space above, we're
202 * now looking for strings of the form
203 * <key>[::space::]*=[::space::]*<value>
204 * where both key and value could contain inner whitespace. */
205
206 /* First get the key (an applet name in our case). */
207 e = strchr(s, '=');
208 if (e) {
209 s = get_trimmed_slice(s, e);
210 }
211 if (!e || !*s) { /* Missing '=' or empty key. */
212 parse_error("keyword");
213 }
214
215 /* Ok, we have an applet name. Process the rhs if this
216 * applet is currently built in and ignore it otherwise.
217 * Note: this can hide config file bugs which only pop
218 * up when the busybox configuration is changed. */
219 applet = find_applet_by_name(s);
220 if (applet) {
221 /* Note: We currently don't check for duplicates!
222 * The last config line for each applet will be the
223 * one used since we insert at the head of the list.
224 * I suppose this could be considered a feature. */
225 sct = xmalloc(sizeof(struct BB_suid_config));
226 sct->m_applet = applet;
227 sct->m_mode = 0;
228 sct->m_next = sct_head;
229 sct_head = sct;
230
231 /* Get the specified mode. */
232
233 e = skip_whitespace(e+1);
234
235 for (i = 0; i < 3; i++) {
236 /* There are 4 chars + 1 nul for each of user/group/other. */
237 static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-";
238
239 const char *q;
240 q = strchrnul(mode_chars + 5*i, *e++);
241 if (!*q) {
242 parse_error("mode");
243 }
244 /* Adjust by -i to account for nul. */
245 sct->m_mode |= mode_mask[(q - mode_chars) - i];
246 }
247
248 /* Now get the the user/group info. */
249
250 s = skip_whitespace(e);
251
252 /* Note: we require whitespace between the mode and the
253 * user/group info. */
254 if ((s == e) || !(e = strchr(s, '.'))) {
255 parse_error("<uid>.<gid>");
256 }
257 *e++ = '\0';
258
259 /* We can't use get_ug_id here since it would exit()
260 * if a uid or gid was not found. Oh well... */
261 sct->m_uid = bb_strtoul(s, NULL, 10);
262 if (errno) {
263 struct passwd *pwd = getpwnam(s);
264 if (!pwd) {
265 parse_error("user");
266 }
267 sct->m_uid = pwd->pw_uid;
268 }
269
270 sct->m_gid = bb_strtoul(e, NULL, 10);
271 if (errno) {
272 struct group *grp;
273 grp = getgrnam(e);
274 if (!grp) {
275 parse_error("group");
276 }
277 sct->m_gid = grp->gr_gid;
278 }
279 }
280 continue;
281 }
282
283 /* Unknown sections are ignored. */
284
285 /* Encountering configuration lines prior to seeing a
286 * section header is treated as an error. This is how
287 * the old code worked, but it may not be desirable.
288 * We may want to simply ignore such lines in case they
289 * are used in some future version of busybox. */
290 if (!section) {
291 parse_error("keyword outside section");
292 }
293
294 } /* while (1) */
295
296 pe_label:
297 fprintf(stderr, "Parse error in %s, line %d: %s\n",
298 config_file, lc, errmsg);
299
300 fclose(f);
301 /* Release any allocated memory before returning. */
302 while (sct_head) {
303 sct = sct_head->m_next;
304 free(sct_head);
305 sct_head = sct;
306 }
307}
308#else
309static inline void parse_config_file(void)
310{
311 USE_FEATURE_SUID(ruid = getuid();)
312}
313#endif /* FEATURE_SUID_CONFIG */
314
315
316#if ENABLE_FEATURE_SUID
317static void check_suid(const struct bb_applet *applet)
318{
319 gid_t rgid; /* real gid */
320
321 if (ruid == 0) /* set by parse_config_file() */
322 return; /* run by root - no need to check more */
323 rgid = getgid();
324
325#if ENABLE_FEATURE_SUID_CONFIG
326 if (suid_cfg_readable) {
327 uid_t uid;
328 struct BB_suid_config *sct;
329 mode_t m;
330
331 for (sct = suid_config; sct; sct = sct->m_next) {
332 if (sct->m_applet == applet)
333 goto found;
334 }
335 /* default: drop all privileges */
336 xsetgid(rgid);
337 xsetuid(ruid);
338 return;
339 found:
340 m = sct->m_mode;
341 if (sct->m_uid == ruid)
342 /* same uid */
343 m >>= 6;
344 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
345 /* same group / in group */
346 m >>= 3;
347
348 if (!(m & S_IXOTH)) /* is x bit not set ? */
349 bb_error_msg_and_die("you have no permission to run this applet!");
350
351 /* _both_ sgid and group_exec have to be set for setegid */
352 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
353 rgid = sct->m_gid;
354 /* else (no setegid) we will set egid = rgid */
355
356 /* We set effective AND saved ids. If saved-id is not set
357 * like we do below, seteiud(0) can still later succeed! */
358 if (setresgid(-1, rgid, rgid))
359 bb_perror_msg_and_die("setresgid");
360
361 /* do we have to set effective uid? */
362 uid = ruid;
363 if (sct->m_mode & S_ISUID)
364 uid = sct->m_uid;
365 /* else (no seteuid) we will set euid = ruid */
366
367 if (setresuid(-1, uid, uid))
368 bb_perror_msg_and_die("setresuid");
369 return;
370 }
371#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
372 {
373 static bool onetime = 0;
374
375 if (!onetime) {
376 onetime = 1;
377 fprintf(stderr, "Using fallback suid method\n");
378 }
379 }
380#endif
381#endif
382
383 if (applet->need_suid == _BB_SUID_ALWAYS) {
384 /* Real uid is not 0. If euid isn't 0 too, suid bit
385 * is most probably not set on our executable */
386 if (geteuid())
387 bb_error_msg_and_die("applet requires root privileges!");
388 } else if (applet->need_suid == _BB_SUID_NEVER) {
389 xsetgid(rgid); /* drop all privileges */
390 xsetuid(ruid);
391 }
392}
393#else
394#define check_suid(x) ((void)0)
395#endif /* FEATURE_SUID */
396
397
398#if ENABLE_FEATURE_INSTALLER
399/* create (sym)links for each applet */
400static void install_links(const char *busybox, int use_symbolic_links)
401{
402 /* directory table
403 * this should be consistent w/ the enum,
404 * busybox.h::bb_install_loc_t, or else... */
405 static const char usr_bin [] ALIGN1 = "/usr/bin";
406 static const char usr_sbin[] ALIGN1 = "/usr/sbin";
407 static const char *const install_dir[] = {
408 &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
409 &usr_bin [4], /* "/bin" */
410 &usr_sbin[4], /* "/sbin" */
411 usr_bin,
412 usr_sbin
413 };
414
415 int (*lf)(const char *, const char *) = link;
416 char *fpc;
417 int i;
418 int rc;
419
420 if (use_symbolic_links)
421 lf = symlink;
422
423 for (i = 0; applets[i].name != NULL; i++) {
424 fpc = concat_path_file(
425 install_dir[applets[i].install_loc],
426 applets[i].name);
427 rc = lf(busybox, fpc);
428 if (rc != 0 && errno != EEXIST) {
429 bb_simple_perror_msg(fpc);
430 }
431 free(fpc);
432 }
433}
434#else
435#define install_links(x,y) ((void)0)
436#endif /* FEATURE_INSTALLER */
437
438
439/* If we were called as "busybox..." */
440static int busybox_main(char **argv)
441{
442 if (!argv[1]) {
443 /* Called without arguments */
444 const struct bb_applet *a;
445 int col, output_width;
446 help:
447 output_width = 80;
448 if (ENABLE_FEATURE_AUTOWIDTH) {
449 /* Obtain the terminal width */
450 get_terminal_width_height(0, &output_width, NULL);
451 }
452 /* leading tab and room to wrap */
453 output_width -= sizeof("start-stop-daemon, ") + 8;
454
455 printf("%s multi-call binary\n", bb_banner); /* reuse const string... */
456 printf("Copyright (C) 1998-2006 Erik Andersen, Rob Landley, and others.\n"
457 "Licensed under GPLv2. See source distribution for full notice.\n"
458 "\n"
459 "Usage: busybox [function] [arguments]...\n"
460 " or: [function] [arguments]...\n"
461 "\n"
462 "\tBusyBox is a multi-call binary that combines many common Unix\n"
463 "\tutilities into a single executable. Most people will create a\n"
464 "\tlink to busybox for each function they wish to use and BusyBox\n"
465 "\twill act like whatever it was invoked as!\n"
466 "\nCurrently defined functions:\n");
467 col = 0;
468 a = applets;
469 while (a->name) {
470 if (col > output_width) {
471 puts(",");
472 col = 0;
473 }
474 col += printf("%s%s", (col ? ", " : "\t"), a->name);
475 a++;
476 }
477 puts("\n");
478 return 0;
479 }
480
481 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
482 const char *busybox;
483 busybox = xmalloc_readlink(bb_busybox_exec_path);
484 if (!busybox)
485 busybox = bb_busybox_exec_path;
486 /* -s makes symlinks */
487 install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0);
488 return 0;
489 }
490
491 if (strcmp(argv[1], "--help") == 0) {
492 /* "busybox --help [<applet>]" */
493 if (!argv[2])
494 goto help;
495 /* convert to "<applet> --help" */
496 argv[0] = argv[2];
497 argv[2] = NULL;
498 } else {
499 /* "busybox <applet> arg1 arg2 ..." */
500 argv++;
501 }
502 /* We support "busybox /a/path/to/applet args..." too. Allows for
503 * "#!/bin/busybox"-style wrappers */
504 applet_name = bb_get_last_path_component_nostrip(argv[0]);
505 run_applet_and_exit(applet_name, argv);
506 bb_error_msg_and_die("applet not found");
507}
508
509void run_appletstruct_and_exit(const struct bb_applet *applet, char **argv)
510{
511 int argc = 1;
512
513 while (argv[argc])
514 argc++;
515
516 /* Reinit some shared global data */
517 optind = 1;
518 xfunc_error_retval = EXIT_FAILURE;
519
520 applet_name = applet->name;
521 if (argc == 2 && !strcmp(argv[1], "--help"))
522 bb_show_usage();
523 if (ENABLE_FEATURE_SUID)
524 check_suid(applet);
525 exit(applet->main(argc, argv));
526}
527
528void run_applet_and_exit(const char *name, char **argv)
529{
530 const struct bb_applet *applet = find_applet_by_name(name);
531 if (applet)
532 run_appletstruct_and_exit(applet, argv);
533 if (!strncmp(name, "busybox", 7))
534 exit(busybox_main(argv));
535}
536
537
538int main(int argc, char **argv) 25int main(int argc, char **argv)
539{ 26{
540 bbox_prepare_main(argv); 27 return libbusybox_main(argc, argv);
541
542#if !BB_MMU
543 /* NOMMU re-exec trick sets high-order bit in first byte of name */
544 if (argv[0][0] & 0x80) {
545 re_execed = 1;
546 argv[0][0] &= 0x7f;
547 }
548#endif
549 applet_name = argv[0];
550 if (applet_name[0] == '-')
551 applet_name++;
552 applet_name = bb_basename(applet_name);
553
554 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
555
556 run_applet_and_exit(applet_name, argv);
557 bb_error_msg_and_die("applet not found");
558} 28}
29#endif
diff --git a/include/busybox.h b/include/busybox.h
index 1ab51cf78..1c23b9bb0 100644
--- a/include/busybox.h
+++ b/include/busybox.h
@@ -52,5 +52,8 @@ struct bb_applet {
52extern const struct bb_applet applets[]; 52extern const struct bb_applet applets[];
53extern const unsigned short NUM_APPLETS; 53extern const unsigned short NUM_APPLETS;
54void bbox_prepare_main(char **argv); 54void bbox_prepare_main(char **argv);
55#if ENABLE_BUILD_LIBBUSYBOX
56int libbusybox_main(int argc, char **argv);
57#endif
55 58
56#endif /* _BB_INTERNAL_H_ */ 59#endif /* _BB_INTERNAL_H_ */
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index cfa60a940..7808df501 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -1,11 +1,15 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2/* 2/*
3 * Support for main() which needs to end up in libbusybox, not busybox, 3 * Utility routines.
4 * if one builds libbusybox.
5 * 4 *
6 * Copyright (C) 2007 Denys Vlasenko <vda.linux@googlemail.com> 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.
7 * 8 *
8 * Licensed under GPLv2, see file License in this tarball for details. 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.
9 */ 13 */
10 14
11#include <assert.h> 15#include <assert.h>
@@ -140,3 +144,544 @@ void bbox_prepare_main(char **argv)
140 bb_show_usage(); 144 bb_show_usage();
141#endif 145#endif
142} 146}
147
148/* The code below can well be in applets/applets.c, as it is used only
149 * for busybox binary, not "individual" binaries.
150 * However, keeping it here and linking it into libbusybox.so
151 * (together with remaining tiny applets/applets.o)
152 * makes it possible to avoid --whole-archive at link time.
153 * This makes (shared busybox) + libbusybox smaller.
154 * (--gc-sections would be even better....)
155 */
156
157const char *applet_name;
158#if !BB_MMU
159bool re_execed;
160#endif
161
162USE_FEATURE_SUID(static uid_t ruid;) /* real uid */
163
164#if ENABLE_FEATURE_SUID_CONFIG
165
166/* applets[] is const, so we have to define this "override" structure */
167static struct BB_suid_config {
168 const struct bb_applet *m_applet;
169 uid_t m_uid;
170 gid_t m_gid;
171 mode_t m_mode;
172 struct BB_suid_config *m_next;
173} *suid_config;
174
175static bool suid_cfg_readable;
176
177/* check if u is member of group g */
178static int ingroup(uid_t u, gid_t g)
179{
180 struct group *grp = getgrgid(g);
181
182 if (grp) {
183 char **mem;
184
185 for (mem = grp->gr_mem; *mem; mem++) {
186 struct passwd *pwd = getpwnam(*mem);
187
188 if (pwd && (pwd->pw_uid == u))
189 return 1;
190 }
191 }
192 return 0;
193}
194
195/* This should probably be a libbb routine. In that case,
196 * I'd probably rename it to something like bb_trimmed_slice.
197 */
198static char *get_trimmed_slice(char *s, char *e)
199{
200 /* First, consider the value at e to be nul and back up until we
201 * reach a non-space char. Set the char after that (possibly at
202 * the original e) to nul. */
203 while (e-- > s) {
204 if (!isspace(*e)) {
205 break;
206 }
207 }
208 e[1] = '\0';
209
210 /* Next, advance past all leading space and return a ptr to the
211 * first non-space char; possibly the terminating nul. */
212 return skip_whitespace(s);
213}
214
215/* Don't depend on the tools to combine strings. */
216static const char config_file[] ALIGN1 = "/etc/busybox.conf";
217
218/* We don't supply a value for the nul, so an index adjustment is
219 * necessary below. Also, we use unsigned short here to save some
220 * space even though these are really mode_t values. */
221static const unsigned short mode_mask[] ALIGN2 = {
222 /* SST sst xxx --- */
223 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
224 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
225 0, S_IXOTH, S_IXOTH, 0 /* other */
226};
227
228#define parse_error(x) do { errmsg = x; goto pe_label; } while (0)
229
230static void parse_config_file(void)
231{
232 struct BB_suid_config *sct_head;
233 struct BB_suid_config *sct;
234 const struct bb_applet *applet;
235 FILE *f;
236 const char *errmsg;
237 char *s;
238 char *e;
239 int i;
240 unsigned lc;
241 smallint section;
242 char buffer[256];
243 struct stat st;
244
245 assert(!suid_config); /* Should be set to NULL by bss init. */
246
247 ruid = getuid();
248 if (ruid == 0) /* run by root - don't need to even read config file */
249 return;
250
251 if ((stat(config_file, &st) != 0) /* No config file? */
252 || !S_ISREG(st.st_mode) /* Not a regular file? */
253 || (st.st_uid != 0) /* Not owned by root? */
254 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
255 || !(f = fopen(config_file, "r")) /* Cannot open? */
256 ) {
257 return;
258 }
259
260 suid_cfg_readable = 1;
261 sct_head = NULL;
262 section = lc = 0;
263
264 while (1) {
265 s = buffer;
266
267 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
268 if (ferror(f)) { /* Make sure it wasn't a read error. */
269 parse_error("reading");
270 }
271 fclose(f);
272 suid_config = sct_head; /* Success, so set the pointer. */
273 return;
274 }
275
276 lc++; /* Got a (partial) line. */
277
278 /* If a line is too long for our buffer, we consider it an error.
279 * The following test does mistreat one corner case though.
280 * If the final line of the file does not end with a newline and
281 * yet exactly fills the buffer, it will be treated as too long
282 * even though there isn't really a problem. But it isn't really
283 * worth adding code to deal with such an unlikely situation, and
284 * we do err on the side of caution. Besides, the line would be
285 * too long if it did end with a newline. */
286 if (!strchr(s, '\n') && !feof(f)) {
287 parse_error("line too long");
288 }
289
290 /* Trim leading and trailing whitespace, ignoring comments, and
291 * check if the resulting string is empty. */
292 s = get_trimmed_slice(s, strchrnul(s, '#'));
293 if (!*s) {
294 continue;
295 }
296
297 /* Check for a section header. */
298
299 if (*s == '[') {
300 /* Unlike the old code, we ignore leading and trailing
301 * whitespace for the section name. We also require that
302 * there are no stray characters after the closing bracket. */
303 e = strchr(s, ']');
304 if (!e /* Missing right bracket? */
305 || e[1] /* Trailing characters? */
306 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
307 ) {
308 parse_error("section header");
309 }
310 /* Right now we only have one section so just check it.
311 * If more sections are added in the future, please don't
312 * resort to cascading ifs with multiple strcasecmp calls.
313 * That kind of bloated code is all too common. A loop
314 * and a string table would be a better choice unless the
315 * number of sections is very small. */
316 if (strcasecmp(s, "SUID") == 0) {
317 section = 1;
318 continue;
319 }
320 section = -1; /* Unknown section so set to skip. */
321 continue;
322 }
323
324 /* Process sections. */
325
326 if (section == 1) { /* SUID */
327 /* Since we trimmed leading and trailing space above, we're
328 * now looking for strings of the form
329 * <key>[::space::]*=[::space::]*<value>
330 * where both key and value could contain inner whitespace. */
331
332 /* First get the key (an applet name in our case). */
333 e = strchr(s, '=');
334 if (e) {
335 s = get_trimmed_slice(s, e);
336 }
337 if (!e || !*s) { /* Missing '=' or empty key. */
338 parse_error("keyword");
339 }
340
341 /* Ok, we have an applet name. Process the rhs if this
342 * applet is currently built in and ignore it otherwise.
343 * Note: this can hide config file bugs which only pop
344 * up when the busybox configuration is changed. */
345 applet = find_applet_by_name(s);
346 if (applet) {
347 /* Note: We currently don't check for duplicates!
348 * The last config line for each applet will be the
349 * one used since we insert at the head of the list.
350 * I suppose this could be considered a feature. */
351 sct = xmalloc(sizeof(struct BB_suid_config));
352 sct->m_applet = applet;
353 sct->m_mode = 0;
354 sct->m_next = sct_head;
355 sct_head = sct;
356
357 /* Get the specified mode. */
358
359 e = skip_whitespace(e+1);
360
361 for (i = 0; i < 3; i++) {
362 /* There are 4 chars + 1 nul for each of user/group/other. */
363 static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-";
364
365 const char *q;
366 q = strchrnul(mode_chars + 5*i, *e++);
367 if (!*q) {
368 parse_error("mode");
369 }
370 /* Adjust by -i to account for nul. */
371 sct->m_mode |= mode_mask[(q - mode_chars) - i];
372 }
373
374 /* Now get the the user/group info. */
375
376 s = skip_whitespace(e);
377
378 /* Note: we require whitespace between the mode and the
379 * user/group info. */
380 if ((s == e) || !(e = strchr(s, '.'))) {
381 parse_error("<uid>.<gid>");
382 }
383 *e++ = '\0';
384
385 /* We can't use get_ug_id here since it would exit()
386 * if a uid or gid was not found. Oh well... */
387 sct->m_uid = bb_strtoul(s, NULL, 10);
388 if (errno) {
389 struct passwd *pwd = getpwnam(s);
390 if (!pwd) {
391 parse_error("user");
392 }
393 sct->m_uid = pwd->pw_uid;
394 }
395
396 sct->m_gid = bb_strtoul(e, NULL, 10);
397 if (errno) {
398 struct group *grp;
399 grp = getgrnam(e);
400 if (!grp) {
401 parse_error("group");
402 }
403 sct->m_gid = grp->gr_gid;
404 }
405 }
406 continue;
407 }
408
409 /* Unknown sections are ignored. */
410
411 /* Encountering configuration lines prior to seeing a
412 * section header is treated as an error. This is how
413 * the old code worked, but it may not be desirable.
414 * We may want to simply ignore such lines in case they
415 * are used in some future version of busybox. */
416 if (!section) {
417 parse_error("keyword outside section");
418 }
419
420 } /* while (1) */
421
422 pe_label:
423 fprintf(stderr, "Parse error in %s, line %d: %s\n",
424 config_file, lc, errmsg);
425
426 fclose(f);
427 /* Release any allocated memory before returning. */
428 while (sct_head) {
429 sct = sct_head->m_next;
430 free(sct_head);
431 sct_head = sct;
432 }
433}
434#else
435static inline void parse_config_file(void)
436{
437 USE_FEATURE_SUID(ruid = getuid();)
438}
439#endif /* FEATURE_SUID_CONFIG */
440
441
442#if ENABLE_FEATURE_SUID
443static void check_suid(const struct bb_applet *applet)
444{
445 gid_t rgid; /* real gid */
446
447 if (ruid == 0) /* set by parse_config_file() */
448 return; /* run by root - no need to check more */
449 rgid = getgid();
450
451#if ENABLE_FEATURE_SUID_CONFIG
452 if (suid_cfg_readable) {
453 uid_t uid;
454 struct BB_suid_config *sct;
455 mode_t m;
456
457 for (sct = suid_config; sct; sct = sct->m_next) {
458 if (sct->m_applet == applet)
459 goto found;
460 }
461 /* default: drop all privileges */
462 xsetgid(rgid);
463 xsetuid(ruid);
464 return;
465 found:
466 m = sct->m_mode;
467 if (sct->m_uid == ruid)
468 /* same uid */
469 m >>= 6;
470 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
471 /* same group / in group */
472 m >>= 3;
473
474 if (!(m & S_IXOTH)) /* is x bit not set ? */
475 bb_error_msg_and_die("you have no permission to run this applet!");
476
477 /* _both_ sgid and group_exec have to be set for setegid */
478 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
479 rgid = sct->m_gid;
480 /* else (no setegid) we will set egid = rgid */
481
482 /* We set effective AND saved ids. If saved-id is not set
483 * like we do below, seteiud(0) can still later succeed! */
484 if (setresgid(-1, rgid, rgid))
485 bb_perror_msg_and_die("setresgid");
486
487 /* do we have to set effective uid? */
488 uid = ruid;
489 if (sct->m_mode & S_ISUID)
490 uid = sct->m_uid;
491 /* else (no seteuid) we will set euid = ruid */
492
493 if (setresuid(-1, uid, uid))
494 bb_perror_msg_and_die("setresuid");
495 return;
496 }
497#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
498 {
499 static bool onetime = 0;
500
501 if (!onetime) {
502 onetime = 1;
503 fprintf(stderr, "Using fallback suid method\n");
504 }
505 }
506#endif
507#endif
508
509 if (applet->need_suid == _BB_SUID_ALWAYS) {
510 /* Real uid is not 0. If euid isn't 0 too, suid bit
511 * is most probably not set on our executable */
512 if (geteuid())
513 bb_error_msg_and_die("applet requires root privileges!");
514 } else if (applet->need_suid == _BB_SUID_NEVER) {
515 xsetgid(rgid); /* drop all privileges */
516 xsetuid(ruid);
517 }
518}
519#else
520#define check_suid(x) ((void)0)
521#endif /* FEATURE_SUID */
522
523
524#if ENABLE_FEATURE_INSTALLER
525/* create (sym)links for each applet */
526static void install_links(const char *busybox, int use_symbolic_links)
527{
528 /* directory table
529 * this should be consistent w/ the enum,
530 * busybox.h::bb_install_loc_t, or else... */
531 static const char usr_bin [] ALIGN1 = "/usr/bin";
532 static const char usr_sbin[] ALIGN1 = "/usr/sbin";
533 static const char *const install_dir[] = {
534 &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
535 &usr_bin [4], /* "/bin" */
536 &usr_sbin[4], /* "/sbin" */
537 usr_bin,
538 usr_sbin
539 };
540
541 int (*lf)(const char *, const char *) = link;
542 char *fpc;
543 int i;
544 int rc;
545
546 if (use_symbolic_links)
547 lf = symlink;
548
549 for (i = 0; applets[i].name != NULL; i++) {
550 fpc = concat_path_file(
551 install_dir[applets[i].install_loc],
552 applets[i].name);
553 rc = lf(busybox, fpc);
554 if (rc != 0 && errno != EEXIST) {
555 bb_simple_perror_msg(fpc);
556 }
557 free(fpc);
558 }
559}
560#else
561#define install_links(x,y) ((void)0)
562#endif /* FEATURE_INSTALLER */
563
564/* If we were called as "busybox..." */
565static int busybox_main(char **argv)
566{
567 if (!argv[1]) {
568 /* Called without arguments */
569 const struct bb_applet *a;
570 int col, output_width;
571 help:
572 output_width = 80;
573 if (ENABLE_FEATURE_AUTOWIDTH) {
574 /* Obtain the terminal width */
575 get_terminal_width_height(0, &output_width, NULL);
576 }
577 /* leading tab and room to wrap */
578 output_width -= sizeof("start-stop-daemon, ") + 8;
579
580 printf("%s multi-call binary\n", bb_banner); /* reuse const string... */
581 printf("Copyright (C) 1998-2006 Erik Andersen, Rob Landley, and others.\n"
582 "Licensed under GPLv2. See source distribution for full notice.\n"
583 "\n"
584 "Usage: busybox [function] [arguments]...\n"
585 " or: [function] [arguments]...\n"
586 "\n"
587 "\tBusyBox is a multi-call binary that combines many common Unix\n"
588 "\tutilities into a single executable. Most people will create a\n"
589 "\tlink to busybox for each function they wish to use and BusyBox\n"
590 "\twill act like whatever it was invoked as!\n"
591 "\nCurrently defined functions:\n");
592 col = 0;
593 a = applets;
594 while (a->name) {
595 if (col > output_width) {
596 puts(",");
597 col = 0;
598 }
599 col += printf("%s%s", (col ? ", " : "\t"), a->name);
600 a++;
601 }
602 puts("\n");
603 return 0;
604 }
605
606 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
607 const char *busybox;
608 busybox = xmalloc_readlink(bb_busybox_exec_path);
609 if (!busybox)
610 busybox = bb_busybox_exec_path;
611 /* -s makes symlinks */
612 install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0);
613 return 0;
614 }
615
616 if (strcmp(argv[1], "--help") == 0) {
617 /* "busybox --help [<applet>]" */
618 if (!argv[2])
619 goto help;
620 /* convert to "<applet> --help" */
621 argv[0] = argv[2];
622 argv[2] = NULL;
623 } else {
624 /* "busybox <applet> arg1 arg2 ..." */
625 argv++;
626 }
627 /* We support "busybox /a/path/to/applet args..." too. Allows for
628 * "#!/bin/busybox"-style wrappers */
629 applet_name = bb_get_last_path_component_nostrip(argv[0]);
630 run_applet_and_exit(applet_name, argv);
631 bb_error_msg_and_die("applet not found");
632}
633
634void run_appletstruct_and_exit(const struct bb_applet *applet, char **argv)
635{
636 int argc = 1;
637
638 while (argv[argc])
639 argc++;
640
641 /* Reinit some shared global data */
642 optind = 1;
643 xfunc_error_retval = EXIT_FAILURE;
644
645 applet_name = applet->name;
646 if (argc == 2 && !strcmp(argv[1], "--help"))
647 bb_show_usage();
648 if (ENABLE_FEATURE_SUID)
649 check_suid(applet);
650 exit(applet->main(argc, argv));
651}
652
653void run_applet_and_exit(const char *name, char **argv)
654{
655 const struct bb_applet *applet = find_applet_by_name(name);
656 if (applet)
657 run_appletstruct_and_exit(applet, argv);
658 if (!strncmp(name, "busybox", 7))
659 exit(busybox_main(argv));
660}
661
662
663#if ENABLE_BUILD_LIBBUSYBOX
664int libbusybox_main(int argc, char **argv)
665#else
666int main(int argc, char **argv)
667#endif
668{
669 bbox_prepare_main(argv);
670
671#if !BB_MMU
672 /* NOMMU re-exec trick sets high-order bit in first byte of name */
673 if (argv[0][0] & 0x80) {
674 re_execed = 1;
675 argv[0][0] &= 0x7f;
676 }
677#endif
678 applet_name = argv[0];
679 if (applet_name[0] == '-')
680 applet_name++;
681 applet_name = bb_basename(applet_name);
682
683 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
684
685 run_applet_and_exit(applet_name, argv);
686 bb_error_msg_and_die("applet not found");
687}
diff --git a/scripts/trylink b/scripts/trylink
index fc87df032..6292dc659 100755
--- a/scripts/trylink
+++ b/scripts/trylink
@@ -19,18 +19,16 @@ debug=false
19# List of files to link: 19# List of files to link:
20# $l_list == --start-group -llib1 -llib2 --end-group 20# $l_list == --start-group -llib1 -llib2 --end-group
21# --start-group $O_FILES $A_FILES --end-group 21# --start-group $O_FILES $A_FILES --end-group
22# or for shlib:
23# --start-group --whole-archive $A_FILES --no-whole-archive --end-group
24# otherwise we don't pull in all parts of *.a files. Now we pull in too much.
25# FIXME: make it work without --whole-archive, or better, with --gc-sections.
26# 22#
27# Shared library link: 23# Shared library link:
28# -shared self-explanatory 24# -shared self-explanatory
29# -fPIC position-independent code 25# -fPIC position-independent code
30# -Wl,--enable-new-dtags ? 26# --enable-new-dtags ?
31# -Wl,-z,combreloc ? 27# -z,combreloc ?
32# -Wl,-soname="libbusybox.so.$BB_VER" 28# -soname="libbusybox.so.$BB_VER"
33# -static Not used, but may be useful! manpage: 29# --undefined=libbusybox_main Seed name to start pulling from
30# (otherwise we'll need --whole-archive)
31# -static Not used, but may be useful! manpage:
34# "... This option can be used with -shared. 32# "... This option can be used with -shared.
35# Doing so means that a shared library 33# Doing so means that a shared library
36# is being created but that all of the library's 34# is being created but that all of the library's
@@ -174,9 +172,10 @@ if test "$CONFIG_BUILD_LIBBUSYBOX" = y; then
174 -Wl,--enable-new-dtags \ 172 -Wl,--enable-new-dtags \
175 -Wl,-z,combreloc \ 173 -Wl,-z,combreloc \
176 -Wl,-soname="libbusybox.so.$BB_VER" \ 174 -Wl,-soname="libbusybox.so.$BB_VER" \
175 -Wl,--undefined=libbusybox_main \
177 -Wl,--sort-common \ 176 -Wl,--sort-common \
178 -Wl,--sort-section -Wl,alignment \ 177 -Wl,--sort-section -Wl,alignment \
179 -Wl,--start-group -Wl,--whole-archive $A_FILES -Wl,--no-whole-archive -Wl,--end-group \ 178 -Wl,--start-group $A_FILES -Wl,--end-group \
180 $l_list \ 179 $l_list \
181 -Wl,--warn-common \ 180 -Wl,--warn-common \
182 -Wl,-Map -Wl,$EXE.map \ 181 -Wl,-Map -Wl,$EXE.map \