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