-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Drop root privileges before forking #333
Conversation
b4de0bf
to
3ca8f2b
Compare
With current changes you cannot call on any process, that needs sudo through lo2s. TL;DR I would like to add an option, that disables the sudo/doas check. Is this valid? |
I would do it the other way around, enabling this getuid functionality only if some option is given. This feels like something that might lead to weird-to-detect errors if lo2s would just do privilege dropping automatically Ideally, this option would also accept some username, so that you could specify that you want to switch to that specific user. You can get the UID/GID of some username by using getpwnam (https://linux.die.net/man/3/getpwnam) |
This way a user could forget to add the option. Question is which case is worse to run into: accidentally launching a process with root or potentially having some weird errors. And regarding the username-thing: I have a weird feeling about giving a user the ability to act as every other user. Nothing would stop them from switching to some daemon or a nologin user. |
33385d9
to
28f1c45
Compare
src/monitor/process_monitor_main.cpp
Outdated
else | ||
{ | ||
// sudo is garanteed in this case, see check in config.hpp | ||
original_uid = atoi(std::getenv("SUDO_UID")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use stoi() instead of atoi()
Print an error message if stoi can not parse SUDO_UID to a number
src/monitor/process_monitor_main.cpp
Outdated
auto orig_user = getpwnam(config().user.c_str()); | ||
if (orig_user == nullptr) | ||
{ | ||
Log::error() << "No user found with specified username."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
print which user failed here
man/lo2s.1.pod
Outdated
@@ -154,6 +154,14 @@ determine the trace path instead, including variable substitution. | |||
Attach to a running process with process ID I<PID> instead of launching | |||
I<COMMAND>. | |||
|
|||
=item B<-u>, B<--as-current-user> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally dont like "current" in the option and in the text wording, maybe --as-pre-sudo-user?
man/lo2s.1.pod
Outdated
|
||
=item B<-U>, B<--as-user> I<USERNAME> | ||
|
||
Launch I<COMMAND> as the specified user. Implies B<-u> but does not require sudo, but the current user has to have setuid() privileges. Will take priority over B<-u> if specified together. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
user I instead of "the specified user" . I think the part about "implies" in its current implementation confuses more than it helps
src/config.cpp
Outdated
.toggle("as-current-user", "Drop root privileges to current user before launching COMMAND.") | ||
.short_name("u"); | ||
|
||
general_options.option("as-user", "Specify a user to drop privileges to.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change wording in accordance with man page comment above.
src/config.cpp
Outdated
@@ -421,6 +431,18 @@ void parse_program_options(int argc, const char** argv) | |||
} | |||
} | |||
|
|||
if (arguments.given("as-current-user") && std::getenv("SUDO_UID") == NULL) | |||
{ | |||
Log::error() << "-u was specified but no sudo was used."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
used -> detected
man/lo2s.1.pod
Outdated
|
||
=item B<-U>, B<--as-user> I<USERNAME> | ||
|
||
Launch I<COMMAND> as the specified user. Implies B<-u> but does not require sudo, but the current user has to have setuid() privileges. Will take priority over B<-u> if specified together. | ||
Launch I<COMMAND> as I<USERNAME>. I<USERNAME> has to have setuid() privileges. Will take priority over B<-u> if specified together. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
USERNAME is not the user that needs to be able to do setuid. The user we are before we use setuid() to switch to USERNAME needs to be able to do setuid().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think "the caller" or "the calling user" is a good description for this user? Just wanted to ask before I commit again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"the caller needs to have CAP_SETUID" is the most correct way to write it me thinks
655fa5b
to
8722131
Compare
I have tested this now with our main use-case (which is running the IOR benchmark with mpirun, while lo2s runs as root) which seems to run fine, so we can check of testing |
@@ -185,6 +185,14 @@ void parse_program_options(int argc, const char** argv) | |||
.metavar("PID") | |||
.optional(); | |||
|
|||
general_options.toggle("as-pre-sudo-user", "Drop root privileges before launching COMMAND.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The internal config option is called drop_root
, the description is "Drop root privileges ...", but the thing is called --as-pre-sudo-user
. I think that is confusing. Don't differ between "internal" and "external" naming of features. I think --drop-root(-privileges)
would have been a better name.
general_options.toggle("as-pre-sudo-user", "Drop root privileges before launching COMMAND.") | ||
.short_name("u"); | ||
|
||
general_options.option("as-user", "User to drop privileges to before launching COMMAND.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is easier to understand:
"Launch the COMMAND as the user USERNAME"
@@ -421,6 +430,18 @@ void parse_program_options(int argc, const char** argv) | |||
} | |||
} | |||
|
|||
if (arguments.given("as-pre-sudo-user") && std::getenv("SUDO_UID") == NULL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the behavior I would expect.
While lo2s -a -A
works fine, lo2s -u -U $USER
complains about -u was specified but no sudo was detected.
Or in other words, -U
should imply -u
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, in C++, we don't use NULL
. Use nullptr
instead.
} | ||
else | ||
{ | ||
// sudo is garanteed in this case, see check in config.hpp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't only put your assumptions into comments. Put them in assert()
s.
} | ||
catch (std::invalid_argument const& e) | ||
{ | ||
Log::error() << "Cannot parse SUDO_UID/SUDO_GID into int."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be a user-facing message. Users don't know what an int
is or that SUDO_UID/SUDO_GID
are environment variables.
Cannot parse the environment variables SUDO_UID and/or SUDO_GID.
catch (std::invalid_argument const& e) | ||
{ | ||
Log::error() << "Cannot parse SUDO_UID/SUDO_GID into int."; | ||
throw_errno(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::stoi
raises an exception, but does not set errno
. throw_errno()
would need that.
Replace with throw;
, which rethrows the current exception.
Log::error() << "Cannot parse SUDO_UID/SUDO_GID into int."; | ||
throw_errno(); | ||
} | ||
catch (std::out_of_range const& e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the distinction between both exceptions types adds any value here. I'd rather have one "parsing failed" message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you remove the second catch statement and catch general exceptions or duplicate the error handling for both types?
I'd go for the second option, but redundant code is wack and a function would be overkill.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd go with one handler catch(const std::exception& e)
. As users typically do not set these environment variables themselves, I don't think any other message than "failed to parse ..." is necessary, but rather confusing.
catch (std::out_of_range const& e) | ||
{ | ||
Log::error() << "SUDO_UID/SUDO_GID out of range."; | ||
throw_errno(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
throw_errno()
doesn't work here, but remove it completely.
Edit setuid() options as requested in PR #333
Implemented a way for lo2s to detect a call with sudo and then drop its privileges before launching any process. Different methods of gaining root aren't taken into account (yet).
This refers to #301
To Do: