-
-
Notifications
You must be signed in to change notification settings - Fork 444
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
FlexMeter: Add FlexMeter functionality #1571
base: main
Are you sure you want to change the base?
Conversation
Please have a very close look at your implementation again as I noticed several trivial buffer overflows in the file iteration/handling code. Furthermore I'd like to point you to our styleguide which gives additional guidance on how the code should be set up. Also when integrating this meter, we have to take care of privilege escalations when running the specified commands. This is in particular true when running htop as root via sudo, when the home directory is still set to the logged-in user's HOME directory. In that situation a command in |
Just wondering, why was this meter called FlexMeter? Was it a random name you thought of? Also, I agree with @BenBE on the security issue here. The shell script to launch should have its owner same as the EUID or else htop should refuse to execute it. |
@BenBE I looking in codding stile which I might not follow strictly , and thanks for remark on overflow issue I am aware of it but totally forget to fix it @Explorer09 FlexMeter was chosen because you can select name of the Meter created from configuration file, I thought it was good. I was looking for easy and simple way to extend my htop with some stats, which would require development of bunch of specific meters for simple one line shell for example. Regarding PCPDynamicMeter - I was looking for something simpler. This was my idea. I was looking to report some custom stuff from my system like peripherals battery status or UPS work temperature.
I will fix security issue too as far it is possible. |
The buffer overflow was just one thing in the implementation. What initially tipped me off was the extensive use of static buffers all over the place. Overall,
I think naming-wise I'm fine with both: FlexMeter or DynamicMeter. Depending on how much infrastructure can be shared with the PCP implementation, calling it DynamicMeter might be an option; but that might be a source of confusion.
AFAICS the current implementation only implements text mode? Maybe we should limit it to that too; thinking re #1387 …
Both the buffer overflows (CWE-787, CWE-121, and CWE-122) and the privilege escalation (CWE-250, CWE-265, CWE-266,, CWE-269, CWE-270, and CWE-273,) are all security issues; the privilege escalation is just the more obvious architectural one, which needs some more thorough thoughts to mitigate. A good rule of thumb is to assume that every bug your code has will wipe your system. Now write your code like (if) you value your data … |
275830e
to
595b1ee
Compare
I have been working to make FlexMeter more compliant with all comments above. Used xStrdup in place of previous implementation. I followed If we agree lest stick with FlexMeter, since AFAIK it does not share functionality and implementation with PCP and might be confusing.
Yes it implements only text mode at the moment. I kept type in implementation since I thought it might be cool to implements and others. So far it look like it is not needed.
Most of this are addressed. I need to handle privileges for files located in |
FlexMeter.c
Outdated
#include <time.h> | ||
#include <sys/time.h> | ||
#include <dirent.h> | ||
#include "CRT.h" | ||
#include "Object.h" | ||
|
||
#include <unistd.h> | ||
#include <sys/types.h> | ||
#include <pwd.h> |
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.
Header order please.
FlexMeter.c
Outdated
} | ||
} | ||
|
||
if (!strncmp(line,"name=",5)) |
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.
Please use String_startsWith
when possible. It saves your time manually updating the string length.
By the way, if we are forbidding whitespace between name
and equal sign, better state that explicitly in the documentation.
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.
Please use
String_startsWith
when possible. It saves your time manually updating the string length.
Replacing it with String_startsWith is causing - Segmentation fault
.
By the way, if we are forbidding whitespace between
name
and equal sign, better state that explicitly in the documentation.
Is man page enough ?
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.
Is man page enough ?
As I have comments (or maybe "criticisms") about the current format of the template file, I think we can think of updating the man page later. Yes, the format is supposed to be documented, but we can think of documentation later, until the code is stable enough.
FlexMeter.c
Outdated
// path to home folder 1 for zero 1 for slash | ||
char * home = (char * ) xCalloc(1,(strlen(homedir) + strlen(FLEX_CFG_FOLDER) + 2)); | ||
|
||
sprintf(home,"%s/%s",homedir,FLEX_CFG_FOLDER); |
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 xSnprintf
. sprintf
is insecure.
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 forgot to mention this: htop complies with XDG Base Directory specification and that means we fetch user's config directory with the variable XDG_CONFIG_HOME
. Specifically the flex meter directories should go in $XDG_CONFIG_HOME/htop/FlexMeter
. Don't hard-code the .config
directory part and don't use $HOME/.config/htop/FlexMeter
. Look in the Settings.c file in htop source code to see how we fetch the XDG_CONFIG_HOME
variable and use the fallback default if it's unset.
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 will check that, thanks
FlexMeter.c
Outdated
|
||
static void FlexMeter_updateValues(Meter* this) | ||
{ | ||
for (int i =0 ; i < meters_count; i++) |
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
size_t
for iterator when possible. - Opening brace on the same line of
for
keyword, please. (Our style is closer to K&R rather than Allman here.)
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 note the placement of blanks/spaces according to the styleguide.
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 will start applying astyle -xb -s3 -p -xg -c -k1 -W1 -H FlexMeter.c
after every change this should be fine, right ?
Before I review more about the code style and quality of the meter, I have some general comments:
Honestly, I don't like the current design of the template file format. It mixes static data and executable code, and that's a bad idea. Also, it does not allow me to indicate the shell to use (i.e. it contains no shebang). I know you are using In other words, I wish the FlexMeter template files be shell scripts themselves, with appropriate shebang and execute permission (mode) bits. This would help the security in the long run. (The last thing I wish to see is a systemd-style, INI-like format for managing executable sub-processes. I was referring to this "comparison of init systems" page.)
AFAIK, the |
P.S. I have a rough logic of determining whether the user can or has intent to run the commands. But it needs execute permission on the template files themselves:
I'm not sure if it's worth it to implements a "drop privilege" mechanism or switch back to UID on this. But if there is a way to drop privilege, then the logic of checking whether the commands should run with the UID would be same as when checking with the EUID. |
@Explorer09 What do you think about handling group and user sticky bits on the files? How to handle symlinks? |
Note that my proposed logic only checks for execute permission of the meter (template) files themselves, and not on the executability of the commands. My imagined case is when the meter command was
Update: I've missed one thing about symlinks. We should only follow the symlink if it's created or owned by the user (here I mean EUID). It's unsafe to follow a link created by someone else. If a symlink is created by the user named "alice" but she runs htop as root, then the meter script should drop privileges and run as if EUID = Alice's UID. |
@Explorer09 Thanks for linking that old issue. @bogdanovs What do you think of the ideas/protocol mentioned in that issue #526? |
This issue is similar to my proposal but more complex to be used in first glance. In next iteration of FlexMeter could be implemented different than TEXT_MODE, but this will require scripting on background to provide more specific data. This is not bad but more demanding from user. FlexMeter is some how simple , not so tight to anything else. You can show whatever data you want. Issue described is more restrictive and might be harder for most average used to jump in directly. In the proposal is mentioned just 4 additional meters which is really limiting. Currently with this implementation I have between 6 an 8 all the time. If it is custom , let it be custom. I dont like part with open pipe all the time and somebody just feeding it. |
|
595b1ee
to
2a39429
Compare
2a39429
to
f56e58e
Compare
I didn't get this. Would you explain?
My vision is that this file would work both as a configuration and executable script. The file format would be a polyglot. When htop starts, it loads the whole file into memory (there's a good reason for this - if the file is changed in the middle we don't want the htop instance to reflect the change until it restarts), parsing the first few lines for the meter information. It will not execute the command. When the users adds the meter, htop runs the command and grab its standard output. This is the format of my vision: #!/bin/sh
#htop_meter_name=Meter name
#htop_meter_modes=text
#htop_meter_caption=Caption
# Commands follow...
uptime |
Permissions for .ssh folder and keys
That is ok like format, but still cant get the point of having |
Imagine you are root and you accidentally put a |
You mentioned that earlier, but this is always possibility if you are running like root. In case we use XDG_CONFIG_HOME, if run like root htop will look for meters in /root/.config/htop/FlexMeter. You think this is too open, I am confused? We could try to not allow execution or rm with simple regex while loading meters? |
XDG_CONFIG_HOME can point to any directory. And we cannot assume the directory is always safe.
Any program that can call unlink(2) can come with a risk of ruining your computer. Besides, there can be |
@Explorer09 all that you stated is true, I agree. But If we think in that direction even htop binary could be security risk. Every single program running on the system could be malicious and could harm your system. Every systemd service or other daemon or even Do you have an idea how this risk could be mitigated? I understand you concerns so we will not provide automatically generated sample on the system. |
The Unix practice is to avoid running things as root whenever possible. Unix gives so much power to the root user, and it assumes that root is competent and responsible. How responsible can a root account be for machines that aren't ours? Well, we can't tell. But what we can do is to avoid accidentally running things that a user (root or not) would not intend to run. And there is one feature designed for this purpose: The execute permission bit. That was why I suggest flex meter files should have I won't prevent a root user from running |
Now is clean what you mean with 700. Meter configuration file will not be executed and it is not meant to be executed like standalone script, just loaded from htop at start. Now we are on the same page, I will implement it that way. |
Correct. If the mode is 600, the user can still read and edit the file but would work as a sign that "this meter wasn't ready / it's a draft".
I could make it work like a standalone script and use watch(1) command to monitor the same thing. |
Yes it make sens to use But I still think |
Your popen(3) call will launch the command under the |
Correct Well it should not affect FlexMeter configuration if there is other shebang, I can generally ignore it while loading and care only for whatever keys words I am supporting. |
f56e58e
to
b9e5907
Compare
Summary on latest update :
|
I would choose to run the meter command only when the meter is added. Not any earlier. Think of when the command is I will have comments about the permission check code later. |
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.
Possibly split the unrelated changes into a separate commit.
Also, the types within FlexMeter.c
don't follow the usual naming convention?
Any reason for the hard limit on supported flex meters?
return 0; | ||
} | ||
|
||
static int load_config(char* file) |
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.
bool
is a thing …
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.
Some coding styles do use int
to return a status of an operation (e.g. Linux kernel). It might be simply that they don't understand htop uses bool
for operation statuses and I would personally tolerate that.
But, if there is no need to indicate statuses other than success and failure, then do stick with existing code and use bool
.
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 prefer int
tbh
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.
My preference is to avoid plain int
and long
as much as possible, especially if there is a proper type that carries the intended semantics (bool
, size_t
, uintptr_t
, …) or provides an indication of what range of numbers (uint32_t
) the author expected a given variable to carry.
struct passwd* pw = getpwuid(getuid()); | ||
const char* homedir = pw->pw_dir; |
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.
Note the comment by @Explorer09 regarding the XDG_HOME_DIR
variable and fallbacks …
@BenBE Is it mandatory to user |
If semantics are pure truth values: yes. If these functions provide full error reasons as their return value, If using |
FlexMeter provides functionality which will allow users to make custom meters without need of rebuilding every time htop binary and adding source to the project. It can be used to print some device status, free disk space CPU or other specific temeraturer, fan RPM and many more. Everything that can be fetched from linux shell with one line result can be printer. For fething information can be used anything from shell, python, precompiled binary or simply reading file located somewhere in file system. New meter will appear uppon restart of htop in list with available meters. Configuration folder location where metes should be placed: - /home/$USER/.config/htop/FlexMeter/ On start folder will be created if does not exist, together with template file .Template in same folder. Note: Files starting with '.' (.Template for examlpe) are ignored Meter Example: File name : Template name=<NAME SHOWN IN AvailableMeters> command=<COMMAND WHICH WILL BE EXECUTED> type=<METER TYPE FOR NO ONLY "TEXT_METER"> caption="CAPTION TEXT SHOWN IN THE BEGGINING OF THE METER" According to this implementation 30 Flex meter can be added Currently they have hardcoded limit of 30 meter in addition to all that already exist. Signed-off-by: Stoyan Bogdanov <[email protected]>
Signed-off-by: Stoyan Bogdanov <[email protected]>
b9e5907
to
12a91fd
Compare
FYI: Just pushed latest version which is without some comments which are already under some kind of discussion. |
#include <sys/time.h> | ||
#include <sys/types.h> | ||
#include <unistd.h> | ||
#include <dirent.h> | ||
#include <time.h> | ||
#include <pwd.h> | ||
#include "FlexMeter.h" | ||
#include "Object.h" | ||
#include "config.h" | ||
#include "CRT.h" |
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.
Header order; cf. styleguide …
meter_list[meters_count].name = xStrdup(dir->d_name); | ||
xAsprintf(&path, "%s/%s", home, dir->d_name); | ||
|
||
if (access(path, R_OK | W_OK | X_OK) == 0) { |
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 we need write access here? RX should be fine.
FlexMeter_class[i].name = (const char*) xStrdup(meter_list[i].name); | ||
FlexMeter_class[i].uiName = (const char*) xStrdup(meter_list[i].uiName); | ||
FlexMeter_class[i].caption = (const char*) xStrdup(meter_list[i].caption); |
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.
Given the unrestricted nature of how the uiName
and caption
are set, a FlexMeter could just configure to use the same strings as another (built-in) meter and thus impersonate another (e.g. built-in) meter. This is not a security issue directly, but more a point for discussion, if we want to allow the user to create FlexMeters that are indistinguishable from built-in meters.
return 0; | ||
} | ||
|
||
static int load_config(char* file) |
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.
My preference is to avoid plain int
and long
as much as possible, especially if there is a proper type that carries the intended semantics (bool
, size_t
, uintptr_t
, …) or provides an indication of what range of numbers (uint32_t
) the author expected a given variable to carry.
FlexMeter provides functionality which will allow
users to make custom meters without need of rebuilding every time htop binary and adding source to the project. It can be used to print some device status, free disk space CPU or other specific temeraturer, fan RPM and many more. Everything that can be fetched from linux shell
with one line result can be printer. For fething information can be used anything from shell, python, precompiled binary or simply reading file located somewhere in file system.
New meter will appear uppon restart of htop in list with available meters.
Configuration folder location where metes should be placed:
Note: Files starting with '.' (.Template for examlpe) are ignored
Meter Example:
File name : Template
name=
command=
type=<METER TYPE FOR NO ONLY "TEXT_METER">
caption="CAPTION TEXT SHOWN IN THE BEGGINING OF THE METER"
According to this implementation 30 Flex meter can be added Currently they have hardcoded limit of 30 meter in addition to all that already exist.
I am using this functionality for about an years maybe, so far had no issues. It might not be most optimal implementation by try to follow project stile while developed it. I am open for suggestion to improvements.