#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_CLOSE", "SSH_MSG_CHANNEL_CLOSE_CONFIRMATION", "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"}; voidbadmsg(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); p = (uchar*)&m[1]; m->c = c; m->bp = p; m->ep = p+len; m->wp = p; m->type = type; return m;} static Msg*recvmsg0(Conn *c){ int pad; uchar *p, buf[4]; ulong crc, crc0, len; Msg *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); 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 %d\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 && (m==nil || m->type!=type)) badmsg(m, type); return m;} intsendmsg(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 %d\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; 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) return -1; return 0;} uchargetbyte(Msg *m){ if(m->rp >= m->ep) error(Edecode); return *m->rp++;} ushortgetshort(Msg *m){ ushort x; if(m->rp+2 > m->ep) error(Edecode); x = SHORT(m->rp); m->rp += 2; return x;} ulonggetlong(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;} voidputbyte(Msg *m, uchar x){ if(m->wp >= m->ep) error(Eencode); *m->wp++ = x;} voidputshort(Msg *m, ushort x){ if(m->wp+2 > m->ep) error(Eencode); PSHORT(m->wp, x); m->wp += 2;} voidputlong(Msg *m, ulong x){ if(m->wp+4 > m->ep) error(Eencode); PLONG(m->wp, x); m->wp += 4;} voidputstring(Msg *m, char *s){ int len; len = strlen(s); putlong(m, len); putbytes(m, s, len);} voidputbytes(Msg *m, void *a, long n){ if(m->wp+n > m->ep) error(Eencode); memmove(m->wp, a, n); m->wp += n;} voidputmpint(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;} voidputRSApub(Msg *m, RSApub *key){ putlong(m, mpsignif(key->n)); putmpint(m, key->ek); putmpint(m, key->n);} static ulong crctab[256]; static voidinitsum32(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 ulongsum32(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[256]; 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[256]; 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);} voidmptoberjust(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;}
#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_CLOSE",
"SSH_MSG_CHANNEL_CLOSE_CONFIRMATION",
"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);
p = (uchar*)&m[1];
m->c = c;
m->bp = p;
m->ep = p+len;
m->wp = p;
m->type = type;
return m;
}
static Msg*
recvmsg0(Conn *c)
{
int pad;
uchar *p, buf[4];
ulong crc, crc0, len;
Msg *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);
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 %d\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 && (m==nil || m->type!=type))
badmsg(m, type);
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 %d\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;
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)
return -1;
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[256];
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[256];
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;
}