summaryrefslogtreecommitdiffstats
path: root/methods/aptmethod.h
diff options
context:
space:
mode:
Diffstat (limited to 'methods/aptmethod.h')
-rw-r--r--methods/aptmethod.h595
1 files changed, 595 insertions, 0 deletions
diff --git a/methods/aptmethod.h b/methods/aptmethod.h
new file mode 100644
index 0000000..26b8c0b
--- /dev/null
+++ b/methods/aptmethod.h
@@ -0,0 +1,595 @@
+#ifndef APT_APTMETHOD_H
+#define APT_APTMETHOD_H
+
+#include "config.h"
+
+#include <apt-pkg/acquire-method.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/netrc.h>
+#include <apt-pkg/strutl.h>
+
+#include <algorithm>
+#include <locale>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <cstdlib>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+
+#ifdef HAVE_SECCOMP
+#include <csignal>
+
+#include <seccomp.h>
+#endif
+
+enum class ResultState
+{
+ TRANSIENT_ERROR,
+ FATAL_ERROR,
+ SUCCESSFUL
+};
+
+static bool hasDoubleColon(std::string const &n)
+{
+ return n.find("::") != std::string::npos;
+}
+
+class aptConfigWrapperForMethods
+{
+protected:
+ std::vector<std::string> methodNames;
+public:
+ void setPostfixForMethodNames(char const * const postfix) APT_NONNULL(2)
+ {
+ methodNames.erase(std::remove_if(methodNames.begin(), methodNames.end(), hasDoubleColon), methodNames.end());
+ decltype(methodNames) toAdd;
+ for (auto && name: methodNames)
+ toAdd.emplace_back(name + "::" + postfix);
+ std::move(toAdd.begin(), toAdd.end(), std::back_inserter(methodNames));
+ }
+
+ bool DebugEnabled() const
+ {
+ if (methodNames.empty())
+ return false;
+ auto const sni = std::find_if_not(methodNames.crbegin(), methodNames.crend(), hasDoubleColon);
+ if (unlikely(sni == methodNames.crend()))
+ return false;
+ auto const ln = methodNames[methodNames.size() - 1];
+ // worst case: all three are the same
+ std::string confln, confsn, confpn;
+ strprintf(confln, "Debug::Acquire::%s", ln.c_str());
+ strprintf(confsn, "Debug::Acquire::%s", sni->c_str());
+ auto const pni = sni->substr(0, sni->find('+'));
+ strprintf(confpn, "Debug::Acquire::%s", pni.c_str());
+ return _config->FindB(confln,_config->FindB(confsn, _config->FindB(confpn, false)));
+ }
+ std::string ConfigFind(char const * const postfix, std::string const &defValue) const APT_NONNULL(2)
+ {
+ for (auto name = methodNames.rbegin(); name != methodNames.rend(); ++name)
+ {
+ std::string conf;
+ strprintf(conf, "Acquire::%s::%s", name->c_str(), postfix);
+ auto value = _config->Find(conf);
+ if (not value.empty())
+ return value;
+ }
+ return defValue;
+ }
+ std::string ConfigFind(std::string const &postfix, std::string const &defValue) const
+ {
+ return ConfigFind(postfix.c_str(), defValue);
+ }
+ bool ConfigFindB(char const * const postfix, bool const defValue) const APT_NONNULL(2)
+ {
+ return StringToBool(ConfigFind(postfix, defValue ? "yes" : "no"), defValue);
+ }
+ int ConfigFindI(char const * const postfix, int const defValue) const APT_NONNULL(2)
+ {
+ char *End;
+ std::string const value = ConfigFind(postfix, "");
+ auto const Res = strtol(value.c_str(), &End, 0);
+ if (value.c_str() == End)
+ return defValue;
+ return Res;
+ }
+
+ explicit aptConfigWrapperForMethods(std::string const &name) : methodNames{{name}} {}
+ explicit aptConfigWrapperForMethods(std::vector<std::string> &&names) : methodNames{std::move(names)} {}
+};
+
+class aptMethod : public pkgAcqMethod, public aptConfigWrapperForMethods
+{
+protected:
+ std::string const Binary;
+ unsigned long SeccompFlags;
+ enum Seccomp
+ {
+ BASE = (1 << 1),
+ NETWORK = (1 << 2),
+ DIRECTORY = (1 << 3),
+ };
+
+ public:
+ virtual bool Configuration(std::string Message) APT_OVERRIDE
+ {
+ if (pkgAcqMethod::Configuration(Message) == false)
+ return false;
+
+ std::string const conf = std::string("Binary::") + Binary;
+ _config->MoveSubTree(conf.c_str(), NULL);
+
+ DropPrivsOrDie();
+ if (LoadSeccomp() == false)
+ return false;
+
+ return true;
+ }
+
+ bool RunningInQemu(void)
+ {
+ int status;
+ pid_t pid;
+
+ pid = fork();
+ if (pid == 0)
+ {
+ close(0);
+ close(1);
+ close(2);
+ setenv("QEMU_VERSION", "meow", 1);
+ char path[] = LIBEXEC_DIR "/apt-helper";
+ char *const argv[] = {path, NULL};
+ execv(argv[0], argv);
+ _exit(255);
+ }
+
+ // apt-helper is supposed to exit with an error. If it exited with 0,
+ // qemu-user had problems with QEMU_VERSION and returned 0 => running in
+ // qemu-user.
+
+ if (waitpid(pid, &status, 0) == pid && WIFEXITED(status) && WEXITSTATUS(status) == 0)
+ return true;
+
+ return false;
+ }
+
+ bool LoadSeccomp()
+ {
+#ifdef HAVE_SECCOMP
+ int rc;
+ scmp_filter_ctx ctx = NULL;
+
+ if (SeccompFlags == 0)
+ return true;
+
+ if (_config->FindB("APT::Sandbox::Seccomp", false) == false)
+ return true;
+
+ if (RunningInQemu() == true)
+ {
+ Warning("Running in qemu-user, not using seccomp");
+ return true;
+ }
+
+ ctx = seccomp_init(SCMP_ACT_TRAP);
+ if (ctx == NULL)
+ return _error->FatalE("HttpMethod::Configuration", "Cannot init seccomp");
+
+#define ALLOW(what) \
+ if ((rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(what), 0))) \
+ return _error->FatalE("HttpMethod::Configuration", "Cannot allow %s: %s", #what, strerror(-rc));
+
+ for (auto &custom : _config->FindVector("APT::Sandbox::Seccomp::Trap"))
+ {
+ if ((rc = seccomp_rule_add(ctx, SCMP_ACT_TRAP, seccomp_syscall_resolve_name(custom.c_str()), 0)))
+ return _error->FatalE("HttpMethod::Configuration", "Cannot trap %s: %s", custom.c_str(), strerror(-rc));
+ }
+
+ ALLOW(access);
+ ALLOW(arch_prctl);
+ ALLOW(brk);
+ ALLOW(chmod);
+ ALLOW(chown);
+ ALLOW(chown32);
+ ALLOW(clock_getres);
+ ALLOW(clock_getres_time64);
+ ALLOW(clock_gettime);
+ ALLOW(clock_gettime64);
+ ALLOW(clock_nanosleep);
+ ALLOW(clock_nanosleep_time64);
+ ALLOW(close);
+ ALLOW(creat);
+ ALLOW(dup);
+ ALLOW(dup2);
+ ALLOW(dup3);
+ ALLOW(exit);
+ ALLOW(exit_group);
+ ALLOW(faccessat);
+ ALLOW(fchmod);
+ ALLOW(fchmodat);
+ ALLOW(fchown);
+ ALLOW(fchown32);
+ ALLOW(fchownat);
+ ALLOW(fcntl);
+ ALLOW(fcntl64);
+ ALLOW(fdatasync);
+ ALLOW(flock);
+ ALLOW(fstat);
+ ALLOW(fstat64);
+ ALLOW(fstatat64);
+ ALLOW(fstatfs);
+ ALLOW(fstatfs64);
+ ALLOW(fsync);
+ ALLOW(ftime);
+ ALLOW(ftruncate);
+ ALLOW(ftruncate64);
+ ALLOW(futex);
+ ALLOW(futex_time64);
+ ALLOW(futimesat);
+ ALLOW(getegid);
+ ALLOW(getegid32);
+ ALLOW(geteuid);
+ ALLOW(geteuid32);
+ ALLOW(getgid);
+ ALLOW(getgid32);
+ ALLOW(getgroups);
+ ALLOW(getgroups32);
+ ALLOW(getpeername);
+ ALLOW(getpgid);
+ ALLOW(getpgrp);
+ ALLOW(getpid);
+ ALLOW(getppid);
+ ALLOW(getrandom);
+ ALLOW(getresgid);
+ ALLOW(getresgid32);
+ ALLOW(getresuid);
+ ALLOW(getresuid32);
+ ALLOW(getrlimit);
+ ALLOW(get_robust_list);
+ ALLOW(getrusage);
+ ALLOW(gettid);
+ ALLOW(gettimeofday);
+ ALLOW(getuid);
+ ALLOW(getuid32);
+ ALLOW(ioctl);
+ ALLOW(lchown);
+ ALLOW(lchown32);
+ ALLOW(_llseek);
+ ALLOW(lseek);
+ ALLOW(lstat);
+ ALLOW(lstat64);
+ ALLOW(madvise);
+ ALLOW(mmap);
+ ALLOW(mmap2);
+ ALLOW(mprotect);
+ ALLOW(mremap);
+ ALLOW(msync);
+ ALLOW(munmap);
+ ALLOW(nanosleep);
+ ALLOW(newfstatat);
+ ALLOW(_newselect);
+ ALLOW(oldfstat);
+ ALLOW(oldlstat);
+ ALLOW(oldolduname);
+ ALLOW(oldstat);
+ ALLOW(olduname);
+ ALLOW(open);
+ ALLOW(openat);
+ ALLOW(pipe);
+ ALLOW(pipe2);
+ ALLOW(poll);
+ ALLOW(ppoll);
+ ALLOW(ppoll_time64);
+ ALLOW(prctl);
+ ALLOW(prlimit64);
+ ALLOW(pselect6);
+ ALLOW(pselect6_time64);
+ ALLOW(read);
+ ALLOW(readv);
+ ALLOW(rename);
+ ALLOW(renameat);
+ ALLOW(renameat2);
+ ALLOW(restart_syscall);
+ ALLOW(rt_sigaction);
+ ALLOW(rt_sigpending);
+ ALLOW(rt_sigprocmask);
+ ALLOW(rt_sigqueueinfo);
+ ALLOW(rt_sigreturn);
+ ALLOW(rt_sigsuspend);
+ ALLOW(rt_sigtimedwait);
+ ALLOW(sched_yield);
+ ALLOW(select);
+ ALLOW(set_robust_list);
+ ALLOW(sigaction);
+ ALLOW(sigpending);
+ ALLOW(sigprocmask);
+ ALLOW(sigreturn);
+ ALLOW(sigsuspend);
+ ALLOW(stat);
+ ALLOW(stat64);
+ ALLOW(statfs);
+ ALLOW(statfs64);
+#ifdef __NR_statx
+ ALLOW(statx);
+#endif
+ ALLOW(sync);
+ ALLOW(syscall);
+ ALLOW(sysinfo);
+ ALLOW(time);
+ ALLOW(truncate);
+ ALLOW(truncate64);
+ ALLOW(ugetrlimit);
+ ALLOW(umask);
+ ALLOW(uname);
+ ALLOW(unlink);
+ ALLOW(unlinkat);
+ ALLOW(utime);
+ ALLOW(utimensat);
+ ALLOW(utimensat_time64);
+ ALLOW(utimes);
+ ALLOW(write);
+ ALLOW(writev);
+
+ if ((SeccompFlags & Seccomp::NETWORK) != 0)
+ {
+ ALLOW(bind);
+ ALLOW(connect);
+ ALLOW(getsockname);
+ ALLOW(getsockopt);
+ ALLOW(recv);
+ ALLOW(recvfrom);
+ ALLOW(recvmmsg);
+ ALLOW(recvmmsg_time64);
+ ALLOW(recvmsg);
+ ALLOW(send);
+ ALLOW(sendmmsg);
+ ALLOW(sendmsg);
+ ALLOW(sendto);
+ ALLOW(setsockopt);
+ ALLOW(shutdown);
+ ALLOW(socket);
+ ALLOW(socketcall);
+ }
+
+ if ((SeccompFlags & Seccomp::DIRECTORY) != 0)
+ {
+ ALLOW(readdir);
+ ALLOW(getdents);
+ ALLOW(getdents64);
+ }
+
+ if (getenv("FAKED_MODE"))
+ {
+ ALLOW(semop);
+ ALLOW(semget);
+ ALLOW(msgsnd);
+ ALLOW(msgrcv);
+ ALLOW(msgget);
+ ALLOW(msgctl);
+ ALLOW(ipc);
+ }
+
+ for (auto &custom : _config->FindVector("APT::Sandbox::Seccomp::Allow"))
+ {
+ if ((rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, seccomp_syscall_resolve_name(custom.c_str()), 0)))
+ return _error->FatalE("aptMethod::Configuration", "Cannot allow %s: %s", custom.c_str(), strerror(-rc));
+ }
+
+#undef ALLOW
+
+ rc = seccomp_load(ctx);
+ if (rc == -EINVAL)
+ {
+ std::string msg;
+ strprintf(msg, "aptMethod::Configuration: could not load seccomp policy: %s", strerror(-rc));
+ Warning(std::move(msg));
+ }
+ else if (rc != 0)
+ return _error->FatalE("aptMethod::Configuration", "could not load seccomp policy: %s", strerror(-rc));
+
+ if (_config->FindB("APT::Sandbox::Seccomp::Print", true))
+ {
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ sigemptyset(&action.sa_mask);
+ action.sa_sigaction = [](int, siginfo_t *info, void *) {
+ // Formats a number into a 10 digit ASCII string
+ char buffer[10];
+ int number = info->si_syscall;
+
+ for (int i = sizeof(buffer) - 1; i >= 0; i--)
+ {
+ buffer[i] = (number % 10) + '0';
+ number /= 10;
+ }
+
+ constexpr const char *str1 = "\n **** Seccomp prevented execution of syscall ";
+ constexpr const char *str2 = " on architecture ";
+ constexpr const char *str3 = " ****\n";
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-result"
+#endif
+ write(2, str1, strlen(str1));
+ write(2, buffer, sizeof(buffer));
+ write(2, str2, strlen(str2));
+ write(2, COMMON_ARCH, strlen(COMMON_ARCH));
+ write(2, str3, strlen(str3));
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+ _exit(31);
+ };
+ action.sa_flags = SA_SIGINFO;
+
+ sigaction(SIGSYS, &action, nullptr);
+ }
+#endif
+ return true;
+ }
+
+ bool CalculateHashes(FetchItem const * const Itm, FetchResult &Res) const APT_NONNULL(2)
+ {
+ Hashes Hash(Itm->ExpectedHashes);
+ FileFd Fd;
+ if (Fd.Open(Res.Filename, FileFd::ReadOnly) == false || Hash.AddFD(Fd) == false)
+ return false;
+ Res.TakeHashes(Hash);
+ return true;
+ }
+
+ void Warning(std::string &&msg)
+ {
+ std::unordered_map<std::string, std::string> fields;
+ if (Queue != 0)
+ fields.emplace("URI", Queue->Uri);
+ else
+ fields.emplace("URI", "<UNKNOWN>");
+ if (not UsedMirror.empty())
+ fields.emplace("UsedMirror", UsedMirror);
+ fields.emplace("Message", std::move(msg));
+ SendMessage("104 Warning", std::move(fields));
+ }
+
+ bool TransferModificationTimes(char const * const From, char const * const To, time_t &LastModified) APT_NONNULL(2, 3)
+ {
+ if (strcmp(To, "/dev/null") == 0)
+ return true;
+
+ struct stat Buf2;
+ if (lstat(To, &Buf2) != 0 || S_ISLNK(Buf2.st_mode))
+ return true;
+
+ struct stat Buf;
+ if (stat(From, &Buf) != 0)
+ return _error->Errno("stat",_("Failed to stat"));
+
+ // we don't use utimensat here for compatibility reasons: #738567
+ struct timeval times[2];
+ times[0].tv_sec = Buf.st_atime;
+ LastModified = times[1].tv_sec = Buf.st_mtime;
+ times[0].tv_usec = times[1].tv_usec = 0;
+ if (utimes(To, times) != 0)
+ return _error->Errno("utimes",_("Failed to set modification time"));
+ return true;
+ }
+
+ // This is a copy of #pkgAcqMethod::Dequeue which is private & hidden
+ void Dequeue()
+ {
+ FetchItem const *const Tmp = Queue;
+ Queue = Queue->Next;
+ if (Tmp == QueueBack)
+ QueueBack = Queue;
+ delete Tmp;
+ }
+ static std::string URIEncode(std::string const &part)
+ {
+ // The "+" is encoded as a workaround for an S3 bug (LP#1003633 and LP#1086997)
+ return QuoteString(part, _config->Find("Acquire::URIEncode", "+~ ").c_str());
+ }
+
+ static std::string DecodeSendURI(std::string const &part)
+ {
+ if (_config->FindB("Acquire::Send-URI-Encoded", false))
+ return DeQuoteString(part);
+ return part;
+ }
+
+ aptMethod(std::string &&Binary, char const *const Ver, unsigned long const Flags) APT_NONNULL(3)
+ : pkgAcqMethod(Ver, Flags), aptConfigWrapperForMethods(Binary), Binary(std::move(Binary)), SeccompFlags(0)
+ {
+ try {
+ std::locale::global(std::locale(""));
+ } catch (...) {
+ setlocale(LC_ALL, "");
+ }
+ }
+};
+class aptAuthConfMethod : public aptMethod
+{
+ std::vector<std::unique_ptr<FileFd>> authconfs;
+
+ public:
+ virtual bool Configuration(std::string Message) APT_OVERRIDE
+ {
+ if (pkgAcqMethod::Configuration(Message) == false)
+ return false;
+
+ std::string const conf = std::string("Binary::") + Binary;
+ _config->MoveSubTree(conf.c_str(), NULL);
+
+ // ignore errors with opening the auth file as it doesn't need to exist
+ _error->PushToStack();
+ auto const netrc = _config->FindFile("Dir::Etc::netrc");
+ if (netrc.empty() == false)
+ {
+ authconfs.emplace_back(new FileFd());
+ authconfs.back()->Open(netrc, FileFd::ReadOnly);
+ }
+
+ auto const netrcparts = _config->FindDir("Dir::Etc::netrcparts");
+ if (netrcparts.empty() == false)
+ {
+ for (auto &&netrcpart : GetListOfFilesInDir(netrcparts, "conf", true, true))
+ {
+ authconfs.emplace_back(new FileFd());
+ authconfs.back()->Open(netrcpart, FileFd::ReadOnly);
+ }
+ }
+ _error->RevertToStack();
+
+ DropPrivsOrDie();
+
+ if (LoadSeccomp() == false)
+ return false;
+
+ return true;
+ }
+
+ bool MaybeAddAuthTo(URI &uri)
+ {
+ bool result = true;
+
+ if (uri.User.empty() == false || uri.Password.empty() == false)
+ return true;
+
+ _error->PushToStack();
+ for (auto &authconf : authconfs)
+ {
+ if (authconf->IsOpen() == false)
+ continue;
+ if (authconf->Seek(0) == false)
+ {
+ result = false;
+ continue;
+ }
+
+ result &= MaybeAddAuth(*authconf, uri);
+ }
+
+ while (not _error->empty())
+ {
+ std::string message;
+ _error->PopMessage(message);
+ Warning(std::move(message));
+ }
+ _error->RevertToStack();
+
+ return result;
+ }
+
+ aptAuthConfMethod(std::string &&Binary, char const *const Ver, unsigned long const Flags) APT_NONNULL(3)
+ : aptMethod(std::move(Binary), Ver, Flags) {}
+};
+#endif