diff options
author | David Kalnischkies <david@kalnischkies.de> | 2017-07-26 19:09:59 +0200 |
---|---|---|
committer | David Kalnischkies <david@kalnischkies.de> | 2017-07-26 19:09:59 +0200 |
commit | 2920e9428e26004f90a1f1ea86f07850b2204f85 (patch) | |
tree | 601b38dc82e987ad0ed141caa31b480e6f6fb4ca | |
parent | f2f8e89f08cdf01c83a0b8ab053c65329d85ca90 (diff) | |
parent | 8580574ec63fedd39a3ab3b9f0025e08eae5f620 (diff) |
Merge branch 'feature/authconf'
31 files changed, 874 insertions, 347 deletions
diff --git a/CMake/config.h.in b/CMake/config.h.in index ee822e204..f5a03eedd 100644 --- a/CMake/config.h.in +++ b/CMake/config.h.in @@ -68,6 +68,7 @@ #define APT_8_CLEANER_HEADERS #define APT_9_CLEANER_HEADERS #define APT_10_CLEANER_HEADERS +#define APT_15_CLEANER_HEADERS /* unrolling is faster combined with an optimizing compiler */ #define SHA2_UNROLL_TRANSFORM diff --git a/apt-pkg/acquire.cc b/apt-pkg/acquire.cc index 5e5e146a8..9272c2402 100644 --- a/apt-pkg/acquire.cc +++ b/apt-pkg/acquire.cc @@ -74,21 +74,6 @@ void pkgAcquire::Initialize() QueueMode = QueueHost; if (strcasecmp(Mode.c_str(),"access") == 0) QueueMode = QueueAccess; - - // chown the auth.conf file as it will be accessed by our methods - std::string const SandboxUser = _config->Find("APT::Sandbox::User"); - if (getuid() == 0 && SandboxUser.empty() == false && SandboxUser != "root") // if we aren't root, we can't chown, so don't try it - { - struct passwd const * const pw = getpwnam(SandboxUser.c_str()); - struct group const * const gr = getgrnam(ROOT_GROUP); - if (pw != NULL && gr != NULL) - { - std::string const AuthConf = _config->FindFile("Dir::Etc::netrc"); - if(AuthConf.empty() == false && RealFileExists(AuthConf) && - chown(AuthConf.c_str(), pw->pw_uid, gr->gr_gid) != 0) - _error->WarningE("SetupAPTPartialDirectory", "chown to %s:root of file %s failed", SandboxUser.c_str(), AuthConf.c_str()); - } - } } /*}}}*/ // Acquire::GetLock - lock directory and prepare for action /*{{{*/ diff --git a/apt-pkg/cachefile.cc b/apt-pkg/cachefile.cc index 0116308e5..c070f4b9e 100644 --- a/apt-pkg/cachefile.cc +++ b/apt-pkg/cachefile.cc @@ -161,11 +161,11 @@ bool pkgCacheFile::BuildPolicy(OpProgress * /*Progress*/) if (_error->PendingError() == true) return false; - if (ReadPinFile(*Policy) == false || ReadPinDir(*Policy) == false) - return false; + ReadPinFile(*Policy); + ReadPinDir(*Policy); this->Policy = Policy.release(); - return true; + return _error->PendingError() == false; } /*}}}*/ // CacheFile::BuildDepCache - Open and build the dependency cache /*{{{*/ diff --git a/apt-pkg/contrib/configuration.cc b/apt-pkg/contrib/configuration.cc index 442e31dff..eb873bdba 100644 --- a/apt-pkg/contrib/configuration.cc +++ b/apt-pkg/contrib/configuration.cc @@ -840,9 +840,9 @@ bool ReadConfigFile(Configuration &Conf,const string &FName,bool const &AsSectio unsigned const &Depth) { // Open the stream for reading - ifstream F(FName.c_str(),ios::in); - if (F.fail() == true) - return _error->Errno("ifstream::ifstream",_("Opening configuration file %s"),FName.c_str()); + FileFd F; + if (OpenConfigurationFileFd(FName, F) == false) + return false; string LineBuffer; std::stack<std::string> Stack; @@ -852,26 +852,15 @@ bool ReadConfigFile(Configuration &Conf,const string &FName,bool const &AsSectio int CurLine = 0; bool InComment = false; - while (F.eof() == false) + while (F.Eof() == false) { // The raw input line. std::string Input; + if (F.ReadLine(Input) == false) + Input.clear(); // The input line with comments stripped. std::string Fragment; - // Grab the next line of F and place it in Input. - do - { - char *Buffer = new char[1024]; - - F.clear(); - F.getline(Buffer,sizeof(Buffer) / 2); - - Input += Buffer; - delete[] Buffer; - } - while (F.fail() && !F.eof()); - // Expand tabs in the input line and remove leading and trailing // whitespace. { @@ -1161,13 +1150,10 @@ bool ReadConfigFile(Configuration &Conf,const string &FName,bool const &AsSectio bool ReadConfigDir(Configuration &Conf,const string &Dir, bool const &AsSectional, unsigned const &Depth) { - vector<string> const List = GetListOfFilesInDir(Dir, "conf", true, true); - - // Read the files - for (vector<string>::const_iterator I = List.begin(); I != List.end(); ++I) - if (ReadConfigFile(Conf,*I,AsSectional,Depth) == false) - return false; - return true; + bool good = true; + for (auto const &I : GetListOfFilesInDir(Dir, "conf", true, true)) + good = ReadConfigFile(Conf, I, AsSectional, Depth) && good; + return good; } /*}}}*/ // MatchAgainstConfig Constructor /*{{{*/ diff --git a/apt-pkg/contrib/fileutl.cc b/apt-pkg/contrib/fileutl.cc index 630a98ce4..33f4f7e09 100644 --- a/apt-pkg/contrib/fileutl.cc +++ b/apt-pkg/contrib/fileutl.cc @@ -402,7 +402,10 @@ std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> c DIR *D = opendir(Dir.c_str()); if (D == 0) { - _error->Errno("opendir",_("Unable to read %s"),Dir.c_str()); + if (errno == EACCES) + _error->WarningE("opendir", _("Unable to read %s"), Dir.c_str()); + else + _error->Errno("opendir", _("Unable to read %s"), Dir.c_str()); return List; } @@ -2493,9 +2496,6 @@ bool FileFd::Read(int const Fd, void *To, unsigned long long Size, unsigned long } /*}}}*/ // FileFd::ReadLine - Read a complete line from the file /*{{{*/ -// --------------------------------------------------------------------- -/* Beware: This method can be quite slow for big buffers on UNcompressed - files because of the naive implementation! */ char* FileFd::ReadLine(char *To, unsigned long long const Size) { *To = '\0'; @@ -2503,6 +2503,29 @@ char* FileFd::ReadLine(char *To, unsigned long long const Size) return nullptr; return d->InternalReadLine(To, Size); } +bool FileFd::ReadLine(std::string &To) +{ + To.clear(); + if (d == nullptr || Failed()) + return false; + constexpr size_t buflen = 4096; + char buffer[buflen]; + size_t len; + do + { + if (d->InternalReadLine(buffer, buflen) == nullptr) + return false; + len = strlen(buffer); + To.append(buffer, len); + } while (len == buflen - 1 && buffer[len - 2] != '\n'); + // remove the newline at the end + auto const i = To.find_last_not_of("\r\n"); + if (i == std::string::npos) + To.clear(); + else + To.erase(i + 1); + return true; +} /*}}}*/ // FileFd::Flush - Flush the file /*{{{*/ bool FileFd::Flush() @@ -3104,3 +3127,15 @@ bool DropPrivileges() /*{{{*/ return true; } /*}}}*/ +bool OpenConfigurationFileFd(std::string const &File, FileFd &Fd) /*{{{*/ +{ + int const fd = open(File.c_str(), O_RDONLY | O_CLOEXEC | O_NOCTTY); + if (fd == -1) + return _error->WarningE("open", _("Unable to read %s"), File.c_str()); + APT::Configuration::Compressor none(".", "", "", nullptr, nullptr, 0); + if (Fd.OpenDescriptor(fd, FileFd::ReadOnly, none) == false) + return false; + Fd.SetFileName(File); + return true; +} + /*}}}*/ diff --git a/apt-pkg/contrib/fileutl.h b/apt-pkg/contrib/fileutl.h index 5e857b5c8..19b4ed49e 100644 --- a/apt-pkg/contrib/fileutl.h +++ b/apt-pkg/contrib/fileutl.h @@ -87,7 +87,28 @@ class FileFd } bool Read(void *To,unsigned long long Size,unsigned long long *Actual = 0); bool static Read(int const Fd, void *To, unsigned long long Size, unsigned long long * const Actual = 0); + /** read a complete line or until buffer is full + * + * The buffer will always be \\0 terminated, so at most Size-1 characters are read. + * If the buffer holds a complete line the last character (before \\0) will be + * the newline character \\n otherwise the line was longer than the buffer. + * + * @param To buffer which will hold the line + * @param Size of the buffer to fill + * @param \b nullptr is returned in error cases, otherwise + * the parameter \b To now filled with the line. + */ char* ReadLine(char *To, unsigned long long const Size); + /** read a complete line from the file + * + * Similar to std::getline() the string does \b not include + * the newline, but just the content of the line as the newline + * is not needed to distinguish cases as for the other #ReadLine method. + * + * @param To string which will hold the line + * @return \b true if successful, otherwise \b false + */ + bool ReadLine(std::string &To); bool Flush(); bool Write(const void *From,unsigned long long Size); bool static Write(int Fd, const void *From, unsigned long long Size); @@ -140,6 +161,7 @@ class FileFd inline bool Eof() {return (Flags & HitEof) == HitEof;}; inline bool IsCompressed() {return (Flags & Compressed) == Compressed;}; inline std::string &Name() {return FileName;}; + inline void SetFileName(std::string const &name) { FileName = name; }; FileFd(std::string FileName,unsigned int const Mode,unsigned long AccessMode = 0666); FileFd(std::string FileName,unsigned int const Mode, CompressMode Compress, unsigned long AccessMode = 0666); @@ -256,5 +278,6 @@ std::vector<std::string> Glob(std::string const &pattern, int flags=0); bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode, bool CaptureStderr); bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode); +APT_HIDDEN bool OpenConfigurationFileFd(std::string const &File, FileFd &Fd); #endif diff --git a/apt-pkg/contrib/netrc.cc b/apt-pkg/contrib/netrc.cc index 88027c989..27511d413 100644 --- a/apt-pkg/contrib/netrc.cc +++ b/apt-pkg/contrib/netrc.cc @@ -14,205 +14,129 @@ #include <config.h> #include <apt-pkg/configuration.h> +#include <apt-pkg/fileutl.h> #include <apt-pkg/strutl.h> #include <iostream> -#include <pwd.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> #include "netrc.h" -using std::string; - /* Get user and password from .netrc when given a machine name */ - -enum { - NOTHING, - HOSTFOUND, /* the 'machine' keyword was found */ - HOSTCOMPLETE, /* the machine name following the keyword was found too */ - HOSTVALID, /* this is "our" machine! */ - HOSTEND /* LAST enum */ -}; - -/* make sure we have room for at least this size: */ -#define LOGINSIZE 256 -#define PASSWORDSIZE 256 -#define NETRC DOT_CHAR "netrc" - -/* returns -1 on failure, 0 if the host is found, 1 is the host isn't found */ -static int parsenetrc_string (char *host, std::string &login, std::string &password, char *netrcfile = NULL) +bool MaybeAddAuth(FileFd &NetRCFile, URI &Uri) { - FILE *file; - int retcode = 1; - int specific_login = (login.empty() == false); - bool netrc_alloc = false; - - if (!netrcfile) { - char const * home = getenv ("HOME"); /* portable environment reader */ - - if (!home) { - struct passwd *pw; - pw = getpwuid (geteuid ()); - if(pw) - home = pw->pw_dir; - } - - if (!home) - return -1; - - if (asprintf (&netrcfile, "%s%s%s", home, DIR_CHAR, NETRC) == -1 || netrcfile == NULL) - return -1; - else - netrc_alloc = true; - } - - file = fopen (netrcfile, "r"); - if(file) { - char *tok; - char *tok_buf; - bool done = false; - char *netrcbuffer = NULL; - size_t netrcbuffer_size = 0; - - int state = NOTHING; - char state_login = 0; /* Found a login keyword */ - char state_password = 0; /* Found a password keyword */ - int state_our_login = false; /* With specific_login, - found *our* login name */ - - while (!done && getline(&netrcbuffer, &netrcbuffer_size, file) != -1) { - tok = strtok_r (netrcbuffer, " \t\n", &tok_buf); - while (!done && tok) { - if(login.empty() == false && password.empty() == false) { - done = true; - break; - } - - switch(state) { - case NOTHING: - if (!strcasecmp ("machine", tok)) { - /* the next tok is the machine name, this is in itself the - delimiter that starts the stuff entered for this machine, - after this we need to search for 'login' and - 'password'. */ - state = HOSTFOUND; - } - break; - case HOSTFOUND: - /* extended definition of a "machine" if we have a "/" - we match the start of the string (host.startswith(token) */ - if ((strchr(host, '/') && strstr(host, tok) == host) || - (!strcasecmp (host, tok))) { - /* and yes, this is our host! */ - state = HOSTVALID; - retcode = 0; /* we did find our host */ - } - else - /* not our host */ - state = NOTHING; - break; - case HOSTVALID: - /* we are now parsing sub-keywords regarding "our" host */ - if (state_login) { - if (specific_login) - state_our_login = !strcasecmp (login.c_str(), tok); - else - login = tok; - state_login = 0; - } else if (state_password) { - if (state_our_login || !specific_login) - password = tok; - state_password = 0; - } else if (!strcasecmp ("login", tok)) - state_login = 1; - else if (!strcasecmp ("password", tok)) - state_password = 1; - else if(!strcasecmp ("machine", tok)) { - /* ok, there's machine here go => */ - state = HOSTFOUND; - state_our_login = false; - } - break; - } /* switch (state) */ - - tok = strtok_r (NULL, " \t\n", &tok_buf); - } /* while(tok) */ - } /* while getline() */ - - free(netrcbuffer); - fclose(file); - } - - if (netrc_alloc) - free(netrcfile); - - return retcode; -} - -void maybe_add_auth (URI &Uri, string NetRCFile) -{ - if (_config->FindB("Debug::Acquire::netrc", false) == true) - std::clog << "maybe_add_auth: " << (string)Uri - << " " << NetRCFile << std::endl; - if (Uri.Password.empty () == true || Uri.User.empty () == true) - { - if (NetRCFile.empty () == false) - { - std::string login, password; - char *netrcfile = strdup(NetRCFile.c_str()); - - // first check for a generic host based netrc entry - char *host = strdup(Uri.Host.c_str()); - if (host && parsenetrc_string(host, login, password, netrcfile) == 0) + if (Uri.User.empty() == false || Uri.Password.empty() == false) + return true; + if (NetRCFile.IsOpen() == false || NetRCFile.Failed()) + return false; + auto const Debug = _config->FindB("Debug::Acquire::netrc", false); + + std::string lookfor; + if (Uri.Port != 0) + strprintf(lookfor, "%s:%i%s", Uri.Host.c_str(), Uri.Port, Uri.Path.c_str()); + else + lookfor.append(Uri.Host).append(Uri.Path); + + enum + { + NO, + MACHINE, + GOOD_MACHINE, + LOGIN, + PASSWORD + } active_token = NO; + std::string line; + while (NetRCFile.Eof() == false || line.empty() == false) + { + if (line.empty()) { - if (_config->FindB("Debug::Acquire::netrc", false) == true) - std::clog << "host: " << host - << " user: " << login - << " pass-size: " << password.size() - << std::endl; - Uri.User = login; - Uri.Password = password; - free(netrcfile); - free(host); - return; + if (NetRCFile.ReadLine(line) == false) + break; + else if (line.empty()) + continue; } - free(host); - - // if host did not work, try Host+Path next, this will trigger - // a lookup uri.startswith(host) in the netrc file parser (because - // of the "/" - char *hostpath = strdup((Uri.Host + Uri.Path).c_str()); - if (hostpath && parsenetrc_string(hostpath, login, password, netrcfile) == 0) + auto tokenend = line.find_first_of("\t "); + std::string token; + if (tokenend != std::string::npos) + { + token = line.substr(0, tokenend); + line.erase(0, tokenend + 1); + } + else + std::swap(line, token); + if (token.empty()) + continue; + switch (active_token) { - if (_config->FindB("Debug::Acquire::netrc", false) == true) - std::clog << "hostpath: " << hostpath - << " user: " << login - << " pass-size: " << password.size() - << std::endl; - Uri.User = login; - Uri.Password = password; + case NO: + if (token == "machine") + active_token = MACHINE; + break; + case MACHINE: + if (token.find('/') == std::string::npos) + { + if (Uri.Port != 0 && Uri.Host == token) + active_token = GOOD_MACHINE; + else if (lookfor.compare(0, lookfor.length() - Uri.Path.length(), token) == 0) + active_token = GOOD_MACHINE; + else + active_token = NO; + } + else + { + if (APT::String::Startswith(lookfor, token)) + active_token = GOOD_MACHINE; + else + active_token = NO; + } + break; + case GOOD_MACHINE: + if (token == "login") + active_token = LOGIN; + else if (token == "password") + active_token = PASSWORD; + else if (token == "machine") + { + if (Debug) + std::clog << "MaybeAddAuth: Found matching host adding '" << Uri.User << "' and '" << Uri.Password << "' for " + << (std::string)Uri << " from " << NetRCFile.Name() << std::endl; + return true; + } + break; + case LOGIN: + std::swap(Uri.User, token); + active_token = GOOD_MACHINE; + break; + case PASSWORD: + std::swap(Uri.Password, token); + active_token = GOOD_MACHINE; + break; } - free(netrcfile); - free(hostpath); - } - } + } + if (active_token == GOOD_MACHINE) + { + if (Debug) + std::clog << "MaybeAddAuth: Found matching host adding '" << Uri.User << "' and '" << Uri.Password << "' for " + << (std::string)Uri << " from " << NetRCFile.Name() << std::endl; + return true; + } + else if (active_token == NO) + { + if (Debug) + std::clog << "MaybeAddAuth: Found no matching host for " + << (std::string)Uri << " from " << NetRCFile.Name() << std::endl; + return true; + } + else if (Debug) + std::clog << "MaybeAddAuth: Found no matching host (syntax error: " << active_token << ") for " + << (std::string)Uri << " from " << NetRCFile.Name() << std::endl; + return false; } -#ifdef DEBUG -int main(int argc, char* argv[]) +void maybe_add_auth(URI &Uri, std::string NetRCFile) { - char login[64] = ""; - char password[64] = ""; - - if(argc < 2) - return -1; - - if(0 == parsenetrc (argv[1], login, password, argv[2])) { - printf("HOST: %s LOGIN: %s PASSWORD: %s\n", argv[1], login, password); - } + if (FileExists(NetRCFile) == false) + return; + FileFd fd; + if (fd.Open(NetRCFile, FileFd::ReadOnly)) + MaybeAddAuth(fd, Uri); } -#endif diff --git a/apt-pkg/contrib/netrc.h b/apt-pkg/contrib/netrc.h index b5b56f5d4..46d8cab3d 100644 --- a/apt-pkg/contrib/netrc.h +++ b/apt-pkg/contrib/netrc.h @@ -22,10 +22,15 @@ #include <apt-pkg/strutl.h> #endif +#ifndef APT_15_CLEANER_HEADERS #define DOT_CHAR "." #define DIR_CHAR "/" +#endif class URI; +class FileFd; -void maybe_add_auth (URI &Uri, std::string NetRCFile); +APT_DEPRECATED_MSG("Use FileFd-based MaybeAddAuth instead") +void maybe_add_auth(URI &Uri, std::string NetRCFile); +bool MaybeAddAuth(FileFd &NetRCFile, URI &Uri); #endif diff --git a/apt-pkg/init.cc b/apt-pkg/init.cc index af4e6faa0..207f0d902 100644 --- a/apt-pkg/init.cc +++ b/apt-pkg/init.cc @@ -212,14 +212,13 @@ bool pkgInitConfig(Configuration &Cnf) Cnf.CndSet("Acquire::Changelogs::URI::Origin::Ultimedia", "http://packages.ultimediaos.com/changelogs/pool/@CHANGEPATH@/changelog.txt"); Cnf.CndSet("Acquire::Changelogs::AlwaysOnline::Origin::Ubuntu", true); - bool Res = true; - // Read an alternate config file + _error->PushToStack(); const char *Cfg = getenv("APT_CONFIG"); if (Cfg != 0 && strlen(Cfg) != 0) { if (RealFileExists(Cfg) == true) - Res &= ReadConfigFile(Cnf,Cfg); + ReadConfigFile(Cnf, Cfg); else _error->WarningE("RealFileExists",_("Unable to read %s"),Cfg); } @@ -227,30 +226,29 @@ bool pkgInitConfig(Configuration &Cnf) // Read the configuration parts dir std::string const Parts = Cnf.FindDir("Dir::Etc::parts", "/dev/null"); if (DirectoryExists(Parts) == true) - Res &= ReadConfigDir(Cnf,Parts); + ReadConfigDir(Cnf, Parts); else if (APT::String::Endswith(Parts, "/dev/null") == false) _error->WarningE("DirectoryExists",_("Unable to read %s"),Parts.c_str()); // Read the main config file std::string const FName = Cnf.FindFile("Dir::Etc::main", "/dev/null"); if (RealFileExists(FName) == true) - Res &= ReadConfigFile(Cnf,FName); - - if (Res == false) - return false; + ReadConfigFile(Cnf, FName); if (Cnf.FindB("Debug::pkgInitConfig",false) == true) Cnf.Dump(); - + #ifdef APT_DOMAIN if (Cnf.Exists("Dir::Locale")) - { + { bindtextdomain(APT_DOMAIN,Cnf.FindDir("Dir::Locale").c_str()); bindtextdomain(textdomain(0),Cnf.FindDir("Dir::Locale").c_str()); } #endif - return true; + auto const good = _error->PendingError() == false; + _error->MergeWithStack(); + return good; } /*}}}*/ // pkgInitSystem - Initialize the _system calss /*{{{*/ diff --git a/apt-pkg/policy.cc b/apt-pkg/policy.cc index 008c98ecb..030bab26b 100644 --- a/apt-pkg/policy.cc +++ b/apt-pkg/policy.cc @@ -324,10 +324,10 @@ bool ReadPinDir(pkgPolicy &Plcy,string Dir) return false; // Read the files + bool good = true; for (vector<string>::const_iterator I = List.begin(); I != List.end(); ++I) - if (ReadPinFile(Plcy, *I) == false) - return false; - return true; + good = ReadPinFile(Plcy, *I) && good; + return good; } /*}}}*/ // ReadPinFile - Load the pin file into a Policy /*{{{*/ @@ -343,8 +343,11 @@ bool ReadPinFile(pkgPolicy &Plcy,string File) if (RealFileExists(File) == false) return true; - - FileFd Fd(File,FileFd::ReadOnly); + + FileFd Fd; + if (OpenConfigurationFileFd(File, Fd) == false) + return false; + pkgTagFile TF(&Fd, pkgTagFile::SUPPORT_COMMENTS); if (Fd.IsOpen() == false || Fd.Failed()) return false; diff --git a/apt-pkg/sourcelist.cc b/apt-pkg/sourcelist.cc index 17c5c7a11..adf598e48 100644 --- a/apt-pkg/sourcelist.cc +++ b/apt-pkg/sourcelist.cc @@ -300,37 +300,32 @@ pkgSourceList::~pkgSourceList() /* */ bool pkgSourceList::ReadMainList() { - // CNC:2003-03-03 - Multiple sources list support. - bool Res = true; -#if 0 - Res = ReadVendors(); - if (Res == false) - return false; -#endif - Reset(); // CNC:2003-11-28 - Entries in sources.list have priority over // entries in sources.list.d. string Main = _config->FindFile("Dir::Etc::sourcelist", "/dev/null"); string Parts = _config->FindDir("Dir::Etc::sourceparts", "/dev/null"); - + + _error->PushToStack(); if (RealFileExists(Main) == true) - Res &= ReadAppend(Main); + ReadAppend(Main); else if (DirectoryExists(Parts) == false && APT::String::Endswith(Parts, "/dev/null") == false) // Only warn if there are no sources.list.d. _error->WarningE("DirectoryExists", _("Unable to read %s"), Parts.c_str()); if (DirectoryExists(Parts) == true) - Res &= ReadSourceDir(Parts); + ReadSourceDir(Parts); else if (Main.empty() == false && RealFileExists(Main) == false && APT::String::Endswith(Parts, "/dev/null") == false) // Only warn if there is no sources.list file. _error->WarningE("RealFileExists", _("Unable to read %s"), Main.c_str()); for (auto && file: _config->FindVector("APT::Sources::With")) - Res &= AddVolatileFile(file, nullptr); + AddVolatileFile(file, nullptr); - return Res; + auto good = _error->PendingError() == false; + _error->MergeWithStack(); + return good; } /*}}}*/ // SourceList::Reset - Clear the sourcelist contents /*{{{*/ @@ -368,13 +363,12 @@ bool pkgSourceList::ReadAppend(string const &File) /* */ bool pkgSourceList::ParseFileOldStyle(std::string const &File) { - // Open the stream for reading - ifstream F(File.c_str(),ios::in /*| ios::nocreate*/); - if (F.fail() == true) - return _error->Errno("ifstream::ifstream",_("Opening %s"),File.c_str()); + FileFd Fd; + if (OpenConfigurationFileFd(File, Fd) == false) + return false; std::string Buffer; - for (unsigned int CurLine = 1; std::getline(F, Buffer); ++CurLine) + for (unsigned int CurLine = 1; Fd.ReadLine(Buffer); ++CurLine) { // remove comments size_t curpos = 0; @@ -423,7 +417,9 @@ bool pkgSourceList::ParseFileOldStyle(std::string const &File) bool pkgSourceList::ParseFileDeb822(string const &File) { // see if we can read the file - FileFd Fd(File, FileFd::ReadOnly); + FileFd Fd; + if (OpenConfigurationFileFd(File, Fd) == false) + return false; pkgTagFile Sources(&Fd, pkgTagFile::SUPPORT_COMMENTS); if (Fd.IsOpen() == false || Fd.Failed()) return _error->Error(_("Malformed stanza %u in source list %s (type)"),0,File.c_str()); @@ -497,17 +493,12 @@ bool pkgSourceList::GetIndexes(pkgAcquire *Owner, bool GetAll) const /* */ bool pkgSourceList::ReadSourceDir(string const &Dir) { - std::vector<std::string> ext; - ext.push_back("list"); - ext.push_back("sources"); - std::vector<std::string> const List = GetListOfFilesInDir(Dir, ext, true); - + std::vector<std::string> const ext = {"list", "sources"}; // Read the files - for (vector<string>::const_iterator I = List.begin(); I != List.end(); ++I) - if (ReadAppend(*I) == false) - return false; - return true; - + bool good = true; + for (auto const &I : GetListOfFilesInDir(Dir, ext, true)) + good = ReadAppend(I) && good; + return good; } /*}}}*/ // GetLastModified() /*{{{*/ diff --git a/apt-private/private-update.cc b/apt-private/private-update.cc index 8949dab30..c9113ddd3 100644 --- a/apt-private/private-update.cc +++ b/apt-private/private-update.cc @@ -8,6 +8,7 @@ #include <apt-pkg/configuration.h> #include <apt-pkg/error.h> #include <apt-pkg/fileutl.h> +#include <apt-pkg/metaindex.h> #include <apt-pkg/sourcelist.h> #include <apt-pkg/update.h> @@ -79,6 +80,44 @@ bool DoUpdate(CommandLine &CmdL) if (Cache.BuildCaches(false) == false) return false; + if (_config->FindB("APT::Get::Update::SourceListWarnings", true)) + { + List = Cache.GetSourceList(); + for (pkgSourceList::const_iterator S = List->begin(); S != List->end(); ++S) + { + if (APT::String::Startswith((*S)->GetURI(), "ftp://") == false) + continue; + pkgCache::RlsFileIterator const RlsFile = (*S)->FindInCache(Cache, false); + if (RlsFile.end() || RlsFile->Origin == 0 || RlsFile->Label == 0) + continue; + char const *const affected[][2] = { + {"Debian", "Debian"}, + {"Debian", "Debian-Security"}, + {"Debian Backports", "Debian Backports"}, + }; + auto const matchRelease = [&](decltype(affected[0]) a) { + return strcmp(RlsFile.Origin(), a[0]) == 0 && strcmp(RlsFile.Label(), a[1]) == 0; + }; + if (std::find_if(std::begin(affected), std::end(affected), matchRelease) != std::end(affected)) + _error->Warning("Debian shuts down public FTP services currently still used in your sources.list(5) as '%s'.\n" + "See press release %s for details.", + (*S)->GetURI().c_str(), "https://debian.org/News/2017/20170425"); + } + for (pkgSourceList::const_iterator S = List->begin(); S != List->end(); ++S) + { + URI uri((*S)->GetURI()); + if (uri.User.empty() && uri.Password.empty()) + continue; + // we can't really predict if a +http method supports everything http does, + // so we play it safe and use a whitelist here. + char const *const affected[] = {"http", "https", "tor+http", "tor+https", "ftp"}; + if (std::find(std::begin(affected), std::end(affected), uri.Access) != std::end(affected)) + // TRANSLATOR: the first two are manpage references, the last the URI from a sources.list + _error->Notice(_("Usage of %s should be preferred over embedding login information directly in the %s entry for '%s'"), + "apt_auth.conf(5)", "sources.list(5)", URI::ArchiveOnly(uri).c_str()); + } + } + // show basic stats (if the user whishes) if (_config->FindB("APT::Cmd::Show-Update-Stats", false) == true) { diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index a1491428f..d7241eb5e 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -66,6 +66,7 @@ endif() add_docbook(apt-man MANPAGE ALL DOCUMENTS apt.8.xml + apt_auth.conf.5.xml apt-cache.8.xml apt-cdrom.8.xml apt.conf.5.xml diff --git a/doc/apt-verbatim.ent b/doc/apt-verbatim.ent index b555c5de5..be599d393 100644 --- a/doc/apt-verbatim.ent +++ b/doc/apt-verbatim.ent @@ -15,6 +15,12 @@ </citerefentry>" > +<!ENTITY apt-authconf "<citerefentry> + <refentrytitle><filename>apt_auth.conf</filename></refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry>" +> + <!ENTITY apt-get "<citerefentry> <refentrytitle><command>apt-get</command></refentrytitle> <manvolnum>8</manvolnum> diff --git a/doc/apt_auth.conf.5.xml b/doc/apt_auth.conf.5.xml new file mode 100644 index 000000000..8a1882604 --- /dev/null +++ b/doc/apt_auth.conf.5.xml @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" + "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ +<!ENTITY % aptent SYSTEM "apt.ent"> %aptent; +<!ENTITY % aptverbatiment SYSTEM "apt-verbatim.ent"> %aptverbatiment; +<!ENTITY % aptvendor SYSTEM "apt-vendor.ent"> %aptvendor; +]> + +<refentry> + + <refentryinfo> + &apt-author.team; + &apt-email; + &apt-product; + <!-- The last update date --> + <date>2017-07-07T00:00:00Z</date> + </refentryinfo> + + <refmeta> + <refentrytitle>apt_auth.conf</refentrytitle> + <manvolnum>5</manvolnum> + <refmiscinfo class="manual">APT</refmiscinfo> + </refmeta> + + <!-- Man page title --> + <refnamediv> + <refname>apt_auth.conf</refname> + <refpurpose>Login configuration file for APT sources and proxies</refpurpose> + </refnamediv> + +<refsect1><title>Description</title> +<para>APT configuration files like &sources-list; or &apt-conf; need to be accessible +for everyone using apt tools on the system to have access to all package-related +information like the available packages in a repository. Login information +needed to connect to a proxy or to download data from a repository on the other +hand shouldn't always be accessible by everyone and can hence not be placed in a +file with world-readable file permissions.</para> + +<para>The APT auth.conf file <filename>/etc/apt/auth.conf</filename> can be used to store +login information in a netrc-like format with restrictive file permissions.</para> +</refsect1> + +<refsect1><title>netrc-like format</title> +<para>The format defined here is similar to the format of the <filename>~/.netrc</filename> +file used by <citerefentry><refentrytitle><command>ftp</command></refentrytitle><manvolnum>1</manvolnum></citerefentry> +and similar programs interacting with servers. +It is a simple token-based format with the following tokens being recognized; +Unknown tokens will be ignored. Tokens may be separated by spaces, tabs or newlines.</para> + +<variablelist> +<varlistentry> +<term><literal>machine</literal> <replaceable>hostname</replaceable>[:<replaceable>port</replaceable>][/<replaceable>path</replaceable>]</term> +<listitem><para>Entries are looked up by searching for the +<emphasis><literal>machine</literal></emphasis> token matching the +hostname of the URI apt needs login information for. Extending the netrc-format +a portnumber can be specified. If no port is given the token matches for all ports. +Similar the path is optional and only needed and useful if multiple repositories with +different login information reside on the same server. A machine token with a path +matches if the path in the URI starts with the path given in the token. +Once a match is made, the subsequent tokens are processed, stopping when the +end of file is reached or another <emphasis><literal>machine</literal></emphasis> +token is encountered.</para></listitem> +</varlistentry> + +<varlistentry> +<term><literal>login</literal> <replaceable>name</replaceable></term> +<listitem><para>The username to be used.</para></listitem> +</varlistentry> + +<varlistentry> +<term><literal>password</literal> <replaceable>string</replaceable></term> +<listitem><para>The password to be used.</para></listitem> +</varlistentry> + +</variablelist> + +</refsect1> + +<refsect1><title>Example</title> +<para>Supplying login information for a user named <literal>apt</literal> +with the password <literal>debian</literal> for the &sources-list; entry +<literallayout>deb http://example.org/debian &debian-stable-codename; main</literallayout> +could be done in the entry directly: +<literallayout>deb http://apt:debian@example.org/debian &debian-stable-codename; main</literallayout> +Alternatively an entry like the following in the auth.conf file could be used: +<literallayout>machine example.org +login apt +password debian</literallayout> +Or alternatively within a single line: +<literallayout>machine example.org login apt password debian</literallayout> +If you need to be more specific all of these lines will also apply to the example entry:</para> +<literallayout>machine example.org/deb login apt password debian +machine example.org/debian login apt password debian +machine example.org/debian/ login apt password debian +</literallayout> +On the other hand neither of the following lines apply: +<literallayout>machine example.org:80 login apt password debian +machine example.org/deb/ login apt password debian +machine example.org/ubuntu login apt password debian +machine example.orga login apt password debian +machine example.net login apt password debian +</literallayout> +</refsect1> + +<refsect1><title>Notes</title> +<para>Basic support for this feature is present since version 0.7.25, but was +undocumented for years. The documentation was added in version 1.5 changing +also the implementation slightly. For maximum backward compatibility you should +avoid multiple <literal>machine</literal> tokens with the same hostname, but if +you need multiple they should all have a path specified in the +<literal>machine</literal> token.</para> +</refsect1> + +<refsect1> +<title>Files</title> + <variablelist> + <varlistentry><term><filename>/etc/apt/auth.conf</filename></term> + <listitem><para>Login information for APT sources and proxies in a netrc-like format. + Configuration Item: <literal>Dir::Etc::netrc</literal>.</para></listitem> + </varlistentry> + </variablelist> +</refsect1> + +<refsect1> +<title>See Also</title> +<para>&apt-conf; &sources-list; +</para> +</refsect1> + + &manbugs; + +</refentry> diff --git a/doc/examples/configure-index b/doc/examples/configure-index index 244d7c1c3..61a749495 100644 --- a/doc/examples/configure-index +++ b/doc/examples/configure-index @@ -107,7 +107,11 @@ APT IndexTargets::ReleaseInfo "<BOOL>"; IndexTargets::format "<STRING>"; - Update::InteractiveReleaseInfoChanges "<BOOL>"; + Update + { + InteractiveReleaseInfoChanges "<BOOL>"; + SourceListWarnings "<BOOL>"; + }; }; Cache diff --git a/doc/sources.list.5.xml b/doc/sources.list.5.xml index dd057eb32..c4df9aa58 100644 --- a/doc/sources.list.5.xml +++ b/doc/sources.list.5.xml @@ -350,6 +350,40 @@ deb-src [ option1=value1 option2=value2 ] uri suite [component1] [component2] [. <para>The currently recognized URI types are: <variablelist> + <varlistentry><term><command>http</command></term> + <listitem><para> + The http scheme specifies an HTTP server for an archive and is the most + commonly used method, with many options in the + <literal>Acquire::http</literal> scope detailed in &apt-conf;. The URI can + directly include login information if the archive requires it, but the use + of &apt-authconf; should be preferred. The method also supports SOCKS5 and + HTTP(S) proxies either configured via apt-specific configuration or + specified by the environment variable <envar>http_proxy</envar> in the + format (assuming an HTTP proxy requiring authentication) + <replaceable>http://user:pass@server:port/</replaceable>. + The authentication details for proxies can also be supplied via + &apt-authconf;.</para> + <para>Note that these forms of authentication are insecure as the whole + communication with the remote server (or proxy) is not encrypted so a + sufficiently capable attacker can observe and record login as well as all + other interactions. The attacker can <emphasis>not</emphasis> modify the + communication through as APTs data security model is independent of the + chosen transport method. See &apt-secure; for details.</para></listitem> + </varlistentry> + + <varlistentry><term><command>https</command></term> + <listitem><para> + The https scheme specifies an HTTPS server for an archive and is very + similar in use and available options to the http scheme. The main + difference is that the communication between apt and server (or proxy) is + encrypted. Note that the encryption does not prevent an attacker from + knowing which server (or proxy) apt is communicating with and deeper + analyses can potentially still reveal which data was downloaded. If this is + a concern the Tor-based schemes mentioned further below might be a suitable + alternative.</para></listitem> + </varlistentry> + + <varlistentry><term><command>file</command></term> <listitem><para> The file scheme allows an arbitrary directory in the file system to be @@ -359,27 +393,19 @@ deb-src [ option1=value1 option2=value2 ] uri suite [component1] [component2] [. <varlistentry><term><command>cdrom</command></term> <listitem><para> - The cdrom scheme allows APT to use a local CD-ROM drive with media + The cdrom scheme allows APT to use a local CD-ROM, DVD or USB drive with media swapping. Use the &apt-cdrom; program to create cdrom entries in the source list.</para></listitem> </varlistentry> - <varlistentry><term><command>http</command></term> - <listitem><para> - The http scheme specifies an HTTP server for the archive. If an environment - variable <envar>http_proxy</envar> is set with the format - http://server:port/, the proxy server specified in - <envar>http_proxy</envar> will be used. Users of authenticated - HTTP/1.1 proxies may use a string of the format - http://user:pass@server:port/. - Note that this is an insecure method of authentication.</para></listitem> - </varlistentry> - <varlistentry><term><command>ftp</command></term> <listitem><para> - The ftp scheme specifies an FTP server for the archive. APT's FTP behavior - is highly configurable; for more information see the - &apt-conf; manual page. Please note that an FTP proxy can be specified + The ftp scheme specifies an FTP server for an archive. Use of FTP is on the + decline in favour of <literal>http</literal> and <literal>https</literal> + and many archives either never offered or are retiring FTP access. If you + still need this method many configuration options for it are available in + the <literal>Acquire::ftp</literal> scope and detailed in &apt-conf;.</para> + <para>Please note that an FTP proxy can be specified by using the <envar>ftp_proxy</envar> environment variable. It is possible to specify an HTTP proxy (HTTP proxy servers often understand FTP URLs) using this environment variable and <emphasis>only</emphasis> this @@ -407,9 +433,8 @@ deb-src [ option1=value1 option2=value2 ] uri suite [component1] [component2] [. <listitem><para> APT can be extended with more methods shipped in other optional packages, which should follow the naming scheme <package>apt-transport-<replaceable>method</replaceable></package>. - For instance, the APT team also maintains the package <package>apt-transport-https</package>, - which provides access methods for HTTPS URIs with features similar to the http method. - Methods for using e.g. debtorrent are also available - see &apt-transport-debtorrent;. + For instance, the APT team also maintains the package <package>apt-transport-tor</package>, + which provides access methods for HTTP and HTTPS URIs routed via the Tor network. </para></listitem> </varlistentry> </variablelist> diff --git a/methods/aptmethod.h b/methods/aptmethod.h index 04858e29d..23fd036dd 100644 --- a/methods/aptmethod.h +++ b/methods/aptmethod.h @@ -5,6 +5,7 @@ #include <apt-pkg/configuration.h> #include <apt-pkg/error.h> #include <apt-pkg/fileutl.h> +#include <apt-pkg/netrc.h> #include <algorithm> #include <locale> @@ -148,5 +149,44 @@ public: } } }; +class aptAuthConfMethod : public aptMethod +{ + FileFd authconf; +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); + + auto const netrc = _config->FindFile("Dir::Etc::netrc"); + if (netrc.empty() == false) + { + // ignore errors with opening the auth file as it doesn't need to exist + _error->PushToStack(); + authconf.Open(netrc, FileFd::ReadOnly); + _error->RevertToStack(); + } + DropPrivsOrDie(); + + return true; + } + + bool MaybeAddAuthTo(URI &uri) + { + if (uri.User.empty() == false || uri.Password.empty() == false) + return true; + if (authconf.IsOpen() == false) + return true; + if (authconf.Seek(0) == false) + return false; + return MaybeAddAuth(authconf, uri); + } + + aptAuthConfMethod(std::string &&Binary, char const * const Ver, unsigned long const Flags) APT_NONNULL(3) : + aptMethod(std::move(Binary), Ver, Flags) {} +}; #endif diff --git a/methods/basehttp.cc b/methods/basehttp.cc index cc5039c75..0eb617f89 100644 --- a/methods/basehttp.cc +++ b/methods/basehttp.cc @@ -830,14 +830,14 @@ unsigned long long BaseHttpMethod::FindMaximumObjectSizeInQueue() const /*{{{*/ } /*}}}*/ BaseHttpMethod::BaseHttpMethod(std::string &&Binary, char const * const Ver,unsigned long const Flags) :/*{{{*/ - aptMethod(std::move(Binary), Ver, Flags), Server(nullptr), PipelineDepth(10), + aptAuthConfMethod(std::move(Binary), Ver, Flags), Server(nullptr), PipelineDepth(10), AllowRedirect(false), Debug(false) { } /*}}}*/ bool BaseHttpMethod::Configuration(std::string Message) /*{{{*/ { - if (aptMethod::Configuration(Message) == false) + if (aptAuthConfMethod::Configuration(Message) == false) return false; _config->CndSet("Acquire::tor::Proxy", @@ -845,8 +845,9 @@ bool BaseHttpMethod::Configuration(std::string Message) /*{{{*/ return true; } /*}}}*/ -bool BaseHttpMethod::AddProxyAuth(URI &Proxy, URI const &Server) const /*{{{*/ +bool BaseHttpMethod::AddProxyAuth(URI &Proxy, URI const &Server) /*{{{*/ { + MaybeAddAuthTo(Proxy); if (std::find(methodNames.begin(), methodNames.end(), "tor") != methodNames.end() && Proxy.User == "apt-transport-tor" && Proxy.Password.empty()) { @@ -857,7 +858,6 @@ bool BaseHttpMethod::AddProxyAuth(URI &Proxy, URI const &Server) const /*{{{*/ else Proxy.Password = std::move(pass); } - // FIXME: should we support auth.conf for proxies? return true; } /*}}}*/ diff --git a/methods/basehttp.h b/methods/basehttp.h index 7000e7b89..aadd59168 100644 --- a/methods/basehttp.h +++ b/methods/basehttp.h @@ -115,7 +115,7 @@ struct ServerState virtual ~ServerState() {}; }; -class BaseHttpMethod : public aptMethod +class BaseHttpMethod : public aptAuthConfMethod { protected: virtual bool Fetch(FetchItem *) APT_OVERRIDE; @@ -164,7 +164,7 @@ class BaseHttpMethod : public aptMethod virtual void RotateDNS() = 0; virtual bool Configuration(std::string Message) APT_OVERRIDE; - bool AddProxyAuth(URI &Proxy, URI const &Server) const; + bool AddProxyAuth(URI &Proxy, URI const &Server); BaseHttpMethod(std::string &&Binary, char const * const Ver,unsigned long const Flags); virtual ~BaseHttpMethod() {}; diff --git a/methods/curl.cc b/methods/curl.cc index 71149217a..8e06d858d 100644 --- a/methods/curl.cc +++ b/methods/curl.cc @@ -270,7 +270,7 @@ bool HttpsMethod::Fetch(FetchItem *Itm) if (SetupProxy() == false) return _error->Error("Unsupported proxy configured: %s", URI::SiteOnly(Proxy).c_str()); - maybe_add_auth (Uri, _config->FindFile("Dir::Etc::netrc")); + MaybeAddAuthTo(Uri); if (Server == nullptr || Server->Comp(Itm->Uri) == false) Server = CreateServerState(Itm->Uri); diff --git a/methods/ftp.cc b/methods/ftp.cc index 4972337e3..341230f69 100644 --- a/methods/ftp.cc +++ b/methods/ftp.cc @@ -21,7 +21,6 @@ #include <apt-pkg/error.h> #include <apt-pkg/fileutl.h> #include <apt-pkg/hashes.h> -#include <apt-pkg/netrc.h> #include <apt-pkg/strutl.h> #include <iostream> @@ -961,7 +960,7 @@ bool FTPConn::Get(const char *Path,FileFd &To,unsigned long long Resume, // FtpMethod::FtpMethod - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ -FtpMethod::FtpMethod() : aptMethod("ftp","1.0",SendConfig) +FtpMethod::FtpMethod() : aptAuthConfMethod("ftp", "1.0", SendConfig) { signal(SIGTERM,SigTerm); signal(SIGINT,SigTerm); @@ -996,7 +995,7 @@ void FtpMethod::SigTerm(int) /* We stash the desired pipeline depth */ bool FtpMethod::Configuration(string Message) { - if (aptMethod::Configuration(Message) == false) + if (aptAuthConfMethod::Configuration(Message) == false) return false; TimeOut = _config->FindI("Acquire::Ftp::Timeout",TimeOut); @@ -1015,7 +1014,7 @@ bool FtpMethod::Fetch(FetchItem *Itm) Res.Filename = Itm->DestFile; Res.IMSHit = false; - maybe_add_auth (Get, _config->FindFile("Dir::Etc::netrc")); + MaybeAddAuthTo(Get); // Connect to the server if (Server == 0 || Server->Comp(Get) == false) diff --git a/methods/ftp.h b/methods/ftp.h index 67d00d9f1..1859ddce0 100644 --- a/methods/ftp.h +++ b/methods/ftp.h @@ -72,7 +72,7 @@ class FTPConn ~FTPConn(); }; -class FtpMethod : public aptMethod +class FtpMethod : public aptAuthConfMethod { virtual bool Fetch(FetchItem *Itm) APT_OVERRIDE; virtual bool Configuration(std::string Message) APT_OVERRIDE; diff --git a/methods/http.cc b/methods/http.cc index db4542981..fc22180d3 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -23,7 +23,6 @@ #include <apt-pkg/error.h> #include <apt-pkg/fileutl.h> #include <apt-pkg/hashes.h> -#include <apt-pkg/netrc.h> #include <apt-pkg/proxy.h> #include <apt-pkg/strutl.h> @@ -330,7 +329,7 @@ struct HttpConnectFd : public MethodFd }; bool UnwrapHTTPConnect(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, - unsigned long Timeout, aptMethod *Owner) + unsigned long Timeout, aptAuthConfMethod *Owner) { Owner->Status(_("Connecting to %s (%s)"), "HTTP proxy", URI::SiteOnly(Proxy).c_str()); // The HTTP server expects a hostname with a trailing :port @@ -348,9 +347,8 @@ bool UnwrapHTTPConnect(std::string Host, int Port, URI Proxy, std::unique_ptr<Me Req << "Host: " << ProperHost << ":" << std::to_string(Proxy.Port) << "\r\n"; else Req << "Host: " << ProperHost << "\r\n"; - ; - maybe_add_auth(Proxy, _config->FindFile("Dir::Etc::netrc")); + Owner->MaybeAddAuthTo(Proxy); if (Proxy.User.empty() == false || Proxy.Password.empty() == false) Req << "Proxy-Authorization: Basic " << Base64Encode(Proxy.User + ":" + Proxy.Password) << "\r\n"; @@ -931,7 +929,7 @@ void HttpMethod::SendReq(FetchItem *Itm) Req << "Proxy-Authorization: Basic " << Base64Encode(Server->Proxy.User + ":" + Server->Proxy.Password) << "\r\n"; - maybe_add_auth (Uri, _config->FindFile("Dir::Etc::netrc")); + MaybeAddAuthTo(Uri); if (Uri.User.empty() == false || Uri.Password.empty() == false) Req << "Authorization: Basic " << Base64Encode(Uri.User + ":" + Uri.Password) << "\r\n"; diff --git a/methods/http.h b/methods/http.h index 7a763675c..6d44fbdd4 100644 --- a/methods/http.h +++ b/methods/http.h @@ -93,7 +93,7 @@ class CircleBuf ~CircleBuf(); }; -bool UnwrapHTTPConnect(std::string To, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, unsigned long Timeout, aptMethod *Owner); +bool UnwrapHTTPConnect(std::string To, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, unsigned long Timeout, aptAuthConfMethod *Owner); struct HttpServerState: public ServerState { diff --git a/test/integration/test-apt-get-update-sourceslist-warning b/test/integration/test-apt-get-update-sourceslist-warning new file mode 100755 index 000000000..a99356b8b --- /dev/null +++ b/test/integration/test-apt-get-update-sourceslist-warning @@ -0,0 +1,45 @@ +#!/bin/sh +set -e + +TESTDIR="$(readlink -f "$(dirname "$0")")" +. "$TESTDIR/framework" + +setupenvironment +configarchitecture 'amd64' +setupaptarchive --no-update + +testsuccess apt update +testsuccess apt update --no-download + +echo 'deb ftp://ftp.tlh.debian.org/debian zurg main' > rootdir/etc/apt/sources.list.d/ftpshutdown.list +cat > rootdir/var/lib/apt/lists/ftp.tlh.debian.org_debian_dists_zurg_Release <<EOF +Origin: Debian +Label: Debian +Suite: unreleased +Codename: zurg +Date: Fri, 14 Jul 2017 11:34:35 +0000 +Architectures: amd64 +Components: main +Description: Debian x.y Zurg - Not Released +EOF +chmod 644 rootdir/var/lib/apt/lists/ftp.tlh.debian.org_debian_dists_zurg_Release + +testwarningequal "Reading package lists... +Building dependency tree... +All packages are up to date. +W: Debian shuts down public FTP services currently still used in your sources.list(5) as 'ftp://ftp.tlh.debian.org/debian/'. + See press release https://debian.org/News/2017/20170425 for details." apt update --no-download + + +echo 'deb http://apt:debian@ftp.tlh.debian.org/debian zurg main' > rootdir/etc/apt/sources.list.d/ftpshutdown.list +testsuccessequal "Reading package lists... +Building dependency tree... +All packages are up to date. +N: Usage of apt_auth.conf(5) should be preferred over embedding login information directly in the sources.list(5) entry for 'http://ftp.tlh.debian.org/debian'" apt update --no-download + + +echo 'deb tor+https://apt:debian@ftp.tlh.debian.org/debian zurg main' > rootdir/etc/apt/sources.list.d/ftpshutdown.list +testsuccessequal "Reading package lists... +Building dependency tree... +All packages are up to date. +N: Usage of apt_auth.conf(5) should be preferred over embedding login information directly in the sources.list(5) entry for 'tor+https://ftp.tlh.debian.org/debian'" apt update --no-download diff --git a/test/integration/test-authentication-basic b/test/integration/test-authentication-basic index 3bfd076ce..011f205af 100755 --- a/test/integration/test-authentication-basic +++ b/test/integration/test-authentication-basic @@ -13,6 +13,7 @@ setupaptarchive --no-update changetohttpswebserver --authorization="$(printf '%s' 'star@irc:hunter2' | base64 )" echo 'See, when YOU type hunter2, it shows to us as *******' > aptarchive/bash +echo 'Debug::Acquire::netrc "true";' > rootdir/etc/apt/apt.conf.d/netrcdebug.conf testauthfailure() { testfailure apthelper download-file "${1}/bash" ./downloaded/bash @@ -37,7 +38,11 @@ testauthsuccess() { fi rm -rf rootdir/var/lib/apt/lists - testsuccess aptget update + if expr index "$1" '@' >/dev/null; then + testsuccesswithnotice aptget update + else + testsuccess aptget update + fi testsuccessequal 'Reading package lists... Building dependency tree... The following NEW packages will be installed: @@ -94,7 +99,9 @@ rewritesourceslist "http://localhost:${APTHTTPPORT}" msgmsg 'proxy to server basic auth' webserverconfig 'aptwebserver::request::absolute' 'uri' -export http_proxy="http://localhost:${APTHTTPPORT}" +# using ip instead of localhost avoids picking up the auth for the repo +# for the proxy as well as we serve them both over the same server… +export http_proxy="http://127.0.0.1:${APTHTTPPORT}" runtest "http://localhost:${APTHTTPPORT}" unset http_proxy diff --git a/test/integration/test-bug-818628-unreadable-source b/test/integration/test-bug-818628-unreadable-source index cddc79398..59051fb5f 100755 --- a/test/integration/test-bug-818628-unreadable-source +++ b/test/integration/test-bug-818628-unreadable-source @@ -16,6 +16,8 @@ insertpackage 'unstable' 'foo' 'amd64' '2' setupaptarchive --no-update +touch rootdir/etc/apt/sources.list.d/apt-test-unstable-deb-src.list +touch rootdir/etc/apt/sources.list.d/apt-test-unstable-deb-src.sources touch rootdir/etc/apt/apt.conf.d/unreadable.conf touch rootdir/etc/apt/preferences.d/unreadable.pref @@ -31,56 +33,61 @@ foo/unstable 2 amd64 [upgradable from: 1] N: There is 1 additional version. Please use the '-a' switch to see it" apt list --upgradable runthemall() { - local ERR1="$1" - local ERR2="$1$2" - testfailureequal "$ERR1" aptcache policy - testfailureequal "$ERR1" aptcache policy foo - testfailureequal "$ERR2" aptcache depends foo - testfailureequal "$ERR2" aptcache rdepends foo - testfailureequal "$ERR2" aptcache search foo - testfailureequal "$ERR1" apt policy - testfailureequal "$ERR1" apt policy foo - testfailureequal "$ERR2" apt depends foo - testfailureequal "$ERR2" apt rdepends foo - testfailureequal "$ERR2" apt search foo - testfailureequal "$ERR2" apt list --upgradable - testfailureequal "$ERR2" apt show foo - testfailureequal "$ERR2" aptcache show foo --no-all-versions - testfailureequal "$ERR2" aptmark auto foo - testfailureequal "$ERR2" aptmark manual foo - testfailureequal "$ERR2" aptmark auto foo + local ERR="$1" + local ERRNOTICEVER="$1${2- +N: There is 1 additional version. Please use the '-a' switch to see it}" + local ERRNOTICEREC="$1${2- +N: There is 1 additional record. Please use the '-a' switch to see it}" + testwarningmsg "$ERR" aptcache policy + testwarningmsg "$ERR" aptcache policy foo + testwarningmsg "$ERR" aptcache depends foo + testwarningmsg "$ERR" aptcache rdepends foo + testwarningmsg "$ERR" aptcache search foo + testwarningmsg "$ERR" apt policy + testwarningmsg "$ERR" apt policy foo + testwarningmsg "$ERR" apt depends foo + testwarningmsg "$ERR" apt rdepends foo + testwarningmsg "$ERR" apt search foo + testwarningmsg "$ERRNOTICEVER" apt list --upgradable + testwarningmsg "$ERRNOTICEREC" apt show foo + testwarningmsg "$ERRNOTICEREC" aptcache show foo --no-all-versions + testwarningmsg "$ERR" aptmark auto foo + testwarningmsg "$ERR" aptmark manual foo + testwarningmsg "$ERR" aptmark auto foo } echo 'Apt::Cmd::Disable-Script-Warning "true";' >> aptconfig.conf -msgmsg 'Unreadable sources file' +msgmsg 'Unreadable one-line-style sources file' chmod -r rootdir/etc/apt/sources.list.d/apt-test-unstable-deb-src.list -runthemall "E: Opening $TMPWORKINGDIRECTORY/rootdir/etc/apt/sources.list.d/apt-test-unstable-deb-src.list - ifstream::ifstream (13: Permission denied) -E: The list of sources could not be read." +runthemall "W: Unable to read $TMPWORKINGDIRECTORY/rootdir/etc/apt/sources.list.d/apt-test-unstable-deb-src.list - open (13: Permission denied)" chmod +r rootdir/etc/apt/sources.list.d/apt-test-unstable-deb-src.list +msgmsg 'Unreadable deb822-style sources file' +chmod -r rootdir/etc/apt/sources.list.d/apt-test-unstable-deb-src.sources +runthemall "W: Unable to read $TMPWORKINGDIRECTORY/rootdir/etc/apt/sources.list.d/apt-test-unstable-deb-src.sources - open (13: Permission denied)" +chmod +r rootdir/etc/apt/sources.list.d/apt-test-unstable-deb-src.sources + msgmsg 'Unreadable config file' chmod -r rootdir/etc/apt/apt.conf.d/unreadable.conf -runthemall "E: Opening configuration file ${TMPWORKINGDIRECTORY}/rootdir/etc/apt/apt.conf.d/unreadable.conf - ifstream::ifstream (13: Permission denied)" +runthemall "W: Unable to read ${TMPWORKINGDIRECTORY}/rootdir/etc/apt/apt.conf.d/unreadable.conf - open (13: Permission denied)" chmod +r rootdir/etc/apt/apt.conf.d/unreadable.conf msgmsg 'Unreadable preferences file' chmod -r rootdir/etc/apt/preferences.d/unreadable.pref -runthemall "E: Could not open file ${TMPWORKINGDIRECTORY}/rootdir/etc/apt/preferences.d/unreadable.pref - open (13: Permission denied)" +runthemall "W: Unable to read ${TMPWORKINGDIRECTORY}/rootdir/etc/apt/preferences.d/unreadable.pref - open (13: Permission denied)" chmod +r rootdir/etc/apt/preferences.d/unreadable.pref msgmsg 'Unreadable sources directory' chmod -r rootdir/etc/apt/sources.list.d -runthemall "E: Unable to read $TMPWORKINGDIRECTORY/rootdir/etc/apt/sources.list.d/ - opendir (13: Permission denied)" " -W: You may want to run apt-get update to correct these problems -E: The package cache file is corrupted" +runthemall "W: Unable to read $TMPWORKINGDIRECTORY/rootdir/etc/apt/sources.list.d/ - opendir (13: Permission denied)" "" chmod +r rootdir/etc/apt/sources.list.d msgmsg 'Unreadable config directory' chmod -r rootdir/etc/apt/apt.conf.d -runthemall "E: Unable to read ${TMPWORKINGDIRECTORY}/rootdir/etc/apt/apt.conf.d/ - opendir (13: Permission denied)" +runthemall "W: Unable to read ${TMPWORKINGDIRECTORY}/rootdir/etc/apt/apt.conf.d/ - opendir (13: Permission denied)" chmod +r rootdir/etc/apt/apt.conf.d msgmsg 'Unreadable preferences directory' chmod -r rootdir/etc/apt/preferences.d -runthemall "E: Unable to read ${TMPWORKINGDIRECTORY}/rootdir/etc/apt/preferences.d/ - opendir (13: Permission denied)" +runthemall "W: Unable to read ${TMPWORKINGDIRECTORY}/rootdir/etc/apt/preferences.d/ - opendir (13: Permission denied)" chmod +r rootdir/etc/apt/preferences.d diff --git a/test/libapt/authconf_test.cc b/test/libapt/authconf_test.cc new file mode 100644 index 000000000..10d818ec9 --- /dev/null +++ b/test/libapt/authconf_test.cc @@ -0,0 +1,223 @@ +#include <config.h> + +#include <apt-pkg/fileutl.h> +#include <apt-pkg/netrc.h> +#include <apt-pkg/strutl.h> + +#include <string> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +TEST(NetRCTest, Parsing) +{ + FileFd fd; + URI U("http://file.not/open"); + EXPECT_FALSE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + EXPECT_EQ("file.not", U.Host); + EXPECT_EQ("/open", U.Path); + + createTemporaryFile("doublesignedfile", fd, nullptr, R"apt( +machine example.netter login bar password foo +machine example.net login foo password bar + +machine example.org:90 login apt password apt +machine example.org:8080 +login +example password foobar + +machine example.org +login anonymous +password pass + +machine example.com/foo login user1 unknown token password pass1 +machine example.com/bar password pass2 login user2 + unknown token +machine example.com/user login user +machine example.netter login unused password firstentry +machine example.last/debian login debian password rules)apt"); + U = URI("http://example.net/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo", U.User); + EXPECT_EQ("bar", U.Password); + EXPECT_EQ("example.net", U.Host); + EXPECT_EQ("/foo", U.Path); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://user:pass@example.net/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("user", U.User); + EXPECT_EQ("pass", U.Password); + EXPECT_EQ("example.net", U.Host); + EXPECT_EQ("/foo", U.Path); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://example.org:90/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("apt", U.User); + EXPECT_EQ("apt", U.Password); + EXPECT_EQ("example.org", U.Host); + EXPECT_EQ(90, U.Port); + EXPECT_EQ("/foo", U.Path); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://example.org:8080/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("example", U.User); + EXPECT_EQ("foobar", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://example.net:42/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo", U.User); + EXPECT_EQ("bar", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://example.org/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("anonymous", U.User); + EXPECT_EQ("pass", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://example.com/apt"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://example.com/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("user1", U.User); + EXPECT_EQ("pass1", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://example.com/fooo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("user1", U.User); + EXPECT_EQ("pass1", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://example.com/fo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://example.com/bar"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("user2", U.User); + EXPECT_EQ("pass2", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://example.com/user"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("user", U.User); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("socks5h://example.last/debian"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("debian", U.User); + EXPECT_EQ("rules", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("socks5h://example.debian/"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("socks5h://user:pass@example.debian/"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("user", U.User); + EXPECT_EQ("pass", U.Password); +} +TEST(NetRCTest, BadFileNoMachine) +{ + FileFd fd; + createTemporaryFile("doublesignedfile", fd, nullptr, R"apt( +foo example.org login foo1 password bar +machin example.org login foo2 password bar +machine2 example.org login foo3 password bar +)apt"); + + URI U("http://example.org/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); +} +TEST(NetRCTest, BadFileEndsMachine) +{ + FileFd fd; + createTemporaryFile("doublesignedfile", fd, nullptr, R"apt( +machine example.org login foo1 password bar +machine)apt"); + + URI U("http://example.org/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo1", U.User); + EXPECT_EQ("bar", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://example.net/foo"); + EXPECT_FALSE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://foo:bar@example.net/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo", U.User); + EXPECT_EQ("bar", U.Password); +} +TEST(NetRCTest, BadFileEndsLogin) +{ + FileFd fd; + createTemporaryFile("doublesignedfile", fd, nullptr, R"apt( +machine example.org login foo1 password bar +machine example.net login)apt"); + + URI U("http://example.org/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo1", U.User); + EXPECT_EQ("bar", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://example.net/foo"); + EXPECT_FALSE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://foo:bar@example.net/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo", U.User); + EXPECT_EQ("bar", U.Password); +} +TEST(NetRCTest, BadFileEndsPassword) +{ + FileFd fd; + createTemporaryFile("doublesignedfile", fd, nullptr, R"apt( +machine example.org login foo1 password bar +machine example.net password)apt"); + + URI U("http://example.org/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo1", U.User); + EXPECT_EQ("bar", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://example.net/foo"); + EXPECT_FALSE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://foo:bar@example.net/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo", U.User); + EXPECT_EQ("bar", U.Password); +} diff --git a/test/libapt/configuration_test.cc b/test/libapt/configuration_test.cc index bdc17cbf4..bfb7144ce 100644 --- a/test/libapt/configuration_test.cc +++ b/test/libapt/configuration_test.cc @@ -1,14 +1,14 @@ #include <config.h> #include <apt-pkg/configuration.h> +#include <apt-pkg/fileutl.h> #include <string> #include <vector> #include <gtest/gtest.h> -//FIXME: Test for configuration file parsing; -// currently only integration/ tests test them implicitly +#include "file-helpers.h" TEST(ConfigurationTest,Lists) { @@ -195,3 +195,40 @@ TEST(ConfigurationTest,Merge) EXPECT_EQ("bar", Cnf.Find("option::foo")); EXPECT_EQ("", Cnf.Find("option::empty")); } +TEST(ConfigurationTest, Parsing) +{ + Configuration Cnf; + std::string tempfile; + FileFd fd; + createTemporaryFile("doublesignedfile", fd, &tempfile, R"apt( +SimpleOption "true"; +/* SimpleOption "false"; */ +Answer::Simple "42"; +# This is a comment +List::Option { "In"; "One"; "Line"; }; +// this a comment as well +List::Option2 { "Multi"; +"Line"; # inline comment + "Options"; +}; Trailing "true"; +/* Commented::Out "true"; */ +)apt"); + EXPECT_TRUE(ReadConfigFile(Cnf, tempfile)); + if (tempfile.empty() == false) + unlink(tempfile.c_str()); + EXPECT_TRUE(Cnf.FindB("SimpleOption")); + EXPECT_EQ(42, Cnf.FindI("Answer::Simple")); + EXPECT_TRUE(Cnf.Exists("List::Option")); + auto const singleline = Cnf.FindVector("List::Option"); + EXPECT_EQ(3, singleline.size()); + EXPECT_EQ("In", singleline[0]); + EXPECT_EQ("One", singleline[1]); + EXPECT_EQ("Line", singleline[2]); + auto const multiline = Cnf.FindVector("List::Option2"); + EXPECT_EQ(3, multiline.size()); + EXPECT_EQ("Multi", multiline[0]); + EXPECT_EQ("Line", multiline[1]); + EXPECT_EQ("Options", multiline[2]); + EXPECT_TRUE(Cnf.FindB("Trailing")); + EXPECT_FALSE(Cnf.Exists("Commented::Out")); +} diff --git a/test/libapt/fileutl_test.cc b/test/libapt/fileutl_test.cc index 6cc850033..a702c16ec 100644 --- a/test/libapt/fileutl_test.cc +++ b/test/libapt/fileutl_test.cc @@ -152,6 +152,19 @@ static void TestFileFd(mode_t const a_umask, mode_t const ExpectedFilePermission EXPECT_EQ(strlen(expect), f.Tell()); } #undef APT_INIT_READBACK + { + test.erase(test.length() - 1); + EXPECT_TRUE(f.Seek(0)); + EXPECT_FALSE(f.Eof()); + std::string line; + EXPECT_TRUE(f.ReadLine(line)); + EXPECT_FALSE(f.Failed()); + EXPECT_FALSE(f.Eof()); + EXPECT_EQ(line.length(), test.length()); + EXPECT_EQ(line.length() + 1, f.Tell()); + EXPECT_EQ(f.Size(), f.Tell()); + EXPECT_EQ(line, test); + } f.Close(); EXPECT_FALSE(f.IsOpen()); |