diff options
author | Timo Weingärtner <timo@tiwe.de> | 2013-10-26 01:34:57 +0200 |
---|---|---|
committer | Timo Weingärtner <timo@tiwe.de> | 2013-10-26 01:34:57 +0200 |
commit | 516e7ee8307721afe519fe4b77ba015058409ae2 (patch) | |
tree | 07c7cf7502f4daf70a784ee2c23296a21ad50b66 /ssh-agent-filter.C | |
parent | 6f131d437f00e41a83fd158e03d2648ea2131d50 (diff) | |
parent | bd2154e05c0ab15d8e25f997fb4e6ec61f7a4c1f (diff) | |
download | ssh-agent-filter-516e7ee8307721afe519fe4b77ba015058409ae2.tar.gz |
Merge tag '0.3' into debian
0.3
Diffstat (limited to 'ssh-agent-filter.C')
-rw-r--r-- | ssh-agent-filter.C | 149 |
1 files changed, 135 insertions, 14 deletions
diff --git a/ssh-agent-filter.C b/ssh-agent-filter.C index a5ff38e..dd1b508 100644 --- a/ssh-agent-filter.C +++ b/ssh-agent-filter.C @@ -37,9 +37,11 @@ namespace fs = boost::filesystem; #include <csignal> #include <cstdlib> #include <fcntl.h> +#include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> +#include <sys/wait.h> #include <sysexits.h> #include <nettle/md5.h> #include <nettle/base64.h> @@ -49,12 +51,21 @@ namespace fs = boost::filesystem; #include "ssh-agent.h" #include "version.h" +#ifndef SOCK_CLOEXEC +#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; std::set<rfc4251string> allowed_pubkeys; +std::map<rfc4251string, std::string> confirmed_pubkeys; bool debug{false}; +bool all_confirmed{false}; +std::string saf_name; fs::path path; @@ -88,10 +99,14 @@ int make_upstream_agent_conn () { exit(EX_UNAVAILABLE); } - if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + 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); + } addr.sun_family = AF_UNIX; @@ -114,10 +129,14 @@ int make_listen_sock () { int sock; struct sockaddr_un addr; - if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + 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); + } addr.sun_family = AF_UNIX; @@ -144,12 +163,17 @@ int make_listen_sock () { void parse_cmdline (int const argc, char const * const * const argv) { po::options_description opts{"OPTIONS"}; opts.add_options() - ("comment,c", po::value(&allowed_comment), "key specified by comment") - ("debug,d", po::bool_switch(&debug), "show some debug info, don't fork") - ("fingerprint,fp,f", po::value(&allowed_md5), "key specified by pubkey's hex-encoded md5 fingerprint") - ("help,h", "print this help message") - ("key,k", po::value(&allowed_b64), "key specified by base64-encoded pubkey") - ("version,V", "print version information") + ("all-confirmed,A", po::bool_switch(&all_confirmed),"allow all other keys with confirmation") + ("comment,c", po::value(&allowed_comment), "key specified by comment") + ("comment-confirmed,C", po::value(&confirmed_comment), "key specified by comment, with confirmation") + ("debug,d", po::bool_switch(&debug), "show some debug info, don't fork") + ("fingerprint,fp,f", po::value(&allowed_md5), "key specified by pubkey's hex-encoded md5 fingerprint") + ("fingerprint-confirmed,F", po::value(&confirmed_md5), "key specified by pubkey's hex-encoded md5 fingerprint, with confirmation") + ("help,h", "print this help message") + ("key,k", po::value(&allowed_b64), "key specified by base64-encoded pubkey") + ("key-confirmed,K", po::value(&confirmed_b64), "key specified by base64-encoded pubkey, with confirmation") + ("name,n", po::value(&saf_name), "name for this instance of ssh-agent-filter, for confirmation puposes") + ("version,V", "print version information") ; po::variables_map config; store(parse_command_line(argc, argv, opts), config); @@ -206,23 +230,91 @@ void setup_filters () { std::string comm(comment); if (debug) std::clog << comm << std::endl; + bool allow{false}; + if (std::count(allowed_b64.begin(), allowed_b64.end(), b64)) { - allowed_pubkeys.insert(key); + allow = true; if (debug) std::clog << "key allowed by equal base64 representation" << std::endl; } if (std::count(allowed_md5.begin(), allowed_md5.end(), md5)) { - allowed_pubkeys.insert(key); + allow = true; if (debug) std::clog << "key allowed by matching md5 fingerprint" << std::endl; } if (std::count(allowed_comment.begin(), allowed_comment.end(), comm)) { - allowed_pubkeys.insert(key); + allow = true; if (debug) std::clog << "key allowed by matching comment" << std::endl; } + if (allow) allowed_pubkeys.emplace(std::move(key)); + else { + bool confirm{false}; + + if (std::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 (std::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 (std::count(confirmed_comment.begin(), confirmed_comment.end(), comm)) { + confirm = true; + if (debug) std::clog << "key allowed with confirmation by matching comment" << std::endl; + } + if (all_confirmed) { + confirm = true; + if (debug) std::clog << "key allowed with confirmation by catch-all (-A)" << std::endl; + } + + if (confirm) confirmed_pubkeys.emplace(std::move(key), std::move(comm)); + } + if (debug) std::clog << std::endl; } } +bool confirm (std::string const & question) { + char const * sap; + if (!(sap = getenv("SSH_ASKPASS"))) + sap = "ssh-askpass"; + pid_t pid = fork(); + if (pid < 0) + throw std::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); + } else { + // parent + int status; + return waitpid(pid, &status, 0) > 0 && WIFEXITED(status) && WEXITSTATUS(status) == 0; + } +} + +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); + + // 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} + "'."; + + return true; +} catch (...) { + return false; +} + rfc4251string handle_request (rfc4251string const & r) { std::istringstream request{r}; std::ostringstream answer; @@ -242,6 +334,7 @@ rfc4251string handle_request (rfc4251string const & r) { // 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; @@ -252,7 +345,7 @@ rfc4251string handle_request (rfc4251string const & r) { rfc4251string key; rfc4251string comment; agent_answer_iss >> key >> comment; - if (allowed_pubkeys.count(key)) + if (allowed_pubkeys.count(key) or confirmed_pubkeys.count(key)) keys.emplace_back(std::move(key), std::move(comment)); } answer << answer_code << rfc4251uint32{static_cast<uint32_t>(keys.size())}; @@ -263,8 +356,33 @@ rfc4251string handle_request (rfc4251string const & r) { case SSH2_AGENTC_SIGN_REQUEST: { rfc4251string key; - request >> key; - if (allowed_pubkeys.count(key)) { + rfc4251string data; + rfc4251uint32 flags; + request >> key >> data >> flags; + bool allow{false}; + + if (allowed_pubkeys.count(key)) + allow = true; + else { + auto it = confirmed_pubkeys.find(key); + if (it != confirmed_pubkeys.end()) { + std::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"; + if (saf_name.length()) + question += " named '" + saf_name + "'"; + question += " requested use of the key named '" + it->second + "'.\n"; + question += request_description; + allow = confirm(question); + } + } + + if (allow) { __gnu_cxx::stdio_filebuf<char> agent_filebuf{make_upstream_agent_conn(), std::ios::in | std::ios::out}; std::iostream agent{&agent_filebuf}; agent.exceptions(std::ios::badbit | std::ios::failbit); @@ -362,6 +480,9 @@ int main (int const argc, char const * const * const argv) { dup2(devnull, 1); 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; } signal(SIGINT, sighandler); |