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