summaryrefslogtreecommitdiff
path: root/appl/grid/remotelogon.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/grid/remotelogon.b')
-rw-r--r--appl/grid/remotelogon.b427
1 files changed, 427 insertions, 0 deletions
diff --git a/appl/grid/remotelogon.b b/appl/grid/remotelogon.b
new file mode 100644
index 00000000..89c1953f
--- /dev/null
+++ b/appl/grid/remotelogon.b
@@ -0,0 +1,427 @@
+implement WmLogon;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+#
+# get a certificate to enable remote access.
+#
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Screen, Display, Image, Context, Point, Rect: import draw;
+include "tk.m";
+ tk: Tk;
+include "tkclient.m";
+ tkclient: Tkclient;
+include "arg.m";
+include "sh.m";
+include "newns.m";
+include "keyring.m";
+ keyring: Keyring;
+include "security.m";
+ login: Login;
+include "registries.m";
+ registries: Registries;
+ Registry, Attributes: import registries;
+
+
+# XXX where to put the certificate: is the username already set to
+# something appropriate, with a home directory and keyring directory in that?
+
+# how do we find out the signer; presumably from the registry?
+# should do that before signing on; if we can't get it, then prompt for it.
+WmLogon: module {
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+registry: ref Registry;
+usr := "";
+passwd := "";
+loginaddr := "";
+signerpkhash := "";
+
+cfg := array[] of {
+ "frame .f -bd 2 -relief raised",
+ "label .f.p -bitmap @/icons/inferno.bit -borderwidth 2 -relief raised",
+ "label .f.ul -text {User Name:} -anchor w",
+ "entry .f.ue -bg white -width 10w",
+ "label .f.pl -text {Password:} -anchor w",
+ "entry .f.pe -bg white -show *",
+ "checkbutton .f.ck -variable newuser -text {New}",
+ "frame .f.f -borderwidth 2 -relief raised",
+ "frame .f.u",
+ "pack .f.ue -in .f.u -side left -expand 1 -fill x",
+ "pack .f.ck -in .f.u -side left",
+ "grid .f.ul -row 0 -column 0 -sticky e -in .f.f",
+ "grid .f.u -row 0 -column 1 -sticky ew -in .f.f",
+ "grid .f.pl -row 1 -column 0 -sticky e -in .f.f",
+ "grid .f.pe -row 1 -column 1 -sticky ew -in .f.f",
+ "pack .f.p .f.f -fill x",
+ "bind .f.ue <Key-\n> {focus .f.pe}",
+ "bind .f.ue {<Key-\t>} {focus .f.pe}",
+ "bind .f.pe <Key-\n> {send panelcmd ok}",
+ "bind .f.pe {<Key-\t>} {focus .f.ue}",
+ "focus .f.ue",
+};
+
+notecfg := array[] of {
+ "frame .n -bd 2 -relief raised",
+ "frame .n.f",
+ "label .n.f.m -anchor nw",
+ "label .n.f.l -bitmap error -foreground red",
+ "button .n.b -text Continue -command {send notecmd done}",
+ "focus .n.f",
+ "bind .n.f <Key-\n> {send notecmd done}",
+ "pack .n.f.l .n.f.m -side left -expand 1",
+ "pack .n.f .n.b",
+};
+
+checkload[T](x: T, p: string): T
+{
+ if(x == nil)
+ error(sys->sprint("cannot load %s: %r\n", p));
+ return x;
+}
+
+init(ctxt: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ draw = checkload(load Draw Draw->PATH, Draw->PATH);
+ tk = checkload(load Tk Tk->PATH, Tk->PATH);
+ tkclient = checkload(load Tkclient Tkclient->PATH, Tkclient->PATH);
+ tkclient->init();
+ login = checkload(load Login Login->PATH, Login->PATH);
+ keyring = checkload(load Keyring Keyring->PATH, Keyring->PATH);
+ registries = checkload(load Registries Registries->PATH, Registries->PATH);
+ registries->init();
+
+ arg := load Arg Arg->PATH;
+ if(arg != nil){
+ arg->init(argv);
+ arg->setusage("usage: logon [-u user] [-p passwd] [-a loginaddr] command [arg...]]\n");
+ while((opt := arg->opt()) != 0){
+ case opt{
+ 'a' =>
+ loginaddr = arg->earg();
+ 'k' =>
+ signerpkhash = arg->earg();
+ 'u' =>
+ usr = arg->earg();
+ 'p' =>
+ passwd = arg->earg();
+ * =>
+ arg->usage();
+ }
+ }
+ argv = arg->argv();
+ arg = nil;
+ } else {
+ if(tl argv != nil)
+ sys->fprint(stderr(), "remotelogon: cannot load %s: %r; ignoring arguments\n", Arg->PATH);
+ argv = nil;
+ }
+ sys->pctl(Sys->FORKNS, nil);
+
+ sync := chan of (ref Keyring->Authinfo, string);
+ spawn logon(ctxt, sync);
+ (key, err) := <-sync;
+ if(key == nil)
+ raise "fail:" + err;
+ registry = nil;
+ servekeyfile(key);
+
+ errch := chan of string;
+ spawn exec(ctxt, argv, errch);
+ err = <-errch;
+ if (err != nil)
+ error(err);
+}
+
+# run in a separate process so that we keep the outer namespace unsullied by
+# mounted registries.
+logon(ctxt: ref Draw->Context, sync: chan of (ref Keyring->Authinfo, string))
+{
+ sys->pctl(Sys->NEWPGRP|Sys->FORKNS, nil);
+
+ {
+ logon1(ctxt, sync);
+ } exception e {
+ "fail:*" =>
+ sync <-= (nil, e[5:]);
+ }
+}
+
+logon1(ctxt: ref Draw->Context, sync: chan of (ref Keyring->Authinfo, string))
+{
+ if(ctxt == nil)
+ ctxt = tkclient->makedrawcontext();
+
+ (top, ctl) := tkclient->toplevel(ctxt, nil, nil, Tkclient->Plain);
+ tkclient->startinput(top, "kbd" :: "ptr" :: nil);
+ tkclient->onscreen(top, "onscreen");
+ stop := chan of int;
+ spawn tkclient->handler(top, stop);
+ if(usr != nil){
+ fa := loginaddr;
+ if(fa == nil)
+ fa = findloginresource(top, signerpkhash);
+ if(getauthinfo(top, fa, 0, sync)){
+ cleanup();
+ stop <-= 1;
+ exit;
+ }
+ }
+
+ cmd(top, "canvas .c -buffer none -bg #777777");
+ cmd(top, "pack .c -fill both -expand 1");
+ enter := makepanel(top);
+ for(;;) {
+ cmd(top, "focus .f.ue; update");
+ <-enter;
+ usr = cmd(top, ".f.ue get");
+ if(usr == nil) {
+ notice(top, "You must supply a user name to login");
+ continue;
+ }
+ passwd = cmd(top, ".f.pe get");
+
+ if(getauthinfo(top, loginaddr, int cmd(top, "variable newuser"), sync)){
+ cleanup();
+ stop <-= 1;
+ exit;
+ }
+ cmd(top, ".f.ue delete 0 end");
+ cmd(top, ".f.pe delete 0 end");
+ }
+}
+
+findloginresource(top: ref Tk->Toplevel, signerpkhash: string): string
+{
+ mountregistry();
+ attrs := ("resource", "login")::nil;
+ if(signerpkhash != nil)
+ attrs = ("pk", signerpkhash) :: attrs;
+ (svc, err) := registry.find(attrs);
+ if(svc == nil){
+ notice(top, "cannot find name of login server");
+ return nil;
+ }
+ return (hd svc).addr;
+}
+
+cleanup()
+{
+ # get rid of spurious mouse/kbd reading processes.
+ # XXX should probably implement "stop" ctl message in wmlib
+ sys->fprint(sys->open("/prog/"+string sys->pctl(0, nil)+"/ctl", Sys->OWRITE), "killgrp");
+}
+
+getauthinfo(top: ref Tk->Toplevel, addr: string, newuser: int, sync: chan of (ref Keyring->Authinfo, string)): int
+{
+ if(newuser)
+ if(createuser(top, usr, passwd, signerpkhash) == 0)
+ return 0;
+
+ if(addr == nil){
+ addr = findloginresource(top, signerpkhash);
+ if(addr == nil)
+ return 0;
+ }
+ (err, info) := login->login(usr, passwd, addr);
+ if(info == nil){
+ notice(top, "Login failed:\n" + err);
+ return 0;
+ }
+ sync <-= (info, nil);
+ return 1;
+}
+
+createuser(top: ref Tk->Toplevel, user, passwd: string, signerpkhash: string): int
+{
+ mountregistry();
+ attrs := ("resource", "createuser")::nil;
+ if(signerpkhash != nil)
+ attrs = ("signer", signerpkhash) :: attrs;
+ (svcs, err) := registry.find(attrs);
+ if(svcs == nil){
+ notice(top, "cannot find name of login server");
+ return 0;
+ }
+ addr := (hd svcs).addr;
+ (ok, c) := sys->dial(addr, nil);
+ if(ok == -1){
+ notice(top, sys->sprint("cannot dial %s: %r", addr));
+ return 0;
+ }
+ if(sys->mount(c.dfd, nil, "/tmp", Sys->MREPL, nil) == -1){
+ notice(top, sys->sprint("cannot mount %s: %r", addr));
+ return 0;
+ }
+ fd := sys->open("/tmp/createuser", Sys->OWRITE);
+ if(fd == nil){
+ notice(top, sys->sprint("cannot open createuser: %r"));
+ return 0;
+ }
+ if(sys->fprint(fd, "%q %q", user, passwd) <= 0){
+ notice(top, sys->sprint("cannot create user: %r"));
+ return 0;
+ }
+ signerpkhash = (hd svcs).attrs.get("signer");
+ return 1;
+}
+
+servekeyfile(info: ref Keyring->Authinfo)
+{
+ keys := "/usr/" + user() + "/keyring";
+ if(sys->bind("#s", keys, Sys->MBEFORE) == -1)
+ error(sys->sprint("cannot bind #s: %r"));
+ fio := sys->file2chan(keys, "default");
+ if(fio == nil)
+ error(sys->sprint("cannot make %s: %r", keys + "/default"));
+ sync := chan of int;
+ spawn infofile(fio, sync);
+ <-sync;
+
+ if(keyring->writeauthinfo(keys + "/default", info) == -1)
+ error(sys->sprint("cannot write %s: %r", keys + "/default"));
+}
+
+mountregistry()
+{
+ if(registry == nil)
+ registry = Registry.new("/mnt/registry");
+ if(registry == nil)
+ registry = Registry.connect(nil, nil, nil);
+ if(registry == nil){
+ sys->fprint(stderr(), "logon: cannot contact registry: %r\n");
+ raise "fail:no registry";
+ }
+}
+
+infofile(fileio: ref Sys->FileIO, sync: chan of int)
+{
+ sys->pctl(Sys->NEWPGRP|Sys->NEWFD|Sys->NEWNS, nil);
+ sync <-= 1;
+
+ infodata: array of byte;
+ for(;;) alt {
+ (off, nbytes, fid, rc) := <-fileio.read =>
+ if(rc == nil)
+ break;
+ if(off > len infodata)
+ off = len infodata;
+ rc <-= (infodata[off:], nil);
+
+ (off, data, fid, wc) := <-fileio.write =>
+ if(wc == nil)
+ break;
+
+ if(off != len infodata){
+ wc <-= (0, "cannot be rewritten");
+ } else {
+ nid := array[len infodata+len data] of byte;
+ nid[0:] = infodata;
+ nid[len infodata:] = data;
+ infodata = nid;
+ wc <-= (len data, nil);
+ }
+ }
+}
+
+exec(ctxt: ref Draw->Context, argv: list of string, errch: chan of string)
+{
+ sys->pctl(sys->NEWFD, 0 :: 1 :: 2 :: nil);
+ if(argv == nil)
+ argv = "/dis/wm/wm.dis" :: nil;
+ else {
+ sh := load Sh Sh->PATH;
+ if(sh != nil){
+ sh->run(ctxt, "{$* &}" :: argv);
+ errch <-= nil;
+ exit;
+ }
+ }
+ {
+ cmd := load Command hd argv;
+ if (cmd == nil) {
+ errch <-= sys->sprint("cannot load %s: %r", hd argv);
+ } else {
+ errch <-= nil;
+ spawn cmd->init(ctxt, argv);
+ }
+ }exception{
+ "fail:*" =>
+ exit;
+ }
+}
+
+makepanel(top: ref Tk->Toplevel): chan of string
+{
+ c := chan of string;
+ tk->namechan(top, c, "panelcmd");
+
+ for(i := 0; i < len cfg; i++)
+ cmd(top, cfg[i]);
+ centre(top, ".f");
+ return c;
+}
+
+centre(top: ref Tk->Toplevel, w: string): string
+{
+ ir := tk->rect(top, w, Tk->Required);
+ r := tk->rect(top, ".", 0);
+ org := Point(r.dx() / 2 - ir.dx() / 2, r.dy() / 3 - ir.dy() / 2);
+ if (org.y < 0)
+ org.y = 0;
+ if(org.x < 0)
+ org.x = 0;
+ return cmd(top, ".c create window "+string org.x+" "+string org.y+" -window "+w+" -anchor nw");
+}
+
+notice(top: ref Tk->Toplevel, message: string)
+{
+ if(top == nil)
+ error(message);
+ c := chan of string;
+ tk->namechan(top, c, "notecmd");
+ for(i := 0; i < len notecfg; i++)
+ cmd(top, notecfg[i]);
+ cmd(top, ".n.f.m configure -text '" + message);
+ id := centre(top, ".n");
+ cmd(top, "update");
+ <-c;
+ cmd(top, ".c delete " + id);
+ cmd(top, "destroy .n");
+ cmd(top, "update");
+}
+
+error(e: string)
+{
+ sys->fprint(stderr(), "remotelogon: %s\n", e);
+ raise "fail:error";
+}
+
+stderr(): ref Sys->FD
+{
+ return sys->fildes(2);
+}
+
+user(): string
+{
+ fd := sys->open("/dev/user", Sys->OREAD);
+ buf := array[8192] of byte;
+ if((n := sys->read(fd, buf, len buf)) > 0)
+ return string buf[0:n];
+ return "none";
+}
+
+cmd(top: ref Tk->Toplevel, s: string): string
+{
+ e := tk->cmd(top, s);
+ if (e != nil && e[0] == '!')
+ sys->fprint(stderr(), "remotelogon: tk error on '%s': %s\n", s, e);
+ return e;
+}