implement Auth; include "sys.m"; sys: Sys; include "keyring.m"; kr: Keyring; IPint: import kr; PK, SK: import kr; include "factotum.m"; factotum: Factotum; Attr: import factotum; findattrval: import factotum; include "sshio.m"; sshio: Sshio; Conn, Msg: import sshio; debug: import sshio; id(): int{ return SSH_AUTH_RSA;} init(mod: Sshio){ sshio = mod; sys = load Sys Sys->PATH; kr = load Keyring Keyring->PATH; factotum = load Factotum Factotum->PATH; factotum->init();} firstmsg(): int{ return SSH_CMSG_AUTH_RSA;} authsrv(c: ref Conn, m: ref Msg): ref AuthInfo{ # TO DO: use factotum hismod := m.getipint(); if(hismod.bits() < 512){ debug(DBG_AUTH, sys->sprint("rsa key for %s < 512 bits\n", c.user)); return nil; } hispk := readpk("/keydb/ssh/"+c.user); if(hispk == nil){ debug(DBG_AUTH, sys->sprint("no ssh/rsa key for %s: %r\n", c.user)); return nil; } if(!hispk.n.eq(hismod)){ debug(DBG_AUTH, sys->sprint("%s rsa key doesn't match modulus\n", c.user)); return nil; } # encrypt a challenge with his pk# chal := IPint.random(256).expmod(IPint.inttoip(1), hismod); chal := IPint.random(256); echal := kr->rsaencrypt(hispk, x := sshio->rsapad(chal, (hispk.n.bits()+7)/8));debug(DBG_AUTH, sys->sprint("padded %s\nrsa chal %s\n", x.iptostr(16), echal.iptostr(16))); m = Msg.mk(SSH_SMSG_AUTH_RSA_CHALLENGE, 2048); m.putipint(echal); c.out <-= m; m = sshio->recvmsg(c, SSH_CMSG_AUTH_RSA_RESPONSE); response := m.getbytes(Keyring->MD5dlen); chalbuf := array[32+SESSIDLEN] of byte; sshio->iptorjustbe(chal, chalbuf, 32); debug(DBG_AUTH, sys->sprint("\trjusted %s\n", sshio->hex(chalbuf[0:32]))); chalbuf[32:] = c.sessid[0: SESSIDLEN]; debug(DBG_AUTH, sys->sprint("\tappend sessid %s\n", sshio->hex(chalbuf))); expected := array[Keyring->MD5dlen] of byte; kr->md5(chalbuf, 32+SESSIDLEN, expected, nil); if(sshio->eqbytes(expected, response, len expected)) return ref AuthInfo(c.user, nil); return nil;} readpk(file: string): ref PK.RSA{ fd := sys->open(file, Sys->OREAD); if(fd == nil) return nil; buf := array[8192] of byte; nr := sys->readn(fd, buf, len buf); if(nr < 0) return nil; attrs := factotum->parseattrs(string buf[0: nr]); if(findattrval(attrs, "proto") != "rsa" || (ns := findattrval(attrs, "n")) == nil || (eks := findattrval(attrs, "ek")) == nil){ sys->werrstr("missing rsa key attributes"); return nil; } n := IPint.strtoip(ns, 16); ek := IPint.strtoip(eks, 16); if(n == nil || ek == nil){ sys->werrstr("invalid rsa key values"); return nil; } return ref PK.RSA(n, ek);} auth(c: ref Conn): int{ chalbuf := array[32+SESSIDLEN] of byte; response := array[Keyring->MD5dlen] of byte; debug(DBG_AUTH, "authrsa\n"); afd := sys->open("/mnt/factotum/rpc", Sys->ORDWR); if(afd == nil){ debug(DBG_AUTH, sys->sprint("open /mnt/factotum/rpc: %r\n")); return -1; } s := "proto=rsa role=client"; if(factotum->rpc(afd, "start", array of byte s).t0 != "ok"){ debug(DBG_AUTH, sys->sprint("auth_rpc start %s failed: %r\n", s)); return -1; } debug(DBG_AUTH, "trying factotum rsa keys\n"); for(;;){ (tag, value) := factotum->rpc(afd, "read", nil); if(tag != "ok") break; textkey := string value; sshio->debug(DBG_AUTH, sys->sprint("try %q\n", textkey)); mod := IPint.strtoip(textkey, 16); m := Msg.mk(SSH_CMSG_AUTH_RSA, 16+(mod.bits()+7/8)); m.putipint(mod); c.out <-= m; m = sshio->recvmsg(c, -1); case m.mtype { SSH_SMSG_FAILURE => debug(DBG_AUTH, "\tnot accepted\n"); continue; SSH_SMSG_AUTH_RSA_CHALLENGE => ; * => sshio->badmsg(m, 0, nil); } chal := m.getipint(); p := chal.iptostr(16); debug(DBG_AUTH, sys->sprint("\tgot challenge %s\n", p)); unpad: ref IPint; if(factotum->rpc(afd, "write", array of byte p).t0 == "ok" && ((tag, value) = factotum->rpc(afd, "read", nil)).t0 == "ok"){ debug(DBG_AUTH, sys->sprint("\tfactotum said %q\n", string value)); decr := IPint.strtoip(string value, 16); if(decr != nil){ debug(DBG_AUTH, sys->sprint("\tdecrypted %s\n", decr.iptostr(16))); unpad = sshio->rsaunpad(decr); }else unpad = IPint.inttoip(0); }else{ debug(DBG_AUTH, sys->sprint("\tauth_rpc write or read failed: %r\n")); unpad = IPint.inttoip(0); # it will fail, we'll go round again } debug(DBG_AUTH, sys->sprint("\tunpadded %s\n", unpad.iptostr(16))); sshio->iptorjustbe(unpad, chalbuf, 32);# debug(DBG_AUTH, sys->sprint("\trjusted %.*H\n", 32, chalbuf)); chalbuf[32:] = c.sessid[0: SESSIDLEN];# debug(DBG_AUTH, sys->sprint("\tappend sesskey %.*H\n", 32, chalbuf)); kr->md5(chalbuf, 32+SESSIDLEN, response, nil); m = Msg.mk(SSH_CMSG_AUTH_RSA_RESPONSE, Keyring->MD5dlen); m.putbytes(response, Keyring->MD5dlen); c.out <-= m; m = sshio->recvmsg(c, -1); case m.mtype { SSH_SMSG_FAILURE => ; # retry SSH_SMSG_SUCCESS => return 0; * => sshio->badmsg(m, 0, nil); } } return -1;}
implement Auth;
include "sys.m";
sys: Sys;
include "keyring.m";
kr: Keyring;
IPint: import kr;
PK, SK: import kr;
include "factotum.m";
factotum: Factotum;
Attr: import factotum;
findattrval: import factotum;
include "sshio.m";
sshio: Sshio;
Conn, Msg: import sshio;
debug: import sshio;
id(): int
{
return SSH_AUTH_RSA;
}
init(mod: Sshio)
{
sshio = mod;
sys = load Sys Sys->PATH;
kr = load Keyring Keyring->PATH;
factotum = load Factotum Factotum->PATH;
factotum->init();
}
firstmsg(): int
{
return SSH_CMSG_AUTH_RSA;
}
authsrv(c: ref Conn, m: ref Msg): ref AuthInfo
{
# TO DO: use factotum
hismod := m.getipint();
if(hismod.bits() < 512){
debug(DBG_AUTH, sys->sprint("rsa key for %s < 512 bits\n", c.user));
return nil;
}
hispk := readpk("/keydb/ssh/"+c.user);
if(hispk == nil){
debug(DBG_AUTH, sys->sprint("no ssh/rsa key for %s: %r\n", c.user));
return nil;
}
if(!hispk.n.eq(hismod)){
debug(DBG_AUTH, sys->sprint("%s rsa key doesn't match modulus\n", c.user));
return nil;
}
# encrypt a challenge with his pk
# chal := IPint.random(256).expmod(IPint.inttoip(1), hismod);
chal := IPint.random(256);
echal := kr->rsaencrypt(hispk, x := sshio->rsapad(chal, (hispk.n.bits()+7)/8));
debug(DBG_AUTH, sys->sprint("padded %s\nrsa chal %s\n", x.iptostr(16), echal.iptostr(16)));
m = Msg.mk(SSH_SMSG_AUTH_RSA_CHALLENGE, 2048);
m.putipint(echal);
c.out <-= m;
m = sshio->recvmsg(c, SSH_CMSG_AUTH_RSA_RESPONSE);
response := m.getbytes(Keyring->MD5dlen);
chalbuf := array[32+SESSIDLEN] of byte;
sshio->iptorjustbe(chal, chalbuf, 32);
debug(DBG_AUTH, sys->sprint("\trjusted %s\n", sshio->hex(chalbuf[0:32])));
chalbuf[32:] = c.sessid[0: SESSIDLEN];
debug(DBG_AUTH, sys->sprint("\tappend sessid %s\n", sshio->hex(chalbuf)));
expected := array[Keyring->MD5dlen] of byte;
kr->md5(chalbuf, 32+SESSIDLEN, expected, nil);
if(sshio->eqbytes(expected, response, len expected))
return ref AuthInfo(c.user, nil);
return nil;
}
readpk(file: string): ref PK.RSA
{
fd := sys->open(file, Sys->OREAD);
if(fd == nil)
return nil;
buf := array[8192] of byte;
nr := sys->readn(fd, buf, len buf);
if(nr < 0)
return nil;
attrs := factotum->parseattrs(string buf[0: nr]);
if(findattrval(attrs, "proto") != "rsa" ||
(ns := findattrval(attrs, "n")) == nil ||
(eks := findattrval(attrs, "ek")) == nil){
sys->werrstr("missing rsa key attributes");
return nil;
}
n := IPint.strtoip(ns, 16);
ek := IPint.strtoip(eks, 16);
if(n == nil || ek == nil){
sys->werrstr("invalid rsa key values");
return nil;
}
return ref PK.RSA(n, ek);
}
auth(c: ref Conn): int
{
chalbuf := array[32+SESSIDLEN] of byte;
response := array[Keyring->MD5dlen] of byte;
debug(DBG_AUTH, "authrsa\n");
afd := sys->open("/mnt/factotum/rpc", Sys->ORDWR);
if(afd == nil){
debug(DBG_AUTH, sys->sprint("open /mnt/factotum/rpc: %r\n"));
return -1;
}
s := "proto=rsa role=client";
if(factotum->rpc(afd, "start", array of byte s).t0 != "ok"){
debug(DBG_AUTH, sys->sprint("auth_rpc start %s failed: %r\n", s));
return -1;
}
debug(DBG_AUTH, "trying factotum rsa keys\n");
for(;;){
(tag, value) := factotum->rpc(afd, "read", nil);
if(tag != "ok")
break;
textkey := string value;
sshio->debug(DBG_AUTH, sys->sprint("try %q\n", textkey));
mod := IPint.strtoip(textkey, 16);
m := Msg.mk(SSH_CMSG_AUTH_RSA, 16+(mod.bits()+7/8));
m.putipint(mod);
c.out <-= m;
m = sshio->recvmsg(c, -1);
case m.mtype {
SSH_SMSG_FAILURE =>
debug(DBG_AUTH, "\tnot accepted\n");
continue;
SSH_SMSG_AUTH_RSA_CHALLENGE =>
;
* =>
sshio->badmsg(m, 0, nil);
}
chal := m.getipint();
p := chal.iptostr(16);
debug(DBG_AUTH, sys->sprint("\tgot challenge %s\n", p));
unpad: ref IPint;
if(factotum->rpc(afd, "write", array of byte p).t0 == "ok" &&
((tag, value) = factotum->rpc(afd, "read", nil)).t0 == "ok"){
debug(DBG_AUTH, sys->sprint("\tfactotum said %q\n", string value));
decr := IPint.strtoip(string value, 16);
if(decr != nil){
debug(DBG_AUTH, sys->sprint("\tdecrypted %s\n", decr.iptostr(16)));
unpad = sshio->rsaunpad(decr);
}else
unpad = IPint.inttoip(0);
}else{
debug(DBG_AUTH, sys->sprint("\tauth_rpc write or read failed: %r\n"));
unpad = IPint.inttoip(0); # it will fail, we'll go round again
}
debug(DBG_AUTH, sys->sprint("\tunpadded %s\n", unpad.iptostr(16)));
sshio->iptorjustbe(unpad, chalbuf, 32);
# debug(DBG_AUTH, sys->sprint("\trjusted %.*H\n", 32, chalbuf));
chalbuf[32:] = c.sessid[0: SESSIDLEN];
# debug(DBG_AUTH, sys->sprint("\tappend sesskey %.*H\n", 32, chalbuf));
kr->md5(chalbuf, 32+SESSIDLEN, response, nil);
m = Msg.mk(SSH_CMSG_AUTH_RSA_RESPONSE, Keyring->MD5dlen);
m.putbytes(response, Keyring->MD5dlen);
c.out <-= m;
m = sshio->recvmsg(c, -1);
case m.mtype {
SSH_SMSG_FAILURE =>
; # retry
SSH_SMSG_SUCCESS =>
return 0;
* =>
sshio->badmsg(m, 0, nil);
}
}
return -1;
}