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