]> git.saurik.com Git - apt.git/commitdiff
add test for Basic Authentication scheme
authorDavid Kalnischkies <david@kalnischkies.de>
Thu, 23 Oct 2014 08:42:18 +0000 (10:42 +0200)
committerDavid Kalnischkies <david@kalnischkies.de>
Thu, 23 Oct 2014 14:54:27 +0000 (16:54 +0200)
Git-Dch: Ignore

test/integration/framework
test/integration/test-apt-cdrom
test/integration/test-authentication-basic [new file with mode: 0755]
test/interactive-helper/aptwebserver.cc
test/libapt/strutil_test.cc

index 23ff0983b018dc22e5ee2fc218d57beccf30bc6b..fcaa4ddd348216521ea927dfe024158ab425e911 100644 (file)
@@ -944,23 +944,35 @@ signreleasefiles() {
 }
 
 webserverconfig() {
-       msgtest "Set webserver config option '${1}' to" "$2"
+       local NOCHECK=false
+       if [ "$1" = '--no-check' ]; then
+               NOCHECK=true
+               shift
+       fi
        local DOWNLOG='rootdir/tmp/download-testfile.log'
-       local STATUS='rootdir/tmp/webserverconfig.status'
+       local STATUS='downloaded/webserverconfig.status'
        rm -f "$STATUS" "$DOWNLOG"
-       if downloadfile "http://localhost:8080/_config/set/${1}/${2}" "$STATUS" > "$DOWNLOG"; then
+       local URI
+       if [ -n "$2" ]; then
+               msgtest "Set webserver config option '${1}' to" "$2"
+               URI="http://localhost:8080/_config/set/${1}/${2}"
+       else
+               msgtest 'Clear webserver config option' "${1}"
+               URI="http://localhost:8080/_config/clear/${1}"
+       fi
+       if downloadfile "$URI" "$STATUS" > "$DOWNLOG"; then
                msgpass
        else
-               cat "$DOWNLOG" "$STATUS"
+               cat "$DOWNLOG" "$STATUS" || true
                msgfail
        fi
-       testwebserverlaststatuscode '200'
+       $NOCHECK || testwebserverlaststatuscode '200'
 }
 
 rewritesourceslist() {
        local APTARCHIVE="file://$(readlink -f "${TMPWORKINGDIRECTORY}/aptarchive")"
        for LIST in $(find rootdir/etc/apt/sources.list.d/ -name 'apt-test-*.list'); do
-               sed -i $LIST -e "s#$APTARCHIVE#${1}#" -e "s#http://localhost:8080/#${1}#" -e "s#http://localhost:4433/#${1}#"
+               sed -i $LIST -e "s#$APTARCHIVE#${1}#" -e "s#http://localhost:8080/#${1}#" -e "s#https://localhost:4433/#${1}#"
        done
 }
 
@@ -1047,7 +1059,7 @@ acquire::cdrom::autodetect 0;" > rootdir/etc/apt/apt.conf.d/00cdrom
        mv "${CD}" "${CD}-unmounted"
        # we don't want the disk to be modifiable
        addtrap 'prefix' "chmod -f -R +w $PWD/rootdir/media/cdrom/dists/ $PWD/rootdir/media/cdrom-unmounted/dists/ || true;"
-       chmod -R -w rootdir/media/cdrom-unmounted/dists
+       chmod -R 555 rootdir/media/cdrom-unmounted/dists
 }
 
 downloadfile() {
@@ -1055,7 +1067,7 @@ downloadfile() {
        apthelper -o Debug::Acquire::${PROTO}=1 \
                download-file "$1" "$2" 2>&1 || true
        # only if the file exists the download was successful
-       if [ -e "$2" ]; then
+       if [ -r "$2" ]; then
                return 0
        else
                return 1
@@ -1312,8 +1324,7 @@ testwebserverlaststatuscode() {
        local STATUS='downloaded/webserverstatus-statusfile.log'
        rm -f "$DOWNLOG" "$STATUS"
        msgtest 'Test last status code from the webserver was' "$1"
-       downloadfile "http://localhost:8080/_config/find/aptwebserver::last-status-code" "$STATUS" > "$DOWNLOG"
-       if [ "$(cat "$STATUS")" = "$1" ]; then
+       if downloadfile "http://localhost:8080/_config/find/aptwebserver::last-status-code" "$STATUS" > "$DOWNLOG" && [ "$(cat "$STATUS")" = "$1" ]; then
                msgpass
        else
                echo >&2
index 4489143e4f9d2c892535359601edbc545aa481b7..3a33219fe166ec2b41c6600730c27ee25b9ae20a 100755 (executable)
@@ -21,7 +21,7 @@ echo 'Description-de: automatisch generiertes Testpaket testing=0.8.15/stable
 ' >> Translation-de
 compressfile Translation-de
 rm -f Translation-en Translation-de
-chmod -R -w .
+chmod -R 555 .
 cd - > /dev/null
 
 aptcdromlog() {
@@ -144,3 +144,8 @@ testcdromusage
 testsuccess aptget update
 testfileequal rootdir/tmp/testsuccess.output 'Reading package lists...'
 testcdromusage
+
+msgmsg 'Check that nothing touched our' 'CD-ROM'
+for file in $(find rootdir/media/cdrom-unmounted/dists); do
+       testfilestats "$file" '%U:%G:%a' '=' "${USER}:${USER}:555"
+done
diff --git a/test/integration/test-authentication-basic b/test/integration/test-authentication-basic
new file mode 100755 (executable)
index 0000000..4b0ead5
--- /dev/null
@@ -0,0 +1,106 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+
+setupenvironment
+configarchitecture 'i386'
+
+insertpackage 'unstable' 'foo' 'all' '1'
+setupaptarchive --no-update
+
+changetohttpswebserver --authorization="$(printf '%s' 'star:hunter2' | base64 )"
+
+echo 'See, when YOU type hunter2, it shows to us as *******' > aptarchive/bash
+
+testauthfailure() {
+       testfailure apthelper download-file "${1}/bash" ./downloaded/bash
+       # crappy test, but http and https output are wastely different…
+       testsuccess grep 401 rootdir/tmp/testfailure.output
+       testsuccess test ! -s ./downloaded/bash
+}
+
+testauthsuccess() {
+       testsuccess apthelper download-file "${1}/bash" ./downloaded/bash
+       testfileequal ./downloaded/bash "$(cat aptarchive/bash)"
+       testfilestats ./downloaded/bash '%U:%G:%a' '=' "${USER}:${USER}:644"
+       rm -f ./downloaded/bash
+
+       # lets see if got/retains acceptable permissions
+       if [ -n "$AUTHCONF" ]; then
+               if [ "$(id -u)" = '0' ]; then
+                       testfilestats "$AUTHCONF" '%U:%G:%a' '=' "_apt:root:600"
+               else
+                       testfilestats "$AUTHCONF" '%U:%G:%a' '=' "${USER}:${USER}:600"
+               fi
+       fi
+
+       rm -rf rootdir/var/lib/apt/lists
+       testsuccess aptget update
+       testequal 'Reading package lists...
+Building dependency tree...
+The following NEW packages will be installed:
+  foo
+0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
+Inst foo (1 unstable [all])
+Conf foo (1 unstable [all])' aptget install foo -s
+}
+
+authfile() {
+       local AUTHCONF='rootdir/etc/apt/auth.conf'
+       rm -f "$AUTHCONF"
+       printf '%s' "$1" > "$AUTHCONF"
+       chmod 600 "$AUTHCONF"
+}
+
+runtest() {
+       # unauthorized fails
+       authfile ''
+       testauthfailure "$1"
+
+       # good auth
+       authfile 'machine localhost
+login star
+password hunter2'
+       testauthsuccess "$1"
+
+       # bad auth
+       authfile 'machine localhost
+login anonymous
+password hunter2'
+       testauthfailure "$1"
+
+       # 2 stanzas: unmatching + good auth
+       authfile 'machine debian.org
+login debian
+password jessie
+
+machine localhost
+login star
+password hunter2'
+       testauthsuccess "$1"
+}
+
+msgmsg 'server basic auth'
+rewritesourceslist 'http://localhost:8080'
+runtest 'http://localhost:8080'
+rewritesourceslist 'https://localhost:4433'
+runtest 'https://localhost:4433'
+rewritesourceslist 'http://localhost:8080'
+
+msgmsg 'proxy to server basic auth'
+webserverconfig 'aptwebserver::request::absolute' 'uri'
+export http_proxy='http://localhost:8080'
+runtest 'http://localhost:8080'
+unset http_proxy
+
+msgmsg 'proxy basic auth to server basic auth'
+webserverconfig 'aptwebserver::proxy-authorization' "$(printf 'moon:deer2' | base64)"
+export http_proxy='http://moon:deer2@localhost:8080'
+runtest 'http://localhost:8080'
+
+msgmsg 'proxy basic auth to server'
+authfile ''
+webserverconfig 'aptwebserver::authorization' ''
+testauthsuccess 'http://localhost:8080'
index 34476e1af44452753338e0320e6f07ea46b7b7f2..7474cf1486e8ee4b50a031a4adf75d694e145a6b 100644 (file)
@@ -157,9 +157,8 @@ static bool sendData(int const client, std::string const &data)             /*{{{*/
 }
                                                                        /*}}}*/
 static void sendError(int const client, int const httpcode, std::string const &request,/*{{{*/
-              bool content, std::string const &error = "")
+              bool content, std::string const &error = "", std::list<std::string> headers = std::list<std::string>())
 {
-   std::list<std::string> headers;
    std::string response("<html><head><title>");
    response.append(httpcodeToStr(httpcode)).append("</title></head>");
    response.append("<body><h1>").append(httpcodeToStr(httpcode)).append("</h1>");
@@ -367,22 +366,88 @@ static bool parseFirstLine(int const client, std::string const &request,/*{{{*/
 
    // Proxies require absolute uris, so this is a simple proxy-fake option
    std::string const absolute = _config->Find("aptwebserver::request::absolute", "uri,path");
-   if (strncmp(host.c_str(), filename.c_str(), host.length()) == 0)
+   if (strncmp(host.c_str(), filename.c_str(), host.length()) == 0 && APT::String::Startswith(filename, "/_config/") == false)
    {
       if (absolute.find("uri") == std::string::npos)
       {
         sendError(client, 400, request, sendContent, "Request is absoluteURI, but configured to not accept that");
         return false;
       }
+
       // strip the host from the request to make it an absolute path
       filename.erase(0, host.length());
+
+      std::string const authConf = _config->Find("aptwebserver::proxy-authorization", "");
+      std::string auth = LookupTag(request, "Proxy-Authorization", "");
+      if (authConf.empty() != auth.empty())
+      {
+        if (auth.empty())
+           sendError(client, 407, request, sendContent, "Proxy requires authentication");
+        else
+           sendError(client, 407, request, sendContent, "Client wants to authenticate to proxy, but proxy doesn't need it");
+       return false;
+      }
+      if (authConf.empty() == false)
+      {
+        char const * const basic = "Basic ";
+        if (strncmp(auth.c_str(), basic, strlen(basic)) == 0)
+        {
+           auth.erase(0, strlen(basic));
+           if (auth != authConf)
+           {
+              sendError(client, 407, request, sendContent, "Proxy-Authentication doesn't match");
+              return false;
+           }
+        }
+        else
+        {
+           std::list<std::string> headers;
+           headers.push_back("Proxy-Authenticate: Basic");
+           sendError(client, 407, request, sendContent, "Unsupported Proxy-Authentication Scheme", headers);
+           return false;
+        }
+      }
    }
-   else if (absolute.find("path") == std::string::npos)
+   else if (absolute.find("path") == std::string::npos && APT::String::Startswith(filename, "/_config/") == false)
    {
       sendError(client, 400, request, sendContent, "Request is absolutePath, but configured to not accept that");
       return false;
    }
 
+   if (APT::String::Startswith(filename, "/_config/") == false)
+   {
+      std::string const authConf = _config->Find("aptwebserver::authorization", "");
+      std::string auth = LookupTag(request, "Authorization", "");
+      if (authConf.empty() != auth.empty())
+      {
+        if (auth.empty())
+           sendError(client, 401, request, sendContent, "Server requires authentication");
+        else
+           sendError(client, 401, request, sendContent, "Client wants to authenticate to server, but server doesn't need it");
+        return false;
+      }
+      if (authConf.empty() == false)
+      {
+        char const * const basic = "Basic ";
+        if (strncmp(auth.c_str(), basic, strlen(basic)) == 0)
+        {
+           auth.erase(0, strlen(basic));
+           if (auth != authConf)
+           {
+              sendError(client, 401, request, sendContent, "Authentication doesn't match");
+              return false;
+           }
+        }
+        else
+        {
+           std::list<std::string> headers;
+           headers.push_back("WWW-Authenticate: Basic");
+           sendError(client, 401, request, sendContent, "Unsupported Authentication Scheme", headers);
+           return false;
+        }
+      }
+   }
+
    size_t paramspos = filename.find('?');
    if (paramspos != std::string::npos)
    {
@@ -656,6 +721,8 @@ int main(int const argc, const char * argv[])
    CommandLine::Args Args[] = {
       {0, "port", "aptwebserver::port", CommandLine::HasArg},
       {0, "request-absolute", "aptwebserver::request::absolute", CommandLine::HasArg},
+      {0, "authorization", "aptwebserver::authorization", CommandLine::HasArg},
+      {0, "proxy-authorization", "aptwebserver::proxy-authorization", CommandLine::HasArg},
       {'c',"config-file",0,CommandLine::ConfigFile},
       {'o',"option",0,CommandLine::ArbItem},
       {0,0,0,0}
index 8dd9114ecb122e8631daf0f6cd5cc64e0a5f47d0..494159c8779c96831dc5b485d6bd921b37a22cd1 100644 (file)
@@ -85,7 +85,7 @@ TEST(StrUtilTest,EndsWith)
    EXPECT_FALSE(Endswith("abcd", "x"));
    EXPECT_FALSE(Endswith("abcd", "abcndefg"));
 }
-TEST(StrUtilTest,StartWith)
+TEST(StrUtilTest,StartsWith)
 {
    using APT::String::Startswith;
    EXPECT_TRUE(Startswith("abcd", "a"));
@@ -129,3 +129,17 @@ TEST(StrUtilTest,SubstVar)
    EXPECT_EQ(" bb a bb a bb a bb ", SubstVar(" aaa a aaa a aaa a aaa ", "aaa", "bb"));
 
 }
+TEST(StrUtilTest,Base64Encode)
+{
+   EXPECT_EQ("QWxhZGRpbjpvcGVuIHNlc2FtZQ==", Base64Encode("Aladdin:open sesame"));
+   EXPECT_EQ("cGxlYXN1cmUu", Base64Encode("pleasure."));
+   EXPECT_EQ("bGVhc3VyZS4=", Base64Encode("leasure."));
+   EXPECT_EQ("ZWFzdXJlLg==", Base64Encode("easure."));
+   EXPECT_EQ("YXN1cmUu", Base64Encode("asure."));
+   EXPECT_EQ("c3VyZS4=", Base64Encode("sure."));
+   EXPECT_EQ("dXJlLg==", Base64Encode("ure."));
+   EXPECT_EQ("cmUu", Base64Encode("re."));
+   EXPECT_EQ("ZS4=", Base64Encode("e."));
+   EXPECT_EQ("Lg==", Base64Encode("."));
+   EXPECT_EQ("", Base64Encode(""));
+}