123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #include <u.h>
- #include <libc.h>
- #include <bio.h>
- #include <auth.h>
- #include <libsec.h>
- #include "../ip/telnet.h"
- /* console state (for consctl) */
- typedef struct Consstate Consstate;
- struct Consstate{
- int raw;
- int hold;
- };
- Consstate *cons;
- int notefd; /* for sending notes to the child */
- int noproto; /* true if we shouldn't be using the telnet protocol */
- int trusted; /* true if we need not authenticate - current user
- is ok */
- int nonone = 1; /* don't allow none logins */
- int noworldonly; /* only noworld accounts */
- enum
- {
- Maxpath= 256,
- Maxuser= 64,
- Maxvar= 32,
- };
- /* input and output buffers for network connection */
- Biobuf netib;
- Biobuf childib;
- char remotesys[Maxpath]; /* name of remote system */
- int alnum(int);
- int conssim(void);
- int fromchild(char*, int);
- int fromnet(char*, int);
- int termchange(Biobuf*, int);
- int termsub(Biobuf*, uint8_t*, int);
- int xlocchange(Biobuf*, int);
- int xlocsub(Biobuf*, uint8_t*, int);
- int challuser(char*);
- int noworldlogin(char*);
- void* share(uint32_t);
- int doauth(char*);
- #define TELNETLOG "telnet"
- void
- logit(char *fmt, ...)
- {
- va_list arg;
- char buf[8192];
- va_start(arg, fmt);
- vseprint(buf, buf + sizeof(buf) / sizeof(*buf), fmt, arg);
- va_end(arg);
- syslog(0, TELNETLOG, "(%s) %s", remotesys, buf);
- }
- void
- getremote(char *dir)
- {
- int fd, n;
- char remfile[Maxpath];
- sprint(remfile, "%s/remote", dir);
- fd = open(remfile, OREAD);
- if(fd < 0)
- strcpy(remotesys, "unknown2");
- n = read(fd, remotesys, sizeof(remotesys)-1);
- if(n>0)
- remotesys[n-1] = 0;
- else
- strcpy(remotesys, remfile);
- close(fd);
- }
- void
- main(int argc, char *argv[])
- {
- char buf[1024];
- int fd;
- char user[Maxuser];
- int tries = 0;
- int childpid;
- int n, eofs;
- memset(user, 0, sizeof(user));
- ARGBEGIN {
- case 'n':
- opt[Echo].local = 1;
- noproto = 1;
- break;
- case 'p':
- noproto = 1;
- break;
- case 'a':
- nonone = 0;
- break;
- case 't':
- trusted = 1;
- strncpy(user, getuser(), sizeof(user)-1);
- break;
- case 'u':
- strncpy(user, ARGF(), sizeof(user)-1);
- break;
- case 'd':
- debug = 1;
- break;
- case 'N':
- noworldonly = 1;
- break;
- } ARGEND
- if(argc)
- getremote(argv[argc-1]);
- else
- strcpy(remotesys, "unknown");
- /* options we need routines for */
- opt[Term].change = termchange;
- opt[Term].sub = termsub;
- opt[Xloc].sub = xlocsub;
- /* setup default telnet options */
- if(!noproto){
- send3(1, Iac, Will, opt[Echo].code);
- send3(1, Iac, Do, opt[Term].code);
- send3(1, Iac, Do, opt[Xloc].code);
- }
- /* shared data for console state */
- cons = share(sizeof(Consstate));
- if(cons == 0)
- fatal("shared memory", 0, 0);
- /* authenticate and create new name space */
- Binit(&netib, 0, OREAD);
- if (!trusted){
- while(doauth(user) < 0)
- if(++tries == 5){
- logit("failed as %s: %r", user);
- print("authentication failure:%r\r\n");
- exits("authentication");
- }
- }
- logit("logged in as %s", user);
- putenv("service", "con");
- /* simulate /dev/consctl and /dev/cons using pipes */
- fd = conssim();
- if(fd < 0)
- fatal("simulating", 0, 0);
- Binit(&childib, fd, OREAD);
- /* start a shell in a different process group */
- switch(childpid = rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG)){
- case -1:
- fatal("fork", 0, 0);
- case 0:
- close(fd);
- fd = open("/dev/cons", OREAD);
- dup(fd, 0);
- close(fd);
- fd = open("/dev/cons", OWRITE);
- dup(fd, 1);
- dup(fd, 2);
- close(fd);
- segdetach(cons);
- execl("/bin/rc", "rc", "-il", nil);
- fatal("/bin/rc", 0, 0);
- default:
- sprint(buf, "/proc/%d/notepg", childpid);
- notefd = open(buf, OWRITE);
- break;
- }
- /* two processes to shuttle bytes twixt children and network */
- switch(fork()){
- case -1:
- fatal("fork", 0, 0);
- case 0:
- eofs = 0;
- for(;;){
- n = fromchild(buf, sizeof(buf));
- if(n <= 0){
- if(eofs++ > 2)
- break;
- continue;
- }
- eofs = 0;
- if(write(1, buf, n) != n)
- break;
- }
- break;
- default:
- while((n = fromnet(buf, sizeof(buf))) >= 0)
- if(write(fd, buf, n) != n)
- break;
- break;
- }
- /* kill off all server processes */
- sprint(buf, "/proc/%d/notepg", getpid());
- fd = open(buf, OWRITE);
- write(fd, "die", 3);
- exits(0);
- }
- void
- prompt(char *p, char *b, int n, int raw)
- {
- char *e;
- int i;
- int echo;
- echo = opt[Echo].local;
- if(raw)
- opt[Echo].local = 0;
- print("%s: ", p);
- for(e = b+n; b < e;){
- i = fromnet(b, e-b);
- if(i <= 0)
- exits("fromnet: hungup");
- b += i;
- if(*(b-1) == '\n' || *(b-1) == '\r'){
- *(b-1) = 0;
- break;
- }
- }
- if(raw)
- opt[Echo].local = echo;
- }
- /*
- * challenge user
- */
- int
- challuser(char *user)
- {
- char nchall[64];
- char response[64];
- Chalstate *ch;
- AuthInfo *ai;
- if(strcmp(user, "none") == 0){
- if(nonone)
- return -1;
- newns("none", nil);
- return 0;
- }
- if((ch = auth_challenge("proto=p9cr role=server user=%q", user)) == nil)
- return -1;
- snprint(nchall, sizeof nchall, "challenge: %s\r\nresponse", ch->chal);
- prompt(nchall, response, sizeof response, 0);
- ch->resp = response;
- ch->nresp = strlen(response);
- ai = auth_response(ch);
- auth_freechal(ch);
- if(ai == nil){
- rerrstr(response, sizeof response);
- print("!%s\n", response);
- return -1;
- }
- if(auth_chuid(ai, nil) < 0)
- return -1;
- return 0;
- }
- /*
- * use the in the clear apop password to change user id
- */
- int
- noworldlogin(char *user)
- {
- char password[256];
- prompt("password", password, sizeof(password), 1);
- if(login(user, password, "/lib/namespace.noworld") < 0)
- return -1;
- rfork(RFNOMNT); /* sandbox */
- return 0;
- }
- int
- doauth(char *user)
- {
- if(*user == 0)
- prompt("user", user, Maxuser, 0);
- if(noworld(user))
- return noworldlogin(user);
- if(noworldonly)
- return -1;
- return challuser(user);
-
- }
- /*
- * Process some input from the child, add protocol if needed. If
- * the input buffer goes empty, return.
- */
- int
- fromchild(char *bp, int len)
- {
- int c;
- char *start;
- for(start = bp; bp-start < len-1; ){
- c = Bgetc(&childib);
- if(c < 0){
- if(bp == start)
- return -1;
- else
- break;
- }
- if(cons->raw == 0 && c == '\n')
- *bp++ = '\r';
- *bp++ = c;
- if(Bbuffered(&childib) == 0)
- break;
- }
- return bp-start;
- }
- /*
- * Read from the network up to a '\n' or some other break.
- *
- * If in binary mode, buffer characters but don't
- *
- * The following characters are special:
- * '\r\n's and '\r's get turned into '\n's.
- * ^H erases the last character buffered.
- * ^U kills the whole line buffered.
- * ^W erases the last word
- * ^D causes a 0-length line to be returned.
- * Intr causes an "interrupt" note to be sent to the children.
- */
- #define ECHO(c) { *ebp++ = (c); }
- int
- fromnet(char *bp, int len)
- {
- int c;
- char echobuf[1024];
- char *ebp;
- char *start;
- static int crnl;
- static int doeof;
- /* simulate an EOF as a 0 length input */
- if(doeof){
- doeof = 0;
- return 0;
- }
- for(ebp = echobuf,start = bp; bp-start < len && ebp-echobuf < sizeof(echobuf); ){
- c = Bgetc(&netib);
- if(c < 0){
- if(bp == start)
- return -1;
- else
- break;
- }
- /* telnet protocol only */
- if(!noproto){
- /* protocol messages */
- switch(c){
- case Iac:
- crnl = 0;
- c = Bgetc(&netib);
- if(c == Iac)
- break;
- control(&netib, c);
- continue;
- }
- }
- /* \r\n or \n\r become \n */
- if(c == '\r' || c == '\n'){
- if(crnl && crnl != c){
- crnl = 0;
- continue;
- }
- if(cons->raw == 0 && opt[Echo].local){
- ECHO('\r');
- ECHO('\n');
- }
- crnl = c;
- if(cons->raw == 0)
- *bp++ = '\n';
- else
- *bp++ = c;
- break;
- } else
- crnl = 0;
- /* raw processing (each character terminates */
- if(cons->raw){
- *bp++ = c;
- break;
- }
- /* in binary mode, there are no control characters */
- if(opt[Binary].local){
- if(opt[Echo].local)
- ECHO(c);
- *bp++ = c;
- continue;
- }
- /* cooked processing */
- switch(c){
- case 0x00:
- if(noproto) /* telnet ignores nulls */
- *bp++ = c;
- continue;
- case 0x04:
- if(bp != start)
- doeof = 1;
- goto out;
- case 0x08: /* ^H */
- if(start < bp)
- bp--;
- if(opt[Echo].local)
- ECHO(c);
- break;
- case 0x15: /* ^U */
- bp = start;
- if(opt[Echo].local){
- ECHO('^');
- ECHO('U');
- ECHO('\r');
- ECHO('\n');
- }
- break;
- case 0x17: /* ^W */
- if (opt[Echo].local) {
- while (--bp >= start && !alnum(*bp))
- ECHO('\b');
- while (bp >= start && alnum(*bp)) {
- ECHO('\b');
- bp--;
- }
- bp++;
- }
- break;
- case 0x7f: /* Del */
- write(notefd, "interrupt", 9);
- bp = start;
- break;
- default:
- if(opt[Echo].local)
- ECHO(c);
- *bp++ = c;
- }
- if(ebp != echobuf)
- write(1, echobuf, ebp-echobuf);
- ebp = echobuf;
- }
- out:
- if(ebp != echobuf)
- write(1, echobuf, ebp-echobuf);
- return bp - start;
- }
- int
- termchange(Biobuf *bp, int cmd)
- {
- char buf[8];
- char *p = buf;
- if(cmd != Will)
- return 0;
- /* ask other side to send term type info */
- *p++ = Iac;
- *p++ = Sb;
- *p++ = opt[Term].code;
- *p++ = 1;
- *p++ = Iac;
- *p++ = Se;
- return iwrite(Bfildes(bp), buf, p-buf);
- }
- int
- termsub(Biobuf *bp, uint8_t *sub, int n)
- {
- char term[Maxvar];
- USED(bp);
- if(n-- < 1 || sub[0] != 0)
- return 0;
- if(n >= sizeof term)
- n = sizeof term;
- strncpy(term, (char*)sub, n);
- putenv("TERM", term);
- return 0;
- }
- int
- xlocchange(Biobuf *bp, int cmd)
- {
- char buf[8];
- char *p = buf;
- if(cmd != Will)
- return 0;
- /* ask other side to send x display info */
- *p++ = Iac;
- *p++ = Sb;
- *p++ = opt[Xloc].code;
- *p++ = 1;
- *p++ = Iac;
- *p++ = Se;
- return iwrite(Bfildes(bp), buf, p-buf);
- }
- int
- xlocsub(Biobuf *bp, uint8_t *sub, int n)
- {
- char xloc[Maxvar];
- USED(bp);
- if(n-- < 1 || sub[0] != 0)
- return 0;
- if(n >= sizeof xloc)
- n = sizeof xloc;
- strncpy(xloc, (char*)sub, n);
- putenv("DISPLAY", xloc);
- return 0;
- }
- /*
- * create a shared segment. Make is start 2 meg higher than the current
- * end of process memory.
- */
- void*
- share(uint32_t len)
- {
- uint8_t *vastart;
- vastart = sbrk(0);
- if(vastart == (void*)-1)
- return 0;
- vastart += 2*1024*1024;
- if(segattach(0, "shared", vastart, len) == (void*)-1)
- return 0;
- return vastart;
- }
- /*
- * bind a pipe onto consctl and keep reading it to
- * get changes to console state.
- */
- int
- conssim(void)
- {
- int i, n;
- int fd;
- int tries;
- char buf[128];
- char *field[10];
- /* a pipe to simulate the /dev/cons */
- if(bind("#|", "/mnt/cons/cons", MREPL) < 0)
- fatal("/dev/cons1", 0, 0);
- if(bind("/mnt/cons/cons/data1", "/dev/cons", MREPL) < 0)
- fatal("/dev/cons2", 0, 0);
- /* a pipe to simulate consctl */
- if(bind("#|", "/mnt/cons/consctl", MBEFORE) < 0
- || bind("/mnt/cons/consctl/data1", "/dev/consctl", MREPL) < 0)
- fatal("/dev/consctl", 0, 0);
- /* a process to read /dev/consctl and set the state in cons */
- switch(fork()){
- case -1:
- fatal("forking", 0, 0);
- case 0:
- break;
- default:
- return open("/mnt/cons/cons/data", ORDWR);
- }
- for(tries = 0; tries < 100; tries++){
- cons->raw = 0;
- cons->hold = 0;
- fd = open("/mnt/cons/consctl/data", OREAD);
- if(fd < 0)
- continue;
- tries = 0;
- for(;;){
- n = read(fd, buf, sizeof(buf)-1);
- if(n <= 0)
- break;
- buf[n] = 0;
- n = getfields(buf, field, 10, 1, " ");
- for(i = 0; i < n; i++){
- if(strcmp(field[i], "rawon") == 0) {
- if(debug) fprint(2, "raw = 1\n");
- cons->raw = 1;
- } else if(strcmp(field[i], "rawoff") == 0) {
- if(debug) fprint(2, "raw = 0\n");
- cons->raw = 0;
- } else if(strcmp(field[i], "holdon") == 0) {
- cons->hold = 1;
- if(debug) fprint(2, "raw = 1\n");
- } else if(strcmp(field[i], "holdoff") == 0) {
- cons->hold = 0;
- if(debug) fprint(2, "raw = 0\n");
- }
- }
- }
- close(fd);
- }
- exits(0);
- return -1;
- }
- int
- alnum(int c)
- {
- /*
- * Hard to get absolutely right. Use what we know about ASCII
- * and assume anything above the Latin control characters is
- * potentially an alphanumeric.
- */
- if(c <= ' ')
- return 0;
- if(0x7F<=c && c<=0xA0)
- return 0;
- if(strchr("!\"#$%&'()*+,-./:;<=>?@`[\\]^{|}~", c))
- return 0;
- return 1;
- }
|