aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Weingärtner <timo@tiwe.de>2014-05-27 00:19:21 +0200
committerTimo Weingärtner <timo@tiwe.de>2014-05-27 00:19:21 +0200
commit4dc4729a7ca2182568dae75db0cbfe22e6f90cd1 (patch)
tree1ba10f29d20cfaa31ee14273ea2a22b1d8ca07fa
parent9fe480b831273397908ec224a80c6439cb1fabed (diff)
parentd9ffc789b1c7e781acbdd8310aef871dc3a41c13 (diff)
downloadssh-agent-filter-4dc4729a7ca2182568dae75db0cbfe22e6f90cd1.tar.gz
Merge tag '0.4' into debian
0.4
-rw-r--r--Makefile3
-rw-r--r--changelog125
-rw-r--r--rfc4251.C56
-rw-r--r--rfc4251.h77
-rw-r--r--ssh-agent-filter.C409
-rw-r--r--version.h2
6 files changed, 461 insertions, 211 deletions
diff --git a/Makefile b/Makefile
index 378c33d..fcecbaa 100644
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,7 @@
CXXFLAGS ?= -g -O2 -Wall -Wold-style-cast
+CPPFLAGS += -D_FILE_OFFSET_BITS=64
CXXFLAGS += -std=c++11
LDLIBS = -lstdc++ -lboost_program_options -lboost_filesystem -lboost_system -lboost_iostreams -lnettle
@@ -32,7 +33,7 @@ ssh-agent-filter.1: ssh-agent-filter
ssh-agent-filter: ssh-agent-filter.o
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
version.h:
diff --git a/changelog b/changelog
index f0e6f8e..57f7e7e 100644
--- a/changelog
+++ b/changelog
@@ -1,3 +1,128 @@
+commit 4318a9a998f78f1d6ee4d32facd0fc8e1e231179
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-05-26 23:36:33 +0200
+
+ add dissection of pam_ssh_agent_auth data
+
+commit 5cc6f72612187001f70255f6097437381cf49bba
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-05-26 20:41:08 +0200
+
+ rfc4251uint64: fix byte order conversion
+
+commit 023a56e7d7483531219280af2e17760168cfaf7e
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-05-12 21:39:42 +0200
+
+ use a mutex for fd creation and forks
+
+ so we can have the CLOEXEC flag applied before a concurrent fork&exec.
+ use select() to avoid blocking while holding the mutex.
+
+commit 38a60755c7fa8e90b08273c6f2a0563ffc433f75
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-05-10 00:48:36 +0200
+
+ also replace custom fatal errors with exceptions
+
+commit 655b6084a5133d01a513c976b8b2567a71ba30c4
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-05-09 22:21:52 +0200
+
+ throw system_error instead of calling perror, exit
+
+commit 823dba2ac91f84a4ae75372c5636ad49d6cef1ba
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-05-09 21:42:10 +0200
+
+ factor out cloexec setting and stream exception arming
+
+commit b7d43361c44a214fa1b891fba7ea096650a6adca
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-05-07 21:34:53 +0200
+
+ add more #include's, add using's for often-used names
+
+commit a257a44837e78d283f1735b4685618c270aa54bc
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-04-20 23:35:04 +0200
+
+ use boost::iostreams instead of std::stringstreams
+
+ boost::iostreams::array_source instead of istringstream
+ boost::iostreams::back_insert_device instead of ostringstream
+
+ this should save at least one copy each and still has length checks (input)
+ or dynamic growth (output)
+
+commit a7f6e0c1d8553df678aa7b401eb374cf179210f7
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-04-18 13:55:30 +0200
+
+ use a boost stream directly
+
+ instead of declaring an intermediate stream_buffer
+
+commit 185d1b9525cb132bc802cdecbc6901ed30d58555
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-04-17 21:30:27 +0200
+
+ rfc4251string: drop unused and dangerous constructor from just a pointer
+
+ better use std::istringstream or boost::iostream::array_source
+
+commit ec5920ac2ed11b29135acb834ce6d633259aad75
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-04-15 20:09:21 +0200
+
+ rfc4251: move remaining non-inline functions to impl file
+
+commit afc8cbf6fad71317ee11b6e6374f8f3ce4816197
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-04-14 21:38:45 +0200
+
+ rfc4251*: add constructors from std::istream and use them
+
+commit ca4c9a28590919b6c0fc49cefdaf967758d5e4a0
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-04-14 21:10:11 +0200
+
+ do clean exit after receiving signal
+
+commit 26b246ac93d59a7f7ba29cff36ad1a3e61306a58
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-04-14 21:08:54 +0200
+
+ cosmetic: fix double semicolons
+
+commit 7d0f47c171b716b7e138157f8165ebf790227582
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-03-23 23:40:49 +0100
+
+ rfc4251string: add more operators
+
+ this adds <= > >= !=
+
+commit f4343c2060ea70639dfc7c9a5eeb70f0b5ec5697
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-03-22 22:34:35 +0100
+
+ tab align auth_data_ssh dissection for readability
+
+commit ed9ca11a8dc24bed5f8e69e2a62169e7a484d8fb
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2014-03-06 21:22:57 +0100
+
+ enable large file support
+
+ this should make some tests for LFS happy; we don't use sizes of or offsets in files here
+
+commit 477a0a0f1507413be7a5d2882aba28d01f7e6111 (tag: 0.3.1)
+Author: Timo Weingärtner <timo@tiwe.de>
+Date: 2013-12-19 15:47:43 +0100
+
+ release 0.3.1
+
commit a8c1ef855655e7419e54317a8e782f3993d99f7d
Author: Timo Weingärtner <timo@tiwe.de>
Date: 2013-11-13 23:42:46 +0100
diff --git a/rfc4251.C b/rfc4251.C
new file mode 100644
index 0000000..8ca5f2b
--- /dev/null
+++ b/rfc4251.C
@@ -0,0 +1,56 @@
+/*
+ * rfc4251.C -- support for name-list type from RFC 4251, section 5
+ *
+ * These are the conversions between an rfc4251string containing a name-list
+ * and vector<string>.
+ *
+ * Copyright (C) 2013 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/>.
+ */
+
+#include "rfc4251.h"
+
+rfc4251string::rfc4251string (std::vector<std::string> const & v) {
+ for (auto it = v.begin(); it != v.end();) {
+ if (it->size() == 0)
+ throw std::length_error{"name of zero length"};
+ if (value.size() + it->size() > std::numeric_limits<uint32_t>::max())
+ throw std::length_error{"32-bit limit for rfc4251string exceeded"};
+ value.insert(value.end(), it->data(), it->data() + it->size());
+ ++it;
+ if (it == v.end())
+ break;
+ value.push_back(',');
+ }
+}
+
+rfc4251string::operator std::vector<std::string> () const {
+ std::vector<std::string> ret;
+ auto name_start = value.begin();
+ if (name_start != value.end())
+ for (auto it = name_start; ; ++it) {
+ if (it == value.end() or *it == ',') {
+ if (it == name_start)
+ throw std::length_error{"name of zero length"};
+ ret.emplace_back(name_start, it);
+ name_start = it + 1;
+ }
+ if (it == value.end())
+ break;
+ }
+ return ret;
+}
diff --git a/rfc4251.h b/rfc4251.h
index b2f9658..d9ac93e 100644
--- a/rfc4251.h
+++ b/rfc4251.h
@@ -10,7 +10,7 @@
* those structs contain the objects in their RFC 4251 representation,
* conversions are provided via constructors and cast operators
*
- * Copyright (C) 2013 Timo Weingärtner <timo@tiwe.de>
+ * Copyright (C) 2013-2014 Timo Weingärtner <timo@tiwe.de>
*
* This file is part of ssh-agent-filter.
*
@@ -35,6 +35,7 @@
#include <stdexcept>
#include <arpa/inet.h> // ntohl() / htonl()
#include <gmpxx.h>
+#include <boost/operators.hpp>
struct rfc4251byte {
union {
@@ -44,6 +45,7 @@ struct rfc4251byte {
rfc4251byte () = default;
explicit rfc4251byte (uint8_t v) : value(v) {}
+ inline explicit rfc4251byte (std::istream &);
operator uint8_t () const { return value; }
};
@@ -53,9 +55,12 @@ inline std::istream & operator>> (std::istream & is, rfc4251byte & x) {
}
inline std::ostream & operator<< (std::ostream & os, rfc4251byte const & x) {
- return os.write(x.buf, sizeof(x.buf));;
+ return os.write(x.buf, sizeof(x.buf));
}
+inline rfc4251byte::rfc4251byte (std::istream & is) {
+ is >> *this;
+}
struct rfc4251bool {
union {
@@ -65,6 +70,7 @@ struct rfc4251bool {
rfc4251bool () = default;
explicit rfc4251bool (uint8_t v) : value(v) {}
+ inline explicit rfc4251bool (std::istream &);
operator uint8_t () const { return value; }
};
@@ -74,9 +80,12 @@ inline std::istream & operator>> (std::istream & is, rfc4251bool & x) {
}
inline std::ostream & operator<< (std::ostream & os, rfc4251bool const & x) {
- return os.write(x.buf, sizeof(x.buf));;
+ return os.write(x.buf, sizeof(x.buf));
}
+inline rfc4251bool::rfc4251bool (std::istream & is) {
+ is >> *this;
+}
struct rfc4251uint32 {
union {
@@ -86,6 +95,7 @@ struct rfc4251uint32 {
rfc4251uint32 () = default;
explicit rfc4251uint32 (uint32_t v) { value = htonl(v); }
+ inline explicit rfc4251uint32 (std::istream &);
operator uint32_t () const { return ntohl(value); }
};
@@ -95,9 +105,12 @@ inline std::istream & operator>> (std::istream & is, rfc4251uint32 & x) {
}
inline std::ostream & operator<< (std::ostream & os, rfc4251uint32 const & x) {
- return os.write(x.buf, sizeof(x.buf));;
+ return os.write(x.buf, sizeof(x.buf));
}
+inline rfc4251uint32::rfc4251uint32 (std::istream & is) {
+ is >> *this;
+}
struct rfc4251uint64 {
union {
@@ -107,6 +120,7 @@ struct rfc4251uint64 {
rfc4251uint64 () = default;
inline explicit rfc4251uint64 (uint64_t v);
+ inline explicit rfc4251uint64 (std::istream &);
inline explicit operator uint64_t () const;
};
@@ -121,8 +135,8 @@ inline rfc4251uint64::rfc4251uint64 (uint64_t v) {
inline rfc4251uint64::operator uint64_t () const {
uint64_t ret{0};
for (uint_fast8_t i{0}; i < 8; ++i) {
- ret |= buf[i];
ret <<= 8;
+ ret |= static_cast<uint8_t>(buf[i]);
}
return ret;
}
@@ -132,68 +146,39 @@ inline std::istream & operator>> (std::istream & is, rfc4251uint64 & x) {
}
inline std::ostream & operator<< (std::ostream & os, rfc4251uint64 const & x) {
- return os.write(x.buf, sizeof(x.buf));;
+ return os.write(x.buf, sizeof(x.buf));
}
+inline rfc4251uint64::rfc4251uint64 (std::istream & is) {
+ is >> *this;
+}
-struct rfc4251string {
+struct rfc4251string : boost::totally_ordered<rfc4251string> {
std::vector<char> value;
rfc4251string () = default;
- inline explicit rfc4251string (char const *);
inline explicit rfc4251string (char const *, size_t);
explicit rfc4251string (std::string const & s) : rfc4251string{s.data(), s.size()} {}
explicit rfc4251string (std::vector<std::string> const &);
explicit rfc4251string (mpz_srcptr);
explicit rfc4251string (mpz_class const & x) : rfc4251string{x.get_mpz_t()} {}
+ inline explicit rfc4251string (std::istream &);
+
+ size_t size () const { return value.size(); }
+ char const * data () const { return value.data(); }
+ char * data () { return value.data(); }
operator std::string () const { return {value.begin(), value.end()}; }
operator std::vector<std::string> () const;
operator mpz_class () const;
};
-inline rfc4251string::rfc4251string (char const * s) {
- auto len = ntohl(*reinterpret_cast<uint32_t const *>(s));
- value.insert(value.begin(), s + 4, s + 4 + len);
-}
-
inline rfc4251string::rfc4251string (char const * s, size_t l) {
if (l > std::numeric_limits<uint32_t>::max())
throw std::length_error{"32-bit limit for rfc4251string exceeded"};
value.insert(value.end(), s, s + l);
}
-rfc4251string::rfc4251string (std::vector<std::string> const & v) {
- for (auto it = v.begin(); it != v.end();) {
- if (it->size() == 0)
- throw std::length_error{"name of zero length"};
- if (value.size() + it->size() > std::numeric_limits<uint32_t>::max())
- throw std::length_error{"32-bit limit for rfc4251string exceeded"};
- value.insert(value.end(), it->data(), it->data() + it->size());
- ++it;
- if (it == v.end())
- break;
- value.push_back(',');
- }
-}
-
-rfc4251string::operator std::vector<std::string> () const {
- std::vector<std::string> ret;
- auto name_start = value.begin();
- if (name_start != value.end())
- for (auto it = name_start; ; ++it) {
- if (it == value.end() or *it == ',') {
- if (it == name_start)
- throw std::length_error{"name of zero length"};
- ret.emplace_back(name_start, it);
- name_start = it + 1;
- }
- if (it == value.end())
- break;
- }
- return ret;
-}
-
inline std::istream & operator>> (std::istream & is, rfc4251string & s) {
s.value.clear();
rfc4251uint32 len;
@@ -212,6 +197,10 @@ inline std::ostream & operator<< (std::ostream & os, rfc4251string const & s) {
return os;
}
+inline rfc4251string::rfc4251string (std::istream & is) {
+ is >> *this;
+}
+
inline bool operator== (rfc4251string const & l, rfc4251string const & r) {
return l.value == r.value;
}
diff --git a/ssh-agent-filter.C b/ssh-agent-filter.C
index 8ee6aaf..faa31f9 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 Timo Weingärtner <timo@tiwe.de>
+ * Copyright (C) 2013-2014 Timo Weingärtner <timo@tiwe.de>
*
* This file is part of ssh-agent-filter.
*
@@ -25,24 +25,57 @@ namespace po = boost::program_options;
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
-#include <boost/iostreams/stream_buffer.hpp>
+#include <boost/iostreams/stream.hpp>
+#include <boost/iostreams/device/array.hpp>
+#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
namespace io = boost::iostreams;
#include <string>
+using std::string;
+
#include <vector>
+using std::vector;
+
#include <set>
+#include <map>
#include <iostream>
-#include <sstream>
+using std::cout;
+using std::clog;
+using std::endl;
+using std::flush;
+
#include <stdexcept>
+using std::runtime_error;
+using std::length_error;
+using std::invalid_argument;
+
+#include <system_error>
+using std::system_error;
+using std::system_category;
+
+#include <utility>
+using std::pair;
+using std::move;
+
+#include <algorithm>
+using std::count;
+
#include <thread>
+#include <mutex>
+using std::mutex;
+using std::lock_guard;
+
+#include <chrono>
+#include <cerrno>
#include <csignal>
#include <cstdlib>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/select.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <sysexits.h>
@@ -58,21 +91,22 @@ namespace io = boost::iostreams;
#define SOCK_CLOEXEC 0
#endif
-std::vector<std::string> allowed_b64;
-std::vector<std::string> allowed_md5;
-std::vector<std::string> allowed_comment;
-std::vector<std::string> confirmed_b64;
-std::vector<std::string> confirmed_md5;
-std::vector<std::string> confirmed_comment;
+vector<string> allowed_b64;
+vector<string> allowed_md5;
+vector<string> allowed_comment;
+vector<string> confirmed_b64;
+vector<string> confirmed_md5;
+vector<string> confirmed_comment;
std::set<rfc4251string> allowed_pubkeys;
-std::map<rfc4251string, std::string> confirmed_pubkeys;
+std::map<rfc4251string, string> confirmed_pubkeys;
bool debug{false};
bool all_confirmed{false};
-std::string saf_name;
+string saf_name;
fs::path path;
+mutex fd_fork_mutex;
-std::string md5_hex (std::string const & s) {
+string md5_hex (string const & s) {
struct md5_ctx ctx;
md5_init(&ctx);
md5_update(&ctx, s.size(), reinterpret_cast<uint8_t const *>(s.data()));
@@ -83,7 +117,7 @@ std::string md5_hex (std::string const & s) {
return {reinterpret_cast<char const *>(hex), sizeof(hex)};
}
-std::string base64_encode (std::string const & s) {
+string base64_encode (string const & s) {
struct base64_encode_ctx ctx;
base64_encode_init(&ctx);
uint8_t b64[BASE64_ENCODE_LENGTH(s.size())];
@@ -92,38 +126,39 @@ std::string base64_encode (std::string const & s) {
return {reinterpret_cast<char const *>(b64), len};
}
+void cloexec (int fd) {
+ if (fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC))
+ throw system_error(errno, system_category(), "fcntl");
+}
+
+void arm(std::ios & stream) {
+ stream.exceptions(stream.badbit | stream.failbit);
+}
+
int make_upstream_agent_conn () {
char const * path;
int sock;
struct sockaddr_un addr;
- if (!(path = getenv("SSH_AUTH_SOCK"))) {
- std::clog << "no $SSH_AUTH_SOCK" << std::endl;
- exit(EX_UNAVAILABLE);
- }
+ if (!(path = getenv("SSH_AUTH_SOCK")))
+ throw invalid_argument("no $SSH_AUTH_SOCK");
- if ((sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) {
- perror("socket");
- exit(EX_UNAVAILABLE);
- }
- if (fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC)) {
- perror("fcntl");
- exit(EX_UNAVAILABLE);
+ {
+ lock_guard<mutex> lock{fd_fork_mutex};
+ if ((sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1)
+ throw system_error(errno, system_category(), "socket");
+ cloexec(sock);
}
addr.sun_family = AF_UNIX;
- if (strlen(path) >= sizeof(addr.sun_path)) {
- std::clog << "$SSH_AUTH_SOCK too long" << std::endl;
- exit(EX_UNAVAILABLE);
- }
+ if (strlen(path) >= sizeof(addr.sun_path))
+ throw length_error("$SSH_AUTH_SOCK too long");
strcpy(addr.sun_path, path);
- if (connect(sock, reinterpret_cast<struct sockaddr const *>(&addr), sizeof(addr))) {
- perror("connect");
- exit(EX_UNAVAILABLE);
- }
+ if (connect(sock, reinterpret_cast<struct sockaddr const *>(&addr), sizeof(addr)))
+ throw system_error(errno, system_category(), "connect");
return sock;
}
@@ -132,33 +167,28 @@ int make_listen_sock () {
int sock;
struct sockaddr_un addr;
- if ((sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) {
- perror("socket");
- exit(EX_UNAVAILABLE);
- }
- if (fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC)) {
- perror("fcntl");
- exit(EX_UNAVAILABLE);
+ {
+ lock_guard<mutex> lock{fd_fork_mutex};
+ if ((sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1)
+ throw system_error(errno, system_category(), "socket");
+ cloexec(sock);
}
+ if (fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK))
+ throw system_error(errno, system_category(), "fcntl");
+
addr.sun_family = AF_UNIX;
- if (path.native().length() >= sizeof(addr.sun_path)) {
- std::clog << "path for listen socket too long" << std::endl;
- exit(EX_UNAVAILABLE);
- }
+ if (path.native().length() >= sizeof(addr.sun_path))
+ throw length_error("path for listen socket too long");
strcpy(addr.sun_path, path.c_str());
- if (bind(sock, reinterpret_cast<struct sockaddr const *>(&addr), sizeof(addr))) {
- perror("bind");
- exit(EX_UNAVAILABLE);
- }
+ if (bind(sock, reinterpret_cast<struct sockaddr const *>(&addr), sizeof(addr)))
+ throw system_error(errno, system_category(), "bind");
- if (listen(sock, 0)) {
- perror("listen");
- exit(EX_UNAVAILABLE);
- }
+ if (listen(sock, 0))
+ throw system_error(errno, system_category(), "listen");
return sock;
}
@@ -183,13 +213,13 @@ void parse_cmdline (int const argc, char const * const * const argv) {
notify(config);
if (config.count("help")) {
- std::cout << "Invocation: ssh-agent-filter [ OPTIONS ]" << std::endl;
- std::cout << opts << std::endl;
+ cout << "Invocation: ssh-agent-filter [ OPTIONS ]" << endl;
+ cout << opts << endl;
exit(EX_OK);
}
if (config.count("version")) {
- std::cout << SSH_AGENT_FILTER_VERSION << std::endl;
+ cout << SSH_AGENT_FILTER_VERSION << endl;
exit(EX_OK);
}
@@ -204,92 +234,90 @@ void parse_cmdline (int const argc, char const * const * const argv) {
}
void setup_filters () {
- io::stream_buffer<io::file_descriptor> agent_filebuf{make_upstream_agent_conn(), io::close_handle};
- std::iostream agent{&agent_filebuf};
- agent.exceptions(std::ios::badbit | std::ios::failbit);
+ io::stream<io::file_descriptor> agent{make_upstream_agent_conn(), io::close_handle};
+ arm(agent);
- agent << rfc4251string{std::string{SSH2_AGENTC_REQUEST_IDENTITIES}};
- rfc4251string answer;
- agent >> answer;
- std::istringstream answer_iss{answer};
- answer_iss.exceptions(std::ios::badbit | std::ios::failbit);
- rfc4251byte resp_code;
- answer_iss >> resp_code;
+ agent << rfc4251string{string{SSH2_AGENTC_REQUEST_IDENTITIES}};
+ rfc4251string answer{agent};
+ io::stream<io::array_source> answer_iss{answer.data(), answer.size()};
+ arm(answer_iss);
+ rfc4251byte resp_code{answer_iss};
if (resp_code != SSH2_AGENT_IDENTITIES_ANSWER)
- throw std::runtime_error{"unexpected answer from ssh-agent"};
- rfc4251uint32 keycount;
- answer_iss >> keycount;
+ throw runtime_error{"unexpected answer from ssh-agent"};
+ rfc4251uint32 keycount{answer_iss};
for (uint32_t i = keycount; i; --i) {
- rfc4251string key;
- rfc4251string comment;
- answer_iss >> key >> comment;
+ rfc4251string key{answer_iss};
+ rfc4251string comment{answer_iss};
auto b64 = base64_encode(key);
- if (debug) std::clog << b64 << std::endl;
+ if (debug) clog << b64 << endl;
auto md5 = md5_hex(key);
- if (debug) std::clog << md5 << std::endl;
+ if (debug) clog << md5 << endl;
- std::string comm(comment);
- if (debug) std::clog << comm << std::endl;
+ string comm(comment);
+ if (debug) clog << comm << endl;
bool allow{false};
- if (std::count(allowed_b64.begin(), allowed_b64.end(), b64)) {
+ if (count(allowed_b64.begin(), allowed_b64.end(), b64)) {
allow = true;
- if (debug) std::clog << "key allowed by equal base64 representation" << std::endl;
+ if (debug) clog << "key allowed by equal base64 representation" << endl;
}
- if (std::count(allowed_md5.begin(), allowed_md5.end(), md5)) {
+ if (count(allowed_md5.begin(), allowed_md5.end(), md5)) {
allow = true;
- if (debug) std::clog << "key allowed by matching md5 fingerprint" << std::endl;
+ if (debug) clog << "key allowed by matching md5 fingerprint" << endl;
}
- if (std::count(allowed_comment.begin(), allowed_comment.end(), comm)) {
+ if (count(allowed_comment.begin(), allowed_comment.end(), comm)) {
allow = true;
- if (debug) std::clog << "key allowed by matching comment" << std::endl;
+ if (debug) clog << "key allowed by matching comment" << endl;
}
- if (allow) allowed_pubkeys.emplace(std::move(key));
+ if (allow) allowed_pubkeys.emplace(move(key));
else {
bool confirm{false};
- if (std::count(confirmed_b64.begin(), confirmed_b64.end(), b64)) {
+ if (count(confirmed_b64.begin(), confirmed_b64.end(), b64)) {
confirm = true;
- if (debug) std::clog << "key allowed with confirmation by equal base64 representation" << std::endl;
+ if (debug) clog << "key allowed with confirmation by equal base64 representation" << endl;
}
- if (std::count(confirmed_md5.begin(), confirmed_md5.end(), md5)) {
+ if (count(confirmed_md5.begin(), confirmed_md5.end(), md5)) {
confirm = true;
- if (debug) std::clog << "key allowed with confirmation by matching md5 fingerprint" << std::endl;
+ if (debug) clog << "key allowed with confirmation by matching md5 fingerprint" << endl;
}
- if (std::count(confirmed_comment.begin(), confirmed_comment.end(), comm)) {
+ if (count(confirmed_comment.begin(), confirmed_comment.end(), comm)) {
confirm = true;
- if (debug) std::clog << "key allowed with confirmation by matching comment" << std::endl;
+ if (debug) clog << "key allowed with confirmation by matching comment" << endl;
}
if (all_confirmed) {
confirm = true;
- if (debug) std::clog << "key allowed with confirmation by catch-all (-A)" << std::endl;
+ if (debug) clog << "key allowed with confirmation by catch-all (-A)" << endl;
}
- if (confirm) confirmed_pubkeys.emplace(std::move(key), std::move(comm));
+ if (confirm) confirmed_pubkeys.emplace(move(key), move(comm));
}
- if (debug) std::clog << std::endl;
+ if (debug) clog << endl;
}
}
-bool confirm (std::string const & question) {
+bool confirm (string const & question) {
char const * sap;
if (!(sap = getenv("SSH_ASKPASS")))
sap = "ssh-askpass";
- pid_t pid = fork();
+ pid_t pid;
+ {
+ lock_guard<mutex> lock{fd_fork_mutex};
+ pid = fork();
+ }
if (pid < 0)
- throw std::runtime_error("fork()");
+ throw runtime_error("fork()");
if (pid == 0) {
// child
char const * args[3] = { sap, question.c_str(), nullptr };
// see execvp(3p) for cast rationale
execvp(sap, const_cast<char * const *>(args));
- perror("exec");
- exit(EX_UNAVAILABLE);
+ throw system_error(errno, system_category(), "exec");
} else {
// parent
int status;
@@ -297,59 +325,104 @@ bool confirm (std::string const & question) {
}
}
-bool dissect_auth_data_ssh (rfc4251string const & data, std::string & request_description) try {
- std::istringstream datastream{data};
- datastream.exceptions(std::ios::badbit | std::ios::failbit);
+bool dissect_auth_data_ssh (rfc4251string const & data, string & request_description) try {
+ io::stream<io::array_source> datastream{data.data(), data.size()};
+ arm(datastream);
// Format specified in RFC 4252 Section 7
- rfc4251string session_identifier; datastream >> session_identifier;
- rfc4251byte requesttype; datastream >> requesttype;
- rfc4251string username; datastream >> username;
- rfc4251string servicename; datastream >> servicename;
- rfc4251string publickeystring; datastream >> publickeystring;
- rfc4251bool shouldbetrue; datastream >> shouldbetrue;
- rfc4251string publickeyalgorithm; datastream >> publickeyalgorithm;
- rfc4251string publickey; datastream >> publickey;
-
- request_description = "The request is for an ssh connection as user '" + std::string{username} + "' with service name '" + std::string{servicename} + "'.";
-
+ rfc4251string session_identifier{datastream};
+ rfc4251byte requesttype{datastream};
+ rfc4251string username{datastream};
+ rfc4251string servicename{datastream};
+ rfc4251string publickeystring{datastream};
+ rfc4251bool shouldbetrue{datastream};
+ rfc4251string publickeyalgorithm{datastream};
+ rfc4251string publickey{datastream};
+
+ request_description = "The request is for an ssh connection as user '" + string{username} + "' with service name '" + string{servicename} + "'.";
+
+ if (string{servicename} == "pam_ssh_agent_auth") try {
+ clog << base64_encode(session_identifier) << endl;
+ io::stream<io::array_source> idstream{session_identifier.data(), session_identifier.size()};
+ arm(idstream);
+
+ rfc4251uint32 type{idstream};
+ if (type == 101) {
+ // PAM_SSH_AGENT_AUTH_REQUESTv1
+ rfc4251string cookie{idstream};
+ rfc4251string user{idstream};
+ rfc4251string ruser{idstream};
+ rfc4251string pam_service{idstream};
+ rfc4251string pwd{idstream};
+ rfc4251string action{idstream};
+ rfc4251string hostname{idstream};
+ rfc4251uint64 timestamp{idstream};
+
+ string singleuser{user};
+ if (user != ruser)
+ singleuser += " (" + string{ruser} + ")";
+
+ string additional;
+ additional += "User '" + singleuser + "' wants to use '" + string{pam_service};
+ additional += "' in '" + string{pwd};
+
+ io::stream<io::array_source> actionstream{action.data(), action.size()};
+ arm(actionstream);
+
+ rfc4251uint32 argc{actionstream};
+
+ if (argc) {
+ additional += " to run";
+ for (uint32_t i = argc; i; --i) {
+ rfc4251string argv{actionstream};
+ additional += ' ' + string{argv};
+ }
+ }
+
+ additional += " on " + string{hostname} + ".\n";
+
+ auto now = std::chrono::system_clock::now();
+ auto req_time = std::chrono::system_clock::from_time_t(static_cast<uint64_t>(timestamp));
+ auto timediff = std::chrono::duration_cast<std::chrono::seconds>(now - req_time).count();
+
+ additional += "The request was generated " + std::to_string(timediff) + " seconds ago.\n";
+ request_description = move(additional);
+ }
+ } catch (...) {}
+
return true;
} catch (...) {
return false;
}
rfc4251string handle_request (rfc4251string const & r) {
- std::istringstream request{r};
- std::ostringstream answer;
- request.exceptions(std::ios::badbit | std::ios::failbit);
- answer.exceptions(std::ios::badbit | std::ios::failbit);
- rfc4251byte request_code;
- request >> request_code;
+ io::stream<io::array_source> request{r.data(), r.size()};
+ rfc4251string ret;
+ io::stream<io::back_insert_device<vector<char>>> answer{ret.value};
+ arm(request);
+ arm(answer);
+ rfc4251byte request_code{request};
switch (request_code) {
case SSH2_AGENTC_REQUEST_IDENTITIES:
{
- io::stream_buffer<io::file_descriptor> agent_filebuf{make_upstream_agent_conn(), io::close_handle};
- std::iostream agent{&agent_filebuf};
- agent.exceptions(std::ios::badbit | std::ios::failbit);
- rfc4251string agent_answer;
- agent << rfc4251string{std::string{SSH2_AGENTC_REQUEST_IDENTITIES}};
- agent >> agent_answer;
+ io::stream<io::file_descriptor> agent{make_upstream_agent_conn(), io::close_handle};
+ arm(agent);
+ agent << rfc4251string{string{SSH2_AGENTC_REQUEST_IDENTITIES}};
// temp to test key filtering when signing
- //return agent_answer;
- std::istringstream agent_answer_iss{agent_answer};
- agent_answer_iss.exceptions(std::ios::badbit | std::ios::failbit);
- rfc4251byte answer_code;
- rfc4251uint32 keycount;
- agent_answer_iss >> answer_code >> keycount;
+ //return rfc4251string{agent};
+ rfc4251string agent_answer{agent};
+ io::stream<io::array_source> agent_answer_iss{agent_answer.data(), agent_answer.size()};
+ arm(agent_answer_iss);
+ rfc4251byte answer_code{agent_answer_iss};
+ rfc4251uint32 keycount{agent_answer_iss};
if (answer_code != SSH2_AGENT_IDENTITIES_ANSWER)
- throw std::runtime_error{"unexpected answer from ssh-agent"};
- std::vector<std::pair<rfc4251string, rfc4251string>> keys;
+ throw runtime_error{"unexpected answer from ssh-agent"};
+ vector<pair<rfc4251string, rfc4251string>> keys;
for (uint32_t i = keycount; i; --i) {
- rfc4251string key;
- rfc4251string comment;
- agent_answer_iss >> key >> comment;
+ rfc4251string key{agent_answer_iss};
+ rfc4251string comment{agent_answer_iss};
if (allowed_pubkeys.count(key) or confirmed_pubkeys.count(key))
- keys.emplace_back(std::move(key), std::move(comment));
+ keys.emplace_back(move(key), move(comment));
}
answer << answer_code << rfc4251uint32{static_cast<uint32_t>(keys.size())};
for (auto const & k : keys)
@@ -358,10 +431,9 @@ rfc4251string handle_request (rfc4251string const & r) {
break;
case SSH2_AGENTC_SIGN_REQUEST:
{
- rfc4251string key;
- rfc4251string data;
- rfc4251uint32 flags;
- request >> key >> data >> flags;
+ rfc4251string key{request};
+ rfc4251string data{request};
+ rfc4251uint32 flags{request};
bool allow{false};
if (allowed_pubkeys.count(key))
@@ -369,14 +441,14 @@ rfc4251string handle_request (rfc4251string const & r) {
else {
auto it = confirmed_pubkeys.find(key);
if (it != confirmed_pubkeys.end()) {
- std::string request_description;
+ string request_description;
bool dissect_ok{false};
if (!dissect_ok)
dissect_ok = dissect_auth_data_ssh(data, request_description);
if (!dissect_ok)
request_description = "The request format is unknown.";
- std::string question = "Something behind the ssh-agent-filter";
+ string question = "Something behind the ssh-agent-filter";
if (saf_name.length())
question += " named '" + saf_name + "'";
question += " requested use of the key named '" + it->second + "'.\n";
@@ -386,14 +458,12 @@ rfc4251string handle_request (rfc4251string const & r) {
}
if (allow) {
- io::stream_buffer<io::file_descriptor> agent_filebuf{make_upstream_agent_conn(), io::close_handle};
- std::iostream agent{&agent_filebuf};
- agent.exceptions(std::ios::badbit | std::ios::failbit);
+ io::stream<io::file_descriptor> agent{make_upstream_agent_conn(), io::close_handle};
+ arm(agent);
rfc4251string agent_answer;
agent << r;
- agent >> agent_answer;
- return agent_answer;
+ return rfc4251string{agent};
} else
answer << rfc4251byte{SSH_AGENT_FAILURE};
}
@@ -424,19 +494,16 @@ rfc4251string handle_request (rfc4251string const & r) {
break;
}
- return rfc4251string{answer.str()};
+ answer << flush;
+ return ret;
}
void handle_client (int const sock) try {
- io::stream_buffer<io::file_descriptor> client_filebuf{sock, io::close_handle};
- std::iostream client{&client_filebuf};
- client.exceptions(std::ios::badbit | std::ios::failbit);
+ io::stream<io::file_descriptor> client{sock, io::close_handle};
+ arm(client);
- for (;;) {
- rfc4251string request;
- client >> request;
- client << handle_request(request) << std::flush;
- }
+ for (;;)
+ client << handle_request(rfc4251string{client}) << flush;
} catch (...) {
}
@@ -446,7 +513,7 @@ void sighandler (int sig) {
break;
default:
remove(path);
- std::abort();
+ std::exit(0);
}
}
@@ -460,14 +527,12 @@ int main (int const argc, char const * const * const argv) {
if (not debug) {
pid_t pid = fork();
- if (pid == -1) {
- perror("fork");
- exit(EX_OSERR);
- }
+ if (pid == -1)
+ throw system_error(errno, system_category(), "fork");
if (pid > 0) {
- std::cout << "SSH_AUTH_SOCK='" << path.native() << "'; export SSH_AUTH_SOCK;" << std::endl;
- std::cout << "SSH_AGENT_PID='" << pid << "'; export SSH_AGENT_PID;" << std::endl;
- std::cout << "echo 'Agent pid " << pid << "';" << std::endl;
+ cout << "SSH_AUTH_SOCK='" << path.native() << "'; export SSH_AUTH_SOCK;" << endl;
+ cout << "SSH_AGENT_PID='" << pid << "'; export SSH_AGENT_PID;" << endl;
+ cout << "echo 'Agent pid " << pid << "';" << endl;
exit(EX_OK);
}
@@ -479,8 +544,8 @@ int main (int const argc, char const * const * const argv) {
dup2(devnull, 2);
close(devnull);
} else {
- std::cout << "copy this to another terminal:" << std::endl;
- std::cout << "SSH_AUTH_SOCK='" << path.native() << "'; export SSH_AUTH_SOCK;" << std::endl;
+ cout << "copy this to another terminal:" << endl;
+ cout << "SSH_AUTH_SOCK='" << path.native() << "'; export SSH_AUTH_SOCK;" << endl;
}
signal(SIGINT, sighandler);
@@ -488,8 +553,22 @@ int main (int const argc, char const * const * const argv) {
signal(SIGHUP, sighandler);
signal(SIGTERM, sighandler);
- int client_sock;
- while ((client_sock = accept(listen_sock, nullptr, nullptr)) != -1) {
+ for (;;) {
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(listen_sock, &fds);
+ select(listen_sock + 1, &fds, nullptr, nullptr, nullptr);
+ int client_sock;
+ {
+ lock_guard<mutex> lock{fd_fork_mutex};
+ if ((client_sock = accept(listen_sock, nullptr, nullptr)) == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ continue;
+ else
+ break;
+ }
+ cloexec(client_sock);
+ }
std::thread t{handle_client, client_sock};
t.detach();
}
diff --git a/version.h b/version.h
index 9572eb4..70a2285 100644
--- a/version.h
+++ b/version.h
@@ -1 +1 @@
-#define SSH_AGENT_FILTER_VERSION "ssh-agent-filter 0.3.1"
+#define SSH_AGENT_FILTER_VERSION "ssh-agent-filter 0.4"