mirror of
https://github.com/NixOS/nix.git
synced 2024-11-25 08:12:29 +00:00
Ensure error messages don't leak private key
Since #8766, invalid base64 is rendered in errors, but we don't actually want to show this in the case of an invalid private keys. Co-Authored-By: Eelco Dolstra <edolstra@gmail.com>
This commit is contained in:
parent
d0c351bf43
commit
2b6b03d8df
@ -583,7 +583,13 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||||||
std::string re = R"(Good "git" signature for \* with .* key SHA256:[)";
|
std::string re = R"(Good "git" signature for \* with .* key SHA256:[)";
|
||||||
for (const fetchers::PublicKey & k : publicKeys){
|
for (const fetchers::PublicKey & k : publicKeys){
|
||||||
// Calculate sha256 fingerprint from public key and escape the regex symbol '+' to match the key literally
|
// Calculate sha256 fingerprint from public key and escape the regex symbol '+' to match the key literally
|
||||||
auto fingerprint = trim(hashString(HashAlgorithm::SHA256, base64Decode(k.key)).to_string(nix::HashFormat::Base64, false), "=");
|
std::string keyDecoded;
|
||||||
|
try {
|
||||||
|
keyDecoded = base64Decode(k.key);
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace({}, "while decoding public key '%s' used for git signature", k.key);
|
||||||
|
}
|
||||||
|
auto fingerprint = trim(hashString(HashAlgorithm::SHA256, keyDecoded).to_string(nix::HashFormat::Base64, false), "=");
|
||||||
auto escaped_fingerprint = std::regex_replace(fingerprint, std::regex("\\+"), "\\+" );
|
auto escaped_fingerprint = std::regex_replace(fingerprint, std::regex("\\+"), "\\+" );
|
||||||
re += "(" + escaped_fingerprint + ")";
|
re += "(" + escaped_fingerprint + ")";
|
||||||
}
|
}
|
||||||
|
@ -159,8 +159,9 @@ static Machine parseBuilderLine(const std::set<std::string> & defaultSystems, co
|
|||||||
const auto & str = tokens[fieldIndex];
|
const auto & str = tokens[fieldIndex];
|
||||||
try {
|
try {
|
||||||
base64Decode(str);
|
base64Decode(str);
|
||||||
} catch (const Error & e) {
|
} catch (FormatError & e) {
|
||||||
throw FormatError("bad machine specification: a column #%lu in a row: '%s' is not valid base64 string: %s", fieldIndex, line, e.what());
|
e.addTrace({}, "while parsing machine specification at a column #%lu in a row: '%s'", fieldIndex, line);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
@ -7,6 +7,16 @@
|
|||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
static std::string parsePublicHostKey(std::string_view host, std::string_view sshPublicHostKey)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return base64Decode(sshPublicHostKey);
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace({}, "while decoding ssh public host key for host '%s'", host);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SSHMaster::SSHMaster(
|
SSHMaster::SSHMaster(
|
||||||
std::string_view host,
|
std::string_view host,
|
||||||
std::string_view keyFile,
|
std::string_view keyFile,
|
||||||
@ -15,7 +25,7 @@ SSHMaster::SSHMaster(
|
|||||||
: host(host)
|
: host(host)
|
||||||
, fakeSSH(host == "localhost")
|
, fakeSSH(host == "localhost")
|
||||||
, keyFile(keyFile)
|
, keyFile(keyFile)
|
||||||
, sshPublicHostKey(sshPublicHostKey)
|
, sshPublicHostKey(parsePublicHostKey(host, sshPublicHostKey))
|
||||||
, useMaster(useMaster && !fakeSSH)
|
, useMaster(useMaster && !fakeSSH)
|
||||||
, compress(compress)
|
, compress(compress)
|
||||||
, logFD(logFD)
|
, logFD(logFD)
|
||||||
@ -39,7 +49,7 @@ void SSHMaster::addCommonSSHOpts(Strings & args)
|
|||||||
std::filesystem::path fileName = state->tmpDir->path() / "host-key";
|
std::filesystem::path fileName = state->tmpDir->path() / "host-key";
|
||||||
auto p = host.rfind("@");
|
auto p = host.rfind("@");
|
||||||
std::string thost = p != std::string::npos ? std::string(host, p + 1) : host;
|
std::string thost = p != std::string::npos ? std::string(host, p + 1) : host;
|
||||||
writeFile(fileName.string(), thost + " " + base64Decode(sshPublicHostKey) + "\n");
|
writeFile(fileName.string(), thost + " " + sshPublicHostKey + "\n");
|
||||||
args.insert(args.end(), {"-oUserKnownHostsFile=" + fileName.string()});
|
args.insert(args.end(), {"-oUserKnownHostsFile=" + fileName.string()});
|
||||||
}
|
}
|
||||||
if (compress)
|
if (compress)
|
||||||
|
@ -14,6 +14,9 @@ private:
|
|||||||
const std::string host;
|
const std::string host;
|
||||||
bool fakeSSH;
|
bool fakeSSH;
|
||||||
const std::string keyFile;
|
const std::string keyFile;
|
||||||
|
/**
|
||||||
|
* Raw bytes, not Base64 encoding.
|
||||||
|
*/
|
||||||
const std::string sshPublicHostKey;
|
const std::string sshPublicHostKey;
|
||||||
const bool useMaster;
|
const bool useMaster;
|
||||||
const bool compress;
|
const bool compress;
|
||||||
|
@ -245,7 +245,12 @@ Hash::Hash(std::string_view rest, HashAlgorithm algo, bool isSRI)
|
|||||||
}
|
}
|
||||||
|
|
||||||
else if (isSRI || rest.size() == base64Len()) {
|
else if (isSRI || rest.size() == base64Len()) {
|
||||||
auto d = base64Decode(rest);
|
std::string d;
|
||||||
|
try {
|
||||||
|
d = base64Decode(rest);
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace({}, "While decoding hash '%s'", rest);
|
||||||
|
}
|
||||||
if (d.size() != hashSize)
|
if (d.size() != hashSize)
|
||||||
throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", rest);
|
throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", rest);
|
||||||
assert(hashSize);
|
assert(hashSize);
|
||||||
|
@ -14,17 +14,25 @@ BorrowedCryptoValue BorrowedCryptoValue::parse(std::string_view s)
|
|||||||
return {s.substr(0, colon), s.substr(colon + 1)};
|
return {s.substr(0, colon), s.substr(colon + 1)};
|
||||||
}
|
}
|
||||||
|
|
||||||
Key::Key(std::string_view s)
|
Key::Key(std::string_view s, bool sensitiveValue)
|
||||||
{
|
{
|
||||||
auto ss = BorrowedCryptoValue::parse(s);
|
auto ss = BorrowedCryptoValue::parse(s);
|
||||||
|
|
||||||
name = ss.name;
|
name = ss.name;
|
||||||
key = ss.payload;
|
key = ss.payload;
|
||||||
|
|
||||||
if (name == "" || key == "")
|
try {
|
||||||
throw Error("key is corrupt");
|
if (name == "" || key == "")
|
||||||
|
throw FormatError("key is corrupt");
|
||||||
|
|
||||||
key = base64Decode(key);
|
key = base64Decode(key);
|
||||||
|
} catch (Error & e) {
|
||||||
|
std::string extra;
|
||||||
|
if (!sensitiveValue)
|
||||||
|
extra = fmt(" with raw value '%s'", key);
|
||||||
|
e.addTrace({}, "while decoding key named '%s'%s", name, extra);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Key::to_string() const
|
std::string Key::to_string() const
|
||||||
@ -33,7 +41,7 @@ std::string Key::to_string() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
SecretKey::SecretKey(std::string_view s)
|
SecretKey::SecretKey(std::string_view s)
|
||||||
: Key(s)
|
: Key{s, true}
|
||||||
{
|
{
|
||||||
if (key.size() != crypto_sign_SECRETKEYBYTES)
|
if (key.size() != crypto_sign_SECRETKEYBYTES)
|
||||||
throw Error("secret key is not valid");
|
throw Error("secret key is not valid");
|
||||||
@ -66,7 +74,7 @@ SecretKey SecretKey::generate(std::string_view name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
PublicKey::PublicKey(std::string_view s)
|
PublicKey::PublicKey(std::string_view s)
|
||||||
: Key(s)
|
: Key{s, false}
|
||||||
{
|
{
|
||||||
if (key.size() != crypto_sign_PUBLICKEYBYTES)
|
if (key.size() != crypto_sign_PUBLICKEYBYTES)
|
||||||
throw Error("public key is not valid");
|
throw Error("public key is not valid");
|
||||||
@ -83,7 +91,12 @@ bool PublicKey::verifyDetached(std::string_view data, std::string_view sig) cons
|
|||||||
|
|
||||||
bool PublicKey::verifyDetachedAnon(std::string_view data, std::string_view sig) const
|
bool PublicKey::verifyDetachedAnon(std::string_view data, std::string_view sig) const
|
||||||
{
|
{
|
||||||
auto sig2 = base64Decode(sig);
|
std::string sig2;
|
||||||
|
try {
|
||||||
|
sig2 = base64Decode(sig);
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace({}, "while decoding signature '%s'", sig);
|
||||||
|
}
|
||||||
if (sig2.size() != crypto_sign_BYTES)
|
if (sig2.size() != crypto_sign_BYTES)
|
||||||
throw Error("signature is not valid");
|
throw Error("signature is not valid");
|
||||||
|
|
||||||
|
@ -31,15 +31,19 @@ struct Key
|
|||||||
std::string name;
|
std::string name;
|
||||||
std::string key;
|
std::string key;
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct Key from a string in the format
|
|
||||||
* ‘<name>:<key-in-base64>’.
|
|
||||||
*/
|
|
||||||
Key(std::string_view s);
|
|
||||||
|
|
||||||
std::string to_string() const;
|
std::string to_string() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct Key from a string in the format
|
||||||
|
* ‘<name>:<key-in-base64>’.
|
||||||
|
*
|
||||||
|
* @param sensitiveValue Avoid displaying the raw Base64 in error
|
||||||
|
* messages to avoid leaking private keys.
|
||||||
|
*/
|
||||||
|
Key(std::string_view s, bool sensitiveValue);
|
||||||
|
|
||||||
Key(std::string_view name, std::string && key)
|
Key(std::string_view name, std::string && key)
|
||||||
: name(name), key(std::move(key)) { }
|
: name(name), key(std::move(key)) { }
|
||||||
};
|
};
|
||||||
|
@ -244,7 +244,7 @@ std::string base64Decode(std::string_view s)
|
|||||||
|
|
||||||
char digit = base64DecodeChars[(unsigned char) c];
|
char digit = base64DecodeChars[(unsigned char) c];
|
||||||
if (digit == npos)
|
if (digit == npos)
|
||||||
throw Error("invalid character in Base64 string: '%c'", c);
|
throw FormatError("invalid character in Base64 string: '%c'", c);
|
||||||
|
|
||||||
bits += 6;
|
bits += 6;
|
||||||
d = d << 6 | digit;
|
d = d << 6 | digit;
|
||||||
|
@ -172,9 +172,13 @@ constexpr char treeNull[] = " ";
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base64 encoding/decoding.
|
* Encode arbitrary bytes as Base64.
|
||||||
*/
|
*/
|
||||||
std::string base64Encode(std::string_view s);
|
std::string base64Encode(std::string_view s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode arbitrary bytes to Base64.
|
||||||
|
*/
|
||||||
std::string base64Decode(std::string_view s);
|
std::string base64Decode(std::string_view s);
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#include "tests/nix_api_expr.hh"
|
#include "tests/nix_api_expr.hh"
|
||||||
#include "tests/string_callback.hh"
|
#include "tests/string_callback.hh"
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
namespace nixC {
|
namespace nixC {
|
||||||
|
Loading…
Reference in New Issue
Block a user