2 * Copyright (c) 1997, 1998, 1999 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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
37 enum auth_method auth_method
;
41 int do_unique_tkfile
= 0;
42 char *unique_tkfile
= NULL
;
43 char tkfile
[MAXPATHLEN
];
45 krb5_keyblock
*keyblock
;
47 des_key_schedule schedule
;
55 static int input
= 1; /* Read from stdin */
58 loop (int s
, int errsock
)
63 FD_ZERO(&real_readset
);
64 FD_SET(s
, &real_readset
);
66 FD_SET(errsock
, &real_readset
);
70 FD_SET(STDIN_FILENO
, &real_readset
);
77 readset
= real_readset
;
78 ret
= select (max(s
, errsock
) + 1, &readset
, NULL
, NULL
, NULL
);
85 if (FD_ISSET(s
, &readset
)) {
86 ret
= do_read (s
, buf
, sizeof(buf
));
91 FD_CLR(s
, &real_readset
);
95 net_write (STDOUT_FILENO
, buf
, ret
);
97 if (errsock
!= -1 && FD_ISSET(errsock
, &readset
)) {
98 ret
= do_read (errsock
, buf
, sizeof(buf
));
103 FD_CLR(errsock
, &real_readset
);
107 net_write (STDERR_FILENO
, buf
, ret
);
109 if (FD_ISSET(STDIN_FILENO
, &readset
)) {
110 ret
= read (STDIN_FILENO
, buf
, sizeof(buf
));
114 close (STDIN_FILENO
);
115 FD_CLR(STDIN_FILENO
, &real_readset
);
116 shutdown (s
, SHUT_WR
);
118 do_write (s
, buf
, ret
);
125 send_krb4_auth(int s
,
126 struct sockaddr
*thisaddr
,
127 struct sockaddr
*thataddr
,
128 const char *hostname
,
129 const char *remote_user
,
130 const char *local_user
,
140 status
= krb_sendauth (do_encrypt
? KOPT_DO_MUTUAL
: 0,
142 (char *)hostname
, krb_realmofhost (hostname
),
143 getpid(), &msg
, &cred
, schedule
,
144 (struct sockaddr_in
*)thisaddr
,
145 (struct sockaddr_in
*)thataddr
,
147 if (status
!= KSUCCESS
) {
148 warnx ("%s: %s", hostname
, krb_get_err_text(status
));
151 memcpy (iv
, cred
.session
, sizeof(iv
));
153 len
= strlen(remote_user
) + 1;
154 if (net_write (s
, remote_user
, len
) != len
) {
158 if (net_write (s
, cmd
, cmd_len
) != cmd_len
) {
167 * Send forward information on `s' for host `hostname', them being
168 * forwardable themselves if `forwardable'
172 krb5_forward_cred (krb5_auth_context auth_context
,
174 const char *hostname
,
180 krb5_kdc_flags flags
;
182 krb5_principal principal
;
184 memset (&creds
, 0, sizeof(creds
));
186 ret
= krb5_cc_default (context
, &ccache
);
188 warnx ("could not forward creds: krb5_cc_default: %s",
189 krb5_get_err_text (context
, ret
));
193 ret
= krb5_cc_get_principal (context
, ccache
, &principal
);
195 warnx ("could not forward creds: krb5_cc_get_principal: %s",
196 krb5_get_err_text (context
, ret
));
200 creds
.client
= principal
;
202 ret
= krb5_build_principal (context
,
204 strlen(principal
->realm
),
211 warnx ("could not forward creds: krb5_build_principal: %s",
212 krb5_get_err_text (context
, ret
));
216 creds
.times
.endtime
= 0;
219 flags
.b
.forwarded
= 1;
220 flags
.b
.forwardable
= forwardable
;
222 ret
= krb5_get_forwarded_creds (context
,
230 warnx ("could not forward creds: krb5_get_forwarded_creds: %s",
231 krb5_get_err_text (context
, ret
));
235 ret
= krb5_write_message (context
,
238 krb5_data_free (&out_data
);
241 warnx ("could not forward creds: krb5_write_message: %s",
242 krb5_get_err_text (context
, ret
));
247 send_krb5_auth(int s
,
248 struct sockaddr
*thisaddr
,
249 struct sockaddr
*thataddr
,
250 const char *hostname
,
251 const char *remote_user
,
252 const char *local_user
,
256 krb5_principal server
;
257 krb5_data cksum_data
;
260 krb5_auth_context auth_context
= NULL
;
262 status
= krb5_sname_to_principal(context
,
268 warnx ("%s: %s", hostname
, krb5_get_err_text(context
, status
));
272 cksum_data
.length
= asprintf ((char **)&cksum_data
.data
,
274 ntohs(socket_get_port(thataddr
)),
275 do_encrypt
? "-x " : "",
279 status
= krb5_sendauth (context
,
285 do_encrypt
? AP_OPTS_MUTUAL_REQUIRED
: 0,
293 warnx ("%s: %s", hostname
, krb5_get_err_text(context
, status
));
297 status
= krb5_auth_con_getkey (context
, auth_context
, &keyblock
);
299 warnx ("krb5_auth_con_getkey: %s", krb5_get_err_text(context
, status
));
303 status
= krb5_auth_con_setaddrs_from_fd (context
,
307 warnx("krb5_auth_con_setaddrs_from_fd: %s",
308 krb5_get_err_text(context
, status
));
312 status
= krb5_crypto_init(context
, keyblock
, 0, &crypto
);
314 warnx ("krb5_crypto_init: %s", krb5_get_err_text(context
, status
));
318 len
= strlen(remote_user
) + 1;
319 if (net_write (s
, remote_user
, len
) != len
) {
323 if (do_encrypt
&& net_write (s
, "-x ", 3) != 3) {
327 if (net_write (s
, cmd
, cmd_len
) != cmd_len
) {
332 if (do_unique_tkfile
) {
333 if (net_write (s
, tkfile
, strlen(tkfile
)) != strlen(tkfile
)) {
338 len
= strlen(local_user
) + 1;
339 if (net_write (s
, local_user
, len
) != len
) {
345 || krb5_forward_cred (auth_context
, s
, hostname
, do_forwardable
)) {
346 /* Empty forwarding info */
348 u_char zero
[4] = {0, 0, 0, 0};
351 krb5_auth_con_free (context
, auth_context
);
356 send_broken_auth(int s
,
357 struct sockaddr
*thisaddr
,
358 struct sockaddr
*thataddr
,
359 const char *hostname
,
360 const char *remote_user
,
361 const char *local_user
,
367 len
= strlen(local_user
) + 1;
368 if (net_write (s
, local_user
, len
) != len
) {
372 len
= strlen(remote_user
) + 1;
373 if (net_write (s
, remote_user
, len
) != len
) {
377 if (net_write (s
, cmd
, cmd_len
) != cmd_len
) {
385 proto (int s
, int errsock
,
386 const char *hostname
, const char *local_user
, const char *remote_user
,
387 const char *cmd
, size_t cmd_len
,
388 int (*auth_func
)(int s
,
389 struct sockaddr
*this, struct sockaddr
*that
,
390 const char *hostname
, const char *remote_user
,
391 const char *local_user
, size_t cmd_len
,
399 struct sockaddr_storage thisaddr_ss
;
400 struct sockaddr
*thisaddr
= (struct sockaddr
*)&thisaddr_ss
;
401 struct sockaddr_storage thataddr_ss
;
402 struct sockaddr
*thataddr
= (struct sockaddr
*)&thataddr_ss
;
403 struct sockaddr_storage erraddr_ss
;
404 struct sockaddr
*erraddr
= (struct sockaddr
*)&erraddr_ss
;
408 addrlen
= sizeof(thisaddr_ss
);
409 if (getsockname (s
, thisaddr
, &addrlen
) < 0) {
410 warn ("getsockname(%s)", hostname
);
413 addrlen
= sizeof(thataddr_ss
);
414 if (getpeername (s
, thataddr
, &addrlen
) < 0) {
415 warn ("getpeername(%s)", hostname
);
421 addrlen
= sizeof(erraddr_ss
);
422 if (getsockname (errsock
, erraddr
, &addrlen
) < 0) {
423 warn ("getsockname");
427 if (listen (errsock
, 1) < 0) {
433 snprintf (p
, sizeof(buf
), "%u",
434 ntohs(socket_get_port(erraddr
)));
435 len
= strlen(buf
) + 1;
436 if(net_write (s
, buf
, len
) != len
) {
442 errsock2
= accept (errsock
, NULL
, NULL
);
451 if (net_write (s
, "0", 2) != 2) {
458 if ((*auth_func
)(s
, thisaddr
, thataddr
, hostname
,
459 remote_user
, local_user
,
465 ret
= net_read (s
, &reply
, 1);
470 } else if (ret
== 0) {
471 warnx ("unexpected EOF from %s", hostname
);
477 warnx ("Error from rshd at %s:", hostname
);
479 while ((ret
= read (s
, buf
, sizeof(buf
))) > 0)
480 write (STDOUT_FILENO
, buf
, ret
);
481 write (STDOUT_FILENO
,"\n",1);
486 return loop (s
, errsock2
);
490 * Return in `res' a copy of the concatenation of `argc, argv' into
495 construct_command (char **res
, int argc
, char **argv
)
501 for (i
= 0; i
< argc
; ++i
)
502 len
+= strlen(argv
[i
]) + 1;
506 errx (1, "malloc %u failed", len
);
509 for (i
= 0; i
< argc
- 1; ++i
) {
510 strcat (tmp
, argv
[i
]);
514 strcat (tmp
, argv
[argc
-1]);
520 print_addr (const struct sockaddr_in
*sin
)
525 inet_ntop (AF_INET
, &sin
->sin_addr
, addr_str
, sizeof(addr_str
));
526 res
= strdup(addr_str
);
528 errx (1, "malloc: out of memory");
533 doit_broken (int argc
,
537 const char *remote_user
,
538 const char *local_user
,
545 struct addrinfo
*ai
, *a
;
546 struct addrinfo hints
;
548 char portstr
[NI_MAXSERV
];
550 if (priv_socket1
< 0) {
551 warnx ("unable to bind reserved port: is rsh setuid root?");
555 memset (&hints
, 0, sizeof(hints
));
556 hints
.ai_socktype
= SOCK_STREAM
;
557 hints
.ai_protocol
= IPPROTO_TCP
;
558 hints
.ai_family
= AF_INET
;
560 snprintf (portstr
, sizeof(portstr
), "%u", ntohs(port
));
562 error
= getaddrinfo (host
, portstr
, &hints
, &ai
);
564 warnx ("%s: %s", host
, gai_strerror(error
));
568 if (connect (priv_socket1
, ai
->ai_addr
, ai
->ai_addrlen
) < 0) {
569 if (ai
->ai_next
== NULL
) {
577 for (a
= ai
->ai_next
; a
!= NULL
; a
= a
->ai_next
) {
586 struct sockaddr_in
*sin
= (struct sockaddr_in
*)a
->ai_addr
;
588 new_argv
= malloc((argc
+ 2) * sizeof(*new_argv
));
589 if (new_argv
== NULL
)
590 errx (1, "malloc: out of memory");
591 new_argv
[i
] = argv
[i
];
594 new_argv
[i
++] = print_addr (sin
);
595 new_argv
[i
++] = "-K";
596 for(; i
<= argc
; ++i
)
597 new_argv
[i
] = argv
[i
- 1];
599 new_argv
[optind
+ 1] = print_addr(sin
);
600 new_argv
[argc
+ 1] = NULL
;
601 execv(PATH_RSH
, new_argv
);
602 err(1, "execv(%s)", PATH_RSH
);
608 while(waitpid(pid
, &status
, 0) < 0)
610 if(WIFEXITED(status
) && WEXITSTATUS(status
) == 0)
620 ret
= proto (priv_socket1
, priv_socket2
,
622 local_user
, remote_user
,
630 doit (const char *hostname
,
631 const char *remote_user
,
632 const char *local_user
,
637 int (*auth_func
)(int s
,
638 struct sockaddr
*this, struct sockaddr
*that
,
639 const char *hostname
, const char *remote_user
,
640 const char *local_user
, size_t cmd_len
,
643 struct addrinfo
*ai
, *a
;
644 struct addrinfo hints
;
646 char portstr
[NI_MAXSERV
];
649 memset (&hints
, 0, sizeof(hints
));
650 hints
.ai_socktype
= SOCK_STREAM
;
651 hints
.ai_protocol
= IPPROTO_TCP
;
653 snprintf (portstr
, sizeof(portstr
), "%u", ntohs(port
));
655 error
= getaddrinfo (hostname
, portstr
, &hints
, &ai
);
657 errx (1, "%s: %s", hostname
, gai_strerror(error
));
661 for (a
= ai
; a
!= NULL
; a
= a
->ai_next
) {
665 s
= socket (a
->ai_family
, a
->ai_socktype
, a
->ai_protocol
);
668 if (connect (s
, a
->ai_addr
, a
->ai_addrlen
) < 0) {
669 warn ("connect(%s)", hostname
);
675 struct addrinfo hints
;
677 memset (&hints
, 0, sizeof(hints
));
678 hints
.ai_socktype
= a
->ai_socktype
;
679 hints
.ai_protocol
= a
->ai_protocol
;
680 hints
.ai_family
= a
->ai_family
;
681 hints
.ai_flags
= AI_PASSIVE
;
683 error
= getaddrinfo (NULL
, "0", &hints
, &ea
);
685 errx (1, "getaddrinfo: %s", gai_strerror(error
));
686 errsock
= socket (ea
->ai_family
, ea
->ai_socktype
, ea
->ai_protocol
);
689 if (bind (errsock
, ea
->ai_addr
, ea
->ai_addrlen
) < 0)
696 ret
= proto (s
, errsock
,
698 local_user
, remote_user
,
699 cmd
, cmd_len
, auth_func
);
703 warnx ("failed to contact %s", hostname
);
709 static int use_v4
= -1;
711 static int use_v5
= -1;
712 static int use_only_broken
= 0;
713 static int use_broken
= 1;
714 static char *port_str
;
715 static const char *user
;
716 static int do_version
;
718 static int do_errsock
= 1;
720 struct getargs args
[] = {
722 { "krb4", '4', arg_flag
, &use_v4
, "Use Kerberos V4",
725 { "krb5", '5', arg_flag
, &use_v5
, "Use Kerberos V5",
727 { "broken", 'K', arg_flag
, &use_only_broken
, "Use priv port",
729 { "input", 'n', arg_negative_flag
, &input
, "Close stdin",
731 { "encrypt", 'x', arg_flag
, &do_encrypt
, "Encrypt connection",
733 { "encrypt", 'z', arg_negative_flag
, &do_encrypt
,
734 "Don't encrypt connection", NULL
},
735 { "forward", 'f', arg_flag
, &do_forward
, "Forward credentials",
737 { "forward", 'G', arg_negative_flag
,&do_forward
, "Forward credentials",
739 { "forwardable", 'F', arg_flag
, &do_forwardable
,
740 "Forward forwardable credentials", NULL
},
741 { "unique", 'u', arg_flag
, &do_unique_tkfile
,
742 "Use unique remote tkfile", NULL
},
743 { "tkfile", 'U', arg_string
, &unique_tkfile
,
744 "Use that remote tkfile", NULL
},
745 { "port", 'p', arg_string
, &port_str
, "Use this port",
746 "number-or-service" },
747 { "user", 'l', arg_string
, &user
, "Run as this user",
749 { "stderr", 'e', arg_negative_flag
, &do_errsock
, "don't open stderr"},
750 { "version", 0, arg_flag
, &do_version
, "Print version",
752 { "help", 0, arg_flag
, &do_help
, NULL
,
759 arg_printusage (args
,
760 sizeof(args
) / sizeof(args
[0]),
771 main(int argc
, char **argv
)
773 int priv_port1
, priv_port2
;
774 int priv_socket1
, priv_socket2
;
780 const char *local_user
;
785 priv_port1
= priv_port2
= IPPORT_RESERVED
-1;
786 priv_socket1
= rresvport(&priv_port1
);
787 priv_socket2
= rresvport(&priv_port2
);
790 set_progname (argv
[0]);
792 if (argc
>= 2 && argv
[1][0] != '-') {
793 host
= argv
[host_index
= 1];
797 status
= krb5_init_context (&context
);
799 errx(1, "krb5_init_context failed: %u", status
);
801 do_forwardable
= krb5_config_get_bool (context
, NULL
,
806 do_forward
= krb5_config_get_bool (context
, NULL
,
811 do_encrypt
= krb5_config_get_bool (context
, NULL
,
816 if (getarg (args
, sizeof(args
) / sizeof(args
[0]), argc
, argv
,
823 #if defined(KRB4) && defined(KRB5)
824 if(use_v4
== -1 && use_v5
== 1)
826 if(use_v5
== -1 && use_v4
== 1)
830 if (use_only_broken
) {
841 print_version (NULL
);
845 if (do_unique_tkfile
&& unique_tkfile
!= NULL
)
846 errx (1, "Only one of -u and -U allowed.");
848 if (do_unique_tkfile
)
849 strcpy(tkfile
,"-u ");
850 else if (unique_tkfile
!= NULL
) {
851 if (strchr(unique_tkfile
,' ') != NULL
) {
852 warnx("Space is not allowed in tkfilename");
855 do_unique_tkfile
= 1;
856 snprintf (tkfile
, sizeof(tkfile
), "-U %s ", unique_tkfile
);
860 if (argc
- optind
< 1)
863 host
= argv
[host_index
= optind
++];
866 if (optind
== argc
) {
867 close (priv_socket1
);
868 close (priv_socket2
);
870 execvp ("rlogin", argv
);
871 err (1, "execvp rlogin");
875 struct servent
*s
= roken_getservbyname (port_str
, "tcp");
882 port
= strtol (port_str
, &ptr
, 10);
883 if (port
== 0 && ptr
== port_str
)
884 errx (1, "Bad port `%s'", port_str
);
889 local_user
= get_default_username ();
890 if (local_user
== NULL
)
891 errx (1, "who are you?");
896 cmd_len
= construct_command(&cmd
, argc
- optind
, argv
+ optind
);
899 * Try all different authentication methods
908 tmp_port
= krb5_getportbyname (context
, "kshell", "tcp", 544);
910 auth_method
= AUTH_KRB5
;
911 ret
= doit (host
, user
, local_user
, tmp_port
, cmd
, cmd_len
,
922 tmp_port
= krb5_getportbyname (context
, "ekshell", "tcp", 545);
924 tmp_port
= krb5_getportbyname (context
, "kshell", "tcp", 544);
926 auth_method
= AUTH_KRB4
;
927 ret
= doit (host
, user
, local_user
, tmp_port
, cmd
, cmd_len
,
932 if (ret
&& use_broken
) {
938 tmp_port
= krb5_getportbyname(context
, "shell", "tcp", 514);
939 auth_method
= AUTH_BROKEN
;
940 ret
= doit_broken (argc
, argv
, host_index
, host
,
944 do_errsock
? priv_socket2
: -1,