ssh: R.I.P.

This commit is contained in:
khm 2017-01-12 16:36:38 -08:00
parent cb1555c7d7
commit dc8c7bf2b7
25 changed files with 0 additions and 6281 deletions

View file

@ -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

View file

@ -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.

View file

@ -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);
}
}
}

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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

View file

@ -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];
}

View file

@ -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");
}
}
}