Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

An odd failure of setuid(0)

by Llew_Llaw_Gyffes (Scribe)
on Jun 25, 2006 at 23:58 UTC ( [id://557492]=perlquestion: print w/replies, xml ) Need Help??

Llew_Llaw_Gyffes has asked for the wisdom of the Perl Monks concerning the following question:

I have a tool which downloads photos from a digital camera which appears as a removable USB device.  It includes the following code:

my $u = $<; setuid(0) || die "Cannot change UID from $u to 0"; system(split(' ','/sbin/modprobe usb-storage')); setuid($u) || die "Cannot drop privileges"; system(split(' ','/sbin/mount /mnt/usb/flash')); [do some file manipulation in here] chdir ('/'); system(split(' ','/sbin/umount /mnt/usb/flash')); sleep (2); # this is pure raging paranoia setuid(0) || die "Cannot change UID from $u to 0"; system(split(' ','/sbin/modprobe -r usb-storage')); setuid($u) || die "Cannot drop privileges";

The tool is mode 6775 root:console.  Invariably, the first setuid(0) succeeds, as does the first setuid($u).  Also invariably, the second setuid(0) fails, as in the following example:

babylon5:alaric:~:17 $ getpics Will copy images from camera canon /mnt/usb/flash/dcim is mounted /mnt/usb/flash/dcim/104canon/img_0458.jpg -> /minbar/camera/canon/img_ +0458.jpg /mnt/usb/flash/dcim/104canon/img_0459.jpg -> /minbar/camera/canon/img_ +0459.jpg /mnt/usb/flash/dcim/104canon/img_0460.jpg -> /minbar/camera/canon/img_ +0460.jpg Cannot become root from UID 1000! at /usr/local/bin/getpics line 133. babylon5:alaric:~:18 $

Can anyone offer any explanation of why this might happen?

Replies are listed 'Best First'.
Re: An odd failure of setuid(0)
by betterworld (Curate) on Jun 26, 2006 at 00:23 UTC

    The answer is simple: Once you have dropped your privileges with setuid($u), you run as a normal user, and no normal user may setuid(0).

    You may change to root if your effective or saved uid is zero. However, setuid($u) sets both of them to $u. I believe you could use the seteuid system call instead of setuid to set only the effective uid. Unfortunately I cannot find a seteuid function in the POSIX module..

    Compare to the setuid(2) man page and the like.

      I believe you've nailed it.  I was overthinking the problem (see previous comment), then failing to consider that my first setuid($u) was negating my setuid/sgid bits that were allowing me to setuid(0) in the first place.

Re: An odd failure of setuid(0)
by shmem (Chancellor) on Jun 26, 2006 at 00:22 UTC
    Read on perlvar, variables $> and $<. You can drop privileges temporarily assigning to $> (effective uid of the process), but if you assign to $< (real uid of the process) and $> a uid higher than 0, you don't get back to 0.
    Setting $< or $> has no effect if neither real nor effective uid are 0.

    #!/usr/bin/perl print "effective: $>, real: $<\n"; $> = 111; open(O,">foo") or die "Can't write foo: $!\n"; close O or die "Can't close O: $!\n"; $> = 0; open(O,">bar") or die "Can't write bar: $!\n"; close O or die "Can't close O: $!\n"; $> = 111 ; # this must fail. open(O,">bar") or warn "Can't write bar: $!\n"; # change real uid $< = 111; # oops, forgot to set $> to 0 print "effective: $>, real: $<\n"; $< = 0; # no effect print "effective: $>, real: $<\n";

    This outputs:

    effective: 0, real: 0 Can't write bar: Permission denied effective: 111, real: 111 effective: 111, real: 111

    As you see, the second change of the real uid had no effect. Let's see what's in here:

    quux [gm] /tmp/foo # ls -l total 4 -rw-r--r-- 1 root root 0 2006-06-26 02:31 bar -rw-r--r-- 1 111 root 0 2006-06-26 02:31 foo -rw-r--r-- 1 root root 338 2006-06-26 02:29 setuid.pl

    Where do you get the function setuid from? can't find that in my perlfunc...

    update: ah, POSIX.

    setuid Sets the real user identifier and the effective user identi +- fier for this process. Similar to assigning a value to the Perl's builtin $< variable, see "$UID" in perlvar, except that the latter will change only the real user identifier.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

      Oh, sorry. setuid() is from POSIX.pm.  I omitted the script header, which includes "use POSIX qw(setuid);"

      I actually think maybe I shot myself in the foot here by trying to do setuid operations that were probably unnecessary since I was already running with setuid/setgid bits.  A case of overthinking the problem.

        Including all relevant parts of a script is always a good idea.. ;-)

        cheers,
        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: An odd failure of setuid(0)
by Tanktalus (Canon) on Jun 26, 2006 at 04:10 UTC

    I think you got the answer to your main question. So let me answer on how I did the same thing. And perhaps some ideas on how to get around your issues.

    First, I don't bother dropping the modules. In fact, I just put them in to always load during boot, and then forget about it. (Actually, since moving to Gentoo, I just compiled 'em right into the kernel so they aren't modules anymore.) Then I set /mnt/camera to be mountable by non-root by adding ",user" to the options string. In your case, that would be /mnt/usb/flash.

    Next option would be to have a load/unload script, say, /usr/local/bin/mount_flash, which just was:

    #!/bin/sh unmount=$1 if [ $unmount = "-d" ] then /sbin/umount /mnt/usb/flash sleep 2 /sbin/modprobe -r usb-storage else /sbin/modprobe usb-storage /sbin/mount /mnt/usb/flash fi
    Then add this to /etc/sudoers such that you can run it without a password:
    alaric NOPASSWD: /usr/local/bin/mount_flash, /usr/local/bin/mount_flas +h -d
    (I think), and now just run system(qw(sudo /usr/local/bin/mount_flash)) and system(qw(sudo /usr/local/bin/mount_flash -d)). Completely avoiding setuid().

      I initially did it this way myself.  I discovered, though, that what tended to happen was that the kernel didn't know when the flash card had been removed and reinserted in the reader, and so it assumed that the previous contents were still valid -- even across mounts.  The only way to ensure that directory listings of the flashcard contents were actually correct was to remove the module after unmounting, and reload it again before mounting.

      Of course, that was several years ago.  I haven't tested to see whether this problem still exists.  It would most certainly simplify life if it was fixed now.

Re: An odd failure of setuid(0)
by ambrus (Abbot) on Jun 26, 2006 at 10:33 UTC

    You might also want to read the relevant section from the info documentation of glibc, eg info "(libc)Enable/Disable Setuid"

      Thanks for the pointer.

      Now if I can just figure out how to get out of the never-to-be-sufficiently-accursed 'info' browser ........  I may be old-fashioned, but what was wrong with 'man'?

        When I read documentation and both info and man are available, I decide by content, not by the format. As I don't have to write either manpages (in troff format) or info pages (in texinfo format) I don't have to decide which one is better.

        Now if I can just figure out how to get out of the never-to-be-sufficiently-accursed 'info' browser

        I can understand if you don't want to learn using the info browser, it is indeed a bit difficult to use. Thus, let me give you some hints.

        If you want to learn using the info browser anyway, there's a short tutorial for it that tells only about the most important commands. You invoke this tutorial by pressing h in the info browser. (There're two ways to start an info browser, one is to run the command-line info program, the other is to use the info browser in emacs. These are two separate implementations but their interface is virtually the same.) Btw, you quit from the info browser by pressing q, or, if that types a letter q, hit control-G a few times and then q. If you started emacs, get out of it with control-X control-C.

        If you don't want to learn about the info browser, you don't have to. The info browser is really nothing but a fancy (and difficult to use) pager. The info files are really almost plain text, so you can read the info files with any pager, except that you may have to gunzip the info files and concatenate them if they're broken to smaller pieces. This can be done automatically by e.g. info -o ./libc.txt --subnodes libc (this method loses any text in the info file that's not part of any node, and the \x1F chars separating the nodes, but you don't need those anyway).

        If you don't like plain text, you can also compile the texinfo sources to pdf, html, or other formats. This is because the info files you see are usually not the source, but are automatically created from texinfo sources by the makeinfo programs. (Makeinfo can output info, plain text, html, xml, or docbook; while dvi, postscript, and pdf are created by processing the texinfo source with the tex program only using a special format file.) However, as normally only the info files are installed, you don't have the texinfo sources, so you have to get the source package of any program to make a pdf or html documentation, and you have to use some special make target. (This is quite similar to perl, which normally installs only man documentation, but you can compile the pods to html or other formats if you choose a special config option.)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://557492]
Approved by GrandFather
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (2)
As of 2024-04-26 02:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found