views:

155

answers:

5

I have a situation where I would like to elevate the permissions I have in a web environment so that I can access a serial device.

The specific case is where I have a web interface for configuring a modem that comes up on /dev/ttyUSB[0-9].

Zero or more modems will be plugged in by an end user. I am writing some software that is capable of discerning which is a USB Wireless Modem by reading /sys/devices and talking to the modem using some AT commands.

I would like to be able to open the device and do something like:

ser = serial.Serial(tty, baudrate=115200, timeout=10)
ser.write('AT+CGSN\r\n')
imei = ser.readline()

The problem is that pyserial does this: self.fd = os.open(self.portstr, os.O_RDWR|os.O_NOCTTY|os.O_NONBLOCK) to open the serial port, where portstr is /dev/ttyUSB0, but it does it as the nobody user, which is unprivileged.

Serial ports on this system are owned by root:uucp and are set as 0660 (i.e. rw-rw----).

What is the best way for a user such as nobody who should have as few permissions as possible to open a file in dev?

Ideas I will consider:

  • Doing things in a subprocess using sudo.
  • Changing permissions of the files in /dev/ (instructions on how to do this properly using udev are appreciated!)
  • Using another API or piece of software I have not considered.
A: 

The sudo idea could be possible. IIRC, you can set specific commands to be sudo-able, but without requiring a password.

The other option is to put nobody in a group that has access to the device you want, or to start Apache as the group that does have access.

If you're using fastcgi (or equiv), I think you can have it run scripts as the owning user (some shared hosts do this).

To change permissions of files in /dev, just chmod them.

Richard Levasseur
+2  A: 

In this case, I would write a daemon run by a system user that accepted socket connections and relayed them to the appropriate device, then use sockets within the web application to talk to the daemon. This also helps keep the web app from blocking when opening a device that isn't quite ready to deal with users, settle locks a little more sanely than you could with CGI, etc.

However, if you wish to give the application the ability to talk to the devices directly, give them the same permissions that null has.

Tim Post
This is probably the most workable idea suggested thus far.
Jerub
+1  A: 

"What is the best way for a user such as nobody who should have as few permissions as possible to open a file in dev?"

Actually, you're better off using mod_wsgi in daemon mode for your web application. The mod_wsgi user can be any username (and group) you provide.

You can run as a user with appropriately defined privileges.

See http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIDaemonProcess

S.Lott
Yuck, the idea is that the user remain unprivileged. If I wanted to run the web scripts as a different user I would. The idea is to give the user the least amount of capabilities possible.
Jerub
Correct. With the mod_wsgi usernames and groups, you give them the least privileges possible.
S.Lott
I already have a user with few permissions. I want to give them slightly more permission in a single context - not elevate its permissions for every python web script. I don't understand why you think that this is a solution to my problem?
Jerub
@Jerub: I think it's a solution because it allows you to associate a specific mod_wsgi daemon with a specific user name and that user's specific permissions. It sounds like you want a script to have a specific set of permissions. This is one way to achieve that.
S.Lott
+2  A: 

There's another way of doing this: Unix/Linux allows sending file descriptors via Unix sockets. There's even a Perl module for that: PassAccessRights.pm. Didn't find similar module for Python but it could be easily implemented.

Eugene Morozov
Interesting idea. I do have the code required to pass file descriptors over unix sockets, but if I were going this far, I would end up just doing the work in a daemon and communicating via a RPC mechanism.
Jerub
+1  A: 

Configure a udev rule to chgrp the new device to nobody, if it is acceptable that every access via the web interface be permitted the same access to the device. Here's what I put in my eee-bpw package in file /etc/udev/rules.d/99-bpw.rules.

# Sierra Wireless AirCard 880 U
BUS=="usb", KERNEL=="ttyUSB2*", ACTION=="add", \
   PRODUCT=="1199/6855/0", DEVNAME=="/dev/tts/USB2", \
   OWNER="root", GROUP="dialout", \
   SYMLINK+="bpw", RUN="/usr/sbin/bpw"

Substitute nobody for dialout. This particular rule assumes the device name to be /dev/ttyUSB2, but you can extend the rule considerably, see the udev documentation.