#include <set>
#include <algorithm>
+#include <memory>
#ifdef HAVE_ZLIB
#include <zlib.h>
if (From.IsOpen() == false || To.IsOpen() == false ||
From.Failed() == true || To.Failed() == true)
return false;
-
+
// Buffered copy between fds
- SPtrArray<unsigned char> Buf = new unsigned char[64000];
- unsigned long long Size = From.Size();
- while (Size != 0)
- {
- unsigned long long ToRead = Size;
- if (Size > 64000)
- ToRead = 64000;
-
- if (From.Read(Buf,ToRead) == false ||
- To.Write(Buf,ToRead) == false)
+ std::unique_ptr<unsigned char[]> Buf(new unsigned char[64000]);
+ constexpr unsigned long long BufSize = sizeof(Buf.get())/sizeof(Buf.get()[0]);
+ unsigned long long ToRead = 0;
+ do {
+ if (From.Read(Buf.get(),BufSize, &ToRead) == false ||
+ To.Write(Buf.get(),ToRead) == false)
return false;
-
- Size -= ToRead;
- }
+ } while (ToRead != 0);
- return true;
+ return true;
}
/*}}}*/
// GetLock - Gets a lock file /*{{{*/
char *p = realpath(File.c_str(), NULL);
if (p == NULL)
{
- _error->Errno("realpath", "flAbsPath failed");
+ _error->Errno("realpath", "flAbsPath on %s failed", File.c_str());
return "";
}
std::string AbsPath(p);
signal(SIGCONT,SIG_DFL);
signal(SIGTSTP,SIG_DFL);
- // Close all of our FDs - just in case
- for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++)
+ DIR *dir = opendir("/proc/self/fd");
+ if (dir != NULL)
{
- if(KeepFDs.find(K) == KeepFDs.end())
- fcntl(K,F_SETFD,FD_CLOEXEC);
+ struct dirent *ent;
+ while ((ent = readdir(dir)))
+ {
+ int fd = atoi(ent->d_name);
+ // If fd > 0, it was a fd number and not . or ..
+ if (fd >= 3 && KeepFDs.find(fd) == KeepFDs.end())
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ }
+ closedir(dir);
+ } else {
+ long ScOpenMax = sysconf(_SC_OPEN_MAX);
+ // Close all of our FDs - just in case
+ for (int K = 3; K != ScOpenMax; K++)
+ {
+ if(KeepFDs.find(K) == KeepFDs.end())
+ fcntl(K,F_SETFD,FD_CLOEXEC);
+ }
}
}
return true;
}
/*}}}*/
+// ChangeOwnerAndPermissionOfFile - set file attributes to requested values /*{{{*/
+bool ChangeOwnerAndPermissionOfFile(char const * const requester, char const * const file, char const * const user, char const * const group, mode_t const mode)
+{
+ if (strcmp(file, "/dev/null") == 0)
+ return true;
+ bool Res = true;
+ if (getuid() == 0 && strlen(user) != 0 && strlen(group) != 0) // if we aren't root, we can't chown, so don't try it
+ {
+ // ensure the file is owned by root and has good permissions
+ struct passwd const * const pw = getpwnam(user);
+ struct group const * const gr = getgrnam(group);
+ if (pw != NULL && gr != NULL && chown(file, pw->pw_uid, gr->gr_gid) != 0)
+ Res &= _error->WarningE(requester, "chown to %s:%s of file %s failed", user, group, file);
+ }
+ if (chmod(file, mode) != 0)
+ Res &= _error->WarningE(requester, "chmod 0%o of file %s failed", mode, file);
+ return Res;
+}
+ /*}}}*/
class FileFdPrivate { /*{{{*/
public:
~FileFdPrivate() { CloseDown(""); }
};
/*}}}*/
+// FileFd Constructors /*{{{*/
+FileFd::FileFd(std::string FileName,unsigned int const Mode,unsigned long AccessMode) : iFd(-1), Flags(0), d(NULL)
+{
+ Open(FileName,Mode, None, AccessMode);
+}
+FileFd::FileFd(std::string FileName,unsigned int const Mode, CompressMode Compress, unsigned long AccessMode) : iFd(-1), Flags(0), d(NULL)
+{
+ Open(FileName,Mode, Compress, AccessMode);
+}
+FileFd::FileFd() : iFd(-1), Flags(AutoClose), d(NULL) {}
+FileFd::FileFd(int const Fd, unsigned int const Mode, CompressMode Compress) : iFd(-1), Flags(0), d(NULL)
+{
+ OpenDescriptor(Fd, Mode, Compress);
+}
+FileFd::FileFd(int const Fd, bool const AutoClose) : iFd(-1), Flags(0), d(NULL)
+{
+ OpenDescriptor(Fd, ReadWrite, None, AutoClose);
+}
+ /*}}}*/
// FileFd::Open - Open a file /*{{{*/
// ---------------------------------------------------------------------
/* The most commonly used open mode combinations are given with Mode */
if ((Mode & ReadWrite) == 0)
return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
- if ((Mode & Atomic) == Atomic)
+ unsigned int OpenMode = Mode;
+ if (FileName == "/dev/null")
+ OpenMode = OpenMode & ~(Atomic | Exclusive | Create | Empty);
+
+ if ((OpenMode & Atomic) == Atomic)
{
Flags |= Replace;
}
- else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
+ else if ((OpenMode & (Exclusive | Create)) == (Exclusive | Create))
{
// for atomic, this will be done by rename in Close()
unlink(FileName.c_str());
}
- if ((Mode & Empty) == Empty)
+ if ((OpenMode & Empty) == Empty)
{
struct stat Buf;
if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
}
int fileflags = 0;
- #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
+ #define if_FLAGGED_SET(FLAG, MODE) if ((OpenMode & FLAG) == FLAG) fileflags |= MODE
if_FLAGGED_SET(ReadWrite, O_RDWR);
else if_FLAGGED_SET(ReadOnly, O_RDONLY);
else if_FLAGGED_SET(WriteOnly, O_WRONLY);
if_FLAGGED_SET(Exclusive, O_EXCL);
#undef if_FLAGGED_SET
- if ((Mode & Atomic) == Atomic)
+ if ((OpenMode & Atomic) == Atomic)
{
char *name = strdup((FileName + ".XXXXXX").c_str());
iFd = open(FileName.c_str(), fileflags, AccessMode);
this->FileName = FileName;
- if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
+ if (iFd == -1 || OpenInternDescriptor(OpenMode, compressor) == false)
{
if (iFd != -1)
{
{
if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
- if (d != NULL)
- {
- Res &= d->CloseDown(FileName);
- delete d;
- d = NULL;
- }
+ }
+
+ if (d != NULL)
+ {
+ Res &= d->CloseDown(FileName);
+ delete d;
+ d = NULL;
}
if ((Flags & Replace) == Replace) {
struct stat st;
if (!tmpdir || strlen(tmpdir) == 0 || // tmpdir is set
- stat(tmpdir, &st) != 0 || (st.st_mode & S_IFDIR) == 0 || // exists and is directory
- access(tmpdir, R_OK | W_OK | X_OK) != 0 // current user has rwx access to directory
- )
+ stat(tmpdir, &st) != 0 || (st.st_mode & S_IFDIR) == 0) // exists and is directory
+ tmpdir = "/tmp";
+ else if (geteuid() != 0 && // root can do everything anyway
+ faccessat(-1, tmpdir, R_OK | W_OK | X_OK, AT_EACCESS | AT_SYMLINK_NOFOLLOW) != 0) // current user has rwx access to directory
tmpdir = "/tmp";
return string(tmpdir);
+}
+std::string GetTempDir(std::string const &User)
+{
+ // no need/possibility to drop privs
+ if(getuid() != 0 || User.empty() || User == "root")
+ return GetTempDir();
+
+ struct passwd const * const pw = getpwnam(User.c_str());
+ if (pw == NULL)
+ return GetTempDir();
+
+ gid_t const old_euid = geteuid();
+ gid_t const old_egid = getegid();
+ if (setegid(pw->pw_gid) != 0)
+ _error->Errno("setegid", "setegid %u failed", pw->pw_gid);
+ if (seteuid(pw->pw_uid) != 0)
+ _error->Errno("seteuid", "seteuid %u failed", pw->pw_uid);
+
+ std::string const tmp = GetTempDir();
+
+ if (seteuid(old_euid) != 0)
+ _error->Errno("seteuid", "seteuid %u failed", old_euid);
+ if (setegid(old_egid) != 0)
+ _error->Errno("setegid", "setegid %u failed", old_egid);
+
+ return tmp;
}
/*}}}*/
-FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink) /*{{{*/
+FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink, FileFd * const TmpFd) /*{{{*/
{
char fn[512];
- FileFd *Fd = new FileFd();
+ FileFd * const Fd = TmpFd == NULL ? new FileFd() : TmpFd;
- std::string tempdir = GetTempDir();
- snprintf(fn, sizeof(fn), "%s/%s.XXXXXX",
+ std::string const tempdir = GetTempDir();
+ snprintf(fn, sizeof(fn), "%s/%s.XXXXXX",
tempdir.c_str(), Prefix.c_str());
- int fd = mkstemp(fn);
+ int const fd = mkstemp(fn);
if(ImmediateUnlink)
unlink(fn);
- if (fd < 0)
+ if (fd < 0)
{
_error->Errno("GetTempFile",_("Unable to mkstemp %s"), fn);
return NULL;
}
- if (!Fd->OpenDescriptor(fd, FileFd::WriteOnly, FileFd::None, true))
+ if (!Fd->OpenDescriptor(fd, FileFd::ReadWrite, FileFd::None, true))
{
_error->Errno("GetTempFile",_("Unable to write to %s"),fn);
return NULL;
}
-
return Fd;
}
/*}}}*/
{
close(Pipe[1]);
fd = Pipe[0];
- } else if(Mode == FileFd::WriteOnly)
+ }
+ else if(Mode == FileFd::WriteOnly)
{
close(Pipe[0]);
fd = Pipe[1];
}
+ else
+ return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
Fd.OpenDescriptor(fd, Mode, FileFd::None, true);
return true;
return _error->Error("No user %s, can not drop rights", toUser.c_str());
// Do not change the order here, it might break things
+ // Get rid of all our supplementary groups first
if (setgroups(1, &pw->pw_gid))
return _error->Errno("setgroups", "Failed to setgroups");
+ // Now change the group ids to the new user
+#ifdef HAVE_SETRESGID
+ if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0)
+ return _error->Errno("setresgid", "Failed to set new group ids");
+#else
if (setegid(pw->pw_gid) != 0)
return _error->Errno("setegid", "Failed to setegid");
if (setgid(pw->pw_gid) != 0)
return _error->Errno("setgid", "Failed to setgid");
+#endif
+ // Change the user ids to the new user
+#ifdef HAVE_SETRESUID
+ if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
+ return _error->Errno("setresuid", "Failed to set new user ids");
+#else
if (setuid(pw->pw_uid) != 0)
return _error->Errno("setuid", "Failed to setuid");
-
- // the seteuid() is probably uneeded (at least thats what the linux
- // man-page says about setuid(2)) but we cargo culted it anyway
if (seteuid(pw->pw_uid) != 0)
return _error->Errno("seteuid", "Failed to seteuid");
+#endif
// Verify that the user has only a single group, and the correct one
gid_t groups[1];