123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- /*
- * 'psusan': Pseudo Ssh for Untappable, Separately Authenticated Networks
- *
- * This is a standalone application that speaks on its standard I/O
- * (or a listening Unix-domain socket) the server end of the bare
- * ssh-connection protocol used by PuTTY's connection sharing.
- *
- * The idea of this tool is that you can use it to communicate across
- * any 8-bit-clean data channel between two inconveniently separated
- * domains, provided the channel is already (as the name suggests)
- * adequately secured against eavesdropping and modification and
- * already authenticated as the right user.
- *
- * If you're sitting at one end of such a channel and want to type
- * commands into the other end, the most obvious thing to do is to run
- * a terminal session directly over it. But if you run psusan at one
- * end, and a PuTTY (or compatible) client at the other end, then you
- * not only get a single terminal session: you get all the other SSH
- * amenities, like the ability to spawn extra terminal sessions,
- * forward ports or X11 connections, even forward an SSH agent.
- *
- * There are a surprising number of channels of that kind; see the man
- * page for some examples.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <assert.h>
- #include <stdarg.h>
- #include <signal.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <termios.h>
- #include <pwd.h>
- #include <sys/ioctl.h>
- #include <sys/time.h>
- #include "putty.h"
- #include "mpint.h"
- #include "ssh.h"
- #include "ssh/server.h"
- void modalfatalbox(const char *p, ...)
- {
- va_list ap;
- fprintf(stderr, "FATAL ERROR: ");
- va_start(ap, p);
- vfprintf(stderr, p, ap);
- va_end(ap);
- fputc('\n', stderr);
- exit(1);
- }
- void nonfatal(const char *p, ...)
- {
- va_list ap;
- fprintf(stderr, "ERROR: ");
- va_start(ap, p);
- vfprintf(stderr, p, ap);
- va_end(ap);
- fputc('\n', stderr);
- }
- char *platform_default_s(const char *name)
- {
- return NULL;
- }
- bool platform_default_b(const char *name, bool def)
- {
- return def;
- }
- int platform_default_i(const char *name, int def)
- {
- return def;
- }
- FontSpec *platform_default_fontspec(const char *name)
- {
- return fontspec_new_default();
- }
- Filename *platform_default_filename(const char *name)
- {
- return filename_from_str("");
- }
- char *x_get_default(const char *key)
- {
- return NULL; /* this is a stub */
- }
- void old_keyfile_warning(void) { }
- void timer_change_notify(unsigned long next)
- {
- }
- char *platform_get_x_display(void) { return NULL; }
- void make_unix_sftp_filehandle_key(void *vdata, size_t size)
- {
- /* psusan runs without a random number generator, so we can't make
- * this up by random_read. Fortunately, psusan is also
- * non-adversarial, so it's safe to generate this trivially. */
- unsigned char *data = (unsigned char *)vdata;
- for (size_t i = 0; i < size; i++)
- data[i] = (unsigned)rand() / ((unsigned)RAND_MAX / 256);
- }
- static bool verbose;
- struct server_instance {
- unsigned id;
- LogPolicy logpolicy;
- };
- static void log_to_stderr(unsigned id, const char *msg)
- {
- if (!verbose)
- return;
- if (id != (unsigned)-1)
- fprintf(stderr, "#%u: ", id);
- fputs(msg, stderr);
- fputc('\n', stderr);
- fflush(stderr);
- }
- static void server_eventlog(LogPolicy *lp, const char *event)
- {
- struct server_instance *inst = container_of(
- lp, struct server_instance, logpolicy);
- if (verbose)
- log_to_stderr(inst->id, event);
- }
- static void server_logging_error(LogPolicy *lp, const char *event)
- {
- struct server_instance *inst = container_of(
- lp, struct server_instance, logpolicy);
- log_to_stderr(inst->id, event); /* unconditional */
- }
- static int server_askappend(
- LogPolicy *lp, Filename *filename,
- void (*callback)(void *ctx, int result), void *ctx)
- {
- return 2; /* always overwrite (FIXME: could make this a cmdline option) */
- }
- static const LogPolicyVtable server_logpolicy_vt = {
- .eventlog = server_eventlog,
- .askappend = server_askappend,
- .logging_error = server_logging_error,
- .verbose = null_lp_verbose_no,
- };
- static void show_help(FILE *fp)
- {
- fputs("usage: psusan [options]\n"
- "options: --listen SOCKETPATH listen for connections on a Unix-domain socket\n"
- " --listen-once (with --listen) stop after one connection\n"
- " --verbose print log messages to standard error\n"
- " --sessiondir DIR cwd for session subprocess (default $HOME)\n"
- " --sshlog FILE write ssh-connection packet log to FILE\n"
- " --sshrawlog FILE write packets and raw data log to FILE\n"
- "also: psusan --help show this text\n"
- " psusan --version show version information\n", fp);
- }
- static void show_version_and_exit(void)
- {
- char *buildinfo_text = buildinfo("\n");
- printf("%s: %s\n%s\n", appname, ver, buildinfo_text);
- sfree(buildinfo_text);
- exit(0);
- }
- const bool buildinfo_gtk_relevant = false;
- static bool listening = false, listen_once = false;
- static bool finished = false;
- void server_instance_terminated(LogPolicy *lp)
- {
- struct server_instance *inst = container_of(
- lp, struct server_instance, logpolicy);
- if (listening && !listen_once) {
- log_to_stderr(inst->id, "connection terminated");
- } else {
- finished = true;
- }
- sfree(inst);
- }
- bool psusan_continue(void *ctx, bool fd, bool cb)
- {
- return !finished;
- }
- static bool longoptarg(const char *arg, const char *expected,
- const char **val, int *argcp, char ***argvp)
- {
- int len = strlen(expected);
- if (memcmp(arg, expected, len))
- return false;
- if (arg[len] == '=') {
- *val = arg + len + 1;
- return true;
- } else if (arg[len] == '\0') {
- if (--*argcp > 0) {
- *val = *++*argvp;
- return true;
- } else {
- fprintf(stderr, "%s: option %s expects an argument\n",
- appname, expected);
- exit(1);
- }
- }
- return false;
- }
- static bool longoptnoarg(const char *arg, const char *expected)
- {
- int len = strlen(expected);
- if (memcmp(arg, expected, len))
- return false;
- if (arg[len] == '=') {
- fprintf(stderr, "%s: option %s expects no argument\n",
- appname, expected);
- exit(1);
- } else if (arg[len] == '\0') {
- return true;
- }
- return false;
- }
- struct server_config {
- Conf *conf;
- const SshServerConfig *ssc;
- unsigned next_id;
- Socket *listening_socket;
- Plug listening_plug;
- };
- static Plug *server_conn_plug(
- struct server_config *cfg, struct server_instance **inst_out)
- {
- struct server_instance *inst = snew(struct server_instance);
- memset(inst, 0, sizeof(*inst));
- inst->id = cfg->next_id++;
- inst->logpolicy.vt = &server_logpolicy_vt;
- if (inst_out)
- *inst_out = inst;
- return ssh_server_plug(
- cfg->conf, cfg->ssc, NULL, 0, NULL, NULL,
- &inst->logpolicy, &unix_live_sftpserver_vt);
- }
- static void server_log(Plug *plug, Socket *s, PlugLogType type, SockAddr *addr,
- int port, const char *error_msg, int error_code)
- {
- log_to_stderr(-1, error_msg);
- }
- static void server_closing(Plug *plug, PlugCloseType type,
- const char *error_msg)
- {
- if (type != PLUGCLOSE_NORMAL)
- log_to_stderr(-1, error_msg);
- }
- static int server_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx)
- {
- struct server_config *cfg = container_of(
- p, struct server_config, listening_plug);
- Socket *s;
- const char *err;
- struct server_instance *inst;
- if (listen_once) {
- if (!cfg->listening_socket) /* in case of rapid double-accept */
- return 1;
- sk_close(cfg->listening_socket);
- cfg->listening_socket = NULL;
- }
- Plug *plug = server_conn_plug(cfg, &inst);
- s = constructor(ctx, plug);
- if ((err = sk_socket_error(s)) != NULL)
- return 1;
- SocketEndpointInfo *pi = sk_peer_info(s);
- char *msg = dupprintf("new connection from %s", pi->log_text);
- log_to_stderr(inst->id, msg);
- sfree(msg);
- sk_free_endpoint_info(pi);
- sk_set_frozen(s, false);
- ssh_server_start(plug, s);
- return 0;
- }
- static const PlugVtable server_plugvt = {
- .log = server_log,
- .closing = server_closing,
- .accepting = server_accepting,
- };
- unsigned auth_methods(AuthPolicy *ap)
- { return 0; }
- bool auth_none(AuthPolicy *ap, ptrlen username)
- { return false; }
- int auth_password(AuthPolicy *ap, ptrlen username, ptrlen password,
- ptrlen *new_password_opt)
- { return 0; }
- bool auth_publickey(AuthPolicy *ap, ptrlen username, ptrlen public_blob)
- { return false; }
- RSAKey *auth_publickey_ssh1(
- AuthPolicy *ap, ptrlen username, mp_int *rsa_modulus)
- { return NULL; }
- AuthKbdInt *auth_kbdint_prompts(AuthPolicy *ap, ptrlen username)
- { return NULL; }
- int auth_kbdint_responses(AuthPolicy *ap, const ptrlen *responses)
- { return -1; }
- char *auth_ssh1int_challenge(AuthPolicy *ap, unsigned method, ptrlen username)
- { return NULL; }
- bool auth_ssh1int_response(AuthPolicy *ap, ptrlen response)
- { return false; }
- bool auth_successful(AuthPolicy *ap, ptrlen username, unsigned method)
- { return false; }
- int main(int argc, char **argv)
- {
- const char *listen_socket = NULL;
- SshServerConfig ssc;
- Conf *conf = make_ssh_server_conf();
- memset(&ssc, 0, sizeof(ssc));
- ssc.application_name = "PSUSAN";
- ssc.session_starting_dir = getenv("HOME");
- ssc.bare_connection = true;
- while (--argc > 0) {
- const char *arg = *++argv;
- const char *val;
- if (longoptnoarg(arg, "--help")) {
- show_help(stdout);
- exit(0);
- } else if (longoptnoarg(arg, "--version")) {
- show_version_and_exit();
- } else if (longoptnoarg(arg, "--verbose") || !strcmp(arg, "-v")) {
- verbose = true;
- } else if (longoptarg(arg, "--sessiondir", &val, &argc, &argv)) {
- ssc.session_starting_dir = val;
- } else if (longoptarg(arg, "--sshlog", &val, &argc, &argv) ||
- longoptarg(arg, "-sshlog", &val, &argc, &argv)) {
- Filename *logfile = filename_from_str(val);
- conf_set_filename(conf, CONF_logfilename, logfile);
- filename_free(logfile);
- conf_set_int(conf, CONF_logtype, LGTYP_PACKETS);
- conf_set_int(conf, CONF_logxfovr, LGXF_OVR);
- } else if (longoptarg(arg, "--sshrawlog", &val, &argc, &argv) ||
- longoptarg(arg, "-sshrawlog", &val, &argc, &argv)) {
- Filename *logfile = filename_from_str(val);
- conf_set_filename(conf, CONF_logfilename, logfile);
- filename_free(logfile);
- conf_set_int(conf, CONF_logtype, LGTYP_SSHRAW);
- conf_set_int(conf, CONF_logxfovr, LGXF_OVR);
- } else if (longoptarg(arg, "--listen", &val, &argc, &argv)) {
- listen_socket = val;
- } else if (!strcmp(arg, "--listen-once")) {
- listen_once = true;
- } else {
- fprintf(stderr, "%s: unrecognised option '%s'\n", appname, arg);
- exit(1);
- }
- }
- sk_init();
- uxsel_init();
- struct server_config scfg;
- scfg.conf = conf;
- scfg.ssc = &ssc;
- scfg.next_id = 0;
- if (listen_socket) {
- listening = true;
- scfg.listening_plug.vt = &server_plugvt;
- SockAddr *addr = unix_sock_addr(listen_socket);
- scfg.listening_socket = new_unix_listener(addr, &scfg.listening_plug);
- char *msg = dupprintf("listening on Unix socket %s", listen_socket);
- log_to_stderr(-1, msg);
- sfree(msg);
- } else {
- struct server_instance *inst;
- Plug *plug = server_conn_plug(&scfg, &inst);
- ssh_server_start(plug, make_fd_socket(0, 1, -1, NULL, 0, plug));
- log_to_stderr(inst->id, "running directly on stdio");
- }
- cli_main_loop(cliloop_no_pw_setup, cliloop_no_pw_check,
- psusan_continue, NULL);
- return 0;
- }
|