aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Weingärtner <timo@tiwe.de>2018-01-11 21:45:50 +0100
committerTimo Weingärtner <timo@tiwe.de>2018-01-11 21:45:50 +0100
commit7fc55df2837845374888318958794049ecbcf9f6 (patch)
tree3da424b7815cdc884e2eee375634bd943d6d54f3
parent5b2392b2684a31faeb174affd1a6f518c44f146a (diff)
parentd731f74d7ccf56874953c395bcfcc93efdd70f00 (diff)
downloadssh-agent-filter-7fc55df2837845374888318958794049ecbcf9f6.tar.gz
Merge tag '0.5' into debian
0.5
-rw-r--r--Makefile5
-rwxr-xr-xafssh3
-rw-r--r--changelog55
-rw-r--r--ssh-agent-filter.C74
-rwxr-xr-xtests104
-rw-r--r--version.h2
6 files changed, 234 insertions, 9 deletions
diff --git a/Makefile b/Makefile
index b2e05ec..9c6a8a4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 2013-2016 Timo Weingärtner <timo@tiwe.de>
+# Copyright (C) 2013-2018 Timo Weingärtner <timo@tiwe.de>
#
# This file is part of ssh-agent-filter.
#
@@ -36,6 +36,9 @@ ssh-agent-filter.o: ssh-agent-filter.C rfc4251.H ssh-agent.h version.h
rfc4251.o: rfc4251.C rfc4251.H
rfc4251_gmp.o: rfc4251_gmp.C rfc4251.H
+test:
+ PATH="$$(pwd):$$PATH" ./tests
+
version.h:
test ! -d .git || git describe | sed 's/^.*$$/#define SSH_AGENT_FILTER_VERSION "ssh-agent-filter \0"/' > $@
diff --git a/afssh b/afssh
index c482aea..3c715e3 100755
--- a/afssh
+++ b/afssh
@@ -52,7 +52,8 @@ fi
declare -a agent_filter_args
if [ -x "${BASH_SOURCE%/*}/ssh-agent-filter" ]; then
- SAF=$(readlink -f "${BASH_SOURCE%/*}/ssh-agent-filter")
+ type realpath >/dev/null 2>&1 || realpath () { readlink -f "$@"; }
+ SAF=$(realpath "${BASH_SOURCE%/*}/ssh-agent-filter")
else
SAF=$(which ssh-agent-filter)
fi
diff --git a/changelog b/changelog
index 8a06f55..5ae2897 100644
--- a/changelog
+++ b/changelog
@@ -1,3 +1,58 @@
+commit 3b9460a74b51119e15e0d57dafb2e0c66326890a
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2018-01-08 21:08:09 +0100
+
+ update copyright
+
+commit bae7e0329adf50af874bf323cefc9e54c2cd00a0
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2018-01-08 20:44:32 +0100
+
+ add tests using shunit2
+
+commit c97c3087ad393eef928f961869b0c5e400f8a66f
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2018-01-06 12:55:28 +0100
+
+ add dissection for cert signing requests
+
+commit 6b20192d53aea20ea5ba9725aebe2b7c7d8d8875
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2017-12-27 16:54:54 +0100
+
+ follow API change in nettle 3.4
+
+ Closes: 884400
+
+commit 3944f8c3b52e23e7e617fbfa24307ce8c07757e6
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2016-10-11 16:42:02 +0200
+
+ fixup! afssh: use realpath instead of readlink -f, with fallback
+
+commit 2f994963899ca8e039dd0aa38884693a9928e116 (tiwe/master)
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2016-10-11 14:25:39 +0200
+
+ afssh: use realpath instead of readlink -f, with fallback
+
+ this add compatibility for some BSD systems which don't have -f to readlink
+ while retaining compatibility with GNU systems without a readlink command
+
+ Thanks: Konstantinos Koukopoulos <kouk@transifex.com>
+
+commit f8e42e89e3404f91d397e325c94af6494786e90f
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2016-10-09 12:31:27 +0200
+
+ avoid warning about unused result of chdir()
+
+commit d765ef1acb318e1b97481805b66f7b45f8f08f41 (tag: 0.4.2)
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2016-08-27 22:51:46 +0200
+
+ release 0.4.2
+
commit 7152b927e22ef602011f8acf865c3cafc113c502
Author: Timo Weingärtner <timo@tiwe.de>
Date: 2016-08-27 22:47:48 +0200
diff --git a/ssh-agent-filter.C b/ssh-agent-filter.C
index 2878678..74b15ab 100644
--- a/ssh-agent-filter.C
+++ b/ssh-agent-filter.C
@@ -1,7 +1,7 @@
/*
* ssh-agent-filter.C -- filtering proxy for ssh-agent meant to be forwarded to untrusted servers
*
- * Copyright (C) 2013-2016 Timo Weingärtner <timo@tiwe.de>
+ * Copyright (C) 2013-2018 Timo Weingärtner <timo@tiwe.de>
*
* This file is part of ssh-agent-filter.
*
@@ -110,18 +110,18 @@ string md5_hex (string const & s) {
md5_update(&ctx, s.size(), reinterpret_cast<uint8_t const *>(s.data()));
uint8_t bin[MD5_DIGEST_SIZE];
md5_digest(&ctx, MD5_DIGEST_SIZE, bin);
- uint8_t hex[BASE16_ENCODE_LENGTH(MD5_DIGEST_SIZE)];
+ char hex[BASE16_ENCODE_LENGTH(MD5_DIGEST_SIZE)];
base16_encode_update(hex, MD5_DIGEST_SIZE, bin);
- return {reinterpret_cast<char const *>(hex), sizeof(hex)};
+ return {hex, sizeof(hex)};
}
string base64_encode (string const & s) {
struct base64_encode_ctx ctx;
base64_encode_init(&ctx);
- uint8_t b64[BASE64_ENCODE_LENGTH(s.size())];
+ char b64[BASE64_ENCODE_LENGTH(s.size())];
auto len = base64_encode_update(&ctx, b64, s.size(), reinterpret_cast<uint8_t const *>(s.data()));
len += base64_encode_final(&ctx, b64 + len);
- return {reinterpret_cast<char const *>(b64), len};
+ return {b64, len};
}
void cloexec (int fd) {
@@ -329,6 +329,65 @@ bool confirm (string const & question) {
}
}
+bool dissect_auth_data_ssh_cert (rfc4251::string const & data, string & request_description) try {
+ io::stream<io::array_source> datastream{data.data(), data.size()};
+ arm(datastream);
+
+ // Format specified in https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=1.13
+ rfc4251::string keytype{datastream};
+ std::string keytype_str{keytype};
+ {
+ // check for and remove suffix to get the base keytype
+ std::string const suffix{"-cert-v01@openssh.com"};
+ if (keytype_str.length() <= suffix.length())
+ return false;
+ auto suffix_start = keytype_str.end() - suffix.length();
+ if (!std::equal(suffix.begin(), suffix.end(), suffix_start))
+ return false;
+ keytype_str.erase(suffix_start, keytype_str.end());
+ }
+ rfc4251::string nonce{datastream};
+ std::ostringstream key_to_be_signed{};
+ if (keytype_str == "ssh-rsa") {
+ rfc4251::string e{datastream};
+ rfc4251::string n{datastream};
+ key_to_be_signed << rfc4251::string{keytype_str} << e << n;
+ } else if (keytype_str == "ssh-dss") {
+ rfc4251::string p{datastream};
+ rfc4251::string q{datastream};
+ rfc4251::string g{datastream};
+ rfc4251::string y{datastream};
+ key_to_be_signed << rfc4251::string{keytype_str} << p << q << g << y;
+ } else if (keytype_str == "ecdsa-sha2-nistp256"
+ || keytype_str == "ecdsa-sha2-nistp384"
+ || keytype_str == "ecdsa-sha2-nistp521") {
+ rfc4251::string curve{datastream};
+ rfc4251::string public_key{datastream};
+ key_to_be_signed << rfc4251::string{keytype_str} << curve << public_key;
+ } else if (keytype_str == "ssh-ed25519") {
+ rfc4251::string pk{datastream};
+ key_to_be_signed << rfc4251::string{keytype_str} << pk;
+ } else {
+ return false;
+ }
+ rfc4251::uint64 serial{datastream};
+ rfc4251::uint32 type{datastream};
+ rfc4251::string key_id{datastream};
+ rfc4251::string valid_principals{datastream};
+ rfc4251::uint64 valid_after{datastream};
+ rfc4251::uint64 valid_before{datastream};
+ rfc4251::string critical_options{datastream};
+ rfc4251::string extensions{datastream};
+ rfc4251::string reserved{datastream};
+ rfc4251::string signature_key{datastream};
+
+ request_description = "The request is for a certificate signature on key " + base64_encode(key_to_be_signed.str()) + ".";
+
+ return true;
+} catch (...) {
+ return false;
+}
+
bool dissect_auth_data_ssh (rfc4251::string const & data, string & request_description) try {
io::stream<io::array_source> datastream{data.data(), data.size()};
arm(datastream);
@@ -448,6 +507,8 @@ rfc4251::string handle_request (rfc4251::string const & r) {
string request_description;
bool dissect_ok{false};
if (!dissect_ok)
+ dissect_ok = dissect_auth_data_ssh_cert(data, request_description);
+ if (!dissect_ok)
dissect_ok = dissect_auth_data_ssh(data, request_description);
if (!dissect_ok)
request_description = "The request format is unknown.";
@@ -543,8 +604,9 @@ int main (int const argc, char const * const * const argv) {
exit(EX_OK);
}
+ // the following stuff is optional, so we don't do error checking
setsid();
- chdir("/");
+ static_cast<void>(chdir("/"));
int devnull = open("/dev/null", O_RDWR);
dup2(devnull, 0);
dup2(devnull, 1);
diff --git a/tests b/tests
new file mode 100755
index 0000000..d47daf1
--- /dev/null
+++ b/tests
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+# tests for ssh-agent-filter
+#
+# Copyright (C) 2018 Timo Weingärtner <timo@tiwe.de>
+#
+# This file is part of ssh-agent-filter.
+#
+# ssh-agent-filter is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# ssh-agent-filter is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with ssh-agent-filter. If not, see <http://www.gnu.org/licenses/>.
+
+oneTimeSetUp () {
+ set -e
+
+ # prepare keys
+ ( cd "$SHUNIT_TMPDIR"; ssh-keygen -q -t ed25519 -N '' -C key0 -f key0 )
+ ( cd "$SHUNIT_TMPDIR"; ssh-keygen -q -t ed25519 -N '' -C key1 -f key1 )
+
+ # prepare agent
+ eval "$(ssh-agent)"
+
+ ( cd "$SHUNIT_TMPDIR"; ssh-add key0 key1 )
+
+ # delete private keys from file system, they are in the agent now
+ ( cd "$SHUNIT_TMPDIR"; rm key0 key1 )
+
+ set +e
+}
+
+oneTimeTearDown () {
+ [ -z "$SSH_AGENT_PID" ] || kill "$SSH_AGENT_PID"
+}
+
+with_saf_in_tmp () {
+ set -e
+ cd "$SHUNIT_TMPDIR"
+ unset SSH_AGENT_PID
+ eval "$(ssh-agent-filter "$@")" > /dev/null
+ trap 'kill "$SSH_AGENT_PID"' EXIT
+}
+
+produce_filtered_list () (
+ with_saf_in_tmp "$@"
+ ssh-add -L
+)
+
+test_list_filter () {
+ reference_out=$(ssh-add -L | grep ' key0$')
+
+ # sanity check: unfiltered shold be different from filtered
+ assertNotSame "$reference_out" "$(ssh-add -L)"
+
+ assertSame "$reference_out" "$(produce_filtered_list --comment key0)"
+ assertSame "$reference_out" "$(produce_filtered_list --comment-confirmed key0)"
+
+ key0_md5=$(cut -d\ -f2 "$SHUNIT_TMPDIR/key0.pub" | base64 -d | md5sum - | cut -d\ -f1)
+ assertSame "$reference_out" "$(produce_filtered_list --fingerprint "$key0_md5")"
+ assertSame "$reference_out" "$(produce_filtered_list --fingerprint-confirmed "$key0_md5")"
+
+ key0_base64=$(cut -d\ -f2 "$SHUNIT_TMPDIR/key0.pub")
+ assertSame "$reference_out" "$(produce_filtered_list --key "$key0_base64")"
+ assertSame "$reference_out" "$(produce_filtered_list --key-confirmed "$key0_base64")"
+}
+
+sign_key_with_key_filtered () (
+ key_to_be_signed="$1"
+ signing_key="$2"
+ shift 2
+ with_saf_in_tmp "$@"
+ ssh-keygen -Us "$signing_key" -I identify "$key_to_be_signed"
+)
+
+test_sign_filter () {
+ # try to sign with a key that is allowed by the filter
+ assertTrue 'sign_key_with_key_filtered key0 key1 --comment key1'
+
+ # try to sign with a key that is not allowed by the filter
+ assertFalse 'sign_key_with_key_filtered key1 key0 --comment key1'
+}
+
+test_confirmation () {
+ assertTrue 'export SSH_ASKPASS=/bin/true; sign_key_with_key_filtered key0 key1 --comment-confirmed key1'
+ assertFalse 'export SSH_ASKPASS=/bin/false; sign_key_with_key_filtered key0 key1 --comment-confirmed key1'
+
+ cat > "$SHUNIT_TMPDIR/sap" <<-EOT
+ #!/bin/sh
+ echo "\$1" > "$SHUNIT_TMPDIR/sap_out"
+ EOT
+ chmod +x "$SHUNIT_TMPDIR/sap"
+ assertTrue 'export SSH_ASKPASS="$SHUNIT_TMPDIR/sap"; sign_key_with_key_filtered key0 key1 --comment-confirmed key1'
+ assertSame "Something behind the ssh-agent-filter requested use of the key named 'key1'." "$(head -n1 "$SHUNIT_TMPDIR/sap_out")"
+}
+
+. shunit2
diff --git a/version.h b/version.h
index 6d8ba97..2268da0 100644
--- a/version.h
+++ b/version.h
@@ -1 +1 @@
-#define SSH_AGENT_FILTER_VERSION "ssh-agent-filter 0.4.2"
+#define SSH_AGENT_FILTER_VERSION "ssh-agent-filter 0.5"