ssh: R.I.P.
This commit is contained in:
parent
cb1555c7d7
commit
dc8c7bf2b7
25 changed files with 0 additions and 6281 deletions
101
rc/bin/srvssh
101
rc/bin/srvssh
|
@ -1,101 +0,0 @@
|
|||
#!/bin/rc
|
||||
|
||||
# Serve Unix u9fs over SSH
|
||||
#
|
||||
# Basically, try each of the following until you find one that works:
|
||||
#
|
||||
# srvssh unix
|
||||
# srvssh -r unix
|
||||
# srvssh -R unix
|
||||
# srvssh -r -s unix
|
||||
# srvssh -R -s unix
|
||||
#
|
||||
# and then never look back. Note that "srvssh unix" should always
|
||||
# work. It's just that if you're talking with certain sshd's, you'll get
|
||||
# hit by Nagle's algorithm and need to explore the other flags.
|
||||
|
||||
# When using ssh to start u9fs, the only way to turn off
|
||||
# Nagle's algorithm (which kills the performance of RPC-based
|
||||
# protocols like 9P) is to allocate a pseudo-terminal. The
|
||||
# command ssh -Rmp attempts to allocate a pseudo-terminal and
|
||||
# then put it in a transparent mode. Especially when
|
||||
# connected to older SSH daemons, the connection ends up not
|
||||
# quite transparent. To get around this, we explicity set the tty
|
||||
# mode on the command line as well. The hope is that -Rmp makes
|
||||
# the connection transparent enough for the Tversion, and the stty
|
||||
# command will do the rest. If -Rmp doesn't make the connection
|
||||
# transparent enough for the Tversion (but the stty commands do
|
||||
# make the connection fully transparent) then add "-s 5" to the srv
|
||||
# command to tell it to wait 5 seconds before sending the Tversion.
|
||||
# That should be enough time for the stty to take effect.
|
||||
|
||||
rfork e
|
||||
|
||||
fn usage {
|
||||
echo 'usage: srvssh [-R] [-r] [-s] [-u u9fspath] system [srvname [mtpt]]' >[1=2]
|
||||
exit usage
|
||||
}
|
||||
|
||||
rawhack=''
|
||||
sleephack=()
|
||||
u9fspath=u9fs
|
||||
rawflags=''
|
||||
|
||||
while(~ $1 -*){
|
||||
switch($1){
|
||||
case -r
|
||||
rawflags='-Rmp'
|
||||
shift
|
||||
case -R
|
||||
rawflags='-Rmp'
|
||||
rawhack=('stty raw -echo '';''')
|
||||
shift
|
||||
case -s
|
||||
sleephack=(-s 5)
|
||||
shift
|
||||
case -u
|
||||
shift
|
||||
u9fspath=$1
|
||||
shift
|
||||
case -u*
|
||||
u9fspath=`{echo $1 | sed s/-u//}
|
||||
shift
|
||||
case *
|
||||
usage
|
||||
}
|
||||
}
|
||||
|
||||
if(! ~ $#* 1 2 3)
|
||||
usage
|
||||
|
||||
switch($#*){
|
||||
case 1
|
||||
srv=$1
|
||||
mtpt=/n/$1
|
||||
case 2
|
||||
srv=$2
|
||||
mtpt=/n/$1
|
||||
case 3
|
||||
srv=$2
|
||||
mtpt=$3
|
||||
}
|
||||
|
||||
x=(srv $sleephack -e \
|
||||
'ssh '$rawflags' '$1' '$rawhack' '$u9fspath' -na none -u ''$''USER -l ''$''HOME/u9fs.log' \
|
||||
$srv $mtpt)
|
||||
$x
|
||||
|
||||
# Sometimes /srv/whatever can be a closed pipe, in which case
|
||||
# srv will have been killed for writing to it, without a chance to
|
||||
# defend itself. Rerun it in this case.
|
||||
|
||||
ss=$status
|
||||
if(~ $ss *'write on closed pipe'*){
|
||||
rm -f /srv/$srv
|
||||
$x
|
||||
ss=$status
|
||||
}
|
||||
|
||||
if(! ~ $ss '')
|
||||
echo srvssh: $ss >[1=2]
|
||||
exit $ss
|
346
sys/man/1/ssh
346
sys/man/1/ssh
|
@ -1,346 +0,0 @@
|
|||
.TH SSH 1
|
||||
.SH NAME
|
||||
ssh, sshnet, scp, sshserve \- secure login and file copy from/to Unix or Plan 9
|
||||
.SH SYNOPSIS
|
||||
.B ssh
|
||||
[
|
||||
.B -CfiImPpRrw
|
||||
]
|
||||
[
|
||||
.B -A
|
||||
.I authlist
|
||||
]
|
||||
[
|
||||
.B -c
|
||||
.I cipherlist
|
||||
]
|
||||
[
|
||||
.B -[lu]
|
||||
.I user
|
||||
]
|
||||
.RI [ user\fB@ ] host
|
||||
[
|
||||
.I cmd
|
||||
[
|
||||
.I args
|
||||
\&... ]]
|
||||
.PP
|
||||
.B sshnet
|
||||
[
|
||||
.B -A
|
||||
.I authlist
|
||||
]
|
||||
[
|
||||
.B -c
|
||||
.I cipherlist
|
||||
]
|
||||
[
|
||||
.B -m
|
||||
.I mtpt
|
||||
]
|
||||
[
|
||||
.B -s
|
||||
.I service
|
||||
]
|
||||
.RI [ user\fB@ ] host
|
||||
.PP
|
||||
.B scp
|
||||
[host:]file [host:]file
|
||||
.br
|
||||
.B scp
|
||||
[host:]file ... [host:]dir
|
||||
.PP
|
||||
.B aux/sshserve
|
||||
[
|
||||
.B -p
|
||||
]
|
||||
.I address
|
||||
.SH DESCRIPTION
|
||||
.I Ssh
|
||||
allows authenticated login over an encrypted channel to hosts that
|
||||
support the ssh protocol (see the RFCs listed below for encryption and
|
||||
authentication details).
|
||||
.LP
|
||||
.I Ssh
|
||||
takes the host name of the machine to connect to as its mandatory argument.
|
||||
It may be specified as a domain name or an IP address.
|
||||
Normally, login is attempted using the user name from /dev/user.
|
||||
.PP
|
||||
Command-line options are:
|
||||
.TP
|
||||
.B -C
|
||||
force input to be read in cooked mode:
|
||||
``line at a time'' with local echo.
|
||||
.TP
|
||||
.B -f
|
||||
enable agent forwarding.
|
||||
With this flag,
|
||||
.I ssh
|
||||
uses SSH's agent forwarding protocol to allow
|
||||
programs running on the remote server to
|
||||
interact with
|
||||
.IR factotum (4)
|
||||
to perform RSA authentication.
|
||||
.TP
|
||||
.B -i
|
||||
force interactive mode.
|
||||
In interactive mode,
|
||||
.I ssh
|
||||
prompts for passwords and confirmations of
|
||||
new host keys when necessary.
|
||||
(In non-interactive mode, password requests
|
||||
are rejected and unrecognized host keys are
|
||||
cause for disconnecting.)
|
||||
By default,
|
||||
.I ssh
|
||||
runs in interactive mode only when its
|
||||
input file descriptor is
|
||||
.BR /dev/cons .
|
||||
.TP
|
||||
.B -I
|
||||
force non-interactive mode.
|
||||
.TP
|
||||
.B -m
|
||||
disable the
|
||||
.RB control- \e
|
||||
menu, described below.
|
||||
.TP
|
||||
.B -p
|
||||
force pseudoterminal request.
|
||||
The
|
||||
.I ssh
|
||||
protocol, grounded in Unix tradition,
|
||||
differentiates between connections
|
||||
that request controlling pseudoterminals
|
||||
and those that do not.
|
||||
By default,
|
||||
.I ssh
|
||||
requests a pseudoterminal only when no
|
||||
.I command
|
||||
is given.
|
||||
.TP
|
||||
.B -P
|
||||
force no pseudoterminal request.
|
||||
.TP
|
||||
.B -r
|
||||
strip carriage returns.
|
||||
.TP
|
||||
.B -R
|
||||
put the allocated pseudoterminal, if any, in raw mode.
|
||||
.TP
|
||||
.B -w
|
||||
notify the remote side whenever the window changes size.
|
||||
.TP
|
||||
.BR - [ lu ] "\fI user
|
||||
specify user name.
|
||||
This option is deprecated in favor of the
|
||||
.IB user @ hostname
|
||||
syntax.
|
||||
.TP
|
||||
.B "-A\fI authlist
|
||||
specify an ordered space-separated list of authentication protocols to try.
|
||||
The full set of authentication protocols is
|
||||
.B rsa
|
||||
(RSA using
|
||||
.IR factotum (4)
|
||||
to moderate key usage),
|
||||
.B password
|
||||
(use a password gathered from factotum),
|
||||
and
|
||||
.B tis
|
||||
(challenge-response).
|
||||
The default list is all three in that order.
|
||||
.TP
|
||||
.B "-c\fI cipherlist
|
||||
specify an ordered space-separated list of allowed ciphers to use when encrypting the channel.
|
||||
The full set of ciphers is
|
||||
.B des
|
||||
(standard DES),
|
||||
.B 3des
|
||||
(a somewhat doubtful variation on triple DES),
|
||||
.B blowfish
|
||||
(Bruce Schneier's Blowfish),
|
||||
.B rc4
|
||||
(RC4),
|
||||
and
|
||||
.B none
|
||||
(no encryption).
|
||||
The default cipher list is
|
||||
.B blowfish
|
||||
.B rc4
|
||||
.BR 3des .
|
||||
.PD
|
||||
.PP
|
||||
The
|
||||
.RB control\- \e
|
||||
character is a local escape, as in
|
||||
.IR con (1).
|
||||
It prompts with
|
||||
.BR >>> .
|
||||
Legitimate responses to the prompt are
|
||||
.TP
|
||||
.B q
|
||||
Exit.
|
||||
.TP
|
||||
.B .
|
||||
Return from the escape.
|
||||
.TP
|
||||
.B !cmd
|
||||
Run the command with the network connection as its
|
||||
standard input and standard output.
|
||||
Standard error will go to the screen.
|
||||
.TP
|
||||
.B r
|
||||
Toggle printing of carriage returns.
|
||||
.PD
|
||||
.LP
|
||||
If no command is specified,
|
||||
a login session is started on the remote
|
||||
host.
|
||||
Otherwise, the command is executed with its arguments.
|
||||
.LP
|
||||
.I Ssh
|
||||
establishes a connection with an ssh daemon on the remote host.
|
||||
The daemon sends to
|
||||
.I ssh
|
||||
its RSA public host key and session key.
|
||||
Using these,
|
||||
.I ssh
|
||||
sends a session key which, presumably, only the
|
||||
daemon can decipher. After this, both sides start encrypting their
|
||||
data with this session key.
|
||||
.LP
|
||||
When the daemon's host key has been received,
|
||||
.I ssh
|
||||
looks it up in
|
||||
.B $home/lib/keyring
|
||||
and in
|
||||
.BR /sys/lib/ssh/keyring .
|
||||
If
|
||||
the key is found there, and it matches the received key,
|
||||
.I ssh
|
||||
is satisfied. If not,
|
||||
.I ssh
|
||||
reports this and offers to add the key to
|
||||
.BR $home/lib/keyring .
|
||||
.LP
|
||||
Over the encrypted channel,
|
||||
.I ssh
|
||||
attempts to convince the daemon to accept the call
|
||||
using the listed authentication protocols
|
||||
(see the
|
||||
.B -A
|
||||
option above).
|
||||
.LP
|
||||
The preferred way to authenticate is a
|
||||
.IR netkey -style
|
||||
challenge/response or via a SecurID token.
|
||||
.I Ssh
|
||||
users on other systems than Plan 9 should enable \s-2TIS_A\s0uthentication.
|
||||
.LP
|
||||
When the connection is authenticated, the given command line,
|
||||
(by default, a login shell) is executed on the remote host.
|
||||
.sp 1
|
||||
The SSH protocol allows clients to make outgoing TCP calls via the server.
|
||||
.I Sshnet
|
||||
establishes an SSH connection and, rather than execute a remote command,
|
||||
presents the remote server's TCP stack as a network stack
|
||||
(see the discussion of TCP in
|
||||
.IR ip (3))
|
||||
mounted at
|
||||
.I mtpt
|
||||
(default
|
||||
.BR /net ),
|
||||
optionally posting a 9P service
|
||||
descriptor for the new file system as
|
||||
.IB /srv/ service \fR.
|
||||
The
|
||||
.B -A
|
||||
and
|
||||
.B -c
|
||||
arguments are as in
|
||||
.IR ssh .
|
||||
.sp 1
|
||||
.I Scp
|
||||
uses
|
||||
.I ssh
|
||||
to copy files from one host to another. A remote file is identified by
|
||||
a host name, a colon and a file name (no spaces).
|
||||
.I Scp
|
||||
can copy files from remote hosts and to remote hosts.
|
||||
.sp 1
|
||||
.I Sshserve
|
||||
is the server that services
|
||||
.I ssh
|
||||
calls from remote hosts.
|
||||
The
|
||||
.B -A
|
||||
and
|
||||
.B -c
|
||||
options set valid authentication methods and ciphers
|
||||
as in
|
||||
.IR ssh ,
|
||||
except that there is no
|
||||
.B rsa
|
||||
authentication method.
|
||||
Unlike in
|
||||
.IR ssh ,
|
||||
the list is not ordered: the server presents a set and the client makes the choice.
|
||||
The default sets are
|
||||
.B tis
|
||||
and
|
||||
.B blowfish
|
||||
.B rc4
|
||||
.BR 3des .
|
||||
By default, users start with the namespace defined in
|
||||
.BR /lib/namespace .
|
||||
Users in group
|
||||
.B noworld
|
||||
in
|
||||
.B /adm/users
|
||||
start with the namespace defined in
|
||||
.BR /lib/namespace.noworld .
|
||||
.I Sshserve
|
||||
does not provide the TCP forwarding functionality used
|
||||
by
|
||||
.IR sshnet ,
|
||||
because many Unix clients present
|
||||
this capability in an insecure manner.
|
||||
.PP
|
||||
.I Sshserve
|
||||
requires that
|
||||
.IR factotum (4)
|
||||
hold the host key,
|
||||
identified by having attributes
|
||||
.B proto=rsa
|
||||
.BR service=sshserve .
|
||||
To generate a host key:
|
||||
.IP
|
||||
.EX
|
||||
auth/rsagen -t 'service=sshserve' >/mnt/factotum/ctl
|
||||
.EE
|
||||
.LP
|
||||
To extract the public part of the host key in the form
|
||||
used by SSH key rings:
|
||||
.IP
|
||||
.EX
|
||||
grep 'service=sshserve' /mnt/factotum/ctl | auth/rsa2ssh
|
||||
.EE
|
||||
.SH FILES
|
||||
.TP
|
||||
.B /sys/lib/ssh/keyring
|
||||
System key ring file containing public keys for remote ssh clients and servers.
|
||||
.TP
|
||||
.B /usr/\fIuser\fP/lib/keyring
|
||||
Personal key ring file containing public keys for remote ssh clients and
|
||||
servers.
|
||||
.SH SOURCE
|
||||
.B /sys/src/cmd/ssh
|
||||
.SH "SEE ALSO"
|
||||
.B /lib/rfc/rfc425[0-6]
|
||||
.br
|
||||
.IR factotum (4),
|
||||
.IR authsrv (6),
|
||||
.IR rsa (8)
|
||||
.SH BUGS
|
||||
Only version 1 of the SSH protocol is implemented.
|
|
@ -1,450 +0,0 @@
|
|||
#include "ssh.h"
|
||||
#include <bio.h>
|
||||
|
||||
typedef struct Key Key;
|
||||
struct Key
|
||||
{
|
||||
mpint *mod;
|
||||
mpint *ek;
|
||||
char *comment;
|
||||
};
|
||||
|
||||
typedef struct Achan Achan;
|
||||
struct Achan
|
||||
{
|
||||
int open;
|
||||
u32int chan; /* of remote */
|
||||
uchar lbuf[4];
|
||||
uint nlbuf;
|
||||
uint len;
|
||||
uchar *data;
|
||||
int ndata;
|
||||
int needeof;
|
||||
int needclosed;
|
||||
};
|
||||
|
||||
Achan achan[16];
|
||||
|
||||
static char*
|
||||
find(char **f, int nf, char *k)
|
||||
{
|
||||
int i, len;
|
||||
|
||||
len = strlen(k);
|
||||
for(i=1; i<nf; i++) /* i=1: f[0] is "key" */
|
||||
if(strncmp(f[i], k, len) == 0 && f[i][len] == '=')
|
||||
return f[i]+len+1;
|
||||
return nil;
|
||||
}
|
||||
|
||||
static int
|
||||
listkeys(Key **kp)
|
||||
{
|
||||
Biobuf *b;
|
||||
Key *k;
|
||||
int nk;
|
||||
char *p, *f[20];
|
||||
int nf;
|
||||
mpint *mod, *ek;
|
||||
|
||||
*kp = nil;
|
||||
if((b = Bopen("/mnt/factotum/ctl", OREAD)) == nil)
|
||||
return -1;
|
||||
|
||||
k = nil;
|
||||
nk = 0;
|
||||
while((p = Brdline(b, '\n')) != nil){
|
||||
p[Blinelen(b)-1] = '\0';
|
||||
nf = tokenize(p, f, nelem(f));
|
||||
if(nf == 0 || strcmp(f[0], "key") != 0)
|
||||
continue;
|
||||
p = find(f, nf, "proto");
|
||||
if(p == nil || strcmp(p, "rsa") != 0)
|
||||
continue;
|
||||
p = find(f, nf, "n");
|
||||
if(p == nil || (mod = strtomp(p, nil, 16, nil)) == nil)
|
||||
continue;
|
||||
p = find(f, nf, "ek");
|
||||
if(p == nil || (ek = strtomp(p, nil, 16, nil)) == nil){
|
||||
mpfree(mod);
|
||||
continue;
|
||||
}
|
||||
p = find(f, nf, "comment");
|
||||
if(p == nil)
|
||||
p = "";
|
||||
k = erealloc(k, (nk+1)*sizeof(k[0]));
|
||||
k[nk].mod = mod;
|
||||
k[nk].ek = ek;
|
||||
k[nk].comment = emalloc(strlen(p)+1);
|
||||
strcpy(k[nk].comment, p);
|
||||
nk++;
|
||||
}
|
||||
Bterm(b);
|
||||
*kp = k;
|
||||
return nk;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
dorsa(mpint *mod, mpint *exp, mpint *chal, uchar chalbuf[32])
|
||||
{
|
||||
int afd;
|
||||
AuthRpc *rpc;
|
||||
mpint *m;
|
||||
char buf[4096], *p;
|
||||
mpint *decr, *unpad;
|
||||
|
||||
USED(exp);
|
||||
|
||||
snprint(buf, sizeof buf, "proto=rsa service=ssh role=client");
|
||||
if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){
|
||||
debug(DBG_AUTH, "open /mnt/factotum/rpc: %r\n");
|
||||
return -1;
|
||||
}
|
||||
if((rpc = auth_allocrpc(afd)) == nil){
|
||||
debug(DBG_AUTH, "auth_allocrpc: %r\n");
|
||||
close(afd);
|
||||
return -1;
|
||||
}
|
||||
if(auth_rpc(rpc, "start", buf, strlen(buf)) != ARok){
|
||||
debug(DBG_AUTH, "auth_rpc start failed: %r\n");
|
||||
Die:
|
||||
auth_freerpc(rpc);
|
||||
close(afd);
|
||||
return -1;
|
||||
}
|
||||
m = nil;
|
||||
debug(DBG_AUTH, "trying factotum rsa keys\n");
|
||||
while(auth_rpc(rpc, "read", nil, 0) == ARok){
|
||||
debug(DBG_AUTH, "try %s\n", (char*)rpc->arg);
|
||||
m = strtomp(rpc->arg, nil, 16, nil);
|
||||
if(mpcmp(m, mod) == 0)
|
||||
break;
|
||||
mpfree(m);
|
||||
m = nil;
|
||||
}
|
||||
if(m == nil)
|
||||
goto Die;
|
||||
mpfree(m);
|
||||
|
||||
p = mptoa(chal, 16, nil, 0);
|
||||
if(p == nil){
|
||||
debug(DBG_AUTH, "\tmptoa failed: %r\n");
|
||||
goto Die;
|
||||
}
|
||||
if(auth_rpc(rpc, "write", p, strlen(p)) != ARok){
|
||||
debug(DBG_AUTH, "\tauth_rpc write failed: %r\n");
|
||||
free(p);
|
||||
goto Die;
|
||||
}
|
||||
free(p);
|
||||
if(auth_rpc(rpc, "read", nil, 0) != ARok){
|
||||
debug(DBG_AUTH, "\tauth_rpc read failed: %r\n");
|
||||
goto Die;
|
||||
}
|
||||
decr = strtomp(rpc->arg, nil, 16, nil);
|
||||
if(decr == nil){
|
||||
debug(DBG_AUTH, "\tdecr %s failed\n", rpc->arg);
|
||||
goto Die;
|
||||
}
|
||||
debug(DBG_AUTH, "\tdecrypted %B\n", decr);
|
||||
unpad = rsaunpad(decr);
|
||||
if(unpad == nil){
|
||||
debug(DBG_AUTH, "\tunpad %B failed\n", decr);
|
||||
mpfree(decr);
|
||||
goto Die;
|
||||
}
|
||||
debug(DBG_AUTH, "\tunpadded %B\n", unpad);
|
||||
mpfree(decr);
|
||||
mptoberjust(unpad, chalbuf, 32);
|
||||
mpfree(unpad);
|
||||
auth_freerpc(rpc);
|
||||
close(afd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
startagent(Conn *c)
|
||||
{
|
||||
int ret;
|
||||
Msg *m;
|
||||
|
||||
m = allocmsg(c, SSH_CMSG_AGENT_REQUEST_FORWARDING, 0);
|
||||
sendmsg(m);
|
||||
|
||||
m = recvmsg(c, -1);
|
||||
switch(m->type){
|
||||
case SSH_SMSG_SUCCESS:
|
||||
debug(DBG_AUTH, "agent allocated\n");
|
||||
ret = 0;
|
||||
break;
|
||||
case SSH_SMSG_FAILURE:
|
||||
debug(DBG_AUTH, "agent failed to allocate\n");
|
||||
ret = -1;
|
||||
break;
|
||||
default:
|
||||
badmsg(m, 0);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
free(m);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void handlefullmsg(Conn*, Achan*);
|
||||
|
||||
void
|
||||
handleagentmsg(Msg *m)
|
||||
{
|
||||
u32int chan, len;
|
||||
int n;
|
||||
Achan *a;
|
||||
|
||||
assert(m->type == SSH_MSG_CHANNEL_DATA);
|
||||
|
||||
debug(DBG_AUTH, "agent data\n");
|
||||
debug(DBG_AUTH, "\t%.*H\n", (int)(m->ep - m->rp), m->rp);
|
||||
chan = getlong(m);
|
||||
len = getlong(m);
|
||||
if(m->rp+len != m->ep)
|
||||
sysfatal("got bad channel data");
|
||||
|
||||
if(chan >= nelem(achan))
|
||||
error("bad channel in agent request");
|
||||
|
||||
a = &achan[chan];
|
||||
|
||||
while(m->rp < m->ep){
|
||||
if(a->nlbuf < 4){
|
||||
a->lbuf[a->nlbuf++] = getbyte(m);
|
||||
if(a->nlbuf == 4){
|
||||
a->len = (a->lbuf[0]<<24) | (a->lbuf[1]<<16) | (a->lbuf[2]<<8) | a->lbuf[3];
|
||||
a->data = erealloc(a->data, a->len);
|
||||
a->ndata = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(a->ndata < a->len){
|
||||
n = a->len - a->ndata;
|
||||
if(n > m->ep - m->rp)
|
||||
n = m->ep - m->rp;
|
||||
memmove(a->data+a->ndata, getbytes(m, n), n);
|
||||
a->ndata += n;
|
||||
}
|
||||
if(a->ndata == a->len){
|
||||
handlefullmsg(m->c, a);
|
||||
a->nlbuf = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handlefullmsg(Conn *c, Achan *a)
|
||||
{
|
||||
int i;
|
||||
u32int chan, len, n, rt;
|
||||
uchar type;
|
||||
Msg *m, mm;
|
||||
Msg *r;
|
||||
Key *k;
|
||||
int nk;
|
||||
mpint *mod, *ek, *chal;
|
||||
uchar sessid[16];
|
||||
uchar chalbuf[32];
|
||||
uchar digest[16];
|
||||
DigestState *s;
|
||||
static int first;
|
||||
|
||||
assert(a->len == a->ndata);
|
||||
|
||||
chan = a->chan;
|
||||
mm.rp = a->data;
|
||||
mm.ep = a->data+a->ndata;
|
||||
mm.c = c;
|
||||
m = &mm;
|
||||
|
||||
type = getbyte(m);
|
||||
|
||||
if(first == 0){
|
||||
first++;
|
||||
fmtinstall('H', encodefmt);
|
||||
}
|
||||
|
||||
switch(type){
|
||||
default:
|
||||
debug(DBG_AUTH, "unknown msg type\n");
|
||||
Failure:
|
||||
debug(DBG_AUTH, "agent sending failure\n");
|
||||
r = allocmsg(m->c, SSH_MSG_CHANNEL_DATA, 13);
|
||||
putlong(r, chan);
|
||||
putlong(r, 5);
|
||||
putlong(r, 1);
|
||||
putbyte(r, SSH_AGENT_FAILURE);
|
||||
sendmsg(r);
|
||||
return;
|
||||
|
||||
case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
|
||||
debug(DBG_AUTH, "agent request identities\n");
|
||||
nk = listkeys(&k);
|
||||
if(nk < 0)
|
||||
goto Failure;
|
||||
len = 1+4; /* type, nk */
|
||||
for(i=0; i<nk; i++){
|
||||
len += 4;
|
||||
len += 2+(mpsignif(k[i].ek)+7)/8;
|
||||
len += 2+(mpsignif(k[i].mod)+7)/8;
|
||||
len += 4+strlen(k[i].comment);
|
||||
}
|
||||
r = allocmsg(m->c, SSH_MSG_CHANNEL_DATA, 12+len);
|
||||
putlong(r, chan);
|
||||
putlong(r, len+4);
|
||||
putlong(r, len);
|
||||
putbyte(r, SSH_AGENT_RSA_IDENTITIES_ANSWER);
|
||||
putlong(r, nk);
|
||||
for(i=0; i<nk; i++){
|
||||
debug(DBG_AUTH, "\t%B %B %s\n", k[i].ek, k[i].mod, k[i].comment);
|
||||
putlong(r, mpsignif(k[i].mod));
|
||||
putmpint(r, k[i].ek);
|
||||
putmpint(r, k[i].mod);
|
||||
putstring(r, k[i].comment);
|
||||
mpfree(k[i].ek);
|
||||
mpfree(k[i].mod);
|
||||
free(k[i].comment);
|
||||
}
|
||||
free(k);
|
||||
sendmsg(r);
|
||||
break;
|
||||
|
||||
case SSH_AGENTC_RSA_CHALLENGE:
|
||||
n = getlong(m);
|
||||
USED(n); /* number of bits in key; who cares? */
|
||||
ek = getmpint(m);
|
||||
mod = getmpint(m);
|
||||
chal = getmpint(m);
|
||||
memmove(sessid, getbytes(m, 16), 16);
|
||||
rt = getlong(m);
|
||||
debug(DBG_AUTH, "agent challenge %B %B %B %ud (%p %p)\n",
|
||||
ek, mod, chal, rt, m->rp, m->ep);
|
||||
if(rt != 1 || dorsa(mod, ek, chal, chalbuf) < 0){
|
||||
mpfree(ek);
|
||||
mpfree(mod);
|
||||
mpfree(chal);
|
||||
goto Failure;
|
||||
}
|
||||
s = md5(chalbuf, 32, nil, nil);
|
||||
md5(sessid, 16, digest, s);
|
||||
r = allocmsg(m->c, SSH_MSG_CHANNEL_DATA, 12+1+16);
|
||||
putlong(r, chan);
|
||||
putlong(r, 4+16+1);
|
||||
putlong(r, 16+1);
|
||||
putbyte(r, SSH_AGENT_RSA_RESPONSE);
|
||||
putbytes(r, digest, 16);
|
||||
debug(DBG_AUTH, "digest %.16H\n", digest);
|
||||
sendmsg(r);
|
||||
mpfree(ek);
|
||||
mpfree(mod);
|
||||
mpfree(chal);
|
||||
return;
|
||||
|
||||
case SSH_AGENTC_ADD_RSA_IDENTITY:
|
||||
goto Failure;
|
||||
/*
|
||||
n = getlong(m);
|
||||
pubmod = getmpint(m);
|
||||
pubexp = getmpint(m);
|
||||
privexp = getmpint(m);
|
||||
pinversemodq = getmpint(m);
|
||||
p = getmpint(m);
|
||||
q = getmpint(m);
|
||||
comment = getstring(m);
|
||||
add to factotum;
|
||||
send SSH_AGENT_SUCCESS or SSH_AGENT_FAILURE;
|
||||
*/
|
||||
|
||||
case SSH_AGENTC_REMOVE_RSA_IDENTITY:
|
||||
goto Failure;
|
||||
/*
|
||||
n = getlong(m);
|
||||
pubmod = getmpint(m);
|
||||
pubexp = getmpint(m);
|
||||
tell factotum to del key
|
||||
send SSH_AGENT_SUCCESS or SSH_AGENT_FAILURE;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handleagentopen(Msg *m)
|
||||
{
|
||||
int i;
|
||||
u32int remote;
|
||||
|
||||
assert(m->type == SSH_SMSG_AGENT_OPEN);
|
||||
remote = getlong(m);
|
||||
debug(DBG_AUTH, "agent open %d\n", remote);
|
||||
|
||||
for(i=0; i<nelem(achan); i++)
|
||||
if(achan[i].open == 0 && achan[i].needeof == 0 && achan[i].needclosed == 0)
|
||||
break;
|
||||
if(i == nelem(achan)){
|
||||
m = allocmsg(m->c, SSH_MSG_CHANNEL_OPEN_FAILURE, 4);
|
||||
putlong(m, remote);
|
||||
sendmsg(m);
|
||||
return;
|
||||
}
|
||||
|
||||
debug(DBG_AUTH, "\tremote %d is local %d\n", remote, i);
|
||||
achan[i].open = 1;
|
||||
achan[i].needeof = 1;
|
||||
achan[i].needclosed = 1;
|
||||
achan[i].nlbuf = 0;
|
||||
achan[i].chan = remote;
|
||||
m = allocmsg(m->c, SSH_MSG_CHANNEL_OPEN_CONFIRMATION, 8);
|
||||
putlong(m, remote);
|
||||
putlong(m, i);
|
||||
sendmsg(m);
|
||||
}
|
||||
|
||||
void
|
||||
handleagentieof(Msg *m)
|
||||
{
|
||||
u32int local;
|
||||
|
||||
assert(m->type == SSH_MSG_CHANNEL_INPUT_EOF);
|
||||
local = getlong(m);
|
||||
debug(DBG_AUTH, "agent close %d\n", local);
|
||||
if(local < nelem(achan)){
|
||||
debug(DBG_AUTH, "\tlocal %d is remote %d\n", local, achan[local].chan);
|
||||
achan[local].open = 0;
|
||||
/*
|
||||
m = allocmsg(m->c, SSH_MSG_CHANNEL_OUTPUT_CLOSED, 4);
|
||||
putlong(m, achan[local].chan);
|
||||
sendmsg(m);
|
||||
*/
|
||||
if(achan[local].needeof){
|
||||
achan[local].needeof = 0;
|
||||
m = allocmsg(m->c, SSH_MSG_CHANNEL_INPUT_EOF, 4);
|
||||
putlong(m, achan[local].chan);
|
||||
sendmsg(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handleagentoclose(Msg *m)
|
||||
{
|
||||
u32int local;
|
||||
|
||||
assert(m->type == SSH_MSG_CHANNEL_OUTPUT_CLOSED);
|
||||
local = getlong(m);
|
||||
debug(DBG_AUTH, "agent close %d\n", local);
|
||||
if(local < nelem(achan)){
|
||||
debug(DBG_AUTH, "\tlocal %d is remote %d\n", local, achan[local].chan);
|
||||
if(achan[local].needclosed){
|
||||
achan[local].needclosed = 0;
|
||||
m = allocmsg(m->c, SSH_MSG_CHANNEL_OUTPUT_CLOSED, 4);
|
||||
putlong(m, achan[local].chan);
|
||||
sendmsg(m);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
static int
|
||||
authpasswordfn(Conn *c)
|
||||
{
|
||||
Msg *m;
|
||||
UserPasswd *up;
|
||||
|
||||
up = auth_getuserpasswd(c->interactive ? auth_getkey : nil, "proto=pass service=ssh server=%q user=%q", c->host, c->user);
|
||||
if(up == nil){
|
||||
debug(DBG_AUTH, "getuserpasswd returned nothing (interactive=%d)\n", c->interactive);
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug(DBG_AUTH, "try using password from factotum\n");
|
||||
m = allocmsg(c, SSH_CMSG_AUTH_PASSWORD, 4+strlen(up->passwd));
|
||||
putstring(m, up->passwd);
|
||||
sendmsg(m);
|
||||
|
||||
m = recvmsg(c, -1);
|
||||
switch(m->type){
|
||||
default:
|
||||
badmsg(m, 0);
|
||||
case SSH_SMSG_SUCCESS:
|
||||
free(m);
|
||||
return 0;
|
||||
case SSH_SMSG_FAILURE:
|
||||
free(m);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Auth authpassword =
|
||||
{
|
||||
SSH_AUTH_PASSWORD,
|
||||
"password",
|
||||
authpasswordfn,
|
||||
};
|
|
@ -1,113 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
static int
|
||||
authrsafn(Conn *c)
|
||||
{
|
||||
uchar chalbuf[32+SESSIDLEN], response[MD5dlen];
|
||||
char *s, *p;
|
||||
int afd, ret;
|
||||
AuthRpc *rpc;
|
||||
Msg *m;
|
||||
mpint *chal, *decr, *unpad, *mod;
|
||||
|
||||
debug(DBG_AUTH, "rsa!\n");
|
||||
|
||||
if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){
|
||||
debug(DBG_AUTH, "open /mnt/factotum/rpc: %r\n");
|
||||
return -1;
|
||||
}
|
||||
if((rpc = auth_allocrpc(afd)) == nil){
|
||||
debug(DBG_AUTH, "auth_allocrpc: %r\n");
|
||||
close(afd);
|
||||
return -1;
|
||||
}
|
||||
s = "proto=rsa role=client";
|
||||
if(auth_rpc(rpc, "start", s, strlen(s)) != ARok){
|
||||
debug(DBG_AUTH, "auth_rpc start %s failed: %r\n", s);
|
||||
auth_freerpc(rpc);
|
||||
close(afd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = -1;
|
||||
debug(DBG_AUTH, "trying factotum rsa keys\n");
|
||||
while(auth_rpc(rpc, "read", nil, 0) == ARok){
|
||||
debug(DBG_AUTH, "try %s\n", (char*)rpc->arg);
|
||||
mod = strtomp(rpc->arg, nil, 16, nil);
|
||||
m = allocmsg(c, SSH_CMSG_AUTH_RSA, 16+(mpsignif(mod)+7/8));
|
||||
putmpint(m, mod);
|
||||
sendmsg(m);
|
||||
mpfree(mod);
|
||||
m = recvmsg(c, -1);
|
||||
switch(m->type){
|
||||
case SSH_SMSG_FAILURE:
|
||||
debug(DBG_AUTH, "\tnot accepted %s\n", (char*)rpc->arg);
|
||||
free(m);
|
||||
continue;
|
||||
default:
|
||||
badmsg(m, 0);
|
||||
case SSH_SMSG_AUTH_RSA_CHALLENGE:
|
||||
break;
|
||||
}
|
||||
chal = getmpint(m);
|
||||
debug(DBG_AUTH, "\tgot challenge %B\n", chal);
|
||||
free(m);
|
||||
p = mptoa(chal, 16, nil, 0);
|
||||
mpfree(chal);
|
||||
if(p == nil){
|
||||
debug(DBG_AUTH, "\tmptoa failed: %r\n");
|
||||
unpad = mpnew(0);
|
||||
goto Keepgoing;
|
||||
}
|
||||
if(auth_rpc(rpc, "write", p, strlen(p)) != ARok){
|
||||
debug(DBG_AUTH, "\tauth_rpc write failed: %r\n");
|
||||
free(p);
|
||||
unpad = mpnew(0); /* it will fail, we'll go round again */
|
||||
goto Keepgoing;
|
||||
}
|
||||
free(p);
|
||||
if(auth_rpc(rpc, "read", nil, 0) != ARok){
|
||||
debug(DBG_AUTH, "\tauth_rpc read failed: %r\n");
|
||||
unpad = mpnew(0);
|
||||
goto Keepgoing;
|
||||
}
|
||||
decr = strtomp(rpc->arg, nil, 16, nil);
|
||||
debug(DBG_AUTH, "\tdecrypted %B\n", decr);
|
||||
unpad = rsaunpad(decr);
|
||||
debug(DBG_AUTH, "\tunpadded %B\n", unpad);
|
||||
mpfree(decr);
|
||||
Keepgoing:
|
||||
mptoberjust(unpad, chalbuf, 32);
|
||||
mpfree(unpad);
|
||||
debug(DBG_AUTH, "\trjusted %.*H\n", 32, chalbuf);
|
||||
memmove(chalbuf+32, c->sessid, SESSIDLEN);
|
||||
debug(DBG_AUTH, "\tappend sesskey %.*H\n", 32, chalbuf);
|
||||
md5(chalbuf, 32+SESSIDLEN, response, nil);
|
||||
m = allocmsg(c, SSH_CMSG_AUTH_RSA_RESPONSE, MD5dlen);
|
||||
putbytes(m, response, MD5dlen);
|
||||
sendmsg(m);
|
||||
|
||||
m = recvmsg(c, -1);
|
||||
switch(m->type){
|
||||
case SSH_SMSG_FAILURE:
|
||||
free(m);
|
||||
continue;
|
||||
default:
|
||||
badmsg(m, 0);
|
||||
case SSH_SMSG_SUCCESS:
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
auth_freerpc(rpc);
|
||||
close(afd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Auth authrsa =
|
||||
{
|
||||
SSH_AUTH_RSA,
|
||||
"rsa",
|
||||
authrsafn,
|
||||
};
|
|
@ -1,22 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
static AuthInfo*
|
||||
authsrvpasswordfn(Conn *c, Msg *m)
|
||||
{
|
||||
char *pass;
|
||||
AuthInfo *ai;
|
||||
|
||||
pass = getstring(m);
|
||||
ai = auth_userpasswd(c->user, pass);
|
||||
free(m);
|
||||
return ai;
|
||||
}
|
||||
|
||||
Authsrv authsrvpassword =
|
||||
{
|
||||
SSH_AUTH_PASSWORD,
|
||||
"password",
|
||||
SSH_CMSG_AUTH_PASSWORD,
|
||||
authsrvpasswordfn,
|
||||
};
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
static AuthInfo*
|
||||
authsrvtisfn(Conn *conn, Msg *m)
|
||||
{
|
||||
char *s;
|
||||
AuthInfo *ai;
|
||||
Chalstate *c;
|
||||
|
||||
free(m);
|
||||
if((c = auth_challenge("proto=p9cr user=%q role=server", conn->user)) == nil){
|
||||
sshlog("auth_challenge failed for %s", conn->user);
|
||||
return nil;
|
||||
}
|
||||
s = smprint("Challenge: %s\nResponse: ", c->chal);
|
||||
if(s == nil){
|
||||
auth_freechal(c);
|
||||
return nil;
|
||||
}
|
||||
m = allocmsg(conn, SSH_SMSG_AUTH_TIS_CHALLENGE, 4+strlen(s));
|
||||
putstring(m, s);
|
||||
sendmsg(m);
|
||||
free(s);
|
||||
|
||||
m = recvmsg(conn, 0);
|
||||
if(m == nil){
|
||||
auth_freechal(c);
|
||||
return nil;
|
||||
}
|
||||
if(m->type != SSH_CMSG_AUTH_TIS_RESPONSE){
|
||||
/*
|
||||
* apparently you can just give up on
|
||||
* this protocol and start a new one.
|
||||
*/
|
||||
unrecvmsg(conn, m);
|
||||
return nil;
|
||||
}
|
||||
|
||||
c->resp = getstring(m);
|
||||
c->nresp = strlen(c->resp);
|
||||
ai = auth_response(c);
|
||||
auth_freechal(c);
|
||||
return ai;
|
||||
}
|
||||
|
||||
Authsrv authsrvtis =
|
||||
{
|
||||
SSH_AUTH_TIS,
|
||||
"tis",
|
||||
SSH_CMSG_AUTH_TIS,
|
||||
authsrvtisfn,
|
||||
};
|
|
@ -1,65 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
static int
|
||||
authtisfn(Conn *c)
|
||||
{
|
||||
int fd, n;
|
||||
char *chal, resp[256];
|
||||
Msg *m;
|
||||
|
||||
if(!c->interactive)
|
||||
return -1;
|
||||
|
||||
debug(DBG_AUTH, "try TIS\n");
|
||||
sendmsg(allocmsg(c, SSH_CMSG_AUTH_TIS, 0));
|
||||
|
||||
m = recvmsg(c, -1);
|
||||
switch(m->type){
|
||||
default:
|
||||
badmsg(m, SSH_SMSG_AUTH_TIS_CHALLENGE);
|
||||
case SSH_SMSG_FAILURE:
|
||||
free(m);
|
||||
return -1;
|
||||
case SSH_SMSG_AUTH_TIS_CHALLENGE:
|
||||
break;
|
||||
}
|
||||
|
||||
chal = getstring(m);
|
||||
free(m);
|
||||
|
||||
if((fd = open("/dev/cons", ORDWR)) < 0)
|
||||
error("can't open console");
|
||||
|
||||
fprint(fd, "TIS Authentication\n%s", chal);
|
||||
n = read(fd, resp, sizeof resp-1);
|
||||
if(n < 0)
|
||||
resp[0] = '\0';
|
||||
else
|
||||
resp[n] = '\0';
|
||||
|
||||
if(resp[0] == 0 || resp[0] == '\n')
|
||||
return -1;
|
||||
|
||||
m = allocmsg(c, SSH_CMSG_AUTH_TIS_RESPONSE, 4+strlen(resp));
|
||||
putstring(m, resp);
|
||||
sendmsg(m);
|
||||
|
||||
m = recvmsg(c, -1);
|
||||
switch(m->type){
|
||||
default:
|
||||
badmsg(m, 0);
|
||||
case SSH_SMSG_SUCCESS:
|
||||
free(m);
|
||||
return 0;
|
||||
case SSH_SMSG_FAILURE:
|
||||
free(m);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Auth authtis =
|
||||
{
|
||||
SSH_AUTH_TIS,
|
||||
"tis",
|
||||
authtisfn,
|
||||
};
|
|
@ -1,47 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
struct CipherState
|
||||
{
|
||||
DESstate enc3des[3];
|
||||
DESstate dec3des[3];
|
||||
};
|
||||
|
||||
static CipherState*
|
||||
init3des(Conn *c, int)
|
||||
{
|
||||
int i;
|
||||
CipherState *cs;
|
||||
|
||||
cs = emalloc(sizeof(CipherState));
|
||||
for(i=0; i<3; i++){
|
||||
setupDESstate(&cs->enc3des[i], c->sesskey+8*i, nil);
|
||||
setupDESstate(&cs->dec3des[i], c->sesskey+8*i, nil);
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
static void
|
||||
encrypt3des(CipherState *cs, uchar *buf, int nbuf)
|
||||
{
|
||||
desCBCencrypt(buf, nbuf, &cs->enc3des[0]);
|
||||
desCBCdecrypt(buf, nbuf, &cs->enc3des[1]);
|
||||
desCBCencrypt(buf, nbuf, &cs->enc3des[2]);
|
||||
}
|
||||
|
||||
static void
|
||||
decrypt3des(CipherState *cs, uchar *buf, int nbuf)
|
||||
{
|
||||
desCBCdecrypt(buf, nbuf, &cs->dec3des[2]);
|
||||
desCBCencrypt(buf, nbuf, &cs->dec3des[1]);
|
||||
desCBCdecrypt(buf, nbuf, &cs->dec3des[0]);
|
||||
}
|
||||
|
||||
Cipher cipher3des =
|
||||
{
|
||||
SSH_CIPHER_3DES,
|
||||
"3des",
|
||||
init3des,
|
||||
encrypt3des,
|
||||
decrypt3des,
|
||||
};
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
struct CipherState
|
||||
{
|
||||
BFstate enc;
|
||||
BFstate dec;
|
||||
};
|
||||
|
||||
static CipherState*
|
||||
initblowfish(Conn *c, int)
|
||||
{
|
||||
CipherState *cs;
|
||||
|
||||
cs = emalloc(sizeof(CipherState));
|
||||
setupBFstate(&cs->enc, c->sesskey, SESSKEYLEN, nil);
|
||||
setupBFstate(&cs->dec, c->sesskey, SESSKEYLEN, nil);
|
||||
return cs;
|
||||
}
|
||||
|
||||
static void
|
||||
encryptblowfish(CipherState *cs, uchar *buf, int nbuf)
|
||||
{
|
||||
bfCBCencrypt(buf, nbuf, &cs->enc);
|
||||
}
|
||||
|
||||
static void
|
||||
decryptblowfish(CipherState *cs, uchar *buf, int nbuf)
|
||||
{
|
||||
bfCBCdecrypt(buf, nbuf, &cs->dec);
|
||||
}
|
||||
|
||||
Cipher cipherblowfish =
|
||||
{
|
||||
SSH_CIPHER_BLOWFISH,
|
||||
"blowfish",
|
||||
initblowfish,
|
||||
encryptblowfish,
|
||||
decryptblowfish,
|
||||
};
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
struct CipherState
|
||||
{
|
||||
DESstate enc;
|
||||
DESstate dec;
|
||||
};
|
||||
|
||||
static CipherState*
|
||||
initdes(Conn *c, int)
|
||||
{
|
||||
CipherState *cs;
|
||||
|
||||
cs = emalloc(sizeof(CipherState));
|
||||
setupDESstate(&cs->enc, c->sesskey, nil);
|
||||
setupDESstate(&cs->dec, c->sesskey, nil);
|
||||
return cs;
|
||||
}
|
||||
|
||||
static void
|
||||
encryptdes(CipherState *cs, uchar *buf, int nbuf)
|
||||
{
|
||||
desCBCencrypt(buf, nbuf, &cs->enc);
|
||||
}
|
||||
|
||||
static void
|
||||
decryptdes(CipherState *cs, uchar *buf, int nbuf)
|
||||
{
|
||||
desCBCdecrypt(buf, nbuf, &cs->dec);
|
||||
}
|
||||
|
||||
Cipher cipherdes =
|
||||
{
|
||||
SSH_CIPHER_DES,
|
||||
"des",
|
||||
initdes,
|
||||
encryptdes,
|
||||
decryptdes,
|
||||
};
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
static CipherState*
|
||||
initnone(Conn*, int)
|
||||
{
|
||||
/* must be non-nil */
|
||||
return (CipherState*)~0;
|
||||
}
|
||||
|
||||
static void
|
||||
encryptnone(CipherState*, uchar*, int)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
decryptnone(CipherState*, uchar*, int)
|
||||
{
|
||||
}
|
||||
|
||||
Cipher ciphernone =
|
||||
{
|
||||
SSH_CIPHER_NONE,
|
||||
"none",
|
||||
initnone,
|
||||
encryptnone,
|
||||
decryptnone,
|
||||
};
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
struct CipherState
|
||||
{
|
||||
RC4state enc;
|
||||
RC4state dec;
|
||||
};
|
||||
|
||||
static CipherState*
|
||||
initrc4(Conn *c, int isserver)
|
||||
{
|
||||
CipherState *cs;
|
||||
|
||||
cs = emalloc(sizeof(CipherState));
|
||||
if(isserver){
|
||||
setupRC4state(&cs->enc, c->sesskey, 16);
|
||||
setupRC4state(&cs->dec, c->sesskey+16, 16);
|
||||
}else{
|
||||
setupRC4state(&cs->dec, c->sesskey, 16);
|
||||
setupRC4state(&cs->enc, c->sesskey+16, 16);
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
static void
|
||||
encryptrc4(CipherState *cs, uchar *buf, int nbuf)
|
||||
{
|
||||
rc4(&cs->enc, buf, nbuf);
|
||||
}
|
||||
|
||||
static void
|
||||
decryptrc4(CipherState *cs, uchar *buf, int nbuf)
|
||||
{
|
||||
rc4(&cs->dec, buf, nbuf);
|
||||
}
|
||||
|
||||
Cipher cipherrc4 =
|
||||
{
|
||||
SSH_CIPHER_RC4,
|
||||
"rc4",
|
||||
initrc4,
|
||||
encryptrc4,
|
||||
decryptrc4,
|
||||
};
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
static CipherState*
|
||||
inittwiddle(Conn *c, int)
|
||||
{
|
||||
/* must be non-nil */
|
||||
fprint(2, "twiddle key is %.*H\n", SESSKEYLEN, c->sesskey);
|
||||
return (CipherState*)~0;
|
||||
}
|
||||
|
||||
static void
|
||||
twiddle(CipherState*, uchar *buf, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<n; i++)
|
||||
buf[i] ^= 0xFF;
|
||||
}
|
||||
|
||||
Cipher ciphertwiddle =
|
||||
{
|
||||
SSH_CIPHER_TWIDDLE,
|
||||
"twiddle",
|
||||
inittwiddle,
|
||||
twiddle,
|
||||
twiddle,
|
||||
};
|
||||
|
|
@ -1,376 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
static void
|
||||
recv_ssh_smsg_public_key(Conn *c)
|
||||
{
|
||||
Msg *m;
|
||||
|
||||
m = recvmsg(c, SSH_SMSG_PUBLIC_KEY);
|
||||
memmove(c->cookie, getbytes(m, COOKIELEN), COOKIELEN);
|
||||
c->serverkey = getRSApub(m);
|
||||
c->hostkey = getRSApub(m);
|
||||
c->flags = getlong(m);
|
||||
c->ciphermask = getlong(m);
|
||||
c->authmask = getlong(m);
|
||||
free(m);
|
||||
}
|
||||
|
||||
static void
|
||||
send_ssh_cmsg_session_key(Conn *c)
|
||||
{
|
||||
int i, n, buflen, serverkeylen, hostkeylen;
|
||||
mpint *b;
|
||||
uchar *buf;
|
||||
Msg *m;
|
||||
RSApub *ksmall, *kbig;
|
||||
|
||||
m = allocmsg(c, SSH_CMSG_SESSION_KEY, 2048);
|
||||
putbyte(m, c->cipher->id);
|
||||
putbytes(m, c->cookie, COOKIELEN);
|
||||
|
||||
serverkeylen = mpsignif(c->serverkey->n);
|
||||
hostkeylen = mpsignif(c->hostkey->n);
|
||||
ksmall = kbig = nil;
|
||||
if(serverkeylen+128 <= hostkeylen){
|
||||
ksmall = c->serverkey;
|
||||
kbig = c->hostkey;
|
||||
}else if(hostkeylen+128 <= serverkeylen){
|
||||
ksmall = c->hostkey;
|
||||
kbig = c->serverkey;
|
||||
}else
|
||||
error("server session and host keys do not differ by at least 128 bits");
|
||||
|
||||
buflen = (mpsignif(kbig->n)+7)/8;
|
||||
buf = emalloc(buflen);
|
||||
|
||||
debug(DBG_CRYPTO, "session key is %.*H\n", SESSKEYLEN, c->sesskey);
|
||||
memmove(buf, c->sesskey, SESSKEYLEN);
|
||||
for(i = 0; i < SESSIDLEN; i++)
|
||||
buf[i] ^= c->sessid[i];
|
||||
debug(DBG_CRYPTO, "munged session key is %.*H\n", SESSKEYLEN, buf);
|
||||
|
||||
b = rsaencryptbuf(ksmall, buf, SESSKEYLEN);
|
||||
n = (mpsignif(ksmall->n)+7) / 8;
|
||||
mptoberjust(b, buf, n);
|
||||
mpfree(b);
|
||||
debug(DBG_CRYPTO, "encrypted with ksmall is %.*H\n", n, buf);
|
||||
|
||||
b = rsaencryptbuf(kbig, buf, n);
|
||||
putmpint(m, b);
|
||||
debug(DBG_CRYPTO, "encrypted with kbig is %B\n", b);
|
||||
mpfree(b);
|
||||
|
||||
memset(buf, 0, buflen);
|
||||
free(buf);
|
||||
|
||||
putlong(m, c->flags);
|
||||
sendmsg(m);
|
||||
}
|
||||
|
||||
static int
|
||||
authuser(Conn *c)
|
||||
{
|
||||
int i;
|
||||
Msg *m;
|
||||
|
||||
m = allocmsg(c, SSH_CMSG_USER, 4+strlen(c->user));
|
||||
putstring(m, c->user);
|
||||
sendmsg(m);
|
||||
|
||||
m = recvmsg(c, -1);
|
||||
switch(m->type){
|
||||
case SSH_SMSG_SUCCESS:
|
||||
free(m);
|
||||
return 0;
|
||||
case SSH_SMSG_FAILURE:
|
||||
free(m);
|
||||
break;
|
||||
default:
|
||||
badmsg(m, 0);
|
||||
}
|
||||
|
||||
for(i=0; i<c->nokauth; i++){
|
||||
debug(DBG_AUTH, "authmask %#lux, consider %s (%#x)\n",
|
||||
c->authmask, c->okauth[i]->name, 1<<c->okauth[i]->id);
|
||||
if(c->authmask & (1<<c->okauth[i]->id))
|
||||
if((*c->okauth[i]->fn)(c) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug(DBG_AUTH, "no auth methods worked; (authmask=%#lux)\n", c->authmask);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char
|
||||
ask(Conn *c, char *answers, char *question)
|
||||
{
|
||||
int fd;
|
||||
char buf[256];
|
||||
|
||||
if(!c->interactive)
|
||||
return answers[0];
|
||||
|
||||
if((fd = open("/dev/cons", ORDWR)) < 0)
|
||||
return answers[0];
|
||||
|
||||
fprint(fd, "%s", question);
|
||||
if(read(fd, buf, 256) <= 0 || buf[0]=='\n'){
|
||||
close(fd);
|
||||
return answers[0];
|
||||
}
|
||||
close(fd);
|
||||
return buf[0];
|
||||
}
|
||||
static void
|
||||
checkkey(Conn *c)
|
||||
{
|
||||
char *home, *keyfile;
|
||||
|
||||
debug(DBG_CRYPTO, "checking key %B %B\n", c->hostkey->n, c->hostkey->ek);
|
||||
switch(findkey("/sys/lib/ssh/keyring", c->aliases, c->hostkey)){
|
||||
default:
|
||||
abort();
|
||||
case KeyOk:
|
||||
return;
|
||||
case KeyWrong:
|
||||
fprint(2, "server presented public key different than expected\n");
|
||||
fprint(2, "(expected key in /sys/lib/ssh/keyring). will not continue.\n");
|
||||
error("bad server key");
|
||||
|
||||
case NoKey:
|
||||
case NoKeyFile:
|
||||
break;
|
||||
}
|
||||
|
||||
home = getenv("home");
|
||||
if(home == nil){
|
||||
fprint(2, "server %s not on keyring; will not continue.\n", c->host);
|
||||
error("bad server key");
|
||||
}
|
||||
|
||||
keyfile = smprint("%s/lib/keyring", home);
|
||||
if(keyfile == nil)
|
||||
error("out of memory");
|
||||
|
||||
switch(findkey(keyfile, c->aliases, c->hostkey)){
|
||||
default:
|
||||
abort();
|
||||
case KeyOk:
|
||||
return;
|
||||
case KeyWrong:
|
||||
fprint(2, "server %s presented public key different than expected\n", c->host);
|
||||
fprint(2, "(expected key in %s). will not continue.\n", keyfile);
|
||||
fprint(2, "this could be a man-in-the-middle attack.\n");
|
||||
switch(ask(c, "eri", "replace key in keyfile (r), continue without replacing key (c), or exit (e) [e]")){
|
||||
case 'e':
|
||||
error("bad key");
|
||||
case 'r':
|
||||
if(replacekey(keyfile, c->aliases, c->hostkey) < 0)
|
||||
error("replacekey: %r");
|
||||
break;
|
||||
case 'c':
|
||||
break;
|
||||
}
|
||||
return;
|
||||
case NoKey:
|
||||
case NoKeyFile:
|
||||
fprint(2, "server %s not on keyring.\n", c->host);
|
||||
switch(ask(c, "eac", "add key to keyfile (a), continue without adding key (c), or exit (e) [e]")){
|
||||
case 'e':
|
||||
error("bad key");
|
||||
case 'a':
|
||||
if(appendkey(keyfile, c->aliases, c->hostkey) < 0)
|
||||
error("appendkey: %r");
|
||||
break;
|
||||
case 'c':
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sshclienthandshake(Conn *c)
|
||||
{
|
||||
char buf[128], *p;
|
||||
int i;
|
||||
Msg *m;
|
||||
|
||||
/* receive id string */
|
||||
if(readstrnl(c->fd[0], buf, sizeof buf) < 0)
|
||||
error("reading server version: %r");
|
||||
|
||||
/* id string is "SSH-m.n-comment". We need m=1, n>=5. */
|
||||
if(strncmp(buf, "SSH-", 4) != 0
|
||||
|| strtol(buf+4, &p, 10) != 1
|
||||
|| *p != '.'
|
||||
|| strtol(p+1, &p, 10) < 5
|
||||
|| *p != '-')
|
||||
error("protocol mismatch; got %s, need SSH-1.x for x>=5", buf);
|
||||
|
||||
/* send id string */
|
||||
fprint(c->fd[1], "SSH-1.5-Plan 9\n");
|
||||
|
||||
recv_ssh_smsg_public_key(c);
|
||||
checkkey(c);
|
||||
|
||||
for(i=0; i<SESSKEYLEN; i++)
|
||||
c->sesskey[i] = fastrand();
|
||||
c->cipher = nil;
|
||||
for(i=0; i<c->nokcipher; i++)
|
||||
if((1<<c->okcipher[i]->id) & c->ciphermask){
|
||||
c->cipher = c->okcipher[i];
|
||||
break;
|
||||
}
|
||||
if(c->cipher == nil)
|
||||
error("can't agree on ciphers: remote side supports %#lux", c->ciphermask);
|
||||
|
||||
calcsessid(c);
|
||||
|
||||
send_ssh_cmsg_session_key(c);
|
||||
|
||||
c->cstate = (*c->cipher->init)(c, 0); /* turns on encryption */
|
||||
m = recvmsg(c, SSH_SMSG_SUCCESS);
|
||||
free(m);
|
||||
|
||||
if(authuser(c) < 0)
|
||||
error("client authentication failed");
|
||||
}
|
||||
|
||||
static int
|
||||
intgetenv(char *name, int def)
|
||||
{
|
||||
char *s;
|
||||
int n, val;
|
||||
|
||||
val = def;
|
||||
if((s = getenv(name))!=nil){
|
||||
if((n=atoi(s)) > 0)
|
||||
val = n;
|
||||
free(s);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* assumes that if you care, you're running under vt
|
||||
* and therefore these are set.
|
||||
*/
|
||||
int
|
||||
readgeom(int *nrow, int *ncol, int *width, int *height)
|
||||
{
|
||||
static int fd = -1;
|
||||
char buf[64];
|
||||
|
||||
if(fd < 0 && (fd = open("/dev/wctl", OREAD)) < 0)
|
||||
return -1;
|
||||
/* wait for event, but don't care what it says */
|
||||
if(read(fd, buf, sizeof buf) < 0)
|
||||
return -1;
|
||||
*nrow = intgetenv("LINES", 24);
|
||||
*ncol = intgetenv("COLS", 80);
|
||||
*width = intgetenv("XPIXELS", 640);
|
||||
*height = intgetenv("YPIXELS", 480);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sendwindowsize(Conn *c, int nrow, int ncol, int width, int height)
|
||||
{
|
||||
Msg *m;
|
||||
|
||||
m = allocmsg(c, SSH_CMSG_WINDOW_SIZE, 4*4);
|
||||
putlong(m, nrow);
|
||||
putlong(m, ncol);
|
||||
putlong(m, width);
|
||||
putlong(m, height);
|
||||
sendmsg(m);
|
||||
}
|
||||
|
||||
/*
|
||||
* In each option line, the first byte is the option number
|
||||
* and the second is either a boolean bit or actually an
|
||||
* ASCII code.
|
||||
*/
|
||||
static uchar ptyopt[] =
|
||||
{
|
||||
0x01, 0x7F, /* interrupt = DEL */
|
||||
0x02, 0x11, /* quit = ^Q */
|
||||
0x03, 0x08, /* backspace = ^H */
|
||||
0x04, 0x15, /* line kill = ^U */
|
||||
0x05, 0x04, /* EOF = ^D */
|
||||
0x20, 0x00, /* don't strip high bit */
|
||||
0x48, 0x01, /* give us CRs */
|
||||
|
||||
0x00, /* end options */
|
||||
};
|
||||
|
||||
static uchar rawptyopt[] =
|
||||
{
|
||||
30, 0, /* ignpar */
|
||||
31, 0, /* parmrk */
|
||||
32, 0, /* inpck */
|
||||
33, 0, /* istrip */
|
||||
34, 0, /* inlcr */
|
||||
35, 0, /* igncr */
|
||||
36, 0, /* icnrl */
|
||||
37, 0, /* iuclc */
|
||||
38, 0, /* ixon */
|
||||
39, 1, /* ixany */
|
||||
40, 0, /* ixoff */
|
||||
41, 0, /* imaxbel */
|
||||
|
||||
50, 0, /* isig: intr, quit, susp processing */
|
||||
51, 0, /* icanon: erase and kill processing */
|
||||
52, 0, /* xcase */
|
||||
|
||||
53, 0, /* echo */
|
||||
|
||||
57, 0, /* noflsh */
|
||||
58, 0, /* tostop */
|
||||
59, 0, /* iexten: impl defined control chars */
|
||||
|
||||
70, 0, /* opost */
|
||||
|
||||
0x00,
|
||||
};
|
||||
|
||||
void
|
||||
requestpty(Conn *c)
|
||||
{
|
||||
char *term;
|
||||
int nrow, ncol, width, height;
|
||||
Msg *m;
|
||||
|
||||
m = allocmsg(c, SSH_CMSG_REQUEST_PTY, 1024);
|
||||
if((term = getenv("TERM")) == nil)
|
||||
term = "9term";
|
||||
putstring(m, term);
|
||||
|
||||
readgeom(&nrow, &ncol, &width, &height);
|
||||
putlong(m, nrow); /* characters */
|
||||
putlong(m, ncol);
|
||||
putlong(m, width); /* pixels */
|
||||
putlong(m, height);
|
||||
|
||||
if(rawhack)
|
||||
putbytes(m, rawptyopt, sizeof rawptyopt);
|
||||
else
|
||||
putbytes(m, ptyopt, sizeof ptyopt);
|
||||
|
||||
sendmsg(m);
|
||||
|
||||
m = recvmsg(c, 0);
|
||||
switch(m->type){
|
||||
case SSH_SMSG_SUCCESS:
|
||||
debug(DBG_IO, "PTY allocated\n");
|
||||
break;
|
||||
case SSH_SMSG_FAILURE:
|
||||
debug(DBG_IO, "PTY allocation failed\n");
|
||||
break;
|
||||
default:
|
||||
badmsg(m, 0);
|
||||
}
|
||||
free(m);
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
</$objtype/mkfile
|
||||
|
||||
HFILES=ssh.h
|
||||
|
||||
TARG=\
|
||||
scp\
|
||||
ssh\
|
||||
sshnet\
|
||||
sshserve\
|
||||
|
||||
AUTHOFILES=\
|
||||
authpasswd.$O\
|
||||
authrsa.$O\
|
||||
authtis.$O\
|
||||
|
||||
AUTHSRVOFILES=\
|
||||
authsrvpasswd.$O\
|
||||
authsrvtis.$O\
|
||||
|
||||
CIPHEROFILES=\
|
||||
cipher3des.$O\
|
||||
cipherblowfish.$O\
|
||||
cipherdes.$O\
|
||||
ciphernone.$O\
|
||||
cipherrc4.$O\
|
||||
ciphertwiddle.$O\
|
||||
|
||||
OFILES=\
|
||||
msg.$O\
|
||||
util.$O\
|
||||
|
||||
BIN=/$objtype/bin
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
agent.c\
|
||||
cmsg.c\
|
||||
smsg.c\
|
||||
pubkey.c\
|
||||
$HFILES\
|
||||
${OFILES:%.$O=%.c}\
|
||||
${AUTHOFILES:%.$O=%.c}\
|
||||
${AUTHSRVOFILES:%.$O=%.c}\
|
||||
${CIPHEROFILES:%.$O=%.c}\
|
||||
${TARG:%=%.c}\
|
||||
|
||||
</sys/src/cmd/mkmany
|
||||
|
||||
$O.ssh: \
|
||||
$AUTHOFILES\
|
||||
$CIPHEROFILES\
|
||||
agent.$O\
|
||||
cmsg.$O\
|
||||
pubkey.$O\
|
||||
|
||||
$O.sshserve: \
|
||||
$AUTHSRVOFILES\
|
||||
$CIPHEROFILES\
|
||||
smsg.$O\
|
||||
|
||||
$O.sshnet: \
|
||||
$AUTHOFILES\
|
||||
$CIPHEROFILES\
|
||||
cmsg.$O\
|
||||
pubkey.$O\
|
||||
|
||||
$BIN/sshserve:VQ: $BIN/aux/sshserve
|
||||
;
|
||||
|
||||
$BIN/aux/sshserve: $O.sshserve
|
||||
cp $O.sshserve $BIN/aux/sshserve
|
||||
|
||||
$BIN/aux/ssh_genkey: $O.ssh_genkey
|
||||
cp $O.ssh_genkey $BIN/aux/ssh_genkey
|
||||
|
||||
sshserve.safeinstall:
|
||||
test -e $BIN/aux/sshserve && mv $BIN/aux/sshserve $BIN/aux/_sshserve
|
||||
mk sshserve.install
|
|
@ -1,512 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
static ulong sum32(ulong, void*, int);
|
||||
|
||||
char *msgnames[] =
|
||||
{
|
||||
/* 0 */
|
||||
"SSH_MSG_NONE",
|
||||
"SSH_MSG_DISCONNECT",
|
||||
"SSH_SMSG_PUBLIC_KEY",
|
||||
"SSH_CMSG_SESSION_KEY",
|
||||
"SSH_CMSG_USER",
|
||||
"SSH_CMSG_AUTH_RHOSTS",
|
||||
"SSH_CMSG_AUTH_RSA",
|
||||
"SSH_SMSG_AUTH_RSA_CHALLENGE",
|
||||
"SSH_CMSG_AUTH_RSA_RESPONSE",
|
||||
"SSH_CMSG_AUTH_PASSWORD",
|
||||
|
||||
/* 10 */
|
||||
"SSH_CMSG_REQUEST_PTY",
|
||||
"SSH_CMSG_WINDOW_SIZE",
|
||||
"SSH_CMSG_EXEC_SHELL",
|
||||
"SSH_CMSG_EXEC_CMD",
|
||||
"SSH_SMSG_SUCCESS",
|
||||
"SSH_SMSG_FAILURE",
|
||||
"SSH_CMSG_STDIN_DATA",
|
||||
"SSH_SMSG_STDOUT_DATA",
|
||||
"SSH_SMSG_STDERR_DATA",
|
||||
"SSH_CMSG_EOF",
|
||||
|
||||
/* 20 */
|
||||
"SSH_SMSG_EXITSTATUS",
|
||||
"SSH_MSG_CHANNEL_OPEN_CONFIRMATION",
|
||||
"SSH_MSG_CHANNEL_OPEN_FAILURE",
|
||||
"SSH_MSG_CHANNEL_DATA",
|
||||
"SSH_MSG_CHANNEL_INPUT_EOF",
|
||||
"SSH_MSG_CHANNEL_OUTPUT_CLOSED",
|
||||
"SSH_MSG_UNIX_DOMAIN_X11_FORWARDING (obsolete)",
|
||||
"SSH_SMSG_X11_OPEN",
|
||||
"SSH_CMSG_PORT_FORWARD_REQUEST",
|
||||
"SSH_MSG_PORT_OPEN",
|
||||
|
||||
/* 30 */
|
||||
"SSH_CMSG_AGENT_REQUEST_FORWARDING",
|
||||
"SSH_SMSG_AGENT_OPEN",
|
||||
"SSH_MSG_IGNORE",
|
||||
"SSH_CMSG_EXIT_CONFIRMATION",
|
||||
"SSH_CMSG_X11_REQUEST_FORWARDING",
|
||||
"SSH_CMSG_AUTH_RHOSTS_RSA",
|
||||
"SSH_MSG_DEBUG",
|
||||
"SSH_CMSG_REQUEST_COMPRESSION",
|
||||
"SSH_CMSG_MAX_PACKET_SIZE",
|
||||
"SSH_CMSG_AUTH_TIS",
|
||||
|
||||
/* 40 */
|
||||
"SSH_SMSG_AUTH_TIS_CHALLENGE",
|
||||
"SSH_CMSG_AUTH_TIS_RESPONSE",
|
||||
"SSH_CMSG_AUTH_KERBEROS",
|
||||
"SSH_SMSG_AUTH_KERBEROS_RESPONSE",
|
||||
"SSH_CMSG_HAVE_KERBEROS_TGT"
|
||||
};
|
||||
|
||||
void
|
||||
badmsg(Msg *m, int want)
|
||||
{
|
||||
char *s, buf[20+ERRMAX];
|
||||
|
||||
if(m==nil){
|
||||
snprint(buf, sizeof buf, "<early eof: %r>");
|
||||
s = buf;
|
||||
}else{
|
||||
snprint(buf, sizeof buf, "<unknown type %d>", m->type);
|
||||
s = buf;
|
||||
if(0 <= m->type && m->type < nelem(msgnames))
|
||||
s = msgnames[m->type];
|
||||
}
|
||||
if(want)
|
||||
error("got %s message expecting %s", s, msgnames[want]);
|
||||
error("got unexpected %s message", s);
|
||||
}
|
||||
|
||||
Msg*
|
||||
allocmsg(Conn *c, int type, int len)
|
||||
{
|
||||
uchar *p;
|
||||
Msg *m;
|
||||
|
||||
if(len > 256*1024)
|
||||
abort();
|
||||
|
||||
m = (Msg*)emalloc(sizeof(Msg)+4+8+1+len+4);
|
||||
setmalloctag(m, getcallerpc(&c));
|
||||
p = (uchar*)&m[1];
|
||||
m->c = c;
|
||||
m->bp = p;
|
||||
m->ep = p+len;
|
||||
m->wp = p;
|
||||
m->type = type;
|
||||
return m;
|
||||
}
|
||||
|
||||
void
|
||||
unrecvmsg(Conn *c, Msg *m)
|
||||
{
|
||||
debug(DBG_PROTO, "unreceived %s len %zd\n", msgnames[m->type], m->ep - m->rp);
|
||||
free(c->unget);
|
||||
c->unget = m;
|
||||
}
|
||||
|
||||
static Msg*
|
||||
recvmsg0(Conn *c)
|
||||
{
|
||||
int pad;
|
||||
uchar *p, buf[4];
|
||||
ulong crc, crc0, len;
|
||||
Msg *m;
|
||||
|
||||
if(c->unget){
|
||||
m = c->unget;
|
||||
c->unget = nil;
|
||||
return m;
|
||||
}
|
||||
|
||||
if(readn(c->fd[0], buf, 4) != 4){
|
||||
werrstr("short net read: %r");
|
||||
return nil;
|
||||
}
|
||||
|
||||
len = LONG(buf);
|
||||
if(len > 256*1024){
|
||||
werrstr("packet size far too big: %.8lux", len);
|
||||
return nil;
|
||||
}
|
||||
|
||||
pad = 8 - len%8;
|
||||
|
||||
m = (Msg*)emalloc(sizeof(Msg)+pad+len);
|
||||
setmalloctag(m, getcallerpc(&c));
|
||||
m->c = c;
|
||||
m->bp = (uchar*)&m[1];
|
||||
m->ep = m->bp + pad+len-4; /* -4: don't include crc */
|
||||
m->rp = m->bp;
|
||||
|
||||
if(readn(c->fd[0], m->bp, pad+len) != pad+len){
|
||||
werrstr("short net read: %r");
|
||||
free(m);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(c->cipher)
|
||||
c->cipher->decrypt(c->cstate, m->bp, len+pad);
|
||||
|
||||
crc = sum32(0, m->bp, pad+len-4);
|
||||
p = m->bp + pad+len-4;
|
||||
crc0 = LONG(p);
|
||||
if(crc != crc0){
|
||||
werrstr("bad crc %#lux != %#lux (packet length %lud)", crc, crc0, len);
|
||||
free(m);
|
||||
return nil;
|
||||
}
|
||||
|
||||
m->rp += pad;
|
||||
m->type = *m->rp++;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
Msg*
|
||||
recvmsg(Conn *c, int type)
|
||||
{
|
||||
Msg *m;
|
||||
|
||||
while((m = recvmsg0(c)) != nil){
|
||||
debug(DBG_PROTO, "received %s len %zd\n", msgnames[m->type], m->ep - m->rp);
|
||||
if(m->type != SSH_MSG_DEBUG && m->type != SSH_MSG_IGNORE)
|
||||
break;
|
||||
if(m->type == SSH_MSG_DEBUG)
|
||||
debug(DBG_PROTO, "remote DEBUG: %s\n", getstring(m));
|
||||
free(m);
|
||||
}
|
||||
if(type == 0){
|
||||
/* no checking */
|
||||
}else if(type == -1){
|
||||
/* must not be nil */
|
||||
if(m == nil)
|
||||
error(Ehangup);
|
||||
}else{
|
||||
/* must be given type */
|
||||
if(m==nil || m->type!=type)
|
||||
badmsg(m, type);
|
||||
}
|
||||
setmalloctag(m, getcallerpc(&c));
|
||||
return m;
|
||||
}
|
||||
|
||||
int
|
||||
sendmsg(Msg *m)
|
||||
{
|
||||
int i, pad;
|
||||
uchar *p;
|
||||
ulong datalen, len, crc;
|
||||
Conn *c;
|
||||
|
||||
datalen = m->wp - m->bp;
|
||||
len = datalen + 5;
|
||||
pad = 8 - len%8;
|
||||
|
||||
debug(DBG_PROTO, "sending %s len %lud\n", msgnames[m->type], datalen);
|
||||
|
||||
p = m->bp;
|
||||
memmove(m->bp+4+pad+1, m->bp, datalen); /* slide data to correct position */
|
||||
|
||||
PLONG(p, len);
|
||||
p += 4;
|
||||
|
||||
if(m->c->cstate){
|
||||
for(i=0; i<pad; i++)
|
||||
*p++ = fastrand();
|
||||
}else{
|
||||
memset(p, 0, pad);
|
||||
p += pad;
|
||||
}
|
||||
|
||||
*p++ = m->type;
|
||||
|
||||
/* data already in position */
|
||||
p += datalen;
|
||||
|
||||
crc = sum32(0, m->bp+4, pad+1+datalen);
|
||||
PLONG(p, crc);
|
||||
p += 4;
|
||||
|
||||
c = m->c;
|
||||
qlock(c);
|
||||
if(c->cstate)
|
||||
c->cipher->encrypt(c->cstate, m->bp+4, len+pad);
|
||||
|
||||
if(write(c->fd[1], m->bp, p - m->bp) != p-m->bp){
|
||||
qunlock(c);
|
||||
free(m);
|
||||
return -1;
|
||||
}
|
||||
qunlock(c);
|
||||
free(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uchar
|
||||
getbyte(Msg *m)
|
||||
{
|
||||
if(m->rp >= m->ep)
|
||||
error(Edecode);
|
||||
return *m->rp++;
|
||||
}
|
||||
|
||||
ushort
|
||||
getshort(Msg *m)
|
||||
{
|
||||
ushort x;
|
||||
|
||||
if(m->rp+2 > m->ep)
|
||||
error(Edecode);
|
||||
|
||||
x = SHORT(m->rp);
|
||||
m->rp += 2;
|
||||
return x;
|
||||
}
|
||||
|
||||
ulong
|
||||
getlong(Msg *m)
|
||||
{
|
||||
ulong x;
|
||||
|
||||
if(m->rp+4 > m->ep)
|
||||
error(Edecode);
|
||||
|
||||
x = LONG(m->rp);
|
||||
m->rp += 4;
|
||||
return x;
|
||||
}
|
||||
|
||||
char*
|
||||
getstring(Msg *m)
|
||||
{
|
||||
char *p;
|
||||
ulong len;
|
||||
|
||||
/* overwrites length to make room for NUL */
|
||||
len = getlong(m);
|
||||
if(m->rp+len > m->ep)
|
||||
error(Edecode);
|
||||
p = (char*)m->rp-1;
|
||||
memmove(p, m->rp, len);
|
||||
p[len] = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
void*
|
||||
getbytes(Msg *m, int n)
|
||||
{
|
||||
uchar *p;
|
||||
|
||||
if(m->rp+n > m->ep)
|
||||
error(Edecode);
|
||||
p = m->rp;
|
||||
m->rp += n;
|
||||
return p;
|
||||
}
|
||||
|
||||
mpint*
|
||||
getmpint(Msg *m)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = (getshort(m)+7)/8; /* getshort returns # bits */
|
||||
return betomp(getbytes(m, n), n, nil);
|
||||
}
|
||||
|
||||
RSApub*
|
||||
getRSApub(Msg *m)
|
||||
{
|
||||
RSApub *key;
|
||||
|
||||
getlong(m);
|
||||
key = rsapuballoc();
|
||||
if(key == nil)
|
||||
error(Ememory);
|
||||
key->ek = getmpint(m);
|
||||
key->n = getmpint(m);
|
||||
setmalloctag(key, getcallerpc(&m));
|
||||
return key;
|
||||
}
|
||||
|
||||
void
|
||||
putbyte(Msg *m, uchar x)
|
||||
{
|
||||
if(m->wp >= m->ep)
|
||||
error(Eencode);
|
||||
*m->wp++ = x;
|
||||
}
|
||||
|
||||
void
|
||||
putshort(Msg *m, ushort x)
|
||||
{
|
||||
if(m->wp+2 > m->ep)
|
||||
error(Eencode);
|
||||
PSHORT(m->wp, x);
|
||||
m->wp += 2;
|
||||
}
|
||||
|
||||
void
|
||||
putlong(Msg *m, ulong x)
|
||||
{
|
||||
if(m->wp+4 > m->ep)
|
||||
error(Eencode);
|
||||
PLONG(m->wp, x);
|
||||
m->wp += 4;
|
||||
}
|
||||
|
||||
void
|
||||
putstring(Msg *m, char *s)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = strlen(s);
|
||||
putlong(m, len);
|
||||
putbytes(m, s, len);
|
||||
}
|
||||
|
||||
void
|
||||
putbytes(Msg *m, void *a, long n)
|
||||
{
|
||||
if(m->wp+n > m->ep)
|
||||
error(Eencode);
|
||||
memmove(m->wp, a, n);
|
||||
m->wp += n;
|
||||
}
|
||||
|
||||
void
|
||||
putmpint(Msg *m, mpint *b)
|
||||
{
|
||||
int bits, n;
|
||||
|
||||
bits = mpsignif(b);
|
||||
putshort(m, bits);
|
||||
n = (bits+7)/8;
|
||||
if(m->wp+n > m->ep)
|
||||
error(Eencode);
|
||||
mptobe(b, m->wp, n, nil);
|
||||
m->wp += n;
|
||||
}
|
||||
|
||||
void
|
||||
putRSApub(Msg *m, RSApub *key)
|
||||
{
|
||||
putlong(m, mpsignif(key->n));
|
||||
putmpint(m, key->ek);
|
||||
putmpint(m, key->n);
|
||||
}
|
||||
|
||||
static ulong crctab[256];
|
||||
|
||||
static void
|
||||
initsum32(void)
|
||||
{
|
||||
ulong crc, poly;
|
||||
int i, j;
|
||||
|
||||
poly = 0xEDB88320;
|
||||
for(i = 0; i < 256; i++){
|
||||
crc = i;
|
||||
for(j = 0; j < 8; j++){
|
||||
if(crc & 1)
|
||||
crc = (crc >> 1) ^ poly;
|
||||
else
|
||||
crc >>= 1;
|
||||
}
|
||||
crctab[i] = crc;
|
||||
}
|
||||
}
|
||||
|
||||
static ulong
|
||||
sum32(ulong lcrc, void *buf, int n)
|
||||
{
|
||||
static int first=1;
|
||||
uchar *s = buf;
|
||||
ulong crc = lcrc;
|
||||
|
||||
if(first){
|
||||
first=0;
|
||||
initsum32();
|
||||
}
|
||||
while(n-- > 0)
|
||||
crc = crctab[(crc^*s++)&0xff] ^ (crc>>8);
|
||||
return crc;
|
||||
}
|
||||
|
||||
mpint*
|
||||
rsapad(mpint *b, int n)
|
||||
{
|
||||
int i, pad, nbuf;
|
||||
uchar buf[2560];
|
||||
mpint *c;
|
||||
|
||||
if(n > sizeof buf)
|
||||
error("buffer too small in rsapad");
|
||||
|
||||
nbuf = (mpsignif(b)+7)/8;
|
||||
pad = n - nbuf;
|
||||
assert(pad >= 3);
|
||||
mptobe(b, buf, nbuf, nil);
|
||||
memmove(buf+pad, buf, nbuf);
|
||||
|
||||
buf[0] = 0;
|
||||
buf[1] = 2;
|
||||
for(i=2; i<pad-1; i++)
|
||||
buf[i]=1+fastrand()%255;
|
||||
buf[pad-1] = 0;
|
||||
c = betomp(buf, n, nil);
|
||||
memset(buf, 0, sizeof buf);
|
||||
return c;
|
||||
}
|
||||
|
||||
mpint*
|
||||
rsaunpad(mpint *b)
|
||||
{
|
||||
int i, n;
|
||||
uchar buf[2560];
|
||||
|
||||
n = (mpsignif(b)+7)/8;
|
||||
if(n > sizeof buf)
|
||||
error("buffer too small in rsaunpad");
|
||||
mptobe(b, buf, n, nil);
|
||||
|
||||
/* the initial zero has been eaten by the betomp -> mptobe sequence */
|
||||
if(buf[0] != 2)
|
||||
error("bad data in rsaunpad");
|
||||
for(i=1; i<n; i++)
|
||||
if(buf[i]==0)
|
||||
break;
|
||||
return betomp(buf+i, n-i, nil);
|
||||
}
|
||||
|
||||
void
|
||||
mptoberjust(mpint *b, uchar *buf, int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = mptobe(b, buf, len, nil);
|
||||
assert(n >= 0);
|
||||
if(n < len){
|
||||
len -= n;
|
||||
memmove(buf+len, buf, n);
|
||||
memset(buf, 0, len);
|
||||
}
|
||||
}
|
||||
|
||||
mpint*
|
||||
rsaencryptbuf(RSApub *key, uchar *buf, int nbuf)
|
||||
{
|
||||
int n;
|
||||
mpint *a, *b, *c;
|
||||
|
||||
n = (mpsignif(key->n)+7)/8;
|
||||
a = betomp(buf, nbuf, nil);
|
||||
b = rsapad(a, n);
|
||||
mpfree(a);
|
||||
c = rsaencrypt(key, b, nil);
|
||||
mpfree(b);
|
||||
return c;
|
||||
}
|
||||
|
|
@ -1,227 +0,0 @@
|
|||
#include "ssh.h"
|
||||
#include <bio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
static int
|
||||
parsepubkey(char *s, RSApub *key, char **sp, int base)
|
||||
{
|
||||
int n;
|
||||
char *host, *p, *z;
|
||||
|
||||
z = nil;
|
||||
n = strtoul(s, &p, 10);
|
||||
host = nil;
|
||||
if(n < 256 || !isspace(*p)){ /* maybe this is a host name */
|
||||
host = s;
|
||||
s = strpbrk(s, " \t");
|
||||
if(s == nil)
|
||||
return -1;
|
||||
z = s;
|
||||
*s++ = '\0';
|
||||
s += strspn(s, " \t");
|
||||
|
||||
n = strtoul(s, &p, 10);
|
||||
if(n < 256 || !isspace(*p)){
|
||||
if(z)
|
||||
*z = ' ';
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if((key->ek = strtomp(p, &p, base, nil)) == nil
|
||||
|| (key->n = strtomp(p, &p, base, nil)) == nil
|
||||
|| (*p != '\0' && !isspace(*p))
|
||||
|| mpsignif(key->n) < 256){ /* 256 is just a sanity check */
|
||||
mpfree(key->ek);
|
||||
mpfree(key->n);
|
||||
key->ek = nil;
|
||||
key->n = nil;
|
||||
if(z)
|
||||
*z = ' ';
|
||||
return -1;
|
||||
}
|
||||
if(host == nil){
|
||||
if(*p != '\0'){
|
||||
p += strspn(p, " \t");
|
||||
if(*p != '\0'){
|
||||
host = emalloc(strlen(p)+1);
|
||||
strcpy(host, p);
|
||||
}
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
*sp = host;
|
||||
return 0;
|
||||
}
|
||||
|
||||
RSApub*
|
||||
readpublickey(Biobuf *b, char **sp)
|
||||
{
|
||||
char *s;
|
||||
RSApub *key;
|
||||
|
||||
key = rsapuballoc();
|
||||
if(key == nil)
|
||||
return nil;
|
||||
|
||||
for(;;){
|
||||
if((s = Brdstr(b, '\n', 1)) == nil){
|
||||
rsapubfree(key);
|
||||
return nil;
|
||||
}
|
||||
if(s[0]=='#'){
|
||||
free(s);
|
||||
continue;
|
||||
}
|
||||
if(parsepubkey(s, key, sp, 10)==0
|
||||
|| parsepubkey(s, key, sp, 16)==0)
|
||||
return key;
|
||||
fprint(2, "warning: skipping line '%s'; cannot parse\n", s);
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
match(char *pattern, char *aliases)
|
||||
{
|
||||
char *s, *snext;
|
||||
char *a, *anext, *ae;
|
||||
|
||||
for(s=pattern; s && *s; s=snext){
|
||||
if((snext=strchr(s, ',')) != nil)
|
||||
*snext++ = '\0';
|
||||
for(a=aliases; a && *a; a=anext){
|
||||
if((anext=strchr(a, ',')) != nil){
|
||||
ae = anext;
|
||||
anext++;
|
||||
}else
|
||||
ae = a+strlen(a);
|
||||
if(ae-a == strlen(s) && memcmp(s, a, ae-a)==0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
findkey(char *keyfile, char *host, RSApub *key)
|
||||
{
|
||||
char *h;
|
||||
Biobuf *b;
|
||||
RSApub *k;
|
||||
|
||||
if((b = Bopen(keyfile, OREAD)) == nil)
|
||||
return NoKeyFile;
|
||||
|
||||
for(;;){
|
||||
if((k = readpublickey(b, &h)) == nil){
|
||||
Bterm(b);
|
||||
return NoKey;
|
||||
}
|
||||
if(match(h, host) != 0){
|
||||
free(h);
|
||||
rsapubfree(k);
|
||||
continue;
|
||||
}
|
||||
if(mpcmp(k->n, key->n) != 0 || mpcmp(k->ek, key->ek) != 0){
|
||||
free(h);
|
||||
rsapubfree(k);
|
||||
Bterm(b);
|
||||
return KeyWrong;
|
||||
}
|
||||
free(h);
|
||||
rsapubfree(k);
|
||||
Bterm(b);
|
||||
return KeyOk;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
replacekey(char *keyfile, char *host, RSApub *hostkey)
|
||||
{
|
||||
char *h, *nkey, *p;
|
||||
Biobuf *br, *bw;
|
||||
Dir *d, nd;
|
||||
RSApub *k;
|
||||
|
||||
nkey = smprint("%s.new", keyfile);
|
||||
if(nkey == nil)
|
||||
return -1;
|
||||
|
||||
if((br = Bopen(keyfile, OREAD)) == nil){
|
||||
free(nkey);
|
||||
return -1;
|
||||
}
|
||||
if((bw = Bopen(nkey, OWRITE)) == nil){
|
||||
Bterm(br);
|
||||
free(nkey);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while((k = readpublickey(br, &h)) != nil){
|
||||
if(match(h, host) != 0){
|
||||
Bprint(bw, "%s %d %.10B %.10B\n",
|
||||
h, mpsignif(k->n), k->ek, k->n);
|
||||
}
|
||||
free(h);
|
||||
rsapubfree(k);
|
||||
}
|
||||
Bprint(bw, "%s %d %.10B %.10B\n", host, mpsignif(hostkey->n), hostkey->ek, hostkey->n);
|
||||
Bterm(bw);
|
||||
Bterm(br);
|
||||
|
||||
d = dirstat(nkey);
|
||||
if(d == nil){
|
||||
fprint(2, "new key file disappeared?\n");
|
||||
free(nkey);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = strrchr(d->name, '.');
|
||||
if(p==nil || strcmp(p, ".new")!=0){
|
||||
fprint(2, "new key file changed names? %s to %s\n", nkey, d->name);
|
||||
free(d);
|
||||
free(nkey);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
nulldir(&nd);
|
||||
nd.name = d->name;
|
||||
if(remove(keyfile) < 0){
|
||||
fprint(2, "error removing %s: %r\n", keyfile);
|
||||
free(d);
|
||||
free(nkey);
|
||||
return -1;
|
||||
}
|
||||
if(dirwstat(nkey, &nd) < 0){
|
||||
fprint(2, "error renaming %s to %s: %r\n", nkey, d->name);
|
||||
free(nkey);
|
||||
free(d);
|
||||
return -1;
|
||||
}
|
||||
free(d);
|
||||
free(nkey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
appendkey(char *keyfile, char *host, RSApub *key)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if((fd = open(keyfile, OWRITE)) < 0){
|
||||
fd = create(keyfile, OWRITE, 0666);
|
||||
if(fd < 0){
|
||||
fprint(2, "cannot open nor create %s: %r\n", keyfile);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if(seek(fd, 0, 2) < 0
|
||||
|| fprint(fd, "%s %d %.10B %.10B\n", host, mpsignif(key->n), key->ek, key->n) < 0){
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
|
@ -1,799 +0,0 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <ctype.h>
|
||||
|
||||
int
|
||||
isatty(int fd)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
buf[0] = '\0';
|
||||
fd2path(fd, buf, sizeof buf);
|
||||
if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define OK 0x00
|
||||
#define ERROR 0x01
|
||||
#define FATAL 0x02
|
||||
|
||||
char *progname;
|
||||
|
||||
int dflag;
|
||||
int fflag;
|
||||
int iflag;
|
||||
int pflag;
|
||||
int rflag;
|
||||
int tflag;
|
||||
int vflag;
|
||||
|
||||
int remote;
|
||||
|
||||
char *exitflag = nil;
|
||||
|
||||
void scperror(int, char*, ...);
|
||||
void mustbedir(char*);
|
||||
void receive(char*);
|
||||
char *fileaftercolon(char*);
|
||||
void destislocal(char *cmd, int argc, char *argv[], char *dest);
|
||||
void destisremote(char *cmd, int argc, char *argv[], char *host, char *dest);
|
||||
int remotessh(char *host, char *cmd);
|
||||
void send(char*);
|
||||
void senddir(char*, int, Dir*);
|
||||
int getresponse(void);
|
||||
|
||||
char theuser[32];
|
||||
|
||||
char ssh[] = "/bin/ssh";
|
||||
|
||||
int remotefd0;
|
||||
int remotefd1;
|
||||
|
||||
int
|
||||
runcommand(char *cmd)
|
||||
{
|
||||
Waitmsg *w;
|
||||
int pid;
|
||||
char *argv[4];
|
||||
|
||||
if (cmd == nil)
|
||||
return -1;
|
||||
switch(pid = fork()){
|
||||
case -1:
|
||||
return -1;
|
||||
case 0:
|
||||
argv[0] = "rc";
|
||||
argv[1] = "-c";
|
||||
argv[2] = cmd;
|
||||
argv[3] = nil;
|
||||
exec("/bin/rc", argv);
|
||||
exits("exec failed");
|
||||
}
|
||||
for(;;){
|
||||
w = wait();
|
||||
if(w == nil)
|
||||
return -1;
|
||||
if(w->pid == pid)
|
||||
break;
|
||||
free(w);
|
||||
}
|
||||
if(w->msg[0]){
|
||||
free(w);
|
||||
return -1;
|
||||
}
|
||||
free(w);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
vprint(char *fmt, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
va_list arg;
|
||||
static char *name;
|
||||
|
||||
if(vflag == 0)
|
||||
return;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vseprint(buf, buf+sizeof(buf), fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
if(name == nil){
|
||||
name = sysname();
|
||||
if(name == nil)
|
||||
name = "<unknown>";
|
||||
}
|
||||
fprint(2, "%s: %s\n", name, buf);
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "Usage: scp [-Iidfprtv] source ... destination\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
|
||||
#pragma varargck type "F" int
|
||||
#pragma varargck type "V" char*
|
||||
static int flag;
|
||||
|
||||
/* flag: if integer flag, take following char *value */
|
||||
int
|
||||
flagfmt(Fmt *f)
|
||||
{
|
||||
flag = va_arg(f->args, int);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* flag: if previous integer flag, take char *value */
|
||||
int
|
||||
valfmt(Fmt *f)
|
||||
{
|
||||
char *value;
|
||||
|
||||
value = va_arg(f->args, char*);
|
||||
if(flag)
|
||||
return fmtprint(f, " %s", value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sendokresponse(void)
|
||||
{
|
||||
char ok = OK;
|
||||
|
||||
write(remotefd1, &ok, 1);
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i, fd;
|
||||
char cmd[32];
|
||||
char *p;
|
||||
|
||||
progname = argv[0];
|
||||
fmtinstall('F', flagfmt);
|
||||
fmtinstall('V', valfmt);
|
||||
iflag = -1;
|
||||
|
||||
ARGBEGIN {
|
||||
case 'I':
|
||||
iflag = 0;
|
||||
break;
|
||||
case 'i':
|
||||
iflag = 1;
|
||||
break;
|
||||
case 'd':
|
||||
dflag++;
|
||||
break;
|
||||
case 'f':
|
||||
fflag++;
|
||||
remote++;
|
||||
break;
|
||||
case 'p':
|
||||
pflag++;
|
||||
break;
|
||||
case 'r':
|
||||
rflag++;
|
||||
break;
|
||||
case 't':
|
||||
tflag++;
|
||||
remote++;
|
||||
break;
|
||||
case 'v':
|
||||
vflag++;
|
||||
break;
|
||||
default:
|
||||
scperror(1, "unknown option %c", ARGC());
|
||||
} ARGEND
|
||||
|
||||
if(iflag == -1)
|
||||
iflag = isatty(0);
|
||||
|
||||
remotefd0 = 0;
|
||||
remotefd1 = 1;
|
||||
|
||||
if(fflag){
|
||||
getresponse();
|
||||
for(i=0; i<argc; i++)
|
||||
send(argv[i]);
|
||||
exits(0);
|
||||
}
|
||||
if(tflag){
|
||||
if(argc != 1)
|
||||
usage();
|
||||
receive(argv[0]);
|
||||
exits(0);
|
||||
}
|
||||
|
||||
if (argc < 2)
|
||||
usage();
|
||||
if (argc > 2)
|
||||
dflag = 1;
|
||||
|
||||
i = 0;
|
||||
fd = open("/dev/user", OREAD);
|
||||
if(fd >= 0){
|
||||
i = read(fd, theuser, sizeof theuser - 1);
|
||||
close(fd);
|
||||
}
|
||||
if(i <= 0)
|
||||
scperror(1, "can't read /dev/user: %r");
|
||||
|
||||
remotefd0 = -1;
|
||||
remotefd1 = -1;
|
||||
|
||||
snprint(cmd, sizeof cmd, "scp%F%V%F%V%F%V%F%V",
|
||||
dflag, "-d",
|
||||
pflag, "-p",
|
||||
rflag, "-r",
|
||||
vflag, "-v");
|
||||
|
||||
p = fileaftercolon(argv[argc-1]);
|
||||
if(p != nil) /* send to remote machine. */
|
||||
destisremote(cmd, argc-1, argv, argv[argc-1], p);
|
||||
else{
|
||||
if(dflag)
|
||||
mustbedir(argv[argc-1]);
|
||||
destislocal(cmd, argc-1, argv, argv[argc-1]);
|
||||
}
|
||||
|
||||
exits(exitflag);
|
||||
}
|
||||
|
||||
void
|
||||
destislocal(char *cmd, int argc, char *argv[], char *dst)
|
||||
{
|
||||
int i;
|
||||
char *src;
|
||||
char buf[4096];
|
||||
|
||||
for(i = 0; i<argc; i++){
|
||||
src = fileaftercolon(argv[i]);
|
||||
if(src == nil){
|
||||
/* local file; no network */
|
||||
snprint(buf, sizeof buf, "exec cp%F%V%F%V %s %s",
|
||||
rflag, "-r",
|
||||
pflag, "-p",
|
||||
argv[i], dst);
|
||||
vprint("remotetolocal: %s", buf);
|
||||
if(runcommand(buf) < 0)
|
||||
exitflag = "local cp exec";
|
||||
}else{
|
||||
/* remote file; use network */
|
||||
snprint(buf, sizeof buf, "%s -f %s", cmd, src);
|
||||
if(remotessh(argv[i], buf) < 0)
|
||||
exitflag = "remote ssh exec";
|
||||
else{
|
||||
receive(dst);
|
||||
close(remotefd0);
|
||||
remotefd0 = -1;
|
||||
remotefd1 = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
destisremote(char *cmd, int argc, char *argv[], char *host, char *dest)
|
||||
{
|
||||
int i;
|
||||
char *src;
|
||||
char buf[4096];
|
||||
|
||||
for(i = 0; i < argc; i++){
|
||||
vprint("remote destination: send %s to %s:%s", argv[i], host, dest);
|
||||
/* destination is remote, but source may be local */
|
||||
src = fileaftercolon(argv[i]);
|
||||
if(src != nil){
|
||||
/* remote to remote */
|
||||
snprint(buf, sizeof buf, "exec %s%F%V%F%V %s %s %s '%s:%s'",
|
||||
ssh,
|
||||
iflag, " -i",
|
||||
vflag, "-v",
|
||||
argv[i], cmd, src,
|
||||
host, dest);
|
||||
vprint("localtoremote: %s", buf);
|
||||
runcommand(buf);
|
||||
}else{
|
||||
/* local to remote */
|
||||
if(remotefd0 == -1){
|
||||
snprint(buf, sizeof buf, "%s -t %s", cmd, dest);
|
||||
if(remotessh(host, buf) < 0)
|
||||
exits("remotessh");
|
||||
if(getresponse() < 0)
|
||||
exits("bad response");
|
||||
}
|
||||
send(argv[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
readhdr(char *p, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<n; i++){
|
||||
if(read(remotefd0, &p[i], 1) != 1)
|
||||
break;
|
||||
if(p[i] == '\n'){
|
||||
p[i] = '\0';
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* if at beginning, this is regular EOF */
|
||||
if(i == 0)
|
||||
exits(nil);
|
||||
scperror(1, "read error on receive header: %r");
|
||||
}
|
||||
|
||||
Dir *
|
||||
receivedir(char *dir, int exists, Dir *d, int settimes, ulong atime, ulong mtime, ulong mode)
|
||||
{
|
||||
Dir nd;
|
||||
int setmodes;
|
||||
int fd;
|
||||
|
||||
setmodes = pflag;
|
||||
if(exists){
|
||||
if(!(d->qid.type & QTDIR)) {
|
||||
scperror(0, "%s: protocol botch: directory requrest for non-directory", dir);
|
||||
return d;
|
||||
}
|
||||
}else{
|
||||
/* create it writeable; will fix later */
|
||||
setmodes = 1;
|
||||
fd = create(dir, OREAD, DMDIR|mode|0700);
|
||||
if (fd < 0){
|
||||
scperror(0, "%s: can't create: %r", dir);
|
||||
return d;
|
||||
}
|
||||
d = dirfstat(fd);
|
||||
close(fd);
|
||||
if(d == nil){
|
||||
scperror(0, "%s: can't stat: %r", dir);
|
||||
return d;
|
||||
}
|
||||
}
|
||||
receive(dir);
|
||||
if(settimes || setmodes){
|
||||
nulldir(&nd);
|
||||
if(settimes){
|
||||
nd.atime = atime;
|
||||
nd.mtime = mtime;
|
||||
d->atime = nd.atime;
|
||||
d->mtime = nd.mtime;
|
||||
}
|
||||
if(setmodes){
|
||||
nd.mode = DMDIR | (mode & 0777);
|
||||
d->mode = nd.mode;
|
||||
}
|
||||
if(dirwstat(dir, &nd) < 0){
|
||||
scperror(0, "can't wstat %s: %r", dir);
|
||||
free(d);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
void
|
||||
receive(char *dest)
|
||||
{
|
||||
int isdir, settimes, mode;
|
||||
int exists, n, i, fd, m;
|
||||
int errors;
|
||||
ulong atime, mtime, size;
|
||||
char buf[8192], *p;
|
||||
char name[1024];
|
||||
Dir *d;
|
||||
Dir nd;
|
||||
|
||||
mtime = 0L;
|
||||
atime = 0L;
|
||||
settimes = 0;
|
||||
isdir = 0;
|
||||
if ((d = dirstat(dest)) && (d->qid.type & QTDIR)) {
|
||||
isdir = 1;
|
||||
}
|
||||
if(dflag && !isdir)
|
||||
scperror(1, "%s: not a directory: %r", dest);
|
||||
|
||||
sendokresponse();
|
||||
|
||||
for (;;) {
|
||||
readhdr(buf, sizeof buf);
|
||||
|
||||
switch(buf[0]){
|
||||
case ERROR:
|
||||
case FATAL:
|
||||
if(!remote)
|
||||
fprint(2, "%s\n", buf+1);
|
||||
exitflag = "bad receive";
|
||||
if(buf[0] == FATAL)
|
||||
exits(exitflag);
|
||||
continue;
|
||||
|
||||
case 'E':
|
||||
sendokresponse();
|
||||
return;
|
||||
|
||||
case 'T':
|
||||
settimes = 1;
|
||||
p = buf + 1;
|
||||
mtime = strtol(p, &p, 10);
|
||||
if(*p++ != ' '){
|
||||
Badtime:
|
||||
scperror(1, "bad time format: %s", buf+1);
|
||||
}
|
||||
strtol(p, &p, 10);
|
||||
if(*p++ != ' ')
|
||||
goto Badtime;
|
||||
atime = strtol(p, &p, 10);
|
||||
if(*p++ != ' ')
|
||||
goto Badtime;
|
||||
strtol(p, &p, 10);
|
||||
if(*p++ != 0)
|
||||
goto Badtime;
|
||||
|
||||
sendokresponse();
|
||||
continue;
|
||||
|
||||
case 'D':
|
||||
case 'C':
|
||||
p = buf + 1;
|
||||
mode = strtol(p, &p, 8);
|
||||
if (*p++ != ' '){
|
||||
Badmode:
|
||||
scperror(1, "bad mode/size format: %s", buf+1);
|
||||
}
|
||||
size = strtoll(p, &p, 10);
|
||||
if(*p++ != ' ')
|
||||
goto Badmode;
|
||||
|
||||
if(isdir){
|
||||
if(dest[0] == '\0')
|
||||
snprint(name, sizeof name, "%s", p);
|
||||
else
|
||||
snprint(name, sizeof name, "%s/%s", dest, p);
|
||||
}else
|
||||
snprint(name, sizeof name, "%s", dest);
|
||||
if(strlen(name) > sizeof name-UTFmax)
|
||||
scperror(1, "file name too long: %s", dest);
|
||||
|
||||
exists = 1;
|
||||
free(d);
|
||||
if((d = dirstat(name)) == nil)
|
||||
exists = 0;
|
||||
|
||||
if(buf[0] == 'D'){
|
||||
vprint("receive directory %s", name);
|
||||
d = receivedir(name, exists, d, settimes, atime, mtime, mode);
|
||||
settimes = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
vprint("receive file %s by %s", name, getuser());
|
||||
fd = create(name, OWRITE, mode);
|
||||
if(fd < 0){
|
||||
scperror(0, "can't create %s: %r", name);
|
||||
continue;
|
||||
}
|
||||
sendokresponse();
|
||||
|
||||
/*
|
||||
* Committed to receive size bytes
|
||||
*/
|
||||
errors = 0;
|
||||
for(i = 0; i < size; i += m){
|
||||
n = sizeof buf;
|
||||
if(n > size - i)
|
||||
n = size - i;
|
||||
m = readn(remotefd0, buf, n);
|
||||
if(m <= 0)
|
||||
scperror(1, "read error on connection: %r");
|
||||
if(errors == 0){
|
||||
n = write(fd, buf, m);
|
||||
if(n != m)
|
||||
errors = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* if file exists, modes could be wrong */
|
||||
if(errors)
|
||||
scperror(0, "%s: write error: %r", name);
|
||||
else if(settimes || (exists && (d->mode&0777) != (mode&0777))){
|
||||
nulldir(&nd);
|
||||
if(settimes){
|
||||
settimes = 0;
|
||||
nd.atime = atime;
|
||||
nd.mtime = mtime;
|
||||
}
|
||||
if(exists && (d->mode&0777) != (mode&0777))
|
||||
nd.mode = (d->mode & ~0777) | (mode&0777);
|
||||
if(dirwstat(name, &nd) < 0)
|
||||
scperror(0, "can't wstat %s: %r", name);
|
||||
}
|
||||
free(d);
|
||||
d = nil;
|
||||
close(fd);
|
||||
getresponse();
|
||||
if(errors)
|
||||
exits("write error");
|
||||
sendokresponse();
|
||||
break;
|
||||
|
||||
default:
|
||||
scperror(0, "unrecognized header type char %c", buf[0]);
|
||||
scperror(1, "input line: %s", buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Lastelem is called when we have a Dir with the final element, but if the file
|
||||
* has been bound, we want the original name that was used rather than
|
||||
* the contents of the stat buffer, so do this lexically.
|
||||
*/
|
||||
char*
|
||||
lastelem(char *file)
|
||||
{
|
||||
char *elem;
|
||||
|
||||
elem = strrchr(file, '/');
|
||||
if(elem == nil)
|
||||
return file;
|
||||
return elem+1;
|
||||
}
|
||||
|
||||
void
|
||||
send(char *file)
|
||||
{
|
||||
Dir *d;
|
||||
ulong i;
|
||||
int m, n, fd;
|
||||
char buf[8192];
|
||||
|
||||
if((fd = open(file, OREAD)) < 0){
|
||||
scperror(0, "can't open %s: %r", file);
|
||||
return;
|
||||
}
|
||||
if((d = dirfstat(fd)) == nil){
|
||||
scperror(0, "can't fstat %s: %r", file);
|
||||
goto Return;
|
||||
}
|
||||
|
||||
if(d->qid.type & QTDIR){
|
||||
if(rflag)
|
||||
senddir(file, fd, d);
|
||||
else
|
||||
scperror(0, "%s: is a directory", file);
|
||||
goto Return;
|
||||
}
|
||||
|
||||
if(pflag){
|
||||
fprint(remotefd1, "T%lud 0 %lud 0\n", d->mtime, d->atime);
|
||||
if(getresponse() < 0)
|
||||
goto Return;
|
||||
}
|
||||
|
||||
fprint(remotefd1, "C%.4luo %lld %s\n", d->mode&0777, d->length, lastelem(file));
|
||||
if(getresponse() < 0)
|
||||
goto Return;
|
||||
|
||||
/*
|
||||
* We are now committed to send d.length bytes, regardless
|
||||
*/
|
||||
for(i=0; i<d->length; i+=m){
|
||||
n = sizeof buf;
|
||||
if(n > d->length - i)
|
||||
n = d->length - i;
|
||||
m = readn(fd, buf, n);
|
||||
if(m <= 0)
|
||||
break;
|
||||
write(remotefd1, buf, m);
|
||||
}
|
||||
|
||||
if(i == d->length)
|
||||
sendokresponse();
|
||||
else{
|
||||
/* continue to send gibberish up to d.length */
|
||||
for(; i<d->length; i+=n){
|
||||
n = sizeof buf;
|
||||
if(n > d->length - i)
|
||||
n = d->length - i;
|
||||
write(remotefd1, buf, n);
|
||||
}
|
||||
scperror(0, "%s: %r", file);
|
||||
}
|
||||
|
||||
getresponse();
|
||||
|
||||
Return:
|
||||
free(d);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int
|
||||
getresponse(void)
|
||||
{
|
||||
uchar first, byte, buf[256];
|
||||
int i;
|
||||
|
||||
if (read(remotefd0, &first, 1) != 1)
|
||||
scperror(1, "lost connection");
|
||||
|
||||
if(first == 0)
|
||||
return 0;
|
||||
|
||||
i = 0;
|
||||
if(first > FATAL){
|
||||
fprint(2, "scp: unexpected response character 0x%.2ux\n", first);
|
||||
buf[i++] = first;
|
||||
}
|
||||
|
||||
/* read error message up to newline */
|
||||
for(;;){
|
||||
if(read(remotefd0, &byte, 1) != 1)
|
||||
scperror(1, "response: dropped connection");
|
||||
if(byte == '\n')
|
||||
break;
|
||||
if(i < sizeof buf)
|
||||
buf[i++] = byte;
|
||||
}
|
||||
|
||||
exitflag = "bad response";
|
||||
if(!remote)
|
||||
fprint(2, "%.*s\n", utfnlen((char*)buf, i), (char*)buf);
|
||||
|
||||
if (first == ERROR)
|
||||
return -1;
|
||||
exits(exitflag);
|
||||
return 0; /* not reached */
|
||||
}
|
||||
|
||||
void
|
||||
senddir(char *name, int fd, Dir *dirp)
|
||||
{
|
||||
Dir *d, *dir;
|
||||
int n;
|
||||
char file[256];
|
||||
|
||||
if(pflag){
|
||||
fprint(remotefd1, "T%lud 0 %lud 0\n", dirp->mtime, dirp->atime);
|
||||
if(getresponse() < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
vprint("directory %s mode: D%.4lo %d %.1024s", name, dirp->mode&0777, 0, lastelem(name));
|
||||
|
||||
fprint(remotefd1, "D%.4lo %d %.1024s\n", dirp->mode&0777, 0, dirp->name);
|
||||
if(getresponse() < 0)
|
||||
return;
|
||||
|
||||
n = dirreadall(fd, &dir);
|
||||
for(d = dir; d < &dir[n]; d++){
|
||||
/* shouldn't happen with plan 9, but worth checking anyway */
|
||||
if(strcmp(d->name, ".")==0 || strcmp(d->name, "..")==0)
|
||||
continue;
|
||||
if(snprint(file, sizeof file, "%s/%s", name, d->name) > sizeof file-UTFmax){
|
||||
scperror(0, "%.20s.../%s: name too long; skipping file", file, d->name);
|
||||
continue;
|
||||
}
|
||||
send(file);
|
||||
}
|
||||
free(dir);
|
||||
fprint(remotefd1, "E\n");
|
||||
getresponse();
|
||||
}
|
||||
|
||||
int
|
||||
remotessh(char *host, char *cmd)
|
||||
{
|
||||
int i, p[2];
|
||||
char *arg[32];
|
||||
|
||||
vprint("remotessh: %s: %s", host, cmd);
|
||||
|
||||
if(pipe(p) < 0)
|
||||
scperror(1, "pipe: %r");
|
||||
|
||||
switch(fork()){
|
||||
case -1:
|
||||
scperror(1, "fork: %r");
|
||||
|
||||
case 0:
|
||||
/* child */
|
||||
close(p[0]);
|
||||
dup(p[1], 0);
|
||||
dup(p[1], 1);
|
||||
for (i = 3; i < 100; i++)
|
||||
close(i);
|
||||
|
||||
i = 0;
|
||||
arg[i++] = ssh;
|
||||
arg[i++] = "-x";
|
||||
arg[i++] = "-a";
|
||||
arg[i++] = "-m";
|
||||
if(iflag)
|
||||
arg[i++] = "-i";
|
||||
if(vflag)
|
||||
arg[i++] = "-v";
|
||||
arg[i++] = host;
|
||||
arg[i++] = cmd;
|
||||
arg[i] = nil;
|
||||
|
||||
exec(ssh, arg);
|
||||
exits("exec failed");
|
||||
|
||||
default:
|
||||
/* parent */
|
||||
close(p[1]);
|
||||
remotefd0 = p[0];
|
||||
remotefd1 = p[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
scperror(int exit, char *fmt, ...)
|
||||
{
|
||||
char buf[2048];
|
||||
va_list arg;
|
||||
|
||||
|
||||
va_start(arg, fmt);
|
||||
vseprint(buf, buf+sizeof(buf), fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
fprint(remotefd1, "%cscp: %s\n", ERROR, buf);
|
||||
|
||||
if (!remote)
|
||||
fprint(2, "scp: %s\n", buf);
|
||||
exitflag = buf;
|
||||
if(exit)
|
||||
exits(exitflag);
|
||||
}
|
||||
|
||||
char *
|
||||
fileaftercolon(char *file)
|
||||
{
|
||||
char *c, *s;
|
||||
|
||||
c = utfrune(file, ':');
|
||||
if(c == nil)
|
||||
return nil;
|
||||
|
||||
/* colon must be in middle of name to be a separator */
|
||||
if(c == file)
|
||||
return nil;
|
||||
|
||||
/* does slash occur before colon? */
|
||||
s = utfrune(file, '/');
|
||||
if(s != nil && s < c)
|
||||
return nil;
|
||||
|
||||
*c++ = '\0';
|
||||
if(*c == '\0')
|
||||
return ".";
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
mustbedir(char *file)
|
||||
{
|
||||
Dir *d;
|
||||
|
||||
if((d = dirstat(file)) == nil){
|
||||
scperror(1, "%s: %r", file);
|
||||
return;
|
||||
}
|
||||
if(!(d->qid.type & QTDIR))
|
||||
scperror(1, "%s: Not a directory", file);
|
||||
free(d);
|
||||
}
|
|
@ -1,285 +0,0 @@
|
|||
#include "ssh.h"
|
||||
#include <bio.h>
|
||||
|
||||
static void
|
||||
send_ssh_smsg_public_key(Conn *c)
|
||||
{
|
||||
int i;
|
||||
Msg *m;
|
||||
|
||||
m = allocmsg(c, SSH_SMSG_PUBLIC_KEY, 2048);
|
||||
putbytes(m, c->cookie, COOKIELEN);
|
||||
putRSApub(m, c->serverkey);
|
||||
putRSApub(m, c->hostkey);
|
||||
putlong(m, c->flags);
|
||||
|
||||
for(i=0; i<c->nokcipher; i++)
|
||||
c->ciphermask |= 1<<c->okcipher[i]->id;
|
||||
putlong(m, c->ciphermask);
|
||||
for(i=0; i<c->nokauthsrv; i++)
|
||||
c->authmask |= 1<<c->okauthsrv[i]->id;
|
||||
putlong(m, c->authmask);
|
||||
|
||||
sendmsg(m);
|
||||
}
|
||||
|
||||
static mpint*
|
||||
rpcdecrypt(AuthRpc *rpc, mpint *b)
|
||||
{
|
||||
mpint *a;
|
||||
char *p;
|
||||
|
||||
p = mptoa(b, 16, nil, 0);
|
||||
if(auth_rpc(rpc, "write", p, strlen(p)) != ARok)
|
||||
sysfatal("factotum rsa write: %r");
|
||||
free(p);
|
||||
if(auth_rpc(rpc, "read", nil, 0) != ARok)
|
||||
sysfatal("factotum rsa read: %r");
|
||||
a = strtomp(rpc->arg, nil, 16, nil);
|
||||
mpfree(b);
|
||||
return a;
|
||||
}
|
||||
|
||||
static void
|
||||
recv_ssh_cmsg_session_key(Conn *c, AuthRpc *rpc)
|
||||
{
|
||||
int i, id, n, serverkeylen, hostkeylen;
|
||||
mpint *a, *b;
|
||||
uchar *buf;
|
||||
Msg *m;
|
||||
RSApriv *ksmall, *kbig;
|
||||
|
||||
m = recvmsg(c, SSH_CMSG_SESSION_KEY);
|
||||
id = getbyte(m);
|
||||
c->cipher = nil;
|
||||
for(i=0; i<c->nokcipher; i++)
|
||||
if(c->okcipher[i]->id == id)
|
||||
c->cipher = c->okcipher[i];
|
||||
if(c->cipher == nil)
|
||||
sysfatal("invalid cipher selected");
|
||||
|
||||
if(memcmp(getbytes(m, COOKIELEN), c->cookie, COOKIELEN) != 0)
|
||||
sysfatal("bad cookie");
|
||||
|
||||
serverkeylen = mpsignif(c->serverkey->n);
|
||||
hostkeylen = mpsignif(c->hostkey->n);
|
||||
ksmall = kbig = nil;
|
||||
if(serverkeylen+128 <= hostkeylen){
|
||||
ksmall = c->serverpriv;
|
||||
kbig = nil;
|
||||
}else if(hostkeylen+128 <= serverkeylen){
|
||||
ksmall = nil;
|
||||
kbig = c->serverpriv;
|
||||
}else
|
||||
sysfatal("server session and host keys do not differ by at least 128 bits");
|
||||
|
||||
b = getmpint(m);
|
||||
|
||||
debug(DBG_CRYPTO, "encrypted with kbig is %B\n", b);
|
||||
if(kbig){
|
||||
a = rsadecrypt(kbig, b, nil);
|
||||
mpfree(b);
|
||||
b = a;
|
||||
}else
|
||||
b = rpcdecrypt(rpc, b);
|
||||
a = rsaunpad(b);
|
||||
mpfree(b);
|
||||
b = a;
|
||||
|
||||
debug(DBG_CRYPTO, "encrypted with ksmall is %B\n", b);
|
||||
if(ksmall){
|
||||
a = rsadecrypt(ksmall, b, nil);
|
||||
mpfree(b);
|
||||
b = a;
|
||||
}else
|
||||
b = rpcdecrypt(rpc, b);
|
||||
a = rsaunpad(b);
|
||||
mpfree(b);
|
||||
b = a;
|
||||
|
||||
debug(DBG_CRYPTO, "munged is %B\n", b);
|
||||
|
||||
n = (mpsignif(b)+7)/8;
|
||||
if(n > SESSKEYLEN)
|
||||
sysfatal("client sent short session key");
|
||||
|
||||
buf = emalloc(SESSKEYLEN);
|
||||
mptoberjust(b, buf, SESSKEYLEN);
|
||||
mpfree(b);
|
||||
|
||||
for(i=0; i<SESSIDLEN; i++)
|
||||
buf[i] ^= c->sessid[i];
|
||||
|
||||
memmove(c->sesskey, buf, SESSKEYLEN);
|
||||
|
||||
debug(DBG_CRYPTO, "unmunged is %.*H\n", SESSKEYLEN, buf);
|
||||
|
||||
c->flags = getlong(m);
|
||||
free(m);
|
||||
}
|
||||
|
||||
static AuthInfo*
|
||||
responselogin(char *user, char *resp)
|
||||
{
|
||||
Chalstate *c;
|
||||
AuthInfo *ai;
|
||||
|
||||
if((c = auth_challenge("proto=p9cr user=%q role=server", user)) == nil){
|
||||
sshlog("auth_challenge failed for %s", user);
|
||||
return nil;
|
||||
}
|
||||
c->resp = resp;
|
||||
c->nresp = strlen(resp);
|
||||
ai = auth_response(c);
|
||||
auth_freechal(c);
|
||||
return ai;
|
||||
}
|
||||
|
||||
static AuthInfo*
|
||||
authusername(Conn *c)
|
||||
{
|
||||
char *p;
|
||||
AuthInfo *ai;
|
||||
|
||||
/*
|
||||
* hack for sam users: 'name numbers' gets tried as securid login.
|
||||
*/
|
||||
if(p = strchr(c->user, ' ')){
|
||||
*p++ = '\0';
|
||||
if((ai=responselogin(c->user, p)) != nil)
|
||||
return ai;
|
||||
*--p = ' ';
|
||||
sshlog("bad response: %s", c->user);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void
|
||||
authsrvuser(Conn *c)
|
||||
{
|
||||
int i;
|
||||
char *ns, *user;
|
||||
AuthInfo *ai;
|
||||
Msg *m;
|
||||
|
||||
m = recvmsg(c, SSH_CMSG_USER);
|
||||
user = getstring(m);
|
||||
c->user = emalloc(strlen(user)+1);
|
||||
strcpy(c->user, user);
|
||||
free(m);
|
||||
|
||||
ai = authusername(c);
|
||||
while(ai == nil){
|
||||
/*
|
||||
* clumsy: if the client aborted the auth_tis early
|
||||
* we don't send a new failure. we check this by
|
||||
* looking at c->unget, which is only used in that
|
||||
* case.
|
||||
*/
|
||||
if(c->unget != nil)
|
||||
goto skipfailure;
|
||||
sendmsg(allocmsg(c, SSH_SMSG_FAILURE, 0));
|
||||
skipfailure:
|
||||
m = recvmsg(c, -1);
|
||||
for(i=0; i<c->nokauthsrv; i++)
|
||||
if(c->okauthsrv[i]->firstmsg == m->type){
|
||||
ai = (*c->okauthsrv[i]->fn)(c, m);
|
||||
break;
|
||||
}
|
||||
if(i==c->nokauthsrv)
|
||||
badmsg(m, 0);
|
||||
}
|
||||
sendmsg(allocmsg(c, SSH_SMSG_SUCCESS, 0));
|
||||
|
||||
if(noworld(ai->cuid))
|
||||
ns = "/lib/namespace.noworld";
|
||||
else
|
||||
ns = nil;
|
||||
if(auth_chuid(ai, ns) < 0){
|
||||
sshlog("auth_chuid to %s: %r", ai->cuid);
|
||||
sysfatal("auth_chuid: %r");
|
||||
}
|
||||
sshlog("logged in as %s", ai->cuid);
|
||||
auth_freeAI(ai);
|
||||
}
|
||||
|
||||
void
|
||||
sshserverhandshake(Conn *c)
|
||||
{
|
||||
char *p, buf[128];
|
||||
Biobuf *b;
|
||||
Attr *a;
|
||||
int i, afd;
|
||||
mpint *m;
|
||||
AuthRpc *rpc;
|
||||
RSApub *key;
|
||||
|
||||
/*
|
||||
* BUG: should use `attr' to get the key attributes
|
||||
* after the read, but that's not implemented yet.
|
||||
*/
|
||||
if((b = Bopen("/mnt/factotum/ctl", OREAD)) == nil)
|
||||
sysfatal("open /mnt/factotum/ctl: %r");
|
||||
while((p = Brdline(b, '\n')) != nil){
|
||||
p[Blinelen(b)-1] = '\0';
|
||||
if(strstr(p, " proto=rsa ") && strstr(p, " service=sshserve "))
|
||||
break;
|
||||
}
|
||||
if(p == nil)
|
||||
sysfatal("no sshserve keys found in /mnt/factotum/ctl");
|
||||
a = _parseattr(p);
|
||||
Bterm(b);
|
||||
key = emalloc(sizeof(*key));
|
||||
if((p = _strfindattr(a, "n")) == nil)
|
||||
sysfatal("no n in sshserve key");
|
||||
if((key->n = strtomp(p, &p, 16, nil)) == nil || *p != 0)
|
||||
sysfatal("bad n in sshserve key");
|
||||
if((p = _strfindattr(a, "ek")) == nil)
|
||||
sysfatal("no ek in sshserve key");
|
||||
if((key->ek = strtomp(p, &p, 16, nil)) == nil || *p != 0)
|
||||
sysfatal("bad ek in sshserve key");
|
||||
_freeattr(a);
|
||||
|
||||
if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0)
|
||||
sysfatal("open /mnt/factotum/rpc: %r");
|
||||
if((rpc = auth_allocrpc(afd)) == nil)
|
||||
sysfatal("auth_allocrpc: %r");
|
||||
p = "proto=rsa role=client service=sshserve";
|
||||
if(auth_rpc(rpc, "start", p, strlen(p)) != ARok)
|
||||
sysfatal("auth_rpc start %s: %r", p);
|
||||
if(auth_rpc(rpc, "read", nil, 0) != ARok)
|
||||
sysfatal("auth_rpc read: %r");
|
||||
m = strtomp(rpc->arg, nil, 16, nil);
|
||||
if(mpcmp(m, key->n) != 0)
|
||||
sysfatal("key in /mnt/factotum/ctl does not match rpc key");
|
||||
mpfree(m);
|
||||
c->hostkey = key;
|
||||
|
||||
/* send id string */
|
||||
fprint(c->fd[0], "SSH-1.5-Plan9\n");
|
||||
|
||||
/* receive id string */
|
||||
if(readstrnl(c->fd[0], buf, sizeof buf) < 0)
|
||||
sysfatal("reading server version: %r");
|
||||
|
||||
/* id string is "SSH-m.n-comment". We need m=1, n>=5. */
|
||||
if(strncmp(buf, "SSH-", 4) != 0
|
||||
|| strtol(buf+4, &p, 10) != 1
|
||||
|| *p != '.'
|
||||
|| strtol(p+1, &p, 10) < 5
|
||||
|| *p != '-')
|
||||
sysfatal("protocol mismatch; got %s, need SSH-1.x for x>=5", buf);
|
||||
|
||||
for(i=0; i<COOKIELEN; i++)
|
||||
c->cookie[i] = fastrand();
|
||||
calcsessid(c);
|
||||
send_ssh_smsg_public_key(c);
|
||||
recv_ssh_cmsg_session_key(c, rpc);
|
||||
auth_freerpc(rpc);
|
||||
close(afd);
|
||||
|
||||
c->cstate = (*c->cipher->init)(c, 1); /* turns on encryption */
|
||||
sendmsg(allocmsg(c, SSH_SMSG_SUCCESS, 0));
|
||||
|
||||
authsrvuser(c);
|
||||
}
|
|
@ -1,592 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
int cooked = 0; /* user wants cooked mode */
|
||||
int raw = 0; /* console is in raw mode */
|
||||
int crstrip;
|
||||
int interactive = -1;
|
||||
int usemenu = 1;
|
||||
int isatty(int);
|
||||
int rawhack;
|
||||
int forwardagent = 0;
|
||||
char *buildcmd(int, char**);
|
||||
void fromnet(Conn*);
|
||||
void fromstdin(Conn*);
|
||||
void winchanges(Conn*);
|
||||
static void sendwritemsg(Conn *c, char *buf, int n);
|
||||
|
||||
Cipher *allcipher[] = {
|
||||
&cipherrc4,
|
||||
&cipherblowfish,
|
||||
&cipher3des,
|
||||
&cipherdes,
|
||||
&ciphernone,
|
||||
&ciphertwiddle,
|
||||
};
|
||||
|
||||
Auth *allauth[] = {
|
||||
&authpassword,
|
||||
&authrsa,
|
||||
&authtis,
|
||||
};
|
||||
|
||||
char *cipherlist = "blowfish rc4 3des";
|
||||
char *authlist = "rsa password tis";
|
||||
|
||||
Cipher*
|
||||
findcipher(char *name, Cipher **list, int nlist)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<nlist; i++)
|
||||
if(strcmp(name, list[i]->name) == 0)
|
||||
return list[i];
|
||||
error("unknown cipher %s", name);
|
||||
return nil;
|
||||
}
|
||||
|
||||
Auth*
|
||||
findauth(char *name, Auth **list, int nlist)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<nlist; i++)
|
||||
if(strcmp(name, list[i]->name) == 0)
|
||||
return list[i];
|
||||
error("unknown auth %s", name);
|
||||
return nil;
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: ssh [-CiImPpRr] [-A authlist] [-c cipherlist] [user@]hostname [cmd [args]]\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int i, dowinchange, fd, usepty;
|
||||
char *host, *cmd, *user, *p;
|
||||
char *f[16];
|
||||
Conn c;
|
||||
Msg *m;
|
||||
|
||||
fmtinstall('B', mpfmt);
|
||||
fmtinstall('H', encodefmt);
|
||||
atexit(atexitkiller);
|
||||
atexitkill(getpid());
|
||||
|
||||
dowinchange = 0;
|
||||
if(getenv("LINES"))
|
||||
dowinchange = 1;
|
||||
usepty = -1;
|
||||
user = nil;
|
||||
ARGBEGIN{
|
||||
case 'B': /* undocumented, debugging */
|
||||
doabort = 1;
|
||||
break;
|
||||
case 'D': /* undocumented, debugging */
|
||||
debuglevel = strtol(EARGF(usage()), nil, 0);
|
||||
break;
|
||||
case 'l': /* deprecated */
|
||||
case 'u':
|
||||
user = EARGF(usage());
|
||||
break;
|
||||
case 'a': /* used by Unix scp implementations; we must ignore them. */
|
||||
case 'x':
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
authlist = EARGF(usage());
|
||||
break;
|
||||
case 'C':
|
||||
cooked = 1;
|
||||
break;
|
||||
case 'c':
|
||||
cipherlist = EARGF(usage());
|
||||
break;
|
||||
case 'f':
|
||||
forwardagent = 1;
|
||||
break;
|
||||
case 'I':
|
||||
interactive = 0;
|
||||
break;
|
||||
case 'i':
|
||||
interactive = 1;
|
||||
break;
|
||||
case 'm':
|
||||
usemenu = 0;
|
||||
break;
|
||||
case 'P':
|
||||
usepty = 0;
|
||||
break;
|
||||
case 'p':
|
||||
usepty = 1;
|
||||
break;
|
||||
case 'R':
|
||||
rawhack = 1;
|
||||
break;
|
||||
case 'r':
|
||||
crstrip = 1;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}ARGEND
|
||||
|
||||
if(argc < 1)
|
||||
usage();
|
||||
|
||||
host = argv[0];
|
||||
|
||||
cmd = nil;
|
||||
if(argc > 1)
|
||||
cmd = buildcmd(argc-1, argv+1);
|
||||
|
||||
if((p = strchr(host, '@')) != nil){
|
||||
*p++ = '\0';
|
||||
user = host;
|
||||
host = p;
|
||||
}
|
||||
if(user == nil)
|
||||
user = getenv("user");
|
||||
if(user == nil)
|
||||
sysfatal("cannot find user name");
|
||||
|
||||
privatefactotum();
|
||||
if(interactive==-1)
|
||||
interactive = isatty(0);
|
||||
|
||||
if((fd = dial(netmkaddr(host, "tcp", "ssh"), nil, nil, nil)) < 0)
|
||||
sysfatal("dialing %s: %r", host);
|
||||
|
||||
memset(&c, 0, sizeof c);
|
||||
c.interactive = interactive;
|
||||
c.fd[0] = c.fd[1] = fd;
|
||||
c.user = user;
|
||||
c.host = host;
|
||||
setaliases(&c, host);
|
||||
|
||||
c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", ");
|
||||
c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher);
|
||||
for(i=0; i<c.nokcipher; i++)
|
||||
c.okcipher[i] = findcipher(f[i], allcipher, nelem(allcipher));
|
||||
|
||||
c.nokauth = getfields(authlist, f, nelem(f), 1, ", ");
|
||||
c.okauth = emalloc(sizeof(Auth*)*c.nokauth);
|
||||
for(i=0; i<c.nokauth; i++)
|
||||
c.okauth[i] = findauth(f[i], allauth, nelem(allauth));
|
||||
|
||||
sshclienthandshake(&c);
|
||||
|
||||
if(forwardagent){
|
||||
if(startagent(&c) < 0)
|
||||
forwardagent = 0;
|
||||
}
|
||||
if(usepty == -1)
|
||||
usepty = cmd==nil;
|
||||
if(usepty)
|
||||
requestpty(&c);
|
||||
if(cmd){
|
||||
m = allocmsg(&c, SSH_CMSG_EXEC_CMD, 4+strlen(cmd));
|
||||
putstring(m, cmd);
|
||||
}else
|
||||
m = allocmsg(&c, SSH_CMSG_EXEC_SHELL, 0);
|
||||
sendmsg(m);
|
||||
|
||||
fromstdin(&c);
|
||||
rfork(RFNOTEG); /* only fromstdin gets notes */
|
||||
if(dowinchange)
|
||||
winchanges(&c);
|
||||
fromnet(&c);
|
||||
exits(0);
|
||||
}
|
||||
|
||||
int
|
||||
isatty(int fd)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
buf[0] = '\0';
|
||||
fd2path(fd, buf, sizeof buf);
|
||||
if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char*
|
||||
buildcmd(int argc, char **argv)
|
||||
{
|
||||
int i, len;
|
||||
char *s, *t;
|
||||
|
||||
len = argc-1;
|
||||
for(i=0; i<argc; i++)
|
||||
len += strlen(argv[i]);
|
||||
s = emalloc(len+1);
|
||||
t = s;
|
||||
for(i=0; i<argc; i++){
|
||||
if(i)
|
||||
*t++ = ' ';
|
||||
strcpy(t, argv[i]);
|
||||
t += strlen(t);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
fromnet(Conn *c)
|
||||
{
|
||||
int fd, len;
|
||||
char *s, *es, *r, *w;
|
||||
ulong ex;
|
||||
char buf[64];
|
||||
Msg *m;
|
||||
|
||||
for(;;){
|
||||
m = recvmsg(c, -1);
|
||||
if(m == nil)
|
||||
break;
|
||||
switch(m->type){
|
||||
default:
|
||||
badmsg(m, 0);
|
||||
|
||||
case SSH_SMSG_EXITSTATUS:
|
||||
ex = getlong(m);
|
||||
if(ex==0)
|
||||
exits(0);
|
||||
sprint(buf, "%lud", ex);
|
||||
exits(buf);
|
||||
|
||||
case SSH_MSG_DISCONNECT:
|
||||
s = getstring(m);
|
||||
error("disconnect: %s", s);
|
||||
|
||||
/*
|
||||
* If we ever add reverse port forwarding, we'll have to
|
||||
* revisit this. It assumes that the agent connections are
|
||||
* the only ones.
|
||||
*/
|
||||
case SSH_SMSG_AGENT_OPEN:
|
||||
if(!forwardagent)
|
||||
error("server tried to use agent forwarding");
|
||||
handleagentopen(m);
|
||||
break;
|
||||
case SSH_MSG_CHANNEL_INPUT_EOF:
|
||||
if(!forwardagent)
|
||||
error("server tried to use agent forwarding");
|
||||
handleagentieof(m);
|
||||
break;
|
||||
case SSH_MSG_CHANNEL_OUTPUT_CLOSED:
|
||||
if(!forwardagent)
|
||||
error("server tried to use agent forwarding");
|
||||
handleagentoclose(m);
|
||||
break;
|
||||
case SSH_MSG_CHANNEL_DATA:
|
||||
if(!forwardagent)
|
||||
error("server tried to use agent forwarding");
|
||||
handleagentmsg(m);
|
||||
break;
|
||||
|
||||
case SSH_SMSG_STDOUT_DATA:
|
||||
fd = 1;
|
||||
goto Dataout;
|
||||
case SSH_SMSG_STDERR_DATA:
|
||||
fd = 2;
|
||||
goto Dataout;
|
||||
Dataout:
|
||||
len = getlong(m);
|
||||
s = (char*)getbytes(m, len);
|
||||
if(crstrip){
|
||||
es = s+len;
|
||||
for(r=w=s; r<es; r++)
|
||||
if(*r != '\r')
|
||||
*w++ = *r;
|
||||
len = w-s;
|
||||
}
|
||||
write(fd, s, len);
|
||||
break;
|
||||
}
|
||||
free(m);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Lifted from telnet.c, con.c
|
||||
*/
|
||||
|
||||
static int consctl = -1;
|
||||
static int outfd1=1, outfd2=2; /* changed during system */
|
||||
static void system(Conn*, char*);
|
||||
|
||||
/*
|
||||
* turn keyboard raw mode on
|
||||
*/
|
||||
static void
|
||||
rawon(void)
|
||||
{
|
||||
if(raw)
|
||||
return;
|
||||
if(cooked)
|
||||
return;
|
||||
if(consctl < 0)
|
||||
consctl = open("/dev/consctl", OWRITE);
|
||||
if(consctl < 0)
|
||||
return;
|
||||
if(write(consctl, "rawon", 5) != 5)
|
||||
return;
|
||||
raw = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* turn keyboard raw mode off
|
||||
*/
|
||||
static void
|
||||
rawoff(void)
|
||||
{
|
||||
if(raw == 0)
|
||||
return;
|
||||
if(consctl < 0)
|
||||
return;
|
||||
if(write(consctl, "rawoff", 6) != 6)
|
||||
return;
|
||||
close(consctl);
|
||||
consctl = -1;
|
||||
raw = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* control menu
|
||||
*/
|
||||
#define STDHELP "\t(q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n"
|
||||
|
||||
static int
|
||||
menu(Conn *c)
|
||||
{
|
||||
char buf[1024];
|
||||
long n;
|
||||
int done;
|
||||
int wasraw;
|
||||
|
||||
wasraw = raw;
|
||||
if(wasraw)
|
||||
rawoff();
|
||||
|
||||
buf[0] = '?';
|
||||
fprint(2, ">>> ");
|
||||
for(done = 0; !done; ){
|
||||
n = read(0, buf, sizeof(buf)-1);
|
||||
if(n <= 0)
|
||||
return -1;
|
||||
buf[n] = 0;
|
||||
switch(buf[0]){
|
||||
case '!':
|
||||
print(buf);
|
||||
system(c, buf+1);
|
||||
print("!\n");
|
||||
done = 1;
|
||||
break;
|
||||
case 'i':
|
||||
buf[0] = 0x1c;
|
||||
sendwritemsg(c, buf, 1);
|
||||
done = 1;
|
||||
break;
|
||||
case '.':
|
||||
case 'q':
|
||||
done = 1;
|
||||
break;
|
||||
case 'r':
|
||||
crstrip = 1-crstrip;
|
||||
done = 1;
|
||||
break;
|
||||
default:
|
||||
fprint(2, STDHELP);
|
||||
break;
|
||||
}
|
||||
if(!done)
|
||||
fprint(2, ">>> ");
|
||||
}
|
||||
|
||||
if(wasraw)
|
||||
rawon();
|
||||
else
|
||||
rawoff();
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
static void
|
||||
sendwritemsg(Conn *c, char *buf, int n)
|
||||
{
|
||||
Msg *m;
|
||||
|
||||
if(n==0)
|
||||
m = allocmsg(c, SSH_CMSG_EOF, 0);
|
||||
else{
|
||||
m = allocmsg(c, SSH_CMSG_STDIN_DATA, 4+n);
|
||||
putlong(m, n);
|
||||
putbytes(m, buf, n);
|
||||
}
|
||||
sendmsg(m);
|
||||
}
|
||||
|
||||
/*
|
||||
* run a command with the network connection as standard IO
|
||||
*/
|
||||
static void
|
||||
system(Conn *c, char *cmd)
|
||||
{
|
||||
int pid;
|
||||
int p;
|
||||
int pfd[2];
|
||||
int n;
|
||||
int wasconsctl;
|
||||
char buf[4096];
|
||||
|
||||
if(pipe(pfd) < 0){
|
||||
perror("pipe");
|
||||
return;
|
||||
}
|
||||
outfd1 = outfd2 = pfd[1];
|
||||
|
||||
wasconsctl = consctl;
|
||||
close(consctl);
|
||||
consctl = -1;
|
||||
switch(pid = fork()){
|
||||
case -1:
|
||||
perror("con");
|
||||
return;
|
||||
case 0:
|
||||
close(pfd[1]);
|
||||
dup(pfd[0], 0);
|
||||
dup(pfd[0], 1);
|
||||
close(c->fd[0]); /* same as c->fd[1] */
|
||||
close(pfd[0]);
|
||||
if(*cmd)
|
||||
execl("/bin/rc", "rc", "-c", cmd, nil);
|
||||
else
|
||||
execl("/bin/rc", "rc", nil);
|
||||
perror("con");
|
||||
exits("exec");
|
||||
break;
|
||||
default:
|
||||
close(pfd[0]);
|
||||
while((n = read(pfd[1], buf, sizeof(buf))) > 0)
|
||||
sendwritemsg(c, buf, n);
|
||||
p = waitpid();
|
||||
outfd1 = 1;
|
||||
outfd2 = 2;
|
||||
close(pfd[1]);
|
||||
if(p < 0 || p != pid)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
if(wasconsctl >= 0){
|
||||
consctl = open("/dev/consctl", OWRITE);
|
||||
if(consctl < 0)
|
||||
error("cannot open consctl");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cookedcatchint(void*, char *msg)
|
||||
{
|
||||
if(strstr(msg, "interrupt"))
|
||||
noted(NCONT);
|
||||
else if(strstr(msg, "kill"))
|
||||
noted(NDFLT);
|
||||
else
|
||||
noted(NCONT);
|
||||
}
|
||||
|
||||
static int
|
||||
wasintr(void)
|
||||
{
|
||||
char err[64];
|
||||
|
||||
rerrstr(err, sizeof err);
|
||||
return strstr(err, "interrupt") != 0;
|
||||
}
|
||||
|
||||
void
|
||||
fromstdin(Conn *c)
|
||||
{
|
||||
int n;
|
||||
char buf[1024];
|
||||
int pid;
|
||||
int eofs;
|
||||
|
||||
switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){
|
||||
case -1:
|
||||
error("fork: %r");
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
atexitkill(pid);
|
||||
return;
|
||||
}
|
||||
|
||||
atexit(atexitkiller);
|
||||
if(interactive)
|
||||
rawon();
|
||||
|
||||
notify(cookedcatchint);
|
||||
|
||||
eofs = 0;
|
||||
for(;;){
|
||||
n = read(0, buf, sizeof(buf));
|
||||
if(n < 0){
|
||||
if(wasintr()){
|
||||
if(!raw){
|
||||
buf[0] = 0x7f;
|
||||
n = 1;
|
||||
}else
|
||||
continue;
|
||||
}else
|
||||
break;
|
||||
}
|
||||
if(n == 0){
|
||||
if(!c->interactive || ++eofs > 32)
|
||||
break;
|
||||
}else
|
||||
eofs = 0;
|
||||
if(interactive && usemenu && n && memchr(buf, 0x1c, n)) {
|
||||
if(menu(c)=='q'){
|
||||
sendwritemsg(c, "", 0);
|
||||
exits("quit");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(!raw && n==0){
|
||||
buf[0] = 0x4;
|
||||
n = 1;
|
||||
}
|
||||
sendwritemsg(c, buf, n);
|
||||
}
|
||||
sendwritemsg(c, "", 0);
|
||||
atexitdont(atexitkiller);
|
||||
exits(nil);
|
||||
}
|
||||
|
||||
void
|
||||
winchanges(Conn *c)
|
||||
{
|
||||
int nrow, ncol, width, height;
|
||||
int pid;
|
||||
|
||||
switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){
|
||||
case -1:
|
||||
error("fork: %r");
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
atexitkill(pid);
|
||||
return;
|
||||
}
|
||||
|
||||
for(;;){
|
||||
if(readgeom(&nrow, &ncol, &width, &height) < 0)
|
||||
break;
|
||||
sendwindowsize(c, nrow, ncol, width, height);
|
||||
}
|
||||
exits(nil);
|
||||
}
|
|
@ -1,303 +0,0 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <mp.h>
|
||||
#include <auth.h>
|
||||
#include <libsec.h>
|
||||
|
||||
enum /* internal debugging flags */
|
||||
{
|
||||
DBG= 1<<0,
|
||||
DBG_CRYPTO= 1<<1,
|
||||
DBG_PACKET= 1<<2,
|
||||
DBG_AUTH= 1<<3,
|
||||
DBG_PROC= 1<<4,
|
||||
DBG_PROTO= 1<<5,
|
||||
DBG_IO= 1<<6,
|
||||
DBG_SCP= 1<<7,
|
||||
};
|
||||
|
||||
enum /* protocol packet types */
|
||||
{
|
||||
/* 0 */
|
||||
SSH_MSG_NONE=0,
|
||||
SSH_MSG_DISCONNECT,
|
||||
SSH_SMSG_PUBLIC_KEY,
|
||||
SSH_CMSG_SESSION_KEY,
|
||||
SSH_CMSG_USER,
|
||||
SSH_CMSG_AUTH_RHOSTS,
|
||||
SSH_CMSG_AUTH_RSA,
|
||||
SSH_SMSG_AUTH_RSA_CHALLENGE,
|
||||
SSH_CMSG_AUTH_RSA_RESPONSE,
|
||||
SSH_CMSG_AUTH_PASSWORD,
|
||||
|
||||
/* 10 */
|
||||
SSH_CMSG_REQUEST_PTY,
|
||||
SSH_CMSG_WINDOW_SIZE,
|
||||
SSH_CMSG_EXEC_SHELL,
|
||||
SSH_CMSG_EXEC_CMD,
|
||||
SSH_SMSG_SUCCESS,
|
||||
SSH_SMSG_FAILURE,
|
||||
SSH_CMSG_STDIN_DATA,
|
||||
SSH_SMSG_STDOUT_DATA,
|
||||
SSH_SMSG_STDERR_DATA,
|
||||
SSH_CMSG_EOF,
|
||||
|
||||
/* 20 */
|
||||
SSH_SMSG_EXITSTATUS,
|
||||
SSH_MSG_CHANNEL_OPEN_CONFIRMATION,
|
||||
SSH_MSG_CHANNEL_OPEN_FAILURE,
|
||||
SSH_MSG_CHANNEL_DATA,
|
||||
SSH_MSG_CHANNEL_INPUT_EOF,
|
||||
SSH_MSG_CHANNEL_OUTPUT_CLOSED,
|
||||
SSH_MSG_UNIX_DOMAIN_X11_FORWARDING, /* obsolete */
|
||||
SSH_SMSG_X11_OPEN,
|
||||
SSH_CMSG_PORT_FORWARD_REQUEST,
|
||||
SSH_MSG_PORT_OPEN,
|
||||
|
||||
/* 30 */
|
||||
SSH_CMSG_AGENT_REQUEST_FORWARDING,
|
||||
SSH_SMSG_AGENT_OPEN,
|
||||
SSH_MSG_IGNORE,
|
||||
SSH_CMSG_EXIT_CONFIRMATION,
|
||||
SSH_CMSG_X11_REQUEST_FORWARDING,
|
||||
SSH_CMSG_AUTH_RHOSTS_RSA,
|
||||
SSH_MSG_DEBUG,
|
||||
SSH_CMSG_REQUEST_COMPRESSION,
|
||||
SSH_CMSG_MAX_PACKET_SIZE,
|
||||
SSH_CMSG_AUTH_TIS,
|
||||
|
||||
/* 40 */
|
||||
SSH_SMSG_AUTH_TIS_CHALLENGE,
|
||||
SSH_CMSG_AUTH_TIS_RESPONSE,
|
||||
SSH_CMSG_AUTH_KERBEROS,
|
||||
SSH_SMSG_AUTH_KERBEROS_RESPONSE,
|
||||
SSH_CMSG_HAVE_KERBEROS_TGT,
|
||||
};
|
||||
|
||||
enum /* protocol flags */
|
||||
{
|
||||
SSH_PROTOFLAG_SCREEN_NUMBER=1<<0,
|
||||
SSH_PROTOFLAG_HOST_IN_FWD_OPEN=1<<1,
|
||||
};
|
||||
|
||||
enum /* agent protocol packet types */
|
||||
{
|
||||
SSH_AGENTC_NONE = 0,
|
||||
SSH_AGENTC_REQUEST_RSA_IDENTITIES,
|
||||
SSH_AGENT_RSA_IDENTITIES_ANSWER,
|
||||
SSH_AGENTC_RSA_CHALLENGE,
|
||||
SSH_AGENT_RSA_RESPONSE,
|
||||
SSH_AGENT_FAILURE,
|
||||
SSH_AGENT_SUCCESS,
|
||||
SSH_AGENTC_ADD_RSA_IDENTITY,
|
||||
SSH_AGENTC_REMOVE_RSA_IDENTITY,
|
||||
};
|
||||
|
||||
enum /* protocol constants */
|
||||
{
|
||||
SSH_MAX_DATA = 256*1024,
|
||||
SSH_MAX_MSG = SSH_MAX_DATA+4,
|
||||
|
||||
SESSKEYLEN = 32,
|
||||
SESSIDLEN = 16,
|
||||
|
||||
COOKIELEN = 8,
|
||||
};
|
||||
|
||||
enum /* crypto ids */
|
||||
{
|
||||
SSH_CIPHER_NONE = 0,
|
||||
SSH_CIPHER_IDEA,
|
||||
SSH_CIPHER_DES,
|
||||
SSH_CIPHER_3DES,
|
||||
SSH_CIPHER_TSS,
|
||||
SSH_CIPHER_RC4,
|
||||
SSH_CIPHER_BLOWFISH,
|
||||
SSH_CIPHER_TWIDDLE, /* for debugging */
|
||||
};
|
||||
|
||||
enum /* auth method ids */
|
||||
{
|
||||
SSH_AUTH_RHOSTS = 1,
|
||||
SSH_AUTH_RSA = 2,
|
||||
SSH_AUTH_PASSWORD = 3,
|
||||
SSH_AUTH_RHOSTS_RSA = 4,
|
||||
SSH_AUTH_TIS = 5,
|
||||
SSH_AUTH_USER_RSA = 6,
|
||||
};
|
||||
|
||||
typedef struct Auth Auth;
|
||||
typedef struct Authsrv Authsrv;
|
||||
typedef struct Cipher Cipher;
|
||||
typedef struct CipherState CipherState;
|
||||
typedef struct Conn Conn;
|
||||
typedef struct Msg Msg;
|
||||
|
||||
#pragma incomplete CipherState
|
||||
|
||||
struct Auth
|
||||
{
|
||||
int id;
|
||||
char *name;
|
||||
int (*fn)(Conn*);
|
||||
};
|
||||
|
||||
struct Authsrv
|
||||
{
|
||||
int id;
|
||||
char *name;
|
||||
int firstmsg;
|
||||
AuthInfo *(*fn)(Conn*, Msg*);
|
||||
};
|
||||
|
||||
struct Cipher
|
||||
{
|
||||
int id;
|
||||
char *name;
|
||||
CipherState *(*init)(Conn*, int isserver);
|
||||
void (*encrypt)(CipherState*, uchar*, int);
|
||||
void (*decrypt)(CipherState*, uchar*, int);
|
||||
};
|
||||
|
||||
struct Conn
|
||||
{
|
||||
QLock;
|
||||
int fd[2];
|
||||
CipherState *cstate;
|
||||
uchar cookie[COOKIELEN];
|
||||
uchar sessid[SESSIDLEN];
|
||||
uchar sesskey[SESSKEYLEN];
|
||||
RSApub *serverkey;
|
||||
RSApub *hostkey;
|
||||
ulong flags;
|
||||
ulong ciphermask;
|
||||
Cipher *cipher; /* chosen cipher */
|
||||
Cipher **okcipher; /* list of acceptable ciphers */
|
||||
int nokcipher;
|
||||
ulong authmask;
|
||||
Auth **okauth;
|
||||
int nokauth;
|
||||
char *user;
|
||||
char *host;
|
||||
char *aliases;
|
||||
int interactive;
|
||||
Msg *unget;
|
||||
|
||||
RSApriv *serverpriv; /* server only */
|
||||
RSApriv *hostpriv;
|
||||
Authsrv **okauthsrv;
|
||||
int nokauthsrv;
|
||||
};
|
||||
|
||||
struct Msg
|
||||
{
|
||||
Conn *c;
|
||||
uchar type;
|
||||
ulong len; /* output: #bytes before pos, input: #bytes after pos */
|
||||
uchar *bp; /* beginning of allocated space */
|
||||
uchar *rp; /* read pointer */
|
||||
uchar *wp; /* write pointer */
|
||||
uchar *ep; /* end of allocated space */
|
||||
Msg *link; /* for sshnet */
|
||||
};
|
||||
|
||||
#define LONG(p) (((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|((p)[3]))
|
||||
#define PLONG(p, l) \
|
||||
(((p)[0]=(l)>>24),((p)[1]=(l)>>16),\
|
||||
((p)[2]=(l)>>8),((p)[3]=(l)))
|
||||
#define SHORT(p) (((p)[0]<<8)|(p)[1])
|
||||
#define PSHORT(p,l) \
|
||||
(((p)[0]=(l)>>8),((p)[1]=(l)))
|
||||
|
||||
extern char Edecode[];
|
||||
extern char Eencode[];
|
||||
extern char Ememory[];
|
||||
extern char Ehangup[];
|
||||
extern int doabort;
|
||||
extern int debuglevel;
|
||||
|
||||
extern Auth authpassword;
|
||||
extern Auth authrsa;
|
||||
extern Auth authtis;
|
||||
|
||||
extern Authsrv authsrvpassword;
|
||||
extern Authsrv authsrvtis;
|
||||
|
||||
extern Cipher cipher3des;
|
||||
extern Cipher cipherblowfish;
|
||||
extern Cipher cipherdes;
|
||||
extern Cipher cipherrc4;
|
||||
extern Cipher ciphernone;
|
||||
extern Cipher ciphertwiddle;
|
||||
|
||||
/* msg.c */
|
||||
Msg* allocmsg(Conn*, int, int);
|
||||
void badmsg(Msg*, int);
|
||||
Msg* recvmsg(Conn*, int);
|
||||
void unrecvmsg(Conn*, Msg*);
|
||||
int sendmsg(Msg*);
|
||||
uchar getbyte(Msg*);
|
||||
ushort getshort(Msg*);
|
||||
ulong getlong(Msg*);
|
||||
char* getstring(Msg*);
|
||||
void* getbytes(Msg*, int);
|
||||
mpint* getmpint(Msg*);
|
||||
RSApub* getRSApub(Msg*);
|
||||
void putbyte(Msg*, uchar);
|
||||
void putshort(Msg*, ushort);
|
||||
void putlong(Msg*, ulong);
|
||||
void putstring(Msg*, char*);
|
||||
void putbytes(Msg*, void*, long);
|
||||
void putmpint(Msg*, mpint*);
|
||||
void putRSApub(Msg*, RSApub*);
|
||||
mpint* rsapad(mpint*, int);
|
||||
mpint* rsaunpad(mpint*);
|
||||
void mptoberjust(mpint*, uchar*, int);
|
||||
mpint* rsaencryptbuf(RSApub*, uchar*, int);
|
||||
|
||||
/* cmsg.c */
|
||||
void sshclienthandshake(Conn*);
|
||||
void requestpty(Conn*);
|
||||
int readgeom(int*, int*, int*, int*);
|
||||
void sendwindowsize(Conn*, int, int, int, int);
|
||||
int rawhack;
|
||||
|
||||
/* smsg.c */
|
||||
void sshserverhandshake(Conn*);
|
||||
|
||||
/* pubkey.c */
|
||||
enum
|
||||
{
|
||||
KeyOk,
|
||||
KeyWrong,
|
||||
NoKey,
|
||||
NoKeyFile,
|
||||
};
|
||||
int appendkey(char*, char*, RSApub*);
|
||||
int findkey(char*, char*, RSApub*);
|
||||
int replacekey(char*, char*, RSApub*);
|
||||
|
||||
/* agent.c */
|
||||
int startagent(Conn*);
|
||||
void handleagentmsg(Msg*);
|
||||
void handleagentopen(Msg*);
|
||||
void handleagentieof(Msg*);
|
||||
void handleagentoclose(Msg*);
|
||||
|
||||
/* util.c */
|
||||
void debug(int, char*, ...);
|
||||
void* emalloc(long);
|
||||
void* erealloc(void*, long);
|
||||
void error(char*, ...);
|
||||
RSApriv* readsecretkey(char*);
|
||||
int readstrnl(int, char*, int);
|
||||
void atexitkill(int);
|
||||
void atexitkiller(void);
|
||||
void calcsessid(Conn*);
|
||||
void sshlog(char*, ...);
|
||||
void setaliases(Conn*, char*);
|
||||
void privatefactotum(void);
|
||||
|
||||
#pragma varargck argpos debug 2
|
||||
#pragma varargck argpos error 1
|
||||
#pragma varargck argpos sshlog 2
|
File diff suppressed because it is too large
Load diff
|
@ -1,315 +0,0 @@
|
|||
#include "ssh.h"
|
||||
|
||||
char *cipherlist = "blowfish rc4 3des";
|
||||
char *authlist = "tis";
|
||||
|
||||
void fromnet(Conn*);
|
||||
void startcmd(Conn*, char*, int*, int*);
|
||||
int maxmsg = 256*1024;
|
||||
|
||||
Cipher *allcipher[] = {
|
||||
&cipherrc4,
|
||||
&cipherblowfish,
|
||||
&cipher3des,
|
||||
&cipherdes,
|
||||
&ciphernone,
|
||||
&ciphertwiddle,
|
||||
};
|
||||
|
||||
Authsrv *allauthsrv[] = {
|
||||
&authsrvpassword,
|
||||
&authsrvtis,
|
||||
};
|
||||
|
||||
Cipher*
|
||||
findcipher(char *name, Cipher **list, int nlist)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<nlist; i++)
|
||||
if(strcmp(name, list[i]->name) == 0)
|
||||
return list[i];
|
||||
error("unknown cipher %s", name);
|
||||
return nil;
|
||||
}
|
||||
|
||||
Authsrv*
|
||||
findauthsrv(char *name, Authsrv **list, int nlist)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<nlist; i++)
|
||||
if(strcmp(name, list[i]->name) == 0)
|
||||
return list[i];
|
||||
error("unknown authsrv %s", name);
|
||||
return nil;
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: sshserve [-A authlist] [-c cipherlist] client-ip-address\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *f[16];
|
||||
int i;
|
||||
Conn c;
|
||||
|
||||
fmtinstall('B', mpfmt);
|
||||
fmtinstall('H', encodefmt);
|
||||
atexit(atexitkiller);
|
||||
atexitkill(getpid());
|
||||
|
||||
memset(&c, 0, sizeof c);
|
||||
|
||||
ARGBEGIN{
|
||||
case 'D':
|
||||
debuglevel = atoi(EARGF(usage()));
|
||||
break;
|
||||
case 'A':
|
||||
authlist = EARGF(usage());
|
||||
break;
|
||||
case 'c':
|
||||
cipherlist = EARGF(usage());
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}ARGEND
|
||||
|
||||
if(argc != 1)
|
||||
usage();
|
||||
c.host = argv[0];
|
||||
|
||||
sshlog("connect from %s", c.host);
|
||||
|
||||
/* limit of 768 bits in remote host key? */
|
||||
c.serverpriv = rsagen(768, 6, 0);
|
||||
if(c.serverpriv == nil)
|
||||
sysfatal("rsagen failed: %r");
|
||||
c.serverkey = &c.serverpriv->pub;
|
||||
|
||||
c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", ");
|
||||
c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher);
|
||||
for(i=0; i<c.nokcipher; i++)
|
||||
c.okcipher[i] = findcipher(f[i], allcipher, nelem(allcipher));
|
||||
|
||||
c.nokauthsrv = getfields(authlist, f, nelem(f), 1, ", ");
|
||||
c.okauthsrv = emalloc(sizeof(Authsrv*)*c.nokauthsrv);
|
||||
for(i=0; i<c.nokauthsrv; i++)
|
||||
c.okauthsrv[i] = findauthsrv(f[i], allauthsrv, nelem(allauthsrv));
|
||||
|
||||
sshserverhandshake(&c);
|
||||
|
||||
fromnet(&c);
|
||||
}
|
||||
|
||||
void
|
||||
fromnet(Conn *c)
|
||||
{
|
||||
int infd, kidpid, n;
|
||||
char *cmd;
|
||||
Msg *m;
|
||||
|
||||
infd = kidpid = -1;
|
||||
for(;;){
|
||||
m = recvmsg(c, -1);
|
||||
if(m == nil)
|
||||
exits(nil);
|
||||
switch(m->type){
|
||||
default:
|
||||
//badmsg(m, 0);
|
||||
sendmsg(allocmsg(c, SSH_SMSG_FAILURE, 0));
|
||||
break;
|
||||
|
||||
case SSH_MSG_DISCONNECT:
|
||||
sysfatal("client disconnected");
|
||||
|
||||
case SSH_CMSG_REQUEST_PTY:
|
||||
sendmsg(allocmsg(c, SSH_SMSG_SUCCESS, 0));
|
||||
break;
|
||||
|
||||
case SSH_CMSG_X11_REQUEST_FORWARDING:
|
||||
sendmsg(allocmsg(c, SSH_SMSG_FAILURE, 0));
|
||||
break;
|
||||
|
||||
case SSH_CMSG_MAX_PACKET_SIZE:
|
||||
maxmsg = getlong(m);
|
||||
sendmsg(allocmsg(c, SSH_SMSG_SUCCESS, 0));
|
||||
break;
|
||||
|
||||
case SSH_CMSG_REQUEST_COMPRESSION:
|
||||
sendmsg(allocmsg(c, SSH_SMSG_FAILURE, 0));
|
||||
break;
|
||||
|
||||
case SSH_CMSG_EXEC_SHELL:
|
||||
startcmd(c, nil, &kidpid, &infd);
|
||||
goto InteractiveMode;
|
||||
|
||||
case SSH_CMSG_EXEC_CMD:
|
||||
cmd = getstring(m);
|
||||
startcmd(c, cmd, &kidpid, &infd);
|
||||
goto InteractiveMode;
|
||||
}
|
||||
free(m);
|
||||
}
|
||||
|
||||
InteractiveMode:
|
||||
for(;;){
|
||||
free(m);
|
||||
m = recvmsg(c, -1);
|
||||
if(m == nil)
|
||||
exits(nil);
|
||||
switch(m->type){
|
||||
default:
|
||||
badmsg(m, 0);
|
||||
|
||||
case SSH_MSG_DISCONNECT:
|
||||
postnote(PNGROUP, kidpid, "hangup");
|
||||
sysfatal("client disconnected");
|
||||
|
||||
case SSH_CMSG_STDIN_DATA:
|
||||
if(infd != 0){
|
||||
n = getlong(m);
|
||||
write(infd, getbytes(m, n), n);
|
||||
}
|
||||
break;
|
||||
|
||||
case SSH_CMSG_EOF:
|
||||
close(infd);
|
||||
infd = -1;
|
||||
break;
|
||||
|
||||
case SSH_CMSG_EXIT_CONFIRMATION:
|
||||
/* sent by some clients as dying breath */
|
||||
exits(nil);
|
||||
|
||||
case SSH_CMSG_WINDOW_SIZE:
|
||||
/* we don't care */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
copyout(Conn *c, int fd, int mtype)
|
||||
{
|
||||
char buf[8192];
|
||||
int n, max, pid;
|
||||
Msg *m;
|
||||
|
||||
max = sizeof buf;
|
||||
if(max > maxmsg - 32) /* 32 is an overestimate of packet overhead */
|
||||
max = maxmsg - 32;
|
||||
if(max <= 0)
|
||||
sysfatal("maximum message size too small");
|
||||
|
||||
switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
|
||||
case -1:
|
||||
sysfatal("fork: %r");
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
atexitkill(pid);
|
||||
return;
|
||||
}
|
||||
|
||||
while((n = read(fd, buf, max)) > 0){
|
||||
m = allocmsg(c, mtype, 4+n);
|
||||
putlong(m, n);
|
||||
putbytes(m, buf, n);
|
||||
sendmsg(m);
|
||||
}
|
||||
exits(nil);
|
||||
}
|
||||
|
||||
void
|
||||
startcmd(Conn *c, char *cmd, int *kidpid, int *kidin)
|
||||
{
|
||||
int i, pid, kpid;
|
||||
int pfd[3][2];
|
||||
char *dir;
|
||||
char *sysname, *tz;
|
||||
Msg *m;
|
||||
Waitmsg *w;
|
||||
|
||||
for(i=0; i<3; i++)
|
||||
if(pipe(pfd[i]) < 0)
|
||||
sysfatal("pipe: %r");
|
||||
|
||||
sysname = getenv("sysname");
|
||||
tz = getenv("timezone");
|
||||
|
||||
switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
|
||||
case -1:
|
||||
sysfatal("fork: %r");
|
||||
case 0:
|
||||
switch(kpid = rfork(RFPROC|RFNOTEG|RFENVG|RFFDG)){
|
||||
case -1:
|
||||
sysfatal("fork: %r");
|
||||
case 0:
|
||||
for(i=0; i<3; i++){
|
||||
if(dup(pfd[i][1], i) < 0)
|
||||
sysfatal("dup: %r");
|
||||
close(pfd[i][0]);
|
||||
close(pfd[i][1]);
|
||||
}
|
||||
putenv("user", c->user);
|
||||
if(sysname)
|
||||
putenv("sysname", sysname);
|
||||
if(tz)
|
||||
putenv("tz", tz);
|
||||
|
||||
dir = smprint("/usr/%s", c->user);
|
||||
if(dir == nil || chdir(dir) < 0)
|
||||
chdir("/");
|
||||
if(cmd){
|
||||
putenv("service", "rx");
|
||||
execl("/bin/rc", "rc", "-lc", cmd, nil);
|
||||
sysfatal("cannot exec /bin/rc: %r");
|
||||
}else{
|
||||
putenv("service", "con");
|
||||
execl("/bin/ip/telnetd", "telnetd", "-tn", nil);
|
||||
sysfatal("cannot exec /bin/ip/telnetd: %r");
|
||||
}
|
||||
default:
|
||||
*kidpid = kpid;
|
||||
rendezvous(kidpid, 0);
|
||||
for(;;){
|
||||
if((w = wait()) == nil)
|
||||
sysfatal("wait: %r");
|
||||
if(w->pid == kpid)
|
||||
break;
|
||||
free(w);
|
||||
}
|
||||
if(w->msg[0]){
|
||||
m = allocmsg(c, SSH_MSG_DISCONNECT, 4+strlen(w->msg));
|
||||
putstring(m, w->msg);
|
||||
sendmsg(m);
|
||||
}else{
|
||||
m = allocmsg(c, SSH_SMSG_EXITSTATUS, 4);
|
||||
putlong(m, 0);
|
||||
sendmsg(m);
|
||||
}
|
||||
for(i=0; i<3; i++)
|
||||
close(pfd[i][0]);
|
||||
free(w);
|
||||
exits(nil);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
atexitkill(pid);
|
||||
rendezvous(kidpid, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
for(i=0; i<3; i++)
|
||||
close(pfd[i][1]);
|
||||
|
||||
copyout(c, pfd[1][0], SSH_SMSG_STDOUT_DATA);
|
||||
copyout(c, pfd[2][0], SSH_SMSG_STDERR_DATA);
|
||||
*kidin = pfd[0][0];
|
||||
}
|
|
@ -1,269 +0,0 @@
|
|||
#include "ssh.h"
|
||||
#include <bio.h>
|
||||
#include <ndb.h>
|
||||
|
||||
char Edecode[] = "error decoding input packet";
|
||||
char Eencode[] = "out of space encoding output packet (BUG)";
|
||||
char Ehangup[] = "hungup connection";
|
||||
char Ememory[] = "out of memory";
|
||||
|
||||
int debuglevel;
|
||||
int doabort;
|
||||
|
||||
void
|
||||
error(char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char buf[2048];
|
||||
|
||||
va_start(arg, fmt);
|
||||
vseprint(buf, buf+sizeof(buf), fmt, arg);
|
||||
va_end(arg);
|
||||
fprint(2, "%s: %s\n", argv0, buf);
|
||||
if(doabort)
|
||||
abort();
|
||||
exits(buf);
|
||||
}
|
||||
|
||||
void
|
||||
debug(int level, char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
if((level&debuglevel) == 0)
|
||||
return;
|
||||
va_start(arg, fmt);
|
||||
vfprint(2, fmt, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
void*
|
||||
emalloc(long n)
|
||||
{
|
||||
void *a;
|
||||
|
||||
a = mallocz(n, 1);
|
||||
if(a == nil)
|
||||
error(Ememory);
|
||||
setmalloctag(a, getcallerpc(&n));
|
||||
return a;
|
||||
}
|
||||
|
||||
void*
|
||||
erealloc(void *v, long n)
|
||||
{
|
||||
v = realloc(v, n);
|
||||
if(v == nil)
|
||||
error(Ememory);
|
||||
setrealloctag(v, getcallerpc(&v));
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
static int killpid[32];
|
||||
static int nkillpid;
|
||||
void
|
||||
atexitkiller(void)
|
||||
{
|
||||
int i, pid;
|
||||
|
||||
pid = getpid();
|
||||
debug(DBG, "atexitkiller: nkillpid=%d mypid=%d\n", nkillpid, pid);
|
||||
for(i=0; i<nkillpid; i++)
|
||||
if(pid != killpid[i]){
|
||||
debug(DBG, "killing %d\n", killpid[i]);
|
||||
postnote(PNPROC, killpid[i], "kill");
|
||||
}
|
||||
}
|
||||
void
|
||||
atexitkill(int pid)
|
||||
{
|
||||
killpid[nkillpid++] = pid;
|
||||
}
|
||||
|
||||
int
|
||||
readstrnl(int fd, char *buf, int nbuf)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<nbuf; i++){
|
||||
switch(read(fd, buf+i, 1)){
|
||||
case -1:
|
||||
return -1;
|
||||
case 0:
|
||||
werrstr("unexpected EOF");
|
||||
return -1;
|
||||
default:
|
||||
if(buf[i]=='\n'){
|
||||
buf[i] = '\0';
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
werrstr("line too long");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
calcsessid(Conn *c)
|
||||
{
|
||||
int n;
|
||||
uchar buf[1024];
|
||||
|
||||
n = mptobe(c->hostkey->n, buf, sizeof buf, nil);
|
||||
n += mptobe(c->serverkey->n, buf+n, sizeof buf-n, nil);
|
||||
memmove(buf+n, c->cookie, COOKIELEN);
|
||||
n += COOKIELEN;
|
||||
md5(buf, n, c->sessid, nil);
|
||||
}
|
||||
|
||||
void
|
||||
sshlog(char *f, ...)
|
||||
{
|
||||
char *s;
|
||||
va_list arg;
|
||||
Fmt fmt;
|
||||
static int pid;
|
||||
|
||||
if(pid == 0)
|
||||
pid = getpid();
|
||||
|
||||
va_start(arg, f);
|
||||
va_end(arg);
|
||||
|
||||
if(fmtstrinit(&fmt) < 0)
|
||||
sysfatal("fmtstrinit: %r");
|
||||
|
||||
fmtprint(&fmt, "[%d] ", pid);
|
||||
fmtvprint(&fmt, f, arg);
|
||||
|
||||
s = fmtstrflush(&fmt);
|
||||
if(s == nil)
|
||||
sysfatal("fmtstrflush: %r");
|
||||
syslog(0, "ssh", "%s", s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* this is far too smart.
|
||||
*/
|
||||
static int
|
||||
pstrcmp(const void *a, const void *b)
|
||||
{
|
||||
return strcmp(*(char**)a, *(char**)b);
|
||||
}
|
||||
|
||||
static char*
|
||||
trim(char *s)
|
||||
{
|
||||
char *t;
|
||||
int i, last, n, nf;
|
||||
char **f;
|
||||
char *p;
|
||||
|
||||
t = emalloc(strlen(s)+1);
|
||||
t[0] = '\0';
|
||||
n = 1;
|
||||
for(p=s; *p; p++)
|
||||
if(*p == ' ')
|
||||
n++;
|
||||
f = emalloc((n+1)*sizeof(f[0]));
|
||||
nf = tokenize(s, f, n+1);
|
||||
qsort(f, nf, sizeof(f[0]), pstrcmp);
|
||||
last=-1;
|
||||
for(i=0; i<nf; i++){
|
||||
if(last==-1 || strcmp(f[last], f[i])!=0){
|
||||
if(last >= 0)
|
||||
strcat(t, ",");
|
||||
strcat(t, f[i]);
|
||||
last = i;
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static void
|
||||
usetuple(Conn *c, Ndbtuple *t, int scanentries)
|
||||
{
|
||||
int first;
|
||||
Ndbtuple *l, *e;
|
||||
char *s;
|
||||
|
||||
first=1;
|
||||
s = c->host;
|
||||
for(l=t; first||l!=t; l=l->line, first=0){
|
||||
if(scanentries){
|
||||
for(e=l; e; e=e->entry){
|
||||
if(strcmp(e->val, c->host) != 0 &&
|
||||
(strcmp(e->attr, "ip")==0 || strcmp(e->attr, "dom")==0 || strcmp(e->attr, "sys")==0)){
|
||||
s = smprint("%s %s", s, e->val);
|
||||
if(s == nil)
|
||||
error("out of memory");
|
||||
}
|
||||
}
|
||||
}
|
||||
if(strcmp(l->val, c->host) != 0 &&
|
||||
(strcmp(l->attr, "ip")==0 || strcmp(l->attr, "dom")==0 || strcmp(l->attr, "sys")==0)){
|
||||
s = smprint("%s %s", s, l->val);
|
||||
if(s == nil)
|
||||
error("out of memory");
|
||||
}
|
||||
}
|
||||
s = trim(s);
|
||||
c->aliases = s;
|
||||
}
|
||||
|
||||
void
|
||||
setaliases(Conn *c, char *name)
|
||||
{
|
||||
char *p, *net;
|
||||
char *attr[2];
|
||||
Ndbtuple *t;
|
||||
|
||||
net = "/net";
|
||||
if(name[0]=='/'){
|
||||
p = strchr(name+1, '/');
|
||||
if(p){
|
||||
net = emalloc(p-name+1);
|
||||
memmove(net, name, p-name);
|
||||
}
|
||||
}
|
||||
if(p = strchr(name, '!'))
|
||||
name = p+1;
|
||||
|
||||
c->host = emalloc(strlen(name)+1);
|
||||
strcpy(c->host, name);
|
||||
|
||||
c->aliases = c->host;
|
||||
attr[0] = "dom";
|
||||
attr[1] = "ip";
|
||||
t = csipinfo(nil, ipattr(name), name, attr, 2);
|
||||
if(t != nil){
|
||||
usetuple(c, t, 0);
|
||||
ndbfree(t);
|
||||
}else{
|
||||
t = dnsquery(net, name, "ip");
|
||||
if(t != nil){
|
||||
usetuple(c, t, 1);
|
||||
ndbfree(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
privatefactotum(void)
|
||||
{
|
||||
char *user;
|
||||
Dir *d;
|
||||
|
||||
if((user=getuser()) && (d=dirstat("/mnt/factotum/rpc")) && strcmp(user, d->uid)!=0){
|
||||
/* grab the terminal's factotum */
|
||||
rfork(RFNAMEG); /* was RFNOTEG, which makes little sense */
|
||||
if(access("/mnt/term/mnt/factotum", AEXIST) >= 0){
|
||||
// fprint(2, "binding terminal's factotum\n");
|
||||
if(bind("/mnt/term/mnt/factotum", "/mnt/factotum", MREPL) < 0)
|
||||
sysfatal("cannot find factotum");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue