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

psftp now works as part of the PuTTY suite

[originally from svn r940]
This commit is contained in:
Simon Tatham 2001-02-24 16:08:56 +00:00
parent 094dd30d95
commit 39cf689fd6
6 changed files with 508 additions and 70 deletions

View File

@ -74,10 +74,12 @@ TOBJS = be_nossh.$(OBJ)
PLOBJS = plink.$(OBJ)
##-- objects pscp
SOBJS = scp.$(OBJ) winnet.$(OBJ) be_none.$(OBJ)
##-- objects putty puttytel pscp plink
##-- objects psftp
SOBJS = psftp.$(OBJ) sftp.$(OBJ) int64.$(OBJ) winnet.$(OBJ) be_none.$(OBJ)
##-- objects putty puttytel pscp psftp plink
MOBJS = misc.$(OBJ) version.$(OBJ) winstore.$(OBJ) settings.$(OBJ)
MOBJ2 = tree234.$(OBJ)
##-- objects putty pscp plink
##-- objects putty pscp psftp plink
OBJS1 = sshcrc.$(OBJ) sshdes.$(OBJ) sshmd5.$(OBJ) sshrsa.$(OBJ) sshrand.$(OBJ)
OBJS2 = sshsha.$(OBJ) sshblowf.$(OBJ) noise.$(OBJ) sshdh.$(OBJ) sshdss.$(OBJ)
OBJS3 = sshbn.$(OBJ) sshpubk.$(OBJ) ssh.$(OBJ) pageantc.$(OBJ) sshzlib.$(OBJ)
@ -96,7 +98,7 @@ PRESRC = win_res.$(RES)
PAGERC = pageant.$(RES)
##-- resources puttygen
GENRC = puttygen.$(RES)
##-- resources pscp
##-- resources pscp psftp
SRESRC = scp.$(RES)
##-- resources plink
LRESRC = plink.$(RES)
@ -109,6 +111,7 @@ LRESRC = plink.$(RES)
# puttygen
##-- console-apps
# pscp
# psftp
# plink ws2_32
##--
@ -256,6 +259,9 @@ pageant.$(OBJ): pageant.c ssh.h puttymem.h tree234.h
pageantc.$(OBJ): pageantc.c puttymem.h
tree234.$(OBJ): tree234.c tree234.h puttymem.h
puttygen.$(OBJ): puttygen.c putty.h ssh.h winstuff.h
psftp.$(OBJ): psftp.c putty.h ssh.h storage.h sftp.h int64.h
sftp.$(OBJ): psftp.c sftp.h int64.h
int64.$(OBJ): int64.c int64.h
##--
# Hack to force version.obj to be rebuilt always

470
psftp.c
View File

@ -2,18 +2,20 @@
* psftp.c: front end for PSFTP.
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#define PUTTY_DO_GLOBALS
#include "putty.h"
#include "storage.h"
#include "ssh.h"
#include "sftp.h"
#include "int64.h"
#define smalloc malloc
#define srealloc realloc
#define sfree free
/* ----------------------------------------------------------------------
* String handling routines.
*/
@ -75,12 +77,20 @@ char *pwd, *homedir;
*/
char *canonify(char *name) {
char *fullname, *canonname;
if (name[0] == '/') {
fullname = dupstr(name);
} else {
fullname = dupcat(pwd, "/", name, NULL);
char *slash;
if (pwd[strlen(pwd)-1] == '/')
slash = "";
else
slash = "/";
fullname = dupcat(pwd, slash, name, NULL);
}
canonname = fxp_realpath(name);
canonname = fxp_realpath(fullname);
if (canonname) {
sfree(fullname);
return canonname;
@ -383,6 +393,7 @@ static struct sftp_cmd_lookup {
*/
{"bye", sftp_cmd_quit},
{"cd", sftp_cmd_cd},
{"dir", sftp_cmd_ls},
{"exit", sftp_cmd_quit},
{"get", sftp_cmd_get},
{"ls", sftp_cmd_ls},
@ -539,15 +550,448 @@ void do_sftp(void) {
if (cmd->obey(cmd) < 0)
break;
}
/* ------------------------------------------------------------------
* We've received an exit command. Tidy up and leave.
*/
io_finish();
}
int main(void) {
io_init();
/* ----------------------------------------------------------------------
* Dirty bits: integration with PuTTY.
*/
static int verbose = 0;
void verify_ssh_host_key(char *host, int port, char *keytype,
char *keystr, char *fingerprint) {
int ret;
static const char absentmsg[] =
"The server's host key is not cached in the registry. You\n"
"have no guarantee that the server is the computer you\n"
"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 do not trust this host, enter \"n\" to abandon the\n"
"connection.\n"
"Continue connecting? (y/n) ";
static const char wrongmsg[] =
"WARNING - POTENTIAL SECURITY BREACH!\n"
"The server's host key does not match the one PuTTY has\n"
"cached in the registry. This means that either the\n"
"server administrator has changed the host key, or you\n"
"have actually connected to another computer pretending\n"
"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 Yes to update PuTTY's cache and continue connecting.\n"
"If you want to carry on connecting but without updating\n"
"the cache, enter No.\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 against the registry.
*/
ret = verify_host_key(host, port, keytype, keystr);
if (ret == 0) /* success - key matched OK */
return;
if (ret == 2) { /* key was different */
fprintf(stderr, wrongmsg, fingerprint);
if (fgets(line, sizeof(line), stdin) &&
line[0] != '\0' && line[0] != '\n') {
if (line[0] == 'y' || line[0] == 'Y')
store_host_key(host, port, keytype, keystr);
} else {
fprintf(stderr, abandoned);
exit(0);
}
}
if (ret == 1) { /* key was absent */
fprintf(stderr, absentmsg, fingerprint);
if (fgets(line, sizeof(line), stdin) &&
(line[0] == 'y' || line[0] == 'Y'))
store_host_key(host, port, keytype, keystr);
else {
fprintf(stderr, abandoned);
exit(0);
}
}
}
/*
* Print an error message and perform a fatal exit.
*/
void fatalbox(char *fmt, ...)
{
char str[0x100]; /* Make the size big enough */
va_list ap;
va_start(ap, fmt);
strcpy(str, "Fatal:");
vsprintf(str+strlen(str), fmt, ap);
va_end(ap);
strcat(str, "\n");
fprintf(stderr, str);
exit(1);
}
void connection_fatal(char *fmt, ...)
{
char str[0x100]; /* Make the size big enough */
va_list ap;
va_start(ap, fmt);
strcpy(str, "Fatal:");
vsprintf(str+strlen(str), fmt, ap);
va_end(ap);
strcat(str, "\n");
fprintf(stderr, str);
exit(1);
}
void logevent(char *string) { }
void ldisc_send(char *buf, int len) {
/*
* This is only here because of the calls to ldisc_send(NULL,
* 0) in ssh.c. Nothing in PSFTP actually needs to use the
* ldisc as an ldisc. So if we get called with any real data, I
* want to know about it.
*/
assert(len == 0);
}
/*
* Be told what socket we're supposed to be using.
*/
static SOCKET sftp_ssh_socket;
char *do_select(SOCKET skt, int startup) {
if (startup)
sftp_ssh_socket = skt;
else
sftp_ssh_socket = INVALID_SOCKET;
return NULL;
}
extern int select_result(WPARAM, LPARAM);
/*
* Receive a block of data from the SSH link. Block until all data
* is available.
*
* To do this, we repeatedly call the SSH protocol module, with our
* own trap in from_backend() to catch the data that comes back. We
* do this until we have enough data.
*/
static unsigned char *outptr; /* where to put the data */
static unsigned outlen; /* how much data required */
static unsigned char *pending = NULL; /* any spare data */
static unsigned pendlen=0, pendsize=0; /* length and phys. size of buffer */
void from_backend(int is_stderr, char *data, int datalen) {
unsigned char *p = (unsigned char *)data;
unsigned len = (unsigned)datalen;
/*
* stderr data is just spouted to local stderr and otherwise
* ignored.
*/
if (is_stderr) {
fwrite(data, 1, len, stderr);
return;
}
/*
* If this is before the real session begins, just return.
*/
if (!outptr)
return;
if (outlen > 0) {
unsigned used = outlen;
if (used > len) used = len;
memcpy(outptr, p, used);
outptr += used; outlen -= used;
p += used; len -= used;
}
if (len > 0) {
if (pendsize < pendlen + len) {
pendsize = pendlen + len + 4096;
pending = (pending ? srealloc(pending, pendsize) :
smalloc(pendsize));
if (!pending)
fatalbox("Out of memory");
}
memcpy(pending+pendlen, p, len);
pendlen += len;
}
}
int sftp_recvdata(char *buf, int len) {
outptr = (unsigned char *)buf;
outlen = len;
/*
* See if the pending-input block contains some of what we
* need.
*/
if (pendlen > 0) {
unsigned pendused = pendlen;
if (pendused > outlen)
pendused = outlen;
memcpy(outptr, pending, pendused);
memmove(pending, pending+pendused, pendlen-pendused);
outptr += pendused;
outlen -= pendused;
pendlen -= pendused;
if (pendlen == 0) {
pendsize = 0;
sfree(pending);
pending = NULL;
}
if (outlen == 0)
return 1;
}
while (outlen > 0) {
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(sftp_ssh_socket, &readfds);
if (select(1, &readfds, NULL, NULL, NULL) < 0)
return 0; /* doom */
select_result((WPARAM)sftp_ssh_socket, (LPARAM)FD_READ);
}
return 1;
}
int sftp_senddata(char *buf, int len) {
back->send((unsigned char *)buf, len);
return 1;
}
/*
* Loop through the ssh connection and authentication process.
*/
static void ssh_sftp_init(void) {
if (sftp_ssh_socket == INVALID_SOCKET)
return;
while (!back->sendok()) {
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(sftp_ssh_socket, &readfds);
if (select(1, &readfds, NULL, NULL, NULL) < 0)
return; /* doom */
select_result((WPARAM)sftp_ssh_socket, (LPARAM)FD_READ);
}
}
static char *password = NULL;
static int get_password(const char *prompt, char *str, int maxlen)
{
HANDLE hin, hout;
DWORD savemode, i;
if (password) {
static int tried_once = 0;
if (tried_once) {
return 0;
} else {
strncpy(str, password, maxlen);
str[maxlen-1] = '\0';
tried_once = 1;
return 1;
}
}
hin = GetStdHandle(STD_INPUT_HANDLE);
hout = GetStdHandle(STD_OUTPUT_HANDLE);
if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
fprintf(stderr, "Cannot get standard input/output handles\n");
exit(1);
}
GetConsoleMode(hin, &savemode);
SetConsoleMode(hin, (savemode & (~ENABLE_ECHO_INPUT)) |
ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT);
WriteFile(hout, prompt, strlen(prompt), &i, NULL);
ReadFile(hin, str, maxlen-1, &i, NULL);
SetConsoleMode(hin, savemode);
if ((int)i > maxlen) i = maxlen-1; else i = i - 2;
str[i] = '\0';
WriteFile(hout, "\r\n", 2, &i, NULL);
return 1;
}
/*
* Initialize the Win$ock driver.
*/
static void init_winsock(void)
{
WORD winsock_ver;
WSADATA wsadata;
winsock_ver = MAKEWORD(1, 1);
if (WSAStartup(winsock_ver, &wsadata)) {
fprintf(stderr, "Unable to initialise WinSock");
exit(1);
}
if (LOBYTE(wsadata.wVersion) != 1 ||
HIBYTE(wsadata.wVersion) != 1) {
fprintf(stderr, "WinSock version is incompatible with 1.1");
exit(1);
}
}
/*
* Short description of parameters.
*/
static void usage(void)
{
printf("PuTTY Secure File Transfer (SFTP) client\n");
printf("%s\n", ver);
printf("Usage: psftp [options] user@host\n");
printf("Options:\n");
printf(" -v show verbose messages\n");
printf(" -P port connect to specified port\n");
printf(" -pw passw login with specified password\n");
exit(1);
}
/*
* Main program. Parse arguments etc.
*/
int main(int argc, char *argv[])
{
int i;
int portnumber = 0;
char *user, *host, *userhost, *realhost;
char *err;
flags = FLAG_STDERR;
ssh_get_password = &get_password;
init_winsock();
sk_init();
userhost = user = NULL;
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-') {
if (userhost)
usage();
else
userhost = dupstr(argv[i]);
} else if (strcmp(argv[i], "-v") == 0) {
verbose = 1, flags |= FLAG_VERBOSE;
} else if (strcmp(argv[i], "-h") == 0 ||
strcmp(argv[i], "-?") == 0) {
usage();
} else if (strcmp(argv[i], "-l") == 0 && i+1 < argc) {
user = argv[++i];
} else if (strcmp(argv[i], "-P") == 0 && i+1 < argc) {
portnumber = atoi(argv[++i]);
} else if (strcmp(argv[i], "-pw") == 0 && i+1 < argc) {
password = argv[++i];
} else if (strcmp(argv[i], "--") == 0) {
i++;
break;
} else {
usage();
}
}
argc -= i;
argv += i;
back = NULL;
if (argc > 0 || !userhost)
usage();
/* Separate host and username */
host = userhost;
host = strrchr(host, '@');
if (host == NULL) {
host = userhost;
} else {
*host++ = '\0';
if (user) {
printf("psftp: multiple usernames specified; using \"%s\"\n", user);
} else
user = userhost;
}
/* Try to load settings for this host */
do_defaults(host, &cfg);
if (cfg.host[0] == '\0') {
/* No settings for this host; use defaults */
do_defaults(NULL, &cfg);
strncpy(cfg.host, host, sizeof(cfg.host)-1);
cfg.host[sizeof(cfg.host)-1] = '\0';
cfg.port = 22;
}
/* Set username */
if (user != NULL && user[0] != '\0') {
strncpy(cfg.username, user, sizeof(cfg.username)-1);
cfg.username[sizeof(cfg.username)-1] = '\0';
}
if (!cfg.username[0]) {
printf("login as: ");
if (!fgets(cfg.username, sizeof(cfg.username), stdin)) {
fprintf(stderr, "psftp: aborting\n");
exit(1);
} else {
int len = strlen(cfg.username);
if (cfg.username[len-1] == '\n')
cfg.username[len-1] = '\0';
}
}
if (cfg.protocol != PROT_SSH)
cfg.port = 22;
if (portnumber)
cfg.port = portnumber;
/* SFTP uses SSH2 by default always */
cfg.sshprot = 2;
/* Set up subsystem name. FIXME: fudge for SSH1. */
strcpy(cfg.remote_cmd, "sftp");
cfg.ssh_subsys = TRUE;
cfg.nopty = TRUE;
back = &ssh_backend;
err = back->init(cfg.host, cfg.port, &realhost);
if (err != NULL) {
fprintf(stderr, "ssh_init: %s", err);
return 1;
}
ssh_sftp_init();
if (verbose && realhost != NULL)
printf("Connected to %s\n", realhost);
do_sftp();
if (back != NULL && back->socket() != NULL) {
char ch;
back->special(TS_EOF);
sftp_recvdata(&ch, 1);
}
WSACleanup();
random_save_seed();
return 0;
}

View File

@ -176,6 +176,7 @@ typedef struct {
int sshprot; /* use v1 or v2 when both available */
int buggymac; /* MAC bug commmercial <=v2.3.x SSH2 */
int try_tis_auth;
int ssh_subsys; /* run a subsystem rather than a command */
/* Telnet options */
char termtype[32];
char termspeed[32];

70
sftp.c
View File

@ -4,8 +4,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include "int64.h"
#include "sftp.h"
@ -168,71 +168,33 @@ static void sftp_pkt_free(struct sftp_packet *pkt) {
}
/* ----------------------------------------------------------------------
* Send and receive packet functions. FIXME: change for PuTTY.
* Send and receive packet functions.
*/
int tossh, fromssh;
int io_init(void) {
int to[2], from[2];
int pid;
assert(pipe(to) == 0);
assert(pipe(from) == 0);
pid = fork();
assert(pid >= 0);
if (pid == 0) {
/* We are child. Dup one end of each pipe to our std[io],
* close other end, exec. */
close(0); dup2(to[0], 0); close(to[1]);
close(1); dup2(from[1], 1); close(from[0]);
execl("/home/simon/src/openssh/openssh_cvs/prefix/bin/ssh", "ssh", "-2", "simon@localhost", "-s", "sftp", NULL);
//execl("/root/ssh-research/ssh-2.4.0/apps/ssh/sftp-server2", "sftp-server2", NULL);
//execl("/usr/lib/sftp-server", "sftp-server", NULL);
assert(0); /* bomb out if not */
} else {
/* We are parent. Close wrong end of each pipe, assign to glob vars. */
close(to[0]); tossh = to[1];
close(from[1]); fromssh = from[0];
}
}
int io_finish(void) {
int pid, status;
close(tossh);
close(fromssh);
pid = wait(&status);
}
int sftp_send(struct sftp_packet *pkt) {
int ret;
char x[4];
PUT_32BIT(x, pkt->length);
assert(4 == write(tossh, x, 4));
assert(pkt->length = write(tossh, pkt->data, pkt->length));
ret = (sftp_senddata(x, 4) &&
sftp_senddata(pkt->data, pkt->length));
sftp_pkt_free(pkt);
return ret;
}
struct sftp_packet *sftp_recv(void) {
struct sftp_packet *pkt;
char x[4];
int p, ret;
for (p = 0; p < 4 ;) {
ret = read(fromssh, x+p, 4-p);
assert(ret >= 0);
if (ret == 0)
return NULL;
p += ret;
}
if (!sftp_recvdata(x, 4))
return NULL;
pkt = smalloc(sizeof(struct sftp_packet));
pkt->savedpos = 0;
pkt->length = pkt->maxlen = GET_32BIT(x);
pkt->data = smalloc(pkt->length);
for (p = 0; p < pkt->length ;) {
ret = read(fromssh, pkt->data+p, pkt->length-p);
assert(ret >= 0);
if (ret == 0) {
sftp_pkt_free(pkt);
return NULL;
}
p += ret;
if (!sftp_recvdata(pkt->data, pkt->length)) {
sftp_pkt_free(pkt);
return NULL;
}
pkt->type = sftp_pkt_getbyte(pkt);
@ -299,7 +261,7 @@ static int fxp_got_status(struct sftp_packet *pktin) {
return -1;
}
static int fxp_internal_error(char *msg) {
static void fxp_internal_error(char *msg) {
fxp_error_message = msg;
fxp_errtype = -1;
}
@ -510,7 +472,7 @@ int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset, int len) {
id = sftp_pkt_getuint32(pktin);
if (id != 0xBCD) {
fxp_internal_error("request ID mismatch");
return;
return -1;
}
if (pktin->type == SSH_FXP_DATA) {
char *str;
@ -548,7 +510,7 @@ struct fxp_names *fxp_readdir(struct fxp_handle *handle) {
id = sftp_pkt_getuint32(pktin);
if (id != 0xABC) {
fxp_internal_error("request ID mismatch\n");
return;
return NULL;
}
if (pktin->type == SSH_FXP_NAME) {
struct fxp_names *ret;
@ -589,6 +551,10 @@ int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset, int len) {
sftp_send(pktout);
pktin = sftp_recv();
id = sftp_pkt_getuint32(pktin);
if (id != 0xDCB) {
fxp_internal_error("request ID mismatch\n");
return NULL;
}
fxp_got_status(pktin);
return fxp_errtype == SSH_FX_OK;
}

17
sftp.h
View File

@ -55,6 +55,18 @@
#define SFTP_PROTO_VERSION 3
/*
* External references. The sftp client module sftp.c expects to be
* able to get at these functions.
*
* sftp_recvdata must never return less than len. It either blocks
* until len is available, or it returns failure.
*
* Both functions return 1 on success, 0 on failure.
*/
int sftp_senddata(char *data, int len);
int sftp_recvdata(char *data, int len);
struct fxp_attrs {
unsigned long flags;
uint64 size;
@ -114,6 +126,11 @@ void fxp_close(struct fxp_handle *handle);
*/
int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset, int len);
/*
* Write to a file. Returns 0 on error, 1 on OK.
*/
int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset, int len);
/*
* Read from a directory.
*/

8
ssh.c
View File

@ -20,7 +20,7 @@
fprintf(stderr, "%s\n", s); }
#define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, \
(s ? sk_close(s), s = NULL : (void)0), \
(s ? sk_close(s), s = NULL : 0), \
connection_fatal msg )
#define SSH1_MSG_DISCONNECT 1 /* 0x1 */
@ -2858,7 +2858,11 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
*/
ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
if (*cfg.remote_cmd) {
if (cfg.ssh_subsys) {
ssh2_pkt_addstring("subsystem");
ssh2_pkt_addbool(1); /* want reply */
ssh2_pkt_addstring(cfg.remote_cmd);
} else if (*cfg.remote_cmd) {
ssh2_pkt_addstring("exec");
ssh2_pkt_addbool(1); /* want reply */
ssh2_pkt_addstring(cfg.remote_cmd);