staticintbusybox_main(char **argv) { if (!argv[1]) { /* Called without arguments */ constchar *a; int col; unsigned output_width; help: output_width = 80; if (ENABLE_FEATURE_AUTOWIDTH) { /* Obtain the terminal width */ output_width = get_terminal_width(2); }
dup2(1, 2); full_write2_str(bb_banner); /* reuse const string */ full_write2_str(" multi-call binary.\n"); /* reuse */ full_write2_str( "BusyBox is copyrighted by many authors between 1998-2015.\n" "Licensed under GPLv2. See source distribution for detailed\n" "copyright notices.\n" "\n" "Usage: busybox [function [arguments]...]\n" " or: busybox --list"IF_FEATURE_INSTALLER("[-full]")"\n" IF_FEATURE_INSTALLER( " or: busybox --install [-s] [DIR]\n" ) " or: function [arguments]...\n" "\n" IF_NOT_FEATURE_SH_STANDALONE( "\tBusyBox is a multi-call binary that combines many common Unix\n" "\tutilities into a single executable. Most people will create a\n" "\tlink to busybox for each function they wish to use and BusyBox\n" "\twill act like whatever it was invoked as.\n" ) IF_FEATURE_SH_STANDALONE( "\tBusyBox is a multi-call binary that combines many common Unix\n" "\tutilities into a single executable. The shell in this build\n" "\tis configured to run built-in utilities without $PATH search.\n" "\tYou don't need to install a link to busybox for each utility.\n" "\tTo run external program, use full path (/sbin/ip instead of ip).\n" ) "\n" "Currently defined functions:\n" ); col = 0; a = applet_names; /* prevent last comma to be in the very last pos */ output_width--; while (*a) { int len2 = strlen(a) + 2; if (col >= (int)output_width - len2) { full_write2_str(",\n"); col = 0; } if (col == 0) { col = 6; full_write2_str("\t"); } else { full_write2_str(", "); } full_write2_str(a); col += len2; a += len2 - 1; } full_write2_str("\n"); return0; }
if (is_prefixed_with(argv[1], "--list")) { unsigned i = 0; constchar *a = applet_names; dup2(1, 2); while (*a) { # if ENABLE_FEATURE_INSTALLER if (argv[1][6]) /* --list-full? */ full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1); # endif full_write2_str(a); full_write2_str("\n"); i++; while (*a++ != '\0') continue; } return0; }
if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) { int use_symbolic_links; constchar *busybox;
busybox = xmalloc_readlink(bb_busybox_exec_path); if (!busybox) { /* bb_busybox_exec_path is usually "/proc/self/exe". * In chroot, readlink("/proc/self/exe") usually fails. * In such case, better use argv[0] as symlink target * if it is a full path name. */ if (argv[0][0] != '/') bb_error_msg_and_die("'%s' is not an absolute path", argv[0]); busybox = argv[0]; } /* busybox --install [-s] [DIR]: * -s: make symlinks * DIR: directory to install links to */ use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && ++argv); install_links(busybox, use_symbolic_links, argv[2]); return0; }
if (strcmp(argv[1], "--help") == 0) { /* "busybox --help [<applet>]" */ if (!argv[2]) goto help; /* convert to "<applet> --help" */ argv[0] = argv[2]; argv[2] = NULL; } else { /* "busybox <applet> arg1 arg2 ..." */ argv++; } /* We support "busybox /a/path/to/applet args..." too. Allows for * "#!/bin/busybox"-style wrappers */ applet_name = bb_get_last_path_component_nostrip(argv[0]); run_applet_and_exit(applet_name, argv); }
static NORETURN voidrun_applet_and_exit(constchar *name, char **argv) { # if ENABLE_BUSYBOX if (is_prefixed_with(name, "busybox")) exit(busybox_main(argv)); # endif # if NUM_APPLETS > 0 /* find_applet_by_name() search is more expensive, so goes second */ { int applet = find_applet_by_name(name); // 根据第一个参数值找到对应的applet if (applet >= 0) run_applet_no_and_exit(applet, argv); // 进入这里 } # endif
/*bb_error_msg_and_die("applet not found"); - links in printf */ full_write2_str(applet_name); full_write2_str(": applet not found\n"); /* POSIX: "If a command is not found, the exit status shall be 127" */ exit(127); }
/* Reinit some shared global data */ xfunc_error_retval = EXIT_FAILURE; applet_name = bb_get_last_path_component_nostrip(argv[0]);
/* Special case. POSIX says "test --help" * should be no different from e.g. "test --foo". * Thus for "test", we skip --help check. * "true" and "false" are also special. */ if (1 # if defined APPLET_NO_test && applet_no != APPLET_NO_test # endif # if defined APPLET_NO_true && applet_no != APPLET_NO_true # endif # if defined APPLET_NO_false && applet_no != APPLET_NO_false # endif ) { if (argc == 2 && strcmp(argv[1], "--help") == 0) { /* Make "foo --help" exit with 0: */ xfunc_error_retval = 0; bb_show_usage(); } } if (ENABLE_FEATURE_SUID) // 默认开启 check_suid(applet_no); // 关键 xfunc_error_retval = applet_main[applet_no](argc, argv); // 启动对应的applet,我们这里对应的是ash_main /* Note: applet_main() may also not return (die on a xfunc or such) */ xfunc_die(); }
staticvoidcheck_suid(int applet_no) { gid_t rgid; /* real gid */
if (ruid == 0) /* set by parse_config_file() */ return; /* run by root - no need to check more */// 斜眼笑.jpg rgid = getgid();
# if ENABLE_FEATURE_SUID_CONFIG if (suid_cfg_readable) { uid_t uid; structsuid_config_t *sct; mode_t m;
for (sct = suid_config; sct; sct = sct->m_next) { if (sct->m_applet == applet_no) goto found; } goto check_need_suid; found: /* Is this user allowed to run this applet? */ m = sct->m_mode; if (sct->m_ugid.uid == ruid) /* same uid */ m >>= 6; elseif ((sct->m_ugid.gid == rgid) || ingroup(ruid, sct->m_ugid.gid)) /* same group / in group */ m >>= 3; if (!(m & S_IXOTH)) /* is x bit not set? */ bb_error_msg_and_die("you have no permission to run this applet");
/* We set effective AND saved ids. If saved-id is not set * like we do below, seteuid(0) can still later succeed! */
/* Are we directed to change gid * (APPLET = *s* USER.GROUP or APPLET = *S* USER.GROUP)? */ if (sct->m_mode & S_ISGID) rgid = sct->m_ugid.gid; /* else: we will set egid = rgid, thus dropping sgid effect */ if (setresgid(-1, rgid, rgid)) bb_perror_msg_and_die("setresgid");
/* Are we directed to change uid * (APPLET = s** USER.GROUP or APPLET = S** USER.GROUP)? */ uid = ruid; if (sct->m_mode & S_ISUID) uid = sct->m_ugid.uid; /* else: we will set euid = ruid, thus dropping suid effect */ if (setresuid(-1, uid, uid)) bb_perror_msg_and_die("setresuid");
if (!onetime) { onetime = 1; bb_error_msg("using fallback suid method"); } } # endif check_need_suid: # endif if (APPLET_SUID(applet_no) == BB_SUID_REQUIRE) { /* Real uid is not 0. If euid isn't 0 too, suid bit * is most probably not set on our executable */ if (geteuid()) bb_error_msg_and_die("must be suid to work properly"); } elseif (APPLET_SUID(applet_no) == BB_SUID_DROP) { xsetgid(rgid); /* drop all privileges */// 如果我们只修改euid&&egid为0,不修改uid&&gid,就会执行这个分支语句,将所有id置为rxID xsetuid(ruid); } # if ENABLE_FEATURE_SUID_CONFIG ret: ; llist_free((llist_t*)suid_config, NULL); # endif }
/** * cap_bprm_set_creds - Set up the proposed credentials for execve(). * @bprm: The execution parameters, including the proposed creds * * Set up the proposed credentials for a new execution context being * constructed by execve(). The proposed creds in @bprm->cred is altered, * which won't take effect immediately. Returns 0 if successful, -ve on error. */ intcap_bprm_set_creds(struct linux_binprm *bprm) { conststructcred *old = current_cred(); structcred *new = bprm->cred; bool effective, has_cap = false, is_setid; int ret; kuid_t root_uid;
if (WARN_ON(!cap_ambient_invariant_ok(old))) return -EPERM;
effective = false; ret = get_file_caps(bprm, &effective, &has_cap); if (ret < 0) return ret;
root_uid = make_kuid(new->user_ns, 0);
if (!issecure(SECURE_NOROOT)) { /* * If the legacy file capability is set, then don't set privs * for a setuid root binary run by a non-root user. Do set it * for a root user just to cause least surprise to an admin. */ if (has_cap && !uid_eq(new->uid, root_uid) && uid_eq(new->euid, root_uid)) { warn_setuid_and_fcaps_mixed(bprm->filename); goto skip; } /* * To support inheritance of root-permissions and suid-root * executables under compatibility mode, we override the * capability sets for the file. * * If only the real uid is 0, we do not set the effective bit. */ if (uid_eq(new->euid, root_uid) || uid_eq(new->uid, root_uid)) { /* pP' = (cap_bset & ~0) | (pI & ~0) */ new->cap_permitted = cap_combine(old->cap_bset, old->cap_inheritable); } if (uid_eq(new->euid, root_uid)) effective = true; } skip:
/* if we have fs caps, clear dangerous personality flags */ if (!cap_issubset(new->cap_permitted, old->cap_permitted)) bprm->per_clear |= PER_CLEAR_ON_SETID;
/* Don't let someone trace a set[ug]id/setpcap binary with the revised * credentials unless they have the appropriate permit. * * In addition, if NO_NEW_PRIVS, then ensure we get no new privs. */ is_setid = !uid_eq(new->euid, old->uid) || !gid_eq(new->egid, old->gid);
if ((is_setid || !cap_issubset(new->cap_permitted, old->cap_permitted)) && bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) { // 如果只修改euid和egid为0,保持uid以及suid为0x3,就不会进到这个分支 /* downgrade; they get no more than they had, and maybe less */ if (!capable(CAP_SETUID) || (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) { new->euid = new->uid; new->egid = new->gid; } new->cap_permitted = cap_intersect(new->cap_permitted, old->cap_permitted); }
/* File caps or setid cancels ambient. */ if (has_cap || is_setid) cap_clear(new->cap_ambient);
/* * Now that we've computed pA', update pP' to give: * pP' = (X & fP) | (pI & fI) | pA' */ new->cap_permitted = cap_combine(new->cap_permitted, new->cap_ambient);
/* * Set pE' = (fE ? pP' : pA'). Because pA' is zero if fE is set, * this is the same as pE' = (fE ? pP' : 0) | pA'. */ if (effective) new->cap_effective = new->cap_permitted; else new->cap_effective = new->cap_ambient;
if (WARN_ON(!cap_ambient_invariant_ok(new))) return -EPERM;
bprm->cap_effective = effective;
/* * Audit candidate if current->cap_effective is set * * We do not bother to audit if 3 things are true: * 1) cap_effective has all caps * 2) we are root * 3) root is supposed to have all caps (SECURE_NOROOT) * Since this is just a normal root execing a process. * * Number 1 above might fail if you don't have a full bset, but I think * that is interesting information to audit. */ if (!cap_issubset(new->cap_effective, new->cap_ambient)) { if (!cap_issubset(CAP_FULL_SET, new->cap_effective) || !uid_eq(new->euid, root_uid) || !uid_eq(new->uid, root_uid) || issecure(SECURE_NOROOT)) { ret = audit_log_bprm_fcaps(bprm, new, old); if (ret < 0) return ret; } }
/* Non-zero means that this shell is running in `privileged' mode. This is required if the shell is to run setuid. If the `-p' option is not supplied at startup, and the real and effective uids or gids differ, disable_priv_mode is called to relinquish setuid status. */ int privileged_mode = 0; ...... /* Fetch the current set of uids and gids and return 1 if we're running setuid or setgid. */ staticint uidget () { uid_t u;