From c769cd6fdc0d675bac07d0bcdf20e7b3ac0598a9 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 25 Jul 2008 19:37:19 +0200 Subject: * merge patch that enforces stricter https server certificate checking (thanks to Arnaud Ebalard, closes: #485960) * allow per-mirror specific https settings (thanks to Arnaud Ebalard, closes: #485965) --- methods/https.cc | 59 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 12 deletions(-) (limited to 'methods') diff --git a/methods/https.cc b/methods/https.cc index b0b05a47e..e53ba1a11 100644 --- a/methods/https.cc +++ b/methods/https.cc @@ -108,6 +108,8 @@ bool HttpsMethod::Fetch(FetchItem *Itm) struct curl_slist *headers=NULL; char curl_errorstr[CURL_ERROR_SIZE]; long curl_responsecode; + URI Uri = Itm->Uri; + string remotehost = Uri.Host; // TODO: // - http::Pipeline-Depth @@ -127,23 +129,56 @@ bool HttpsMethod::Fetch(FetchItem *Itm) curl_easy_setopt(curl, CURLOPT_FAILONERROR, true); curl_easy_setopt(curl, CURLOPT_FILETIME, true); - // FIXME: https: offer various options of verification - bool peer_verify = _config->FindB("Acquire::https::Verify-Peer", false); + // SSL parameters are set by default to the common (non mirror-specific) value + // if available (or a default one) and gets overload by mirror-specific ones. + + // File containing the list of trusted CA. + string cainfo = _config->Find("Acquire::https::CaInfo",""); + string knob = "Acquire::https::"+remotehost+"::CaInfo"; + cainfo = _config->Find(knob.c_str(),cainfo.c_str()); + if(cainfo != "") + curl_easy_setopt(curl, CURLOPT_CAINFO,cainfo.c_str()); + + // Check server certificate against previous CA list ... + bool peer_verify = _config->FindB("Acquire::https::Verify-Peer",true); + knob = "Acquire::https::" + remotehost + "::Verify-Peer"; + peer_verify = _config->FindB(knob.c_str(), peer_verify); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, peer_verify); - // sslcert file + // ... and hostname against cert CN or subjectAltName + int default_verify = 2; + bool verify = _config->FindB("Acquire::https::Verify-Host",true); + knob = "Acquire::https::"+remotehost+"::Verify-Host"; + verify = _config->FindB(knob.c_str(),verify); + if (!verify) + default_verify = 0; + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, verify); + + // For client authentication, certificate file ... string pem = _config->Find("Acquire::https::SslCert",""); + knob = "Acquire::https::"+remotehost+"::SslCert"; + pem = _config->Find(knob.c_str(),pem.c_str()); if(pem != "") curl_easy_setopt(curl, CURLOPT_SSLCERT, pem.c_str()); - - // CA-Dir - string certdir = _config->Find("Acquire::https::CaPath",""); - if(certdir != "") - curl_easy_setopt(curl, CURLOPT_CAPATH, certdir.c_str()); - - // Server-verify - int verify = _config->FindI("Acquire::https::Verify-Host",2); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, verify); + + // ... and associated key. + string key = _config->Find("Acquire::https::SslKey",""); + knob = "Acquire::https::"+remotehost+"::SslKey"; + key = _config->Find(knob.c_str(),key.c_str()); + if(key != "") + curl_easy_setopt(curl, CURLOPT_SSLKEY, key.c_str()); + + // Allow forcing SSL version to SSLv3 or TLSv1 (SSLv2 is not + // supported by GnuTLS). + long final_version = CURL_SSLVERSION_DEFAULT; + string sslversion = _config->Find("Acquire::https::SslForceVersion",""); + knob = "Acquire::https::"+remotehost+"::SslForceVersion"; + sslversion = _config->Find(knob.c_str(),sslversion.c_str()); + if(sslversion == "TLSv1") + final_version = CURL_SSLVERSION_TLSv1; + else if(sslversion == "SSLv3") + final_version = CURL_SSLVERSION_SSLv3; + curl_easy_setopt(curl, CURLOPT_SSLVERSION, final_version); // cache-control if(_config->FindB("Acquire::http::No-Cache",false) == false) -- cgit v1.2.3 From 9b5d79ec68d5380f26b92cd0d9cb7af6a19ac849 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 5 Aug 2008 10:52:09 +0200 Subject: fix various -Wall warnings --- methods/http.cc | 3 ++- methods/https.cc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'methods') diff --git a/methods/http.cc b/methods/http.cc index 26d435dea..b3c791fa0 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -941,7 +941,8 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv) if (Srv->StartPos >= 0) { Res.ResumePoint = Srv->StartPos; - ftruncate(File->Fd(),Srv->StartPos); + if (ftruncate(File->Fd(),Srv->StartPos) < 0) + _error->Errno("ftruncate", _("Failed to truncate file")); } // Set the start point diff --git a/methods/https.cc b/methods/https.cc index e53ba1a11..98dfeefa1 100644 --- a/methods/https.cc +++ b/methods/https.cc @@ -249,7 +249,7 @@ bool HttpsMethod::Fetch(FetchItem *Itm) if(success != 0) { unlink(File->Name().c_str()); - _error->Error(curl_errorstr); + _error->Error("%s", curl_errorstr); Fail(); return true; } -- cgit v1.2.3 From f23153d046f014f96d442fca5b9ef6ede7fcf546 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 9 Dec 2008 14:38:10 -0800 Subject: * methods/gpgv.cc: - fix compiler warning * cmdline/apt-get.cc: - fix "apt-get source pkg=ver" if binary name != source name and show a message (LP: #202219) * apt-pkg/deb/debsystem.cc: - make strings i18n able --- methods/gpgv.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'methods') diff --git a/methods/gpgv.cc b/methods/gpgv.cc index 9f4683e6e..f3277b300 100644 --- a/methods/gpgv.cc +++ b/methods/gpgv.cc @@ -264,7 +264,7 @@ bool GPGVMethod::Fetch(FetchItem *Itm) // least one bad signature. good signatures and NoPubKey signatures // happen easily when a file is signed with multiple signatures if(GoodSigners.empty() or !BadSigners.empty()) - return _error->Error(errmsg.c_str()); + return _error->Error("%s", errmsg.c_str()); } // Just pass the raw output up, because passing it as a real data -- cgit v1.2.3 From 15d7e51550327bf49b95372f84bd0de4e896e7d7 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 30 Jan 2009 20:55:20 +0100 Subject: [ABI break] merge support for http redirects, thanks to Jeff Licquia and Anthony Towns --- methods/http.cc | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- methods/http.h | 3 +++ methods/makefile | 2 +- 3 files changed, 81 insertions(+), 2 deletions(-) (limited to 'methods') diff --git a/methods/http.cc b/methods/http.cc index b3c791fa0..1bf798da4 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -39,6 +39,7 @@ #include #include #include +#include #include // Internet stuff @@ -57,6 +58,7 @@ int HttpMethod::FailFd = -1; time_t HttpMethod::FailTime = 0; unsigned long PipelineDepth = 10; unsigned long TimeOut = 120; +bool AllowRedirect = false; bool Debug = false; URI Proxy; @@ -628,6 +630,12 @@ bool ServerState::HeaderLine(string Line) return true; } + if (stringcasecmp(Tag,"Location:") == 0) + { + Location = Val; + return true; + } + return true; } /*}}}*/ @@ -900,7 +908,9 @@ bool HttpMethod::ServerDie(ServerState *Srv) 1 - IMS hit 3 - Unrecoverable error 4 - Error with error content page - 5 - Unrecoverable non-server error (close the connection) */ + 5 - Unrecoverable non-server error (close the connection) + 6 - Try again with a new or changed URI + */ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv) { // Not Modified @@ -912,6 +922,27 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv) return 1; } + /* Redirect + * + * Note that it is only OK for us to treat all redirection the same + * because we *always* use GET, not other HTTP methods. There are + * three redirection codes for which it is not appropriate that we + * redirect. Pass on those codes so the error handling kicks in. + */ + if (AllowRedirect + && (Srv->Result > 300 && Srv->Result < 400) + && (Srv->Result != 300 // Multiple Choices + && Srv->Result != 304 // Not Modified + && Srv->Result != 306)) // (Not part of HTTP/1.1, reserved) + { + if (!Srv->Location.empty()) + { + NextURI = Srv->Location; + return 6; + } + /* else pass through for error message */ + } + /* We have a reply we dont handle. This should indicate a perm server failure */ if (Srv->Result < 200 || Srv->Result >= 300) @@ -1026,6 +1057,7 @@ bool HttpMethod::Configuration(string Message) if (pkgAcqMethod::Configuration(Message) == false) return false; + AllowRedirect = _config->FindB("Acquire::http::AllowRedirect",true); TimeOut = _config->FindI("Acquire::http::Timeout",TimeOut); PipelineDepth = _config->FindI("Acquire::http::Pipeline-Depth", PipelineDepth); @@ -1039,6 +1071,10 @@ bool HttpMethod::Configuration(string Message) /* */ int HttpMethod::Loop() { + typedef vector StringVector; + typedef vector::iterator StringVectorIterator; + map Redirected; + signal(SIGTERM,SigTerm); signal(SIGINT,SigTerm); @@ -1225,6 +1261,46 @@ int HttpMethod::Loop() break; } + // Try again with a new URL + case 6: + { + // Clear rest of response if there is content + if (Server->HaveContent) + { + File = new FileFd("/dev/null",FileFd::WriteExists); + Server->RunData(); + delete File; + File = 0; + } + + /* Detect redirect loops. No more redirects are allowed + after the same URI is seen twice in a queue item. */ + StringVector &R = Redirected[Queue->DestFile]; + bool StopRedirects = false; + if (R.size() == 0) + R.push_back(Queue->Uri); + else if (R[0] == "STOP" || R.size() > 10) + StopRedirects = true; + else + { + for (StringVectorIterator I = R.begin(); I != R.end(); I++) + if (Queue->Uri == *I) + { + R[0] = "STOP"; + break; + } + + R.push_back(Queue->Uri); + } + + if (StopRedirects == false) + Redirect(NextURI); + else + Fail(); + + break; + } + default: Fail(_("Internal error")); break; diff --git a/methods/http.h b/methods/http.h index 6753a9901..13f02ec77 100644 --- a/methods/http.h +++ b/methods/http.h @@ -99,6 +99,7 @@ struct ServerState enum {Chunked,Stream,Closes} Encoding; enum {Header, Data} State; bool Persistent; + string Location; // This is a Persistent attribute of the server itself. bool Pipeline; @@ -143,6 +144,8 @@ class HttpMethod : public pkgAcqMethod static time_t FailTime; static void SigTerm(int); + string NextURI; + public: friend class ServerState; diff --git a/methods/makefile b/methods/makefile index d9481dbcc..78bdbc96f 100644 --- a/methods/makefile +++ b/methods/makefile @@ -7,7 +7,7 @@ include ../buildlib/defaults.mak BIN := $(BIN)/methods # FIXME.. -LIB_APT_PKG_MAJOR = 4.6 +LIB_APT_PKG_MAJOR = 4.7 APT_DOMAIN := libapt-pkg$(LIB_APT_PKG_MAJOR) # The file method -- cgit v1.2.3 From cb3cc4806c6585757377dfd54b7ce880ae1f99c7 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 3 Feb 2009 10:50:32 +0100 Subject: methods/https.cc: do not unlink partial files (thanks to robbiew) --- methods/https.cc | 1 - 1 file changed, 1 deletion(-) (limited to 'methods') diff --git a/methods/https.cc b/methods/https.cc index 98dfeefa1..7c743a424 100644 --- a/methods/https.cc +++ b/methods/https.cc @@ -248,7 +248,6 @@ bool HttpsMethod::Fetch(FetchItem *Itm) // cleanup if(success != 0) { - unlink(File->Name().c_str()); _error->Error("%s", curl_errorstr); Fail(); return true; -- cgit v1.2.3 From 668ce84da00041c65cae3957d2b49786efa34276 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 3 Feb 2009 14:10:36 +0100 Subject: * methods/https.cc: - add Acquire::https::AllowRedirect support --- methods/https.cc | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'methods') diff --git a/methods/https.cc b/methods/https.cc index 7c743a424..8bf44b52a 100644 --- a/methods/https.cc +++ b/methods/https.cc @@ -208,6 +208,11 @@ bool HttpsMethod::Fetch(FetchItem *Itm) curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout); + // set redirect options and default to 10 redirects + bool AllowRedirect = _config->FindI("Acquire::https::AllowRedirect", true); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, AllowRedirect); + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10); + // debug if(_config->FindB("Debug::Acquire::https", false)) curl_easy_setopt(curl, CURLOPT_VERBOSE, true); -- cgit v1.2.3 From c5d8878d1ffe7484e049f52189a07f3847e4fda9 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 8 Apr 2009 22:42:30 +0200 Subject: * methods/gpgv.cc: - properly check for expired and revoked keys (closes: #433091) --- methods/gpgv.cc | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) (limited to 'methods') diff --git a/methods/gpgv.cc b/methods/gpgv.cc index f3277b300..150c1d315 100644 --- a/methods/gpgv.cc +++ b/methods/gpgv.cc @@ -17,13 +17,18 @@ #define GNUPGBADSIG "[GNUPG:] BADSIG" #define GNUPGNOPUBKEY "[GNUPG:] NO_PUBKEY" #define GNUPGVALIDSIG "[GNUPG:] VALIDSIG" +#define GNUPGGOODSIG "[GNUPG:] GOODSIG" +#define GNUPGKEYEXPIRED "[GNUPG:] KEYEXPIRED" +#define GNUPGREVKEYSIG "[GNUPG:] REVKEYSIG" #define GNUPGNODATA "[GNUPG:] NODATA" class GPGVMethod : public pkgAcqMethod { private: string VerifyGetSigners(const char *file, const char *outfile, - vector &GoodSigners, vector &BadSigners, + vector &GoodSigners, + vector &BadSigners, + vector &WorthlessSigners, vector &NoPubKeySigners); protected: @@ -37,6 +42,7 @@ class GPGVMethod : public pkgAcqMethod string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, vector &GoodSigners, vector &BadSigners, + vector &WorthlessSigners, vector &NoPubKeySigners) { // setup a (empty) stringstream for formating the return value @@ -179,15 +185,27 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, std::cerr << "Got NODATA! " << std::endl; BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX))); } - if (strncmp(buffer, GNUPGVALIDSIG, sizeof(GNUPGVALIDSIG)-1) == 0) + if (strncmp(buffer, GNUPGKEYEXPIRED, sizeof(GNUPGKEYEXPIRED)-1) == 0) + { + if (_config->FindB("Debug::Acquire::gpgv", false)) + std::cerr << "Got KEYEXPIRED! " << std::endl; + WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX))); + } + if (strncmp(buffer, GNUPGREVKEYSIG, sizeof(GNUPGREVKEYSIG)-1) == 0) + { + if (_config->FindB("Debug::Acquire::gpgv", false)) + std::cerr << "Got REVKEYSIG! " << std::endl; + WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX))); + } + if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0) { char *sig = buffer + sizeof(GNUPGPREFIX); - char *p = sig + sizeof("VALIDSIG"); + char *p = sig + sizeof("GOODSIG"); while (*p && isxdigit(*p)) p++; *p = 0; if (_config->FindB("Debug::Acquire::gpgv", false)) - std::cerr << "Got VALIDSIG, key ID:" << sig << std::endl; + std::cerr << "Got GOODSIG, key ID:" << sig << std::endl; GoodSigners.push_back(string(sig)); } } @@ -227,6 +245,8 @@ bool GPGVMethod::Fetch(FetchItem *Itm) string keyID; vector GoodSigners; vector BadSigners; + // a worthless signature is a expired or revoked one + vector WorthlessSigners; vector NoPubKeySigners; FetchResult Res; @@ -235,13 +255,14 @@ bool GPGVMethod::Fetch(FetchItem *Itm) // Run gpgv on file, extract contents and get the key ID of the signer string msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(), - GoodSigners, BadSigners, NoPubKeySigners); + GoodSigners, BadSigners, WorthlessSigners, + NoPubKeySigners); if (GoodSigners.empty() || !BadSigners.empty() || !NoPubKeySigners.empty()) { string errmsg; // In this case, something bad probably happened, so we just go // with what the other method gave us for an error message. - if (BadSigners.empty() && NoPubKeySigners.empty()) + if (BadSigners.empty() && WorthlessSigners.empty() && NoPubKeySigners.empty()) errmsg = msg; else { @@ -252,6 +273,13 @@ bool GPGVMethod::Fetch(FetchItem *Itm) I != BadSigners.end(); I++) errmsg += (*I + "\n"); } + if (!WorthlessSigners.empty()) + { + errmsg += _("The following signatures were invalid:\n"); + for (vector::iterator I = WorthlessSigners.begin(); + I != WorthlessSigners.end(); I++) + errmsg += (*I + "\n"); + } if (!NoPubKeySigners.empty()) { errmsg += _("The following signatures couldn't be verified because the public key is not available:\n"); -- cgit v1.2.3 From ab7f4d7ca647270ffd34b5b6c67339b0cfde51ac Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 2 Jul 2009 14:07:18 +0200 Subject: * apt-pkg/acquire-worker.cc: - show error details of failed methods * apt-pkg/contrib/fileutl.cc: - if a process aborts with signal, show signal number * methods/http.cc: - ignore SIGPIPE, we deal with EPIPE from write in HttpMethod::ServerDie() (LP: #385144) --- methods/http.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'methods') diff --git a/methods/http.cc b/methods/http.cc index 1bf798da4..006e89394 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -1316,9 +1316,11 @@ int HttpMethod::Loop() int main() { setlocale(LC_ALL, ""); + // ignore SIGPIPE, this can happen on write() if the socket + // closes the connection (this is dealt with via ServerDie()) + signal(SIGPIPE, SIG_IGN); HttpMethod Mth; - return Mth.Loop(); } -- cgit v1.2.3