1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

First attempt at a Unix port of Plink. Seems to basically work;

doesn't yet use the SSH agent, no way to specify arbitrary config
options, no manpage yet, couple of other fiddly things need doing,
but it makes SSH connections and doesn't fall over horribly so I say
it's a good start. Now to run it under valgrind...

[originally from svn r2165]
This commit is contained in:
Simon Tatham 2002-10-31 19:49:52 +00:00
parent db71c5006e
commit 317180ed49
9 changed files with 2193 additions and 25 deletions

22
Recipe
View File

@ -96,16 +96,19 @@ GUITERM = window windlg winctrls terminal sizetip wcwidth unicode ldiscucs
NONSSH = telnet raw rlogin ldisc
# SSH back end (putty, plink, pscp, psftp).
SSH = ssh sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf noise
+ sshdh sshcrcda sshpubk pageantc sshzlib sshdss x11fwd portfwd
SSH = ssh sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
+ sshaes sshsh512 sshbn
WINSSH = SSH noise pageantc
UXSSH = SSH uxnoise uxagentc
# SFTP implementation (pscp, psftp).
SFTP = sftp int64 logging
# Miscellaneous objects appearing in all the network utilities (not
# Pageant or PuTTYgen).
MISC = misc version winstore settings tree234 winnet proxy cmdline
WINMISC = misc version winstore settings tree234 winnet proxy cmdline
UXMISC = misc version uxstore settings tree234 uxnet proxy cmdline
# Standard libraries, and the same with WinSocks 1 and 2.
LIBS = advapi32.lib user32.lib gdi32.lib comctl32.lib comdlg32.lib
@ -118,11 +121,12 @@ LIBS2 = LIBS ws2_32.lib
# keywords [G] for Windows GUI app, [C] for Console app, [X] for
# X/GTK Unix app.
putty : [G] GUITERM NONSSH SSH be_all MISC win_res.res LIBS1
puttytel : [G] GUITERM NONSSH be_nossh MISC win_res.res LIBS1
plink : [C] plink console NONSSH SSH be_all logging MISC plink.res LIBS2
pscp : [C] scp console SSH be_none SFTP wildcard MISC scp.res LIBS1
psftp : [C] psftp console SSH be_none SFTP MISC scp.res LIBS1
putty : [G] GUITERM NONSSH WINSSH be_all WINMISC win_res.res LIBS1
puttytel : [G] GUITERM NONSSH be_nossh WINMISC win_res.res LIBS1
plink : [C] plink console NONSSH WINSSH be_all logging WINMISC
+ plink.res LIBS2
pscp : [C] scp console WINSSH be_none SFTP wildcard WINMISC scp.res LIBS1
psftp : [C] psftp console WINSSH be_none SFTP WINMISC scp.res LIBS1
pageant : [G] pageant sshrsa sshpubk sshdes sshbn sshmd5 version tree234
+ misc sshaes sshsha pageantc sshdss sshsh512 winutils
@ -134,3 +138,5 @@ puttygen : [G] puttygen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
pterm : [X] pterm terminal wcwidth uxucs uxmisc tree234 misc ldisc ldiscucs
+ logging uxprint settings pty be_none uxstore
plink : [X] uxplink uxcons NONSSH UXSSH be_all logging UXMISC

View File

@ -16,10 +16,11 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
#include "putty.h"
@ -68,6 +69,13 @@ struct draw_ctx {
static int send_raw_mouse;
static char *app_name = "pterm";
char *x_get_default(char *key)
{
return XGetDefault(GDK_DISPLAY(), app_name, key);
}
void ldisc_update(void *frontend, int echo, int edit)
{
/*
@ -1726,8 +1734,6 @@ char *get_x_display(void *frontend)
return gdk_get_display();
}
char *app_name = "pterm";
static void help(FILE *fp) {
if(fprintf(fp,
"pterm option summary:\n"

View File

@ -35,11 +35,16 @@ char *get_x_display(void *frontend);
int font_dimension(void *frontend, int which);/* 0 for width, 1 for height */
/* Things uxstore.c needs from pterm.c */
char *app_name; /* for doing resource lookups */
char *x_get_default(char *key);
/* Things uxstore.c provides to pterm.c */
void provide_xrm_string(char *string);
/* Things uxnet.c provides to the front end */
int select_result(int fd, int event);
int first_socket(int *state, int *rwx);
int next_socket(int *state, int *rwx);
#define DEFAULT_CODEPAGE 0 /* FIXME: no idea how to do this */
#define strnicmp strncasecmp

25
unix/uxagentc.c Normal file
View File

@ -0,0 +1,25 @@
/*
* SSH agent client code.
*/
#include <stdio.h>
#include <stdlib.h>
#include "misc.h"
#include "puttymem.h"
#define GET_32BIT(cp) \
(((unsigned long)(unsigned char)(cp)[0] << 24) | \
((unsigned long)(unsigned char)(cp)[1] << 16) | \
((unsigned long)(unsigned char)(cp)[2] << 8) | \
((unsigned long)(unsigned char)(cp)[3]))
int agent_exists(void)
{
return FALSE; /* FIXME */
}
void agent_query(void *in, int inlen, void **out, int *outlen)
{
/* FIXME */
}

304
unix/uxcons.c Normal file
View File

@ -0,0 +1,304 @@
/*
* uxcons.c: various interactive-prompt routines shared between the
* Unix console PuTTY tools
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <termios.h>
#include <unistd.h>
#include "putty.h"
#include "storage.h"
#include "ssh.h"
int console_batch_mode = FALSE;
/*
* Clean up and exit.
*/
void cleanup_exit(int code)
{
/*
* Clean up.
*/
sk_cleanup();
exit(code);
}
void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
char *keystr, char *fingerprint)
{
int ret;
static const char absentmsg_batch[] =
"The server's host key is not cached. You have no guarantee\n"
"that the server is the computer you think it is.\n"
"The server's key fingerprint is:\n"
"%s\n"
"Connection abandoned.\n";
static const char absentmsg[] =
"The server's host key is not cached. You have no guarantee\n"
"that the server is the computer you think it is.\n"
"The server's key fingerprint is:\n"
"%s\n"
"If you trust this host, enter \"y\" to add the key to\n"
"PuTTY's cache and carry on connecting.\n"
"If you want to carry on connecting just once, without\n"
"adding the key to the cache, enter \"n\".\n"
"If you do not trust this host, press Return to abandon the\n"
"connection.\n"
"Store key in cache? (y/n) ";
static const char wrongmsg_batch[] =
"WARNING - POTENTIAL SECURITY BREACH!\n"
"The server's host key does not match the one PuTTY has\n"
"cached. This means that either the server administrator\n"
"has changed the host key, or you have actually connected\n"
"to another computer pretending to be the server.\n"
"The new key fingerprint is:\n"
"%s\n"
"Connection abandoned.\n";
static const char wrongmsg[] =
"WARNING - POTENTIAL SECURITY BREACH!\n"
"The server's host key does not match the one PuTTY has\n"
"cached. This means that either the server administrator\n"
"has changed the host key, or you have actually connected\n"
"to another computer pretending to be the server.\n"
"The new key fingerprint is:\n"
"%s\n"
"If you were expecting this change and trust the new key,\n"
"enter \"y\" to update PuTTY's cache and continue connecting.\n"
"If you want to carry on connecting but without updating\n"
"the cache, enter \"n\".\n"
"If you want to abandon the connection completely, press\n"
"Return to cancel. Pressing Return is the ONLY guaranteed\n"
"safe choice.\n"
"Update cached key? (y/n, Return cancels connection) ";
static const char abandoned[] = "Connection abandoned.\n";
char line[32];
/*
* Verify the key.
*/
ret = verify_host_key(host, port, keytype, keystr);
if (ret == 0) /* success - key matched OK */
return;
if (ret == 2) { /* key was different */
if (console_batch_mode) {
fprintf(stderr, wrongmsg_batch, fingerprint);
cleanup_exit(1);
}
fprintf(stderr, wrongmsg, fingerprint);
fflush(stderr);
}
if (ret == 1) { /* key was absent */
if (console_batch_mode) {
fprintf(stderr, absentmsg_batch, fingerprint);
cleanup_exit(1);
}
fprintf(stderr, absentmsg, fingerprint);
fflush(stderr);
}
{
struct termios oldmode, newmode;
tcgetattr(0, &oldmode);
newmode = oldmode;
newmode.c_lflag |= ECHO | ISIG | ICANON;
tcsetattr(0, TCSANOW, &newmode);
line[0] = '\0';
read(0, line, sizeof(line) - 1);
tcsetattr(0, TCSANOW, &oldmode);
}
if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
if (line[0] == 'y' || line[0] == 'Y')
store_host_key(host, port, keytype, keystr);
} else {
fprintf(stderr, abandoned);
cleanup_exit(0);
}
}
/*
* Ask whether the selected cipher is acceptable (since it was
* below the configured 'warn' threshold).
* cs: 0 = both ways, 1 = client->server, 2 = server->client
*/
void askcipher(void *frontend, char *ciphername, int cs)
{
static const char msg[] =
"The first %scipher supported by the server is\n"
"%s, which is below the configured warning threshold.\n"
"Continue with connection? (y/n) ";
static const char msg_batch[] =
"The first %scipher supported by the server is\n"
"%s, which is below the configured warning threshold.\n"
"Connection abandoned.\n";
static const char abandoned[] = "Connection abandoned.\n";
char line[32];
if (console_batch_mode) {
fprintf(stderr, msg_batch,
(cs == 0) ? "" :
(cs == 1) ? "client-to-server " : "server-to-client ",
ciphername);
cleanup_exit(1);
}
fprintf(stderr, msg,
(cs == 0) ? "" :
(cs == 1) ? "client-to-server " : "server-to-client ",
ciphername);
fflush(stderr);
{
struct termios oldmode, newmode;
tcgetattr(0, &oldmode);
newmode = oldmode;
newmode.c_lflag |= ECHO | ISIG | ICANON;
tcsetattr(0, TCSANOW, &newmode);
line[0] = '\0';
read(0, line, sizeof(line) - 1);
tcsetattr(0, TCSANOW, &oldmode);
}
if (line[0] == 'y' || line[0] == 'Y') {
return;
} else {
fprintf(stderr, abandoned);
cleanup_exit(0);
}
}
/*
* Ask whether to wipe a session log file before writing to it.
* Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
*/
int askappend(void *frontend, char *filename)
{
static const char msgtemplate[] =
"The session log file \"%.*s\" already exists.\n"
"You can overwrite it with a new session log,\n"
"append your session log to the end of it,\n"
"or disable session logging for this session.\n"
"Enter \"y\" to wipe the file, \"n\" to append to it,\n"
"or just press Return to disable logging.\n"
"Wipe the log file? (y/n, Return cancels logging) ";
static const char msgtemplate_batch[] =
"The session log file \"%.*s\" already exists.\n"
"Logging will not be enabled.\n";
char line[32];
if (cfg.logxfovr != LGXF_ASK) {
return ((cfg.logxfovr == LGXF_OVR) ? 2 : 1);
}
if (console_batch_mode) {
fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename);
fflush(stderr);
return 0;
}
fprintf(stderr, msgtemplate, FILENAME_MAX, filename);
fflush(stderr);
{
struct termios oldmode, newmode;
tcgetattr(0, &oldmode);
newmode = oldmode;
newmode.c_lflag |= ECHO | ISIG | ICANON;
tcsetattr(0, TCSANOW, &newmode);
line[0] = '\0';
read(0, line, sizeof(line) - 1);
tcsetattr(0, TCSANOW, &oldmode);
}
if (line[0] == 'y' || line[0] == 'Y')
return 2;
else if (line[0] == 'n' || line[0] == 'N')
return 1;
else
return 0;
}
/*
* Warn about the obsolescent key file format.
*
* Uniquely among these functions, this one does _not_ expect a
* frontend handle. This means that if PuTTY is ported to a
* platform which requires frontend handles, this function will be
* an anomaly. Fortunately, the problem it addresses will not have
* been present on that platform, so it can plausibly be
* implemented as an empty function.
*/
void old_keyfile_warning(void)
{
static const char message[] =
"You are loading an SSH 2 private key which has an\n"
"old version of the file format. This means your key\n"
"file is not fully tamperproof. Future versions of\n"
"PuTTY may stop supporting this private key format,\n"
"so we recommend you convert your key to the new\n"
"format.\n"
"\n"
"Once the key is loaded into PuTTYgen, you can perform\n"
"this conversion simply by saving it again.\n";
fputs(message, stderr);
}
void logevent(void *frontend, char *string)
{
}
int console_get_line(const char *prompt, char *str,
int maxlen, int is_pw)
{
struct termios oldmode, newmode;
int i;
if (console_batch_mode) {
if (maxlen > 0)
str[0] = '\0';
} else {
tcgetattr(0, &oldmode);
newmode = oldmode;
newmode.c_lflag |= ISIG | ICANON;
if (is_pw)
newmode.c_lflag &= ~ECHO;
else
newmode.c_lflag |= ECHO;
tcsetattr(0, TCSANOW, &newmode);
fputs(prompt, stdout);
fflush(stdout);
i = read(0, str, maxlen - 1);
tcsetattr(0, TCSANOW, &oldmode);
if (i > 0 && str[i-1] == '\n')
i--;
str[i] = '\0';
if (is_pw)
fputs("\r\n", stdout);
}
return 1;
}
void frontend_keypress(void *handle)
{
/*
* This is nothing but a stub, in console code.
*/
return;
}

1021
unix/uxnet.c Normal file

File diff suppressed because it is too large Load Diff

95
unix/uxnoise.c Normal file
View File

@ -0,0 +1,95 @@
/*
* Noise generation for PuTTY's cryptographic random number
* generator.
*/
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include "putty.h"
#include "ssh.h"
#include "storage.h"
/*
* FIXME. This module currently depends critically on /dev/urandom,
* because it has no fallback mechanism for doing anything else.
*/
static void read_dev_urandom(char *buf, int len)
{
int fd;
int ngot, ret;
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
perror("/dev/urandom: open");
exit(1);
}
ngot = 0;
while (ngot < len) {
ret = read(fd, buf+ngot, len-ngot);
if (ret < 0) {
perror("/dev/urandom: read");
exit(1);
}
ngot += ret;
}
}
/*
* This function is called once, at PuTTY startup. Currently it
* will read 32 bytes out of /dev/urandom and seed the internal
* generator with them.
*/
void noise_get_heavy(void (*func) (void *, int))
{
char buf[32];
read_dev_urandom(buf, sizeof(buf));
func(buf, sizeof(buf));
}
void random_save_seed(void)
{
/* Currently we do nothing here. FIXME? */
}
/*
* This function is called every time the urandom pool needs
* stirring, and will acquire the system time.
*/
void noise_get_light(void (*func) (void *, int))
{
struct timeval tv;
gettimeofday(&tv, NULL);
func(&tv, sizeof(tv));
}
/*
* This function is called on a timer, and it will just pull some
* stuff out of /dev/urandom. FIXME: really I suspect we ought not
* to deplete /dev/urandom like this. Better to grab something more
* harmless.
*/
void noise_regular(void)
{
char buf[4];
read_dev_urandom(buf, sizeof(buf));
random_add_noise(buf, sizeof(buf));
}
/*
* This function is called on every keypress or mouse move, and
* will add the current time to the noise pool. It gets the scan
* code or mouse position passed in, and adds that too.
*/
void noise_ultralight(unsigned long data)
{
struct timeval tv;
gettimeofday(&tv, NULL);
random_add_noise(&tv, sizeof(tv));
random_add_noise(&data, sizeof(data));
}

580
unix/uxplink.c Normal file
View File

@ -0,0 +1,580 @@
/*
* PLink - a command-line (stdin/stdout) variant of PuTTY.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
/* More helpful version of the FD_SET macro, to also handle maxfd. */
#define FD_SET_MAX(fd, max, set) do { \
FD_SET(fd, &set); \
if (max < fd + 1) max = fd + 1; \
} while (0)
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
#include "putty.h"
#include "storage.h"
#include "tree234.h"
#define MAX_STDIN_BACKLOG 4096
void fatalbox(char *p, ...)
{
va_list ap;
fprintf(stderr, "FATAL ERROR: ");
va_start(ap, p);
vfprintf(stderr, p, ap);
va_end(ap);
fputc('\n', stderr);
cleanup_exit(1);
}
void modalfatalbox(char *p, ...)
{
va_list ap;
fprintf(stderr, "FATAL ERROR: ");
va_start(ap, p);
vfprintf(stderr, p, ap);
va_end(ap);
fputc('\n', stderr);
cleanup_exit(1);
}
void connection_fatal(void *frontend, char *p, ...)
{
va_list ap;
fprintf(stderr, "FATAL ERROR: ");
va_start(ap, p);
vfprintf(stderr, p, ap);
va_end(ap);
fputc('\n', stderr);
cleanup_exit(1);
}
void cmdline_error(char *p, ...)
{
va_list ap;
fprintf(stderr, "plink: ");
va_start(ap, p);
vfprintf(stderr, p, ap);
va_end(ap);
fputc('\n', stderr);
exit(1);
}
struct termios orig_termios;
static Backend *back;
static void *backhandle;
char *x_get_default(char *key)
{
return NULL; /* this is a stub */
}
int term_ldisc(Terminal *term, int mode)
{
return FALSE;
}
void ldisc_update(void *frontend, int echo, int edit)
{
/* Update stdin read mode to reflect changes in line discipline. */
struct termios mode;
mode = orig_termios;
if (echo)
mode.c_lflag |= ECHO;
else
mode.c_lflag &= ~ECHO;
if (edit)
mode.c_lflag |= ISIG | ICANON;
else
mode.c_lflag &= ~(ISIG | ICANON);
tcsetattr(0, TCSANOW, &mode);
}
void cleanup_termios(void)
{
tcsetattr(0, TCSANOW, &orig_termios);
}
bufchain stdout_data, stderr_data;
void try_output(int is_stderr)
{
bufchain *chain = (is_stderr ? &stderr_data : &stdout_data);
int fd = (is_stderr ? 2 : 1);
void *senddata;
int sendlen, ret;
bufchain_prefix(chain, &senddata, &sendlen);
ret = write(fd, senddata, sendlen);
if (ret > 0)
bufchain_consume(chain, ret);
else if (ret < 0) {
perror(is_stderr ? "stderr: write" : "stdout: write");
exit(1);
}
}
int from_backend(void *frontend_handle, int is_stderr, char *data, int len)
{
int osize, esize;
assert(len > 0);
if (is_stderr) {
bufchain_add(&stderr_data, data, len);
try_output(1);
} else {
bufchain_add(&stdout_data, data, len);
try_output(0);
}
osize = bufchain_size(&stdout_data);
esize = bufchain_size(&stderr_data);
return osize + esize;
}
/*
* Short description of parameters.
*/
static void usage(void)
{
printf("PuTTY Link: command-line connection utility\n");
printf("%s\n", ver);
printf("Usage: plink [options] [user@]host [command]\n");
printf(" (\"host\" can also be a PuTTY saved session name)\n");
printf("Options:\n");
printf(" -v show verbose messages\n");
printf(" -load sessname Load settings from saved session\n");
printf(" -ssh -telnet -rlogin -raw\n");
printf(" force use of a particular protocol (default SSH)\n");
printf(" -P port connect to specified port\n");
printf(" -l user connect with specified username\n");
printf(" -m file read remote command(s) from file\n");
printf(" -batch disable all interactive prompts\n");
printf("The following options only apply to SSH connections:\n");
printf(" -pw passw login with specified password\n");
printf(" -L listen-port:host:port Forward local port to "
"remote address\n");
printf(" -R listen-port:host:port Forward remote port to"
" local address\n");
printf(" -X -x enable / disable X11 forwarding\n");
printf(" -A -a enable / disable agent forwarding\n");
printf(" -t -T enable / disable pty allocation\n");
printf(" -1 -2 force use of particular protocol version\n");
printf(" -C enable compression\n");
printf(" -i key private key file for authentication\n");
exit(1);
}
int main(int argc, char **argv)
{
int sending;
int portnumber = -1;
int *sklist;
int socket;
int i, skcount, sksize, socketstate;
int connopen;
int exitcode;
void *logctx;
void *ldisc;
ssh_get_line = console_get_line;
sklist = NULL;
skcount = sksize = 0;
/*
* Initialise port and protocol to sensible defaults. (These
* will be overridden by more or less anything.)
*/
default_protocol = PROT_SSH;
default_port = 22;
flags = FLAG_STDERR;
/*
* Process the command line.
*/
do_defaults(NULL, &cfg);
default_protocol = cfg.protocol;
default_port = cfg.port;
{
/*
* Override the default protocol if PLINK_PROTOCOL is set.
*/
char *p = getenv("PLINK_PROTOCOL");
int i;
if (p) {
for (i = 0; backends[i].backend != NULL; i++) {
if (!strcmp(backends[i].name, p)) {
default_protocol = cfg.protocol = backends[i].protocol;
default_port = cfg.port =
backends[i].backend->default_port;
break;
}
}
}
}
while (--argc) {
char *p = *++argv;
if (*p == '-') {
int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL), 1);
if (ret == -2) {
fprintf(stderr,
"plink: option \"%s\" requires an argument\n", p);
} else if (ret == 2) {
--argc, ++argv;
} else if (ret == 1) {
continue;
} else if (!strcmp(p, "-batch")) {
console_batch_mode = 1;
}
} else if (*p) {
if (!*cfg.host) {
char *q = p;
/*
* If the hostname starts with "telnet:", set the
* protocol to Telnet and process the string as a
* Telnet URL.
*/
if (!strncmp(q, "telnet:", 7)) {
char c;
q += 7;
if (q[0] == '/' && q[1] == '/')
q += 2;
cfg.protocol = PROT_TELNET;
p = q;
while (*p && *p != ':' && *p != '/')
p++;
c = *p;
if (*p)
*p++ = '\0';
if (c == ':')
cfg.port = atoi(p);
else
cfg.port = -1;
strncpy(cfg.host, q, sizeof(cfg.host) - 1);
cfg.host[sizeof(cfg.host) - 1] = '\0';
} else {
char *r;
/*
* Before we process the [user@]host string, we
* first check for the presence of a protocol
* prefix (a protocol name followed by ",").
*/
r = strchr(p, ',');
if (r) {
int i, j;
for (i = 0; backends[i].backend != NULL; i++) {
j = strlen(backends[i].name);
if (j == r - p &&
!memcmp(backends[i].name, p, j)) {
default_protocol = cfg.protocol =
backends[i].protocol;
portnumber =
backends[i].backend->default_port;
p = r + 1;
break;
}
}
}
/*
* Three cases. Either (a) there's a nonzero
* length string followed by an @, in which
* case that's user and the remainder is host.
* Or (b) there's only one string, not counting
* a potential initial @, and it exists in the
* saved-sessions database. Or (c) only one
* string and it _doesn't_ exist in the
* database.
*/
r = strrchr(p, '@');
if (r == p)
p++, r = NULL; /* discount initial @ */
if (r == NULL) {
/*
* One string.
*/
Config cfg2;
do_defaults(p, &cfg2);
if (cfg2.host[0] == '\0') {
/* No settings for this host; use defaults */
strncpy(cfg.host, p, sizeof(cfg.host) - 1);
cfg.host[sizeof(cfg.host) - 1] = '\0';
cfg.port = default_port;
} else {
cfg = cfg2;
cfg.remote_cmd_ptr = cfg.remote_cmd;
}
} else {
*r++ = '\0';
strncpy(cfg.username, p, sizeof(cfg.username) - 1);
cfg.username[sizeof(cfg.username) - 1] = '\0';
strncpy(cfg.host, r, sizeof(cfg.host) - 1);
cfg.host[sizeof(cfg.host) - 1] = '\0';
cfg.port = default_port;
}
}
} else {
char *command;
int cmdlen, cmdsize;
cmdlen = cmdsize = 0;
command = NULL;
while (argc) {
while (*p) {
if (cmdlen >= cmdsize) {
cmdsize = cmdlen + 512;
command = srealloc(command, cmdsize);
}
command[cmdlen++]=*p++;
}
if (cmdlen >= cmdsize) {
cmdsize = cmdlen + 512;
command = srealloc(command, cmdsize);
}
command[cmdlen++]=' '; /* always add trailing space */
if (--argc) p = *++argv;
}
if (cmdlen) command[--cmdlen]='\0';
/* change trailing blank to NUL */
cfg.remote_cmd_ptr = command;
cfg.remote_cmd_ptr2 = NULL;
cfg.nopty = TRUE; /* command => no terminal */
break; /* done with cmdline */
}
}
}
if (!*cfg.host) {
usage();
}
/*
* Trim leading whitespace off the hostname if it's there.
*/
{
int space = strspn(cfg.host, " \t");
memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
}
/* See if host is of the form user@host */
if (cfg.host[0] != '\0') {
char *atsign = strchr(cfg.host, '@');
/* Make sure we're not overflowing the user field */
if (atsign) {
if (atsign - cfg.host < sizeof cfg.username) {
strncpy(cfg.username, cfg.host, atsign - cfg.host);
cfg.username[atsign - cfg.host] = '\0';
}
memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
}
}
/*
* Perform command-line overrides on session configuration.
*/
cmdline_run_saved();
/*
* Trim a colon suffix off the hostname if it's there.
*/
cfg.host[strcspn(cfg.host, ":")] = '\0';
/*
* Remove any remaining whitespace from the hostname.
*/
{
int p1 = 0, p2 = 0;
while (cfg.host[p2] != '\0') {
if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
cfg.host[p1] = cfg.host[p2];
p1++;
}
p2++;
}
cfg.host[p1] = '\0';
}
if (!*cfg.remote_cmd_ptr)
flags |= FLAG_INTERACTIVE;
/*
* Select protocol. This is farmed out into a table in a
* separate file to enable an ssh-free variant.
*/
{
int i;
back = NULL;
for (i = 0; backends[i].backend != NULL; i++)
if (backends[i].protocol == cfg.protocol) {
back = backends[i].backend;
break;
}
if (back == NULL) {
fprintf(stderr,
"Internal fault: Unsupported protocol found\n");
return 1;
}
}
/*
* Select port.
*/
if (portnumber != -1)
cfg.port = portnumber;
sk_init();
/*
* Start up the connection.
*/
{
char *error;
char *realhost;
/* nodelay is only useful if stdin is a terminal device */
int nodelay = cfg.tcp_nodelay && isatty(0);
error = back->init(NULL, &backhandle, cfg.host, cfg.port,
&realhost, nodelay);
if (error) {
fprintf(stderr, "Unable to open connection:\n%s", error);
return 1;
}
logctx = log_init(NULL);
back->provide_logctx(backhandle, logctx);
ldisc = ldisc_create(NULL, back, backhandle, NULL);
sfree(realhost);
}
connopen = 1;
/*
* Set up the initial console mode. We don't care if this call
* fails, because we know we aren't necessarily running in a
* console.
*/
tcgetattr(0, &orig_termios);
atexit(cleanup_termios);
ldisc_update(NULL, 1, 1);
sending = FALSE;
while (1) {
fd_set rset, wset, xset;
int maxfd;
int rwx;
int ret;
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&xset);
maxfd = 0;
if (connopen && !sending &&
back->socket(backhandle) != NULL &&
back->sendok(backhandle) &&
back->sendbuffer(backhandle) < MAX_STDIN_BACKLOG) {
/* If we're OK to send, then try to read from stdin. */
FD_SET_MAX(0, maxfd, rset);
}
if (bufchain_size(&stdout_data) > 0) {
/* If we have data for stdout, try to write to stdout. */
FD_SET_MAX(1, maxfd, wset);
}
if (bufchain_size(&stderr_data) > 0) {
/* If we have data for stderr, try to write to stderr. */
FD_SET_MAX(2, maxfd, wset);
}
/* Count the currently active sockets. */
i = 0;
for (socket = first_socket(&socketstate, &rwx); socket >= 0;
socket = next_socket(&socketstate, &rwx)) i++;
/* Expand the sklist buffer if necessary. */
if (i > sksize) {
sksize = i + 16;
sklist = srealloc(sklist, sksize * sizeof(*sklist));
}
/*
* Add all currently open sockets to the select sets, and
* store them in sklist as well.
*/
skcount = 0;
for (socket = first_socket(&socketstate, &rwx); socket >= 0;
socket = next_socket(&socketstate, &rwx)) {
sklist[skcount++] = socket;
if (rwx & 1)
FD_SET_MAX(socket, maxfd, rset);
if (rwx & 2)
FD_SET_MAX(socket, maxfd, wset);
if (rwx & 4)
FD_SET_MAX(socket, maxfd, xset);
}
ret = select(maxfd, &rset, &wset, &xset, NULL);
if (ret < 0) {
perror("select");
exit(1);
}
for (i = 0; i < skcount; i++) {
socket = sklist[i];
if (FD_ISSET(socket, &rset))
select_result(socket, 1);
if (FD_ISSET(socket, &wset))
select_result(socket, 2);
if (FD_ISSET(socket, &xset))
select_result(socket, 4);
}
if (FD_ISSET(0, &rset)) {
char buf[4096];
int ret;
if (connopen && back->socket(backhandle) != NULL) {
ret = read(0, buf, sizeof(buf));
if (ret < 0) {
perror("stdin: read");
exit(1);
} else if (ret == 0) {
back->special(backhandle, TS_EOF);
sending = FALSE; /* send nothing further after this */
} else {
back->send(backhandle, buf, ret);
}
}
}
if (FD_ISSET(1, &wset)) {
try_output(0);
}
if (FD_ISSET(2, &wset)) {
try_output(1);
}
if ((!connopen || back->socket(backhandle) == NULL) &&
bufchain_size(&stdout_data) == 0 &&
bufchain_size(&stderr_data) == 0)
break; /* we closed the connection */
}
exitcode = back->exitcode(backhandle);
if (exitcode < 0) {
fprintf(stderr, "Remote process exit code unavailable\n");
exitcode = 1; /* this is an error condition */
}
return exitcode;
}

View File

@ -6,9 +6,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <X11/Xlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "putty.h"
#include "storage.h"
#include "tree234.h"
@ -46,8 +47,6 @@ void close_settings_w(void *handle)
* immediately work out.
*/
static Display *display;
struct xrm_string {
char *key;
char *value;
@ -104,17 +103,13 @@ char *get_setting(char *key)
if (ret)
return ret->value;
}
return XGetDefault(display, app_name, key);
return x_get_default(key);
}
void *open_settings_r(char *sessionname)
{
static int thing_to_return_an_arbitrary_non_null_pointer_to;
display = GDK_DISPLAY();
if (!display)
return NULL;
else
return &thing_to_return_an_arbitrary_non_null_pointer_to;
return &thing_to_return_an_arbitrary_non_null_pointer_to;
}
char *read_setting_s(void *handle, char *key, char *buffer, int buflen)
@ -160,13 +155,144 @@ void enum_settings_finish(void *handle)
{
}
enum {
INDEX_DIR, INDEX_HOSTKEYS
};
static void make_filename(char *filename, int index)
{
char *home;
int len;
home = getenv("HOME");
strncpy(filename, home, FILENAME_MAX);
len = strlen(filename);
strncpy(filename + len,
index == INDEX_DIR ? "/.putty" :
index == INDEX_HOSTKEYS ? "/.putty/sshhostkeys" :
"/.putty/ERROR", FILENAME_MAX - len);
filename[FILENAME_MAX-1] = '\0';
}
/*
* Read an entire line of text from a file. Return a buffer
* malloced to be as big as necessary (caller must free).
*/
static char *fgetline(FILE *fp)
{
char *ret = smalloc(512);
int size = 512, len = 0;
while (fgets(ret + len, size - len, fp)) {
len += strlen(ret + len);
if (ret[len-1] == '\n')
break; /* got a newline, we're done */
size = len + 512;
ret = srealloc(ret, size);
}
if (len == 0) { /* first fgets returned NULL */
sfree(ret);
return NULL;
}
ret[len] = '\0';
return ret;
}
/*
* Lines in the host keys file are of the form
*
* type@port:hostname keydata
*
* e.g.
*
* rsa@22:foovax.example.org 0x23,0x293487364395345345....2343
*/
int verify_host_key(char *hostname, int port, char *keytype, char *key)
{
return 1; /* key does not exist in registry */
FILE *fp;
char filename[FILENAME_MAX];
char *line;
int ret;
make_filename(filename, INDEX_HOSTKEYS);
fp = fopen(filename, "r");
if (!fp)
return 1; /* key does not exist */
ret = 1;
while ( (line = fgetline(fp)) ) {
int i;
char *p = line;
char porttext[20];
line[strcspn(line, "\n")] = '\0'; /* strip trailing newline */
i = strlen(keytype);
if (strncmp(p, keytype, i))
goto done;
p += i;
if (*p != '@')
goto done;
p++;
sprintf(porttext, "%d", port);
i = strlen(porttext);
if (strncmp(p, porttext, i))
goto done;
p += i;
if (*p != ':')
goto done;
p++;
i = strlen(hostname);
if (strncmp(p, hostname, i))
goto done;
p += i;
if (*p != ' ')
goto done;
p++;
/*
* Found the key. Now just work out whether it's the right
* one or not.
*/
if (!strcmp(p, key))
ret = 0; /* key matched OK */
else
ret = 2; /* key mismatch */
done:
sfree(line);
if (ret != 1)
break;
}
return ret;
}
void store_host_key(char *hostname, int port, char *keytype, char *key)
{
FILE *fp;
int fd;
char filename[FILENAME_MAX];
make_filename(filename, INDEX_HOSTKEYS);
fd = open(filename, O_CREAT | O_APPEND | O_RDWR, 0600);
if (fd < 0) {
char dir[FILENAME_MAX];
make_filename(dir, INDEX_DIR);
mkdir(dir, 0700);
fd = open(filename, O_CREAT | O_APPEND | O_RDWR, 0600);
}
if (fd < 0) {
perror(filename);
exit(1);
}
fp = fdopen(fd, "a");
fprintf(fp, "%s@%d:%s %s\n", keytype, port, hostname, key);
fclose(fp);
}
void read_random_seed(noise_consumer_t consumer)