summaryrefslogtreecommitdiff
path: root/applets
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 /applets
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.
Diffstat (limited to 'applets')
-rw-r--r--applets/applets.c541
1 files changed, 6 insertions, 535 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