Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/applets/applets.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/applets/applets.c
r1765 r2725 1 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 6 * isn't something I'm going to worry about... If you wrote something 7 * here, please feel free to acknowledge your work. 5 * Copyright (C) 2007 Denys Vlasenko <vda.linux@googlemail.com> 8 6 * 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. 7 * Licensed under GPLv2, see file LICENSE in this source tree. 13 8 */ 14 15 #include <assert.h>16 9 #include "busybox.h" 17 10 18 /* Apparently uclibc defines __GLIBC__ (compat trick?). Oh well. */ 19 #if ENABLE_STATIC && defined(__GLIBC__) && !defined(__UCLIBC__) 20 #warning Static linking against glibc produces buggy executables 21 #warning (glibc does not cope well with ld --gc-sections). 22 #warning See sources.redhat.com/bugzilla/show_bug.cgi?id=3400 23 #warning Note that glibc is unsuitable for static linking anyway. 24 #warning If you still want to do it, remove -Wl,--gc-sections 25 #warning from top-level Makefile and remove this warning. 26 #error Aborting compilation. 11 #if ENABLE_BUILD_LIBBUSYBOX 12 int main(int argc UNUSED_PARAM, char **argv) 13 { 14 return lbb_main(argv); 15 } 27 16 #endif 28 29 30 /* Declare <applet>_main() */31 #define PROTOTYPES32 #include "applets.h"33 #undef PROTOTYPES34 35 #if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE36 /* Define usage_messages[] */37 static const char usage_messages[] ALIGN1 = ""38 #define MAKE_USAGE39 #include "usage.h"40 #include "applets.h"41 ;42 #undef MAKE_USAGE43 #else44 #define usage_messages 045 #endif /* SHOW_USAGE */46 47 /* Define struct bb_applet applets[] */48 #include "applets.h"49 /* The -1 arises because of the {0,NULL,0,-1} entry. */50 51 #if ENABLE_FEATURE_SH_STANDALONE52 const unsigned short NUM_APPLETS = ARRAY_SIZE(applets);53 #endif54 const struct bb_applet *current_applet;55 const char *applet_name ATTRIBUTE_EXTERNALLY_VISIBLE;56 #if !BB_MMU57 bool re_execed;58 #endif59 60 USE_FEATURE_SUID(static uid_t ruid;) /* real uid */61 62 #if ENABLE_FEATURE_SUID_CONFIG63 64 /* applets[] is const, so we have to define this "override" structure */65 static struct BB_suid_config {66 const struct bb_applet *m_applet;67 uid_t m_uid;68 gid_t m_gid;69 mode_t m_mode;70 struct BB_suid_config *m_next;71 } *suid_config;72 73 static bool suid_cfg_readable;74 75 /* check if u is member of group g */76 static int ingroup(uid_t u, gid_t g)77 {78 struct group *grp = getgrgid(g);79 80 if (grp) {81 char **mem;82 83 for (mem = grp->gr_mem; *mem; mem++) {84 struct passwd *pwd = getpwnam(*mem);85 86 if (pwd && (pwd->pw_uid == u))87 return 1;88 }89 }90 return 0;91 }92 93 /* This should probably be a libbb routine. In that case,94 * I'd probably rename it to something like bb_trimmed_slice.95 */96 static char *get_trimmed_slice(char *s, char *e)97 {98 /* First, consider the value at e to be nul and back up until we99 * reach a non-space char. Set the char after that (possibly at100 * the original e) to nul. */101 while (e-- > s) {102 if (!isspace(*e)) {103 break;104 }105 }106 e[1] = '\0';107 108 /* Next, advance past all leading space and return a ptr to the109 * first non-space char; possibly the terminating nul. */110 return skip_whitespace(s);111 }112 113 /* Don't depend on the tools to combine strings. */114 static const char config_file[] ALIGN1 = "/etc/busybox.conf";115 116 /* We don't supply a value for the nul, so an index adjustment is117 * necessary below. Also, we use unsigned short here to save some118 * space even though these are really mode_t values. */119 static const unsigned short mode_mask[] ALIGN2 = {120 /* SST sst xxx --- */121 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */122 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */123 0, S_IXOTH, S_IXOTH, 0 /* other */124 };125 126 #define parse_error(x) do { errmsg = x; goto pe_label; } while (0)127 128 static void parse_config_file(void)129 {130 struct BB_suid_config *sct_head;131 struct BB_suid_config *sct;132 const struct bb_applet *applet;133 FILE *f;134 const char *errmsg;135 char *s;136 char *e;137 int i;138 unsigned lc;139 smallint section;140 char buffer[256];141 struct stat st;142 143 assert(!suid_config); /* Should be set to NULL by bss init. */144 145 ruid = getuid();146 if (ruid == 0) /* run by root - don't need to even read config file */147 return;148 149 if ((stat(config_file, &st) != 0) /* No config file? */150 || !S_ISREG(st.st_mode) /* Not a regular file? */151 || (st.st_uid != 0) /* Not owned by root? */152 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */153 || !(f = fopen(config_file, "r")) /* Cannot open? */154 ) {155 return;156 }157 158 suid_cfg_readable = 1;159 sct_head = NULL;160 section = lc = 0;161 162 while (1) {163 s = buffer;164 165 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */166 if (ferror(f)) { /* Make sure it wasn't a read error. */167 parse_error("reading");168 }169 fclose(f);170 suid_config = sct_head; /* Success, so set the pointer. */171 return;172 }173 174 lc++; /* Got a (partial) line. */175 176 /* If a line is too long for our buffer, we consider it an error.177 * The following test does mistreat one corner case though.178 * If the final line of the file does not end with a newline and179 * yet exactly fills the buffer, it will be treated as too long180 * even though there isn't really a problem. But it isn't really181 * worth adding code to deal with such an unlikely situation, and182 * we do err on the side of caution. Besides, the line would be183 * too long if it did end with a newline. */184 if (!strchr(s, '\n') && !feof(f)) {185 parse_error("line too long");186 }187 188 /* Trim leading and trailing whitespace, ignoring comments, and189 * check if the resulting string is empty. */190 s = get_trimmed_slice(s, strchrnul(s, '#'));191 if (!*s) {192 continue;193 }194 195 /* Check for a section header. */196 197 if (*s == '[') {198 /* Unlike the old code, we ignore leading and trailing199 * whitespace for the section name. We also require that200 * there are no stray characters after the closing bracket. */201 e = strchr(s, ']');202 if (!e /* Missing right bracket? */203 || e[1] /* Trailing characters? */204 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */205 ) {206 parse_error("section header");207 }208 /* Right now we only have one section so just check it.209 * If more sections are added in the future, please don't210 * resort to cascading ifs with multiple strcasecmp calls.211 * That kind of bloated code is all too common. A loop212 * and a string table would be a better choice unless the213 * number of sections is very small. */214 if (strcasecmp(s, "SUID") == 0) {215 section = 1;216 continue;217 }218 section = -1; /* Unknown section so set to skip. */219 continue;220 }221 222 /* Process sections. */223 224 if (section == 1) { /* SUID */225 /* Since we trimmed leading and trailing space above, we're226 * now looking for strings of the form227 * <key>[::space::]*=[::space::]*<value>228 * where both key and value could contain inner whitespace. */229 230 /* First get the key (an applet name in our case). */231 e = strchr(s, '=');232 if (e) {233 s = get_trimmed_slice(s, e);234 }235 if (!e || !*s) { /* Missing '=' or empty key. */236 parse_error("keyword");237 }238 239 /* Ok, we have an applet name. Process the rhs if this240 * applet is currently built in and ignore it otherwise.241 * Note: this can hide config file bugs which only pop242 * up when the busybox configuration is changed. */243 applet = find_applet_by_name(s);244 if (applet) {245 /* Note: We currently don't check for duplicates!246 * The last config line for each applet will be the247 * one used since we insert at the head of the list.248 * I suppose this could be considered a feature. */249 sct = xmalloc(sizeof(struct BB_suid_config));250 sct->m_applet = applet;251 sct->m_mode = 0;252 sct->m_next = sct_head;253 sct_head = sct;254 255 /* Get the specified mode. */256 257 e = skip_whitespace(e+1);258 259 for (i = 0; i < 3; i++) {260 /* There are 4 chars + 1 nul for each of user/group/other. */261 static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-";262 263 const char *q;264 q = strchrnul(mode_chars + 5*i, *e++);265 if (!*q) {266 parse_error("mode");267 }268 /* Adjust by -i to account for nul. */269 sct->m_mode |= mode_mask[(q - mode_chars) - i];270 }271 272 /* Now get the the user/group info. */273 274 s = skip_whitespace(e);275 276 /* Note: we require whitespace between the mode and the277 * user/group info. */278 if ((s == e) || !(e = strchr(s, '.'))) {279 parse_error("<uid>.<gid>");280 }281 *e++ = '\0';282 283 /* We can't use get_ug_id here since it would exit()284 * if a uid or gid was not found. Oh well... */285 sct->m_uid = bb_strtoul(s, NULL, 10);286 if (errno) {287 struct passwd *pwd = getpwnam(s);288 if (!pwd) {289 parse_error("user");290 }291 sct->m_uid = pwd->pw_uid;292 }293 294 sct->m_gid = bb_strtoul(e, NULL, 10);295 if (errno) {296 struct group *grp;297 grp = getgrnam(e);298 if (!grp) {299 parse_error("group");300 }301 sct->m_gid = grp->gr_gid;302 }303 }304 continue;305 }306 307 /* Unknown sections are ignored. */308 309 /* Encountering configuration lines prior to seeing a310 * section header is treated as an error. This is how311 * the old code worked, but it may not be desirable.312 * We may want to simply ignore such lines in case they313 * are used in some future version of busybox. */314 if (!section) {315 parse_error("keyword outside section");316 }317 318 } /* while (1) */319 320 pe_label:321 fprintf(stderr, "Parse error in %s, line %d: %s\n",322 config_file, lc, errmsg);323 324 fclose(f);325 /* Release any allocated memory before returning. */326 while (sct_head) {327 sct = sct_head->m_next;328 free(sct_head);329 sct_head = sct;330 }331 }332 #else333 static inline void parse_config_file(void)334 {335 USE_FEATURE_SUID(ruid = getuid();)336 }337 #endif /* FEATURE_SUID_CONFIG */338 339 340 #if ENABLE_FEATURE_SUID341 static void check_suid(const struct bb_applet *applet)342 {343 gid_t rgid; /* real gid */344 345 if (ruid == 0) /* set by parse_config_file() */346 return; /* run by root - no need to check more */347 rgid = getgid();348 349 #if ENABLE_FEATURE_SUID_CONFIG350 if (suid_cfg_readable) {351 uid_t uid;352 struct BB_suid_config *sct;353 mode_t m;354 355 for (sct = suid_config; sct; sct = sct->m_next) {356 if (sct->m_applet == applet)357 goto found;358 }359 /* default: drop all privileges */360 xsetgid(rgid);361 xsetuid(ruid);362 return;363 found:364 m = sct->m_mode;365 if (sct->m_uid == ruid)366 /* same uid */367 m >>= 6;368 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))369 /* same group / in group */370 m >>= 3;371 372 if (!(m & S_IXOTH)) /* is x bit not set ? */373 bb_error_msg_and_die("you have no permission to run this applet!");374 375 /* _both_ sgid and group_exec have to be set for setegid */376 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))377 rgid = sct->m_gid;378 /* else (no setegid) we will set egid = rgid */379 380 /* We set effective AND saved ids. If saved-id is not set381 * like we do below, seteiud(0) can still later succeed! */382 if (setresgid(-1, rgid, rgid))383 bb_perror_msg_and_die("setresgid");384 385 /* do we have to set effective uid? */386 uid = ruid;387 if (sct->m_mode & S_ISUID)388 uid = sct->m_uid;389 /* else (no seteuid) we will set euid = ruid */390 391 if (setresuid(-1, uid, uid))392 bb_perror_msg_and_die("setresuid");393 return;394 }395 #if !ENABLE_FEATURE_SUID_CONFIG_QUIET396 {397 static bool onetime = 0;398 399 if (!onetime) {400 onetime = 1;401 fprintf(stderr, "Using fallback suid method\n");402 }403 }404 #endif405 #endif406 407 if (applet->need_suid == _BB_SUID_ALWAYS) {408 /* Real uid is not 0. If euid isn't 0 too, suid bit409 * is most probably not set on our executable */410 if (geteuid())411 bb_error_msg_and_die("applet requires root privileges!");412 } else if (applet->need_suid == _BB_SUID_NEVER) {413 xsetgid(rgid); /* drop all privileges */414 xsetuid(ruid);415 }416 }417 #else418 #define check_suid(x) ((void)0)419 #endif /* FEATURE_SUID */420 421 422 #if ENABLE_FEATURE_COMPRESS_USAGE423 424 #include "usage_compressed.h"425 #include "unarchive.h"426 427 static const char *unpack_usage_messages(void)428 {429 char *outbuf = NULL;430 bunzip_data *bd;431 int i;432 433 i = start_bunzip(&bd,434 /* src_fd: */ -1,435 /* inbuf: */ packed_usage,436 /* len: */ sizeof(packed_usage));437 /* read_bunzip can longjmp to start_bunzip, and ultimately438 * end up here with i != 0 on read data errors! Not trivial */439 if (!i) {440 /* Cannot use xmalloc: will leak bd in NOFORK case! */441 outbuf = malloc_or_warn(SIZEOF_usage_messages);442 if (outbuf)443 read_bunzip(bd, outbuf, SIZEOF_usage_messages);444 }445 dealloc_bunzip(bd);446 return outbuf;447 }448 #define dealloc_usage_messages(s) free(s)449 450 #else451 452 #define unpack_usage_messages() usage_messages453 #define dealloc_usage_messages(s) ((void)(s))454 455 #endif /* FEATURE_COMPRESS_USAGE */456 457 458 void bb_show_usage(void)459 {460 if (ENABLE_SHOW_USAGE) {461 const char *format_string;462 const char *p;463 const char *usage_string = p = unpack_usage_messages();464 int i;465 466 i = current_applet - applets;467 while (i) {468 while (*p++) continue;469 i--;470 }471 472 fprintf(stderr, "%s multi-call binary\n", bb_banner);473 format_string = "\nUsage: %s %s\n\n";474 if (*p == '\b')475 format_string = "\nNo help available.\n\n";476 fprintf(stderr, format_string, applet_name, p);477 dealloc_usage_messages((char*)usage_string);478 }479 xfunc_die();480 }481 482 483 static int applet_name_compare(const void *name, const void *vapplet)484 {485 const struct bb_applet *applet = vapplet;486 487 return strcmp(name, applet->name);488 }489 490 const struct bb_applet *find_applet_by_name(const char *name)491 {492 /* Do a binary search to find the applet entry given the name. */493 return bsearch(name, applets, ARRAY_SIZE(applets)-1, sizeof(applets[0]),494 applet_name_compare);495 }496 497 498 #if ENABLE_FEATURE_INSTALLER499 /* create (sym)links for each applet */500 static void install_links(const char *busybox, int use_symbolic_links)501 {502 /* directory table503 * this should be consistent w/ the enum,504 * busybox.h::bb_install_loc_t, or else... */505 static const char usr_bin [] ALIGN1 = "/usr/bin";506 static const char usr_sbin[] ALIGN1 = "/usr/sbin";507 static const char *const install_dir[] = {508 &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */509 &usr_bin [4], /* "/bin" */510 &usr_sbin[4], /* "/sbin" */511 usr_bin,512 usr_sbin513 };514 515 int (*lf)(const char *, const char *) = link;516 char *fpc;517 int i;518 int rc;519 520 if (use_symbolic_links)521 lf = symlink;522 523 for (i = 0; applets[i].name != NULL; i++) {524 fpc = concat_path_file(525 install_dir[applets[i].install_loc],526 applets[i].name);527 rc = lf(busybox, fpc);528 if (rc != 0 && errno != EEXIST) {529 bb_perror_msg("%s", fpc);530 }531 free(fpc);532 }533 }534 #else535 #define install_links(x,y) ((void)0)536 #endif /* FEATURE_INSTALLER */537 538 539 /* If we were called as "busybox..." */540 static int busybox_main(char **argv)541 {542 if (!argv[1]) {543 /* Called without arguments */544 const struct bb_applet *a;545 int col, output_width;546 help:547 output_width = 80;548 if (ENABLE_FEATURE_AUTOWIDTH) {549 /* Obtain the terminal width */550 get_terminal_width_height(0, &output_width, NULL);551 }552 /* leading tab and room to wrap */553 output_width -= sizeof("start-stop-daemon, ") + 8;554 555 printf("%s multi-call binary\n", bb_banner); /* reuse const string... */556 printf("Copyright (C) 1998-2006 Erik Andersen, Rob Landley, and others.\n"557 "Licensed under GPLv2. See source distribution for full notice.\n"558 "\n"559 "Usage: busybox [function] [arguments]...\n"560 " or: [function] [arguments]...\n"561 "\n"562 "\tBusyBox is a multi-call binary that combines many common Unix\n"563 "\tutilities into a single executable. Most people will create a\n"564 "\tlink to busybox for each function they wish to use and BusyBox\n"565 "\twill act like whatever it was invoked as!\n"566 "\nCurrently defined functions:\n");567 col = 0;568 a = applets;569 while (a->name) {570 if (col > output_width) {571 puts(",");572 col = 0;573 }574 col += printf("%s%s", (col ? ", " : "\t"), a->name);575 a++;576 }577 puts("\n");578 return 0;579 }580 581 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {582 const char *busybox;583 busybox = xmalloc_readlink(bb_busybox_exec_path);584 if (!busybox)585 busybox = bb_busybox_exec_path;586 /* -s makes symlinks */587 install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0);588 return 0;589 }590 591 if (strcmp(argv[1], "--help") == 0) {592 /* "busybox --help [<applet>]" */593 if (!argv[2])594 goto help;595 /* convert to "<applet> --help" */596 argv[0] = argv[2];597 argv[2] = NULL;598 } else {599 /* "busybox <applet> arg1 arg2 ..." */600 argv++;601 }602 /* we want "<argv[0]>: applet not found", not "busybox: ..." */603 applet_name = argv[0];604 run_applet_and_exit(argv[0], argv);605 bb_error_msg_and_die("applet not found");606 }607 608 void run_current_applet_and_exit(char **argv)609 {610 int argc = 1;611 612 while (argv[argc])613 argc++;614 615 /* Reinit some shared global data */616 optind = 1;617 xfunc_error_retval = EXIT_FAILURE;618 619 applet_name = current_applet->name;620 if (argc == 2 && !strcmp(argv[1], "--help"))621 bb_show_usage();622 if (ENABLE_FEATURE_SUID)623 check_suid(current_applet);624 exit(current_applet->main(argc, argv));625 }626 627 void run_applet_and_exit(const char *name, char **argv)628 {629 current_applet = find_applet_by_name(name);630 if (current_applet)631 run_current_applet_and_exit(argv);632 if (!strncmp(name, "busybox", 7))633 exit(busybox_main(argv));634 }635 636 637 #ifdef __GLIBC__638 /* Make it reside in R/W memory: */639 int *const bb_errno __attribute__ ((section (".data")));640 #endif641 642 int main(int argc, char **argv)643 {644 #ifdef __GLIBC__645 (*(int **)&bb_errno) = __errno_location();646 #endif647 648 #if !BB_MMU649 /* NOMMU re-exec trick sets high-order bit in first byte of name */650 if (argv[0][0] & 0x80) {651 re_execed = 1;652 argv[0][0] &= 0x7f;653 }654 #endif655 applet_name = argv[0];656 if (applet_name[0] == '-')657 applet_name++;658 applet_name = bb_basename(applet_name);659 660 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */661 662 /* Set locale for everybody except 'init' */663 if (ENABLE_LOCALE_SUPPORT && getpid() != 1)664 setlocale(LC_ALL, "");665 666 run_applet_and_exit(applet_name, argv);667 bb_error_msg_and_die("applet not found");668 }
Note:
See TracChangeset
for help on using the changeset viewer.