From mboxrd@z Thu Jan 1 00:00:00 1970 Date: Thu, 17 Aug 1995 02:56:33 -0400 From: Vadim Antonov avg@postman.ncube.com Subject: new telnetd (again) Topicbox-Message-UUID: 17b1c4a6-eac8-11e9-9e20-41e7f4b1d025 Message-ID: <19950817065633.Y-mNCgupmIFHfvXmIgC_WglM2t2-QmCEc9bKGfsXm28@z> The "more improved" line discipline plus the plaintext-password authentication option for those who does not have SecureNet keys or use reasonable firewalls. The plaintext password mode is turned on with -P option (i.e. replace /rc/bin/service/tcp23 with #!/bin/rc exec /bin/aux/telnetd -P Since there were many changes i appended the complete source (/sys/src/cmd/service/telnetd.c): --vadim ---CUT HERE--- #include #include #include #include #include "../ip/telnet.h" /* console state (for consctl) */ typedef struct Consstate Consstate; struct Consstate{ int raw; int hold; int noecho; int col; int scol; }; 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; /* don't allow none logins */ int plaintext; /* use less secure plaintext passowrds */ /* input and output buffers for network connection */ Biobuf netib; Biobuf childib; char remotesys[2*NAMELEN]; /* name of remote system */ int alnum(int); int conssim(void); int fromchild(char*, int); int fromnet(char*, int); void rubout(char*, char*, char**); int termchange(Biobuf*, int); int termsub(Biobuf*, uchar*, int); int xlocchange(Biobuf*, int); int xlocsub(Biobuf*, uchar*, int); int challuser(char*); void* share(int); #define TELNETLOG "telnet" void logit(char *fmt, ...) { char buf[8192], *s; s = buf; s = doprint(s, buf + sizeof(buf) / sizeof(*buf), fmt, &fmt + 1); *s = 0; syslog(0, TELNETLOG, "(%s) %s", remotesys, buf); } void getremote(char *dir) { int fd, n; char remfile[2*NAMELEN]; 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[NAMELEN]; int tries = 0; int childpid; int n; memset(user, 0, sizeof(user)); ARGBEGIN { case 'n': opt[Echo].local = 1; noproto = 1; break; case 'N': nonone = 1; break; case 't': trusted = 1; strncpy(user, getuser(), NAMELEN-1); break; case 'u': strncpy(user, ARGF(), sizeof(user)-1); break; case 'D': debug = 1; break; case 'P': plaintext = 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); cons->scol = -1; /* authenticate and create new name space */ Binit(&netib, 0, OREAD); if (!trusted){ while(challuser(user) < 0) { if(++tries == 5){ logit("failed as %s", user); print("authentication failure\r\n"); exits("authentication"); } *user = 0; } } logit("logged in as %s", user); newns(user, 0); 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: 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", 0); fatal("/bin/rc", 0, 0); default: sprint(buf, "/proc/%d/notepg", childpid); notefd = open(buf, OWRITE); if(notefd < 0) fatal(buf, 0, 0); break; } /* two processes to shuttle bytes twixt children and network */ switch(fork()){ case -1: fatal("fork", 0, 0); case 0: while((n = fromchild(buf, sizeof(buf))) >= 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) { char *e; int i; 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) = 0; return; } } } /* * challenge user */ int challuser(char *user) { char nchall[NETCHLEN+32]; char response[NAMELEN]; Chalstate ch; char key[DESKEYLEN]; if(*user == 0) prompt("user", user, NAMELEN); if(strcmp(user, "none") == 0){ if(nonone) return -1; return 0; } if(getchal(&ch, user) < 0) return -1; if(plaintext) { cons->noecho = 1; prompt("password", response, NAMELEN); cons->noecho = 0; print("\r\n"); passtokey(key, response); strcpy(response, ch.chal); netcrypt(key, response); } else { sprint(nchall, "challenge: %s\r\nresponse", ch.chal); prompt(nchall, response, sizeof response); } if(chalreply(&ch, response) < 0) return -1; return 0; } /* * 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) { switch(c) { case '\n': *bp++ = '\r'; case '\r': cons->col = 0; break; case '\b': if(cons->col > 0) cons->col--; break; case '\t': cons->col = (cons->col + 8) & ~07; break; default: cons->col++; } } *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-lenght line to be returned. * Intr causes an "interrupt" note to be sent to the children. */ #define ECHO(c) { *ebp++ = (c); } #define BACKSPACE() { ECHO('\b'); ECHO(' '); ECHO('\b'); } 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; case 0: /* telnet ignores nulls */ 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'); cons->col = 0; cons->scol = -1; } crnl = c; *bp++ = '\n'; 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 */ if(cons->scol == -1) cons->scol = cons->col; switch(c){ case 0x04: if(bp != start) doeof = 1; goto out; case 0x08: /* ^H */ if(start < bp) { bp--; if(opt[Echo].local) rubout(bp, start, &ebp); } break; case 0x15: /* ^U */ if(opt[Echo].local && cons->scol >= 0) { while (cons->col > cons->scol) { BACKSPACE(); cons->col--; } } bp = start; break; case 0x17: /* ^W */ if (opt[Echo].local) { while (bp > start && !alnum(bp[-1])) rubout(--bp, start, &ebp); while (bp > start && alnum(bp[-1])) { bp--; BACKSPACE(); cons->col--; } } break; case 0x7f: /* Del */ write(notefd, "interrupt", 9); ECHO('\r'); ECHO('\n'); bp = start; cons->col = 0; cons->scol = -1; break; case 0x09: /* Tab */ if(opt[Echo].local) { cons->col = (cons->col + 8) & ~7; ECHO(c); } *bp++ = c; break; default: if(opt[Echo].local) { if( c >= ' ' ) { ECHO(c); cons->col++; } else { ECHO('^'); ECHO(0x40|c); cons->col += 2; } } *bp++ = c; } if(ebp != echobuf && !cons->noecho) write(1, echobuf, ebp-echobuf); ebp = echobuf; } out: if(ebp != echobuf && !cons->noecho) write(1, echobuf, ebp-echobuf); return bp - start; } /* * Rub out an echoed character */ void rubout(char *bp, char *buf, char **pp) { char *ebp = *pp; int col; char *q; if( *bp == '\t' ) { /* tough case */ col = cons->scol; for(q = buf ; q < bp ; q++) { if(*q == '\t') col = (col + 8) & ~07; else if(*q < ' ') col += 2; else col++; } while(cons->col > col) { BACKSPACE(); cons->col--; } *pp = ebp; return; } else if( *bp < ' ' ) { cons->col--; BACKSPACE(); } cons->col--; BACKSPACE(); *pp = ebp; } 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, uchar *sub, int n) { char term[NAMELEN]; 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, uchar *sub, int n) { char xloc[NAMELEN]; 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(int len) { ulong vastart; vastart = ((ulong)sbrk(0)) + 2*1024*1024; if(segattach(0, "shared", (void *)vastart, len) < 0) return 0; return (void*)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 || bind("/mnt/cons/cons/data1", "/dev/cons", MREPL) < 0) fatal("/dev/cons", 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); } setfields(" "); 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 = getmfields(buf, field, 10); for(i = 0; i < n; i++){ if(strcmp(field[i], "rawon") == 0) cons->raw = 1; else if(strcmp(field[i], "rawoff") == 0) cons->raw = 0; else if(strcmp(field[i], "holdon") == 0) cons->hold = 1; else if(strcmp(field[i], "holdoff") == 0) cons->hold = 0; } } 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; }