Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things

Request for comments - Proc::UID

by pjf (Curate)
on Jun 07, 2004 at 08:48 UTC ( #361909=perlmeditation: print w/replies, xml ) Need Help??

Throughout this node when I will refer to Unix UIDs. However everything said also applies to Unix GIDs as well.


My current project is finding ways to improve Perl's handling of Unix privileges. Those of you on the p5p list will have recently seen some discussion from me in this regard.

Unix privileges are a difficult and often inconsistant area to work with. How privileges are handled and manipulated can change between operating systems, between releases of the same operating system, and upon the privileges of the user trying to manipulate privileges.

A good discussion on the tangle of Unix privileges can be found in Setuid Demystified by Chen, Wagner and Dean.

Current Issues

Perl's current handling of Unix privileges is presently incomplete for modern day Unix systems, and one of my goals is to make it complete. The most notable issues that currently exist are:

  • Perl has native concepts of real and effective UIDs, but no native concept of saved UIDs that exist on SysV systems and relations.
  • Not being able to manipulate saved UIDs makes it difficult or impossible for a process to fully drop privileges.
  • All UIDs, including saved UIDs, can be manipulated using syscall(), or breaking into XS, however the programmer must be careful to manually update Perl's internal PL_uid and PL_euid variables, otherwise $< and $> will cache incorrect information.
  • The POSIX::setuid() operates differently depending upon operating system, and differently depending upon current privileges. This is not a fault of Perl, but instead is due to the multiple ways in which vendors have interpreted the POSIX standard for setuid().
  • Any solution that does use POSIX::setuid(), or makes use of syscall or XS is likely to be non-portable between systems.

Proposed Solution - Proc::UID

In order to provide a consistant, portable, and easy-to-understand interface to the Unix UID tangle, I've started work on Proc::UID. This module is based upon the following design goals:

  • It should make available all three commonly found Unix UIDs -- namely the real, effective, and saved UIDs.
  • It should be consistant and easy to understand.
  • It should be difficult to make mistakes.

The the first goal (make available all Unix UIDs) involves coding the appropriate hooks for each Unix flavoured system. This work is not yet complete, but will occur as I arrange access to documentation and testing facilities for each operating system concerned.

The second goal (easy to understand) is achieved by providing a non-cached, variable based interface providing the variables $RUID, $EUID and $SUID, for real, effective, and saved UIDs respectively. Reading a variable retrieves the current UID value, and setting a variable attempts to change that UID (and only that UID) with the operating system.

The second goal is also served by presenting an equivilent functional interface with get[res]uid() and set[res]uid(). Again, these manipulate only a single UID at a time.

Finally, Proc::UID presents a preferred interface based upon the recommendations of Chen, Wagner and Dean. It provides three functions that allow for the most commonly executed logical UID manipulations:

Temporarily drops privileges to $new_uid. This has the effect of setting the effective UID to $new_uid, and the saved UID to the previous effective UID.
Permanently drops privileges to $new_uid by setting the real, effective, and saved UIDs to $new_uid.
Restore privileges previously dropped using drop_uid_temp(). This is done by setting the effective UID to the saved UID.

The third goal, making it difficult for mistakes to happen, is served by having all code that attempts to change privileges check that the change succeeded. Any operation that is intended to permanently drop privileges will also test to ensure they cannot be regained. All the logical operations test to ensure that the expected goal state is obtained.

Any failure to achieve the expected results described above will result in an exception being thrown. This makes it difficult for a careless program to continue to operate after it has failed to successfully manipulate its privileges, and potentially perform undesirable operations.

The Request

I would appreciate feedback on the Proc::UID module, which I have released on CPAN for testing and review. Questions, comments, notifications of glaring holes, better ways to do things, existing wheels, or any other feedback is appreciated.

Many thanks,

Replies are listed 'Best First'.
Re: Request for comments - Proc::UID
by andyf (Pilgrim) on Jun 07, 2004 at 14:55 UTC
    Paul, What a wonderful module to bring some order to all this mess. I am just wondering looking at the .xs, do you really want to croak on those failures? Update: Indeed I see your intention : " This makes it difficult for a careless program to continue to operate after it has failed to successfully manipulate its privileges, and potentially perform undesirable operations." - from a security POV very nice, from a robustness angle no no! Tricky call, since robustness and security are desirable.

      Thank-you for the encouraging feedback. It's very much appreciated.

      My view on exceptions versus return values is that they provide the same information, but with different default actions:

      • When communicating success or failure using a return value, the 'default action' is to ignore the failure.
      • When communicating success or failure using exceptions, the 'default action' is to abort the program.

      We commonly convert between return values and exceptions, with eval {} and or die "..." being the most commonly seen transformations.

      Given that any manipulation of privileges implies that work is being done in a security sensitive context, I feel that the only sensible course of action is to throw an exception should an operation fail. A program that wishes to handle such an event can easily catch the exception and do so.

      The croaks in the XS code definitely need to be standardised to more easily allow for exception handling to occur, and it's my intention to have this done and the exceptions well documented before the module is released in a more stable form.

      Many thanks again for the feedback,

Re: Request for comments - Proc::UID
by mojotoad (Monsignor) on Jun 07, 2004 at 16:23 UTC
    Splendid work and initiative, Paul.

    I have a feature request -- a variable that supports the notion of the damn real user id, or $DRUID.

    Just because? ;)

    Okay, I'm off to check out your new module -- I might be able to assist with the addition/testing of some OS variants.


Re: Request for comments - Proc::UID
by jarich (Curate) on Jun 08, 2004 at 09:29 UTC
    The second goal (easy to understand) is achieved by providing a non-cached, variable based interface providing the variables $RUID, $EUID and $SUID, for real, effective, and saved UIDs respectively.

    And from perldoc perlvar:

    $EFFECTIVE_USER_ID $EUID $> The effective uid of this process. $EFFECTIVE_GROUP_ID $EGID $) The effective gid of this process.

    Doesn't that mean that if I use English and use Proc::UID qw(:vars) then there might be issues as to which $E[UG]ID I'm using? (Although you could argue that I'd get what I deserved).

    How much would it matter? How do I ensure that these two modules work together nicely without having to rely on the order in which they're loaded? Is that the only way?

    You've mentioned that Perl may cache the values for $<, $>, $( and $) in some cases, do you care to go into that a little more?

    Looking good (typos in documentation provided separately)


      Doesn't that mean that if I use English and use Proc::UID qw(:vars) then there might be issues as to which $E[UG]ID I'm using? (Although you could argue that I'd get what I deserved).

      Yes, using both English and Proc::UID qw(:vars) will result in both trying to export $EUID and $EGID. You'll get the variables from the last module used.

      If you want to avoid this ambiguity, you can choose to refer to the Proc::UID variables by their full names: $Proc::UID::EUID and $Proc::UID::EGID. This will always work, even if you didn't use the qw(:vars) option of Proc::UID.

      The main difference between the Proc::UID versions and the English/native versions of the variables is that the Proc::UID is that the native variables can be return cached (incorrect) data, and also fail silently if you attempt to set them and fail. Proc::UID variables on the other hand will always check the current privileges, and will always throw an exception if you alter your privileges in a way the operating system does not allow.

      One final difference is that Proc::UID's group-id variables don't contain information from getgroups(), whereas $( and $) contain this information when evaluated as strings. This may change before the code is released as stable, but probably won't, as I feel it works againt the goal of 'simplicity'. If anything, a new interface will be made to allow access to getgroups() / setgroups(). It will almost certainly use arrays to return/set the supplimentary groups, as this is much more sensible than expecting the user to split, sort, mangle, and join supplimental groups each time.

      As for Perl's special variables caching information, Perl internally uses the interal variables PL_uid PL_euid PL_gid and PL_egid to record the program's current privileges. These are set on program start-up, and are refreshed after certain operations, such as assigning to $>, or (with more recent Perls) calling the POSIX::setuid function. They're not updated after a call to syscall(), and not updated in XS-land unless the code does so explicitly. As such if you're calling privilege-changing calls directly, it's possible for a program that's running with one set of privileges to appear to be running with a different set by Perl. Obviously that's a Bad Thing.

      Getting Perl's native special variables to be more forthcoming about checking the real privileges is another goal of mine, but can be done indepedently of my work on Proc::UID.

      All the very best,

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://361909]
Approved by Corion
Front-paged by jarich
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others imbibing at the Monastery: (5)
As of 2017-02-24 11:27 GMT
Find Nodes?
    Voting Booth?
    Before electricity was invented, what was the Electric Eel called?

    Results (354 votes). Check out past polls.