Remote whitespace at the ends of lines. Remove blank lines from the ends of files. Change modes on source files so that they are not executable. Signed-off-by: Dan Cross <cross@gajendra.net>
1066 lines
18 KiB
C
1066 lines
18 KiB
C
/*
|
|
* Present factotum in ssh agent clothing.
|
|
*/
|
|
#include <u.h>
|
|
#include <libc.h>
|
|
#include <mp.h>
|
|
#include <libsec.h>
|
|
#include <auth.h>
|
|
#include <thread.h>
|
|
#include <9pclient.h>
|
|
|
|
enum
|
|
{
|
|
STACK = 65536
|
|
};
|
|
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,
|
|
SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES,
|
|
|
|
SSH2_AGENTC_REQUEST_IDENTITIES = 11,
|
|
SSH2_AGENT_IDENTITIES_ANSWER,
|
|
SSH2_AGENTC_SIGN_REQUEST,
|
|
SSH2_AGENT_SIGN_RESPONSE,
|
|
|
|
SSH2_AGENTC_ADD_IDENTITY = 17,
|
|
SSH2_AGENTC_REMOVE_IDENTITY,
|
|
SSH2_AGENTC_REMOVE_ALL_IDENTITIES,
|
|
SSH2_AGENTC_ADD_SMARTCARD_KEY,
|
|
SSH2_AGENTC_REMOVE_SMARTCARD_KEY,
|
|
|
|
SSH_AGENTC_LOCK,
|
|
SSH_AGENTC_UNLOCK,
|
|
SSH_AGENTC_ADD_RSA_ID_CONSTRAINED,
|
|
SSH2_AGENTC_ADD_ID_CONSTRAINED,
|
|
SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED,
|
|
|
|
SSH_AGENT_CONSTRAIN_LIFETIME = 1,
|
|
SSH_AGENT_CONSTRAIN_CONFIRM = 2,
|
|
|
|
SSH2_AGENT_FAILURE = 30,
|
|
|
|
SSH_COM_AGENT2_FAILURE = 102,
|
|
SSH_AGENT_OLD_SIGNATURE = 0x01
|
|
};
|
|
|
|
typedef struct Aconn Aconn;
|
|
struct Aconn
|
|
{
|
|
uchar *data;
|
|
uint ndata;
|
|
int ctl;
|
|
int fd;
|
|
char dir[40];
|
|
};
|
|
|
|
typedef struct Msg Msg;
|
|
struct Msg
|
|
{
|
|
uchar *bp;
|
|
uchar *p;
|
|
uchar *ep;
|
|
int bpalloc;
|
|
};
|
|
|
|
char adir[40];
|
|
int afd;
|
|
int chatty;
|
|
char *factotum = "factotum";
|
|
|
|
void agentproc(void *v);
|
|
void* emalloc(int n);
|
|
void* erealloc(void *v, int n);
|
|
void listenproc(void *v);
|
|
int runmsg(Aconn *a);
|
|
void listkeystext(void);
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: 9 ssh-agent [-D] [factotum]\n");
|
|
threadexitsall("usage");
|
|
}
|
|
|
|
void
|
|
threadmain(int argc, char **argv)
|
|
{
|
|
int fd, pid, export, dotextlist;
|
|
char dir[100], *ns;
|
|
char sock[200], addr[200];
|
|
uvlong x;
|
|
|
|
export = 0;
|
|
dotextlist = 0;
|
|
pid = getpid();
|
|
fmtinstall('B', mpfmt);
|
|
fmtinstall('H', encodefmt);
|
|
fmtinstall('[', encodefmt);
|
|
|
|
ARGBEGIN{
|
|
case '9':
|
|
chatty9pclient++;
|
|
break;
|
|
case 'D':
|
|
chatty++;
|
|
break;
|
|
case 'e':
|
|
export = 1;
|
|
break;
|
|
case 'l':
|
|
dotextlist = 1;
|
|
break;
|
|
default:
|
|
usage();
|
|
}ARGEND
|
|
|
|
if(argc > 1)
|
|
usage();
|
|
if(argc == 1)
|
|
factotum = argv[0];
|
|
|
|
if(dotextlist)
|
|
listkeystext();
|
|
|
|
ns = getns();
|
|
snprint(sock, sizeof sock, "%s/ssh-agent.socket", ns);
|
|
if(0){
|
|
x = ((uvlong)fastrand()<<32) | fastrand();
|
|
x ^= ((uvlong)fastrand()<<32) | fastrand();
|
|
snprint(dir, sizeof dir, "/tmp/ssh-%llux", x);
|
|
if((fd = create(dir, OREAD, DMDIR|0700)) < 0)
|
|
sysfatal("mkdir %s: %r", dir);
|
|
close(fd);
|
|
snprint(sock, sizeof sock, "%s/agent.%d", dir, pid);
|
|
}
|
|
snprint(addr, sizeof addr, "unix!%s", sock);
|
|
|
|
if((afd = announce(addr, adir)) < 0)
|
|
sysfatal("announce %s: %r", addr);
|
|
|
|
print("SSH_AUTH_SOCK=%s;\n", sock);
|
|
if(export)
|
|
print("export SSH_AUTH_SOCK;\n");
|
|
print("SSH_AGENT_PID=%d;\n", pid);
|
|
if(export)
|
|
print("export SSH_AGENT_PID;\n");
|
|
close(1);
|
|
rfork(RFNOTEG);
|
|
proccreate(listenproc, nil, STACK);
|
|
threadexits(0);
|
|
}
|
|
|
|
void
|
|
listenproc(void *v)
|
|
{
|
|
Aconn *a;
|
|
|
|
USED(v);
|
|
for(;;){
|
|
a = emalloc(sizeof *a);
|
|
a->ctl = listen(adir, a->dir);
|
|
if(a->ctl < 0)
|
|
sysfatal("listen: %r");
|
|
proccreate(agentproc, a, STACK);
|
|
}
|
|
}
|
|
|
|
void
|
|
agentproc(void *v)
|
|
{
|
|
Aconn *a;
|
|
int n;
|
|
|
|
a = v;
|
|
a->fd = accept(a->ctl, a->dir);
|
|
close(a->ctl);
|
|
a->ctl = -1;
|
|
for(;;){
|
|
a->data = erealloc(a->data, a->ndata+1024);
|
|
n = read(a->fd, a->data+a->ndata, 1024);
|
|
if(n <= 0)
|
|
break;
|
|
a->ndata += n;
|
|
while(runmsg(a))
|
|
;
|
|
}
|
|
close(a->fd);
|
|
free(a);
|
|
threadexits(nil);
|
|
}
|
|
|
|
int
|
|
get1(Msg *m)
|
|
{
|
|
if(m->p >= m->ep)
|
|
return 0;
|
|
return *m->p++;
|
|
}
|
|
|
|
int
|
|
get2(Msg *m)
|
|
{
|
|
uint x;
|
|
|
|
if(m->p+2 > m->ep)
|
|
return 0;
|
|
x = (m->p[0]<<8)|m->p[1];
|
|
m->p += 2;
|
|
return x;
|
|
}
|
|
|
|
int
|
|
get4(Msg *m)
|
|
{
|
|
uint x;
|
|
if(m->p+4 > m->ep)
|
|
return 0;
|
|
x = (m->p[0]<<24)|(m->p[1]<<16)|(m->p[2]<<8)|m->p[3];
|
|
m->p += 4;
|
|
return x;
|
|
}
|
|
|
|
uchar*
|
|
getn(Msg *m, uint n)
|
|
{
|
|
uchar *p;
|
|
|
|
if(m->p+n > m->ep)
|
|
return nil;
|
|
p = m->p;
|
|
m->p += n;
|
|
return p;
|
|
}
|
|
|
|
char*
|
|
getstr(Msg *m)
|
|
{
|
|
uint n;
|
|
uchar *p;
|
|
|
|
n = get4(m);
|
|
p = getn(m, n);
|
|
if(p == nil)
|
|
return nil;
|
|
p--;
|
|
memmove(p, p+1, n);
|
|
p[n] = 0;
|
|
return (char*)p;
|
|
}
|
|
|
|
mpint*
|
|
getmp(Msg *m)
|
|
{
|
|
int n;
|
|
uchar *p;
|
|
|
|
n = (get2(m)+7)/8;
|
|
if((p=getn(m, n)) == nil)
|
|
return nil;
|
|
return betomp(p, n, nil);
|
|
}
|
|
|
|
mpint*
|
|
getmp2(Msg *m)
|
|
{
|
|
int n;
|
|
uchar *p;
|
|
|
|
n = get4(m);
|
|
if((p = getn(m, n)) == nil)
|
|
return nil;
|
|
return betomp(p, n, nil);
|
|
}
|
|
|
|
void
|
|
newmsg(Msg *m)
|
|
{
|
|
memset(m, 0, sizeof *m);
|
|
}
|
|
|
|
void
|
|
mreset(Msg *m)
|
|
{
|
|
if(m->bpalloc){
|
|
memset(m->bp, 0, m->ep-m->bp);
|
|
free(m->bp);
|
|
}
|
|
memset(m, 0, sizeof *m);
|
|
}
|
|
|
|
Msg*
|
|
getm(Msg *m, Msg *mm)
|
|
{
|
|
uint n;
|
|
uchar *p;
|
|
|
|
n = get4(m);
|
|
if((p = getn(m, n)) == nil)
|
|
return nil;
|
|
mm->bp = p;
|
|
mm->p = p;
|
|
mm->ep = p+n;
|
|
mm->bpalloc = 0;
|
|
return mm;
|
|
}
|
|
|
|
uchar*
|
|
ensure(Msg *m, int n)
|
|
{
|
|
int len;
|
|
uchar *p;
|
|
uchar *obp;
|
|
|
|
if(m->bp == nil)
|
|
m->bpalloc = 1;
|
|
if(!m->bpalloc){
|
|
p = emalloc(m->ep - m->bp);
|
|
memmove(p, m->bp, m->ep - m->bp);
|
|
obp = m->bp;
|
|
m->bp = p;
|
|
m->ep += m->bp - obp;
|
|
m->p += m->bp - obp;
|
|
m->bpalloc = 1;
|
|
}
|
|
len = m->ep - m->bp;
|
|
if(m->p+n > m->ep){
|
|
obp = m->bp;
|
|
m->bp = erealloc(m->bp, len+n+1024);
|
|
m->p += m->bp - obp;
|
|
m->ep += m->bp - obp;
|
|
m->ep += n+1024;
|
|
}
|
|
p = m->p;
|
|
m->p += n;
|
|
return p;
|
|
}
|
|
|
|
void
|
|
put4(Msg *m, uint n)
|
|
{
|
|
uchar *p;
|
|
|
|
p = ensure(m, 4);
|
|
p[0] = (n>>24)&0xFF;
|
|
p[1] = (n>>16)&0xFF;
|
|
p[2] = (n>>8)&0xFF;
|
|
p[3] = n&0xFF;
|
|
}
|
|
|
|
void
|
|
put2(Msg *m, uint n)
|
|
{
|
|
uchar *p;
|
|
|
|
p = ensure(m, 2);
|
|
p[0] = (n>>8)&0xFF;
|
|
p[1] = n&0xFF;
|
|
}
|
|
|
|
void
|
|
put1(Msg *m, uint n)
|
|
{
|
|
uchar *p;
|
|
|
|
p = ensure(m, 1);
|
|
p[0] = n&0xFF;
|
|
}
|
|
|
|
void
|
|
putn(Msg *m, void *a, uint n)
|
|
{
|
|
uchar *p;
|
|
|
|
p = ensure(m, n);
|
|
memmove(p, a, n);
|
|
}
|
|
|
|
void
|
|
putmp(Msg *m, mpint *b)
|
|
{
|
|
int bits, n;
|
|
uchar *p;
|
|
|
|
bits = mpsignif(b);
|
|
put2(m, bits);
|
|
n = (bits+7)/8;
|
|
p = ensure(m, n);
|
|
mptobe(b, p, n, nil);
|
|
}
|
|
|
|
void
|
|
putmp2(Msg *m, mpint *b)
|
|
{
|
|
int bits, n;
|
|
uchar *p;
|
|
|
|
if(mpcmp(b, mpzero) == 0){
|
|
put4(m, 0);
|
|
return;
|
|
}
|
|
bits = mpsignif(b);
|
|
n = (bits+7)/8;
|
|
if(bits%8 == 0){
|
|
put4(m, n+1);
|
|
put1(m, 0);
|
|
}else
|
|
put4(m, n);
|
|
p = ensure(m, n);
|
|
mptobe(b, p, n, nil);
|
|
}
|
|
|
|
void
|
|
putstr(Msg *m, char *s)
|
|
{
|
|
int n;
|
|
|
|
n = strlen(s);
|
|
put4(m, n);
|
|
putn(m, s, n);
|
|
}
|
|
|
|
void
|
|
putm(Msg *m, Msg *mm)
|
|
{
|
|
uint n;
|
|
|
|
n = mm->p - mm->bp;
|
|
put4(m, n);
|
|
putn(m, mm->bp, n);
|
|
}
|
|
|
|
void
|
|
newreply(Msg *m, int type)
|
|
{
|
|
memset(m, 0, sizeof *m);
|
|
put4(m, 0);
|
|
put1(m, type);
|
|
}
|
|
|
|
void
|
|
reply(Aconn *a, Msg *m)
|
|
{
|
|
uint n;
|
|
uchar *p;
|
|
|
|
n = (m->p - m->bp) - 4;
|
|
p = m->bp;
|
|
p[0] = (n>>24)&0xFF;
|
|
p[1] = (n>>16)&0xFF;
|
|
p[2] = (n>>8)&0xFF;
|
|
p[3] = n&0xFF;
|
|
if(chatty)
|
|
fprint(2, "respond %d t=%d: %.*H\n", n, p[4], n, m->bp+4);
|
|
write(a->fd, p, n+4);
|
|
mreset(m);
|
|
}
|
|
|
|
typedef struct Key Key;
|
|
struct Key
|
|
{
|
|
mpint *mod;
|
|
mpint *ek;
|
|
char *comment;
|
|
};
|
|
|
|
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
|
|
putrsa1(Msg *m, char **f, int nf)
|
|
{
|
|
char *p;
|
|
mpint *mod, *ek;
|
|
|
|
p = find(f, nf, "n");
|
|
if(p == nil || (mod = strtomp(p, nil, 16, nil)) == nil)
|
|
return -1;
|
|
p = find(f, nf, "ek");
|
|
if(p == nil || (ek = strtomp(p, nil, 16, nil)) == nil){
|
|
mpfree(mod);
|
|
return -1;
|
|
}
|
|
p = find(f, nf, "comment");
|
|
if(p == nil)
|
|
p = "";
|
|
put4(m, mpsignif(mod));
|
|
putmp(m, ek);
|
|
putmp(m, mod);
|
|
putstr(m, p);
|
|
mpfree(mod);
|
|
mpfree(ek);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
printattr(char **f, int nf)
|
|
{
|
|
int i;
|
|
|
|
print("#");
|
|
for(i=0; i<nf; i++)
|
|
print(" %s", f[i]);
|
|
print("\n");
|
|
}
|
|
|
|
void
|
|
printrsa1(char **f, int nf)
|
|
{
|
|
char *p;
|
|
mpint *mod, *ek;
|
|
|
|
p = find(f, nf, "n");
|
|
if(p == nil || (mod = strtomp(p, nil, 16, nil)) == nil)
|
|
return;
|
|
p = find(f, nf, "ek");
|
|
if(p == nil || (ek = strtomp(p, nil, 16, nil)) == nil){
|
|
mpfree(mod);
|
|
return;
|
|
}
|
|
p = find(f, nf, "comment");
|
|
if(p == nil)
|
|
p = "";
|
|
|
|
if(chatty)
|
|
printattr(f, nf);
|
|
print("%d %.10B %.10B %s\n", mpsignif(mod), ek, mod, p);
|
|
mpfree(ek);
|
|
mpfree(mod);
|
|
}
|
|
|
|
static int
|
|
putrsa(Msg *m, char **f, int nf)
|
|
{
|
|
char *p;
|
|
mpint *mod, *ek;
|
|
|
|
p = find(f, nf, "n");
|
|
if(p == nil || (mod = strtomp(p, nil, 16, nil)) == nil)
|
|
return -1;
|
|
p = find(f, nf, "ek");
|
|
if(p == nil || (ek = strtomp(p, nil, 16, nil)) == nil){
|
|
mpfree(mod);
|
|
return -1;
|
|
}
|
|
putstr(m, "ssh-rsa");
|
|
putmp2(m, ek);
|
|
putmp2(m, mod);
|
|
mpfree(ek);
|
|
mpfree(mod);
|
|
return 0;
|
|
}
|
|
|
|
RSApub*
|
|
getrsapub(Msg *m)
|
|
{
|
|
RSApub *k;
|
|
|
|
k = rsapuballoc();
|
|
if(k == nil)
|
|
return nil;
|
|
k->ek = getmp2(m);
|
|
k->n = getmp2(m);
|
|
if(k->ek == nil || k->n == nil){
|
|
rsapubfree(k);
|
|
return nil;
|
|
}
|
|
return k;
|
|
}
|
|
|
|
static int
|
|
putdsa(Msg *m, char **f, int nf)
|
|
{
|
|
char *p;
|
|
int ret;
|
|
mpint *dp, *dq, *dalpha, *dkey;
|
|
|
|
ret = -1;
|
|
dp = dq = dalpha = dkey = nil;
|
|
p = find(f, nf, "p");
|
|
if(p == nil || (dp = strtomp(p, nil, 16, nil)) == nil)
|
|
goto out;
|
|
p = find(f, nf, "q");
|
|
if(p == nil || (dq = strtomp(p, nil, 16, nil)) == nil)
|
|
goto out;
|
|
p = find(f, nf, "alpha");
|
|
if(p == nil || (dalpha = strtomp(p, nil, 16, nil)) == nil)
|
|
goto out;
|
|
p = find(f, nf, "key");
|
|
if(p == nil || (dkey = strtomp(p, nil, 16, nil)) == nil)
|
|
goto out;
|
|
putstr(m, "ssh-dss");
|
|
putmp2(m, dp);
|
|
putmp2(m, dq);
|
|
putmp2(m, dalpha);
|
|
putmp2(m, dkey);
|
|
ret = 0;
|
|
out:
|
|
mpfree(dp);
|
|
mpfree(dq);
|
|
mpfree(dalpha);
|
|
mpfree(dkey);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
putkey2(Msg *m, int (*put)(Msg*,char**,int), char **f, int nf)
|
|
{
|
|
char *p;
|
|
Msg mm;
|
|
|
|
newmsg(&mm);
|
|
if(put(&mm, f, nf) < 0)
|
|
return -1;
|
|
putm(m, &mm);
|
|
mreset(&mm);
|
|
p = find(f, nf, "comment");
|
|
if(p == nil)
|
|
p = "";
|
|
putstr(m, p);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
printkey(char *type, int (*put)(Msg*,char**,int), char **f, int nf)
|
|
{
|
|
Msg m;
|
|
char *p;
|
|
|
|
newmsg(&m);
|
|
if(put(&m, f, nf) < 0)
|
|
return -1;
|
|
p = find(f, nf, "comment");
|
|
if(p == nil)
|
|
p = "";
|
|
if(chatty)
|
|
printattr(f, nf);
|
|
print("%s %.*[ %s\n", type, m.p-m.bp, m.bp, p);
|
|
mreset(&m);
|
|
return 0;
|
|
}
|
|
|
|
DSApub*
|
|
getdsapub(Msg *m)
|
|
{
|
|
DSApub *k;
|
|
|
|
k = dsapuballoc();
|
|
if(k == nil)
|
|
return nil;
|
|
k->p = getmp2(m);
|
|
k->q = getmp2(m);
|
|
k->alpha = getmp2(m);
|
|
k->key = getmp2(m);
|
|
if(!k->p || !k->q || !k->alpha || !k->key){
|
|
dsapubfree(k);
|
|
return nil;
|
|
}
|
|
return k;
|
|
}
|
|
|
|
static int
|
|
listkeys(Msg *m, int version)
|
|
{
|
|
char buf[8192+1], *line[100], *f[20], *p, *s;
|
|
int pnk;
|
|
int i, n, nl, nf, nk;
|
|
CFid *fid;
|
|
|
|
nk = 0;
|
|
pnk = m->p - m->bp;
|
|
put4(m, 0);
|
|
if((fid = nsopen(factotum, nil, "ctl", OREAD)) == nil){
|
|
fprint(2, "ssh-agent: open factotum: %r\n");
|
|
return -1;
|
|
}
|
|
for(;;){
|
|
if((n = fsread(fid, buf, sizeof buf-1)) <= 0)
|
|
break;
|
|
buf[n] = 0;
|
|
nl = getfields(buf, line, nelem(line), 1, "\n");
|
|
for(i=0; i<nl; i++){
|
|
nf = tokenize(line[i], f, nelem(f));
|
|
if(nf == 0 || strcmp(f[0], "key") != 0)
|
|
continue;
|
|
p = find(f, nf, "proto");
|
|
if(p == nil)
|
|
continue;
|
|
s = find(f, nf, "service");
|
|
if(s == nil)
|
|
continue;
|
|
|
|
if(version == 1 && strcmp(p, "rsa") == 0 && strcmp(s, "ssh") == 0)
|
|
if(putrsa1(m, f, nf) >= 0)
|
|
nk++;
|
|
if(version == 2 && strcmp(p, "rsa") == 0 && strcmp(s, "ssh-rsa") == 0)
|
|
if(putkey2(m, putrsa, f, nf) >= 0)
|
|
nk++;
|
|
if(version == 2 && strcmp(p, "dsa") == 0 && strcmp(s, "ssh-dss") == 0)
|
|
if(putkey2(m, putdsa, f, nf) >= 0)
|
|
nk++;
|
|
}
|
|
}
|
|
if(chatty)
|
|
fprint(2, "sending %d keys\n", nk);
|
|
fsclose(fid);
|
|
m->bp[pnk+0] = (nk>>24)&0xFF;
|
|
m->bp[pnk+1] = (nk>>16)&0xFF;
|
|
m->bp[pnk+2] = (nk>>8)&0xFF;
|
|
m->bp[pnk+3] = nk&0xFF;
|
|
return nk;
|
|
}
|
|
|
|
void
|
|
listkeystext(void)
|
|
{
|
|
char buf[8192+1], *line[100], *f[20], *p, *s;
|
|
int i, n, nl, nf;
|
|
CFid *fid;
|
|
|
|
if((fid = nsopen(factotum, nil, "ctl", OREAD)) == nil){
|
|
fprint(2, "ssh-agent: open factotum: %r\n");
|
|
return;
|
|
}
|
|
for(;;){
|
|
if((n = fsread(fid, buf, sizeof buf-1)) <= 0)
|
|
break;
|
|
buf[n] = 0;
|
|
nl = getfields(buf, line, nelem(line), 1, "\n");
|
|
for(i=0; i<nl; i++){
|
|
nf = tokenize(line[i], f, nelem(f));
|
|
if(nf == 0 || strcmp(f[0], "key") != 0)
|
|
continue;
|
|
p = find(f, nf, "proto");
|
|
if(p == nil)
|
|
continue;
|
|
s = find(f, nf, "service");
|
|
if(s == nil)
|
|
continue;
|
|
|
|
if(strcmp(p, "rsa") == 0 && strcmp(s, "ssh") == 0)
|
|
printrsa1(f, nf);
|
|
if(strcmp(p, "rsa") == 0 && strcmp(s, "ssh-rsa") == 0)
|
|
printkey("ssh-rsa", putrsa, f, nf);
|
|
if(strcmp(p, "dsa") == 0 && strcmp(s, "ssh-dss") == 0)
|
|
printkey("ssh-dss", putdsa, f, nf);
|
|
}
|
|
}
|
|
fsclose(fid);
|
|
threadexitsall(nil);
|
|
}
|
|
|
|
mpint*
|
|
rsaunpad(mpint *b)
|
|
{
|
|
int i, n;
|
|
uchar buf[2560];
|
|
|
|
n = (mpsignif(b)+7)/8;
|
|
if(n > sizeof buf){
|
|
werrstr("rsaunpad: too big");
|
|
return nil;
|
|
}
|
|
mptobe(b, buf, n, nil);
|
|
|
|
/* the initial zero has been eaten by the betomp -> mptobe sequence */
|
|
if(buf[0] != 2){
|
|
werrstr("rsaunpad: expected leading 2");
|
|
return nil;
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
static int
|
|
dorsa(Aconn *a, mpint *mod, mpint *exp, mpint *chal, uchar chalbuf[32])
|
|
{
|
|
AuthRpc *rpc;
|
|
char buf[4096], *p;
|
|
mpint *decr, *unpad;
|
|
|
|
USED(exp);
|
|
if((rpc = auth_allocrpc()) == nil){
|
|
fprint(2, "ssh-agent: auth_allocrpc: %r\n");
|
|
return -1;
|
|
}
|
|
snprint(buf, sizeof buf, "proto=rsa service=ssh role=decrypt n=%lB ek=%lB", mod, exp);
|
|
if(chatty)
|
|
fprint(2, "ssh-agent: start %s\n", buf);
|
|
if(auth_rpc(rpc, "start", buf, strlen(buf)) != ARok){
|
|
fprint(2, "ssh-agent: auth 'start' failed: %r\n");
|
|
Die:
|
|
auth_freerpc(rpc);
|
|
return -1;
|
|
}
|
|
|
|
p = mptoa(chal, 16, nil, 0);
|
|
if(p == nil){
|
|
fprint(2, "ssh-agent: dorsa: mptoa: %r\n");
|
|
goto Die;
|
|
}
|
|
if(chatty)
|
|
fprint(2, "ssh-agent: challenge %B => %s\n", chal, p);
|
|
if(auth_rpc(rpc, "writehex", p, strlen(p)) != ARok){
|
|
fprint(2, "ssh-agent: dorsa: auth 'write': %r\n");
|
|
free(p);
|
|
goto Die;
|
|
}
|
|
free(p);
|
|
if(auth_rpc(rpc, "readhex", nil, 0) != ARok){
|
|
fprint(2, "ssh-agent: dorsa: auth 'read': %r\n");
|
|
goto Die;
|
|
}
|
|
decr = strtomp(rpc->arg, nil, 16, nil);
|
|
if(chatty)
|
|
fprint(2, "ssh-agent: response %s => %B\n", rpc->arg, decr);
|
|
if(decr == nil){
|
|
fprint(2, "ssh-agent: dorsa: strtomp: %r\n");
|
|
goto Die;
|
|
}
|
|
unpad = rsaunpad(decr);
|
|
if(chatty)
|
|
fprint(2, "ssh-agent: unpad %B => %B\n", decr, unpad);
|
|
if(unpad == nil){
|
|
fprint(2, "ssh-agent: dorsa: rsaunpad: %r\n");
|
|
mpfree(decr);
|
|
goto Die;
|
|
}
|
|
mpfree(decr);
|
|
mptoberjust(unpad, chalbuf, 32);
|
|
mpfree(unpad);
|
|
auth_freerpc(rpc);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
keysign(Msg *mkey, Msg *mdata, Msg *msig)
|
|
{
|
|
char *s;
|
|
AuthRpc *rpc;
|
|
RSApub *rsa;
|
|
DSApub *dsa;
|
|
char buf[4096];
|
|
uchar digest[SHA1dlen];
|
|
|
|
s = getstr(mkey);
|
|
if(strcmp(s, "ssh-rsa") == 0){
|
|
rsa = getrsapub(mkey);
|
|
if(rsa == nil)
|
|
return -1;
|
|
snprint(buf, sizeof buf, "proto=rsa service=ssh-rsa role=sign n=%lB ek=%lB",
|
|
rsa->n, rsa->ek);
|
|
rsapubfree(rsa);
|
|
}else if(strcmp(s, "ssh-dss") == 0){
|
|
dsa = getdsapub(mkey);
|
|
if(dsa == nil)
|
|
return -1;
|
|
snprint(buf, sizeof buf, "proto=dsa service=ssh-dss role=sign p=%lB q=%lB alpha=%lB key=%lB",
|
|
dsa->p, dsa->q, dsa->alpha, dsa->key);
|
|
dsapubfree(dsa);
|
|
}else{
|
|
fprint(2, "ssh-agent: cannot sign key type %s\n", s);
|
|
werrstr("unknown key type %s", s);
|
|
return -1;
|
|
}
|
|
|
|
if((rpc = auth_allocrpc()) == nil){
|
|
fprint(2, "ssh-agent: auth_allocrpc: %r\n");
|
|
return -1;
|
|
}
|
|
if(chatty)
|
|
fprint(2, "ssh-agent: start %s\n", buf);
|
|
if(auth_rpc(rpc, "start", buf, strlen(buf)) != ARok){
|
|
fprint(2, "ssh-agent: auth 'start' failed: %r\n");
|
|
Die:
|
|
auth_freerpc(rpc);
|
|
return -1;
|
|
}
|
|
sha1(mdata->bp, mdata->ep-mdata->bp, digest, nil);
|
|
if(auth_rpc(rpc, "write", digest, SHA1dlen) != ARok){
|
|
fprint(2, "ssh-agent: auth 'write in sign failed: %r\n");
|
|
goto Die;
|
|
}
|
|
if(auth_rpc(rpc, "read", nil, 0) != ARok){
|
|
fprint(2, "ssh-agent: auth 'read' failed: %r\n");
|
|
goto Die;
|
|
}
|
|
newmsg(msig);
|
|
putstr(msig, s);
|
|
put4(msig, rpc->narg);
|
|
putn(msig, rpc->arg, rpc->narg);
|
|
auth_freerpc(rpc);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
runmsg(Aconn *a)
|
|
{
|
|
char *p;
|
|
int n, nk, type, rt, vers;
|
|
mpint *ek, *mod, *chal;
|
|
uchar sessid[16], chalbuf[32], digest[MD5dlen];
|
|
uint len, flags;
|
|
DigestState *s;
|
|
Msg m, mkey, mdata, msig;
|
|
|
|
if(a->ndata < 4)
|
|
return 0;
|
|
len = (a->data[0]<<24)|(a->data[1]<<16)|(a->data[2]<<8)|a->data[3];
|
|
if(a->ndata < 4+len)
|
|
return 0;
|
|
m.p = a->data+4;
|
|
m.ep = m.p+len;
|
|
type = get1(&m);
|
|
if(chatty)
|
|
fprint(2, "msg %d: %.*H\n", type, len, m.p);
|
|
switch(type){
|
|
default:
|
|
Failure:
|
|
newreply(&m, SSH_AGENT_FAILURE);
|
|
reply(a, &m);
|
|
break;
|
|
|
|
case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
|
|
vers = 1;
|
|
newreply(&m, SSH_AGENT_RSA_IDENTITIES_ANSWER);
|
|
goto Identities;
|
|
case SSH2_AGENTC_REQUEST_IDENTITIES:
|
|
vers = 2;
|
|
newreply(&m, SSH2_AGENT_IDENTITIES_ANSWER);
|
|
Identities:
|
|
nk = listkeys(&m, vers);
|
|
if(nk < 0){
|
|
mreset(&m);
|
|
goto Failure;
|
|
}
|
|
if(chatty)
|
|
fprint(2, "request identities\n", nk);
|
|
reply(a, &m);
|
|
break;
|
|
|
|
case SSH_AGENTC_RSA_CHALLENGE:
|
|
n = get4(&m);
|
|
USED(n);
|
|
ek = getmp(&m);
|
|
mod = getmp(&m);
|
|
chal = getmp(&m);
|
|
if((p = (char*)getn(&m, 16)) == nil){
|
|
Failchal:
|
|
mpfree(ek);
|
|
mpfree(mod);
|
|
mpfree(chal);
|
|
goto Failure;
|
|
}
|
|
memmove(sessid, p, 16);
|
|
rt = get4(&m);
|
|
if(rt != 1 || dorsa(a, mod, ek, chal, chalbuf) < 0)
|
|
goto Failchal;
|
|
s = md5(chalbuf, 32, nil, nil);
|
|
if(s == nil)
|
|
goto Failchal;
|
|
md5(sessid, 16, digest, s);
|
|
print("md5 %.*H %.*H => %.*H\n", 32, chalbuf, 16, sessid, MD5dlen, digest);
|
|
|
|
newreply(&m, SSH_AGENT_RSA_RESPONSE);
|
|
putn(&m, digest, 16);
|
|
reply(a, &m);
|
|
|
|
mpfree(ek);
|
|
mpfree(mod);
|
|
mpfree(chal);
|
|
break;
|
|
|
|
case SSH2_AGENTC_SIGN_REQUEST:
|
|
if(getm(&m, &mkey) == nil
|
|
|| getm(&m, &mdata) == nil)
|
|
goto Failure;
|
|
flags = get4(&m);
|
|
if(flags & SSH_AGENT_OLD_SIGNATURE)
|
|
goto Failure;
|
|
if(keysign(&mkey, &mdata, &msig) < 0)
|
|
goto Failure;
|
|
if(chatty)
|
|
fprint(2, "signature: %.*H\n",
|
|
msig.p-msig.bp, msig.bp);
|
|
newreply(&m, SSH2_AGENT_SIGN_RESPONSE);
|
|
putm(&m, &msig);
|
|
mreset(&msig);
|
|
reply(a, &m);
|
|
break;
|
|
|
|
case SSH_AGENTC_ADD_RSA_IDENTITY:
|
|
/*
|
|
msg: n[4] mod[mp] pubexp[exp] privexp[mp]
|
|
p^-1 mod q[mp] p[mp] q[mp] comment[str]
|
|
*/
|
|
goto Failure;
|
|
|
|
case SSH_AGENTC_REMOVE_RSA_IDENTITY:
|
|
/*
|
|
msg: n[4] mod[mp] pubexp[mp]
|
|
*/
|
|
goto Failure;
|
|
|
|
}
|
|
|
|
a->ndata -= 4+len;
|
|
memmove(a->data, a->data+4+len, a->ndata);
|
|
return 1;
|
|
}
|
|
|
|
void*
|
|
emalloc(int n)
|
|
{
|
|
void *v;
|
|
|
|
v = mallocz(n, 1);
|
|
if(v == nil){
|
|
abort();
|
|
sysfatal("out of memory allocating %d", n);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
void*
|
|
erealloc(void *v, int n)
|
|
{
|
|
v = realloc(v, n);
|
|
if(v == nil){
|
|
abort();
|
|
sysfatal("out of memory reallocating %d", n);
|
|
}
|
|
return v;
|
|
}
|