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