]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
su: pass arguments after <user> to shell
authorcgoesche <cgoesc2@wgu.edu>
Sun, 2 Nov 2025 16:55:09 +0000 (11:55 -0500)
committercgoesche <cgoesc2@wgu.edu>
Mon, 3 Nov 2025 18:16:19 +0000 (13:16 -0500)
The su(1) manpage describes how the arguments after <user>
are passed to the invoked shell. However this is empirically
wrong, as option flags after <user> are interpreted by su(1)
and will eventually never be passed or yield an error that
terminates the program due to an unrecognized option flag.

To fix this we can change getopt(3)'s scanning mode with a '+'
prefixed to 'optstring', this will make it so that getopt(3)
stops processing argv elements on the first occurrence of a
non-option argument, e.g. '-' or '<user>'.

Additionally, if the argument that directly follows '-' is an
option flag, su(1) will assume that this argument and the ones
that follow, are to be passed to a shell invoked by the root user.

Addresses: https://github.com/util-linux/util-linux/pull/1809
Signed-off-by: cgoesche <cgoesc2@wgu.edu>
login-utils/su-common.c

index 38d476a1a29a569c10fc9ccd5505f43d763ecb8c..4d54eab31b5865e3380bb8f151d3f8084c5e8765 100644 (file)
@@ -1036,7 +1036,7 @@ int su_main(int argc, char **argv, int mode)
        su->conv.appdata_ptr = (void *) su;
 
        while ((optc =
-               getopt_long(argc, argv, "c:fg:G:lmpPTs:u:hVw:", longopts,
+               getopt_long(argc, argv, "+c:fg:G:lmpPTs:u:hVw:", longopts,
                            NULL)) != -1) {
 
                err_exclusive_options(optc, longopts, excl, excl_st);
@@ -1142,7 +1142,7 @@ int su_main(int argc, char **argv, int mode)
                }
                FALLTHROUGH;
        case SU_MODE:
-               if (optind < argc)
+               if (optind < argc && *argv[optind] != '-')
                        su->new_user = argv[optind++];
                break;
        }