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