This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: os.setuid and os.setgid have unexpected influence on serial module
Type: behavior Stage:
Components: Extension Modules Versions: Python 2.6
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Tjeerd.Pinkert, ned.deily, r.david.murray
Priority: normal Keywords:

Created on 2010-10-05 20:48 by Tjeerd.Pinkert, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Messages (5)
msg118033 - (view) Author: Tjeerd Pinkert (Tjeerd.Pinkert) Date: 2010-10-05 20:48
If I use os.setgid and os.setuid to switch to an other user in some daemon code, I cannot open the serial port anymore. If I run the same code directly from the user I can open the serial port. Since the serial module is using the open() call to open the serial device I wonder if the mistake is in the serial module or in the os module.

see also:
https://sourceforge.net/tracker/?func=detail&aid=3081643&group_id=46487&atid=446302

Sample code showing the behaviour using the daemon module from:
http://hathawaymix.org/Software/Sketches/daemon.py
(and no it's not this module, also my own crappy code did the same thing and gives the same erroneous behaviour)

------------------------
#!/usr/bin/python

"""Test daemon"""

import daemon
import logging
import time
import serial
import os

class HelloDaemon(daemon.Daemon):
    default_conf = 'test.conf'
    section = 'test'

    def setup_user(self):
        print os.getuid(), os.getgid()
        ser=serial.Serial(0)
        print ser.portstr
        ser.close()

    def run(self):
        while True:
            logging.info('The daemon says hello')
            time.sleep(1)

if __name__ == '__main__':
    HelloDaemon().main()
-----------------------------------------

now make the config file:
--------------------------------
[test]
uid = 
gid = 
pidfile = ./hellodaemon.pid
logfile = ./hellodaemon.log
loglevel = info
----------------------

when I run it as my own user it works fine, e.g.:
tjp@machine$ ./test.py
1000 1000
/dev/ttyS0

it nicely opens the port.

if I fill in tjp for uid and gid in the configfile and run it as:
tjp@machine$ sudo ./test.py
1000 1000
Traceback (most recent call last):
  File "./test.py", line 26, in <module>
    HelloDaemon().main()
  File "/home/tjp/tmp/pydaemon/daemon.py", line 121, in main
    self.start()
  File "/home/tjp/tmp/pydaemon/daemon.py", line 196, in start
    self.setup_user()
  File "./test.py", line 17, in setup_user
    ser=serial.Serial(0)
  File "/usr/lib/python2.6/dist-packages/serial/serialutil.py", line 166, in __init__
    self.open()
  File "/usr/lib/python2.6/dist-packages/serial/serialposix.py", line 175, in open
    raise SerialException("could not open port %s: %s" % (self._port, msg))
serial.serialutil.SerialException: could not open port 0: [Errno 13] Permission denied: '/dev/ttyS0'



I hope someone with more experience can either help me out, or confirm if this should be regarded a bug, and then in which module, os or serial

Yours, Tjeerd
msg118045 - (view) Author: Ned Deily (ned.deily) * (Python committer) Date: 2010-10-06 01:42
While you did not specify what platform you are running this on, the issue here is almost certainly a misunderstanding of how permissions work.  On UNIX-y systems, access to device files is normally governed by permissions like any other file or directory.  On many systems, groups are set up to allow users to access devices without requiring root permission.  User "tjp" is very likely a member of a supplementary group that has group permission to the device in question.  When run under sudo, the program initially can access the device because of the superuser (root) permission.  After it is daemonizied, though, it no longer has root but it does not have the supplementary group permissions it would have had running normally; it only has uid 1000 and gid 1000.

The following example illustrates:

# in this example, /dev/mixer has a gid of 29 (group=audio)
# and the user (uid=501,gid=501) is a member of group 29
$ id -u
501
$ id -g
501
$ id -G
501 6 22 24 25 27 29 44 46 107 112 114 1001 40100 40200
$ ls -ln /dev/mixer
crw-rw---- 1 0 29 14, 0 Oct  5 14:06 /dev/mixer
$ python -c "open('/dev/mixer','r')"
$ sudo python -c "open('/dev/mixer','r')
$ sudo python -c "import os; os.setgid(501); os.setuid(501); open('/dev/mixer','r')"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
IOError: [Errno 13] Permission denied: '/dev/mixer'
$ python -c "import os; print(os.getgroups())"
[6, 22, 24, 25, 27, 29, 44, 46, 107, 112, 114, 501, 1001, 40100, 40200]
$ sudo python -c "import os; print(os.getgroups())"
[0]

If this does not explain the results you are seeing, please re-open with additional supporting information.
msg118047 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2010-10-06 02:37
If you do want to pursue this further note that "[your] own crappy code" is a better reproducer to post than something that depends on a third party module.
msg118057 - (view) Author: Tjeerd Pinkert (Tjeerd.Pinkert) Date: 2010-10-06 08:10
Indeed I use Linux, sorry for the inconvenience of not mentioning.

Thanks Ned, I think this is indeed the case. Using os.setgroups with a list of group ids (one for the file access, one for the serial port) before switching user with os.setgid, os.setuid solved the problem.
I think os.initgroups(username, gid) does just this, only is not yet available in my distro.

It could be a feature of os that the groups of the user are set on a os.setuid call? Or would this break compatibility with the standard unix library behaviour?

To David: Yes you are right, only it would have cost me quite a bit of time to strip the code, and I tried this code to see if the behaviour was consistent when using other daemon code. Next time I will post my own code.
msg118072 - (view) Author: Ned Deily (ned.deily) * (Python committer) Date: 2010-10-06 18:14
"It could be a feature of os that the groups of the user are set on a os.setuid call? Or would this break compatibility with the standard unix library behaviour?"

The POSIX system interface specification specifically prohibits that: "The setuid() function shall not affect the supplementary group list in any way."

http://www.opengroup.org/onlinepubs/009695399/functions/setuid.html
History
Date User Action Args
2022-04-11 14:57:07adminsetgithub: 54241
2010-10-06 18:15:25ned.deilysetmessages: + msg118072
2010-10-06 08:10:23Tjeerd.Pinkertsetmessages: + msg118057
2010-10-06 02:37:13r.david.murraysetnosy: + r.david.murray
messages: + msg118047
2010-10-06 01:42:04ned.deilysetstatus: open -> closed

nosy: + ned.deily
messages: + msg118045

resolution: not a bug
2010-10-05 20:50:10Tjeerd.Pinkertsettype: behavior
2010-10-05 20:48:03Tjeerd.Pinkertcreate