signal(SIGCONT,SIG_DFL);
signal(SIGTSTP,SIG_DFL);
+ long ScOpenMax = sysconf(_SC_OPEN_MAX);
// Close all of our FDs - just in case
- for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++)
+ for (int K = 3; K != ScOpenMax; K++)
{
if(KeepFDs.find(K) == KeepFDs.end())
fcntl(K,F_SETFD,FD_CLOEXEC);
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";
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:
bool eof;
bool compressing;
- LZMAFILE() : file(NULL), eof(false), compressing(false) {}
+ LZMAFILE() : file(NULL), eof(false), compressing(false) { buffer[0] = '\0'; }
~LZMAFILE() {
if (compressing == true)
{
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
#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;
return result;
}
/*}}}*/
-
-std::string GetTempDir()
+std::string GetTempDir() /*{{{*/
{
const char *tmpdir = getenv("TMPDIR");
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) /*{{{*/
{
char fn[512];
FileFd *Fd = new FileFd();
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)
int Pipe[2] = {-1, -1};
if(pipe(Pipe) != 0)
- {
return _error->Errno("pipe", _("Failed to create subprocess IPC"));
- return NULL;
- }
+
std::set<int> keep_fds;
keep_fds.insert(Pipe[0]);
keep_fds.insert(Pipe[1]);
return true;
}
-
-bool DropPrivs()
+ /*}}}*/
+bool DropPrivileges() /*{{{*/
{
- /* uid will be 0 in the end, but gid might be different anyway */
- uid_t old_uid = getuid();
- gid_t old_gid = getgid();
-
- if (old_uid != 0)
- return true;
if(_config->FindB("Debug::NoDropPrivs", false) == true)
return true;
- const std::string nobody = _config->Find("APT::User::Nobody", "Debian-apt");
- struct passwd *pw = getpwnam(nobody.c_str());
- if (pw == NULL)
- return _error->Warning("No user %s, can not drop rights", nobody.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)
+#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
- if (setgroups(1, &pw->pw_gid) != 1)
+ // 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 const old_uid = getuid();
+ gid_t const old_gid = getgid();
+
+ if (old_uid != 0)
+ return true;
+
+ struct passwd *pw = getpwnam(toUser.c_str());
+ if (pw == NULL)
+ return _error->Error("No user %s, can not drop rights", toUser.c_str());
+
+ // Do not change the order here, it might break things
+ if (setgroups(1, &pw->pw_gid))
return _error->Errno("setgroups", "Failed to setgroups");
if (setegid(pw->pw_gid) != 0)
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");
- /* Try changing GID/EGID */
- if (pw->pw_gid != old_gid && (setgid(old_gid) != -1 || setegid(old_gid) != -1))
- return _error->Error("Could restore a gid to root, privilege dropping did not work");
-
- if (pw->pw_uid != old_uid && (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
- return _error->Error("Could restore a uid to root, privilege dropping did not work");
-
- /* Verify the list of supplementary groups is what we expected */
+ // Verify that the user has only a single group, and the correct one
gid_t groups[1];
if (getgroups(1, groups) != 1)
return _error->Errno("getgroups", "Could not get new groups");
if (groups[0] != pw->pw_gid)
return _error->Error("Could not switch group");
- /* Verify gid, egid, uid, and euid */
+
+ // Verify that gid, egid, uid, and euid changed
if (getgid() != pw->pw_gid)
return _error->Error("Could not switch group");
if (getegid() != pw->pw_gid)
if (geteuid() != pw->pw_uid)
return _error->Error("Could not switch effective user");
- /* TODO: Check saved uid/saved gid as well */
+#ifdef HAVE_GETRESUID
+ // verify that the saved set-user-id was changed as well
+ uid_t ruid = 0;
+ uid_t euid = 0;
+ uid_t suid = 0;
+ if (getresuid(&ruid, &euid, &suid))
+ return _error->Errno("getresuid", "Could not get saved set-user-ID");
+ if (suid != pw->pw_uid)
+ return _error->Error("Could not switch saved set-user-ID");
+#endif
+
+#ifdef HAVE_GETRESGID
+ // verify that the saved set-group-id was changed as well
+ gid_t rgid = 0;
+ gid_t egid = 0;
+ gid_t sgid = 0;
+ if (getresgid(&rgid, &egid, &sgid))
+ return _error->Errno("getresuid", "Could not get saved set-group-ID");
+ if (sgid != pw->pw_gid)
+ return _error->Error("Could not switch saved set-group-ID");
+#endif
+
+ // Check that uid and gid changes do not work anymore
+ if (pw->pw_gid != old_gid && (setgid(old_gid) != -1 || setegid(old_gid) != -1))
+ return _error->Error("Could restore a gid to root, privilege dropping did not work");
+
+ if (pw->pw_uid != old_uid && (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
+ return _error->Error("Could restore a uid to root, privilege dropping did not work");
+
return true;
}
+ /*}}}*/