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