Re: [Caml-list] Re: Unix.getlogin () fails when stdin is redirected

61 views
Skip to first unread message

Yaron Minsky

unread,
Mar 20, 2012, 10:10:18 PM3/20/12
to Gerd Stolpmann, caml...@inria.fr, ocaml...@googlegroups.com
Looping in ocaml-core list.

On Tue, Mar 20, 2012 at 8:51 PM, Gerd Stolpmann <in...@gerd-stolpmann.de> wrote:

I second this. There is one OS where getlogin does not follow POSIX and is
maybe insecure, and the fix cannot be to hide the function for all other
OS. IMHO, these differences should be handled on a higher level, and not
in the module providing the bindings.

Semantically, there is a big difference between getlogin and getuid:
getlogin shall also work when the user calls a setuid program which in
turn invokes a script. These script commands can then use getlogin to
identify the original user (which is defined as the user of the session =
the user of the controlling terminal). In contrast, getuid would return
the uid to which setuid switched (for the script).

So, I'd say, you cannot repair getlogin with getuid. The best fix is
probably to just run `/usr/bin/logname </dev/tty` and read the printed
name.

I can think of a few solutions here:
  • We can use getuid only on platforms where getlogin is busted
  • We can name our function something other than "getlogin", to avoid confusion.
  • We can shell-out, in the way you suggest, to implement getlogin on Linux.  My only worry is that this is also going to be somewhat fragile in its own way.  Does calling out to logname with the suggested redirect always work?  
I'm open to other suggestions.

y

Yaron Minsky

unread,
Mar 20, 2012, 10:22:01 PM3/20/12
to Gerd Stolpmann, caml...@inria.fr, ocaml...@googlegroups.com
I'm going to drop caml-list for further discussion of this issue.  People who are interested should feel free to follow along on ocaml-core:


y

Yaron Minsky

unread,
Mar 20, 2012, 10:29:35 PM3/20/12
to ocaml...@googlegroups.com, Gerd Stolpmann
I have to say, the manpage on this is downright hilarious:

Unfortunately, it is often rather easy to fool getlogin(). Sometimes it does
not work at all, because some program messed up the utmp file. Often, it
gives only the first 8 characters of the login name. The user currently
logged in on the controlling tty of our program need not be the user who
started it. Avoid getlogin() for security-related purposes.

Note that glibc does not follow the POSIX specification and uses stdin instead
of /dev/tty. A bug. (Other recent systems, like SunOS 5.8 and HP-UX 11.11
and FreeBSD 4.8 all return the login name also when stdin is redirected.)

Nobody knows precisely what cuserid() does; avoid it in portable programs. Or
avoid it altogether: use getpwuid(geteuid()) instead, if that is what you
meant. Do not use cuserid().

So, on Linux getlogin returns mangled usernames sometimes, and should not be used for any "security" purposes.  getpwuid(getuid()) is sounding better and better...

y

On Tue, Mar 20, 2012 at 10:10 PM, Yaron Minsky <ymi...@janestreet.com> wrote:

Anil Madhavapeddy

unread,
Mar 21, 2012, 7:35:25 AM3/21/12
to ocaml...@googlegroups.com
Bear in mind that getpwuid(getuid()) can return unexpected results if you have multiple usernames mapped to the same numeric id (bad idea, but it does happen, for example with the toor account often used for an alternative root shell).

The only reliable, portable results are the numeric ids from getuid/geteuid.

-anil

Yaron Minsky

unread,
Mar 21, 2012, 7:47:30 AM3/21/12
to ocaml...@googlegroups.com

I'm not sure how bad I feel about having surprising results in that degenerate case!

Anil, do you have a view as to what Core should do for getlogin?  I'm still not sure we should change anything, or if we should, what change to make...

I am sure that I hate Unix, though.  That Xen client thing is sounding better every day...

Anil Madhavapeddy

unread,
Mar 21, 2012, 8:37:11 AM3/21/12
to ocaml...@googlegroups.com
I agree with Till; the least surprising thing is to pass through the underlying {g}libc library call.  Any higher-level replacement can just have a different name (for example, a version that goes through the password database and returns *all* logins associated with the {e}uid might be the safest thing).

Xen thing making good progress. Soon! :-)

-Anil

David House

unread,
Mar 21, 2012, 8:41:49 AM3/21/12
to ocaml...@googlegroups.com, Anil Madhavapeddy
Perhaps we should wrap Unix.getlogin with something with a mangled
type, like:

let getlogin () = `You_probably_mean_get_my_user_name
let getlogin_really () = Unix.getlogin ()
let get_my_user_name () = (Unix.getpwuid (getuid ())).Unix.pw_name

(Plus, obviously, helpful comments in the mli.)

This approach is pretty successful inside async. You are saved from
introducing bugs due to deficiencies in libc, but if you really want
getlogin(3), you have it. If you want it a lot, you can always rebind
it locally.

On Wed 21 Mar 2012 12:37:11 PM GMT, Anil Madhavapeddy wrote:
> I agree with Till; the least surprising thing is to pass through the
> underlying {g}libc library call. Any higher-level replacement can just
> have a different name (for example, a version that goes through the
> password database and returns *all* logins associated with the {e}uid
> might be the safest thing).
>
> Xen thing making good progress. Soon! :-)
>
> -Anil
>
> On 21 Mar 2012, at 11:47, Yaron Minsky wrote:
>
>> I'm not sure how bad I feel about having surprising results in that
>> degenerate case!
>>
>> Anil, do you have a view as to what Core should do for getlogin? I'm
>> still not sure we should change anything, or if we should, what
>> change to make...
>>
>> I am sure that I hate Unix, though. That Xen client thing is sounding
>> better every day...
>>
>> On Mar 21, 2012 7:35 AM, "Anil Madhavapeddy" <an...@recoil.org

>>> * We can use getuid only on platforms where getlogin is busted
>>> * We can name our function something other than


>>> "getlogin", to avoid confusion.

>>> * We can shell-out, in the way you suggest, to implement

Yaron Minsky

unread,
Mar 21, 2012, 10:10:37 AM3/21/12
to ocaml...@googlegroups.com, Anil Madhavapeddy
Maybe we could have more descriptive names for the other functions:

posix_getlogin
get_executing_login

The latter does better describe what the current getlogin does.

y

mb...@panix.com

unread,
Mar 22, 2012, 12:07:03 PM3/22/12
to ocaml...@googlegroups.com
House is on the right track.

I find the name getlogin unfortunate, as it sounds a lot more promising
than it really is. The best solution is probably more explicit names
and education.

(** A wrapper for the libc getlogin call. You should probably
use Sys.get_terminal_user instead.

Raises a Unix_error if something goes wrong.
*)
Unix.posix_getlogin : unit -> string

(** [get_terminal_user ()] attempts to determine the name of the user sitting
at the terminal. This relies on environment clues (such as Unix.posix_getlogin).

This function may fail for daemons and detached processes.

The name returned is not necessarily the user this process is executing as.
See get_effective_user for that.
*)
Sys.get_terminal_user : unit -> string option

(** [get_effective_user ()] looks up the name of the user this process can
access files as, send signals as, etc. The name corresponds to the UNIX
effective uid. It's not necessarily the name of the user sitting at
the terminal.
*)
Sys.get_effective_user : unit -> string

(*
Implementation:
let get_effective_user () = (Unix.getpwuid (Unix.geteuid ())).Unix.pw_name
*)

David House

unread,
Mar 22, 2012, 12:15:26 PM3/22/12
to ocaml...@googlegroups.com
This looks nice, but I'd say that the comment for
[Unix.posix_getlogin] should actually refer the user to
[Sys.get_effective_user] -- right?

mb...@panix.com

unread,
Mar 22, 2012, 2:54:22 PM3/22/12
to ocaml...@googlegroups.com
Maybe it should ask them to look at both, so they think harder about
what they need.

On Thu, Mar 22, 2012 at 04:15:26PM +0000, David House wrote:
> This looks nice, but I'd say that the comment for
> [Unix.posix_getlogin] should actually refer the user to
> [Sys.get_effective_user] -- right?
>
> On 22 March 2012 16:07, <mb...@panix.com> wrote:
> > House is on the right track.
> >
> > I find the name getlogin unfortunate, as it sounds a lot more promising

> > than it really is. ?The best solution is probably more explicit names
> > and education.
> >
> > (** A wrapper for the libc getlogin call. ?You should probably
> > ? ?use Sys.get_terminal_user instead.
> >
> > ? ?Raises a Unix_error if something goes wrong.
> > ?*)


> > Unix.posix_getlogin : unit -> string
> >
> > (** [get_terminal_user ()] attempts to determine the name of the user sitting

> > ? ?at the terminal. ?This relies on environment clues (such as Unix.posix_getlogin).
> >
> > ? ?This function may fail for daemons and detached processes.
> >
> > ? ?The name returned is not necessarily the user this process is executing as.
> > ? ?See get_effective_user for that.
> > ?*)
> > Sys.get_terminal_user ?: unit -> string option


> >
> > (** [get_effective_user ()] looks up the name of the user this process can

> > ? ?access files as, send signals as, etc. ?The name corresponds to the UNIX
> > ? ?effective uid. ?It's not necessarily the name of the user sitting at
> > ? ?the terminal.
> > ?*)


> > Sys.get_effective_user : unit -> string
> >
> > (*

> > ?Implementation:
> > ? ?let get_effective_user () = (Unix.getpwuid (Unix.geteuid ())).Unix.pw_name
> > *)

Yaron Minsky

unread,
Mar 22, 2012, 3:14:52 PM3/22/12
to ocaml...@googlegroups.com
That seems backwards.  In a setuid application, get_effective_user gives you the user that is being su'd to.  getlogin gives you the user who owns the tty.  Using getuid (rather than geteuid) seems like a closer approximation.

y
Reply all
Reply to author
Forward
0 new messages