Add _krb5_log() used for internal logging
[heimdal.git] / appl / rsh / rsh.c
blobf1e2ca32e62f418bd57634808c723a0100404d11
1 /*
2 * Copyright (c) 1997 - 2004 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(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 int sock_debug = 0;
53 #ifdef KRB5
54 static int use_v5 = -1;
55 #endif
56 #if defined(KRB5)
57 static int use_only_broken = 0;
58 #else
59 static int use_only_broken = 1;
60 #endif
61 static int use_broken = 1;
62 static char *port_str;
63 static const char *user;
64 static int do_version;
65 static int do_help;
66 static int do_errsock = 1;
67 #ifdef KRB5
68 static char *protocol_version_str;
69 static int protocol_version = 2;
70 #endif
76 static int input = 1; /* Read from stdin */
78 static int
79 rsh_loop (int s, int errsock)
81 fd_set real_readset;
82 int count = 1;
84 #ifdef KRB5
85 if(auth_method == AUTH_KRB5 && protocol_version == 2)
86 init_ivecs(1, errsock != -1);
87 #endif
89 if (s >= FD_SETSIZE || (errsock != -1 && errsock >= FD_SETSIZE))
90 errx (1, "fd too large");
92 FD_ZERO(&real_readset);
93 FD_SET(s, &real_readset);
94 if (errsock != -1) {
95 FD_SET(errsock, &real_readset);
96 ++count;
98 if(input)
99 FD_SET(STDIN_FILENO, &real_readset);
101 for (;;) {
102 int ret;
103 fd_set readset;
104 char buf[RSH_BUFSIZ];
106 readset = real_readset;
107 ret = select (max(s, errsock) + 1, &readset, NULL, NULL, NULL);
108 if (ret < 0) {
109 if (errno == EINTR)
110 continue;
111 else
112 err (1, "select");
114 if (FD_ISSET(s, &readset)) {
115 ret = do_read (s, buf, sizeof(buf), ivec_in[0]);
116 if (ret < 0)
117 err (1, "read");
118 else if (ret == 0) {
119 close (s);
120 FD_CLR(s, &real_readset);
121 if (--count == 0)
122 return 0;
123 } else
124 net_write (STDOUT_FILENO, buf, ret);
126 if (errsock != -1 && FD_ISSET(errsock, &readset)) {
127 ret = do_read (errsock, buf, sizeof(buf), ivec_in[1]);
128 if (ret < 0)
129 err (1, "read");
130 else if (ret == 0) {
131 close (errsock);
132 FD_CLR(errsock, &real_readset);
133 if (--count == 0)
134 return 0;
135 } else
136 net_write (STDERR_FILENO, buf, ret);
138 if (FD_ISSET(STDIN_FILENO, &readset)) {
139 ret = read (STDIN_FILENO, buf, sizeof(buf));
140 if (ret < 0)
141 err (1, "read");
142 else if (ret == 0) {
143 close (STDIN_FILENO);
144 FD_CLR(STDIN_FILENO, &real_readset);
145 shutdown (s, SHUT_WR);
146 } else
147 do_write (s, buf, ret, ivec_out[0]);
152 #ifdef KRB5
154 * Send forward information on `s' for host `hostname', them being
155 * forwardable themselves if `forwardable'
158 static int
159 krb5_forward_cred (krb5_auth_context auth_context,
160 int s,
161 const char *hostname,
162 int forwardable)
164 krb5_error_code ret;
165 krb5_ccache ccache;
166 krb5_creds creds;
167 krb5_kdc_flags flags;
168 krb5_data out_data;
169 krb5_principal principal;
171 memset (&creds, 0, sizeof(creds));
173 ret = krb5_cc_default (context, &ccache);
174 if (ret) {
175 warnx ("could not forward creds: krb5_cc_default: %s",
176 krb5_get_err_text (context, ret));
177 return 1;
180 ret = krb5_cc_get_principal (context, ccache, &principal);
181 if (ret) {
182 warnx ("could not forward creds: krb5_cc_get_principal: %s",
183 krb5_get_err_text (context, ret));
184 return 1;
187 creds.client = principal;
189 ret = krb5_build_principal (context,
190 &creds.server,
191 strlen(principal->realm),
192 principal->realm,
193 "krbtgt",
194 principal->realm,
195 NULL);
197 if (ret) {
198 warnx ("could not forward creds: krb5_build_principal: %s",
199 krb5_get_err_text (context, ret));
200 return 1;
203 creds.times.endtime = 0;
205 flags.i = 0;
206 flags.b.forwarded = 1;
207 flags.b.forwardable = forwardable;
209 ret = krb5_get_forwarded_creds (context,
210 auth_context,
211 ccache,
212 flags.i,
213 hostname,
214 &creds,
215 &out_data);
216 if (ret) {
217 warnx ("could not forward creds: krb5_get_forwarded_creds: %s",
218 krb5_get_err_text (context, ret));
219 return 1;
222 ret = krb5_write_message (context,
223 (void *)&s,
224 &out_data);
225 krb5_data_free (&out_data);
227 if (ret)
228 warnx ("could not forward creds: krb5_write_message: %s",
229 krb5_get_err_text (context, ret));
230 return 0;
233 static int sendauth_version_error;
235 static int
236 send_krb5_auth(int s,
237 struct sockaddr *thisaddr,
238 struct sockaddr *thataddr,
239 const char *hostname,
240 const char *remote_user,
241 const char *local_user,
242 size_t cmd_len,
243 const char *cmd)
245 krb5_principal server;
246 krb5_data cksum_data;
247 int status;
248 size_t len;
249 krb5_auth_context auth_context = NULL;
250 const char *protocol_string = NULL;
251 krb5_flags ap_opts;
252 char *str;
254 status = krb5_sname_to_principal(context,
255 hostname,
256 "host",
257 KRB5_NT_SRV_HST,
258 &server);
259 if (status) {
260 warnx ("%s: %s", hostname, krb5_get_err_text(context, status));
261 return 1;
264 if(do_encrypt == -1) {
265 krb5_appdefault_boolean(context, NULL,
266 krb5_principal_get_realm(context, server),
267 "encrypt",
268 FALSE,
269 &do_encrypt);
272 cksum_data.length = asprintf (&str,
273 "%u:%s%s%s",
274 ntohs(socket_get_port(thataddr)),
275 do_encrypt ? "-x " : "",
276 cmd,
277 remote_user);
278 if (str == NULL) {
279 warnx ("%s: failed to allocate command", hostname);
280 return 1;
282 cksum_data.data = str;
284 ap_opts = 0;
286 if(do_encrypt)
287 ap_opts |= AP_OPTS_MUTUAL_REQUIRED;
289 switch(protocol_version) {
290 case 2:
291 ap_opts |= AP_OPTS_USE_SUBKEY;
292 protocol_string = KCMD_NEW_VERSION;
293 break;
294 case 1:
295 protocol_string = KCMD_OLD_VERSION;
296 key_usage = KRB5_KU_OTHER_ENCRYPTED;
297 break;
298 default:
299 abort();
302 status = krb5_sendauth (context,
303 &auth_context,
305 protocol_string,
306 NULL,
307 server,
308 ap_opts,
309 &cksum_data,
310 NULL,
311 NULL,
312 NULL,
313 NULL,
314 NULL);
316 /* do this while we have a principal */
317 if(do_forward == -1 || do_forwardable == -1) {
318 krb5_const_realm realm = krb5_principal_get_realm(context, server);
319 if (do_forwardable == -1)
320 krb5_appdefault_boolean(context, NULL, realm,
321 "forwardable", FALSE,
322 &do_forwardable);
323 if (do_forward == -1)
324 krb5_appdefault_boolean(context, NULL, realm,
325 "forward", FALSE,
326 &do_forward);
329 krb5_free_principal(context, server);
330 krb5_data_free(&cksum_data);
332 if (status) {
333 if(status == KRB5_SENDAUTH_REJECTED &&
334 protocol_version == 2 && protocol_version_str == NULL)
335 sendauth_version_error = 1;
336 else
337 krb5_warn(context, status, "%s", hostname);
338 return 1;
341 status = krb5_auth_con_getlocalsubkey (context, auth_context, &keyblock);
342 if(keyblock == NULL)
343 status = krb5_auth_con_getkey (context, auth_context, &keyblock);
344 if (status) {
345 warnx ("krb5_auth_con_getkey: %s", krb5_get_err_text(context, status));
346 return 1;
349 status = krb5_auth_con_setaddrs_from_fd (context,
350 auth_context,
351 &s);
352 if (status) {
353 warnx("krb5_auth_con_setaddrs_from_fd: %s",
354 krb5_get_err_text(context, status));
355 return(1);
358 status = krb5_crypto_init(context, keyblock, 0, &crypto);
359 if(status) {
360 warnx ("krb5_crypto_init: %s", krb5_get_err_text(context, status));
361 return 1;
364 len = strlen(remote_user) + 1;
365 if (net_write (s, remote_user, len) != len) {
366 warn ("write");
367 return 1;
369 if (do_encrypt && net_write (s, "-x ", 3) != 3) {
370 warn ("write");
371 return 1;
373 if (net_write (s, cmd, cmd_len) != cmd_len) {
374 warn ("write");
375 return 1;
378 if (do_unique_tkfile) {
379 if (net_write (s, tkfile, strlen(tkfile)) != strlen(tkfile)) {
380 warn ("write");
381 return 1;
384 len = strlen(local_user) + 1;
385 if (net_write (s, local_user, len) != len) {
386 warn ("write");
387 return 1;
390 if (!do_forward
391 || krb5_forward_cred (auth_context, s, hostname, do_forwardable)) {
392 /* Empty forwarding info */
394 u_char zero[4] = {0, 0, 0, 0};
395 write (s, &zero, 4);
397 krb5_auth_con_free (context, auth_context);
398 return 0;
401 #endif /* KRB5 */
403 static int
404 send_broken_auth(int s,
405 struct sockaddr *thisaddr,
406 struct sockaddr *thataddr,
407 const char *hostname,
408 const char *remote_user,
409 const char *local_user,
410 size_t cmd_len,
411 const char *cmd)
413 size_t len;
415 len = strlen(local_user) + 1;
416 if (net_write (s, local_user, len) != len) {
417 warn ("write");
418 return 1;
420 len = strlen(remote_user) + 1;
421 if (net_write (s, remote_user, len) != len) {
422 warn ("write");
423 return 1;
425 if (net_write (s, cmd, cmd_len) != cmd_len) {
426 warn ("write");
427 return 1;
429 return 0;
432 static int
433 proto (int s, int errsock,
434 const char *hostname, const char *local_user, const char *remote_user,
435 const char *cmd, size_t cmd_len,
436 int (*auth_func)(int s,
437 struct sockaddr *this, struct sockaddr *that,
438 const char *hostname, const char *remote_user,
439 const char *local_user, size_t cmd_len,
440 const char *cmd))
442 int errsock2;
443 char buf[BUFSIZ];
444 char *p;
445 size_t len;
446 char reply;
447 struct sockaddr_storage thisaddr_ss;
448 struct sockaddr *thisaddr = (struct sockaddr *)&thisaddr_ss;
449 struct sockaddr_storage thataddr_ss;
450 struct sockaddr *thataddr = (struct sockaddr *)&thataddr_ss;
451 struct sockaddr_storage erraddr_ss;
452 struct sockaddr *erraddr = (struct sockaddr *)&erraddr_ss;
453 socklen_t addrlen;
454 int ret;
456 addrlen = sizeof(thisaddr_ss);
457 if (getsockname (s, thisaddr, &addrlen) < 0) {
458 warn ("getsockname(%s)", hostname);
459 return 1;
461 addrlen = sizeof(thataddr_ss);
462 if (getpeername (s, thataddr, &addrlen) < 0) {
463 warn ("getpeername(%s)", hostname);
464 return 1;
467 if (errsock != -1) {
469 addrlen = sizeof(erraddr_ss);
470 if (getsockname (errsock, erraddr, &addrlen) < 0) {
471 warn ("getsockname");
472 return 1;
475 if (listen (errsock, 1) < 0) {
476 warn ("listen");
477 return 1;
480 p = buf;
481 snprintf (p, sizeof(buf), "%u",
482 ntohs(socket_get_port(erraddr)));
483 len = strlen(buf) + 1;
484 if(net_write (s, buf, len) != len) {
485 warn ("write");
486 close (errsock);
487 return 1;
491 for (;;) {
492 fd_set fdset;
494 if (errsock >= FD_SETSIZE || s >= FD_SETSIZE)
495 errx (1, "fd too large");
497 FD_ZERO(&fdset);
498 FD_SET(errsock, &fdset);
499 FD_SET(s, &fdset);
501 ret = select (max(errsock, s) + 1, &fdset, NULL, NULL, NULL);
502 if (ret < 0) {
503 if (errno == EINTR)
504 continue;
505 warn ("select");
506 close (errsock);
507 return 1;
509 if (FD_ISSET(errsock, &fdset)) {
510 errsock2 = accept (errsock, NULL, NULL);
511 close (errsock);
512 if (errsock2 < 0) {
513 warn ("accept");
514 return 1;
516 break;
520 * there should not arrive any data on this fd so if it's
521 * readable it probably indicates that the other side when
522 * away.
525 if (FD_ISSET(s, &fdset)) {
526 warnx ("socket closed");
527 close (errsock);
528 errsock2 = -1;
529 break;
532 } else {
533 if (net_write (s, "0", 2) != 2) {
534 warn ("write");
535 return 1;
537 errsock2 = -1;
540 if ((*auth_func)(s, thisaddr, thataddr, hostname,
541 remote_user, local_user,
542 cmd_len, cmd)) {
543 close (errsock2);
544 return 1;
547 ret = net_read (s, &reply, 1);
548 if (ret < 0) {
549 warn ("read");
550 close (errsock2);
551 return 1;
552 } else if (ret == 0) {
553 warnx ("unexpected EOF from %s", hostname);
554 close (errsock2);
555 return 1;
557 if (reply != 0) {
559 warnx ("Error from rshd at %s:", hostname);
561 while ((ret = read (s, buf, sizeof(buf))) > 0)
562 write (STDOUT_FILENO, buf, ret);
563 write (STDOUT_FILENO,"\n",1);
564 close (errsock2);
565 return 1;
568 if (sock_debug) {
569 int one = 1;
570 if (setsockopt(s, SOL_SOCKET, SO_DEBUG, (void *)&one, sizeof(one)) < 0)
571 warn("setsockopt remote");
572 if (errsock2 != -1 &&
573 setsockopt(errsock2, SOL_SOCKET, SO_DEBUG,
574 (void *)&one, sizeof(one)) < 0)
575 warn("setsockopt stderr");
578 return rsh_loop (s, errsock2);
582 * Return in `res' a copy of the concatenation of `argc, argv' into
583 * malloced space. */
585 static size_t
586 construct_command (char **res, int argc, char **argv)
588 int i;
589 size_t len = 0;
590 char *tmp;
592 for (i = 0; i < argc; ++i)
593 len += strlen(argv[i]) + 1;
594 len = max (1, len);
595 tmp = malloc (len);
596 if (tmp == NULL)
597 errx (1, "malloc %lu failed", (unsigned long)len);
599 *tmp = '\0';
600 for (i = 0; i < argc - 1; ++i) {
601 strlcat (tmp, argv[i], len);
602 strlcat (tmp, " ", len);
604 if (argc > 0)
605 strlcat (tmp, argv[argc-1], len);
606 *res = tmp;
607 return len;
610 static char *
611 print_addr (const struct sockaddr *sa)
613 char addr_str[256];
614 char *res;
615 const char *as = NULL;
617 if(sa->sa_family == AF_INET)
618 as = inet_ntop (sa->sa_family, &((struct sockaddr_in*)sa)->sin_addr,
619 addr_str, sizeof(addr_str));
620 #ifdef HAVE_INET6
621 else if(sa->sa_family == AF_INET6)
622 as = inet_ntop (sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr,
623 addr_str, sizeof(addr_str));
624 #endif
625 if(as == NULL)
626 return NULL;
627 res = strdup(as);
628 if (res == NULL)
629 errx (1, "malloc: out of memory");
630 return res;
633 static int
634 doit_broken (int argc,
635 char **argv,
636 int hostindex,
637 struct addrinfo *ai,
638 const char *remote_user,
639 const char *local_user,
640 int priv_socket1,
641 int priv_socket2,
642 const char *cmd,
643 size_t cmd_len)
645 struct addrinfo *a;
647 if (connect (priv_socket1, ai->ai_addr, ai->ai_addrlen) < 0) {
648 int save_errno = errno;
650 close(priv_socket1);
651 close(priv_socket2);
653 for (a = ai->ai_next; a != NULL; a = a->ai_next) {
654 pid_t pid;
655 char *adr = print_addr(a->ai_addr);
656 if(adr == NULL)
657 continue;
659 pid = fork();
660 if (pid < 0)
661 err (1, "fork");
662 else if(pid == 0) {
663 char **new_argv;
664 int i = 0;
666 new_argv = malloc((argc + 2) * sizeof(*new_argv));
667 if (new_argv == NULL)
668 errx (1, "malloc: out of memory");
669 new_argv[i] = argv[i];
670 ++i;
671 if (hostindex == i)
672 new_argv[i++] = adr;
673 new_argv[i++] = "-K";
674 for(; i <= argc; ++i)
675 new_argv[i] = argv[i - 1];
676 if (hostindex > 1)
677 new_argv[hostindex + 1] = adr;
678 new_argv[argc + 1] = NULL;
679 execv(PATH_RSH, new_argv);
680 err(1, "execv(%s)", PATH_RSH);
681 } else {
682 int status;
683 free(adr);
685 while(waitpid(pid, &status, 0) < 0)
687 if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
688 return 0;
691 errno = save_errno;
692 warn("%s", argv[hostindex]);
693 return 1;
694 } else {
695 int ret;
697 ret = proto (priv_socket1, priv_socket2,
698 argv[hostindex],
699 local_user, remote_user,
700 cmd, cmd_len,
701 send_broken_auth);
702 return ret;
706 #if defined(KRB5)
707 static int
708 doit (const char *hostname,
709 struct addrinfo *ai,
710 const char *remote_user,
711 const char *local_user,
712 const char *cmd,
713 size_t cmd_len,
714 int (*auth_func)(int s,
715 struct sockaddr *this, struct sockaddr *that,
716 const char *hostname, const char *remote_user,
717 const char *local_user, size_t cmd_len,
718 const char *cmd))
720 int error;
721 struct addrinfo *a;
722 int socketfailed = 1;
723 int ret;
725 for (a = ai; a != NULL; a = a->ai_next) {
726 int s;
727 int errsock;
729 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
730 if (s < 0)
731 continue;
732 socketfailed = 0;
733 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
734 char addr[128];
735 if(getnameinfo(a->ai_addr, a->ai_addrlen,
736 addr, sizeof(addr), NULL, 0, NI_NUMERICHOST) == 0)
737 warn ("connect(%s [%s])", hostname, addr);
738 else
739 warn ("connect(%s)", hostname);
740 close (s);
741 continue;
743 if (do_errsock) {
744 struct addrinfo *ea, *eai;
745 struct addrinfo hints;
747 memset (&hints, 0, sizeof(hints));
748 hints.ai_socktype = a->ai_socktype;
749 hints.ai_protocol = a->ai_protocol;
750 hints.ai_family = a->ai_family;
751 hints.ai_flags = AI_PASSIVE;
753 errsock = -1;
755 error = getaddrinfo (NULL, "0", &hints, &eai);
756 if (error)
757 errx (1, "getaddrinfo: %s", gai_strerror(error));
758 for (ea = eai; ea != NULL; ea = ea->ai_next) {
759 errsock = socket (ea->ai_family, ea->ai_socktype,
760 ea->ai_protocol);
761 if (errsock < 0)
762 continue;
763 if (bind (errsock, ea->ai_addr, ea->ai_addrlen) < 0)
764 err (1, "bind");
765 break;
767 if (errsock < 0)
768 err (1, "socket");
769 freeaddrinfo (eai);
770 } else
771 errsock = -1;
773 ret = proto (s, errsock,
774 hostname,
775 local_user, remote_user,
776 cmd, cmd_len, auth_func);
777 close (s);
778 return ret;
780 if(socketfailed)
781 warnx ("failed to contact %s", hostname);
782 return -1;
784 #endif /* KRB5 */
786 struct getargs args[] = {
787 #ifdef KRB5
788 { "krb5", '5', arg_flag, &use_v5, "Use Kerberos V5" },
789 { "forward", 'f', arg_flag, &do_forward, "Forward credentials [krb5]"},
790 { "forwardable", 'F', arg_flag, &do_forwardable,
791 "Forward forwardable credentials [krb5]" },
792 { NULL, 'G', arg_negative_flag,&do_forward, "Don't forward credentials" },
793 { "unique", 'u', arg_flag, &do_unique_tkfile,
794 "Use unique remote credentials cache [krb5]" },
795 { "tkfile", 'U', arg_string, &unique_tkfile,
796 "Specifies remote credentials cache [krb5]" },
797 { "protocol", 'P', arg_string, &protocol_version_str,
798 "Protocol version [krb5]", "protocol" },
799 #endif
800 { "broken", 'K', arg_flag, &use_only_broken, "Use only priv port" },
801 #if defined(KRB5)
802 { "encrypt", 'x', arg_flag, &do_encrypt, "Encrypt connection" },
803 { NULL, 'z', arg_negative_flag, &do_encrypt,
804 "Don't encrypt connection", NULL },
805 #endif
806 { NULL, 'd', arg_flag, &sock_debug, "Enable socket debugging" },
807 { "input", 'n', arg_negative_flag, &input, "Close stdin" },
808 { "port", 'p', arg_string, &port_str, "Use this port",
809 "port" },
810 { "user", 'l', arg_string, &user, "Run as this user", "login" },
811 { "stderr", 'e', arg_negative_flag, &do_errsock, "Don't open stderr"},
812 #ifdef KRB5
813 #endif
814 { "version", 0, arg_flag, &do_version, NULL },
815 { "help", 0, arg_flag, &do_help, NULL }
818 static void
819 usage (int ret)
821 arg_printusage (args,
822 sizeof(args) / sizeof(args[0]),
823 NULL,
824 "[login@]host [command]");
825 exit (ret);
833 main(int argc, char **argv)
835 int priv_port1, priv_port2;
836 int priv_socket1, priv_socket2;
837 int argindex = 0;
838 int error;
839 struct addrinfo hints, *ai;
840 int ret = 1;
841 char *cmd;
842 char *tmp;
843 size_t cmd_len;
844 const char *local_user;
845 char *host = NULL;
846 int host_index = -1;
847 #ifdef KRB5
848 int status;
849 #endif
850 uid_t uid;
852 priv_port1 = priv_port2 = IPPORT_RESERVED-1;
853 priv_socket1 = rresvport(&priv_port1);
854 priv_socket2 = rresvport(&priv_port2);
855 uid = getuid ();
856 if (setuid (uid) || (uid != 0 && setuid(0) == 0))
857 err (1, "setuid");
859 setprogname (argv[0]);
861 if (argc >= 2 && argv[1][0] != '-') {
862 host = argv[host_index = 1];
863 argindex = 1;
866 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
867 &argindex))
868 usage (1);
870 if (do_help)
871 usage (0);
873 if (do_version) {
874 print_version (NULL);
875 return 0;
878 #ifdef KRB5
879 if(protocol_version_str != NULL) {
880 if(strcasecmp(protocol_version_str, "N") == 0)
881 protocol_version = 2;
882 else if(strcasecmp(protocol_version_str, "O") == 0)
883 protocol_version = 1;
884 else {
885 char *end;
886 int v;
887 v = strtol(protocol_version_str, &end, 0);
888 if(*end != '\0' || (v != 1 && v != 2)) {
889 errx(1, "unknown protocol version \"%s\"",
890 protocol_version_str);
892 protocol_version = v;
896 status = krb5_init_context (&context);
897 if (status) {
898 if(use_v5 == 1)
899 errx(1, "krb5_init_context failed: %d", status);
900 else
901 use_v5 = 0;
904 /* request for forwardable on the command line means we should
905 also forward */
906 if (do_forwardable == 1)
907 do_forward = 1;
909 #endif
911 if (use_only_broken) {
912 #ifdef KRB5
913 use_v5 = 0;
914 #endif
917 if(priv_socket1 < 0) {
918 if (use_only_broken)
919 errx (1, "unable to bind reserved port: is rsh setuid root?");
920 use_broken = 0;
923 #if defined(KRB5)
924 if (do_encrypt == 1 && use_only_broken)
925 errx (1, "encryption not supported with old style authentication");
926 #endif
930 #ifdef KRB5
931 if (do_unique_tkfile && unique_tkfile != NULL)
932 errx (1, "Only one of -u and -U allowed.");
934 if (do_unique_tkfile)
935 strlcpy(tkfile,"-u ", sizeof(tkfile));
936 else if (unique_tkfile != NULL) {
937 if (strchr(unique_tkfile,' ') != NULL) {
938 warnx("Space is not allowed in tkfilename");
939 usage(1);
941 do_unique_tkfile = 1;
942 snprintf (tkfile, sizeof(tkfile), "-U %s ", unique_tkfile);
944 #endif
946 if (host == NULL) {
947 if (argc - argindex < 1)
948 usage (1);
949 else
950 host = argv[host_index = argindex++];
953 if((tmp = strchr(host, '@')) != NULL) {
954 *tmp++ = '\0';
955 user = host;
956 host = tmp;
959 if (argindex == argc) {
960 close (priv_socket1);
961 close (priv_socket2);
962 argv[0] = "rlogin";
963 execvp ("rlogin", argv);
964 err (1, "execvp rlogin");
967 local_user = get_default_username ();
968 if (local_user == NULL)
969 errx (1, "who are you?");
971 if (user == NULL)
972 user = local_user;
974 cmd_len = construct_command(&cmd, argc - argindex, argv + argindex);
977 * Try all different authentication methods
980 #ifdef KRB5
981 if (ret && use_v5) {
982 memset (&hints, 0, sizeof(hints));
983 hints.ai_socktype = SOCK_STREAM;
984 hints.ai_protocol = IPPROTO_TCP;
986 if(port_str == NULL) {
987 error = getaddrinfo(host, "kshell", &hints, &ai);
988 if(error == EAI_NONAME)
989 error = getaddrinfo(host, "544", &hints, &ai);
990 } else
991 error = getaddrinfo(host, port_str, &hints, &ai);
993 if(error)
994 errx (1, "getaddrinfo: %s", gai_strerror(error));
996 auth_method = AUTH_KRB5;
997 again:
998 ret = doit (host, ai, user, local_user, cmd, cmd_len,
999 send_krb5_auth);
1000 if(ret != 0 && sendauth_version_error &&
1001 protocol_version == 2) {
1002 protocol_version = 1;
1003 goto again;
1005 freeaddrinfo(ai);
1007 #endif
1008 if (ret && use_broken) {
1009 memset (&hints, 0, sizeof(hints));
1010 hints.ai_socktype = SOCK_STREAM;
1011 hints.ai_protocol = IPPROTO_TCP;
1013 if(port_str == NULL) {
1014 error = getaddrinfo(host, "shell", &hints, &ai);
1015 if(error == EAI_NONAME)
1016 error = getaddrinfo(host, "514", &hints, &ai);
1017 } else
1018 error = getaddrinfo(host, port_str, &hints, &ai);
1020 if(error)
1021 errx (1, "getaddrinfo: %s", gai_strerror(error));
1023 auth_method = AUTH_BROKEN;
1024 ret = doit_broken (argc, argv, host_index, ai,
1025 user, local_user,
1026 priv_socket1,
1027 do_errsock ? priv_socket2 : -1,
1028 cmd, cmd_len);
1029 freeaddrinfo(ai);
1031 free(cmd);
1032 return ret;