Add heimdal-0.6.3
[dragonfly.git] / crypto / heimdal-0.6.3 / appl / rsh / rsh.c
1 /*
2  * Copyright (c) 1997 - 2003 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,v 1.71 2003/04/16 20:37:20 joda Exp $");
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 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;
72
73 /*
74  *
75  */
76
77 static int input = 1;           /* Read from stdin */
78
79 static int
80 loop (int s, int errsock)
81 {
82     fd_set real_readset;
83     int count = 1;
84
85 #ifdef KRB5
86     if(auth_method == AUTH_KRB5 && protocol_version == 2)
87         init_ivecs(1);
88 #endif
89
90     if (s >= FD_SETSIZE || (errsock != -1 && errsock >= FD_SETSIZE))
91         errx (1, "fd too large");
92     
93     FD_ZERO(&real_readset);
94     FD_SET(s, &real_readset);
95     if (errsock != -1) {
96         FD_SET(errsock, &real_readset);
97         ++count;
98     }
99     if(input)
100         FD_SET(STDIN_FILENO, &real_readset);
101
102     for (;;) {
103         int ret;
104         fd_set readset;
105         char buf[RSH_BUFSIZ];
106
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");
114         }
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);
126         }
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);
138         }
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]);
149         }
150     }
151 }
152
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)
163 {
164     KTEXT_ST text;
165     CREDENTIALS cred;
166     MSG_DAT msg;
167     int status;
168     size_t len;
169
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;
181     }
182     memcpy (iv, cred.session, sizeof(iv));
183
184     len = strlen(remote_user) + 1;
185     if (net_write (s, remote_user, len) != len) {
186         warn("write");
187         return 1;
188     }
189     if (net_write (s, cmd, cmd_len) != cmd_len) {
190         warn("write");
191         return 1;
192     }
193     return 0;
194 }
195 #endif /* KRB4 */
196
197 #ifdef KRB5
198 /*
199  * Send forward information on `s' for host `hostname', them being
200  * forwardable themselves if `forwardable'
201  */
202
203 static int
204 krb5_forward_cred (krb5_auth_context auth_context,
205                    int s,
206                    const char *hostname,
207                    int forwardable)
208 {
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;
215
216     memset (&creds, 0, sizeof(creds));
217
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;
223     }
224
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;
230     }
231
232     creds.client = principal;
233     
234     ret = krb5_build_principal (context,
235                                 &creds.server,
236                                 strlen(principal->realm),
237                                 principal->realm,
238                                 "krbtgt",
239                                 principal->realm,
240                                 NULL);
241
242     if (ret) {
243         warnx ("could not forward creds: krb5_build_principal: %s",
244                krb5_get_err_text (context, ret));
245         return 1;
246     }
247
248     creds.times.endtime = 0;
249
250     flags.i = 0;
251     flags.b.forwarded   = 1;
252     flags.b.forwardable = forwardable;
253
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;
265     }
266
267     ret = krb5_write_message (context,
268                               (void *)&s,
269                               &out_data);
270     krb5_data_free (&out_data);
271
272     if (ret)
273         warnx ("could not forward creds: krb5_write_message: %s",
274                krb5_get_err_text (context, ret));
275     return 0;
276 }
277
278 static int sendauth_version_error;
279
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)
289 {
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;
297
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;
306     }
307
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);
314     }
315
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);
322
323     ap_opts = 0;
324
325     if(do_encrypt)
326         ap_opts |= AP_OPTS_MUTUAL_REQUIRED;
327
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();
339     }
340         
341     status = krb5_sendauth (context,
342                             &auth_context,
343                             &s,
344                             protocol_string,
345                             NULL,
346                             server,
347                             ap_opts,
348                             &cksum_data,
349                             NULL,
350                             NULL,
351                             NULL,
352                             NULL,
353                             NULL);
354
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);
366     }
367     
368     krb5_free_principal(context, server);
369     krb5_data_free(&cksum_data);
370
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;
378     }
379
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;
386     }
387
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);
395     }
396
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;
401     }
402
403     len = strlen(remote_user) + 1;
404     if (net_write (s, remote_user, len) != len) {
405         warn ("write");
406         return 1;
407     }
408     if (do_encrypt && net_write (s, "-x ", 3) != 3) {
409         warn ("write");
410         return 1;
411     }
412     if (net_write (s, cmd, cmd_len) != cmd_len) {
413         warn ("write");
414         return 1;
415     }
416
417     if (do_unique_tkfile) {
418         if (net_write (s, tkfile, strlen(tkfile)) != strlen(tkfile)) {
419             warn ("write");
420             return 1;
421         }
422     }
423     len = strlen(local_user) + 1;
424     if (net_write (s, local_user, len) != len) {
425         warn ("write");
426         return 1;
427     }
428
429     if (!do_forward
430         || krb5_forward_cred (auth_context, s, hostname, do_forwardable)) {
431         /* Empty forwarding info */
432
433         u_char zero[4] = {0, 0, 0, 0};
434         write (s, &zero, 4);
435     }
436     krb5_auth_con_free (context, auth_context);
437     return 0;
438 }
439
440 #endif /* KRB5 */
441
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)
451 {
452     size_t len;
453
454     len = strlen(local_user) + 1;
455     if (net_write (s, local_user, len) != len) {
456         warn ("write");
457         return 1;
458     }
459     len = strlen(remote_user) + 1;
460     if (net_write (s, remote_user, len) != len) {
461         warn ("write");
462         return 1;
463     }
464     if (net_write (s, cmd, cmd_len) != cmd_len) {
465         warn ("write");
466         return 1;
467     }
468     return 0;
469 }
470
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))
480 {
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;
494
495     addrlen = sizeof(thisaddr_ss);
496     if (getsockname (s, thisaddr, &addrlen) < 0) {
497         warn ("getsockname(%s)", hostname);
498         return 1;
499     }
500     addrlen = sizeof(thataddr_ss);
501     if (getpeername (s, thataddr, &addrlen) < 0) {
502         warn ("getpeername(%s)", hostname);
503         return 1;
504     }
505
506     if (errsock != -1) {
507
508         addrlen = sizeof(erraddr_ss);
509         if (getsockname (errsock, erraddr, &addrlen) < 0) {
510             warn ("getsockname");
511             return 1;
512         }
513
514         if (listen (errsock, 1) < 0) {
515             warn ("listen");
516             return 1;
517         }
518
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;
527         }
528
529
530         for (;;) {
531             fd_set fdset;
532
533             if (errsock >= FD_SETSIZE || s >= FD_SETSIZE)
534                 errx (1, "fd too large");
535
536             FD_ZERO(&fdset);
537             FD_SET(errsock, &fdset);
538             FD_SET(s, &fdset);
539
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;
547             }
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;
554                 }
555                 break;
556             }
557
558             /*
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.
562              */
563
564             if (FD_ISSET(s, &fdset)) {
565                 warnx ("socket closed");
566                 close (errsock);
567                 errsock2 = -1;
568                 break;
569             }
570         }
571     } else {
572         if (net_write (s, "0", 2) != 2) {
573             warn ("write");
574             return 1;
575         }
576         errsock2 = -1;
577     }
578
579     if ((*auth_func)(s, thisaddr, thataddr, hostname,
580                      remote_user, local_user,
581                      cmd_len, cmd)) {
582         close (errsock2);
583         return 1;
584     } 
585
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;
595     }
596     if (reply != 0) {
597
598         warnx ("Error from rshd at %s:", hostname);
599
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;
605     }
606
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");
615     }
616     
617     return loop (s, errsock2);
618 }
619
620 /*
621  * Return in `res' a copy of the concatenation of `argc, argv' into
622  * malloced space.  */
623
624 static size_t
625 construct_command (char **res, int argc, char **argv)
626 {
627     int i;
628     size_t len = 0;
629     char *tmp;
630
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);
637
638     *tmp = '\0';
639     for (i = 0; i < argc - 1; ++i) {
640         strcat (tmp, argv[i]);
641         strcat (tmp, " ");
642     }
643     if (argc > 0)
644         strcat (tmp, argv[argc-1]);
645     *res = tmp;
646     return len;
647 }
648
649 static char *
650 print_addr (const struct sockaddr *sa)
651 {
652     char addr_str[256];
653     char *res;
654     const char *as = NULL;
655
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;
670 }
671
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)
683 {
684     struct addrinfo *a;
685
686     if (connect (priv_socket1, ai->ai_addr, ai->ai_addrlen) < 0) {
687         int save_errno = errno;
688         
689         close(priv_socket1);
690         close(priv_socket2);
691
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;
697
698             pid = fork();
699             if (pid < 0)
700                 err (1, "fork");
701             else if(pid == 0) {
702                 char **new_argv;
703                 int i = 0;
704
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);
723
724                 while(waitpid(pid, &status, 0) < 0)
725                     ;
726                 if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
727                     return 0;
728             }
729         }
730         errno = save_errno;
731         warn("%s", argv[hostindex]);
732         return 1;
733     } else {
734         int ret;
735
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;
742     }
743 }
744
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))
759 {
760     int error;
761     struct addrinfo *a;
762     int socketfailed = 1;
763     int ret;
764
765     for (a = ai; a != NULL; a = a->ai_next) {
766         int s;
767         int errsock;
768
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;
782         }
783         if (do_errsock) {
784             struct addrinfo *ea, *eai;
785             struct addrinfo hints;
786
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;
792
793             errsock = -1;
794
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;
806             }
807             if (errsock < 0)
808                 err (1, "socket");
809             freeaddrinfo (eai);
810         } else
811             errsock = -1;
812     
813         ret = proto (s, errsock,
814                      hostname,
815                      local_user, remote_user,
816                      cmd, cmd_len, auth_func);
817         close (s);
818         return ret;
819     }
820     if(socketfailed)
821         warnx ("failed to contact %s", hostname);
822     return -1;
823 }
824 #endif /* KRB4 || KRB5 */
825
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 }
859 };
860
861 static void
862 usage (int ret)
863 {
864     arg_printusage (args,
865                     sizeof(args) / sizeof(args[0]),
866                     NULL,
867                     "[login@]host [command]");
868     exit (ret);
869 }
870
871 /*
872  *
873  */
874
875 int
876 main(int argc, char **argv)
877 {
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;
894
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");
901     
902     setprogname (argv[0]);
903
904     if (argc >= 2 && argv[1][0] != '-') {
905         host = argv[host_index = 1];
906         argindex = 1;
907     }
908     
909     if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
910                 &argindex))
911         usage (1);
912
913     if (do_help)
914         usage (0);
915
916     if (do_version) {
917         print_version (NULL);
918         return 0;
919     }
920
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);
933             }
934             protocol_version = v;
935         }
936     }
937
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;
945     }
946
947     /* request for forwardable on the command line means we should
948        also forward */
949     if (do_forwardable == 1)
950         do_forward = 1;
951
952 #endif
953
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    
960
961     if (use_only_broken) {
962 #ifdef KRB4
963         use_v4 = 0;
964 #endif
965 #ifdef KRB5
966         use_v5 = 0;
967 #endif
968     }
969
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;
974     }
975
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
980
981
982
983 #ifdef KRB5
984     if (do_unique_tkfile && unique_tkfile != NULL)
985         errx (1, "Only one of -u and -U allowed.");
986
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);
993         }
994         do_unique_tkfile = 1;
995         snprintf (tkfile, sizeof(tkfile), "-U %s ", unique_tkfile);
996     }
997 #endif
998
999     if (host == NULL) {
1000         if (argc - argindex < 1)
1001             usage (1);
1002         else
1003             host = argv[host_index = argindex++];
1004     }
1005     
1006     if((tmp = strchr(host, '@')) != NULL) {
1007         *tmp++ = '\0';
1008         user = host;
1009         host = tmp;
1010     }
1011
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");
1018     }
1019
1020     local_user = get_default_username ();
1021     if (local_user == NULL)
1022         errx (1, "who are you?");
1023
1024     if (user == NULL)
1025         user = local_user;
1026
1027     cmd_len = construct_command(&cmd, argc - argindex, argv + argindex);
1028     
1029     /*
1030      * Try all different authentication methods
1031      */
1032
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;
1038
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);
1045
1046         if(error)
1047             errx (1, "getaddrinfo: %s", gai_strerror(error));
1048
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;
1058         }
1059         freeaddrinfo(ai);
1060     }
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;
1067
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);
1077             }
1078         } else
1079             error = getaddrinfo(host, port_str, &hints, &ai);
1080
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);
1088     }
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;
1094
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);
1101
1102         if(error)
1103             errx (1, "getaddrinfo: %s", gai_strerror(error));
1104
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);
1112     }
1113     free(cmd);
1114     return ret;
1115 }