PageRenderTime 89ms CodeModel.GetById 12ms app.highlight 70ms RepoModel.GetById 1ms app.codeStats 0ms

/crypto/heimdal/appl/rsh/rsh.c

https://bitbucket.org/freebsd/freebsd-head/
C | 1032 lines | 854 code | 121 blank | 57 comment | 241 complexity | adf7a9c7026ec7417e83d5146f65110a MD5 | raw file
   1/*
   2 * Copyright (c) 1997 - 2004 Kungliga Tekniska Hรถgskolan
   3 * (Royal Institute of Technology, Stockholm, Sweden).
   4 * All rights reserved.
   5 *
   6 * Redistribution and use in source and binary forms, with or without
   7 * modification, are permitted provided that the following conditions
   8 * are met:
   9 *
  10 * 1. Redistributions of source code must retain the above copyright
  11 *    notice, this list of conditions and the following disclaimer.
  12 *
  13 * 2. Redistributions in binary form must reproduce the above copyright
  14 *    notice, this list of conditions and the following disclaimer in the
  15 *    documentation and/or other materials provided with the distribution.
  16 *
  17 * 3. Neither the name of the Institute nor the names of its contributors
  18 *    may be used to endorse or promote products derived from this software
  19 *    without specific prior written permission.
  20 *
  21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
  22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
  25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31 * SUCH DAMAGE.
  32 */
  33
  34#include "rsh_locl.h"
  35RCSID("$Id$");
  36
  37enum auth_method auth_method;
  38#if defined(KRB5)
  39int do_encrypt       = -1;
  40#endif
  41#ifdef KRB5
  42int do_unique_tkfile = 0;
  43char *unique_tkfile  = NULL;
  44char tkfile[MAXPATHLEN];
  45int do_forward       = -1;
  46int do_forwardable   = -1;
  47krb5_context context;
  48krb5_keyblock *keyblock;
  49krb5_crypto crypto;
  50#endif
  51int sock_debug	     = 0;
  52
  53#ifdef KRB5
  54static int use_v5 = -1;
  55#endif
  56#if defined(KRB5)
  57static int use_only_broken = 0;
  58#else
  59static int use_only_broken = 1;
  60#endif
  61static int use_broken = 1;
  62static char *port_str;
  63static const char *user;
  64static int do_version;
  65static int do_help;
  66static int do_errsock = 1;
  67#ifdef KRB5
  68static char *protocol_version_str;
  69static int protocol_version = 2;
  70#endif
  71
  72/*
  73 *
  74 */
  75
  76static int input = 1;		/* Read from stdin */
  77
  78static int
  79rsh_loop (int s, int errsock)
  80{
  81    fd_set real_readset;
  82    int count = 1;
  83
  84#ifdef KRB5
  85    if(auth_method == AUTH_KRB5 && protocol_version == 2)
  86	init_ivecs(1, errsock != -1);
  87#endif
  88
  89    if (s >= FD_SETSIZE || (errsock != -1 && errsock >= FD_SETSIZE))
  90	errx (1, "fd too large");
  91
  92    FD_ZERO(&real_readset);
  93    FD_SET(s, &real_readset);
  94    if (errsock != -1) {
  95	FD_SET(errsock, &real_readset);
  96	++count;
  97    }
  98    if(input)
  99	FD_SET(STDIN_FILENO, &real_readset);
 100
 101    for (;;) {
 102	int ret;
 103	fd_set readset;
 104	char buf[RSH_BUFSIZ];
 105
 106	readset = real_readset;
 107	ret = select (max(s, errsock) + 1, &readset, NULL, NULL, NULL);
 108	if (ret < 0) {
 109	    if (errno == EINTR)
 110		continue;
 111	    else
 112		err (1, "select");
 113	}
 114	if (FD_ISSET(s, &readset)) {
 115	    ret = do_read (s, buf, sizeof(buf), ivec_in[0]);
 116	    if (ret < 0)
 117		err (1, "read");
 118	    else if (ret == 0) {
 119		close (s);
 120		FD_CLR(s, &real_readset);
 121		if (--count == 0)
 122		    return 0;
 123	    } else
 124		net_write (STDOUT_FILENO, buf, ret);
 125	}
 126	if (errsock != -1 && FD_ISSET(errsock, &readset)) {
 127	    ret = do_read (errsock, buf, sizeof(buf), ivec_in[1]);
 128	    if (ret < 0)
 129		err (1, "read");
 130	    else if (ret == 0) {
 131		close (errsock);
 132		FD_CLR(errsock, &real_readset);
 133		if (--count == 0)
 134		    return 0;
 135	    } else
 136		net_write (STDERR_FILENO, buf, ret);
 137	}
 138	if (FD_ISSET(STDIN_FILENO, &readset)) {
 139	    ret = read (STDIN_FILENO, buf, sizeof(buf));
 140	    if (ret < 0)
 141		err (1, "read");
 142	    else if (ret == 0) {
 143		close (STDIN_FILENO);
 144		FD_CLR(STDIN_FILENO, &real_readset);
 145		shutdown (s, SHUT_WR);
 146	    } else
 147		do_write (s, buf, ret, ivec_out[0]);
 148	}
 149    }
 150}
 151
 152#ifdef KRB5
 153/*
 154 * Send forward information on `s' for host `hostname', them being
 155 * forwardable themselves if `forwardable'
 156 */
 157
 158static int
 159krb5_forward_cred (krb5_auth_context auth_context,
 160		   int s,
 161		   const char *hostname,
 162		   int forwardable)
 163{
 164    krb5_error_code ret;
 165    krb5_ccache     ccache;
 166    krb5_creds      creds;
 167    krb5_kdc_flags  flags;
 168    krb5_data       out_data;
 169    krb5_principal  principal;
 170
 171    memset (&creds, 0, sizeof(creds));
 172
 173    ret = krb5_cc_default (context, &ccache);
 174    if (ret) {
 175	warnx ("could not forward creds: krb5_cc_default: %s",
 176	       krb5_get_err_text (context, ret));
 177	return 1;
 178    }
 179
 180    ret = krb5_cc_get_principal (context, ccache, &principal);
 181    if (ret) {
 182	warnx ("could not forward creds: krb5_cc_get_principal: %s",
 183	       krb5_get_err_text (context, ret));
 184	return 1;
 185    }
 186
 187    creds.client = principal;
 188
 189    ret = krb5_make_principal(context,
 190			      &creds.server,
 191			      principal->realm,
 192			      "krbtgt",
 193			      principal->realm,
 194			      NULL);
 195
 196    if (ret) {
 197	warnx ("could not forward creds: krb5_make_principal: %s",
 198	       krb5_get_err_text (context, ret));
 199	return 1;
 200    }
 201
 202    creds.times.endtime = 0;
 203
 204    flags.i = 0;
 205    flags.b.forwarded   = 1;
 206    flags.b.forwardable = forwardable;
 207
 208    ret = krb5_get_forwarded_creds (context,
 209				    auth_context,
 210				    ccache,
 211				    flags.i,
 212				    hostname,
 213				    &creds,
 214				    &out_data);
 215    if (ret) {
 216	warnx ("could not forward creds: krb5_get_forwarded_creds: %s",
 217	       krb5_get_err_text (context, ret));
 218	return 1;
 219    }
 220
 221    ret = krb5_write_message (context,
 222			      (void *)&s,
 223			      &out_data);
 224    krb5_data_free (&out_data);
 225
 226    if (ret)
 227	warnx ("could not forward creds: krb5_write_message: %s",
 228	       krb5_get_err_text (context, ret));
 229    return 0;
 230}
 231
 232static int sendauth_version_error;
 233
 234static int
 235send_krb5_auth(int s,
 236	       struct sockaddr *thisaddr,
 237	       struct sockaddr *thataddr,
 238	       const char *hostname,
 239	       const char *remote_user,
 240	       const char *local_user,
 241	       size_t cmd_len,
 242	       const char *cmd)
 243{
 244    krb5_principal server;
 245    krb5_data cksum_data;
 246    int status;
 247    size_t len;
 248    krb5_auth_context auth_context = NULL;
 249    const char *protocol_string = NULL;
 250    krb5_flags ap_opts;
 251    char *str;
 252
 253    status = krb5_sname_to_principal(context,
 254				     hostname,
 255				     "host",
 256				     KRB5_NT_SRV_HST,
 257				     &server);
 258    if (status) {
 259	warnx ("%s: %s", hostname, krb5_get_err_text(context, status));
 260	return 1;
 261    }
 262
 263    if(do_encrypt == -1) {
 264	krb5_appdefault_boolean(context, NULL,
 265				krb5_principal_get_realm(context, server),
 266				"encrypt",
 267				FALSE,
 268				&do_encrypt);
 269    }
 270
 271    cksum_data.length = asprintf (&str,
 272				  "%u:%s%s%s",
 273				  ntohs(socket_get_port(thataddr)),
 274				  do_encrypt ? "-x " : "",
 275				  cmd,
 276				  remote_user);
 277    if (str == NULL) {
 278	warnx ("%s: failed to allocate command", hostname);
 279	return 1;
 280    }
 281    cksum_data.data = str;
 282
 283    ap_opts = 0;
 284
 285    if(do_encrypt)
 286	ap_opts |= AP_OPTS_MUTUAL_REQUIRED;
 287
 288    switch(protocol_version) {
 289    case 2:
 290	ap_opts |= AP_OPTS_USE_SUBKEY;
 291	protocol_string = KCMD_NEW_VERSION;
 292	break;
 293    case 1:
 294	protocol_string = KCMD_OLD_VERSION;
 295	key_usage = KRB5_KU_OTHER_ENCRYPTED;
 296	break;
 297    default:
 298	abort();
 299    }
 300
 301    status = krb5_sendauth (context,
 302			    &auth_context,
 303			    &s,
 304			    protocol_string,
 305			    NULL,
 306			    server,
 307			    ap_opts,
 308			    &cksum_data,
 309			    NULL,
 310			    NULL,
 311			    NULL,
 312			    NULL,
 313			    NULL);
 314
 315    /* do this while we have a principal */
 316    if(do_forward == -1 || do_forwardable == -1) {
 317	krb5_const_realm realm = krb5_principal_get_realm(context, server);
 318	if (do_forwardable == -1)
 319	    krb5_appdefault_boolean(context, NULL, realm,
 320				    "forwardable", FALSE,
 321				    &do_forwardable);
 322	if (do_forward == -1)
 323	    krb5_appdefault_boolean(context, NULL, realm,
 324				    "forward", FALSE,
 325				    &do_forward);
 326    }
 327
 328    krb5_free_principal(context, server);
 329    krb5_data_free(&cksum_data);
 330
 331    if (status) {
 332	if(status == KRB5_SENDAUTH_REJECTED &&
 333	   protocol_version == 2 && protocol_version_str == NULL)
 334	    sendauth_version_error = 1;
 335	else
 336	    krb5_warn(context, status, "%s", hostname);
 337	return 1;
 338    }
 339
 340    status = krb5_auth_con_getlocalsubkey (context, auth_context, &keyblock);
 341    if(keyblock == NULL)
 342	status = krb5_auth_con_getkey (context, auth_context, &keyblock);
 343    if (status) {
 344	warnx ("krb5_auth_con_getkey: %s", krb5_get_err_text(context, status));
 345	return 1;
 346    }
 347
 348    status = krb5_auth_con_setaddrs_from_fd (context,
 349					     auth_context,
 350					     &s);
 351    if (status) {
 352        warnx("krb5_auth_con_setaddrs_from_fd: %s",
 353	      krb5_get_err_text(context, status));
 354        return(1);
 355    }
 356
 357    status = krb5_crypto_init(context, keyblock, 0, &crypto);
 358    if(status) {
 359	warnx ("krb5_crypto_init: %s", krb5_get_err_text(context, status));
 360	return 1;
 361    }
 362
 363    len = strlen(remote_user) + 1;
 364    if (net_write (s, remote_user, len) != len) {
 365	warn ("write");
 366	return 1;
 367    }
 368    if (do_encrypt && net_write (s, "-x ", 3) != 3) {
 369	warn ("write");
 370	return 1;
 371    }
 372    if (net_write (s, cmd, cmd_len) != cmd_len) {
 373	warn ("write");
 374	return 1;
 375    }
 376
 377    if (do_unique_tkfile) {
 378	if (net_write (s, tkfile, strlen(tkfile)) != strlen(tkfile)) {
 379	    warn ("write");
 380	    return 1;
 381	}
 382    }
 383    len = strlen(local_user) + 1;
 384    if (net_write (s, local_user, len) != len) {
 385	warn ("write");
 386	return 1;
 387    }
 388
 389    if (!do_forward
 390	|| krb5_forward_cred (auth_context, s, hostname, do_forwardable)) {
 391	/* Empty forwarding info */
 392
 393	u_char zero[4] = {0, 0, 0, 0};
 394	write (s, &zero, 4);
 395    }
 396    krb5_auth_con_free (context, auth_context);
 397    return 0;
 398}
 399
 400#endif /* KRB5 */
 401
 402static int
 403send_broken_auth(int s,
 404		 struct sockaddr *thisaddr,
 405		 struct sockaddr *thataddr,
 406		 const char *hostname,
 407		 const char *remote_user,
 408		 const char *local_user,
 409		 size_t cmd_len,
 410		 const char *cmd)
 411{
 412    size_t len;
 413
 414    len = strlen(local_user) + 1;
 415    if (net_write (s, local_user, len) != len) {
 416	warn ("write");
 417	return 1;
 418    }
 419    len = strlen(remote_user) + 1;
 420    if (net_write (s, remote_user, len) != len) {
 421	warn ("write");
 422	return 1;
 423    }
 424    if (net_write (s, cmd, cmd_len) != cmd_len) {
 425	warn ("write");
 426	return 1;
 427    }
 428    return 0;
 429}
 430
 431static int
 432proto (int s, int errsock,
 433       const char *hostname, const char *local_user, const char *remote_user,
 434       const char *cmd, size_t cmd_len,
 435       int (*auth_func)(int s,
 436			struct sockaddr *this, struct sockaddr *that,
 437			const char *hostname, const char *remote_user,
 438			const char *local_user, size_t cmd_len,
 439			const char *cmd))
 440{
 441    int errsock2;
 442    char buf[BUFSIZ];
 443    char *p;
 444    size_t len;
 445    char reply;
 446    struct sockaddr_storage thisaddr_ss;
 447    struct sockaddr *thisaddr = (struct sockaddr *)&thisaddr_ss;
 448    struct sockaddr_storage thataddr_ss;
 449    struct sockaddr *thataddr = (struct sockaddr *)&thataddr_ss;
 450    struct sockaddr_storage erraddr_ss;
 451    struct sockaddr *erraddr = (struct sockaddr *)&erraddr_ss;
 452    socklen_t addrlen;
 453    int ret;
 454
 455    addrlen = sizeof(thisaddr_ss);
 456    if (getsockname (s, thisaddr, &addrlen) < 0) {
 457	warn ("getsockname(%s)", hostname);
 458	return 1;
 459    }
 460    addrlen = sizeof(thataddr_ss);
 461    if (getpeername (s, thataddr, &addrlen) < 0) {
 462	warn ("getpeername(%s)", hostname);
 463	return 1;
 464    }
 465
 466    if (errsock != -1) {
 467
 468	addrlen = sizeof(erraddr_ss);
 469	if (getsockname (errsock, erraddr, &addrlen) < 0) {
 470	    warn ("getsockname");
 471	    return 1;
 472	}
 473
 474	if (listen (errsock, 1) < 0) {
 475	    warn ("listen");
 476	    return 1;
 477	}
 478
 479	p = buf;
 480	snprintf (p, sizeof(buf), "%u",
 481		  ntohs(socket_get_port(erraddr)));
 482	len = strlen(buf) + 1;
 483	if(net_write (s, buf, len) != len) {
 484	    warn ("write");
 485	    close (errsock);
 486	    return 1;
 487	}
 488
 489
 490	for (;;) {
 491	    fd_set fdset;
 492
 493	    if (errsock >= FD_SETSIZE || s >= FD_SETSIZE)
 494		errx (1, "fd too large");
 495
 496	    FD_ZERO(&fdset);
 497	    FD_SET(errsock, &fdset);
 498	    FD_SET(s, &fdset);
 499
 500	    ret = select (max(errsock, s) + 1, &fdset, NULL, NULL, NULL);
 501	    if (ret < 0) {
 502		if (errno == EINTR)
 503		    continue;
 504		warn ("select");
 505		close (errsock);
 506		return 1;
 507	    }
 508	    if (FD_ISSET(errsock, &fdset)) {
 509		errsock2 = accept (errsock, NULL, NULL);
 510		close (errsock);
 511		if (errsock2 < 0) {
 512		    warn ("accept");
 513		    return 1;
 514		}
 515		break;
 516	    }
 517
 518	    /*
 519	     * there should not arrive any data on this fd so if it's
 520	     * readable it probably indicates that the other side when
 521	     * away.
 522	     */
 523
 524	    if (FD_ISSET(s, &fdset)) {
 525		warnx ("socket closed");
 526		close (errsock);
 527		errsock2 = -1;
 528		break;
 529	    }
 530	}
 531    } else {
 532	if (net_write (s, "0", 2) != 2) {
 533	    warn ("write");
 534	    return 1;
 535	}
 536	errsock2 = -1;
 537    }
 538
 539    if ((*auth_func)(s, thisaddr, thataddr, hostname,
 540		     remote_user, local_user,
 541		     cmd_len, cmd)) {
 542	close (errsock2);
 543	return 1;
 544    }
 545
 546    ret = net_read (s, &reply, 1);
 547    if (ret < 0) {
 548	warn ("read");
 549	close (errsock2);
 550	return 1;
 551    } else if (ret == 0) {
 552	warnx ("unexpected EOF from %s", hostname);
 553	close (errsock2);
 554	return 1;
 555    }
 556    if (reply != 0) {
 557
 558	warnx ("Error from rshd at %s:", hostname);
 559
 560	while ((ret = read (s, buf, sizeof(buf))) > 0)
 561	    write (STDOUT_FILENO, buf, ret);
 562        write (STDOUT_FILENO,"\n",1);
 563	close (errsock2);
 564	return 1;
 565    }
 566
 567    if (sock_debug) {
 568	int one = 1;
 569	if (setsockopt(s, SOL_SOCKET, SO_DEBUG, (void *)&one, sizeof(one)) < 0)
 570	    warn("setsockopt remote");
 571	if (errsock2 != -1 &&
 572	    setsockopt(errsock2, SOL_SOCKET, SO_DEBUG,
 573		       (void *)&one, sizeof(one)) < 0)
 574	    warn("setsockopt stderr");
 575    }
 576
 577    return rsh_loop (s, errsock2);
 578}
 579
 580/*
 581 * Return in `res' a copy of the concatenation of `argc, argv' into
 582 * malloced space.  */
 583
 584static size_t
 585construct_command (char **res, int argc, char **argv)
 586{
 587    int i;
 588    size_t len = 0;
 589    char *tmp;
 590
 591    for (i = 0; i < argc; ++i)
 592	len += strlen(argv[i]) + 1;
 593    len = max (1, len);
 594    tmp = malloc (len);
 595    if (tmp == NULL)
 596	errx (1, "malloc %lu failed", (unsigned long)len);
 597
 598    *tmp = '\0';
 599    for (i = 0; i < argc - 1; ++i) {
 600	strlcat (tmp, argv[i], len);
 601	strlcat (tmp, " ", len);
 602    }
 603    if (argc > 0)
 604	strlcat (tmp, argv[argc-1], len);
 605    *res = tmp;
 606    return len;
 607}
 608
 609static char *
 610print_addr (const struct sockaddr *sa)
 611{
 612    char addr_str[256];
 613    char *res;
 614    const char *as = NULL;
 615
 616    if(sa->sa_family == AF_INET)
 617	as = inet_ntop (sa->sa_family, &((struct sockaddr_in*)sa)->sin_addr,
 618			addr_str, sizeof(addr_str));
 619#ifdef HAVE_INET6
 620    else if(sa->sa_family == AF_INET6)
 621	as = inet_ntop (sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr,
 622			addr_str, sizeof(addr_str));
 623#endif
 624    if(as == NULL)
 625	return NULL;
 626    res = strdup(as);
 627    if (res == NULL)
 628	errx (1, "malloc: out of memory");
 629    return res;
 630}
 631
 632static int
 633doit_broken (int argc,
 634	     char **argv,
 635	     int hostindex,
 636	     struct addrinfo *ai,
 637	     const char *remote_user,
 638	     const char *local_user,
 639	     int priv_socket1,
 640	     int priv_socket2,
 641	     const char *cmd,
 642	     size_t cmd_len)
 643{
 644    struct addrinfo *a;
 645
 646    if (connect (priv_socket1, ai->ai_addr, ai->ai_addrlen) < 0) {
 647	int save_errno = errno;
 648
 649	close(priv_socket1);
 650	close(priv_socket2);
 651
 652	for (a = ai->ai_next; a != NULL; a = a->ai_next) {
 653	    pid_t pid;
 654	    char *adr = print_addr(a->ai_addr);
 655	    if(adr == NULL)
 656		continue;
 657
 658	    pid = fork();
 659	    if (pid < 0)
 660		err (1, "fork");
 661	    else if(pid == 0) {
 662		char **new_argv;
 663		int i = 0;
 664
 665		new_argv = malloc((argc + 2) * sizeof(*new_argv));
 666		if (new_argv == NULL)
 667		    errx (1, "malloc: out of memory");
 668		new_argv[i] = argv[i];
 669		++i;
 670		if (hostindex == i)
 671		    new_argv[i++] = adr;
 672		new_argv[i++] = "-K";
 673		for(; i <= argc; ++i)
 674		    new_argv[i] = argv[i - 1];
 675		if (hostindex > 1)
 676		    new_argv[hostindex + 1] = adr;
 677		new_argv[argc + 1] = NULL;
 678		execv(PATH_RSH, new_argv);
 679		err(1, "execv(%s)", PATH_RSH);
 680	    } else {
 681		int status;
 682		free(adr);
 683
 684		while(waitpid(pid, &status, 0) < 0)
 685		    ;
 686		if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
 687		    return 0;
 688	    }
 689	}
 690	errno = save_errno;
 691	warn("%s", argv[hostindex]);
 692	return 1;
 693    } else {
 694	int ret;
 695
 696	ret = proto (priv_socket1, priv_socket2,
 697		     argv[hostindex],
 698		     local_user, remote_user,
 699		     cmd, cmd_len,
 700		     send_broken_auth);
 701	return ret;
 702    }
 703}
 704
 705#if defined(KRB5)
 706static int
 707doit (const char *hostname,
 708      struct addrinfo *ai,
 709      const char *remote_user,
 710      const char *local_user,
 711      const char *cmd,
 712      size_t cmd_len,
 713      int (*auth_func)(int s,
 714		       struct sockaddr *this, struct sockaddr *that,
 715		       const char *hostname, const char *remote_user,
 716		       const char *local_user, size_t cmd_len,
 717		       const char *cmd))
 718{
 719    int error;
 720    struct addrinfo *a;
 721    int socketfailed = 1;
 722    int ret;
 723
 724    for (a = ai; a != NULL; a = a->ai_next) {
 725	int s;
 726	int errsock;
 727
 728	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
 729	if (s < 0)
 730	    continue;
 731	socketfailed = 0;
 732	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
 733	    char addr[128];
 734	    if(getnameinfo(a->ai_addr, a->ai_addrlen,
 735			   addr, sizeof(addr), NULL, 0, NI_NUMERICHOST) == 0)
 736		warn ("connect(%s [%s])", hostname, addr);
 737	    else
 738		warn ("connect(%s)", hostname);
 739	    close (s);
 740	    continue;
 741	}
 742	if (do_errsock) {
 743	    struct addrinfo *ea, *eai;
 744	    struct addrinfo hints;
 745
 746	    memset (&hints, 0, sizeof(hints));
 747	    hints.ai_socktype = a->ai_socktype;
 748	    hints.ai_protocol = a->ai_protocol;
 749	    hints.ai_family   = a->ai_family;
 750	    hints.ai_flags    = AI_PASSIVE;
 751
 752	    errsock = -1;
 753
 754	    error = getaddrinfo (NULL, "0", &hints, &eai);
 755	    if (error)
 756		errx (1, "getaddrinfo: %s", gai_strerror(error));
 757	    for (ea = eai; ea != NULL; ea = ea->ai_next) {
 758		errsock = socket (ea->ai_family, ea->ai_socktype,
 759				  ea->ai_protocol);
 760		if (errsock < 0)
 761		    continue;
 762		if (bind (errsock, ea->ai_addr, ea->ai_addrlen) < 0)
 763		    err (1, "bind");
 764		break;
 765	    }
 766	    if (errsock < 0)
 767		err (1, "socket");
 768	    freeaddrinfo (eai);
 769	} else
 770	    errsock = -1;
 771
 772	ret = proto (s, errsock,
 773		     hostname,
 774		     local_user, remote_user,
 775		     cmd, cmd_len, auth_func);
 776	close (s);
 777	return ret;
 778    }
 779    if(socketfailed)
 780	warnx ("failed to contact %s", hostname);
 781    return -1;
 782}
 783#endif /* KRB5 */
 784
 785struct getargs args[] = {
 786#ifdef KRB5
 787    { "krb5",	'5', arg_flag,		&use_v5,	"Use Kerberos V5" },
 788    { "forward", 'f', arg_flag,		&do_forward,	"Forward credentials [krb5]"},
 789    { "forwardable", 'F', arg_flag,	&do_forwardable,
 790      "Forward forwardable credentials [krb5]" },
 791    { NULL, 'G', arg_negative_flag,&do_forward,	"Don't forward credentials" },
 792    { "unique", 'u', arg_flag,	&do_unique_tkfile,
 793      "Use unique remote credentials cache [krb5]" },
 794    { "tkfile", 'U', arg_string,  &unique_tkfile,
 795      "Specifies remote credentials cache [krb5]" },
 796    { "protocol", 'P', arg_string,      &protocol_version_str,
 797      "Protocol version [krb5]", "protocol" },
 798#endif
 799    { "broken", 'K', arg_flag,		&use_only_broken, "Use only priv port" },
 800#if defined(KRB5)
 801    { "encrypt", 'x', arg_flag,		&do_encrypt,	"Encrypt connection" },
 802    { NULL, 	'z', arg_negative_flag,      &do_encrypt,
 803      "Don't encrypt connection", NULL },
 804#endif
 805    { NULL,	'd', arg_flag,		&sock_debug, "Enable socket debugging" },
 806    { "input",	'n', arg_negative_flag,	&input,		"Close stdin" },
 807    { "port",	'p', arg_string,	&port_str,	"Use this port",
 808      "port" },
 809    { "user",	'l', arg_string,	&user,		"Run as this user", "login" },
 810    { "stderr", 'e', arg_negative_flag, &do_errsock,	"Don't open stderr"},
 811#ifdef KRB5
 812#endif
 813    { "version", 0,  arg_flag,		&do_version,	NULL },
 814    { "help",	 0,  arg_flag,		&do_help,	NULL }
 815};
 816
 817static void
 818usage (int ret)
 819{
 820    arg_printusage (args,
 821		    sizeof(args) / sizeof(args[0]),
 822		    NULL,
 823		    "[login@]host [command]");
 824    exit (ret);
 825}
 826
 827/*
 828 *
 829 */
 830
 831int
 832main(int argc, char **argv)
 833{
 834    int priv_port1, priv_port2;
 835    int priv_socket1, priv_socket2;
 836    int argindex = 0;
 837    int error;
 838    struct addrinfo hints, *ai;
 839    int ret = 1;
 840    char *cmd;
 841    char *tmp;
 842    size_t cmd_len;
 843    const char *local_user;
 844    char *host = NULL;
 845    int host_index = -1;
 846#ifdef KRB5
 847    int status;
 848#endif
 849    uid_t uid;
 850
 851    priv_port1 = priv_port2 = IPPORT_RESERVED-1;
 852    priv_socket1 = rresvport(&priv_port1);
 853    priv_socket2 = rresvport(&priv_port2);
 854    uid = getuid ();
 855    if (setuid (uid) || (uid != 0 && setuid(0) == 0))
 856	err (1, "setuid");
 857
 858    setprogname (argv[0]);
 859
 860    if (argc >= 2 && argv[1][0] != '-') {
 861	host = argv[host_index = 1];
 862	argindex = 1;
 863    }
 864
 865    if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
 866		&argindex))
 867	usage (1);
 868
 869    if (do_help)
 870	usage (0);
 871
 872    if (do_version) {
 873	print_version (NULL);
 874	return 0;
 875    }
 876
 877#ifdef KRB5
 878    if(protocol_version_str != NULL) {
 879	if(strcasecmp(protocol_version_str, "N") == 0)
 880	    protocol_version = 2;
 881	else if(strcasecmp(protocol_version_str, "O") == 0)
 882	    protocol_version = 1;
 883	else {
 884	    char *end;
 885	    int v;
 886	    v = strtol(protocol_version_str, &end, 0);
 887	    if(*end != '\0' || (v != 1 && v != 2)) {
 888		errx(1, "unknown protocol version \"%s\"",
 889		     protocol_version_str);
 890	    }
 891	    protocol_version = v;
 892	}
 893    }
 894
 895    status = krb5_init_context (&context);
 896    if (status) {
 897	if(use_v5 == 1)
 898	    errx(1, "krb5_init_context failed: %d", status);
 899	else
 900	    use_v5 = 0;
 901    }
 902
 903    /* request for forwardable on the command line means we should
 904       also forward */
 905    if (do_forwardable == 1)
 906	do_forward = 1;
 907
 908#endif
 909
 910    if (use_only_broken) {
 911#ifdef KRB5
 912	use_v5 = 0;
 913#endif
 914    }
 915
 916    if(priv_socket1 < 0) {
 917	if (use_only_broken)
 918	    errx (1, "unable to bind reserved port: is rsh setuid root?");
 919	use_broken = 0;
 920    }
 921
 922#if defined(KRB5)
 923    if (do_encrypt == 1 && use_only_broken)
 924	errx (1, "encryption not supported with old style authentication");
 925#endif
 926
 927
 928
 929#ifdef KRB5
 930    if (do_unique_tkfile && unique_tkfile != NULL)
 931	errx (1, "Only one of -u and -U allowed.");
 932
 933    if (do_unique_tkfile)
 934	strlcpy(tkfile,"-u ", sizeof(tkfile));
 935    else if (unique_tkfile != NULL) {
 936	if (strchr(unique_tkfile,' ') != NULL) {
 937	    warnx("Space is not allowed in tkfilename");
 938	    usage(1);
 939	}
 940	do_unique_tkfile = 1;
 941	snprintf (tkfile, sizeof(tkfile), "-U %s ", unique_tkfile);
 942    }
 943#endif
 944
 945    if (host == NULL) {
 946	if (argc - argindex < 1)
 947	    usage (1);
 948	else
 949	    host = argv[host_index = argindex++];
 950    }
 951
 952    if((tmp = strchr(host, '@')) != NULL) {
 953	*tmp++ = '\0';
 954	user = host;
 955	host = tmp;
 956    }
 957
 958    if (argindex == argc) {
 959	close (priv_socket1);
 960	close (priv_socket2);
 961	argv[0] = "rlogin";
 962	execvp ("rlogin", argv);
 963	err (1, "execvp rlogin");
 964    }
 965
 966    local_user = get_default_username ();
 967    if (local_user == NULL)
 968	errx (1, "who are you?");
 969
 970    if (user == NULL)
 971	user = local_user;
 972
 973    cmd_len = construct_command(&cmd, argc - argindex, argv + argindex);
 974
 975    /*
 976     * Try all different authentication methods
 977     */
 978
 979#ifdef KRB5
 980    if (ret && use_v5) {
 981	memset (&hints, 0, sizeof(hints));
 982	hints.ai_socktype = SOCK_STREAM;
 983	hints.ai_protocol = IPPROTO_TCP;
 984
 985	if(port_str == NULL) {
 986	    error = getaddrinfo(host, "kshell", &hints, &ai);
 987	    if(error == EAI_NONAME)
 988		error = getaddrinfo(host, "544", &hints, &ai);
 989	} else
 990	    error = getaddrinfo(host, port_str, &hints, &ai);
 991
 992	if(error)
 993	    errx (1, "getaddrinfo: %s", gai_strerror(error));
 994
 995	auth_method = AUTH_KRB5;
 996      again:
 997	ret = doit (host, ai, user, local_user, cmd, cmd_len,
 998		    send_krb5_auth);
 999	if(ret != 0 && sendauth_version_error &&
1000	   protocol_version == 2) {
1001	    protocol_version = 1;
1002	    goto again;
1003	}
1004	freeaddrinfo(ai);
1005    }
1006#endif
1007    if (ret && use_broken) {
1008	memset (&hints, 0, sizeof(hints));
1009	hints.ai_socktype = SOCK_STREAM;
1010	hints.ai_protocol = IPPROTO_TCP;
1011
1012	if(port_str == NULL) {
1013	    error = getaddrinfo(host, "shell", &hints, &ai);
1014	    if(error == EAI_NONAME)
1015		error = getaddrinfo(host, "514", &hints, &ai);
1016	} else
1017	    error = getaddrinfo(host, port_str, &hints, &ai);
1018
1019	if(error)
1020	    errx (1, "getaddrinfo: %s", gai_strerror(error));
1021
1022	auth_method = AUTH_BROKEN;
1023	ret = doit_broken (argc, argv, host_index, ai,
1024			   user, local_user,
1025			   priv_socket1,
1026			   do_errsock ? priv_socket2 : -1,
1027			   cmd, cmd_len);
1028	freeaddrinfo(ai);
1029    }
1030    free(cmd);
1031    return ret;
1032}