/* * cpu.c - Make a connection to a cpu server * * Invoked by listen as 'cpu -R | -N service net netdir' * by users as 'cpu [-h system] [-c cmd args ...]' */ #include #include #include #include #include #include #include "args.h" #include "drawterm.h" #define Maxfdata 8192 #define MaxStr 128 static void fatal(int, char*, ...); static void catcher(void*, char*); static void usage(void); static void writestr(int, char*, char*, int); static int readstr(int, char*, int); static char *rexcall(int*, char*, char*); static int setamalg(char*); static char *keyspec = ""; static AuthInfo *p9any(int); static int notechan; #define system csystem static char *system; static int cflag; int dbg; static char *srvname = "ncpu"; static char *ealgs = "rc4_256 sha1"; /* message size for exportfs; may be larger so we can do big graphics in CPU window */ static int msgsize = Maxfdata+IOHDRSZ; /* authentication mechanisms */ static int netkeyauth(int); static int netkeysrvauth(int, char*); static int p9auth(int); static int srvp9auth(int, char*); static int noauth(int); static int srvnoauth(int, char*); char *authserver; typedef struct AuthMethod AuthMethod; struct AuthMethod { char *name; /* name of method */ int (*cf)(int); /* client side authentication */ int (*sf)(int, char*); /* server side authentication */ } authmethod[] = { { "p9", p9auth, srvp9auth,}, { "netkey", netkeyauth, netkeysrvauth,}, // { "none", noauth, srvnoauth,}, { nil, nil} }; AuthMethod *am = authmethod; /* default is p9 */ char *p9authproto = "p9any"; int setam(char*); void exits(char *s) { print("\ngoodbye\n"); for(;;) osyield(); } void usage(void) { fprint(2, "usage: drawterm [-a authserver] [-c cpuserver] [-s secstore] [-u user]\n"); exits("usage"); } int fdd; int mountfactotum(void) { int fd; if((fd = dialfactotum()) < 0) return; if(sysmount(fd, -1, "/mnt/factotum", MREPL, "") < 0){ fprint(2, "mount factotum: %r\n"); return; } if((fd = open("/mnt/factotum/ctl", OREAD)) < 0){ fprint(2, "open /mnt/factotum/ctl: %r\n"); return; } close(fd); return 0; } void cpumain(int argc, char **argv) { char dat[MaxStr], buf[MaxStr], cmd[MaxStr], *p, *err, *secstoreserver, *s; int fd, ms, data; /* see if we should use a larger message size */ fd = open("/dev/draw", OREAD); if(fd > 0){ ms = iounit(fd); if(msgsize < ms+IOHDRSZ) msgsize = ms+IOHDRSZ; close(fd); } user = getenv("USER"); if(user == nil) user = readcons("user", nil, 0); secstoreserver = nil; ARGBEGIN{ case 'a': authserver = EARGF(usage()); break; case 'e': ealgs = EARGF(usage()); if(*ealgs == 0 || strcmp(ealgs, "clear") == 0) ealgs = nil; break; case 'd': dbg++; break; case 'c': system = EARGF(usage()); break; /* case 'c': cflag++; cmd[0] = '!'; cmd[1] = '\0'; while(p = ARGF()) { strcat(cmd, " "); strcat(cmd, p); } break; */ case 'k': keyspec = EARGF(usage()); break; case 'u': user = EARGF(usage()); break; case 's': secstoreserver = EARGF(usage()); break; default: usage(); }ARGEND; if(mountfactotum() < 0){ if(secstoreserver == nil) secstoreserver = authserver; if(havesecstore(secstoreserver, user)){ s = secstorefetch(secstoreserver, user, nil); if(s){ if(strlen(s) >= sizeof secstorebuf) panic("secstore data too big"); strcpy(secstorebuf, s); } } } if(argc != 0) usage(); if(system == nil) { p = getenv("cpu"); if(p == 0) fatal(0, "set $cpu"); system = p; } if(err = rexcall(&data, system, srvname)) fatal(1, "%s: %s", err, system); /* Tell the remote side the command to execute and where our working directory is */ if(cflag) writestr(data, cmd, "command", 0); if(getcwd(dat, sizeof(dat)) == 0) writestr(data, "NO", "dir", 0); else writestr(data, dat, "dir", 0); /* * Wait for the other end to execute and start our file service * of /mnt/term */ if(readstr(data, buf, sizeof(buf)) < 0) fatal(1, "waiting for FS: %r"); if(strncmp("FS", buf, 2) != 0) { print("remote cpu: %s", buf); exits(buf); } if(readstr(data, buf, sizeof buf) < 0) fatal(1, "waiting for remote export: %r"); if(strcmp(buf, "/") != 0){ print("remote cpu: %s" , buf); exits(buf); } write(data, "OK", 2); /* Begin serving the gnot namespace */ exportfs(data, msgsize); fatal(1, "starting exportfs"); } void fatal(int syserr, char *fmt, ...) { Fmt f; char *str; va_list arg; fmtstrinit(&f); fmtprint(&f, "cpu: "); va_start(arg, fmt); fmtvprint(&f, fmt, arg); va_end(arg); if(syserr) fmtprint(&f, ": %r"); fmtprint(&f, "\n"); str = fmtstrflush(&f); write(2, str, strlen(str)); exits(str); } char *negstr = "negotiating authentication method"; char bug[256]; char* rexcall(int *fd, char *host, char *service) { char *na; char dir[MaxStr]; char err[ERRMAX]; char msg[MaxStr]; int n; na = netmkaddr(host, "tcp", "17010"); if((*fd = dial(na, 0, dir, 0)) < 0) return "can't dial"; /* negotiate authentication mechanism */ if(ealgs != nil) snprint(msg, sizeof(msg), "%s %s", am->name, ealgs); else snprint(msg, sizeof(msg), "%s", am->name); writestr(*fd, msg, negstr, 0); n = readstr(*fd, err, sizeof err); if(n < 0) return negstr; if(*err){ werrstr(err); return negstr; } /* authenticate */ *fd = (*am->cf)(*fd); if(*fd < 0) return "can't authenticate"; return 0; } void writestr(int fd, char *str, char *thing, int ignore) { int l, n; l = strlen(str); n = write(fd, str, l+1); if(!ignore && n < 0) fatal(1, "writing network: %s", thing); } int readstr(int fd, char *str, int len) { int n; while(len) { n = read(fd, str, 1); if(n < 0) return -1; if(*str == '\0') return 0; str++; len--; } return -1; } static int readln(char *buf, int n) { int i; char *p; n--; /* room for \0 */ p = buf; for(i=0; isecret, ai->nsecret); if(ealgs == nil) return fd; /* exchange random numbers */ srand(truerand()); for(i = 0; i < 4; i++) key[i] = rand(); if(write(fd, key, 4) != 4) return -1; if(readn(fd, key+12, 4) != 4) return -1; /* scramble into two secrets */ sha1(key, sizeof(key), digest, nil); mksecret(fromclientsecret, digest); mksecret(fromserversecret, digest+10); /* set up encryption */ i = pushssl(fd, ealgs, fromclientsecret, fromserversecret, nil); if(i < 0) werrstr("can't establish ssl connection: %r"); return i; } int authdial(char *net, char *dom) { int fd; fd = dial(netmkaddr(authserver, "tcp", "567"), 0, 0, 0); //print("authdial %d\n", fd); return fd; } static int getastickets(Ticketreq *tr, char *trbuf, char *tbuf) { int asfd, rv; char *dom; dom = tr->authdom; asfd = authdial(nil, dom); if(asfd < 0) return -1; rv = _asgetticket(asfd, trbuf, tbuf); close(asfd); return rv; } static int mkserverticket(Ticketreq *tr, char *authkey, char *tbuf) { int i; Ticket t; if(strcmp(tr->authid, tr->hostid) != 0) return -1; memset(&t, 0, sizeof(t)); memmove(t.chal, tr->chal, CHALLEN); strcpy(t.cuid, tr->uid); strcpy(t.suid, tr->uid); for(i=0; i= 0) return 0; return mkserverticket(tr, key, tbuf); } /* * prompt user for a key. don't care about memory leaks, runs standalone */ static Attr* promptforkey(char *params) { char *v; int fd; Attr *a, *attr; char *def; fd = open("/dev/cons", ORDWR); if(fd < 0) sysfatal("opening /dev/cons: %r"); attr = _parseattr(params); fprint(fd, "\n!Adding key:"); for(a=attr; a; a=a->next) if(a->type != AttrQuery && a->name[0] != '!') fprint(fd, " %q=%q", a->name, a->val); fprint(fd, "\n"); for(a=attr; a; a=a->next){ v = a->name; if(a->type != AttrQuery || v[0]=='!') continue; def = nil; if(strcmp(v, "user") == 0) def = getuser(); a->val = readcons(v, def, 0); if(a->val == nil) sysfatal("user terminated key input"); a->type = AttrNameval; } for(a=attr; a; a=a->next){ v = a->name; if(a->type != AttrQuery || v[0]!='!') continue; def = nil; if(strcmp(v+1, "user") == 0) def = getuser(); a->val = readcons(v+1, def, 1); if(a->val == nil) sysfatal("user terminated key input"); a->type = AttrNameval; } fprint(fd, "!\n"); close(fd); return attr; } /* * send a key to the mounted factotum */ static int sendkey(Attr *attr) { int fd, rv; char buf[1024]; fd = open("/mnt/factotum/ctl", ORDWR); if(fd < 0) sysfatal("opening /mnt/factotum/ctl: %r"); rv = fprint(fd, "key %A\n", attr); read(fd, buf, sizeof buf); close(fd); return rv; } int askuser(char *params) { Attr *attr; fmtinstall('A', _attrfmt); attr = promptforkey(params); if(attr == nil) sysfatal("no key supplied"); if(sendkey(attr) < 0) sysfatal("sending key to factotum: %r"); return 0; } AuthInfo* p9anyfactotum(int fd, int afd) { return auth_proxy(fd, askuser, "proto=p9any role=client %s", keyspec); } AuthInfo* p9any(int fd) { char buf[1024], buf2[1024], cchal[CHALLEN], *bbuf, *p, *dom, *u; char *pass; char tbuf[TICKETLEN+TICKETLEN+AUTHENTLEN], trbuf[TICKREQLEN]; char authkey[DESKEYLEN]; Authenticator auth; int afd, i, v2, n; Ticketreq tr; Ticket t; AuthInfo *ai; if((afd = open("/mnt/factotum/ctl", ORDWR)) >= 0) return p9anyfactotum(fd, afd); if((n = readstr(fd, buf, sizeof buf)) < 0) fatal(1, "cannot read p9any negotiation"); bbuf = buf; v2 = 0; if(strncmp(buf, "v.2 ", 4) == 0){ v2 = 1; bbuf += 4; } if(p = strchr(bbuf, ' ')) *p = 0; p = bbuf; if((dom = strchr(p, '@')) == nil) fatal(1, "bad p9any domain"); *dom++ = 0; if(strcmp(p, "p9sk1") != 0) fatal(1, "server did not offer p9sk1"); sprint(buf2, "%s %s", p, dom); if(write(fd, buf2, strlen(buf2)+1) != strlen(buf2)+1) fatal(1, "cannot write user/domain choice in p9any"); if(v2){ if((n = readstr(fd, buf, sizeof buf)) != 3) fatal(1, "cannot read OK in p9any"); if(memcmp(buf, "OK\0", 3) != 0) fatal(1, "did not get OK in p9any"); } for(i=0; isecret = mallocz(8, 1); des56to64((uchar*)t.key, ai->secret); ai->nsecret = 8; ai->suid = strdup(t.suid); ai->cuid = strdup(t.cuid); memset(authkey, 0, sizeof authkey); return ai; } static int noauth(int fd) { ealgs = nil; return fd; } static int srvnoauth(int fd, char *user) { strecpy(user, user+MaxStr, getuser()); ealgs = nil; return fd; } void loghex(uchar *p, int n) { char buf[100]; int i; for(i = 0; i < n; i++) sprint(buf+2*i, "%2.2ux", p[i]); // syslog(0, "cpu", buf); } static int srvp9auth(int fd, char *user) { return -1; } /* * set authentication mechanism */ int setam(char *name) { for(am = authmethod; am->name != nil; am++) if(strcmp(am->name, name) == 0) return 0; am = authmethod; return -1; } /* * set authentication mechanism and encryption/hash algs */ int setamalg(char *s) { ealgs = strchr(s, ' '); if(ealgs != nil) *ealgs++ = 0; return setam(s); }