summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Kalnischkies <david@kalnischkies.de>2017-07-07 16:24:21 +0200
committerDavid Kalnischkies <david@kalnischkies.de>2017-07-26 19:09:04 +0200
commitea408c560ed85bb4ef7cf8f72f8463653501332c (patch)
tree30dcfc690db0353e786cc9873b6e11803a157611
parent51751106976b1c6afa8f7991790db87b239fcc84 (diff)
reimplement and document auth.conf
We have support for an netrc-like auth.conf file since 0.7.25 (closing 518473), but it was never documented in apt that it even exists and netrc seems to have fallen out of usage as a manpage for it no longer exists making the feature even more arcane. On top of that the code was a bit of a mess (as it is written in c-style) and as a result the matching of machine tokens to URIs also a bit strange by checking for less specific matches (= without path) first. We now do a single pass over the stanzas. In practice early adopters of the undocumented implementation will not really notice the differences and the 'new' behaviour is simpler to document and more usual for an apt user. Closes: #811181
-rw-r--r--CMake/config.h.in1
-rw-r--r--apt-pkg/contrib/netrc.cc296
-rw-r--r--apt-pkg/contrib/netrc.h7
-rw-r--r--doc/CMakeLists.txt1
-rw-r--r--doc/apt_auth.conf.5.xml132
-rw-r--r--methods/aptmethod.h19
-rw-r--r--methods/basehttp.cc2
-rw-r--r--methods/basehttp.h2
-rw-r--r--methods/curl.cc2
-rw-r--r--methods/ftp.cc3
-rw-r--r--methods/http.cc5
-rwxr-xr-xtest/integration/test-authentication-basic1
-rw-r--r--test/libapt/authconf_test.cc223
13 files changed, 499 insertions, 195 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/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/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_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/methods/aptmethod.h b/methods/aptmethod.h
index 04858e29d..a9af63fb7 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>
@@ -42,6 +43,24 @@ public:
return true;
}
+ bool MaybeAddAuthTo(URI &uri)
+ {
+ if (uri.User.empty() == false || uri.Password.empty() == false)
+ return true;
+ auto const netrc = _config->FindFile("Dir::Etc::netrc");
+ if (netrc.empty() == true)
+ return true;
+ // ignore errors with opening the auth file as it doesn't need to exist
+ _error->PushToStack();
+ FileFd authconf(netrc, FileFd::ReadOnly);
+ _error->RevertToStack();
+ if (authconf.IsOpen() == false)
+ return true;
+ if (authconf.Seek(0) == false)
+ return false;
+ return MaybeAddAuth(authconf, uri);
+ }
+
bool CalculateHashes(FetchItem const * const Itm, FetchResult &Res) const APT_NONNULL(2)
{
Hashes Hash(Itm->ExpectedHashes);
diff --git a/methods/basehttp.cc b/methods/basehttp.cc
index cc5039c75..03409a8d4 100644
--- a/methods/basehttp.cc
+++ b/methods/basehttp.cc
@@ -845,7 +845,7 @@ 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) /*{{{*/
{
if (std::find(methodNames.begin(), methodNames.end(), "tor") != methodNames.end() &&
Proxy.User == "apt-transport-tor" && Proxy.Password.empty())
diff --git a/methods/basehttp.h b/methods/basehttp.h
index 7000e7b89..d2e085968 100644
--- a/methods/basehttp.h
+++ b/methods/basehttp.h
@@ -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..c5c4001fa 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>
@@ -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/http.cc b/methods/http.cc
index db4542981..f0cd77139 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>
@@ -350,7 +349,7 @@ bool UnwrapHTTPConnect(std::string Host, int Port, URI Proxy, std::unique_ptr<Me
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 +930,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/test/integration/test-authentication-basic b/test/integration/test-authentication-basic
index 3bfd076ce..e724e243e 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
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);
+}