1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <auth.h> 5 #include <libsec.h> 6 7 #include "../ip/telnet.h" 8 9 /* console state (for consctl) */ 10 typedef struct Consstate Consstate; 11 struct Consstate{ 12 int raw; 13 int hold; 14 }; 15 Consstate *cons; 16 17 int notefd; /* for sending notes to the child */ 18 int noproto; /* true if we shouldn't be using the telnet protocol */ 19 int trusted; /* true if we need not authenticate - current user 20 is ok */ 21 int nonone = 1; /* don't allow none logins */ 22 int noworldonly; /* only noworld accounts */ 23 24 enum 25 { 26 Maxpath= 256, 27 Maxuser= 64, 28 Maxvar= 32, 29 }; 30 31 /* input and output buffers for network connection */ 32 Biobuf netib; 33 Biobuf childib; 34 char remotesys[Maxpath]; /* name of remote system */ 35 36 int alnum(int); 37 int conssim(void); 38 int fromchild(char*, int); 39 int fromnet(char*, int); 40 int termchange(Biobuf*, int); 41 int termsub(Biobuf*, uchar*, int); 42 int xlocchange(Biobuf*, int); 43 int xlocsub(Biobuf*, uchar*, int); 44 int challuser(char*); 45 int noworldlogin(char*); 46 void* share(ulong); 47 int doauth(char*); 48 49 #define TELNETLOG "telnet" 50 51 void 52 logit(char *fmt, ...) 53 { 54 va_list arg; 55 char buf[8192]; 56 57 va_start(arg, fmt); 58 vseprint(buf, buf + sizeof(buf) / sizeof(*buf), fmt, arg); 59 va_end(arg); 60 syslog(0, TELNETLOG, "(%s) %s", remotesys, buf); 61 } 62 63 void 64 getremote(char *dir) 65 { 66 int fd, n; 67 char remfile[Maxpath]; 68 69 sprint(remfile, "%s/remote", dir); 70 fd = open(remfile, OREAD); 71 if(fd < 0) 72 strcpy(remotesys, "unknown2"); 73 n = read(fd, remotesys, sizeof(remotesys)-1); 74 if(n>0) 75 remotesys[n-1] = 0; 76 else 77 strcpy(remotesys, remfile); 78 close(fd); 79 } 80 81 void 82 main(int argc, char *argv[]) 83 { 84 char buf[1024]; 85 int fd; 86 char user[Maxuser]; 87 int tries = 0; 88 int childpid; 89 int n, eofs; 90 91 memset(user, 0, sizeof(user)); 92 ARGBEGIN { 93 case 'n': 94 opt[Echo].local = 1; 95 noproto = 1; 96 break; 97 case 'p': 98 noproto = 1; 99 break; 100 case 'a': 101 nonone = 0; 102 break; 103 case 't': 104 trusted = 1; 105 strncpy(user, getuser(), sizeof(user)-1); 106 break; 107 case 'u': 108 strncpy(user, ARGF(), sizeof(user)-1); 109 break; 110 case 'd': 111 debug = 1; 112 break; 113 case 'N': 114 noworldonly = 1; 115 break; 116 } ARGEND 117 118 if(argc) 119 getremote(argv[argc-1]); 120 else 121 strcpy(remotesys, "unknown"); 122 123 /* options we need routines for */ 124 opt[Term].change = termchange; 125 opt[Term].sub = termsub; 126 opt[Xloc].sub = xlocsub; 127 128 /* setup default telnet options */ 129 if(!noproto){ 130 send3(1, Iac, Will, opt[Echo].code); 131 send3(1, Iac, Do, opt[Term].code); 132 send3(1, Iac, Do, opt[Xloc].code); 133 } 134 135 /* shared data for console state */ 136 cons = share(sizeof(Consstate)); 137 if(cons == 0) 138 fatal("shared memory", 0, 0); 139 140 /* authenticate and create new name space */ 141 Binit(&netib, 0, OREAD); 142 if (!trusted){ 143 while(doauth(user) < 0) 144 if(++tries == 5){ 145 logit("failed as %s: %r", user); 146 print("authentication failure:%r\r\n"); 147 exits("authentication"); 148 } 149 } 150 logit("logged in as %s", user); 151 putenv("service", "con"); 152 153 /* simulate /dev/consctl and /dev/cons using pipes */ 154 fd = conssim(); 155 if(fd < 0) 156 fatal("simulating", 0, 0); 157 Binit(&childib, fd, OREAD); 158 159 /* start a shell in a different process group */ 160 switch(childpid = rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG)){ 161 case -1: 162 fatal("fork", 0, 0); 163 case 0: 164 close(fd); 165 fd = open("/dev/cons", OREAD); 166 dup(fd, 0); 167 close(fd); 168 fd = open("/dev/cons", OWRITE); 169 dup(fd, 1); 170 dup(fd, 2); 171 close(fd); 172 segdetach(cons); 173 execl("/bin/rc", "rc", "-il", nil); 174 fatal("/bin/rc", 0, 0); 175 default: 176 sprint(buf, "/proc/%d/notepg", childpid); 177 notefd = open(buf, OWRITE); 178 break; 179 } 180 181 /* two processes to shuttle bytes twixt children and network */ 182 switch(fork()){ 183 case -1: 184 fatal("fork", 0, 0); 185 case 0: 186 eofs = 0; 187 for(;;){ 188 n = fromchild(buf, sizeof(buf)); 189 if(n <= 0){ 190 if(eofs++ > 2) 191 break; 192 continue; 193 } 194 eofs = 0; 195 if(write(1, buf, n) != n) 196 break; 197 } 198 break; 199 default: 200 while((n = fromnet(buf, sizeof(buf))) >= 0) 201 if(write(fd, buf, n) != n) 202 break; 203 break; 204 } 205 206 /* kill off all server processes */ 207 sprint(buf, "/proc/%d/notepg", getpid()); 208 fd = open(buf, OWRITE); 209 write(fd, "die", 3); 210 exits(0); 211 } 212 213 void 214 prompt(char *p, char *b, int n, int raw) 215 { 216 char *e; 217 int i; 218 int echo; 219 220 echo = opt[Echo].local; 221 if(raw) 222 opt[Echo].local = 0; 223 print("%s: ", p); 224 for(e = b+n; b < e;){ 225 i = fromnet(b, e-b); 226 if(i <= 0) 227 exits("fromnet: hungup"); 228 b += i; 229 if(*(b-1) == '\n' || *(b-1) == '\r'){ 230 *(b-1) = 0; 231 break; 232 } 233 } 234 if(raw) 235 opt[Echo].local = echo; 236 } 237 238 /* 239 * challenge user 240 */ 241 int 242 challuser(char *user) 243 { 244 char nchall[64]; 245 char response[64]; 246 Chalstate *ch; 247 AuthInfo *ai; 248 249 if(strcmp(user, "none") == 0){ 250 if(nonone) 251 return -1; 252 newns("none", nil); 253 return 0; 254 } 255 if((ch = auth_challenge("proto=p9cr role=server user=%q", user)) == nil) 256 return -1; 257 snprint(nchall, sizeof nchall, "challenge: %s\r\nresponse", ch->chal); 258 prompt(nchall, response, sizeof response, 0); 259 ch->resp = response; 260 ch->nresp = strlen(response); 261 ai = auth_response(ch); 262 auth_freechal(ch); 263 if(ai == nil){ 264 rerrstr(response, sizeof response); 265 print("!%s\n", response); 266 return -1; 267 } 268 if(auth_chuid(ai, nil) < 0) 269 return -1; 270 return 0; 271 } 272 /* 273 * use the in the clear apop password to change user id 274 */ 275 int 276 noworldlogin(char *user) 277 { 278 char password[256]; 279 280 prompt("password", password, sizeof(password), 1); 281 if(login(user, password, "/lib/namespace.noworld") < 0) 282 return -1; 283 rfork(RFNOMNT); /* sandbox */ 284 return 0; 285 } 286 287 int 288 doauth(char *user) 289 { 290 if(*user == 0) 291 prompt("user", user, Maxuser, 0); 292 if(noworld(user)) 293 return noworldlogin(user); 294 if(noworldonly) 295 return -1; 296 return challuser(user); 297 298 } 299 300 /* 301 * Process some input from the child, add protocol if needed. If 302 * the input buffer goes empty, return. 303 */ 304 int 305 fromchild(char *bp, int len) 306 { 307 int c; 308 char *start; 309 310 for(start = bp; bp-start < len-1; ){ 311 c = Bgetc(&childib); 312 if(c < 0){ 313 if(bp == start) 314 return -1; 315 else 316 break; 317 } 318 if(cons->raw == 0 && c == '\n') 319 *bp++ = '\r'; 320 *bp++ = c; 321 if(Bbuffered(&childib) == 0) 322 break; 323 } 324 return bp-start; 325 } 326 327 /* 328 * Read from the network up to a '\n' or some other break. 329 * 330 * If in binary mode, buffer characters but don't 331 * 332 * The following characters are special: 333 * '\r\n's and '\r's get turned into '\n's. 334 * ^H erases the last character buffered. 335 * ^U kills the whole line buffered. 336 * ^W erases the last word 337 * ^D causes a 0-length line to be returned. 338 * Intr causes an "interrupt" note to be sent to the children. 339 */ 340 #define ECHO(c) { *ebp++ = (c); } 341 int 342 fromnet(char *bp, int len) 343 { 344 int c; 345 char echobuf[1024]; 346 char *ebp; 347 char *start; 348 static int crnl; 349 static int doeof; 350 351 352 /* simulate an EOF as a 0 length input */ 353 if(doeof){ 354 doeof = 0; 355 return 0; 356 } 357 358 for(ebp = echobuf,start = bp; bp-start < len && ebp-echobuf < sizeof(echobuf); ){ 359 c = Bgetc(&netib); 360 if(c < 0){ 361 if(bp == start) 362 return -1; 363 else 364 break; 365 } 366 367 /* telnet protocol only */ 368 if(!noproto){ 369 /* protocol messages */ 370 switch(c){ 371 case Iac: 372 crnl = 0; 373 c = Bgetc(&netib); 374 if(c == Iac) 375 break; 376 control(&netib, c); 377 continue; 378 } 379 380 } 381 382 /* \r\n or \n\r become \n */ 383 if(c == '\r' || c == '\n'){ 384 if(crnl && crnl != c){ 385 crnl = 0; 386 continue; 387 } 388 if(cons->raw == 0 && opt[Echo].local){ 389 ECHO('\r'); 390 ECHO('\n'); 391 } 392 crnl = c; 393 if(cons->raw == 0) 394 *bp++ = '\n'; 395 else 396 *bp++ = c; 397 break; 398 } else 399 crnl = 0; 400 401 /* raw processing (each character terminates */ 402 if(cons->raw){ 403 *bp++ = c; 404 break; 405 } 406 407 /* in binary mode, there are no control characters */ 408 if(opt[Binary].local){ 409 if(opt[Echo].local) 410 ECHO(c); 411 *bp++ = c; 412 continue; 413 } 414 415 /* cooked processing */ 416 switch(c){ 417 case 0x00: 418 if(noproto) /* telnet ignores nulls */ 419 *bp++ = c; 420 continue; 421 case 0x04: 422 if(bp != start) 423 doeof = 1; 424 goto out; 425 426 case 0x08: /* ^H */ 427 if(start < bp) 428 bp--; 429 if(opt[Echo].local) 430 ECHO(c); 431 break; 432 433 case 0x15: /* ^U */ 434 bp = start; 435 if(opt[Echo].local){ 436 ECHO('^'); 437 ECHO('U'); 438 ECHO('\r'); 439 ECHO('\n'); 440 } 441 break; 442 443 case 0x17: /* ^W */ 444 if (opt[Echo].local) { 445 while (--bp >= start && !alnum(*bp)) 446 ECHO('\b'); 447 while (bp >= start && alnum(*bp)) { 448 ECHO('\b'); 449 bp--; 450 } 451 bp++; 452 } 453 break; 454 455 case 0x7f: /* Del */ 456 write(notefd, "interrupt", 9); 457 bp = start; 458 break; 459 460 default: 461 if(opt[Echo].local) 462 ECHO(c); 463 *bp++ = c; 464 } 465 if(ebp != echobuf) 466 write(1, echobuf, ebp-echobuf); 467 ebp = echobuf; 468 } 469 out: 470 if(ebp != echobuf) 471 write(1, echobuf, ebp-echobuf); 472 return bp - start; 473 } 474 475 int 476 termchange(Biobuf *bp, int cmd) 477 { 478 char buf[8]; 479 char *p = buf; 480 481 if(cmd != Will) 482 return 0; 483 484 /* ask other side to send term type info */ 485 *p++ = Iac; 486 *p++ = Sb; 487 *p++ = opt[Term].code; 488 *p++ = 1; 489 *p++ = Iac; 490 *p++ = Se; 491 return iwrite(Bfildes(bp), buf, p-buf); 492 } 493 494 int 495 termsub(Biobuf *bp, uchar *sub, int n) 496 { 497 char term[Maxvar]; 498 499 USED(bp); 500 if(n-- < 1 || sub[0] != 0) 501 return 0; 502 if(n >= sizeof term) 503 n = sizeof term; 504 strncpy(term, (char*)sub, n); 505 putenv("TERM", term); 506 return 0; 507 } 508 509 int 510 xlocchange(Biobuf *bp, int cmd) 511 { 512 char buf[8]; 513 char *p = buf; 514 515 if(cmd != Will) 516 return 0; 517 518 /* ask other side to send x display info */ 519 *p++ = Iac; 520 *p++ = Sb; 521 *p++ = opt[Xloc].code; 522 *p++ = 1; 523 *p++ = Iac; 524 *p++ = Se; 525 return iwrite(Bfildes(bp), buf, p-buf); 526 } 527 528 int 529 xlocsub(Biobuf *bp, uchar *sub, int n) 530 { 531 char xloc[Maxvar]; 532 533 USED(bp); 534 if(n-- < 1 || sub[0] != 0) 535 return 0; 536 if(n >= sizeof xloc) 537 n = sizeof xloc; 538 strncpy(xloc, (char*)sub, n); 539 putenv("DISPLAY", xloc); 540 return 0; 541 } 542 543 /* 544 * create a shared segment. Make is start 2 meg higher than the current 545 * end of process memory. 546 */ 547 void* 548 share(ulong len) 549 { 550 uchar *vastart; 551 552 vastart = sbrk(0); 553 if(vastart == (void*)-1) 554 return 0; 555 vastart += 2*1024*1024; 556 557 if(segattach(0, "shared", vastart, len) == (void*)-1) 558 return 0; 559 560 return vastart; 561 } 562 563 /* 564 * bind a pipe onto consctl and keep reading it to 565 * get changes to console state. 566 */ 567 int 568 conssim(void) 569 { 570 int i, n; 571 int fd; 572 int tries; 573 char buf[128]; 574 char *field[10]; 575 576 /* a pipe to simulate the /dev/cons */ 577 if(bind("#|", "/mnt/cons/cons", MREPL) < 0) 578 fatal("/dev/cons1", 0, 0); 579 if(bind("/mnt/cons/cons/data1", "/dev/cons", MREPL) < 0) 580 fatal("/dev/cons2", 0, 0); 581 582 /* a pipe to simulate consctl */ 583 if(bind("#|", "/mnt/cons/consctl", MBEFORE) < 0 584 || bind("/mnt/cons/consctl/data1", "/dev/consctl", MREPL) < 0) 585 fatal("/dev/consctl", 0, 0); 586 587 /* a process to read /dev/consctl and set the state in cons */ 588 switch(fork()){ 589 case -1: 590 fatal("forking", 0, 0); 591 case 0: 592 break; 593 default: 594 return open("/mnt/cons/cons/data", ORDWR); 595 } 596 597 for(tries = 0; tries < 100; tries++){ 598 cons->raw = 0; 599 cons->hold = 0; 600 fd = open("/mnt/cons/consctl/data", OREAD); 601 if(fd < 0) 602 continue; 603 tries = 0; 604 for(;;){ 605 n = read(fd, buf, sizeof(buf)-1); 606 if(n <= 0) 607 break; 608 buf[n] = 0; 609 n = getfields(buf, field, 10, 1, " "); 610 for(i = 0; i < n; i++){ 611 if(strcmp(field[i], "rawon") == 0) { 612 if(debug) fprint(2, "raw = 1\n"); 613 cons->raw = 1; 614 } else if(strcmp(field[i], "rawoff") == 0) { 615 if(debug) fprint(2, "raw = 0\n"); 616 cons->raw = 0; 617 } else if(strcmp(field[i], "holdon") == 0) { 618 cons->hold = 1; 619 if(debug) fprint(2, "raw = 1\n"); 620 } else if(strcmp(field[i], "holdoff") == 0) { 621 cons->hold = 0; 622 if(debug) fprint(2, "raw = 0\n"); 623 } 624 } 625 } 626 close(fd); 627 } 628 exits(0); 629 return -1; 630 } 631 632 int 633 alnum(int c) 634 { 635 /* 636 * Hard to get absolutely right. Use what we know about ASCII 637 * and assume anything above the Latin control characters is 638 * potentially an alphanumeric. 639 */ 640 if(c <= ' ') 641 return 0; 642 if(0x7F<=c && c<=0xA0) 643 return 0; 644 if(strchr("!\"#$%&'()*+,-./:;<=>?@`[\\]^{|}~", c)) 645 return 0; 646 return 1; 647 } 648