This commit was manufactured by cvs2svn to create tag
[heimdal.git] / appl / rsh / rsh.c
blob9453554bff633ec0be50ef16648940a36eb18885
1 /*
2 * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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
31 * SUCH DAMAGE.
34 #include "rsh_locl.h"
35 RCSID("$Id$");
37 enum auth_method auth_method;
38 #if defined(KRB4) || defined(KRB5)
39 int do_encrypt = -1;
40 #endif
41 #ifdef KRB5
42 int do_unique_tkfile = 0;
43 char *unique_tkfile = NULL;
44 char tkfile[MAXPATHLEN];
45 int do_forward = -1;
46 int do_forwardable = -1;
47 krb5_context context;
48 krb5_keyblock *keyblock;
49 krb5_crypto crypto;
50 #endif
51 #ifdef KRB4
52 des_key_schedule schedule;
53 des_cblock iv;
54 #endif
55 int sock_debug = 0;
57 #ifdef KRB4
58 static int use_v4 = -1;
59 #endif
60 #ifdef KRB5
61 static int use_v5 = -1;
62 #endif
63 static int use_only_broken = 0;
64 static int use_broken = 1;
65 static char *port_str;
66 static const char *user;
67 static int do_version;
68 static int do_help;
69 static int do_errsock = 1;
70 static char *protocol_version_str;
71 static int protocol_version = 2;
77 static int input = 1; /* Read from stdin */
79 static int
80 loop (int s, int errsock)
82 fd_set real_readset;
83 int count = 1;
85 #ifdef KRB5
86 if(auth_method == AUTH_KRB5 && protocol_version == 2)
87 init_ivecs(1, errsock != -1);
88 #endif
90 if (s >= FD_SETSIZE || (errsock != -1 && errsock >= FD_SETSIZE))
91 errx (1, "fd too large");
93 FD_ZERO(&real_readset);
94 FD_SET(s, &real_readset);
95 if (errsock != -1) {
96 FD_SET(errsock, &real_readset);
97 ++count;
99 if(input)
100 FD_SET(STDIN_FILENO, &real_readset);
102 for (;;) {
103 int ret;
104 fd_set readset;
105 char buf[RSH_BUFSIZ];
107 readset = real_readset;
108 ret = select (max(s, errsock) + 1, &readset, NULL, NULL, NULL);
109 if (ret < 0) {
110 if (errno == EINTR)
111 continue;
112 else
113 err (1, "select");
115 if (FD_ISSET(s, &readset)) {
116 ret = do_read (s, buf, sizeof(buf), ivec_in[0]);
117 if (ret < 0)
118 err (1, "read");
119 else if (ret == 0) {
120 close (s);
121 FD_CLR(s, &real_readset);
122 if (--count == 0)
123 return 0;
124 } else
125 net_write (STDOUT_FILENO, buf, ret);
127 if (errsock != -1 && FD_ISSET(errsock, &readset)) {
128 ret = do_read (errsock, buf, sizeof(buf), ivec_in[1]);
129 if (ret < 0)
130 err (1, "read");
131 else if (ret == 0) {
132 close (errsock);
133 FD_CLR(errsock, &real_readset);
134 if (--count == 0)
135 return 0;
136 } else
137 net_write (STDERR_FILENO, buf, ret);
139 if (FD_ISSET(STDIN_FILENO, &readset)) {
140 ret = read (STDIN_FILENO, buf, sizeof(buf));
141 if (ret < 0)
142 err (1, "read");
143 else if (ret == 0) {
144 close (STDIN_FILENO);
145 FD_CLR(STDIN_FILENO, &real_readset);
146 shutdown (s, SHUT_WR);
147 } else
148 do_write (s, buf, ret, ivec_out[0]);
153 #ifdef KRB4
154 static int
155 send_krb4_auth(int s,
156 struct sockaddr *thisaddr,
157 struct sockaddr *thataddr,
158 const char *hostname,
159 const char *remote_user,
160 const char *local_user,
161 size_t cmd_len,
162 const char *cmd)
164 KTEXT_ST text;
165 CREDENTIALS cred;
166 MSG_DAT msg;
167 int status;
168 size_t len;
170 /* the normal default for krb4 should be to disable encryption */
171 status = krb_sendauth ((do_encrypt == 1) ? KOPT_DO_MUTUAL : 0,
172 s, &text, "rcmd",
173 (char *)hostname, krb_realmofhost (hostname),
174 getpid(), &msg, &cred, schedule,
175 (struct sockaddr_in *)thisaddr,
176 (struct sockaddr_in *)thataddr,
177 KCMD_OLD_VERSION);
178 if (status != KSUCCESS) {
179 warnx("%s: %s", hostname, krb_get_err_text(status));
180 return 1;
182 memcpy (iv, cred.session, sizeof(iv));
184 len = strlen(remote_user) + 1;
185 if (net_write (s, remote_user, len) != len) {
186 warn("write");
187 return 1;
189 if (net_write (s, cmd, cmd_len) != cmd_len) {
190 warn("write");
191 return 1;
193 return 0;
195 #endif /* KRB4 */
197 #ifdef KRB5
199 * Send forward information on `s' for host `hostname', them being
200 * forwardable themselves if `forwardable'
203 static int
204 krb5_forward_cred (krb5_auth_context auth_context,
205 int s,
206 const char *hostname,
207 int forwardable)
209 krb5_error_code ret;
210 krb5_ccache ccache;
211 krb5_creds creds;
212 krb5_kdc_flags flags;
213 krb5_data out_data;
214 krb5_principal principal;
216 memset (&creds, 0, sizeof(creds));
218 ret = krb5_cc_default (context, &ccache);
219 if (ret) {
220 warnx ("could not forward creds: krb5_cc_default: %s",
221 krb5_get_err_text (context, ret));
222 return 1;
225 ret = krb5_cc_get_principal (context, ccache, &principal);
226 if (ret) {
227 warnx ("could not forward creds: krb5_cc_get_principal: %s",
228 krb5_get_err_text (context, ret));
229 return 1;
232 creds.client = principal;
234 ret = krb5_build_principal (context,
235 &creds.server,
236 strlen(principal->realm),
237 principal->realm,
238 "krbtgt",
239 principal->realm,
240 NULL);
242 if (ret) {
243 warnx ("could not forward creds: krb5_build_principal: %s",
244 krb5_get_err_text (context, ret));
245 return 1;
248 creds.times.endtime = 0;
250 flags.i = 0;
251 flags.b.forwarded = 1;
252 flags.b.forwardable = forwardable;
254 ret = krb5_get_forwarded_creds (context,
255 auth_context,
256 ccache,
257 flags.i,
258 hostname,
259 &creds,
260 &out_data);
261 if (ret) {
262 warnx ("could not forward creds: krb5_get_forwarded_creds: %s",
263 krb5_get_err_text (context, ret));
264 return 1;
267 ret = krb5_write_message (context,
268 (void *)&s,
269 &out_data);
270 krb5_data_free (&out_data);
272 if (ret)
273 warnx ("could not forward creds: krb5_write_message: %s",
274 krb5_get_err_text (context, ret));
275 return 0;
278 static int sendauth_version_error;
280 static int
281 send_krb5_auth(int s,
282 struct sockaddr *thisaddr,
283 struct sockaddr *thataddr,
284 const char *hostname,
285 const char *remote_user,
286 const char *local_user,
287 size_t cmd_len,
288 const char *cmd)
290 krb5_principal server;
291 krb5_data cksum_data;
292 int status;
293 size_t len;
294 krb5_auth_context auth_context = NULL;
295 const char *protocol_string = NULL;
296 krb5_flags ap_opts;
298 status = krb5_sname_to_principal(context,
299 hostname,
300 "host",
301 KRB5_NT_SRV_HST,
302 &server);
303 if (status) {
304 warnx ("%s: %s", hostname, krb5_get_err_text(context, status));
305 return 1;
308 if(do_encrypt == -1) {
309 krb5_appdefault_boolean(context, NULL,
310 krb5_principal_get_realm(context, server),
311 "encrypt",
312 FALSE,
313 &do_encrypt);
316 cksum_data.length = asprintf ((char **)&cksum_data.data,
317 "%u:%s%s%s",
318 ntohs(socket_get_port(thataddr)),
319 do_encrypt ? "-x " : "",
320 cmd,
321 remote_user);
323 ap_opts = 0;
325 if(do_encrypt)
326 ap_opts |= AP_OPTS_MUTUAL_REQUIRED;
328 switch(protocol_version) {
329 case 2:
330 ap_opts |= AP_OPTS_USE_SUBKEY;
331 protocol_string = KCMD_NEW_VERSION;
332 break;
333 case 1:
334 protocol_string = KCMD_OLD_VERSION;
335 key_usage = KRB5_KU_OTHER_ENCRYPTED;
336 break;
337 default:
338 abort();
341 status = krb5_sendauth (context,
342 &auth_context,
344 protocol_string,
345 NULL,
346 server,
347 ap_opts,
348 &cksum_data,
349 NULL,
350 NULL,
351 NULL,
352 NULL,
353 NULL);
355 /* do this while we have a principal */
356 if(do_forward == -1 || do_forwardable == -1) {
357 krb5_const_realm realm = krb5_principal_get_realm(context, server);
358 if (do_forwardable == -1)
359 krb5_appdefault_boolean(context, NULL, realm,
360 "forwardable", FALSE,
361 &do_forwardable);
362 if (do_forward == -1)
363 krb5_appdefault_boolean(context, NULL, realm,
364 "forward", FALSE,
365 &do_forward);
368 krb5_free_principal(context, server);
369 krb5_data_free(&cksum_data);
371 if (status) {
372 if(status == KRB5_SENDAUTH_REJECTED &&
373 protocol_version == 2 && protocol_version_str == NULL)
374 sendauth_version_error = 1;
375 else
376 krb5_warn(context, status, "%s", hostname);
377 return 1;
380 status = krb5_auth_con_getlocalsubkey (context, auth_context, &keyblock);
381 if(keyblock == NULL)
382 status = krb5_auth_con_getkey (context, auth_context, &keyblock);
383 if (status) {
384 warnx ("krb5_auth_con_getkey: %s", krb5_get_err_text(context, status));
385 return 1;
388 status = krb5_auth_con_setaddrs_from_fd (context,
389 auth_context,
390 &s);
391 if (status) {
392 warnx("krb5_auth_con_setaddrs_from_fd: %s",
393 krb5_get_err_text(context, status));
394 return(1);
397 status = krb5_crypto_init(context, keyblock, 0, &crypto);
398 if(status) {
399 warnx ("krb5_crypto_init: %s", krb5_get_err_text(context, status));
400 return 1;
403 len = strlen(remote_user) + 1;
404 if (net_write (s, remote_user, len) != len) {
405 warn ("write");
406 return 1;
408 if (do_encrypt && net_write (s, "-x ", 3) != 3) {
409 warn ("write");
410 return 1;
412 if (net_write (s, cmd, cmd_len) != cmd_len) {
413 warn ("write");
414 return 1;
417 if (do_unique_tkfile) {
418 if (net_write (s, tkfile, strlen(tkfile)) != strlen(tkfile)) {
419 warn ("write");
420 return 1;
423 len = strlen(local_user) + 1;
424 if (net_write (s, local_user, len) != len) {
425 warn ("write");
426 return 1;
429 if (!do_forward
430 || krb5_forward_cred (auth_context, s, hostname, do_forwardable)) {
431 /* Empty forwarding info */
433 u_char zero[4] = {0, 0, 0, 0};
434 write (s, &zero, 4);
436 krb5_auth_con_free (context, auth_context);
437 return 0;
440 #endif /* KRB5 */
442 static int
443 send_broken_auth(int s,
444 struct sockaddr *thisaddr,
445 struct sockaddr *thataddr,
446 const char *hostname,
447 const char *remote_user,
448 const char *local_user,
449 size_t cmd_len,
450 const char *cmd)
452 size_t len;
454 len = strlen(local_user) + 1;
455 if (net_write (s, local_user, len) != len) {
456 warn ("write");
457 return 1;
459 len = strlen(remote_user) + 1;
460 if (net_write (s, remote_user, len) != len) {
461 warn ("write");
462 return 1;
464 if (net_write (s, cmd, cmd_len) != cmd_len) {
465 warn ("write");
466 return 1;
468 return 0;
471 static int
472 proto (int s, int errsock,
473 const char *hostname, const char *local_user, const char *remote_user,
474 const char *cmd, size_t cmd_len,
475 int (*auth_func)(int s,
476 struct sockaddr *this, struct sockaddr *that,
477 const char *hostname, const char *remote_user,
478 const char *local_user, size_t cmd_len,
479 const char *cmd))
481 int errsock2;
482 char buf[BUFSIZ];
483 char *p;
484 size_t len;
485 char reply;
486 struct sockaddr_storage thisaddr_ss;
487 struct sockaddr *thisaddr = (struct sockaddr *)&thisaddr_ss;
488 struct sockaddr_storage thataddr_ss;
489 struct sockaddr *thataddr = (struct sockaddr *)&thataddr_ss;
490 struct sockaddr_storage erraddr_ss;
491 struct sockaddr *erraddr = (struct sockaddr *)&erraddr_ss;
492 socklen_t addrlen;
493 int ret;
495 addrlen = sizeof(thisaddr_ss);
496 if (getsockname (s, thisaddr, &addrlen) < 0) {
497 warn ("getsockname(%s)", hostname);
498 return 1;
500 addrlen = sizeof(thataddr_ss);
501 if (getpeername (s, thataddr, &addrlen) < 0) {
502 warn ("getpeername(%s)", hostname);
503 return 1;
506 if (errsock != -1) {
508 addrlen = sizeof(erraddr_ss);
509 if (getsockname (errsock, erraddr, &addrlen) < 0) {
510 warn ("getsockname");
511 return 1;
514 if (listen (errsock, 1) < 0) {
515 warn ("listen");
516 return 1;
519 p = buf;
520 snprintf (p, sizeof(buf), "%u",
521 ntohs(socket_get_port(erraddr)));
522 len = strlen(buf) + 1;
523 if(net_write (s, buf, len) != len) {
524 warn ("write");
525 close (errsock);
526 return 1;
530 for (;;) {
531 fd_set fdset;
533 if (errsock >= FD_SETSIZE || s >= FD_SETSIZE)
534 errx (1, "fd too large");
536 FD_ZERO(&fdset);
537 FD_SET(errsock, &fdset);
538 FD_SET(s, &fdset);
540 ret = select (max(errsock, s) + 1, &fdset, NULL, NULL, NULL);
541 if (ret < 0) {
542 if (errno == EINTR)
543 continue;
544 warn ("select");
545 close (errsock);
546 return 1;
548 if (FD_ISSET(errsock, &fdset)) {
549 errsock2 = accept (errsock, NULL, NULL);
550 close (errsock);
551 if (errsock2 < 0) {
552 warn ("accept");
553 return 1;
555 break;
559 * there should not arrive any data on this fd so if it's
560 * readable it probably indicates that the other side when
561 * away.
564 if (FD_ISSET(s, &fdset)) {
565 warnx ("socket closed");
566 close (errsock);
567 errsock2 = -1;
568 break;
571 } else {
572 if (net_write (s, "0", 2) != 2) {
573 warn ("write");
574 return 1;
576 errsock2 = -1;
579 if ((*auth_func)(s, thisaddr, thataddr, hostname,
580 remote_user, local_user,
581 cmd_len, cmd)) {
582 close (errsock2);
583 return 1;
586 ret = net_read (s, &reply, 1);
587 if (ret < 0) {
588 warn ("read");
589 close (errsock2);
590 return 1;
591 } else if (ret == 0) {
592 warnx ("unexpected EOF from %s", hostname);
593 close (errsock2);
594 return 1;
596 if (reply != 0) {
598 warnx ("Error from rshd at %s:", hostname);
600 while ((ret = read (s, buf, sizeof(buf))) > 0)
601 write (STDOUT_FILENO, buf, ret);
602 write (STDOUT_FILENO,"\n",1);
603 close (errsock2);
604 return 1;
607 if (sock_debug) {
608 int one = 1;
609 if (setsockopt(s, SOL_SOCKET, SO_DEBUG, (void *)&one, sizeof(one)) < 0)
610 warn("setsockopt remote");
611 if (errsock2 != -1 &&
612 setsockopt(errsock2, SOL_SOCKET, SO_DEBUG,
613 (void *)&one, sizeof(one)) < 0)
614 warn("setsockopt stderr");
617 return loop (s, errsock2);
621 * Return in `res' a copy of the concatenation of `argc, argv' into
622 * malloced space. */
624 static size_t
625 construct_command (char **res, int argc, char **argv)
627 int i;
628 size_t len = 0;
629 char *tmp;
631 for (i = 0; i < argc; ++i)
632 len += strlen(argv[i]) + 1;
633 len = max (1, len);
634 tmp = malloc (len);
635 if (tmp == NULL)
636 errx (1, "malloc %u failed", len);
638 *tmp = '\0';
639 for (i = 0; i < argc - 1; ++i) {
640 strcat (tmp, argv[i]);
641 strcat (tmp, " ");
643 if (argc > 0)
644 strcat (tmp, argv[argc-1]);
645 *res = tmp;
646 return len;
649 static char *
650 print_addr (const struct sockaddr *sa)
652 char addr_str[256];
653 char *res;
654 const char *as = NULL;
656 if(sa->sa_family == AF_INET)
657 as = inet_ntop (sa->sa_family, &((struct sockaddr_in*)sa)->sin_addr,
658 addr_str, sizeof(addr_str));
659 #ifdef HAVE_INET6
660 else if(sa->sa_family == AF_INET6)
661 as = inet_ntop (sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr,
662 addr_str, sizeof(addr_str));
663 #endif
664 if(as == NULL)
665 return NULL;
666 res = strdup(as);
667 if (res == NULL)
668 errx (1, "malloc: out of memory");
669 return res;
672 static int
673 doit_broken (int argc,
674 char **argv,
675 int hostindex,
676 struct addrinfo *ai,
677 const char *remote_user,
678 const char *local_user,
679 int priv_socket1,
680 int priv_socket2,
681 const char *cmd,
682 size_t cmd_len)
684 struct addrinfo *a;
686 if (connect (priv_socket1, ai->ai_addr, ai->ai_addrlen) < 0) {
687 int save_errno = errno;
689 close(priv_socket1);
690 close(priv_socket2);
692 for (a = ai->ai_next; a != NULL; a = a->ai_next) {
693 pid_t pid;
694 char *adr = print_addr(a->ai_addr);
695 if(adr == NULL)
696 continue;
698 pid = fork();
699 if (pid < 0)
700 err (1, "fork");
701 else if(pid == 0) {
702 char **new_argv;
703 int i = 0;
705 new_argv = malloc((argc + 2) * sizeof(*new_argv));
706 if (new_argv == NULL)
707 errx (1, "malloc: out of memory");
708 new_argv[i] = argv[i];
709 ++i;
710 if (hostindex == i)
711 new_argv[i++] = adr;
712 new_argv[i++] = "-K";
713 for(; i <= argc; ++i)
714 new_argv[i] = argv[i - 1];
715 if (hostindex > 1)
716 new_argv[hostindex + 1] = adr;
717 new_argv[argc + 1] = NULL;
718 execv(PATH_RSH, new_argv);
719 err(1, "execv(%s)", PATH_RSH);
720 } else {
721 int status;
722 free(adr);
724 while(waitpid(pid, &status, 0) < 0)
726 if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
727 return 0;
730 errno = save_errno;
731 warn("%s", argv[hostindex]);
732 return 1;
733 } else {
734 int ret;
736 ret = proto (priv_socket1, priv_socket2,
737 argv[hostindex],
738 local_user, remote_user,
739 cmd, cmd_len,
740 send_broken_auth);
741 return ret;
745 #if defined(KRB4) || defined(KRB5)
746 static int
747 doit (const char *hostname,
748 struct addrinfo *ai,
749 const char *remote_user,
750 const char *local_user,
751 const char *cmd,
752 size_t cmd_len,
753 int do_errsock,
754 int (*auth_func)(int s,
755 struct sockaddr *this, struct sockaddr *that,
756 const char *hostname, const char *remote_user,
757 const char *local_user, size_t cmd_len,
758 const char *cmd))
760 int error;
761 struct addrinfo *a;
762 int socketfailed = 1;
763 int ret;
765 for (a = ai; a != NULL; a = a->ai_next) {
766 int s;
767 int errsock;
769 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
770 if (s < 0)
771 continue;
772 socketfailed = 0;
773 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
774 char addr[128];
775 if(getnameinfo(a->ai_addr, a->ai_addrlen,
776 addr, sizeof(addr), NULL, 0, NI_NUMERICHOST) == 0)
777 warn ("connect(%s [%s])", hostname, addr);
778 else
779 warn ("connect(%s)", hostname);
780 close (s);
781 continue;
783 if (do_errsock) {
784 struct addrinfo *ea, *eai;
785 struct addrinfo hints;
787 memset (&hints, 0, sizeof(hints));
788 hints.ai_socktype = a->ai_socktype;
789 hints.ai_protocol = a->ai_protocol;
790 hints.ai_family = a->ai_family;
791 hints.ai_flags = AI_PASSIVE;
793 errsock = -1;
795 error = getaddrinfo (NULL, "0", &hints, &eai);
796 if (error)
797 errx (1, "getaddrinfo: %s", gai_strerror(error));
798 for (ea = eai; ea != NULL; ea = ea->ai_next) {
799 errsock = socket (ea->ai_family, ea->ai_socktype,
800 ea->ai_protocol);
801 if (errsock < 0)
802 continue;
803 if (bind (errsock, ea->ai_addr, ea->ai_addrlen) < 0)
804 err (1, "bind");
805 break;
807 if (errsock < 0)
808 err (1, "socket");
809 freeaddrinfo (eai);
810 } else
811 errsock = -1;
813 ret = proto (s, errsock,
814 hostname,
815 local_user, remote_user,
816 cmd, cmd_len, auth_func);
817 close (s);
818 return ret;
820 if(socketfailed)
821 warnx ("failed to contact %s", hostname);
822 return -1;
824 #endif /* KRB4 || KRB5 */
826 struct getargs args[] = {
827 #ifdef KRB4
828 { "krb4", '4', arg_flag, &use_v4, "Use Kerberos V4" },
829 #endif
830 #ifdef KRB5
831 { "krb5", '5', arg_flag, &use_v5, "Use Kerberos V5" },
832 { "forward", 'f', arg_flag, &do_forward, "Forward credentials (krb5)"},
833 { NULL, 'G', arg_negative_flag,&do_forward, "Don't forward credentials" },
834 { "forwardable", 'F', arg_flag, &do_forwardable,
835 "Forward forwardable credentials" },
836 #endif
837 #if defined(KRB4) || defined(KRB5)
838 { "broken", 'K', arg_flag, &use_only_broken, "Use only priv port" },
839 { "encrypt", 'x', arg_flag, &do_encrypt, "Encrypt connection" },
840 { NULL, 'z', arg_negative_flag, &do_encrypt,
841 "Don't encrypt connection", NULL },
842 #endif
843 #ifdef KRB5
844 { "unique", 'u', arg_flag, &do_unique_tkfile,
845 "Use unique remote tkfile (krb5)" },
846 { "tkfile", 'U', arg_string, &unique_tkfile,
847 "Use that remote tkfile (krb5)" },
848 #endif
849 { NULL, 'd', arg_flag, &sock_debug, "Enable socket debugging" },
850 { "input", 'n', arg_negative_flag, &input, "Close stdin" },
851 { "port", 'p', arg_string, &port_str, "Use this port",
852 "port" },
853 { "user", 'l', arg_string, &user, "Run as this user", "login" },
854 { "stderr", 'e', arg_negative_flag, &do_errsock, "Don't open stderr"},
855 { "protocol", 'P', arg_string, &protocol_version_str,
856 "Protocol version", "protocol" },
857 { "version", 0, arg_flag, &do_version, NULL },
858 { "help", 0, arg_flag, &do_help, NULL }
861 static void
862 usage (int ret)
864 arg_printusage (args,
865 sizeof(args) / sizeof(args[0]),
866 NULL,
867 "[login@]host [command]");
868 exit (ret);
876 main(int argc, char **argv)
878 int priv_port1, priv_port2;
879 int priv_socket1, priv_socket2;
880 int argindex = 0;
881 int error;
882 struct addrinfo hints, *ai;
883 int ret = 1;
884 char *cmd;
885 char *tmp;
886 size_t cmd_len;
887 const char *local_user;
888 char *host = NULL;
889 int host_index = -1;
890 #ifdef KRB5
891 int status;
892 #endif
893 uid_t uid;
895 priv_port1 = priv_port2 = IPPORT_RESERVED-1;
896 priv_socket1 = rresvport(&priv_port1);
897 priv_socket2 = rresvport(&priv_port2);
898 uid = getuid ();
899 if (setuid (uid) || (uid != 0 && setuid(0) == 0))
900 err (1, "setuid");
902 setprogname (argv[0]);
904 if (argc >= 2 && argv[1][0] != '-') {
905 host = argv[host_index = 1];
906 argindex = 1;
909 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
910 &argindex))
911 usage (1);
913 if (do_help)
914 usage (0);
916 if (do_version) {
917 print_version (NULL);
918 return 0;
921 if(protocol_version_str != NULL) {
922 if(strcasecmp(protocol_version_str, "N") == 0)
923 protocol_version = 2;
924 else if(strcasecmp(protocol_version_str, "O") == 0)
925 protocol_version = 1;
926 else {
927 char *end;
928 int v;
929 v = strtol(protocol_version_str, &end, 0);
930 if(*end != '\0' || (v != 1 && v != 2)) {
931 errx(1, "unknown protocol version \"%s\"",
932 protocol_version_str);
934 protocol_version = v;
938 #ifdef KRB5
939 status = krb5_init_context (&context);
940 if (status) {
941 if(use_v5 == 1)
942 errx(1, "krb5_init_context failed: %d", status);
943 else
944 use_v5 = 0;
947 /* request for forwardable on the command line means we should
948 also forward */
949 if (do_forwardable == 1)
950 do_forward = 1;
952 #endif
954 #if defined(KRB4) && defined(KRB5)
955 if(use_v4 == -1 && use_v5 == 1)
956 use_v4 = 0;
957 if(use_v5 == -1 && use_v4 == 1)
958 use_v5 = 0;
959 #endif
961 if (use_only_broken) {
962 #ifdef KRB4
963 use_v4 = 0;
964 #endif
965 #ifdef KRB5
966 use_v5 = 0;
967 #endif
970 if(priv_socket1 < 0) {
971 if (use_only_broken)
972 errx (1, "unable to bind reserved port: is rsh setuid root?");
973 use_broken = 0;
976 #if defined(KRB4) || defined(KRB5)
977 if (do_encrypt == 1 && use_only_broken)
978 errx (1, "encryption not supported with old style authentication");
979 #endif
983 #ifdef KRB5
984 if (do_unique_tkfile && unique_tkfile != NULL)
985 errx (1, "Only one of -u and -U allowed.");
987 if (do_unique_tkfile)
988 strcpy(tkfile,"-u ");
989 else if (unique_tkfile != NULL) {
990 if (strchr(unique_tkfile,' ') != NULL) {
991 warnx("Space is not allowed in tkfilename");
992 usage(1);
994 do_unique_tkfile = 1;
995 snprintf (tkfile, sizeof(tkfile), "-U %s ", unique_tkfile);
997 #endif
999 if (host == NULL) {
1000 if (argc - argindex < 1)
1001 usage (1);
1002 else
1003 host = argv[host_index = argindex++];
1006 if((tmp = strchr(host, '@')) != NULL) {
1007 *tmp++ = '\0';
1008 user = host;
1009 host = tmp;
1012 if (argindex == argc) {
1013 close (priv_socket1);
1014 close (priv_socket2);
1015 argv[0] = "rlogin";
1016 execvp ("rlogin", argv);
1017 err (1, "execvp rlogin");
1020 local_user = get_default_username ();
1021 if (local_user == NULL)
1022 errx (1, "who are you?");
1024 if (user == NULL)
1025 user = local_user;
1027 cmd_len = construct_command(&cmd, argc - argindex, argv + argindex);
1030 * Try all different authentication methods
1033 #ifdef KRB5
1034 if (ret && use_v5) {
1035 memset (&hints, 0, sizeof(hints));
1036 hints.ai_socktype = SOCK_STREAM;
1037 hints.ai_protocol = IPPROTO_TCP;
1039 if(port_str == NULL) {
1040 error = getaddrinfo(host, "kshell", &hints, &ai);
1041 if(error == EAI_NONAME)
1042 error = getaddrinfo(host, "544", &hints, &ai);
1043 } else
1044 error = getaddrinfo(host, port_str, &hints, &ai);
1046 if(error)
1047 errx (1, "getaddrinfo: %s", gai_strerror(error));
1049 auth_method = AUTH_KRB5;
1050 again:
1051 ret = doit (host, ai, user, local_user, cmd, cmd_len,
1052 do_errsock,
1053 send_krb5_auth);
1054 if(ret != 0 && sendauth_version_error &&
1055 protocol_version == 2) {
1056 protocol_version = 1;
1057 goto again;
1059 freeaddrinfo(ai);
1061 #endif
1062 #ifdef KRB4
1063 if (ret && use_v4) {
1064 memset (&hints, 0, sizeof(hints));
1065 hints.ai_socktype = SOCK_STREAM;
1066 hints.ai_protocol = IPPROTO_TCP;
1068 if(port_str == NULL) {
1069 if(do_encrypt) {
1070 error = getaddrinfo(host, "ekshell", &hints, &ai);
1071 if(error == EAI_NONAME)
1072 error = getaddrinfo(host, "545", &hints, &ai);
1073 } else {
1074 error = getaddrinfo(host, "kshell", &hints, &ai);
1075 if(error == EAI_NONAME)
1076 error = getaddrinfo(host, "544", &hints, &ai);
1078 } else
1079 error = getaddrinfo(host, port_str, &hints, &ai);
1081 if(error)
1082 errx (1, "getaddrinfo: %s", gai_strerror(error));
1083 auth_method = AUTH_KRB4;
1084 ret = doit (host, ai, user, local_user, cmd, cmd_len,
1085 do_errsock,
1086 send_krb4_auth);
1087 freeaddrinfo(ai);
1089 #endif
1090 if (ret && use_broken) {
1091 memset (&hints, 0, sizeof(hints));
1092 hints.ai_socktype = SOCK_STREAM;
1093 hints.ai_protocol = IPPROTO_TCP;
1095 if(port_str == NULL) {
1096 error = getaddrinfo(host, "shell", &hints, &ai);
1097 if(error == EAI_NONAME)
1098 error = getaddrinfo(host, "514", &hints, &ai);
1099 } else
1100 error = getaddrinfo(host, port_str, &hints, &ai);
1102 if(error)
1103 errx (1, "getaddrinfo: %s", gai_strerror(error));
1105 auth_method = AUTH_BROKEN;
1106 ret = doit_broken (argc, argv, host_index, ai,
1107 user, local_user,
1108 priv_socket1,
1109 do_errsock ? priv_socket2 : -1,
1110 cmd, cmd_len);
1111 freeaddrinfo(ai);
1113 free(cmd);
1114 return ret;