aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coreutils/test.c91
-rw-r--r--include/libbb.h14
-rw-r--r--libbb/bb_getgroups.c34
-rw-r--r--libbb/hash_md5_sha.c2
-rw-r--r--networking/libiproute/iplink.c21
-rw-r--r--shell/ash.c56
6 files changed, 169 insertions, 49 deletions
diff --git a/coreutils/test.c b/coreutils/test.c
index 008d90b25..b63e33cc0 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -426,8 +426,7 @@ struct test_statics {
426 /* set only by check_operator(), either to bogus struct 426 /* set only by check_operator(), either to bogus struct
427 * or points to matching operator_t struct. Never NULL. */ 427 * or points to matching operator_t struct. Never NULL. */
428 const struct operator_t *last_operator; 428 const struct operator_t *last_operator;
429 gid_t *group_array; 429 struct cached_groupinfo *groupinfo;
430 int ngroups;
431#if BASH_TEST2 430#if BASH_TEST2
432 bool bash_test2; 431 bool bash_test2;
433#endif 432#endif
@@ -440,8 +439,7 @@ extern struct test_statics *BB_GLOBAL_CONST test_ptr_to_statics;
440#define S (*test_ptr_to_statics) 439#define S (*test_ptr_to_statics)
441#define args (S.args ) 440#define args (S.args )
442#define last_operator (S.last_operator) 441#define last_operator (S.last_operator)
443#define group_array (S.group_array ) 442#define groupinfo (S.groupinfo )
444#define ngroups (S.ngroups )
445#define bash_test2 (S.bash_test2 ) 443#define bash_test2 (S.bash_test2 )
446#define leaving (S.leaving ) 444#define leaving (S.leaving )
447 445
@@ -449,7 +447,6 @@ extern struct test_statics *BB_GLOBAL_CONST test_ptr_to_statics;
449 XZALLOC_CONST_PTR(&test_ptr_to_statics, sizeof(S)); \ 447 XZALLOC_CONST_PTR(&test_ptr_to_statics, sizeof(S)); \
450} while (0) 448} while (0)
451#define DEINIT_S() do { \ 449#define DEINIT_S() do { \
452 free(group_array); \
453 free(test_ptr_to_statics); \ 450 free(test_ptr_to_statics); \
454} while (0) 451} while (0)
455 452
@@ -637,62 +634,52 @@ static int binop(void)
637 /*return 1; - NOTREACHED */ 634 /*return 1; - NOTREACHED */
638} 635}
639 636
640static void initialize_group_array(void)
641{
642 group_array = bb_getgroups(&ngroups, NULL);
643}
644
645/* Return non-zero if GID is one that we have in our groups list. */ 637/* Return non-zero if GID is one that we have in our groups list. */
646//XXX: FIXME: duplicate of existing libbb function?
647// see toplevel TODO file:
648// possible code duplication ingroup() and is_a_group_member()
649static int is_a_group_member(gid_t gid) 638static int is_a_group_member(gid_t gid)
650{ 639{
651 int i;
652
653 /* Short-circuit if possible, maybe saving a call to getgroups(). */ 640 /* Short-circuit if possible, maybe saving a call to getgroups(). */
654 if (gid == getgid() || gid == getegid()) 641 if (gid == get_cached_egid(&groupinfo->egid))
655 return 1; 642 return 1;
656 643
657 if (ngroups == 0) 644 return is_in_supplementary_groups(groupinfo, gid);
658 initialize_group_array();
659
660 /* Search through the list looking for GID. */
661 for (i = 0; i < ngroups; i++)
662 if (gid == group_array[i])
663 return 1;
664
665 return 0;
666} 645}
667 646
668 647/*
669/* Do the same thing access(2) does, but use the effective uid and gid, 648 * Similar to what access(2) does, but uses the effective uid and gid.
670 and don't make the mistake of telling root that any file is 649 * Doesn't make the mistake of telling root that any file is executable.
671 executable. */ 650 * Returns non-zero if the file is accessible.
672static int test_eaccess(struct stat *st, int mode) 651 */
652static int test_st_mode(struct stat *st, int mode)
673{ 653{
674 unsigned int euid = geteuid(); 654 enum { ANY_IX = S_IXUSR | S_IXGRP | S_IXOTH };
655 unsigned euid;
656
657 if (mode == X_OK) {
658 /* Do we already know with no extra syscalls? */
659 //if (!S_ISREG(st->st_mode))
660 // return 0; /* not a regular file */
661 // ^^^ bash 5.2.15 "test -x" does not check this!
662 if ((st->st_mode & ANY_IX) == 0)
663 return 0; /* no one can execute */
664 if ((st->st_mode & ANY_IX) == ANY_IX)
665 return 1; /* anyone can execute */
666 }
675 667
668 euid = get_cached_euid(&groupinfo->euid);
676 if (euid == 0) { 669 if (euid == 0) {
677 /* Root can read or write any file. */ 670 /* Root can read or write any file. */
678 if (mode != X_OK) 671 if (mode != X_OK)
679 return 0; 672 return 1;
680 673
681 /* Root can execute any file that has any one of the execute 674 /* Root can execute any file that has any one of the execute
682 * bits set. */ 675 * bits set. */
683 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 676 mode = S_IXUSR | S_IXGRP | S_IXOTH;
684 return 0; 677 } else if (st->st_uid == euid) /* owner */
685 }
686
687 if (st->st_uid == euid) /* owner */
688 mode <<= 6; 678 mode <<= 6;
689 else if (is_a_group_member(st->st_gid)) 679 else if (is_a_group_member(st->st_gid))
690 mode <<= 3; 680 mode <<= 3;
691 681
692 if (st->st_mode & mode) 682 return st->st_mode & mode;
693 return 0;
694
695 return -1;
696} 683}
697 684
698 685
@@ -722,7 +709,7 @@ static int filstat(char *nm, enum token mode)
722 i = W_OK; 709 i = W_OK;
723 if (mode == FILEX) 710 if (mode == FILEX)
724 i = X_OK; 711 i = X_OK;
725 return test_eaccess(&s, i) == 0; 712 return test_st_mode(&s, i);
726 } 713 }
727 if (is_file_type(mode)) { 714 if (is_file_type(mode)) {
728 if (mode == FILREG) 715 if (mode == FILREG)
@@ -760,7 +747,7 @@ static int filstat(char *nm, enum token mode)
760 return ((s.st_mode & i) != 0); 747 return ((s.st_mode & i) != 0);
761 } 748 }
762 if (mode == FILGZ) 749 if (mode == FILGZ)
763 return s.st_size > 0L; 750 return s.st_size != 0L; /* shorter than "> 0" test */
764 if (mode == FILUID) 751 if (mode == FILUID)
765 return s.st_uid == geteuid(); 752 return s.st_uid == geteuid();
766 if (mode == FILGID) 753 if (mode == FILGID)
@@ -891,7 +878,7 @@ static number_t primary(enum token n)
891} 878}
892 879
893 880
894int test_main(int argc, char **argv) 881int FAST_FUNC test_main2(struct cached_groupinfo *pgroupinfo, int argc, char **argv)
895{ 882{
896 int res; 883 int res;
897 const char *arg0; 884 const char *arg0;
@@ -924,6 +911,7 @@ int test_main(int argc, char **argv)
924 911
925 /* We must do DEINIT_S() prior to returning */ 912 /* We must do DEINIT_S() prior to returning */
926 INIT_S(); 913 INIT_S();
914 groupinfo = pgroupinfo;
927 915
928#if BASH_TEST2 916#if BASH_TEST2
929 bash_test2 = bt2; 917 bash_test2 = bt2;
@@ -1026,3 +1014,18 @@ int test_main(int argc, char **argv)
1026 DEINIT_S(); 1014 DEINIT_S();
1027 return res; 1015 return res;
1028} 1016}
1017
1018int test_main(int argc, char **argv)
1019{
1020 struct cached_groupinfo info;
1021 int r;
1022
1023 info.euid = -1;
1024 info.egid = -1;
1025 info.ngroups = 0;
1026 info.supplementary_array = NULL;
1027 r = test_main2(&info, argc, argv);
1028 free(info.supplementary_array);
1029
1030 return r;
1031}
diff --git a/include/libbb.h b/include/libbb.h
index b68f9b684..ff4a67481 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1261,6 +1261,19 @@ void die_if_bad_username(const char* name) FAST_FUNC;
1261 * Dies on errors (on Linux, only xrealloc can cause this, not internal getgroups call). 1261 * Dies on errors (on Linux, only xrealloc can cause this, not internal getgroups call).
1262 */ 1262 */
1263gid_t *bb_getgroups(int *ngroups, gid_t *group_array) FAST_FUNC; 1263gid_t *bb_getgroups(int *ngroups, gid_t *group_array) FAST_FUNC;
1264/*
1265 * True if GID is in our getgroups() result.
1266 * getgroups() is cached in supplementary_array[], to make successive calls faster.
1267 */
1268struct cached_groupinfo {
1269 uid_t euid;
1270 gid_t egid;
1271 int ngroups;
1272 gid_t *supplementary_array;
1273};
1274uid_t FAST_FUNC get_cached_euid(uid_t *euid);
1275gid_t FAST_FUNC get_cached_egid(gid_t *egid);
1276int FAST_FUNC is_in_supplementary_groups(struct cached_groupinfo *groupinfo, gid_t gid);
1264 1277
1265#if ENABLE_FEATURE_UTMP 1278#if ENABLE_FEATURE_UTMP
1266void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); 1279void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname);
@@ -1612,6 +1625,7 @@ int test_main(int argc, char **argv)
1612 MAIN_EXTERNALLY_VISIBLE 1625 MAIN_EXTERNALLY_VISIBLE
1613#endif 1626#endif
1614; 1627;
1628int FAST_FUNC test_main2(struct cached_groupinfo *pgroupinfo, int argc, char **argv);
1615int kill_main(int argc, char **argv) 1629int kill_main(int argc, char **argv)
1616#if ENABLE_KILL || ENABLE_KILLALL || ENABLE_KILLALL5 1630#if ENABLE_KILL || ENABLE_KILLALL || ENABLE_KILLALL5
1617 MAIN_EXTERNALLY_VISIBLE 1631 MAIN_EXTERNALLY_VISIBLE
diff --git a/libbb/bb_getgroups.c b/libbb/bb_getgroups.c
index 5d83c729a..31cff2b41 100644
--- a/libbb/bb_getgroups.c
+++ b/libbb/bb_getgroups.c
@@ -45,3 +45,37 @@ gid_t* FAST_FUNC bb_getgroups(int *ngroups, gid_t *group_array)
45 *ngroups = n; 45 *ngroups = n;
46 return group_array; 46 return group_array;
47} 47}
48
49uid_t FAST_FUNC get_cached_euid(uid_t *euid)
50{
51 if (*euid == (uid_t)-1)
52 *euid = geteuid();
53 return *euid;
54}
55
56gid_t FAST_FUNC get_cached_egid(gid_t *egid)
57{
58 if (*egid == (gid_t)-1)
59 *egid = getegid();
60 return *egid;
61}
62
63/* Return non-zero if GID is in our supplementary group list. */
64int FAST_FUNC is_in_supplementary_groups(struct cached_groupinfo *groupinfo, gid_t gid)
65{
66 int i;
67 int ngroups;
68 gid_t *group_array;
69
70 if (groupinfo->ngroups == 0)
71 groupinfo->supplementary_array = bb_getgroups(&groupinfo->ngroups, NULL);
72 ngroups = groupinfo->ngroups;
73 group_array = groupinfo->supplementary_array;
74
75 /* Search through the list looking for GID. */
76 for (i = 0; i < ngroups; i++)
77 if (gid == group_array[i])
78 return 1;
79
80 return 0;
81}
diff --git a/libbb/hash_md5_sha.c b/libbb/hash_md5_sha.c
index 57a801459..75a61c32c 100644
--- a/libbb/hash_md5_sha.c
+++ b/libbb/hash_md5_sha.c
@@ -1313,7 +1313,9 @@ unsigned FAST_FUNC sha1_end(sha1_ctx_t *ctx, void *resbuf)
1313 hash_size = 8; 1313 hash_size = 8;
1314 if (ctx->process_block == sha1_process_block64 1314 if (ctx->process_block == sha1_process_block64
1315#if ENABLE_SHA1_HWACCEL 1315#if ENABLE_SHA1_HWACCEL
1316# if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
1316 || ctx->process_block == sha1_process_block64_shaNI 1317 || ctx->process_block == sha1_process_block64_shaNI
1318# endif
1317#endif 1319#endif
1318 ) { 1320 ) {
1319 hash_size = 5; 1321 hash_size = 5;
diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c
index 37ed114bc..67602a466 100644
--- a/networking/libiproute/iplink.c
+++ b/networking/libiproute/iplink.c
@@ -51,6 +51,27 @@ struct ifla_vlan_flags {
51}; 51};
52#endif 52#endif
53 53
54#if ENABLE_FEATURE_IP_LINK_CAN
55# ifndef CAN_CTRLMODE_ONE_SHOT
56# define CAN_CTRLMODE_ONE_SHOT 0x08 /* One-Shot mode */
57# endif
58# ifndef CAN_CTRLMODE_BERR_REPORTING
59# define CAN_CTRLMODE_BERR_REPORTING 0x10 /* Bus-error reporting */
60# endif
61# ifndef CAN_CTRLMODE_FD
62# define CAN_CTRLMODE_FD 0x20 /* CAN FD mode */
63# endif
64# ifndef CAN_CTRLMODE_PRESUME_ACK
65# define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */
66# endif
67# ifndef CAN_CTRLMODE_FD_NON_ISO
68# define CAN_CTRLMODE_FD_NON_ISO 0x80 /* CAN FD in non-ISO mode */
69# endif
70# ifndef IFLA_CAN_TERMINATION
71# define IFLA_CAN_TERMINATION 11
72# endif
73#endif
74
54/* taken from linux/sockios.h */ 75/* taken from linux/sockios.h */
55#define SIOCSIFNAME 0x8923 /* set interface name */ 76#define SIOCSIFNAME 0x8923 /* set interface name */
56 77
diff --git a/shell/ash.c b/shell/ash.c
index 679846574..b37446233 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -671,6 +671,10 @@ struct globals_misc {
671 char **trap_ptr; /* used only by "trap hack" */ 671 char **trap_ptr; /* used only by "trap hack" */
672 672
673 /* Rarely referenced stuff */ 673 /* Rarely referenced stuff */
674
675 /* Cached supplementary group array (for testing executable'ity of files) */
676 struct cached_groupinfo groupinfo;
677
674#if ENABLE_ASH_RANDOM_SUPPORT 678#if ENABLE_ASH_RANDOM_SUPPORT
675 random_t random_gen; 679 random_t random_gen;
676#endif 680#endif
@@ -714,6 +718,7 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
714#define may_have_traps (G_misc.may_have_traps ) 718#define may_have_traps (G_misc.may_have_traps )
715#define trap (G_misc.trap ) 719#define trap (G_misc.trap )
716#define trap_ptr (G_misc.trap_ptr ) 720#define trap_ptr (G_misc.trap_ptr )
721#define groupinfo (G_misc.groupinfo )
717#define random_gen (G_misc.random_gen ) 722#define random_gen (G_misc.random_gen )
718#define backgndpid (G_misc.backgndpid ) 723#define backgndpid (G_misc.backgndpid )
719 724
@@ -729,6 +734,8 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
729 curdir = nullstr; \ 734 curdir = nullstr; \
730 physdir = nullstr; \ 735 physdir = nullstr; \
731 trap_ptr = trap; \ 736 trap_ptr = trap; \
737 groupinfo.euid = -1; \
738 groupinfo.egid = -1; \
732} while (0) 739} while (0)
733 740
734 741
@@ -2579,7 +2586,7 @@ initvar(void)
2579#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT 2586#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
2580 vps1.var_text = "PS1=\\w \\$ "; 2587 vps1.var_text = "PS1=\\w \\$ ";
2581#else 2588#else
2582 if (!geteuid()) 2589 if (!get_cached_euid(&groupinfo.euid));
2583 vps1.var_text = "PS1=# "; 2590 vps1.var_text = "PS1=# ";
2584#endif 2591#endif
2585 vp = varinit; 2592 vp = varinit;
@@ -11342,7 +11349,7 @@ static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, a
11342static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); } 11349static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
11343#endif 11350#endif
11344#if ENABLE_ASH_TEST || BASH_TEST2 11351#if ENABLE_ASH_TEST || BASH_TEST2
11345static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); } 11352static int FAST_FUNC testcmd(int argc, char **argv) { return test_main2(&groupinfo, argc, argv); }
11346#endif 11353#endif
11347#if ENABLE_ASH_SLEEP 11354#if ENABLE_ASH_SLEEP
11348static int FAST_FUNC sleepcmd(int argc, char **argv) { return sleep_main(argc, argv); } 11355static int FAST_FUNC sleepcmd(int argc, char **argv) { return sleep_main(argc, argv); }
@@ -15101,6 +15108,42 @@ readcmdfile(char *name)
15101 15108
15102/* ============ find_command inplementation */ 15109/* ============ find_command inplementation */
15103 15110
15111static int test_exec(/*const char *fullname,*/ struct stat *statb)
15112{
15113 /*
15114 * TODO: use faccessat(AT_FDCWD, fullname, X_OK, AT_EACCESS)
15115 * instead: executability may depend on ACLs, capabilities
15116 * and who knows what else, not just mode bits.
15117 * (faccessat2 syscall was added to Linux in May 14 2020)
15118 */
15119 mode_t stmode;
15120 uid_t euid;
15121 enum { ANY_IX = S_IXUSR | S_IXGRP | S_IXOTH };
15122
15123 /* Do we already know with no extra syscalls? */
15124 if (!S_ISREG(statb->st_mode))
15125 return 0; /* not a regular file */
15126 if ((statb->st_mode & ANY_IX) == 0)
15127 return 0; /* no one can execute */
15128 if ((statb->st_mode & ANY_IX) == ANY_IX)
15129 return 1; /* anyone can execute */
15130
15131 /* Executability depends on our euid/egid/supplementary groups */
15132 stmode = S_IXOTH;
15133 euid = get_cached_euid(&groupinfo.euid);
15134 if (euid == 0)
15135 /* for root user, any X bit is good enough */
15136 stmode = ANY_IX;
15137 else if (statb->st_uid == euid)
15138 stmode = S_IXUSR;
15139 else if (statb->st_gid == get_cached_egid(&groupinfo.egid))
15140 stmode = S_IXGRP;
15141 else if (is_in_supplementary_groups(&groupinfo, statb->st_gid))
15142 stmode = S_IXGRP;
15143
15144 return statb->st_mode & stmode;
15145}
15146
15104/* 15147/*
15105 * Resolve a command name. If you change this routine, you may have to 15148 * Resolve a command name. If you change this routine, you may have to
15106 * change the shellexec routine as well. 15149 * change the shellexec routine as well.
@@ -15128,9 +15171,12 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
15128 if (errno == EINTR) 15171 if (errno == EINTR)
15129 continue; 15172 continue;
15130#endif 15173#endif
15174 absfail:
15131 entry->cmdtype = CMDUNKNOWN; 15175 entry->cmdtype = CMDUNKNOWN;
15132 return; 15176 return;
15133 } 15177 }
15178 if (!test_exec(/*name,*/ &statb))
15179 goto absfail;
15134 } 15180 }
15135 entry->cmdtype = CMDNORMAL; 15181 entry->cmdtype = CMDNORMAL;
15136 return; 15182 return;
@@ -15275,9 +15321,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
15275 e = errno; 15321 e = errno;
15276 goto loop; 15322 goto loop;
15277 } 15323 }
15278 e = EACCES; /* if we fail, this will be the error */
15279 if (!S_ISREG(statb.st_mode))
15280 continue;
15281 if (lpathopt) { /* this is a %func directory */ 15324 if (lpathopt) { /* this is a %func directory */
15282 stalloc(len); 15325 stalloc(len);
15283 /* NB: stalloc will return space pointed by fullname 15326 /* NB: stalloc will return space pointed by fullname
@@ -15290,6 +15333,9 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
15290 stunalloc(fullname); 15333 stunalloc(fullname);
15291 goto success; 15334 goto success;
15292 } 15335 }
15336 e = EACCES; /* if we fail, this will be the error */
15337 if (!test_exec(/*fullname,*/ &statb))
15338 continue;
15293 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); 15339 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
15294 if (!updatetbl) { 15340 if (!updatetbl) {
15295 entry->cmdtype = CMDNORMAL; 15341 entry->cmdtype = CMDNORMAL;