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