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