diff --git a/src/ioslaves/file/kauth/filehelper.cpp b/src/ioslaves/file/kauth/filehelper.cpp --- a/src/ioslaves/file/kauth/filehelper.cpp +++ b/src/ioslaves/file/kauth/filehelper.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "filehelper.h" #include "fdsender.h" @@ -38,6 +40,49 @@ return false; } +static bool dropPrivileges(int action, const QByteArray &path, const QByteArray &newPath/*for rename action*/) +{ + //for chmod, chown, utime, drop privileges to that of owner of the target file/dir + //for other actions use owner of the parent directory. + QByteArray target = path; + if (action != CHMOD && action != CHOWN && action != UTIME) { + target = dirname(target.data()); + } + + struct stat buf = {0}; + if (stat(target.data(), &buf) == -1) { + return false; + } + + uid_t newuid = buf.st_uid; + gid_t newgid = buf.st_gid; + if (newuid == 0 || newgid == 0) { + return true; + } + + //rename requires write access in both old and new location so drop privileges only when owners + //are same otherwise continue with root privileges. + if (action == RENAME) { + struct stat buf_new = {0}; + const QByteArray newFilePath = dirname(QByteArray(newPath).data()); + stat(newFilePath.data(), &buf_new); + if (newuid != buf_new.st_uid || newgid != buf_new.st_gid) { + return true; + } + } + + //drop ancillary groups first because it requires root privileges. + if (setgroups(1, &newgid) == -1) { + return false; + } + //change real, effective, saved gid and uid. + if (setregid(newgid, newgid) == -1 || setreuid(newuid, newuid) == -1) { + return false; + } + + return true; +} + ActionReply FileHelper::exec(const QVariantMap &args) { ActionReply reply; @@ -49,89 +94,92 @@ // the path of an existing or a new file/dir upon which the method will operate const QByteArray path = arg1.toByteArray(); + // for rename + const QByteArray newPath = action == RENAME ? arg2.toByteArray() : QByteArray(); - switch(action) { - case CHMOD: { - int mode = arg2.toInt(); - if (chmod(path.data(), mode) == 0) { - return reply; - } - break; - } - case CHOWN: { - int uid = arg2.toInt(); - int gid = arg3.toInt(); - if (chown(path.data(), uid, gid) == 0) { - return reply; + if (dropPrivileges(action, path, newPath)) { + switch(action) { + case CHMOD: { + int mode = arg2.toInt(); + if (chmod(path.data(), mode) == 0) { + return reply; + } + break; } - break; - } - case DEL: { - if (unlink(path.data()) == 0) { - return reply; + case CHOWN: { + int uid = arg2.toInt(); + int gid = arg3.toInt(); + if (chown(path.data(), uid, gid) == 0) { + return reply; + } + break; } - break; - } - case MKDIR: { - if (mkdir(path.data(), 0777) == 0) { - return reply; + case DEL: { + if (unlink(path.data()) == 0) { + return reply; + } + break; } - break; - } - case OPEN: { - int oflags = arg2.toInt(); - int mode = arg3.toInt(); - int fd = open(path.data(), oflags, mode); - bool success = (fd != -1) && sendFileDescriptor(fd, arg4.toByteArray().constData()); - close(fd); - if (success) { - return reply; + case MKDIR: { + if (mkdir(path.data(), 0777) == 0) { + return reply; + } + break; } - break; - } - case OPENDIR: { - DIR *dp = opendir(path.data()); - bool success = false; - if (dp) { - int fd = dirfd(dp); - success = (fd != -1) && sendFileDescriptor(fd, arg4.toByteArray().constData()); - closedir(dp); + case OPEN: { + int oflags = arg2.toInt(); + int mode = arg3.toInt(); + int fd = open(path.data(), oflags, mode); + bool success = (fd != -1) && sendFileDescriptor(fd, arg4.toByteArray().constData()); + close(fd); if (success) { return reply; } + break; } - break; - } - case RENAME: { - const QByteArray newName = arg2.toByteArray(); - if (rename(path.data(), newName.data()) == 0) { - return reply; + case OPENDIR: { + DIR *dp = opendir(path.data()); + bool success = false; + if (dp) { + int fd = dirfd(dp); + success = (fd != -1) && sendFileDescriptor(fd, arg4.toByteArray().constData()); + closedir(dp); + if (success) { + return reply; + } + } + break; } - break; - } - case RMDIR: { - if (rmdir(path.data()) == 0) { - return reply; + case RENAME: { + if (rename(path.data(), newPath.data()) == 0) { + return reply; + } + break; } - break; - } - case SYMLINK: { - const QByteArray target = arg2.toByteArray(); - if (symlink(target.data(), path.data()) == 0) { - return reply; + case RMDIR: { + if (rmdir(path.data()) == 0) { + return reply; + } + break; } - break; - } - case UTIME: { - utimbuf ut; - ut.actime = arg2.toULongLong(); - ut.modtime = arg3.toULongLong(); - if (utime(path.data(), &ut) == 0) { - return reply; + case SYMLINK: { + const QByteArray target = arg2.toByteArray(); + if (symlink(target.data(), path.data()) == 0) { + return reply; + } + break; + } + case UTIME: { + utimbuf ut; + ut.actime = arg2.toULongLong(); + ut.modtime = arg3.toULongLong(); + if (utime(path.data(), &ut) == 0) { + return reply; + } + break; } - break; + }; } - }; reply.setError(errno ? errno : -1); return reply;