]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/contrib/fileutl.cc
apt-pkg/contrib/srvrec.cc: res_query() should not generate a _error->Warning()
[apt.git] / apt-pkg / contrib / fileutl.cc
index 1e1fb595720a98401fcb3b986cb51b3b0a55de82..1be782bac8c4629dd7fc81e1f276189db107d701 100644 (file)
@@ -52,6 +52,7 @@
 
 #include <set>
 #include <algorithm>
+#include <memory>
 
 #ifdef HAVE_ZLIB
        #include <zlib.h>
@@ -159,7 +160,7 @@ bool CopyFile(FileFd &From,FileFd &To)
       return false;
    
    // Buffered copy between fds
-   SPtrArray<unsigned char> Buf = new unsigned char[64000];
+   std::unique_ptr<unsigned char[]> Buf(new unsigned char[64000]);
    unsigned long long Size = From.Size();
    while (Size != 0)
    {
@@ -167,8 +168,8 @@ bool CopyFile(FileFd &From,FileFd &To)
       if (Size > 64000)
         ToRead = 64000;
       
-      if (From.Read(Buf,ToRead) == false || 
-         To.Write(Buf,ToRead) == false)
+      if (From.Read(Buf.get(),ToRead) == false ||
+         To.Write(Buf.get(),ToRead) == false)
         return false;
       
       Size -= ToRead;
@@ -800,11 +801,26 @@ pid_t ExecFork(std::set<int> KeepFDs)
       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);
+        }
       }
    }
    
@@ -857,11 +873,7 @@ bool ExecWait(pid_t Pid,const char *Name,bool Reap)
    return true;
 }
                                                                        /*}}}*/
-
-
-// StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned     /*{{{*/
-// ---------------------------------------------------------------------
-/* */
+// StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned    /*{{{*/
 bool StartsWithGPGClearTextSignature(string const &FileName)
 {
    static const char* SIGMSG = "-----BEGIN PGP SIGNED MESSAGE-----\n";
@@ -877,7 +889,26 @@ bool StartsWithGPGClearTextSignature(string const &FileName)
 
    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:
@@ -995,6 +1026,25 @@ class FileFdPrivate {                                                     /*{{{*/
        ~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 */
@@ -1525,7 +1575,7 @@ bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
            int err;
            char const * const errmsg = BZ2_bzerror(d->bz2, &err);
            if (err != BZ_IO_ERROR)
-              return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
+              return FileFdError("BZ2_bzread: %s %s (%d: %s)", FileName.c_str(), _("Read error"), err, errmsg);
         }
 #endif
 #ifdef HAVE_LZMA
@@ -2035,10 +2085,7 @@ APT_DEPRECATED gzFile FileFd::gzFd() {
 #endif
 }
 
-
-// Glob - wrapper around "glob()"                                      /*{{{*/
-// ---------------------------------------------------------------------
-/* */
+// Glob - wrapper around "glob()"                                      /*{{{*/
 std::vector<std::string> Glob(std::string const &pattern, int flags)
 {
    std::vector<std::string> result;
@@ -2064,8 +2111,7 @@ std::vector<std::string> Glob(std::string const &pattern, int flags)
    return result;
 }
                                                                        /*}}}*/
-
-std::string GetTempDir()
+std::string GetTempDir()                                               /*{{{*/
 {
    const char *tmpdir = getenv("TMPDIR");
 
@@ -2074,51 +2120,52 @@ std::string GetTempDir()
       tmpdir = P_tmpdir;
 #endif
 
-   // check that tmpdir is set and exists
    struct stat st;
-   if (!tmpdir || strlen(tmpdir) == 0 || stat(tmpdir, &st) != 0)
+   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
+      )
       tmpdir = "/tmp";
 
    return string(tmpdir);
 }
-
-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;
 }
-
-bool Rename(std::string From, std::string To)
+                                                                       /*}}}*/
+bool Rename(std::string From, std::string To)                          /*{{{*/
 {
    if (rename(From.c_str(),To.c_str()) != 0)
    {
       _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
                     From.c_str(),To.c_str());
       return false;
-   }   
+   }
    return true;
 }
-
-bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)
+                                                                       /*}}}*/
+bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)/*{{{*/
 {
    int fd;
    if (Mode != FileFd::ReadOnly && Mode != FileFd::WriteOnly)
@@ -2170,46 +2217,67 @@ bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)
 
    return true;
 }
-
-bool DropPrivileges()
+                                                                       /*}}}*/
+bool DropPrivileges()                                                  /*{{{*/
 {
+   if(_config->FindB("Debug::NoDropPrivs", false) == true)
+      return true;
+
+#if __gnu_linux__
+#if defined(PR_SET_NO_NEW_PRIVS) && ( PR_SET_NO_NEW_PRIVS != 38 )
+#error "PR_SET_NO_NEW_PRIVS is defined, but with a different value than expected!"
+#endif
+   // see prctl(2), needs linux3.5 at runtime - magic constant to avoid it at buildtime
+   int ret = prctl(38, 1, 0, 0, 0);
+   // ignore EINVAL - kernel is too old to understand the option
+   if(ret < 0 && errno != EINVAL)
+      _error->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret);
+#endif
+
+   // empty setting disables privilege dropping - this also ensures
+   // backward compatibility, see bug #764506
+   const std::string toUser = _config->Find("APT::Sandbox::User");
+   if (toUser.empty())
+      return true;
+
    // uid will be 0 in the end, but gid might be different anyway
-   uid_t old_uid = getuid();
-   gid_t old_gid = getgid();
+   uid_t const old_uid = getuid();
+   gid_t const old_gid = getgid();
 
    if (old_uid != 0)
       return true;
-   if(_config->FindB("Debug::NoDropPrivs", false) == true)
-      return true;
 
-   const std::string toUser = _config->Find("APT::Sandbox::User", "_apt");
    struct passwd *pw = getpwnam(toUser.c_str());
    if (pw == NULL)
       return _error->Error("No user %s, can not drop rights", toUser.c_str());
 
-#if __gnu_linux__
-   // see prctl(2), needs linux3.5
-   int ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0,0, 0);
-   if(ret < 0)
-      _error->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret);
-#endif
    // 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];
@@ -2259,3 +2327,4 @@ bool DropPrivileges()
 
    return true;
 }
+                                                                       /*}}}*/