rsh.c revision 78527
168349Sobrien/* 268349Sobrien * Copyright (c) 1997 - 2000 Kungliga Tekniska H�gskolan 368349Sobrien * (Royal Institute of Technology, Stockholm, Sweden). 468349Sobrien * All rights reserved. 568349Sobrien * 668349Sobrien * Redistribution and use in source and binary forms, with or without 768349Sobrien * modification, are permitted provided that the following conditions 868349Sobrien * are met: 968349Sobrien * 1068349Sobrien * 1. Redistributions of source code must retain the above copyright 1168349Sobrien * notice, this list of conditions and the following disclaimer. 1268349Sobrien * 1368349Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1468349Sobrien * notice, this list of conditions and the following disclaimer in the 1568349Sobrien * documentation and/or other materials provided with the distribution. 1668349Sobrien * 1768349Sobrien * 3. Neither the name of the Institute nor the names of its contributors 1868349Sobrien * may be used to endorse or promote products derived from this software 1968349Sobrien * without specific prior written permission. 2068349Sobrien * 2168349Sobrien * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 2268349Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2368349Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2468349Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 2568349Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2668349Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2769216Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2869216Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2969216Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3069216Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3169216Sobrien * SUCH DAMAGE. 3269216Sobrien */ 3369216Sobrien 3468349Sobrien#include "rsh_locl.h" 3569216SobrienRCSID("$Id: rsh.c,v 1.58 2001/02/20 01:44:47 assar Exp $"); 3669216Sobrien 3769216Sobrienenum auth_method auth_method; 3869216Sobrienint do_encrypt = -1; 3969216Sobrienint do_forward = -1; 4069216Sobrienint do_forwardable = -1; 4169216Sobrienint do_unique_tkfile = 0; 4269216Sobrienchar *unique_tkfile = NULL; 4369216Sobrienchar tkfile[MAXPATHLEN]; 4469216Sobrienkrb5_context context; 4569216Sobrienkrb5_keyblock *keyblock; 4668349Sobrienkrb5_crypto crypto; 4768349Sobrien#ifdef KRB4 4868349Sobriendes_key_schedule schedule; 4968349Sobriendes_cblock iv; 5068349Sobrien#endif 5168349Sobrien 5268349Sobrien 5368349Sobrien/* 5468349Sobrien * 5568349Sobrien */ 5668349Sobrien 5768349Sobrienstatic int input = 1; /* Read from stdin */ 5868349Sobrien 5968349Sobrienstatic int 6068349Sobrienloop (int s, int errsock) 6168349Sobrien{ 6268349Sobrien fd_set real_readset; 6368349Sobrien int count = 1; 6468349Sobrien 6568349Sobrien if (s >= FD_SETSIZE || errsock >= FD_SETSIZE) 6668349Sobrien errx (1, "fd too large"); 6768349Sobrien 6868349Sobrien FD_ZERO(&real_readset); 6968349Sobrien FD_SET(s, &real_readset); 7068349Sobrien if (errsock != -1) { 7168349Sobrien FD_SET(errsock, &real_readset); 7268349Sobrien ++count; 7368349Sobrien } 7468349Sobrien if(input) 7568349Sobrien FD_SET(STDIN_FILENO, &real_readset); 7668349Sobrien 7768349Sobrien for (;;) { 7868349Sobrien int ret; 7968349Sobrien fd_set readset; 8068349Sobrien char buf[RSH_BUFSIZ]; 8168349Sobrien 82103373Sobrien readset = real_readset; 8368349Sobrien ret = select (max(s, errsock) + 1, &readset, NULL, NULL, NULL); 8468349Sobrien if (ret < 0) { 8568349Sobrien if (errno == EINTR) 8668349Sobrien continue; 8768349Sobrien else 8868349Sobrien err (1, "select"); 8968349Sobrien } 9068349Sobrien if (FD_ISSET(s, &readset)) { 9168349Sobrien ret = do_read (s, buf, sizeof(buf)); 9268349Sobrien if (ret < 0) 9368349Sobrien err (1, "read"); 9468349Sobrien else if (ret == 0) { 9568349Sobrien close (s); 9668349Sobrien FD_CLR(s, &real_readset); 9768349Sobrien if (--count == 0) 9868349Sobrien return 0; 9968349Sobrien } else 10068349Sobrien net_write (STDOUT_FILENO, buf, ret); 10168349Sobrien } 10268349Sobrien if (errsock != -1 && FD_ISSET(errsock, &readset)) { 10368349Sobrien ret = do_read (errsock, buf, sizeof(buf)); 10468349Sobrien if (ret < 0) 10568349Sobrien err (1, "read"); 10668349Sobrien else if (ret == 0) { 10768349Sobrien close (errsock); 10868349Sobrien FD_CLR(errsock, &real_readset); 10968349Sobrien if (--count == 0) 11068349Sobrien return 0; 11168349Sobrien } else 11268349Sobrien net_write (STDERR_FILENO, buf, ret); 11368349Sobrien } 11468349Sobrien if (FD_ISSET(STDIN_FILENO, &readset)) { 11568349Sobrien ret = read (STDIN_FILENO, buf, sizeof(buf)); 11668349Sobrien if (ret < 0) 11768349Sobrien err (1, "read"); 11868349Sobrien else if (ret == 0) { 11968349Sobrien close (STDIN_FILENO); 12068349Sobrien FD_CLR(STDIN_FILENO, &real_readset); 12168349Sobrien shutdown (s, SHUT_WR); 12268349Sobrien } else 12368349Sobrien do_write (s, buf, ret); 12468349Sobrien } 12568349Sobrien } 12668349Sobrien} 12768349Sobrien 12868349Sobrien#ifdef KRB4 12968349Sobrienstatic int 13068349Sobriensend_krb4_auth(int s, 13168349Sobrien struct sockaddr *thisaddr, 13268349Sobrien struct sockaddr *thataddr, 13368349Sobrien const char *hostname, 13468349Sobrien const char *remote_user, 13568349Sobrien const char *local_user, 13668349Sobrien size_t cmd_len, 13768349Sobrien const char *cmd) 13868349Sobrien{ 13968349Sobrien KTEXT_ST text; 14068349Sobrien CREDENTIALS cred; 14168349Sobrien MSG_DAT msg; 14268349Sobrien int status; 14368349Sobrien size_t len; 14468349Sobrien 145110949Sobrien status = krb_sendauth (do_encrypt ? KOPT_DO_MUTUAL : 0, 146110949Sobrien s, &text, "rcmd", 14768349Sobrien (char *)hostname, krb_realmofhost (hostname), 148110949Sobrien getpid(), &msg, &cred, schedule, 14968349Sobrien (struct sockaddr_in *)thisaddr, 15068349Sobrien (struct sockaddr_in *)thataddr, 15168349Sobrien KCMD_VERSION); 15268349Sobrien if (status != KSUCCESS) { 15368349Sobrien warnx ("%s: %s", hostname, krb_get_err_text(status)); 15468349Sobrien return 1; 15568349Sobrien } 15668349Sobrien memcpy (iv, cred.session, sizeof(iv)); 15768349Sobrien 15868349Sobrien len = strlen(remote_user) + 1; 15968349Sobrien if (net_write (s, remote_user, len) != len) { 16068349Sobrien warn("write"); 16168349Sobrien return 1; 16268349Sobrien } 16368349Sobrien if (net_write (s, cmd, cmd_len) != cmd_len) { 16468349Sobrien warn("write"); 16568349Sobrien return 1; 16668349Sobrien } 16768349Sobrien return 0; 16868349Sobrien} 16968349Sobrien#endif /* KRB4 */ 17068349Sobrien 17168349Sobrien/* 17268349Sobrien * Send forward information on `s' for host `hostname', them being 17368349Sobrien * forwardable themselves if `forwardable' 17468349Sobrien */ 17568349Sobrien 17668349Sobrienstatic int 17768349Sobrienkrb5_forward_cred (krb5_auth_context auth_context, 17868349Sobrien int s, 17968349Sobrien const char *hostname, 18068349Sobrien int forwardable) 18168349Sobrien{ 18268349Sobrien krb5_error_code ret; 18368349Sobrien krb5_ccache ccache; 18468349Sobrien krb5_creds creds; 18568349Sobrien krb5_kdc_flags flags; 18668349Sobrien krb5_data out_data; 18768349Sobrien krb5_principal principal; 18868349Sobrien 18968349Sobrien memset (&creds, 0, sizeof(creds)); 19068349Sobrien 19168349Sobrien ret = krb5_cc_default (context, &ccache); 19268349Sobrien if (ret) { 19368349Sobrien warnx ("could not forward creds: krb5_cc_default: %s", 19468349Sobrien krb5_get_err_text (context, ret)); 19568349Sobrien return 1; 19668349Sobrien } 19768349Sobrien 19868349Sobrien ret = krb5_cc_get_principal (context, ccache, &principal); 19968349Sobrien if (ret) { 20068349Sobrien warnx ("could not forward creds: krb5_cc_get_principal: %s", 20168349Sobrien krb5_get_err_text (context, ret)); 20268349Sobrien return 1; 20368349Sobrien } 20468349Sobrien 20568349Sobrien creds.client = principal; 20668349Sobrien 20768349Sobrien ret = krb5_build_principal (context, 20868349Sobrien &creds.server, 20968349Sobrien strlen(principal->realm), 21068349Sobrien principal->realm, 21168349Sobrien "krbtgt", 21268349Sobrien principal->realm, 21368349Sobrien NULL); 21468349Sobrien 21568349Sobrien if (ret) { 21668349Sobrien warnx ("could not forward creds: krb5_build_principal: %s", 21768349Sobrien krb5_get_err_text (context, ret)); 21868349Sobrien return 1; 21968349Sobrien } 22068349Sobrien 22168349Sobrien creds.times.endtime = 0; 22268349Sobrien 22368349Sobrien flags.i = 0; 22468349Sobrien flags.b.forwarded = 1; 22568349Sobrien flags.b.forwardable = forwardable; 22668349Sobrien 22768349Sobrien ret = krb5_get_forwarded_creds (context, 22868349Sobrien auth_context, 22968349Sobrien ccache, 23068349Sobrien flags.i, 23168349Sobrien hostname, 23268349Sobrien &creds, 23368349Sobrien &out_data); 23468349Sobrien if (ret) { 23568349Sobrien warnx ("could not forward creds: krb5_get_forwarded_creds: %s", 23668349Sobrien krb5_get_err_text (context, ret)); 23784685Sobrien return 1; 23884685Sobrien } 23984685Sobrien 24084685Sobrien ret = krb5_write_message (context, 24168349Sobrien (void *)&s, 24268349Sobrien &out_data); 24368349Sobrien krb5_data_free (&out_data); 24468349Sobrien 24568349Sobrien if (ret) 24668349Sobrien warnx ("could not forward creds: krb5_write_message: %s", 24768349Sobrien krb5_get_err_text (context, ret)); 24868349Sobrien return 0; 24968349Sobrien} 25068349Sobrien 25168349Sobrienstatic int 25268349Sobriensend_krb5_auth(int s, 25368349Sobrien struct sockaddr *thisaddr, 25468349Sobrien struct sockaddr *thataddr, 25568349Sobrien const char *hostname, 25668349Sobrien const char *remote_user, 25768349Sobrien const char *local_user, 25868349Sobrien size_t cmd_len, 25968349Sobrien const char *cmd) 26068349Sobrien{ 26168349Sobrien krb5_principal server; 26268349Sobrien krb5_data cksum_data; 26368349Sobrien int status; 26468349Sobrien size_t len; 26568349Sobrien krb5_auth_context auth_context = NULL; 26668349Sobrien 26768349Sobrien status = krb5_sname_to_principal(context, 26868349Sobrien hostname, 26968349Sobrien "host", 27068349Sobrien KRB5_NT_SRV_HST, 27174784Sobrien &server); 27274784Sobrien if (status) { 27374784Sobrien warnx ("%s: %s", hostname, krb5_get_err_text(context, status)); 27474784Sobrien return 1; 27574784Sobrien } 27684685Sobrien 27774784Sobrien cksum_data.length = asprintf ((char **)&cksum_data.data, 27874784Sobrien "%u:%s%s%s", 27974784Sobrien ntohs(socket_get_port(thataddr)), 28074784Sobrien do_encrypt ? "-x " : "", 28174784Sobrien cmd, 28274784Sobrien remote_user); 28374784Sobrien 28474784Sobrien status = krb5_sendauth (context, 28574784Sobrien &auth_context, 28674784Sobrien &s, 28774784Sobrien KCMD_VERSION, 28874784Sobrien NULL, 28974784Sobrien server, 29074784Sobrien do_encrypt ? AP_OPTS_MUTUAL_REQUIRED : 0, 29174784Sobrien &cksum_data, 29274784Sobrien NULL, 29374784Sobrien NULL, 29474784Sobrien NULL, 29574784Sobrien NULL, 29674784Sobrien NULL); 29774784Sobrien if (status) { 29874784Sobrien warnx ("%s: %s", hostname, krb5_get_err_text(context, status)); 29974784Sobrien return 1; 30074784Sobrien } 30174784Sobrien 30274784Sobrien status = krb5_auth_con_getkey (context, auth_context, &keyblock); 30374784Sobrien if (status) { 30474784Sobrien warnx ("krb5_auth_con_getkey: %s", krb5_get_err_text(context, status)); 30574784Sobrien return 1; 30674784Sobrien } 30774784Sobrien 30880588Sobrien status = krb5_auth_con_setaddrs_from_fd (context, 30984685Sobrien auth_context, 31084685Sobrien &s); 31184685Sobrien if (status) { 31280588Sobrien warnx("krb5_auth_con_setaddrs_from_fd: %s", 31384685Sobrien krb5_get_err_text(context, status)); 31484685Sobrien return(1); 31584685Sobrien } 31684685Sobrien 31784685Sobrien status = krb5_crypto_init(context, keyblock, 0, &crypto); 31884685Sobrien if(status) { 31984685Sobrien warnx ("krb5_crypto_init: %s", krb5_get_err_text(context, status)); 32084685Sobrien return 1; 32184685Sobrien } 32284685Sobrien 32384685Sobrien len = strlen(remote_user) + 1; 32484685Sobrien if (net_write (s, remote_user, len) != len) { 32584685Sobrien warn ("write"); 32684685Sobrien return 1; 32784685Sobrien } 32884685Sobrien if (do_encrypt && net_write (s, "-x ", 3) != 3) { 32984685Sobrien warn ("write"); 33084685Sobrien return 1; 33184685Sobrien } 33284685Sobrien if (net_write (s, cmd, cmd_len) != cmd_len) { 33384685Sobrien warn ("write"); 33484685Sobrien return 1; 33584685Sobrien } 33684685Sobrien 33784685Sobrien if (do_unique_tkfile) { 33884685Sobrien if (net_write (s, tkfile, strlen(tkfile)) != strlen(tkfile)) { 339110949Sobrien warn ("write"); 340110949Sobrien return 1; 341110949Sobrien } 342110949Sobrien } 343110949Sobrien len = strlen(local_user) + 1; 344110949Sobrien if (net_write (s, local_user, len) != len) { 345110949Sobrien warn ("write"); 346110949Sobrien return 1; 347110949Sobrien } 348110949Sobrien 349110949Sobrien if (!do_forward 350110949Sobrien || krb5_forward_cred (auth_context, s, hostname, do_forwardable)) { 351110949Sobrien /* Empty forwarding info */ 352110949Sobrien 353110949Sobrien u_char zero[4] = {0, 0, 0, 0}; 354110949Sobrien write (s, &zero, 4); 355110949Sobrien } 356110949Sobrien krb5_auth_con_free (context, auth_context); 357110949Sobrien return 0; 358110949Sobrien} 359110949Sobrien 360110949Sobrienstatic int 361110949Sobriensend_broken_auth(int s, 362 struct sockaddr *thisaddr, 363 struct sockaddr *thataddr, 364 const char *hostname, 365 const char *remote_user, 366 const char *local_user, 367 size_t cmd_len, 368 const char *cmd) 369{ 370 size_t len; 371 372 len = strlen(local_user) + 1; 373 if (net_write (s, local_user, len) != len) { 374 warn ("write"); 375 return 1; 376 } 377 len = strlen(remote_user) + 1; 378 if (net_write (s, remote_user, len) != len) { 379 warn ("write"); 380 return 1; 381 } 382 if (net_write (s, cmd, cmd_len) != cmd_len) { 383 warn ("write"); 384 return 1; 385 } 386 return 0; 387} 388 389static int 390proto (int s, int errsock, 391 const char *hostname, const char *local_user, const char *remote_user, 392 const char *cmd, size_t cmd_len, 393 int (*auth_func)(int s, 394 struct sockaddr *this, struct sockaddr *that, 395 const char *hostname, const char *remote_user, 396 const char *local_user, size_t cmd_len, 397 const char *cmd)) 398{ 399 int errsock2; 400 char buf[BUFSIZ]; 401 char *p; 402 size_t len; 403 char reply; 404 struct sockaddr_storage thisaddr_ss; 405 struct sockaddr *thisaddr = (struct sockaddr *)&thisaddr_ss; 406 struct sockaddr_storage thataddr_ss; 407 struct sockaddr *thataddr = (struct sockaddr *)&thataddr_ss; 408 struct sockaddr_storage erraddr_ss; 409 struct sockaddr *erraddr = (struct sockaddr *)&erraddr_ss; 410 socklen_t addrlen; 411 int ret; 412 413 addrlen = sizeof(thisaddr_ss); 414 if (getsockname (s, thisaddr, &addrlen) < 0) { 415 warn ("getsockname(%s)", hostname); 416 return 1; 417 } 418 addrlen = sizeof(thataddr_ss); 419 if (getpeername (s, thataddr, &addrlen) < 0) { 420 warn ("getpeername(%s)", hostname); 421 return 1; 422 } 423 424 if (errsock != -1) { 425 426 addrlen = sizeof(erraddr_ss); 427 if (getsockname (errsock, erraddr, &addrlen) < 0) { 428 warn ("getsockname"); 429 return 1; 430 } 431 432 if (listen (errsock, 1) < 0) { 433 warn ("listen"); 434 return 1; 435 } 436 437 p = buf; 438 snprintf (p, sizeof(buf), "%u", 439 ntohs(socket_get_port(erraddr))); 440 len = strlen(buf) + 1; 441 if(net_write (s, buf, len) != len) { 442 warn ("write"); 443 close (errsock); 444 return 1; 445 } 446 447 448 for (;;) { 449 fd_set fdset; 450 451 if (errsock >= FD_SETSIZE || s >= FD_SETSIZE) 452 errx (1, "fd too large"); 453 454 FD_ZERO(&fdset); 455 FD_SET(errsock, &fdset); 456 FD_SET(s, &fdset); 457 458 ret = select (max(errsock, s) + 1, &fdset, NULL, NULL, NULL); 459 if (ret < 0) { 460 if (errno == EINTR) 461 continue; 462 warn ("select"); 463 close (errsock); 464 return 1; 465 } 466 if (FD_ISSET(errsock, &fdset)) { 467 errsock2 = accept (errsock, NULL, NULL); 468 close (errsock); 469 if (errsock2 < 0) { 470 warn ("accept"); 471 return 1; 472 } 473 break; 474 } 475 476 /* 477 * there should not arrive any data on this fd so if it's 478 * readable it probably indicates that the other side when 479 * away. 480 */ 481 482 if (FD_ISSET(s, &fdset)) { 483 warnx ("socket closed"); 484 close (errsock); 485 errsock2 = -1; 486 break; 487 } 488 } 489 } else { 490 if (net_write (s, "0", 2) != 2) { 491 warn ("write"); 492 return 1; 493 } 494 errsock2 = -1; 495 } 496 497 if ((*auth_func)(s, thisaddr, thataddr, hostname, 498 remote_user, local_user, 499 cmd_len, cmd)) { 500 close (errsock2); 501 return 1; 502 } 503 504 ret = net_read (s, &reply, 1); 505 if (ret < 0) { 506 warn ("read"); 507 close (errsock2); 508 return 1; 509 } else if (ret == 0) { 510 warnx ("unexpected EOF from %s", hostname); 511 close (errsock2); 512 return 1; 513 } 514 if (reply != 0) { 515 516 warnx ("Error from rshd at %s:", hostname); 517 518 while ((ret = read (s, buf, sizeof(buf))) > 0) 519 write (STDOUT_FILENO, buf, ret); 520 write (STDOUT_FILENO,"\n",1); 521 close (errsock2); 522 return 1; 523 } 524 525 return loop (s, errsock2); 526} 527 528/* 529 * Return in `res' a copy of the concatenation of `argc, argv' into 530 * malloced space. */ 531 532static size_t 533construct_command (char **res, int argc, char **argv) 534{ 535 int i; 536 size_t len = 0; 537 char *tmp; 538 539 for (i = 0; i < argc; ++i) 540 len += strlen(argv[i]) + 1; 541 len = max (1, len); 542 tmp = malloc (len); 543 if (tmp == NULL) 544 errx (1, "malloc %u failed", len); 545 546 *tmp = '\0'; 547 for (i = 0; i < argc - 1; ++i) { 548 strcat (tmp, argv[i]); 549 strcat (tmp, " "); 550 } 551 if (argc > 0) 552 strcat (tmp, argv[argc-1]); 553 *res = tmp; 554 return len; 555} 556 557static char * 558print_addr (const struct sockaddr_in *sin) 559{ 560 char addr_str[256]; 561 char *res; 562 563 inet_ntop (AF_INET, &sin->sin_addr, addr_str, sizeof(addr_str)); 564 res = strdup(addr_str); 565 if (res == NULL) 566 errx (1, "malloc: out of memory"); 567 return res; 568} 569 570static int 571doit_broken (int argc, 572 char **argv, 573 int optind, 574 const char *host, 575 const char *remote_user, 576 const char *local_user, 577 int port, 578 int priv_socket1, 579 int priv_socket2, 580 const char *cmd, 581 size_t cmd_len) 582{ 583 struct addrinfo *ai, *a; 584 struct addrinfo hints; 585 int error; 586 char portstr[NI_MAXSERV]; 587 588 if (priv_socket1 < 0) { 589 warnx ("unable to bind reserved port: is rsh setuid root?"); 590 return 1; 591 } 592 593 memset (&hints, 0, sizeof(hints)); 594 hints.ai_socktype = SOCK_STREAM; 595 hints.ai_protocol = IPPROTO_TCP; 596 hints.ai_family = AF_INET; 597 598 snprintf (portstr, sizeof(portstr), "%u", ntohs(port)); 599 600 error = getaddrinfo (host, portstr, &hints, &ai); 601 if (error) { 602 warnx ("%s: %s", host, gai_strerror(error)); 603 return 1; 604 } 605 606 if (connect (priv_socket1, ai->ai_addr, ai->ai_addrlen) < 0) { 607 if (ai->ai_next == NULL) { 608 freeaddrinfo (ai); 609 return 1; 610 } 611 612 close(priv_socket1); 613 close(priv_socket2); 614 615 for (a = ai->ai_next; a != NULL; a = a->ai_next) { 616 pid_t pid; 617 618 pid = fork(); 619 if (pid < 0) 620 err (1, "fork"); 621 else if(pid == 0) { 622 char **new_argv; 623 int i = 0; 624 struct sockaddr_in *sin = (struct sockaddr_in *)a->ai_addr; 625 626 new_argv = malloc((argc + 2) * sizeof(*new_argv)); 627 if (new_argv == NULL) 628 errx (1, "malloc: out of memory"); 629 new_argv[i] = argv[i]; 630 ++i; 631 if (optind == i) 632 new_argv[i++] = print_addr (sin); 633 new_argv[i++] = "-K"; 634 for(; i <= argc; ++i) 635 new_argv[i] = argv[i - 1]; 636 if (optind > 1) 637 new_argv[optind + 1] = print_addr(sin); 638 new_argv[argc + 1] = NULL; 639 execv(PATH_RSH, new_argv); 640 err(1, "execv(%s)", PATH_RSH); 641 } else { 642 int status; 643 644 freeaddrinfo (ai); 645 646 while(waitpid(pid, &status, 0) < 0) 647 ; 648 if(WIFEXITED(status) && WEXITSTATUS(status) == 0) 649 return 0; 650 } 651 } 652 return 1; 653 } else { 654 int ret; 655 656 freeaddrinfo (ai); 657 658 ret = proto (priv_socket1, priv_socket2, 659 argv[optind], 660 local_user, remote_user, 661 cmd, cmd_len, 662 send_broken_auth); 663 return ret; 664 } 665} 666 667static int 668doit (const char *hostname, 669 const char *remote_user, 670 const char *local_user, 671 int port, 672 const char *cmd, 673 size_t cmd_len, 674 int do_errsock, 675 int (*auth_func)(int s, 676 struct sockaddr *this, struct sockaddr *that, 677 const char *hostname, const char *remote_user, 678 const char *local_user, size_t cmd_len, 679 const char *cmd)) 680{ 681 struct addrinfo *ai, *a; 682 struct addrinfo hints; 683 int error; 684 char portstr[NI_MAXSERV]; 685 int ret; 686 687 memset (&hints, 0, sizeof(hints)); 688 hints.ai_socktype = SOCK_STREAM; 689 hints.ai_protocol = IPPROTO_TCP; 690 691 snprintf (portstr, sizeof(portstr), "%u", ntohs(port)); 692 693 error = getaddrinfo (hostname, portstr, &hints, &ai); 694 if (error) { 695 errx (1, "%s: %s", hostname, gai_strerror(error)); 696 return -1; 697 } 698 699 for (a = ai; a != NULL; a = a->ai_next) { 700 int s; 701 int errsock; 702 703 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 704 if (s < 0) 705 continue; 706 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 707 warn ("connect(%s)", hostname); 708 close (s); 709 continue; 710 } 711 if (do_errsock) { 712 struct addrinfo *ea, *eai; 713 struct addrinfo hints; 714 715 memset (&hints, 0, sizeof(hints)); 716 hints.ai_socktype = a->ai_socktype; 717 hints.ai_protocol = a->ai_protocol; 718 hints.ai_family = a->ai_family; 719 hints.ai_flags = AI_PASSIVE; 720 721 errsock = -1; 722 723 error = getaddrinfo (NULL, "0", &hints, &eai); 724 if (error) 725 errx (1, "getaddrinfo: %s", gai_strerror(error)); 726 for (ea = eai; ea != NULL; ea = ea->ai_next) { 727 errsock = socket (ea->ai_family, ea->ai_socktype, 728 ea->ai_protocol); 729 if (errsock < 0) 730 continue; 731 if (bind (errsock, ea->ai_addr, ea->ai_addrlen) < 0) 732 err (1, "bind"); 733 break; 734 } 735 if (errsock < 0) 736 err (1, "socket"); 737 freeaddrinfo (eai); 738 } else 739 errsock = -1; 740 741 freeaddrinfo (ai); 742 ret = proto (s, errsock, 743 hostname, 744 local_user, remote_user, 745 cmd, cmd_len, auth_func); 746 close (s); 747 return ret; 748 } 749 warnx ("failed to contact %s", hostname); 750 freeaddrinfo (ai); 751 return -1; 752} 753 754#ifdef KRB4 755static int use_v4 = -1; 756#endif 757static int use_v5 = -1; 758static int use_only_broken = 0; 759static int use_broken = 1; 760static char *port_str; 761static const char *user; 762static int do_version; 763static int do_help; 764static int do_errsock = 1; 765 766struct getargs args[] = { 767#ifdef KRB4 768 { "krb4", '4', arg_flag, &use_v4, "Use Kerberos V4", 769 NULL }, 770#endif 771 { "krb5", '5', arg_flag, &use_v5, "Use Kerberos V5", 772 NULL }, 773 { "broken", 'K', arg_flag, &use_only_broken, "Use priv port", 774 NULL }, 775 { "input", 'n', arg_negative_flag, &input, "Close stdin", 776 NULL }, 777 { "encrypt", 'x', arg_flag, &do_encrypt, "Encrypt connection", 778 NULL }, 779 { NULL, 'z', arg_negative_flag, &do_encrypt, 780 "Don't encrypt connection", NULL }, 781 { "forward", 'f', arg_flag, &do_forward, "Forward credentials", 782 NULL }, 783 { "forward", 'G', arg_negative_flag,&do_forward, "Forward credentials", 784 NULL }, 785 { "forwardable", 'F', arg_flag, &do_forwardable, 786 "Forward forwardable credentials", NULL }, 787 { "unique", 'u', arg_flag, &do_unique_tkfile, 788 "Use unique remote tkfile", NULL }, 789 { "tkfile", 'U', arg_string, &unique_tkfile, 790 "Use that remote tkfile", NULL }, 791 { "port", 'p', arg_string, &port_str, "Use this port", 792 "number-or-service" }, 793 { "user", 'l', arg_string, &user, "Run as this user", 794 NULL }, 795 { "stderr", 'e', arg_negative_flag, &do_errsock, "don't open stderr"}, 796 { "version", 0, arg_flag, &do_version, "Print version", 797 NULL }, 798 { "help", 0, arg_flag, &do_help, NULL, 799 NULL } 800}; 801 802static void 803usage (int ret) 804{ 805 arg_printusage (args, 806 sizeof(args) / sizeof(args[0]), 807 NULL, 808 "host [command]"); 809 exit (ret); 810} 811 812/* 813 * 814 */ 815 816int 817main(int argc, char **argv) 818{ 819 int priv_port1, priv_port2; 820 int priv_socket1, priv_socket2; 821 int port = 0; 822 int optind = 0; 823 int ret = 1; 824 char *cmd; 825 size_t cmd_len; 826 const char *local_user; 827 char *host = NULL; 828 int host_index = -1; 829 int status; 830 uid_t uid; 831 832 priv_port1 = priv_port2 = IPPORT_RESERVED-1; 833 priv_socket1 = rresvport(&priv_port1); 834 priv_socket2 = rresvport(&priv_port2); 835 uid = getuid (); 836 if (setuid (uid) || (uid != 0 && setuid(0) == 0)) 837 err (1, "setuid"); 838 839 setprogname (argv[0]); 840 841 if (argc >= 2 && argv[1][0] != '-') { 842 host = argv[host_index = 1]; 843 optind = 1; 844 } 845 846 status = krb5_init_context (&context); 847 if (status) 848 errx(1, "krb5_init_context failed: %d", status); 849 850 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, 851 &optind)) 852 usage (1); 853 854 if (do_forwardable == -1) 855 do_forwardable = krb5_config_get_bool (context, NULL, 856 "libdefaults", 857 "forwardable", 858 NULL); 859 860 if (do_forward == -1) 861 do_forward = krb5_config_get_bool (context, NULL, 862 "libdefaults", 863 "forward", 864 NULL); 865 else if (do_forward == 0) 866 do_forwardable = 0; 867 868 if (do_encrypt == -1) 869 do_encrypt = krb5_config_get_bool (context, NULL, 870 "libdefaults", 871 "encrypt", 872 NULL); 873 874 if (do_forwardable) 875 do_forward = 1; 876 877#if defined(KRB4) && defined(KRB5) 878 if(use_v4 == -1 && use_v5 == 1) 879 use_v4 = 0; 880 if(use_v5 == -1 && use_v4 == 1) 881 use_v5 = 0; 882#endif 883 884 if (use_only_broken) { 885#ifdef KRB4 886 use_v4 = 0; 887#endif 888 use_v5 = 0; 889 } 890 891 if (do_help) 892 usage (0); 893 894 if (do_version) { 895 print_version (NULL); 896 return 0; 897 } 898 899 if (do_unique_tkfile && unique_tkfile != NULL) 900 errx (1, "Only one of -u and -U allowed."); 901 902 if (do_unique_tkfile) 903 strcpy(tkfile,"-u "); 904 else if (unique_tkfile != NULL) { 905 if (strchr(unique_tkfile,' ') != NULL) { 906 warnx("Space is not allowed in tkfilename"); 907 usage(1); 908 } 909 do_unique_tkfile = 1; 910 snprintf (tkfile, sizeof(tkfile), "-U %s ", unique_tkfile); 911 } 912 913 if (host == NULL) { 914 if (argc - optind < 1) 915 usage (1); 916 else 917 host = argv[host_index = optind++]; 918 } 919 920 if (optind == argc) { 921 close (priv_socket1); 922 close (priv_socket2); 923 argv[0] = "rlogin"; 924 execvp ("rlogin", argv); 925 err (1, "execvp rlogin"); 926 } 927 928 if (port_str) { 929 struct servent *s = roken_getservbyname (port_str, "tcp"); 930 931 if (s) 932 port = s->s_port; 933 else { 934 char *ptr; 935 936 port = strtol (port_str, &ptr, 10); 937 if (port == 0 && ptr == port_str) 938 errx (1, "Bad port `%s'", port_str); 939 port = htons(port); 940 } 941 } 942 943 local_user = get_default_username (); 944 if (local_user == NULL) 945 errx (1, "who are you?"); 946 947 if (user == NULL) 948 user = local_user; 949 950 cmd_len = construct_command(&cmd, argc - optind, argv + optind); 951 952 /* 953 * Try all different authentication methods 954 */ 955 956 if (ret && use_v5) { 957 int tmp_port; 958 959 if (port) 960 tmp_port = port; 961 else 962 tmp_port = krb5_getportbyname (context, "kshell", "tcp", 544); 963 964 auth_method = AUTH_KRB5; 965 ret = doit (host, user, local_user, tmp_port, cmd, cmd_len, 966 do_errsock, 967 send_krb5_auth); 968 } 969#ifdef KRB4 970 if (ret && use_v4) { 971 int tmp_port; 972 973 if (port) 974 tmp_port = port; 975 else if (do_encrypt) 976 tmp_port = krb5_getportbyname (context, "ekshell", "tcp", 545); 977 else 978 tmp_port = krb5_getportbyname (context, "kshell", "tcp", 544); 979 980 auth_method = AUTH_KRB4; 981 ret = doit (host, user, local_user, tmp_port, cmd, cmd_len, 982 do_errsock, 983 send_krb4_auth); 984 } 985#endif 986 if (ret && use_broken) { 987 int tmp_port; 988 989 if(port) 990 tmp_port = port; 991 else 992 tmp_port = krb5_getportbyname(context, "shell", "tcp", 514); 993 auth_method = AUTH_BROKEN; 994 if (do_encrypt) 995 errx (1, "encryption not supported with priv port authentication"); 996 ret = doit_broken (argc, argv, host_index, host, 997 user, local_user, 998 tmp_port, 999 priv_socket1, 1000 do_errsock ? priv_socket2 : -1, 1001 cmd, cmd_len); 1002 } 1003 return ret; 1004} 1005