--- /n/sources/plan9/sys/src/cmd/cpu.c Sun Apr 14 16:33:20 2019 +++ /sys/src/cmd/cpu.c Tue Jun 23 00:00:00 2026 @@ -1,8 +1,9 @@ /* - * cpu.c - Make a connection to a cpu server + * cpu.c - Make an encrypted 9P connection to a cpu server, + * export my / as /mnt/term on cpu server. * - * Invoked by listen as 'cpu -R | -N service net netdir' - * by users as 'cpu [-h system] [-c cmd args ...]' + * Invoked by listen as 'cpu -R' + * by users as 'cpu [-h system] [-c cmd arg...]' */ #include @@ -12,33 +13,40 @@ #include #include -#define Maxfdata (16*1024) -#define MaxStr 128 +enum { + Maxfdata = 16*1024, + MaxStr = 512, +}; -void remoteside(void); +void catcher(void*, char*); void fatal(int, char*, ...); void lclnoteproc(int); -void rmtnoteproc(void); -void catcher(void*, char*); -void usage(void); -void writestr(int, char*, char*, int); int readstr(int, char*, int); +void remoteside(void); char *rexcall(int*, char*, char*); +void rmtnoteproc(void); +int setam(char*); int setamalg(char*); -char *keyspec = ""; +void usage(void); +void writestr(int, char*, char*, int); int notechan; int exportpid; char *system; -int cflag; int dbg; +int conndbg; +int tls; +int sslonly; char *user; char *patternfile; char *origargs; +char *keyspec = ""; +int slow; char *srvname = "ncpu"; char *exportfs = "/bin/exportfs"; -char *ealgs = "rc4_256 sha1"; +char *ealgs = "rc4_256 sha1"; /* for ssl only */ +char *tlscert = "/sys/lib/tls/cert.pem"; /* message size for exportfs; may be larger so we can do big graphics in CPU window */ int msgsize = Maxfdata+IOHDRSZ; @@ -67,17 +75,24 @@ char *p9authproto = "p9any"; -int setam(char*); - void usage(void) { - fprint(2, "usage: cpu [-h system] [-u user] [-a authmethod] " - "[-e 'crypt hash'] [-k keypattern] [-P patternfile] " - "[-c cmd arg ...]\n"); + fprint(2, "usage: cpu [-h system] [-sStv] [-u user] " + "[-a authmethod] [-e 'crypt hash'] [-k keypattern] " + "[-P patternfile] [-c cmd arg ...]\n" + " or\tcpu [-t] [-C cert] -R\n" ); exits("usage"); } +void +alarmcatcher(void*, char *text) +{ + if (strstr(text, "alarm") != nil) + noted(NCONT); + noted(NDFLT); +} + /* * reading /proc/pid/args yields either "name args" or "name [display args]", * so return only args or display args. @@ -87,7 +102,7 @@ { int fd, n; char *lp, *rp; - char buf[256]; + char buf[64]; snprint(buf, sizeof buf, "#p/%d/args", getpid()); if((fd = open(buf, OREAD)) < 0) @@ -117,7 +132,7 @@ { int fd; char *cmdname; - char buf[128]; + char buf[64]; va_list arg; va_start(arg, fmt); @@ -133,24 +148,57 @@ free(cmdname); } -void -main(int argc, char **argv) +static void +execexportfs(void) { - char dat[MaxStr], buf[MaxStr], cmd[MaxStr], *p, *err; - int ac, fd, ms, data; + int ac; + char buf[32]; char *av[10]; - quotefmtinstall(); - origargs = procgetname(); - /* see if we should use a larger message size */ + snprint(buf, sizeof buf, "%d", msgsize); + ac = 0; + av[ac++] = exportfs; + av[ac++] = "-m"; + av[ac++] = buf; + if(dbg) + av[ac++] = "-d"; + if(patternfile != nil){ + av[ac++] = "-P"; + av[ac++] = patternfile; + } + av[ac] = nil; + exec(exportfs, av); + /* returns only if exec fails */ +} + +/* see if we should use a larger message size */ +static void +setmsgsize(void) +{ + int fd, ms; + fd = open("/dev/draw", OREAD); - if(fd > 0){ + if(fd >= 0){ ms = iounit(fd); if(msgsize < ms+IOHDRSZ) msgsize = ms+IOHDRSZ; close(fd); } +} +void +main(int argc, char **argv) +{ + char wdir[1024], buf[MaxStr], cmd[MaxStr]; + char *p, *err; + int data, algoverride; + + quotefmtinstall(); + origargs = procgetname(); + setmsgsize(); + + cmd[0] = '\0'; + algoverride = 0; user = getuser(); if(user == nil) fatal(1, "can't read user name"); @@ -160,73 +208,95 @@ if(setam(p) < 0) fatal(0, "unknown auth method %s", p); break; + case 'c': + cmd[0] = '!'; + cmd[1] = '\0'; + while(p = ARGF()) { + if (strlen(cmd) + 1 + strlen(p) + 1 > sizeof cmd) + fatal(0, "-c command too long", p); + strcat(cmd, " "); + strcat(cmd, p); + } + break; + case 'd': + dbg++; + fprint(2, "cpu: debugging on\n"); + break; case 'e': ealgs = EARGF(usage()); if(*ealgs == 0 || strcmp(ealgs, "clear") == 0) ealgs = nil; - break; - case 'd': - dbg++; + algoverride++; break; case 'f': /* ignored but accepted for compatibility */ break; - case 'R': /* From listen */ - remoteside(); - break; case 'h': system = EARGF(usage()); break; - case 'c': - cflag++; - cmd[0] = '!'; - cmd[1] = '\0'; - while(p = ARGF()) { - strcat(cmd, " "); - strcat(cmd, p); - } - break; case 'k': keyspec = smprint("%s %s", keyspec, EARGF(usage())); break; case 'P': patternfile = EARGF(usage()); break; + case 'R': /* From listen; must be last option */ + remoteside(); + break; + case 's': + sslonly = 1; + break; + case 'S': + slow = 1; + break; + case 'C': + tlscert = EARGF(usage()); /* for server side of -t */ + /* fall through */ + case 't': + tls = 1; /* no fallback to ssl */ + break; case 'u': user = EARGF(usage()); keyspec = smprint("%s user=%s", keyspec, user); break; + case 'v': + conndbg = 1; + break; default: usage(); }ARGEND; - if(argc != 0) usage(); + /* dial remote system */ if(system == nil) { - p = getenv("cpu"); - if(p == 0) + system = getenv("cpu"); + if(system == nil) fatal(0, "set $cpu"); - system = p; } - + if (tls) + srvname = "cpu-tls"; + notify(alarmcatcher); if(err = rexcall(&data, system, srvname)) fatal(1, "%s: %s", err, system); - procsetname("%s", origargs); /* Tell the remote side the command to execute and where our working directory is */ - if(cflag) + procsetname("%s", origargs); + if(cmd[0] != '\0') writestr(data, cmd, "command", 0); - if(getwd(dat, sizeof(dat)) == 0) + if(getwd(wdir, sizeof wdir) == nil) writestr(data, "NO", "dir", 0); else - writestr(data, dat, "dir", 0); + writestr(data, wdir, "dir", 0); - /* start up a process to pass along notes */ + /* + * start up a process to pass along notes. + * forks and only child returns. + */ lclnoteproc(data); - /* + /* * Wait for the other end to execute and start our file service * of /mnt/term */ @@ -237,24 +307,11 @@ exits(buf); } - /* Begin serving the gnot namespace */ + /* Begin serving the gnot namespace over net connection */ close(0); dup(data, 0); close(data); - - sprint(buf, "%d", msgsize); - ac = 0; - av[ac++] = exportfs; - av[ac++] = "-m"; - av[ac++] = buf; - if(dbg) - av[ac++] = "-d"; - if(patternfile != nil){ - av[ac++] = "-P"; - av[ac++] = patternfile; - } - av[ac] = nil; - exec(exportfs, av); + execexportfs(); /* returns only if exec fails */ fatal(1, "starting exportfs"); } @@ -285,7 +342,8 @@ void remoteside(void) { - char user[MaxStr], home[MaxStr], buf[MaxStr], xdir[MaxStr], cmd[MaxStr]; + char *authmeth, *home; + char user[MaxStr], buf[MaxStr], xdir[MaxStr], cmd[MaxStr]; int i, n, fd, badchdir, gotcmd; rfork(RFENVG); @@ -296,25 +354,33 @@ n = readstr(fd, cmd, sizeof(cmd)); if(n < 0) fatal(1, "authenticating"); - if(setamalg(cmd) < 0){ + authmeth = strdup(cmd); + werrstr(""); + if(setamalg(cmd) < 0){ /* modifies cmd */ writestr(fd, "unsupported auth method", nil, 0); - fatal(1, "bad auth method %s", cmd); + fatal(1, "bad auth method %s", authmeth); } else writestr(fd, "", "", 1); + free(authmeth); - fd = (*am->sf)(fd, user); + /* + * authenticate, switch to user, begin encrypting 9P session on stdin + * (network connection) using new fd. + */ + fd = (*am->sf)(fd, user); /* user assumed Maxstr long */ if(fd < 0) fatal(1, "srvauth"); /* Set environment values for the user */ putenv("user", user); - sprint(home, "/usr/%s", user); + home = smprint("/usr/%s", user); putenv("home", home); /* Now collect invoking cpu's current directory or possibly a command */ gotcmd = 0; + memset(xdir, '\0', sizeof xdir); if(readstr(fd, xdir, sizeof(xdir)) < 0) - fatal(1, "dir/cmd"); + fatal(1, "dir/cmd %s", xdir); if(xdir[0] == '!') { strcpy(cmd, &xdir[1]); gotcmd = 1; @@ -331,6 +397,7 @@ badchdir = 1; chdir(home); } + free(home); /* Start the gnot serving its namespace */ writestr(fd, "FS", "FS", 0); @@ -340,16 +407,21 @@ if(n != 2 || buf[0] != 'O' || buf[1] != 'K') exits("remote tree"); - /* make sure buffers are big by doing fversion explicitly; pick a huge number; other side will trim */ + /* + * make sure buffers are big by doing fversion explicitly: + * pick a huge number, other side will trim. + */ strcpy(buf, VERSION9P); if(fversion(fd, 64*1024, buf, sizeof buf) < 0) exits("fversion failed"); if(mount(fd, -1, "/mnt/term", MCREATE|MREPL, "") < 0) exits("mount failed"); - close(fd); - /* the remote noteproc uses the mount so it must follow it */ + /* + * the remote noteproc uses the mount so it must follow the mount. + * forks and only child returns. + */ rmtnoteproc(); for(i = 0; i < 3; i++) @@ -372,31 +444,44 @@ } char* -rexcall(int *fd, char *host, char *service) +rexcallsec(int *fd, char *host, char *service, int waitms) { - char *na; - char dir[MaxStr]; - char err[ERRMAX]; - char msg[MaxStr]; - int n; + char *na, *msg; + char dir[NETPATHLEN], err[ERRMAX]; + int n, oalarm; + if (slow) + waitms *= 10; na = netmkaddr(host, 0, service); + if(dbg) + print("dialing %s\n", na); procsetname("dialing %s", na); - if((*fd = dial(na, 0, dir, 0)) < 0) + oalarm = alarm(waitms); + *fd = dial(na, 0, dir, 0); + if(*fd < 0) { + alarm(oalarm); return "can't dial"; + } + USED(dir); /* negotiate authentication mechanism */ if(ealgs != nil) - snprint(msg, sizeof(msg), "%s %s", am->name, ealgs); + msg = smprint("%s %s", am->name, ealgs); else - snprint(msg, sizeof(msg), "%s", am->name); + msg = smprint("%s", am->name); procsetname("writing %s", msg); + if (dbg) + fprint(2, "auth write ealgs `%s'...", msg); writestr(*fd, msg, negstr, 0); + free(msg); + procsetname("awaiting auth method"); + memset(err, 0, sizeof err); n = readstr(*fd, err, sizeof err); - if(n < 0) - return negstr; - if(*err){ + if(*err || n < 0){ + alarm(oalarm); + if (dbg) + fprint(2, "err %s...", err); werrstr(err); return negstr; } @@ -404,11 +489,53 @@ /* authenticate */ procsetname("%s: auth via %s", origargs, am->name); *fd = (*am->cf)(*fd); - if(*fd < 0) + alarm(oalarm); + if(*fd < 0) { + if (dbg) + fprint(2, "%r..."); return "can't authenticate"; + } return 0; } +/* usually try tls first; if tls is not required, try ssl upon failure */ +char* +rexcall(int *fd, char *host, char *service) +{ + int savedtls; + char *err; + + *fd = -1; + if (sslonly) + err = "ssl only"; + else { + savedtls = tls; + tls = 1; + if (conndbg) + fprint(2, "tls..."); + /* be tolerant of slow systems */ + err = rexcallsec(fd, host, "cpu-tls", 20*1000); + if (err) { + fprint(2, "cpu: rexcall to %s!cpu-tls: %s: %r\n", + host, err); + close(*fd); + *fd = -1; + } + tls = savedtls; + } + if (err && !tls) { /* failed & tls not required? try ssl */ + if (conndbg) + fprint(2, "%s...", service); + err = rexcallsec(fd, host, service, 30*1000); + if (!err && *fd >= 0) + fprint(2, "%s: caution using ssl with %s, not tls\n", + argv0, ealgs); + } + if (conndbg) + fprint(2, "\n"); + return err; +} + void writestr(int fd, char *str, char *thing, int ignore) { @@ -420,6 +547,10 @@ fatal(1, "writing network: %s", thing); } +/* + * read up to len bytes into str from fd, stopping at a NUL. + * if not NUL-terminated, returns -1. + */ int readstr(int fd, char *str, int len) { @@ -441,7 +572,7 @@ int i; char *p; - n--; /* room for \0 */ + n--; /* room for \0 */ p = buf; for(i=0; i sizeof buf / 2 - 1) + n = sizeof buf / 2 - 1; for(i = 0; i < n; i++) sprint(buf+2*i, "%2.2ux", p[i]); syslog(0, "cpu", buf); @@ -605,15 +780,14 @@ { uchar key[16]; uchar digest[SHA1dlen]; - char fromclientsecret[21]; - char fromserversecret[21]; + char fromclientsecret[21], fromserversecret[21]; int i; AuthInfo *ai; ai = auth_proxy(0, nil, "proto=%q role=server %s", p9authproto, keyspec); if(ai == nil) return -1; - if(auth_chuid(ai, nil) < 0) + if(auth_chuid(ai, nil) < 0) /* switch to user in keyspec */ return -1; strecpy(user, user+MaxStr, ai->cuid); memset(key, 0, sizeof key); @@ -627,8 +801,10 @@ srand(truerand()); for(i = 0; i < 4; i++) key[i+12] = rand(); + procsetname("reading p9 key"); if(readn(fd, key, 4) != 4) return -1; + procsetname("writing p9 key"); if(write(fd, key+12, 4) != 4) return -1; @@ -638,9 +814,13 @@ mksecret(fromserversecret, digest+10); /* set up encryption */ - i = pushssl(fd, ealgs, fromserversecret, fromclientsecret, nil); + procsetname("push%s server", tls? "tls": "ssl"); + if (tls) + i = pushtlsserver(fd, tlscert); + else + i = pushssl(fd, ealgs, fromserversecret, fromclientsecret, nil); if(i < 0) - werrstr("can't establish ssl connection: %r"); + werrstr("can't establish %s connection: %r", tls? "tls": "ssl"); return i; } @@ -702,24 +882,17 @@ syslog(0, "cpu", "cpu -R: can't open %s", rmtnotefile); _exits(0); } - - for(;;){ - n = read(fd, buf, sizeof(buf)-1); - if(n <= 0){ - postnote(PNGROUP, pid, "hangup"); - _exits(0); - } + while ((n = read(fd, buf, sizeof(buf)-1)) > 0) { buf[n] = 0; postnote(PNGROUP, pid, buf); } + postnote(PNGROUP, pid, "hangup"); + _exits(0); } /* original proc waits for shell proc to die and kills note proc */ - for(;;){ - n = waitpid(); - if(n < 0 || n == pid) - break; - } + while ((n = waitpid()) >= 0 && n != pid) + ; postnote(PNPROC, notepid, "kill"); _exits(0); } @@ -926,11 +1099,10 @@ notefs(int fd) { uchar buf[IOHDRSZ+Maxfdata]; - int i, n, ncpunote; + int doreply, i, n, ncpunote; Fcall f; Qid wqid[MAXWELEM]; Fid *fid, *nfid; - int doreply; rfork(RFNOTEG); fmtinstall('F', fcallfmt); @@ -1086,6 +1258,29 @@ noted(NCONT); } +/* attach note in notebuf to nfs list, forward it to npipe */ +static void +qnote(int npipe) +{ + Note *np; + + /* attach note to nfs list, forward it */ + np = mallocz(sizeof(Note), 1); + if(np == nil) + return; + strncpy(np->msg, notebuf, sizeof np->msg); + + lock(&nfs); + if(nfs.nfirst == nil) + nfs.nfirst = np; + else + nfs.nlast->next = np; + nfs.nlast = np; + unlock(&nfs); + + kick(npipe); +} + /* * mount in /dev a note file for the remote side to read. */ @@ -1093,7 +1288,6 @@ lclnoteproc(int netfd) { Waitmsg *w; - Note *np; int pfd[2]; int pid; @@ -1102,7 +1296,7 @@ return; } - /* new proc mounts and returns to start exportfs */ + /* new proc mounts pipe and returns to start exportfs */ switch(pid = rfork(RFPROC|RFNAMEG|RFFDG|RFMEM)){ default: exportpid = pid; @@ -1110,7 +1304,7 @@ case -1: fprint(2, "cpu: can't start note proc: rfork: %r\n"); return; - case 0: + case 0: /* child process */ close(pfd[0]); if(mount(pfd[1], -1, "/dev", MBEFORE, "") < 0) fprint(2, "cpu: can't mount note proc: %r\n"); @@ -1138,28 +1332,17 @@ *notebuf = 0; free(w); w = wait(); - if(w == nil) { - if(*notebuf == 0) - break; - np = mallocz(sizeof(Note), 1); - if(np != nil){ - strcpy(np->msg, notebuf); - lock(&nfs); - if(nfs.nfirst == nil) - nfs.nfirst = np; - else - nfs.nlast->next = np; - nfs.nlast = np; - unlock(&nfs); - kick(pfd[0]); - } - unlock(&nfs); - } else if(w->pid == exportpid) + if(w == nil) { /* no child proc's left? */ + if(*notebuf == 0) /* no note pending? */ + break; /* we're done */ + qnote(pfd[0]); /* queue note, forward it */ + // unlock(&nfs); /* TODO: this makes no sense */ + } else if(w->pid == exportpid) /* exportfs exited? */ break; } if(w == nil) exits(nil); - exits(0); -/* exits(w->msg); */ +/* exits(w->msg); /* notefs()'s or exportfs's exit status */ + exits(nil); } --- /n/sources/plan9/sys/man/1/cpu Sun Feb 28 06:10:47 2016 +++ /sys/man/1/cpu Tue Jun 23 00:00:00 2026 @@ -1,6 +1,6 @@ .TH CPU 1 .SH NAME -cpu \- connection to CPU server +cpu \- secure connection to CPU server .SH SYNOPSIS .B cpu [ @@ -10,6 +10,8 @@ .B -u .I user ] [ +.B -sStv +] [ .B -a .I auth-method ] [ @@ -28,10 +30,12 @@ .PP .B cpu [ -.B -R -| -.B -O +.B -C +.I cert +] [ +.B -t ] +.B -R .SH DESCRIPTION .I Cpu starts an @@ -97,7 +101,7 @@ .PP The .B -a -command allows the user to specify the authentication mechanism used +option allows the user to specify the authentication mechanism used when connecting to the remote system. The two possibilities for .I auth-method are: @@ -124,10 +128,24 @@ user. .PD .PP +The connection will be encrypted. +.I Cpu +will attempt to establish a TLS connection first, to service +.BR cpu-tls . +TLS will negotiate encryption algorithms common to client and server. +If a TLS connection fails, and in the absence of +.BR -t , +.I cpu +will attempt a less-secure SSLv2 connection to service +.BR ncpu . +With +.BR -s , +only SSL will be attempted +(forces compatibility with old systems). The .B -e option specifies an encryption and/or hash algorithm to -use for the connection. If both are specified, they must +use for any SSLv2 connection. If both are specified, they must be space separated and comprise a single argument, so they must be quoted if in a shell command. The default is .L rc4_256 @@ -135,16 +153,22 @@ .L sha1 hashing. See .IR ssl (3) -for details on possible algorithms. The argument +for details on possible (weak) algorithms. +The argument .L clear -specifies no encryption algorithm and can be used to talk -to older versions of the +specifies no SSLv2 encryption, primarily for debugging. +The +.B -v +option prints connection attempts, identifying the protocol used. +The +.B -S +option makes .I cpu -service. +much more tolerant of a slow remote system. .PP The .B -k -flag specifies a key pattern to use to restrict the keys +option specifies a key pattern to use to restrict the keys selected by the .I auth_proxy call used for authentication. @@ -165,18 +189,30 @@ .PP The .B -R -flag causes +option causes .I cpu -to run the server (remote) side of the protocol. +to run the server (remote) side of the protocol, +and must be the final option. It is run from service files such as -.BR /bin/service/tcp17010 . -The -.B -O -flag is similar but simulates the pre-9P2000 version -of the -.I cpu -protocol. +.BR /bin/service/tcp1701[04] . +With +.BR -t , +.I cpu +will expect a TLS connection; +without it, +.I cpu +will expect an SSLv2 connection. +.B -C +names an alternate +.IR pem (8)-encoded +.I cert +for TLS, +and implies +.BR -t . .SH FILES +.B /sys/lib/tls/cert.pem + default certificate +.PP The name space of the terminal side of the .I cpu command is mounted, via @@ -191,7 +227,9 @@ .SH SEE ALSO .IR rc (1) , .IR rio (1) , -.IR exportfs (4) +.IR tls (3), +.IR exportfs (4), +.IR drawterm (8) .SH BUGS Binds and mounts done after the terminal .B lib/profile @@ -202,3 +240,5 @@ option to `log in' as another user, be aware that resources in the local name space will be made available to that user. +.PP +SSLv2 is deprecated. --- /n/sources/plan9/lib/ndb/common Fri Dec 13 05:23:18 2019 +++ /lib/ndb/common Tue Jun 23 00:00:00 2026 @@ -291,6 +291,7 @@ tcp=ncpu port=17010 tcp=cpu port=17010 # about to rename ncpu to cpu # tcp=cpu port=17013 # phasing out this obsolete protocol used by very old drawterm +tcp=cpu-tls port=17014 # current protocol via tls tcp=glenglenda1 port=17020 tcp=glenglenda2 port=17021 tcp=glenglenda3 port=17022