Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

os.getlogin() Error

473 views
Skip to first unread message

Wildman

unread,
May 4, 2017, 4:03:42 PM5/4/17
to
I wrote a Linux only GUI program using Tk that reports various system
information using a tabbed Notebook. I have tested the program on
Debian, SoldyX and MX-15 and the program runs perfectly.

I tried testing on Mint and Ubuntu and the program would crash. The
GUI would appear briefly and disappear. On Ubuntu a crash report was
created so I was able to figure out what was going on. It had the
traceback and showed that os.getlogin threw an error. This is from
the crash report:

PythonArgs: ['/opt/linfo-tk/linfo-tk.py']
Traceback:
Traceback (most recent call last):
File "/opt/linfo-tk/linfo-tk.py", line 1685, in <module>
app = Window(root)
File "/opt/linfo-tk/linfo-tk.py", line 1393, in __init__
txt = function()
File "/opt/linfo-tk/linfo-tk.py", line 316, in userinfo
user = os.getlogin()
OSError: [Errno 25] Inappropriate ioctl for device

The program installs using the Debian package system (.deb) and an
entry is created in the Applications Menu. The strange thing is
that the crash only occurs when the program is run from the menu.
If I open a terminal and run the program from there, the program
runs fine.

I found a little info on the web about this but it was not clear
whether it is a bug in Linux or a bug in the os module. I also
found a couple of work-arounds but neither of them will work for
my purposes.

user = pwd.getpwuid(os.getuid())[0]
user = getpass.getuser()

I will try to explain...
The program reports system information based on the user's name.
Things such as passwd, groups and shadow info. However, the
program must have elevated privileges to get the shadow info so
the program has the option to 'restart as root' so the shadow
information will be obtainable.

If the program is restarting as root, the work-arounds report
the user as 'root'. Then the system information for passwd,
groups and shadow will be reported for 'root' and not the
user that ran the program. The actual user name that ran
the program is needed for the program to report correct
information.

It seems that only os.getlogin() reports the true user name no
matter if the program is run as a normal user or restarted as
root.

Is there a way to get the actual user name or is there a fix
or a better work-around for the os.getlogin() function?

--
<Wildman> GNU/Linux user #557453
The cow died so I don't need your bull!

Chris Angelico

unread,
May 4, 2017, 5:46:48 PM5/4/17
to
On Fri, May 5, 2017 at 6:03 AM, Wildman via Python-list
<pytho...@python.org> wrote:
> I will try to explain...
> The program reports system information based on the user's name.
> Things such as passwd, groups and shadow info. However, the
> program must have elevated privileges to get the shadow info so
> the program has the option to 'restart as root' so the shadow
> information will be obtainable.
>
> If the program is restarting as root, the work-arounds report
> the user as 'root'. Then the system information for passwd,
> groups and shadow will be reported for 'root' and not the
> user that ran the program. The actual user name that ran
> the program is needed for the program to report correct
> information.
>
> It seems that only os.getlogin() reports the true user name no
> matter if the program is run as a normal user or restarted as
> root.
>
> Is there a way to get the actual user name or is there a fix
> or a better work-around for the os.getlogin() function?

That depends on exactly how the program "restarts as root". One very
common way to do this is to invoke itself through sudo(1). If that's
how you're flipping yourself to root, check os.environ["SUDO_USER"] or
os.environ["SUDO_UID"], both of which will be set by sudo before it
invokes the program.

ChrisA

Wildman

unread,
May 4, 2017, 6:19:18 PM5/4/17
to
I am using pkexec to restart so $SUDO_USER is not set. For some
reason sudo, su and su-to-root will freeze the first instance of
the program and not let it close until the second instance closes.
I have tried every method I can find to launch them and pkexec
is the only one that works correctly.

Thanks for the reply.

Chris Angelico

unread,
May 4, 2017, 6:31:30 PM5/4/17
to
Ah, okay. I wonder if you can do the same thing? Just before you
invoke pkexec, create an environment variable with the current user
credentials. Then pkexec back to yourself, and look in the
environment.

ChrisA

Cameron Simpson

unread,
May 4, 2017, 7:01:26 PM5/4/17
to
On 04May2017 15:03, Wildman <best...@yahoo.com> wrote:
>I tried testing on Mint and Ubuntu and the program would crash. The
>GUI would appear briefly and disappear. On Ubuntu a crash report was
>created so I was able to figure out what was going on. It had the
>traceback and showed that os.getlogin threw an error. This is from
>the crash report:
>
>PythonArgs: ['/opt/linfo-tk/linfo-tk.py']
>Traceback:
> Traceback (most recent call last):
> File "/opt/linfo-tk/linfo-tk.py", line 1685, in <module>
> app = Window(root)
> File "/opt/linfo-tk/linfo-tk.py", line 1393, in __init__
> txt = function()
> File "/opt/linfo-tk/linfo-tk.py", line 316, in userinfo
> user = os.getlogin()
> OSError: [Errno 25] Inappropriate ioctl for device

This usually means that the program does not have a controlling terminal.

>The program installs using the Debian package system (.deb) and an
>entry is created in the Applications Menu. The strange thing is
>that the crash only occurs when the program is run from the menu.
>If I open a terminal and run the program from there, the program
>runs fine.

And this supports that.

getlogin is not magic, and can be overused. The Python docs say "Return the
name of the user logged in on the controlling terminal of the process." Clearly
that will fail.

When you start from a terminal, your command will have that as its controlling
terminal unless it has gone out of its way not to. When you start from a menu,
usually that menu system will not be associated with a terminal. In this case
you need to fall back on other methods of figuring out "who is logged in".

>I found a little info on the web about this but it was not clear
>whether it is a bug in Linux or a bug in the os module. I also
>found a couple of work-arounds but neither of them will work for
>my purposes.
>
> user = pwd.getpwuid(os.getuid())[0]
> user = getpass.getuser()
>
>I will try to explain...
>The program reports system information based on the user's name.
>Things such as passwd, groups and shadow info. However, the
>program must have elevated privileges to get the shadow info so
>the program has the option to 'restart as root' so the shadow
>information will be obtainable.
>
>If the program is restarting as root, the work-arounds report
>the user as 'root'.

The "shadow" information is normally concealed for good reason. I appreciate
that you're trying to only report that information for the current user, but
you should consider whether you should report it at all.

Personally I would be reluctant to write a program that "restarts as root"; it
feels like a tool where a bug would lead easily to privilege escalation - that
the calling user (not root, and from root's point of view untrustworthy) might
get it to perform extra tasks beyond your plan AS ROOT. Risky.

>Is there a way to get the actual user name or is there a fix
>or a better work-around for the os.getlogin() function?

The standard way is not to "restart as root", but to write a setuid program. It
still has all the pitfalls of any program running as root (the change for bugs
to let the calling user do something they should not be allowed to do), but you
can examine the value from os.getuid(): it will be the _calling_ user, while
os.geteuid() will be the effective user (root in your case). Then the
workaround will be effective.

You should also _minimise_ the time and work your program does as root. Along
the lines of:

... program invoked setuid ...
look up os.getuid() to find the uid of the invoker
read as little as possible of the privileged info (i.e. shadow) as required
os.setuid() BACK TO THE ORIGINAL USER SO YOU ARE NO LONGER ROOT
... do everything else ...

Part of your problem is that "who is the currently logged in user" is a
nebulous idea. Supposing you were to address the lack of controlling terminal
by seeing who is logged into the console. That is a little trusting. Supposing
_you_ are logged into the console, running X11. And while so, _I_ ssh into your
machine and run your program without a controlling terminal. Then your program
will _mistakenly_ presume the logged in user is _you_ (because, after all,
you're logged in), and report _your_ information to _me_.

For all that setuid programs have their own security issues, at least they
_know_ who they were invoked by from os.getuid(), without playing insecure
guessing games around "who is logged in". Because the latter is not equivalent
to "whose information should I access?"

I hope this points a way forward.

Personally I would usually resist accessing information not available as the
user, and avoid the need to run as root at all.

Cheers,
Cameron Simpson <c...@zip.com.au>

Wildman

unread,
May 4, 2017, 7:51:03 PM5/4/17
to
I'm afraid that won't work. The user environment is different
than root. A different set of variables. However you have
given me a possible workaround. You can't create a variable
for root unless you are root so that approach is out. But
it might be possible to create the variable for the user
and access it as root. I don't have a lot of experience
using os.environ, but I am going to at it closer.

Thanks.

Chris Angelico

unread,
May 4, 2017, 7:58:21 PM5/4/17
to
On Fri, May 5, 2017 at 9:50 AM, Wildman via Python-list
<pytho...@python.org> wrote:
> I'm afraid that won't work. The user environment is different
> than root. A different set of variables. However you have
> given me a possible workaround. You can't create a variable
> for root unless you are root so that approach is out. But
> it might be possible to create the variable for the user
> and access it as root. I don't have a lot of experience
> using os.environ, but I am going to at it closer.

When you start a subprocess, it inherits your environment. So you can
create an environment variable for yourself, then start the other
process.

But read Cameron's cautionary notes and basically just don't do this.

ChrisA

Wildman

unread,
May 4, 2017, 9:42:39 PM5/4/17
to
On Fri, 05 May 2017 09:00:58 +1000, Cameron Simpson wrote:

> On 04May2017 15:03, Wildman <best...@yahoo.com> wrote:
>
>>The program installs using the Debian package system (.deb) and an
>>entry is created in the Applications Menu. The strange thing is
>>that the crash only occurs when the program is run from the menu.
>>If I open a terminal and run the program from there, the program
>>runs fine.
>
> And this supports that.
>
> getlogin is not magic, and can be overused. The Python docs say "Return the
> name of the user logged in on the controlling terminal of the process." Clearly
> that will fail.
>
> When you start from a terminal, your command will have that as its controlling
> terminal unless it has gone out of its way not to. When you start from a menu,
> usually that menu system will not be associated with a terminal. In this case
> you need to fall back on other methods of figuring out "who is logged in".

What I don't understand is why the program will run from the menu
on some Linux distros and not others. I might need to take a
closer look at the structure of the .desktop file used to launch
my program. Thanks.

> You should also _minimise_ the time and work your program does as root. Along
> the lines of:
>
> ... program invoked setuid ...
> look up os.getuid() to find the uid of the invoker
> read as little as possible of the privileged info (i.e. shadow) as required
> os.setuid() BACK TO THE ORIGINAL USER SO YOU ARE NO LONGER ROOT
> ... do everything else ...

This is interesting. Will do some experimenting.

> Part of your problem is that "who is the currently logged in user" is a
> nebulous idea. Supposing you were to address the lack of controlling terminal
> by seeing who is logged into the console. That is a little trusting. Supposing
> _you_ are logged into the console, running X11. And while so, _I_ ssh into your
> machine and run your program without a controlling terminal. Then your program
> will _mistakenly_ presume the logged in user is _you_ (because, after all,
> you're logged in), and report _your_ information to _me_.
>
> For all that setuid programs have their own security issues, at least they
> _know_ who they were invoked by from os.getuid(), without playing insecure
> guessing games around "who is logged in". Because the latter is not equivalent
> to "whose information should I access?"
>
> I hope this points a way forward.
>
> Personally I would usually resist accessing information not available as the
> user, and avoid the need to run as root at all.
>
> Cheers,
> Cameron Simpson <c...@zip.com.au>

I appreciate the advice and will consider it.

Wildman

unread,
May 4, 2017, 10:05:18 PM5/4/17
to
On Fri, 05 May 2017 09:58:02 +1000, Chris Angelico wrote:

> On Fri, May 5, 2017 at 9:50 AM, Wildman via Python-list
> <pytho...@python.org> wrote:
>> I'm afraid that won't work. The user environment is different
>> than root. A different set of variables. However you have
>> given me a possible workaround. You can't create a variable
>> for root unless you are root so that approach is out. But
>> it might be possible to create the variable for the user
>> and access it as root. I don't have a lot of experience
>> using os.environ, but I am going to at it closer.
>
> When you start a subprocess, it inherits your environment. So you can
> create an environment variable for yourself, then start the other
> process.

I solved this problem by passing the user name to the second
instance as a command line argument. Works perfectly. It
escapes me why I didn't think of this sooner. I solved the
os.login problem by not using os.login, since I no longer
need it. :-)

> But read Cameron's cautionary notes and basically just don't do this.
>
> ChrisA

It has been my experience that Linux users tend to be a little
more conscious of security matters and know what the implications
are when doing anything as root. I expect the user will know the
risks if they choose to restart the program as root. I believe
in user choice.

Thanks again for your replies.

Chris Angelico

unread,
May 4, 2017, 10:21:51 PM5/4/17
to
On Fri, May 5, 2017 at 12:05 PM, Wildman via Python-list
<pytho...@python.org> wrote:
> On Fri, 05 May 2017 09:58:02 +1000, Chris Angelico wrote:
>
>> On Fri, May 5, 2017 at 9:50 AM, Wildman via Python-list
>> <pytho...@python.org> wrote:
>>> I'm afraid that won't work. The user environment is different
>>> than root. A different set of variables. However you have
>>> given me a possible workaround. You can't create a variable
>>> for root unless you are root so that approach is out. But
>>> it might be possible to create the variable for the user
>>> and access it as root. I don't have a lot of experience
>>> using os.environ, but I am going to at it closer.
>>
>> When you start a subprocess, it inherits your environment. So you can
>> create an environment variable for yourself, then start the other
>> process.
>
> I solved this problem by passing the user name to the second
> instance as a command line argument. Works perfectly. It
> escapes me why I didn't think of this sooner. I solved the
> os.login problem by not using os.login, since I no longer
> need it. :-)

That works too!

ChrisA

Cameron Simpson

unread,
May 4, 2017, 11:37:25 PM5/4/17
to
On 04May2017 20:42, Wildman <best...@yahoo.com> wrote:
>On Fri, 05 May 2017 09:00:58 +1000, Cameron Simpson wrote:
>> On 04May2017 15:03, Wildman <best...@yahoo.com> wrote:
>>>The program installs using the Debian package system (.deb) and an
>>>entry is created in the Applications Menu. The strange thing is
>>>that the crash only occurs when the program is run from the menu.
>>>If I open a terminal and run the program from there, the program
>>>runs fine.
>>
>> And this supports that.
>>
>> getlogin is not magic, and can be overused. The Python docs say "Return the
>> name of the user logged in on the controlling terminal of the process." Clearly
>> that will fail.
>>
>> When you start from a terminal, your command will have that as its controlling
>> terminal unless it has gone out of its way not to. When you start from a menu,
>> usually that menu system will not be associated with a terminal. In this case
>> you need to fall back on other methods of figuring out "who is logged in".
>
>What I don't understand is why the program will run from the menu
>on some Linux distros and not others. I might need to take a
>closer look at the structure of the .desktop file used to launch
>my program. Thanks.

I presume it will come down (in this instance) to the controlling tty. Put some
testing code into something you can invoke from a menu. Try to open '/dev/tty'
and see what error results. See if stdin, stdout and stderr are attached to
terminals (or even open). You can os.fstat(sys.stdin.fileno()) and so forth.

And direct stderr to a logfile so you get to see any stack traces; you can
write other info there, or dup stdout to the same logfile.

You can "tail -f" the logfile from a terminal to see your test results easily.

It may also depend on how X11 is started. On some distros you may have a GUI
login, and your X11 session may have no controlling terminal.

On my systems I run text consoles and use "startx" to kick things off. I'd
expect my window manager etc to have a controlling tty, the console. Unless
something lets go of it deliberately before kicking off my desktop.

So you're right, different distros and setups may well present somewhat
different inherited environments to whatever presents your menus.

Cheers,
Cameron Simpson <c...@zip.com.au>

Cameron Simpson

unread,
May 5, 2017, 4:34:13 AM5/5/17
to
On 04May2017 21:05, Wildman <best...@yahoo.com> wrote:
>On Fri, 05 May 2017 09:58:02 +1000, Chris Angelico wrote:
>> On Fri, May 5, 2017 at 9:50 AM, Wildman via Python-list
>> <pytho...@python.org> wrote:
>>> I'm afraid that won't work. The user environment is different
>>> than root. A different set of variables. However you have
>>> given me a possible workaround. You can't create a variable
>>> for root unless you are root so that approach is out. But
>>> it might be possible to create the variable for the user
>>> and access it as root. I don't have a lot of experience
>>> using os.environ, but I am going to at it closer.
>>
>> When you start a subprocess, it inherits your environment. So you can
>> create an environment variable for yourself, then start the other
>> process.

If you go through sudo the environment gets very scubbed for security reasons.
You can turn that off in the config, but it is not a great idea.

>I solved this problem by passing the user name to the second
>instance as a command line argument. Works perfectly. It
>escapes me why I didn't think of this sooner. I solved the
>os.login problem by not using os.login, since I no longer
>need it. :-)

I actually thought about suggesting this and refrained: how do you
avoid user B invoking your program with user A's login name as an
argument? Reliably? Without a way to check that the command line argument is
not a lie, this is an avenue to breaking your security. And to check it you're
back into "who invoked me?" territory. And further, if you _can_ check it, do
you need the argument at all?

So I didn't think it was useful.

>> But read Cameron's cautionary notes and basically just don't do this.
>
>It has been my experience that Linux users tend to be a little
>more conscious of security matters and know what the implications
>are when doing anything as root. I expect the user will know the
>risks if they choose to restart the program as root. I believe
>in user choice.

User choice is great provided the user _is_ actually informed and properly
understands the risks. Ship secure, let the user turn things off carefully. If
people truly want to shoot themselves in the foot nothing will really prevent
them. But don't give them a loaded gun with the safety catch off. Ideally,
don't give them a gun at all. Yeah, poor choice of metaphor, apologies in
advance.

To make this more pointed, you're making this thing but still learning about
the security implications, and what will and will not work. A third party using
your program will know even less (for example, they won't inherently know what
your program does and therefore whether _they_ should trust it in _their_
environment).

We have a nice "Securiing UNIX systems" book on a shelf here. It has chapters
for Solaris, various Linuxes, FreeBSD, OpenBSD. Pleasingly the OpenBSD one
roughly starts: "install OpenBSD; you are now secure; turn more things on as
needed carefully". Versus the other chapters which say: "install, then do all
this stuff". And OpenBSD users are generally far more security conscious than
Linux users, or they wouldn't have gone to OpenBSD.

I'm not saying abandon your project. I'm wanting you to think very carefully
about your security decisions, particularly with things that automatically run
as root. In particular, don't just think about how to get the behaviour you
want; think about how to _break_ it - get root as the wrong person, or get root
to give you information you should not have. Etc.

Cheers,
Cameron Simpson <c...@zip.com.au>

20/20 Lab

unread,
May 5, 2017, 2:37:46 PM5/5/17
to
I'm not sure if this will help you, but I found some stuff on accident
looking at something related.

Not sure if it will help, but looked promising

https://github.com/parmentelat/apssh/issues/1

==Some snippets from the page

From the os.getlogin() docs: "Returns the user logged in to the
controlling terminal of the process." Your script does not have a
controlling terminal when run from cron. The docs go on to suggest: "For
most purposes, it is more useful to use the environment variable LOGNAME
to find out who the user is, or pwd.getpwuid(os.getuid())[0] to get the
login name of the currently effective user id."


I suggest you to replace os.getlogin with:

import pwd
import os

getlogin = lambda: pwd.getpwuid(os.getuid())[0]
default_username = getlogin()

==

Wildman

unread,
May 6, 2017, 12:35:17 PM5/6/17
to
On Fri, 05 May 2017 11:30:41 -0700, 20/20 Lab wrote:

> I'm not sure if this will help you, but I found some stuff on accident
> looking at something related.
>
> Not sure if it will help, but looked promising
>
> https://github.com/parmentelat/apssh/issues/1
>
> ==Some snippets from the page
>
> From the os.getlogin() docs: "Returns the user logged in to the
> controlling terminal of the process." Your script does not have a
> controlling terminal when run from cron. The docs go on to suggest: "For
> most purposes, it is more useful to use the environment variable LOGNAME
> to find out who the user is, or pwd.getpwuid(os.getuid())[0] to get the
> login name of the currently effective user id."
>
>
> I suggest you to replace os.getlogin with:
>
> import pwd
> import os
>
> getlogin = lambda: pwd.getpwuid(os.getuid())[0]
> default_username = getlogin()

I appreciate the reply but the problem has been fixed.
0 new messages