Skip to content

Commit 708d953

Browse files
committed
Restrict htoprc symlink resolution with owner check
Restrict the possibility of a symlink attack on the htoprc file. If the htoprc file to be read is a symlink, only resolve the link if it's owned by the same EUID or root user (UID 0). Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
1 parent ae424de commit 708d953

1 file changed

Lines changed: 62 additions & 3 deletions

File tree

Settings.c

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,67 @@ int Settings_write(const Settings* this, bool onCrash) {
791791
return r;
792792
}
793793

794+
static void Settings_resolveSymlink(char** resolvedPath, const char* path) {
795+
int fd = -1;
796+
int openFlags = O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
797+
#ifdef O_EXEC
798+
openFlags |= O_EXEC;
799+
#else
800+
// O_EXEC is not supported in Linux.
801+
openFlags |= O_RDONLY;
802+
#endif
803+
#ifdef O_PATH
804+
// O_PATH is specific to Linux and FreeBSD.
805+
openFlags |= O_PATH;
806+
#endif
807+
do {
808+
fd = open(path, openFlags);
809+
} while (fd < 0 && errno == EINTR);
810+
811+
if (fd < 0)
812+
goto noPath;
813+
814+
struct stat sb;
815+
int err = fstat(fd, &sb);
816+
if (err)
817+
goto fileBroken;
818+
819+
if (!S_ISLNK(sb.st_mode) || (sb.st_uid != 0 && sb.st_uid != geteuid())) {
820+
// Not a symbolic link or the symbolic link is not trusted.
821+
// Return the path to the link itself. This allows the link
822+
// target to be opened read-only when desirable.
823+
close(fd);
824+
*resolvedPath = xStrdup(path);
825+
return;
826+
}
827+
828+
if (sb.st_size < 0 || sb.st_size >= PATH_MAX)
829+
goto fileBroken;
830+
831+
char buf[PATH_MAX];
832+
ssize_t len = readlinkat(fd, "", buf, PATH_MAX);
833+
close(fd);
834+
835+
if (len < 0 || len > sb.st_size)
836+
goto noPath;
837+
838+
buf[len] = '\0';
839+
char* intermediatePath = xStrdup(buf);
840+
char* ptr = realpath(intermediatePath, buf);
841+
free(intermediatePath);
842+
if (!ptr)
843+
goto noPath;
844+
845+
*resolvedPath = xStrdup(buf);
846+
return;
847+
848+
fileBroken:
849+
close(fd);
850+
noPath:
851+
*resolvedPath = xStrdup("");
852+
return;
853+
}
854+
794855
Settings* Settings_new(const Machine* host, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* dynamicScreens) {
795856
Settings* this = xCalloc(1, sizeof(Settings));
796857

@@ -868,9 +929,7 @@ Settings* Settings_new(const Machine* host, Hashtable* dynamicMeters, Hashtable*
868929
legacyDotfile = String_cat(home, "/.htoprc");
869930
}
870931

871-
this->filename = xMalloc(PATH_MAX);
872-
if (!realpath(this->initialFilename, this->filename))
873-
free_and_xStrdup(&this->filename, this->initialFilename);
932+
Settings_resolveSymlink(&this->filename, this->initialFilename);
874933

875934
this->colorScheme = 0;
876935
#ifdef HAVE_GETMOUSE

0 commit comments

Comments
 (0)