[Toybox] [patch] add su
Rob Landley
rob at landley.net
Wed Aug 14 19:55:17 PDT 2013
On 08/12/2013 11:20:40 PM, Strake wrote:
> =46rom 4693d474e5cdb95898bb0759eaac6a0d693ee33d Mon Sep 17 00:00:00
> 2001
> From: Strake <strake888 at gmail.com>
> Date: Mon, 12 Aug 2013 23:08:53 +0000
> Subject: [PATCH] add su
>
> ---
> toys/pending/su.c | 98
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 98 insertions(+)
> create mode 100644 toys/pending/su.c
>
> diff --git a/toys/pending/su.c b/toys/pending/su.c
> new file mode 100644
> index 0000000..7f0866d
> --- /dev/null
> +++ b/toys/pending/su.c
> @@ -0,0 +1,98 @@
> +/* su.c - switch user
> + *
> + * Copyright 2013 CE Strake <strake888 at gmail.com>
> + *
> + * See
> http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/su.html
> +
> +USE_SU(NEWTOY(su, "lmpc:s:", TOYFLAG_BIN))
> +
> +config SU
> + bool "su"
> + default n
> + help
> + usage: su [-lmp] [-c cmd] [-s shell] [user [argu...]]
I tend to have things like CMD SHELL USER be all caps to show it's not
a literal argument. I also tend to say ARGS instead of argu (there's a
couple existing instances in the tree, trying to be consistent).
I need to do a cleanup pass actually. df has [FILESYSTEM ...] when it
should probably have FILESYSTEM[...]. And of course chgrp has file...
chmod has FILE... cut has [FILE]... xzcat has [filename...] and so on.
(It'd be nice there was a standard on this. I'm sort of copying what
the man pages do but it's cargo cult documenting...)
Right, throw another todo item on the heap.
> + Switch to given user, or root if not given, and call a shell with
> the given arguments.
> +
> + options:
> + -s shell to call
> + -c command to pass to shell with -c
> +
> + flags:
> + -l login shell
> + -(m|p) preserve environment
> +*/
> +
> +#define FOR_su
> +#include "toys.h"
> +
> +GLOBALS(
> + char *sArgu;
> + char *cArgu;
> +)
I'm leaning more and more towards just having the argument variable be
the argument letter unless there's an immediately more obvious name.
TT.s is sort of its own namespace...
> +extern char **environ;
That should be in toys.h
> +static void deny () {
> + printf ("Denied\n");
> + xexit ();
> +}
> +
> +void su_main () {
> + char *name, *passhash, **argu, **argv;
> + struct passwd *up;
> + struct spwd *shp;
> + long ii;
> +
> + if (toys.optc && strcmp ("-", toys.optargs[0]) == 0) {
> + toys.optflags |= FLAG_l;
> + toys.optc--; toys.optargs++;
> + }
> +
> + if (toys.optc) {
> + name = toys.optargs[0];
> + toys.optc--; toys.optargs++;
> + }
This is a common enough idiom I'm starting to think there should be an
xwrap function for it. Hmmm...
> + else name = "root";
> + shp = getspnam (name);
> + if (!shp) perror_exit ("failed to find password");
See earlier discussion on minimal error messages aimed at non-english
speakers who have a hard enough time learning the posix command names
in a non-native alphabet. It's linked from
http://landley.net/toybox/cleanup.html which I _really_ need to catch
up on the writeups for...
The root user is uid 0, which could be _named_ anything. (Crazy people
say "wheel".) So we need a getwpuid(0) else getpwnam(name) and then do
the shadow lookup based on that name.
I'm curious what bionic/android does with this. First version I checked
user accounts weren't really implemented. I know they don't have an
/etc/passwd file. (Last time I implemented this stuff I parsed
/etc/passwd and /etc/shadow myself, but when I looked into android they
had some funky database stored in extended attributes or something; I
forget the details. Everybody keeps thinking the windows registry was
somehow a good idea...)
> + switch (shp -> sp_pwdp[0]) {
> + case '!': deny ();
> + case '$': break;
> + default : error_exit ("bad password format");
> + }
> +
> + if (read_password (toybuf, sizeof (toybuf), "Password: ") != 0)
> perror_exit ("failed to read password");
> +
> + passhash = crypt (toybuf, shp -> sp_pwdp);
> + if (!passhash) perror_exit ("failed to compute password hash");
> + for (ii = 0; toybuf[ii]; ii++) toybuf[ii] = 0;
Proper paranoia would probably swap those last two lines.
Hmmm, should the suid plumbing in main.c mlockall() when we don't drop
privs?
> + if (strcmp (passhash, shp -> sp_pwdp) != 0) deny ();
> +
> + up = getpwnam (name);
> + if (!up) perror_exit ("failed to getpwnam");
> +
> + if (setuid (up -> pw_uid) < 0) perror_exit ("failed to setuid");
> + if (chdir (up -> pw_dir) < 0) perror_exit ("failed to chdir");
xsetuid() and xchdir() are in lib/xwrap.c.
> + argu = xmalloc (sizeof (char *)*(toys.optc + 4));
> + argv = argu;
> + argv[0] = toys.optflags & FLAG_s ? TT.sArgu : up -> pw_shell;
Reading man page, "restricted shell", ala /etc/shell. Lovely. (If you
set a shell to /dev/null the user can't log in, _and_ can't run
anything via su either...)
I note that bash's behavior changes if argv[0] starts with "-".
> + if (toys.optflags & FLAG_c) {
> + argv[1] = toys.optflags & FLAG_l ? "-lc" : "-c";
> + argv[2] = TT.cArgu;
> + argv += 2;
> + }
> + else if (toys.optflags & FLAG_l) (argv++)[1] = "-l";
Is there a reason you can't do -l -c as two arguments and just test
once?
> + for (ii = 0; ii < toys.optc; ii++) argv[ii + 1] = toys.optargs[ii];
Probably memcpy.
> + if (execve (argu[0], argu,
> + toys.optflags & FLAG_l ? (char *[]){
> + xmsprintf ( "HOME=%s", up -> pw_dir),
> + xmsprintf ("SHELL=%s", up -> pw_shell),
> + xmsprintf ( "USER=%s", up -> pw_name),
> + xmsprintf ( "TERM=%s", getenv ("TERM")),
> + 0
> + } : environ) < 0) perror_exit ("failed to exec %s",
> argu[0]);
You never have to check the return value of execve(). I dunno why they
bother having one. If it returns, it failed.
I'll try to take a swipe at cleaning this up and moving it out of
pending.
Thanks,
Rob
More information about the Toybox
mailing list