Open hanetzer opened 8 years ago
Hi there!
Yes, I've already thought about the possibility to run sidewinderd as a non-root user. However, giving non-root users access to /dev/uinput
and /dev/input/event*
sounded like a security issue to me. E.g. my default desktop user is tolga
and I'm adding udev rules, so I can access the needed files with it.
If for some reason a malicious game / software is beeing run by tolga
, it can access /dev/input/event*
to keylog events and use /dev/uinput
for sending malicious key events to the machine.
We could fix this e.g. by creating a dedicated user sidewinderd
, which is only beeing used by this tool and add needed udev rules. This is a legit approach, but it's also a bit more complex than our current solution . Adding it as an option is okay to me, but I don't think it's a better solution to use it as a default. But I'm open for any discussion and am curious to hear your opinion on this.
Either way, I will make sure that sidewinderd doesn't get in the way, if users want to setup udev rules and run sidewinderd as a non-root user.
About the daemon specific code: actually, it's there, but it's not beeing used in a systemd environment. Please note, that initializeDaemon()
is only called, if you run sidewinderd -d
. The service file doesn't include the -d
flag, therefore forking and all the daemon stuff is handled by systemd.
I've added initializeDaemon()
and -d
flag for non-systemd Linux distros and also for a possible Mac OS X / BSD port.
Thank you for your input!
Cheers, Tolga
Yeah, I just noticed that. So theoretically one could simply make a user unit and run it with that without any particular changes to the code itself, no?
Currently, in some parts of the code, seteuid(0)
is hard-coded. This is for gaining root access, in order to read / write /dev/uinput
and /dev/input/event*
.
In your theoretical scenario, seteuid(0)
would simply fail. However, I have added no checks for seteuid(0)
, so the program will simply go on and ignore the fail.
So, I'm not entirely sure about it, but my wild guess is, it probably would work out-of-the-box. I can't try it out myself, as I don't have access to my Linux machine atm.
true... in which case, why not just remove the seteuid(0)
code entirely? there are two options, either you're running as root (in which case all file perm concerns are off the table) or you are running as a non-root user and have configured udev/etc properly already. Set the default pidfile somewhere world-readable (say /tmp/sidewinderd.pid
) and all will be well in the world :)
It's common practice to only elevate privileges when needed, like opening a file with root permissions etc. The goal is to run the program with the least possible privileges.
Current design: you define a user
in /etc/sidewinderd.conf
. Program starts as root, immediately drops root privileges and calls seteuid(user)
. This approach is needed to be able to gain root privileges by calling seteuid(0)
- otherwise, like if the programs was launched as a non-root user, it will fail.
Of course, if you set user
to root
, sidewinderd will run as root all the time. But if you've changed it to a normal user, sidewinderd will run as that user and elevate privileges only when needed (like opening /dev/input/event*
, when recording a macro) and immediately drop it again, once finished doing privileged stuff.
But I agree, there is no need for seteuid(0)
in an environment, where sidewinderd has been launched as a non-root user. Sidewinderd should only call it, when needed. This needs to be addressed.
About the PID-file: http://stackoverflow.com/a/5174433. It seems to be common practice to put PID-files in /var/run
. You're free to configure an alternative location in /etc/sidewinderd.conf
, if the default doesn't work for you. Additionally, we could also set PIDFile=
in the systemd service file. This is for cases, where the PID-file hasn't been cleared for some odd reasons etc.
There is also the option to set the /etc/sidewinderd.conf
as the final fallback if say ~/.config/sidewinderd.conf
or whatnot isn't found.
Something like ~/.config/sidewinderd.conf
would be able to handle multi-person environments in a better way, where the keyboard is shared across multiple users, each with their own configuration. Good idea, I will add this as a milestone!
Brainstormed about this:
Similar to libvirtd, I want to implement system- and session-mode. system-mode is basically our current solution - running sidewinderd, when the system boots up and use a single configuration. This is okay for single-user systems, but may not be optimal for multi-user systems. However, for 0.5.0
, I'd like to stop requiring root permissions and use udev rules instead.
Let's talk about system-mode first: It was a bit difficult to think about a good solution - in terms of usability and security. But I think the following is the best solution so far: on most distros, there is a default group called input
, which allows reading /dev/input/event/*
files (root:input 660
). Using udev, we need to set /dev/uinput
and /dev/hidraw*
from root:root 600
to root:input 660
aswell. We will create a new user sidewinderd
and put it in input
group and run the daemon as that user. We also need to fix permissions in /home/<user>/.local/share/sidewinderd
, so we can record and play macros.
Advantage over other methods: we don't need to set 666
or similar, risky permissions on input devices, which can be an attack point of malicious code (like a browser / office plugin / script, which logs your keys etc.). Another advantage is, this code is systemd independent.
session-mode: this is a mode, where the daemon fires up as soon as the user logs in (as logged in user). This has some advantages: if your harddrive is encrypted, we don't need to hack around it anymore - the daemon gets started at the correct moment. Every user can have it's own configuration and set of macros. Also, we don't need to run the program as root. However, this solution also has it's downsides: we need to run sidewinderd as the logged in user and therefore grant access to input devices to the users. This opens up a security hole, where browser / office plugins / scripts, which are run as the logged in user, can access input devices. We can avoid this by starting sidewinderd as sidewinderd
user and don't grant the user direct access to input devices. I'm not sure, if this is possible. Further research needed.
For session-mode you could have a master and slave setup, where master is running as sidewinderd
and is the only one who has access to uinput, while slaves are spawned by master (or created via systemctl --user
) and can receive actions from the master and play the corresponding macros locally as their own user.
Greetings.
First off, let me thank you for inventing the wheel I was contemplating first, and a very good wheel it is.
With that out of the way, have you considered the possibility of allowing this to be ran as non-root via systemd, eg,
systemct --user start sidewinderd.service
orsystemctl --user enable sidewinderd.service
?This would require some fairly trivial changes to the code and the use of udev rules to allow reading and writing
/dev/uinput
and/dev/input/event*
nodes. Basically you'd remove all the daemon specific code and the seteuid type stuffs and treat it as a normal foreground process, letting systemd itself handle the daemon stuffs.