ref: d0ab3a0dd8f6355b3603d0fb04043a9ae867639b
dir: /appl/cmd/auth/secstore.b/
implement Secstorec;
#
# interact with the Plan 9 secstore
#
include "sys.m";
sys: Sys;
include "draw.m";
include "dial.m";
dial: Dial;
include "bufio.m";
bufio: Bufio;
Iobuf: import bufio;
include "secstore.m";
secstore: Secstore;
include "arg.m";
Secstorec: module
{
init: fn(nil: ref Draw->Context, nil: list of string);
};
Maxfilesize: con 128*1024;
stderr: ref Sys->FD;
conn: ref Dial->Connection;
seckey: array of byte;
filekey: array of byte;
file: array of byte;
verbose := 0;
init(nil: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
bufio = load Bufio Bufio->PATH;
secstore = load Secstore Secstore->PATH;
dial = load Dial Dial->PATH;
sys->pctl(Sys->FORKFD, nil);
stderr = sys->fildes(2);
secstore->init();
secstore->privacy();
addr := "net!$auth!secstore";
user := readfile("/dev/user");
arg := load Arg Arg->PATH;
arg->init(args);
arg->setusage("auth/secstore [-iv] [-k key] [-p pin] [-s net!server!secstore] [-u user] [{drptx} file ...]");
iflag := 0;
pass, pin: string;
while((o := arg->opt()) != 0)
case o {
'i' => iflag = 1;
'k' => pass = arg->earg();
'v' => verbose = 1;
's' => addr = arg->earg();
'u' => user = arg->earg();
'p' => pin = arg->earg();
* =>
arg->usage();
}
args = arg->argv();
op := -1;
if(args != nil){
if(len hd args != 1)
arg->usage();
op = (hd args)[0];
args = tl args;
case op {
'd' or 'r' or 'p' or 'x' =>
if(args == nil)
arg->usage();
't' =>
;
* =>
arg->usage();
}
}
arg = nil;
if(iflag){
buf := array[Secstore->Maxmsg] of byte;
stdin := sys->fildes(0);
for(nr := 0; nr < len buf && (n := sys->read(stdin, buf, len buf-nr)) > 0;)
nr += n;
s := string buf[0:nr];
secstore->erasekey(buf[0:nr]);
(nf, flds) := sys->tokenize(s, "\n");
for(i := 0; i < len s; i++)
s[i] = 0;
if(nf < 1)
error("no password on standard input");
pass = hd flds;
if(nf > 1)
pin = hd tl flds;
}
conn: ref Dial->Connection;
Auth:
for(;;){
if(!iflag)
pass = readpassword("secstore password");
if(pass == nil)
exit;
erase();
seckey = secstore->mkseckey(pass);
filekey = secstore->mkfilekey(pass);
for(i := 0; i < len pass; i++)
pass[i] = 0; # clear it
conn = secstore->dial(dial->netmkaddr(addr, "net", "secstore"));
if(conn == nil)
error(sys->sprint("can't connect to secstore: %r"));
(srvname, diag) := secstore->auth(conn, user, seckey);
if(srvname == nil){
secstore->bye(conn);
sys->fprint(stderr, "secstore: authentication failed: %s\n", diag);
if(iflag)
raise "fail:auth";
continue;
}
case diag {
"" =>
if(verbose)
sys->fprint(stderr, "server: %s\n", srvname);
secstore->erasekey(seckey);
seckey = nil;
break Auth;
"need pin" =>
if(!iflag){
pin = readpassword("STA PIN+SecureID");
if(len pin == 0){
sys->fprint(stderr, "cancelled");
exit;
}
}else if(pin == nil)
raise "fail:no pin";
if(secstore->sendpin(conn, pin) < 0){
sys->fprint(stderr, "secstore: pin rejected: %r\n");
if(iflag)
raise "fail:bad pin";
continue;
}
}
}
if(op == 't'){
erase(); # no longer need the keys
entries := secstore->files(conn);
for(; entries != nil; entries = tl entries){
(name, size, date, hash, nil) := hd entries;
if(args != nil){
for(l := args; l != nil; l = tl l)
if((hd args) == name)
break;
if(args == nil)
continue;
}
if(verbose)
sys->print("%-14q %10d %s %s\n", name, size, date, hash);
else
sys->print("%q\n", name);
}
exit;
}
for(; args != nil; args = tl args){
fname := hd args;
case op {
'd' =>
checkname(fname, 1);
if(secstore->remove(conn, fname) < 0)
error(sys->sprint("can't remove %q: %r", fname));
verb('d', fname);
'p' =>
checkname(fname, 1);
file = getfile(conn, fname, filekey);
lines := secstore->lines(file);
lno := 1;
for(; lines != nil; lines = tl lines){
l := hd lines;
if(sys->write(sys->fildes(1), l, len l) != len l)
sys->fprint(sys->fildes(2), "secstore (%s:%d): %r\n", fname, lno);
lno++;
}
secstore->erasekey(file);
file = nil;
verb('p', fname);
'x' =>
checkname(fname, 1);
file = getfile(conn, fname, filekey);
ofd := sys->create(fname, Sys->OWRITE, 8r600);
if(ofd == nil)
error(sys->sprint("can't create %q: %r", fname));
if(sys->write(ofd, file, len file) != len file)
error(sys->sprint("error writing to %q: %r", fname));
secstore->erasekey(file);
file = nil;
verb('x', fname);
'r' =>
checkname(fname, 1);
fd := sys->open(fname, sys->OREAD);
if(fd == nil)
error(sys->sprint("open %q: %r", fname));
(ok, dir) := sys->fstat(fd);
if(ok != 0)
error(sys->sprint("stat %q: %r", fname));
if(int dir.length > Maxfilesize)
error(sys->sprint("length %bd > Maxfilesize %d", dir.length, Maxfilesize));
file = array[int dir.length] of byte;
if(sys->readn(fd, file, len file) != len file)
error(sys->sprint("short read: %r"));
if(putfile(conn, fname, file, filekey) < 0)
error(sys->sprint("putfile: %r"));
secstore->erasekey(file);
file = nil;
verb('r', fname);
* =>
error(sys->sprint("op %c not implemented", op));
}
}
erase();
}
checkname(s: string, noslash: int): string
{
tail := s;
for(i := 0; i < len s; i++){
if(s[i] == '/'){
if(noslash)
break;
tail = s[i+1:];
}
if(s[i] == '\n' || s[i] <= ' ')
break;
}
if(s == nil || tail == nil || i < len s || s == "..")
error(sys->sprint("can't use %q as a secstore file name", s)); # server checks as well, of course
return tail;
}
verb(op: int, n: string)
{
if(verbose)
sys->fprint(stderr, "%c %q\n", op, n);
}
getfile(conn: ref Dial->Connection, fname: string, key: array of byte): array of byte
{
f := secstore->getfile(conn, fname, 0);
if(f == nil)
error(sys->sprint("can't fetch %q: %r", fname));
if(fname != "."){
f = secstore->decrypt(f, key);
if(f == nil)
error(sys->sprint("can't decrypt %q: %r", fname));
}
return f;
}
putfile(conn: ref Dial->Connection, fname: string, data, key: array of byte): int
{
data = secstore->encrypt(data, key);
if(data == nil)
return -1;
return secstore->putfile(conn, fname, data);
}
erase()
{
if(secstore != nil){
secstore->erasekey(seckey);
secstore->erasekey(filekey);
secstore->erasekey(file);
}
}
error(s: string)
{
erase();
sys->fprint(stderr, "secstore: %s\n", s);
raise "fail:error";
}
readpassword(prompt: string): string
{
cons := sys->open("/dev/cons", Sys->ORDWR);
if(cons == nil)
return nil;
stdin := bufio->fopen(cons, Sys->OREAD);
if(stdin == nil)
return nil;
cfd := sys->open("/dev/consctl", Sys->OWRITE);
if (cfd == nil || sys->fprint(cfd, "rawon") <= 0)
sys->fprint(stderr, "secstore: warning: cannot hide typed password\n");
L:
for(;;){
sys->fprint(cons, "%s: ", prompt);
s := "";
while ((c := stdin.getc()) >= 0){
case c {
'\n' or ('d'&8r037) =>
sys->fprint(cons, "\n");
return s;
'\b' or 8r177 =>
if(len s > 0)
s = s[0:len s - 1];
'u' & 8r037 =>
sys->fprint(cons, "\n");
continue L;
* =>
s[len s] = c;
}
}
break;
}
return nil;
}
readfile(f: string): string
{
fd := sys->open(f, Sys->OREAD);
if(fd == nil)
return "";
buf := array[Sys->NAMEMAX] of byte;
n := sys->read(fd, buf, len buf);
if(n < 0)
return "";
return string buf[0:n];
}