155682Smarkm/* 2233294Sstas * Copyright (c) 1997 - 2004 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 555682Smarkm * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 955682Smarkm * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 1655682Smarkm * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 2055682Smarkm * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 3255682Smarkm */ 3355682Smarkm 3455682Smarkm#include "rsh_locl.h" 35233294SstasRCSID("$Id$"); 3655682Smarkm 3755682Smarkmenum auth_method auth_method; 38233294Sstas#if defined(KRB5) 3972445Sassarint do_encrypt = -1; 40102644Snectar#endif 41102644Snectar#ifdef KRB5 4255682Smarkmint do_unique_tkfile = 0; 4355682Smarkmchar *unique_tkfile = NULL; 4455682Smarkmchar tkfile[MAXPATHLEN]; 45102644Snectarint do_forward = -1; 46102644Snectarint do_forwardable = -1; 4755682Smarkmkrb5_context context; 4855682Smarkmkrb5_keyblock *keyblock; 4955682Smarkmkrb5_crypto crypto; 50102644Snectar#endif 5190926Snectarint sock_debug = 0; 5255682Smarkm 53102644Snectar#ifdef KRB5 5490926Snectarstatic int use_v5 = -1; 55102644Snectar#endif 56233294Sstas#if defined(KRB5) 5790926Snectarstatic int use_only_broken = 0; 58178825Sdfr#else 59178825Sdfrstatic int use_only_broken = 1; 60178825Sdfr#endif 6190926Snectarstatic int use_broken = 1; 6290926Snectarstatic char *port_str; 6390926Snectarstatic const char *user; 6490926Snectarstatic int do_version; 6590926Snectarstatic int do_help; 6690926Snectarstatic int do_errsock = 1; 67178825Sdfr#ifdef KRB5 68103423Snectarstatic char *protocol_version_str; 69103423Snectarstatic int protocol_version = 2; 70178825Sdfr#endif 7155682Smarkm 7255682Smarkm/* 7355682Smarkm * 7455682Smarkm */ 7555682Smarkm 7655682Smarkmstatic int input = 1; /* Read from stdin */ 7755682Smarkm 7855682Smarkmstatic int 79178825Sdfrrsh_loop (int s, int errsock) 8055682Smarkm{ 8155682Smarkm fd_set real_readset; 8255682Smarkm int count = 1; 8355682Smarkm 84103423Snectar#ifdef KRB5 85103423Snectar if(auth_method == AUTH_KRB5 && protocol_version == 2) 86178825Sdfr init_ivecs(1, errsock != -1); 87103423Snectar#endif 88103423Snectar 89120945Snectar if (s >= FD_SETSIZE || (errsock != -1 && errsock >= FD_SETSIZE)) 9072445Sassar errx (1, "fd too large"); 91233294Sstas 9255682Smarkm FD_ZERO(&real_readset); 9355682Smarkm FD_SET(s, &real_readset); 9455682Smarkm if (errsock != -1) { 9555682Smarkm FD_SET(errsock, &real_readset); 9655682Smarkm ++count; 9755682Smarkm } 9855682Smarkm if(input) 9955682Smarkm FD_SET(STDIN_FILENO, &real_readset); 10055682Smarkm 10155682Smarkm for (;;) { 10255682Smarkm int ret; 10355682Smarkm fd_set readset; 10455682Smarkm char buf[RSH_BUFSIZ]; 10555682Smarkm 10655682Smarkm readset = real_readset; 10755682Smarkm ret = select (max(s, errsock) + 1, &readset, NULL, NULL, NULL); 10855682Smarkm if (ret < 0) { 10955682Smarkm if (errno == EINTR) 11055682Smarkm continue; 11155682Smarkm else 11255682Smarkm err (1, "select"); 11355682Smarkm } 11455682Smarkm if (FD_ISSET(s, &readset)) { 115103423Snectar ret = do_read (s, buf, sizeof(buf), ivec_in[0]); 11655682Smarkm if (ret < 0) 11755682Smarkm err (1, "read"); 11855682Smarkm else if (ret == 0) { 11955682Smarkm close (s); 12055682Smarkm FD_CLR(s, &real_readset); 12155682Smarkm if (--count == 0) 12255682Smarkm return 0; 12355682Smarkm } else 12455682Smarkm net_write (STDOUT_FILENO, buf, ret); 12555682Smarkm } 12655682Smarkm if (errsock != -1 && FD_ISSET(errsock, &readset)) { 127103423Snectar ret = do_read (errsock, buf, sizeof(buf), ivec_in[1]); 12855682Smarkm if (ret < 0) 12955682Smarkm err (1, "read"); 13055682Smarkm else if (ret == 0) { 13155682Smarkm close (errsock); 13255682Smarkm FD_CLR(errsock, &real_readset); 13355682Smarkm if (--count == 0) 13455682Smarkm return 0; 13555682Smarkm } else 13655682Smarkm net_write (STDERR_FILENO, buf, ret); 13755682Smarkm } 13855682Smarkm if (FD_ISSET(STDIN_FILENO, &readset)) { 13955682Smarkm ret = read (STDIN_FILENO, buf, sizeof(buf)); 14055682Smarkm if (ret < 0) 14155682Smarkm err (1, "read"); 14255682Smarkm else if (ret == 0) { 14355682Smarkm close (STDIN_FILENO); 14455682Smarkm FD_CLR(STDIN_FILENO, &real_readset); 14555682Smarkm shutdown (s, SHUT_WR); 14655682Smarkm } else 147103423Snectar do_write (s, buf, ret, ivec_out[0]); 14855682Smarkm } 14955682Smarkm } 15055682Smarkm} 15155682Smarkm 152102644Snectar#ifdef KRB5 15355682Smarkm/* 15455682Smarkm * Send forward information on `s' for host `hostname', them being 15555682Smarkm * forwardable themselves if `forwardable' 15655682Smarkm */ 15755682Smarkm 15855682Smarkmstatic int 15955682Smarkmkrb5_forward_cred (krb5_auth_context auth_context, 16055682Smarkm int s, 16155682Smarkm const char *hostname, 16255682Smarkm int forwardable) 16355682Smarkm{ 16455682Smarkm krb5_error_code ret; 16555682Smarkm krb5_ccache ccache; 16655682Smarkm krb5_creds creds; 16755682Smarkm krb5_kdc_flags flags; 16855682Smarkm krb5_data out_data; 16955682Smarkm krb5_principal principal; 17055682Smarkm 17155682Smarkm memset (&creds, 0, sizeof(creds)); 17255682Smarkm 17355682Smarkm ret = krb5_cc_default (context, &ccache); 17455682Smarkm if (ret) { 17555682Smarkm warnx ("could not forward creds: krb5_cc_default: %s", 17655682Smarkm krb5_get_err_text (context, ret)); 17755682Smarkm return 1; 17855682Smarkm } 17955682Smarkm 18055682Smarkm ret = krb5_cc_get_principal (context, ccache, &principal); 18155682Smarkm if (ret) { 18255682Smarkm warnx ("could not forward creds: krb5_cc_get_principal: %s", 18355682Smarkm krb5_get_err_text (context, ret)); 18455682Smarkm return 1; 18555682Smarkm } 18655682Smarkm 18755682Smarkm creds.client = principal; 18855682Smarkm 189233294Sstas ret = krb5_make_principal(context, 190233294Sstas &creds.server, 191233294Sstas principal->realm, 192233294Sstas "krbtgt", 193233294Sstas principal->realm, 194233294Sstas NULL); 195233294Sstas 19655682Smarkm if (ret) { 197233294Sstas warnx ("could not forward creds: krb5_make_principal: %s", 19855682Smarkm krb5_get_err_text (context, ret)); 19955682Smarkm return 1; 20055682Smarkm } 20155682Smarkm 20255682Smarkm creds.times.endtime = 0; 20355682Smarkm 20455682Smarkm flags.i = 0; 20555682Smarkm flags.b.forwarded = 1; 20655682Smarkm flags.b.forwardable = forwardable; 20755682Smarkm 20855682Smarkm ret = krb5_get_forwarded_creds (context, 20955682Smarkm auth_context, 21055682Smarkm ccache, 21155682Smarkm flags.i, 21255682Smarkm hostname, 21355682Smarkm &creds, 21455682Smarkm &out_data); 21555682Smarkm if (ret) { 21655682Smarkm warnx ("could not forward creds: krb5_get_forwarded_creds: %s", 21755682Smarkm krb5_get_err_text (context, ret)); 21855682Smarkm return 1; 21955682Smarkm } 22055682Smarkm 22155682Smarkm ret = krb5_write_message (context, 22255682Smarkm (void *)&s, 22355682Smarkm &out_data); 22455682Smarkm krb5_data_free (&out_data); 22555682Smarkm 22655682Smarkm if (ret) 22755682Smarkm warnx ("could not forward creds: krb5_write_message: %s", 22855682Smarkm krb5_get_err_text (context, ret)); 22955682Smarkm return 0; 23055682Smarkm} 23155682Smarkm 232103423Snectarstatic int sendauth_version_error; 233103423Snectar 23455682Smarkmstatic int 23555682Smarkmsend_krb5_auth(int s, 23655682Smarkm struct sockaddr *thisaddr, 23755682Smarkm struct sockaddr *thataddr, 23855682Smarkm const char *hostname, 23955682Smarkm const char *remote_user, 24055682Smarkm const char *local_user, 24155682Smarkm size_t cmd_len, 24255682Smarkm const char *cmd) 24355682Smarkm{ 24455682Smarkm krb5_principal server; 24555682Smarkm krb5_data cksum_data; 24655682Smarkm int status; 24755682Smarkm size_t len; 24855682Smarkm krb5_auth_context auth_context = NULL; 249103423Snectar const char *protocol_string = NULL; 250103423Snectar krb5_flags ap_opts; 251178825Sdfr char *str; 25255682Smarkm 25355682Smarkm status = krb5_sname_to_principal(context, 25455682Smarkm hostname, 25555682Smarkm "host", 25655682Smarkm KRB5_NT_SRV_HST, 25755682Smarkm &server); 25855682Smarkm if (status) { 25955682Smarkm warnx ("%s: %s", hostname, krb5_get_err_text(context, status)); 26055682Smarkm return 1; 26155682Smarkm } 26255682Smarkm 263120945Snectar if(do_encrypt == -1) { 264233294Sstas krb5_appdefault_boolean(context, NULL, 265233294Sstas krb5_principal_get_realm(context, server), 266233294Sstas "encrypt", 267233294Sstas FALSE, 268120945Snectar &do_encrypt); 269120945Snectar } 270120945Snectar 271178825Sdfr cksum_data.length = asprintf (&str, 27255682Smarkm "%u:%s%s%s", 27355682Smarkm ntohs(socket_get_port(thataddr)), 27455682Smarkm do_encrypt ? "-x " : "", 27555682Smarkm cmd, 27655682Smarkm remote_user); 277178825Sdfr if (str == NULL) { 278178825Sdfr warnx ("%s: failed to allocate command", hostname); 279178825Sdfr return 1; 280178825Sdfr } 281178825Sdfr cksum_data.data = str; 28255682Smarkm 283103423Snectar ap_opts = 0; 284103423Snectar 285103423Snectar if(do_encrypt) 286103423Snectar ap_opts |= AP_OPTS_MUTUAL_REQUIRED; 287103423Snectar 288103423Snectar switch(protocol_version) { 289103423Snectar case 2: 290103423Snectar ap_opts |= AP_OPTS_USE_SUBKEY; 291103423Snectar protocol_string = KCMD_NEW_VERSION; 292103423Snectar break; 293103423Snectar case 1: 294103423Snectar protocol_string = KCMD_OLD_VERSION; 295103423Snectar key_usage = KRB5_KU_OTHER_ENCRYPTED; 296103423Snectar break; 297103423Snectar default: 298103423Snectar abort(); 299103423Snectar } 300233294Sstas 30155682Smarkm status = krb5_sendauth (context, 30255682Smarkm &auth_context, 30355682Smarkm &s, 304103423Snectar protocol_string, 30555682Smarkm NULL, 30655682Smarkm server, 307103423Snectar ap_opts, 30855682Smarkm &cksum_data, 30955682Smarkm NULL, 31055682Smarkm NULL, 31155682Smarkm NULL, 31255682Smarkm NULL, 31355682Smarkm NULL); 314103423Snectar 315120945Snectar /* do this while we have a principal */ 316120945Snectar if(do_forward == -1 || do_forwardable == -1) { 317120945Snectar krb5_const_realm realm = krb5_principal_get_realm(context, server); 318120945Snectar if (do_forwardable == -1) 319120945Snectar krb5_appdefault_boolean(context, NULL, realm, 320233294Sstas "forwardable", FALSE, 321120945Snectar &do_forwardable); 322120945Snectar if (do_forward == -1) 323120945Snectar krb5_appdefault_boolean(context, NULL, realm, 324233294Sstas "forward", FALSE, 325120945Snectar &do_forward); 326120945Snectar } 327233294Sstas 328103423Snectar krb5_free_principal(context, server); 329103423Snectar krb5_data_free(&cksum_data); 330103423Snectar 33155682Smarkm if (status) { 332233294Sstas if(status == KRB5_SENDAUTH_REJECTED && 333103423Snectar protocol_version == 2 && protocol_version_str == NULL) 334103423Snectar sendauth_version_error = 1; 335103423Snectar else 336103423Snectar krb5_warn(context, status, "%s", hostname); 33755682Smarkm return 1; 33855682Smarkm } 33955682Smarkm 340103423Snectar status = krb5_auth_con_getlocalsubkey (context, auth_context, &keyblock); 341103423Snectar if(keyblock == NULL) 342103423Snectar status = krb5_auth_con_getkey (context, auth_context, &keyblock); 34355682Smarkm if (status) { 34455682Smarkm warnx ("krb5_auth_con_getkey: %s", krb5_get_err_text(context, status)); 34555682Smarkm return 1; 34655682Smarkm } 34755682Smarkm 34855682Smarkm status = krb5_auth_con_setaddrs_from_fd (context, 34955682Smarkm auth_context, 35055682Smarkm &s); 35155682Smarkm if (status) { 35255682Smarkm warnx("krb5_auth_con_setaddrs_from_fd: %s", 35355682Smarkm krb5_get_err_text(context, status)); 35455682Smarkm return(1); 35555682Smarkm } 35655682Smarkm 35755682Smarkm status = krb5_crypto_init(context, keyblock, 0, &crypto); 35855682Smarkm if(status) { 35955682Smarkm warnx ("krb5_crypto_init: %s", krb5_get_err_text(context, status)); 36055682Smarkm return 1; 36155682Smarkm } 36255682Smarkm 36355682Smarkm len = strlen(remote_user) + 1; 36455682Smarkm if (net_write (s, remote_user, len) != len) { 36555682Smarkm warn ("write"); 36655682Smarkm return 1; 36755682Smarkm } 36855682Smarkm if (do_encrypt && net_write (s, "-x ", 3) != 3) { 36955682Smarkm warn ("write"); 37055682Smarkm return 1; 37155682Smarkm } 37255682Smarkm if (net_write (s, cmd, cmd_len) != cmd_len) { 37355682Smarkm warn ("write"); 37455682Smarkm return 1; 37555682Smarkm } 37655682Smarkm 37755682Smarkm if (do_unique_tkfile) { 37855682Smarkm if (net_write (s, tkfile, strlen(tkfile)) != strlen(tkfile)) { 37955682Smarkm warn ("write"); 38055682Smarkm return 1; 38155682Smarkm } 38255682Smarkm } 38355682Smarkm len = strlen(local_user) + 1; 38455682Smarkm if (net_write (s, local_user, len) != len) { 38555682Smarkm warn ("write"); 38655682Smarkm return 1; 38755682Smarkm } 38855682Smarkm 38955682Smarkm if (!do_forward 39055682Smarkm || krb5_forward_cred (auth_context, s, hostname, do_forwardable)) { 39155682Smarkm /* Empty forwarding info */ 39255682Smarkm 39355682Smarkm u_char zero[4] = {0, 0, 0, 0}; 39455682Smarkm write (s, &zero, 4); 39555682Smarkm } 39655682Smarkm krb5_auth_con_free (context, auth_context); 39755682Smarkm return 0; 39855682Smarkm} 39955682Smarkm 400102644Snectar#endif /* KRB5 */ 401102644Snectar 40255682Smarkmstatic int 40355682Smarkmsend_broken_auth(int s, 40455682Smarkm struct sockaddr *thisaddr, 40555682Smarkm struct sockaddr *thataddr, 40655682Smarkm const char *hostname, 40755682Smarkm const char *remote_user, 40855682Smarkm const char *local_user, 40955682Smarkm size_t cmd_len, 41055682Smarkm const char *cmd) 41155682Smarkm{ 41255682Smarkm size_t len; 41355682Smarkm 41455682Smarkm len = strlen(local_user) + 1; 41555682Smarkm if (net_write (s, local_user, len) != len) { 41655682Smarkm warn ("write"); 41755682Smarkm return 1; 41855682Smarkm } 41955682Smarkm len = strlen(remote_user) + 1; 42055682Smarkm if (net_write (s, remote_user, len) != len) { 42155682Smarkm warn ("write"); 42255682Smarkm return 1; 42355682Smarkm } 42455682Smarkm if (net_write (s, cmd, cmd_len) != cmd_len) { 42555682Smarkm warn ("write"); 42655682Smarkm return 1; 42755682Smarkm } 42855682Smarkm return 0; 42955682Smarkm} 43055682Smarkm 43155682Smarkmstatic int 43255682Smarkmproto (int s, int errsock, 43355682Smarkm const char *hostname, const char *local_user, const char *remote_user, 43455682Smarkm const char *cmd, size_t cmd_len, 43555682Smarkm int (*auth_func)(int s, 43655682Smarkm struct sockaddr *this, struct sockaddr *that, 43755682Smarkm const char *hostname, const char *remote_user, 43855682Smarkm const char *local_user, size_t cmd_len, 43955682Smarkm const char *cmd)) 44055682Smarkm{ 44155682Smarkm int errsock2; 44255682Smarkm char buf[BUFSIZ]; 44355682Smarkm char *p; 44455682Smarkm size_t len; 44555682Smarkm char reply; 44655682Smarkm struct sockaddr_storage thisaddr_ss; 44755682Smarkm struct sockaddr *thisaddr = (struct sockaddr *)&thisaddr_ss; 44855682Smarkm struct sockaddr_storage thataddr_ss; 44955682Smarkm struct sockaddr *thataddr = (struct sockaddr *)&thataddr_ss; 45055682Smarkm struct sockaddr_storage erraddr_ss; 45155682Smarkm struct sockaddr *erraddr = (struct sockaddr *)&erraddr_ss; 45272445Sassar socklen_t addrlen; 45355682Smarkm int ret; 45455682Smarkm 45555682Smarkm addrlen = sizeof(thisaddr_ss); 45655682Smarkm if (getsockname (s, thisaddr, &addrlen) < 0) { 45755682Smarkm warn ("getsockname(%s)", hostname); 45855682Smarkm return 1; 45955682Smarkm } 46055682Smarkm addrlen = sizeof(thataddr_ss); 46155682Smarkm if (getpeername (s, thataddr, &addrlen) < 0) { 46255682Smarkm warn ("getpeername(%s)", hostname); 46355682Smarkm return 1; 46455682Smarkm } 46555682Smarkm 46655682Smarkm if (errsock != -1) { 46755682Smarkm 46855682Smarkm addrlen = sizeof(erraddr_ss); 46955682Smarkm if (getsockname (errsock, erraddr, &addrlen) < 0) { 47055682Smarkm warn ("getsockname"); 47155682Smarkm return 1; 47255682Smarkm } 47355682Smarkm 47455682Smarkm if (listen (errsock, 1) < 0) { 47555682Smarkm warn ("listen"); 47655682Smarkm return 1; 47755682Smarkm } 47855682Smarkm 47955682Smarkm p = buf; 48055682Smarkm snprintf (p, sizeof(buf), "%u", 48155682Smarkm ntohs(socket_get_port(erraddr))); 48255682Smarkm len = strlen(buf) + 1; 48355682Smarkm if(net_write (s, buf, len) != len) { 48455682Smarkm warn ("write"); 48555682Smarkm close (errsock); 48655682Smarkm return 1; 48755682Smarkm } 48855682Smarkm 48972445Sassar 49072445Sassar for (;;) { 49172445Sassar fd_set fdset; 49272445Sassar 49372445Sassar if (errsock >= FD_SETSIZE || s >= FD_SETSIZE) 49472445Sassar errx (1, "fd too large"); 49572445Sassar 49672445Sassar FD_ZERO(&fdset); 49772445Sassar FD_SET(errsock, &fdset); 49872445Sassar FD_SET(s, &fdset); 49972445Sassar 50072445Sassar ret = select (max(errsock, s) + 1, &fdset, NULL, NULL, NULL); 50172445Sassar if (ret < 0) { 50272445Sassar if (errno == EINTR) 50372445Sassar continue; 50472445Sassar warn ("select"); 50572445Sassar close (errsock); 50672445Sassar return 1; 50772445Sassar } 50872445Sassar if (FD_ISSET(errsock, &fdset)) { 50972445Sassar errsock2 = accept (errsock, NULL, NULL); 51072445Sassar close (errsock); 51172445Sassar if (errsock2 < 0) { 51272445Sassar warn ("accept"); 51372445Sassar return 1; 51472445Sassar } 51572445Sassar break; 51672445Sassar } 51772445Sassar 51872445Sassar /* 51972445Sassar * there should not arrive any data on this fd so if it's 52072445Sassar * readable it probably indicates that the other side when 52172445Sassar * away. 52272445Sassar */ 52372445Sassar 52472445Sassar if (FD_ISSET(s, &fdset)) { 52572445Sassar warnx ("socket closed"); 52672445Sassar close (errsock); 52772445Sassar errsock2 = -1; 52872445Sassar break; 52972445Sassar } 53055682Smarkm } 53155682Smarkm } else { 53255682Smarkm if (net_write (s, "0", 2) != 2) { 53355682Smarkm warn ("write"); 53455682Smarkm return 1; 53555682Smarkm } 53655682Smarkm errsock2 = -1; 53755682Smarkm } 53855682Smarkm 53955682Smarkm if ((*auth_func)(s, thisaddr, thataddr, hostname, 54055682Smarkm remote_user, local_user, 54155682Smarkm cmd_len, cmd)) { 54255682Smarkm close (errsock2); 54355682Smarkm return 1; 544233294Sstas } 54555682Smarkm 54655682Smarkm ret = net_read (s, &reply, 1); 54755682Smarkm if (ret < 0) { 54855682Smarkm warn ("read"); 54955682Smarkm close (errsock2); 55055682Smarkm return 1; 55155682Smarkm } else if (ret == 0) { 55255682Smarkm warnx ("unexpected EOF from %s", hostname); 55355682Smarkm close (errsock2); 55455682Smarkm return 1; 55555682Smarkm } 55655682Smarkm if (reply != 0) { 55755682Smarkm 55855682Smarkm warnx ("Error from rshd at %s:", hostname); 55955682Smarkm 56055682Smarkm while ((ret = read (s, buf, sizeof(buf))) > 0) 56155682Smarkm write (STDOUT_FILENO, buf, ret); 56255682Smarkm write (STDOUT_FILENO,"\n",1); 56355682Smarkm close (errsock2); 56455682Smarkm return 1; 56555682Smarkm } 56655682Smarkm 56790926Snectar if (sock_debug) { 56890926Snectar int one = 1; 56990926Snectar if (setsockopt(s, SOL_SOCKET, SO_DEBUG, (void *)&one, sizeof(one)) < 0) 57090926Snectar warn("setsockopt remote"); 57190926Snectar if (errsock2 != -1 && 57290926Snectar setsockopt(errsock2, SOL_SOCKET, SO_DEBUG, 57390926Snectar (void *)&one, sizeof(one)) < 0) 57490926Snectar warn("setsockopt stderr"); 57590926Snectar } 576233294Sstas 577178825Sdfr return rsh_loop (s, errsock2); 57855682Smarkm} 57955682Smarkm 58055682Smarkm/* 58155682Smarkm * Return in `res' a copy of the concatenation of `argc, argv' into 58272445Sassar * malloced space. */ 58355682Smarkm 58455682Smarkmstatic size_t 58555682Smarkmconstruct_command (char **res, int argc, char **argv) 58655682Smarkm{ 58755682Smarkm int i; 58855682Smarkm size_t len = 0; 58955682Smarkm char *tmp; 59055682Smarkm 59155682Smarkm for (i = 0; i < argc; ++i) 59255682Smarkm len += strlen(argv[i]) + 1; 59355682Smarkm len = max (1, len); 59455682Smarkm tmp = malloc (len); 59555682Smarkm if (tmp == NULL) 596178825Sdfr errx (1, "malloc %lu failed", (unsigned long)len); 59755682Smarkm 59855682Smarkm *tmp = '\0'; 59955682Smarkm for (i = 0; i < argc - 1; ++i) { 600178825Sdfr strlcat (tmp, argv[i], len); 601178825Sdfr strlcat (tmp, " ", len); 60255682Smarkm } 60355682Smarkm if (argc > 0) 604178825Sdfr strlcat (tmp, argv[argc-1], len); 60555682Smarkm *res = tmp; 60655682Smarkm return len; 60755682Smarkm} 60855682Smarkm 60955682Smarkmstatic char * 610120945Snectarprint_addr (const struct sockaddr *sa) 61155682Smarkm{ 61255682Smarkm char addr_str[256]; 61355682Smarkm char *res; 614120945Snectar const char *as = NULL; 61555682Smarkm 616120945Snectar if(sa->sa_family == AF_INET) 617233294Sstas as = inet_ntop (sa->sa_family, &((struct sockaddr_in*)sa)->sin_addr, 618120945Snectar addr_str, sizeof(addr_str)); 619120945Snectar#ifdef HAVE_INET6 620120945Snectar else if(sa->sa_family == AF_INET6) 621233294Sstas as = inet_ntop (sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, 622120945Snectar addr_str, sizeof(addr_str)); 623120945Snectar#endif 624120945Snectar if(as == NULL) 625120945Snectar return NULL; 626120945Snectar res = strdup(as); 62755682Smarkm if (res == NULL) 62855682Smarkm errx (1, "malloc: out of memory"); 62955682Smarkm return res; 63055682Smarkm} 63155682Smarkm 63255682Smarkmstatic int 63355682Smarkmdoit_broken (int argc, 63455682Smarkm char **argv, 635120945Snectar int hostindex, 636102644Snectar struct addrinfo *ai, 63755682Smarkm const char *remote_user, 63855682Smarkm const char *local_user, 63955682Smarkm int priv_socket1, 64055682Smarkm int priv_socket2, 64155682Smarkm const char *cmd, 64255682Smarkm size_t cmd_len) 64355682Smarkm{ 644102644Snectar struct addrinfo *a; 64555682Smarkm 64655682Smarkm if (connect (priv_socket1, ai->ai_addr, ai->ai_addrlen) < 0) { 647120945Snectar int save_errno = errno; 648233294Sstas 64955682Smarkm close(priv_socket1); 65055682Smarkm close(priv_socket2); 65155682Smarkm 65255682Smarkm for (a = ai->ai_next; a != NULL; a = a->ai_next) { 65355682Smarkm pid_t pid; 654120945Snectar char *adr = print_addr(a->ai_addr); 655120945Snectar if(adr == NULL) 656120945Snectar continue; 65755682Smarkm 65855682Smarkm pid = fork(); 65955682Smarkm if (pid < 0) 66055682Smarkm err (1, "fork"); 66155682Smarkm else if(pid == 0) { 66255682Smarkm char **new_argv; 66355682Smarkm int i = 0; 66455682Smarkm 66555682Smarkm new_argv = malloc((argc + 2) * sizeof(*new_argv)); 66655682Smarkm if (new_argv == NULL) 66755682Smarkm errx (1, "malloc: out of memory"); 66855682Smarkm new_argv[i] = argv[i]; 66955682Smarkm ++i; 670120945Snectar if (hostindex == i) 671120945Snectar new_argv[i++] = adr; 67255682Smarkm new_argv[i++] = "-K"; 67355682Smarkm for(; i <= argc; ++i) 67455682Smarkm new_argv[i] = argv[i - 1]; 675120945Snectar if (hostindex > 1) 676120945Snectar new_argv[hostindex + 1] = adr; 67755682Smarkm new_argv[argc + 1] = NULL; 67855682Smarkm execv(PATH_RSH, new_argv); 67955682Smarkm err(1, "execv(%s)", PATH_RSH); 68055682Smarkm } else { 68155682Smarkm int status; 682120945Snectar free(adr); 68355682Smarkm 68455682Smarkm while(waitpid(pid, &status, 0) < 0) 68555682Smarkm ; 68655682Smarkm if(WIFEXITED(status) && WEXITSTATUS(status) == 0) 68755682Smarkm return 0; 68855682Smarkm } 68955682Smarkm } 690120945Snectar errno = save_errno; 691120945Snectar warn("%s", argv[hostindex]); 69255682Smarkm return 1; 69355682Smarkm } else { 69455682Smarkm int ret; 69555682Smarkm 69655682Smarkm ret = proto (priv_socket1, priv_socket2, 697120945Snectar argv[hostindex], 69855682Smarkm local_user, remote_user, 69955682Smarkm cmd, cmd_len, 70055682Smarkm send_broken_auth); 70155682Smarkm return ret; 70255682Smarkm } 70355682Smarkm} 70455682Smarkm 705233294Sstas#if defined(KRB5) 70655682Smarkmstatic int 70755682Smarkmdoit (const char *hostname, 708102644Snectar struct addrinfo *ai, 70955682Smarkm const char *remote_user, 71055682Smarkm const char *local_user, 71155682Smarkm const char *cmd, 71255682Smarkm size_t cmd_len, 71355682Smarkm int (*auth_func)(int s, 71455682Smarkm struct sockaddr *this, struct sockaddr *that, 71555682Smarkm const char *hostname, const char *remote_user, 71655682Smarkm const char *local_user, size_t cmd_len, 71755682Smarkm const char *cmd)) 71855682Smarkm{ 71955682Smarkm int error; 720102644Snectar struct addrinfo *a; 72190926Snectar int socketfailed = 1; 72255682Smarkm int ret; 72355682Smarkm 72455682Smarkm for (a = ai; a != NULL; a = a->ai_next) { 72555682Smarkm int s; 72655682Smarkm int errsock; 72755682Smarkm 72855682Smarkm s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 729233294Sstas if (s < 0) 73055682Smarkm continue; 73190926Snectar socketfailed = 0; 73255682Smarkm if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 73390926Snectar char addr[128]; 734233294Sstas if(getnameinfo(a->ai_addr, a->ai_addrlen, 73590926Snectar addr, sizeof(addr), NULL, 0, NI_NUMERICHOST) == 0) 73690926Snectar warn ("connect(%s [%s])", hostname, addr); 73790926Snectar else 73890926Snectar warn ("connect(%s)", hostname); 73955682Smarkm close (s); 74055682Smarkm continue; 74155682Smarkm } 74255682Smarkm if (do_errsock) { 74372445Sassar struct addrinfo *ea, *eai; 74455682Smarkm struct addrinfo hints; 74555682Smarkm 74655682Smarkm memset (&hints, 0, sizeof(hints)); 74755682Smarkm hints.ai_socktype = a->ai_socktype; 74855682Smarkm hints.ai_protocol = a->ai_protocol; 74955682Smarkm hints.ai_family = a->ai_family; 75055682Smarkm hints.ai_flags = AI_PASSIVE; 75155682Smarkm 75272445Sassar errsock = -1; 75372445Sassar 75472445Sassar error = getaddrinfo (NULL, "0", &hints, &eai); 75555682Smarkm if (error) 75655682Smarkm errx (1, "getaddrinfo: %s", gai_strerror(error)); 75772445Sassar for (ea = eai; ea != NULL; ea = ea->ai_next) { 75872445Sassar errsock = socket (ea->ai_family, ea->ai_socktype, 75972445Sassar ea->ai_protocol); 76072445Sassar if (errsock < 0) 76172445Sassar continue; 76272445Sassar if (bind (errsock, ea->ai_addr, ea->ai_addrlen) < 0) 76372445Sassar err (1, "bind"); 76472445Sassar break; 76572445Sassar } 76655682Smarkm if (errsock < 0) 76755682Smarkm err (1, "socket"); 76872445Sassar freeaddrinfo (eai); 76955682Smarkm } else 77055682Smarkm errsock = -1; 771233294Sstas 77255682Smarkm ret = proto (s, errsock, 77355682Smarkm hostname, 77455682Smarkm local_user, remote_user, 77555682Smarkm cmd, cmd_len, auth_func); 77655682Smarkm close (s); 77755682Smarkm return ret; 77855682Smarkm } 77990926Snectar if(socketfailed) 78090926Snectar warnx ("failed to contact %s", hostname); 78155682Smarkm return -1; 78255682Smarkm} 783233294Sstas#endif /* KRB5 */ 78455682Smarkm 78555682Smarkmstruct getargs args[] = { 786102644Snectar#ifdef KRB5 78790926Snectar { "krb5", '5', arg_flag, &use_v5, "Use Kerberos V5" }, 788178825Sdfr { "forward", 'f', arg_flag, &do_forward, "Forward credentials [krb5]"}, 789178825Sdfr { "forwardable", 'F', arg_flag, &do_forwardable, 790178825Sdfr "Forward forwardable credentials [krb5]" }, 791102644Snectar { NULL, 'G', arg_negative_flag,&do_forward, "Don't forward credentials" }, 792178825Sdfr { "unique", 'u', arg_flag, &do_unique_tkfile, 793178825Sdfr "Use unique remote credentials cache [krb5]" }, 794178825Sdfr { "tkfile", 'U', arg_string, &unique_tkfile, 795178825Sdfr "Specifies remote credentials cache [krb5]" }, 796233294Sstas { "protocol", 'P', arg_string, &protocol_version_str, 797178825Sdfr "Protocol version [krb5]", "protocol" }, 798102644Snectar#endif 799178825Sdfr { "broken", 'K', arg_flag, &use_only_broken, "Use only priv port" }, 800233294Sstas#if defined(KRB5) 80190926Snectar { "encrypt", 'x', arg_flag, &do_encrypt, "Encrypt connection" }, 80272445Sassar { NULL, 'z', arg_negative_flag, &do_encrypt, 80355682Smarkm "Don't encrypt connection", NULL }, 804102644Snectar#endif 805102644Snectar { NULL, 'd', arg_flag, &sock_debug, "Enable socket debugging" }, 806102644Snectar { "input", 'n', arg_negative_flag, &input, "Close stdin" }, 80755682Smarkm { "port", 'p', arg_string, &port_str, "Use this port", 808102644Snectar "port" }, 809102644Snectar { "user", 'l', arg_string, &user, "Run as this user", "login" }, 81090926Snectar { "stderr", 'e', arg_negative_flag, &do_errsock, "Don't open stderr"}, 811178825Sdfr#ifdef KRB5 812178825Sdfr#endif 81390926Snectar { "version", 0, arg_flag, &do_version, NULL }, 81490926Snectar { "help", 0, arg_flag, &do_help, NULL } 81555682Smarkm}; 81655682Smarkm 81755682Smarkmstatic void 81855682Smarkmusage (int ret) 81955682Smarkm{ 82055682Smarkm arg_printusage (args, 82155682Smarkm sizeof(args) / sizeof(args[0]), 82255682Smarkm NULL, 823102644Snectar "[login@]host [command]"); 82455682Smarkm exit (ret); 82555682Smarkm} 82655682Smarkm 82755682Smarkm/* 82855682Smarkm * 82955682Smarkm */ 83055682Smarkm 83155682Smarkmint 83255682Smarkmmain(int argc, char **argv) 83355682Smarkm{ 83455682Smarkm int priv_port1, priv_port2; 83555682Smarkm int priv_socket1, priv_socket2; 836120945Snectar int argindex = 0; 837102644Snectar int error; 838102644Snectar struct addrinfo hints, *ai; 83955682Smarkm int ret = 1; 84055682Smarkm char *cmd; 84190926Snectar char *tmp; 84255682Smarkm size_t cmd_len; 84355682Smarkm const char *local_user; 84455682Smarkm char *host = NULL; 84555682Smarkm int host_index = -1; 846102644Snectar#ifdef KRB5 84772445Sassar int status; 848102644Snectar#endif 84972445Sassar uid_t uid; 85055682Smarkm 85155682Smarkm priv_port1 = priv_port2 = IPPORT_RESERVED-1; 85255682Smarkm priv_socket1 = rresvport(&priv_port1); 85355682Smarkm priv_socket2 = rresvport(&priv_port2); 85472445Sassar uid = getuid (); 85572445Sassar if (setuid (uid) || (uid != 0 && setuid(0) == 0)) 85672445Sassar err (1, "setuid"); 857233294Sstas 85878527Sassar setprogname (argv[0]); 85955682Smarkm 86055682Smarkm if (argc >= 2 && argv[1][0] != '-') { 86155682Smarkm host = argv[host_index = 1]; 862120945Snectar argindex = 1; 86355682Smarkm } 864233294Sstas 86572445Sassar if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, 866120945Snectar &argindex)) 86772445Sassar usage (1); 86872445Sassar 86990926Snectar if (do_help) 87090926Snectar usage (0); 87190926Snectar 87290926Snectar if (do_version) { 87390926Snectar print_version (NULL); 87490926Snectar return 0; 87590926Snectar } 876103423Snectar 877178825Sdfr#ifdef KRB5 878103423Snectar if(protocol_version_str != NULL) { 879103423Snectar if(strcasecmp(protocol_version_str, "N") == 0) 880103423Snectar protocol_version = 2; 881103423Snectar else if(strcasecmp(protocol_version_str, "O") == 0) 882103423Snectar protocol_version = 1; 883103423Snectar else { 884103423Snectar char *end; 885103423Snectar int v; 886103423Snectar v = strtol(protocol_version_str, &end, 0); 887103423Snectar if(*end != '\0' || (v != 1 && v != 2)) { 888233294Sstas errx(1, "unknown protocol version \"%s\"", 889103423Snectar protocol_version_str); 890103423Snectar } 891103423Snectar protocol_version = v; 892103423Snectar } 893103423Snectar } 894103423Snectar 895102644Snectar status = krb5_init_context (&context); 896102644Snectar if (status) { 897102644Snectar if(use_v5 == 1) 898102644Snectar errx(1, "krb5_init_context failed: %d", status); 899102644Snectar else 900102644Snectar use_v5 = 0; 901102644Snectar } 90255682Smarkm 903120945Snectar /* request for forwardable on the command line means we should 904120945Snectar also forward */ 905120945Snectar if (do_forwardable == 1) 906102644Snectar do_forward = 1; 907120945Snectar 908102644Snectar#endif 90955682Smarkm 91055682Smarkm if (use_only_broken) { 911102644Snectar#ifdef KRB5 91255682Smarkm use_v5 = 0; 913102644Snectar#endif 91455682Smarkm } 91555682Smarkm 91690926Snectar if(priv_socket1 < 0) { 91790926Snectar if (use_only_broken) 91890926Snectar errx (1, "unable to bind reserved port: is rsh setuid root?"); 91990926Snectar use_broken = 0; 92090926Snectar } 92155682Smarkm 922233294Sstas#if defined(KRB5) 92390926Snectar if (do_encrypt == 1 && use_only_broken) 92490926Snectar errx (1, "encryption not supported with old style authentication"); 925102644Snectar#endif 92690926Snectar 92790926Snectar 928102644Snectar 929102644Snectar#ifdef KRB5 93055682Smarkm if (do_unique_tkfile && unique_tkfile != NULL) 93155682Smarkm errx (1, "Only one of -u and -U allowed."); 93255682Smarkm 93355682Smarkm if (do_unique_tkfile) 934178825Sdfr strlcpy(tkfile,"-u ", sizeof(tkfile)); 93555682Smarkm else if (unique_tkfile != NULL) { 93655682Smarkm if (strchr(unique_tkfile,' ') != NULL) { 93755682Smarkm warnx("Space is not allowed in tkfilename"); 93855682Smarkm usage(1); 93955682Smarkm } 94055682Smarkm do_unique_tkfile = 1; 94155682Smarkm snprintf (tkfile, sizeof(tkfile), "-U %s ", unique_tkfile); 94255682Smarkm } 943102644Snectar#endif 94455682Smarkm 94555682Smarkm if (host == NULL) { 946120945Snectar if (argc - argindex < 1) 94755682Smarkm usage (1); 94855682Smarkm else 949120945Snectar host = argv[host_index = argindex++]; 95055682Smarkm } 951233294Sstas 95290926Snectar if((tmp = strchr(host, '@')) != NULL) { 95390926Snectar *tmp++ = '\0'; 95490926Snectar user = host; 95590926Snectar host = tmp; 95690926Snectar } 95755682Smarkm 958120945Snectar if (argindex == argc) { 95955682Smarkm close (priv_socket1); 96055682Smarkm close (priv_socket2); 96155682Smarkm argv[0] = "rlogin"; 96255682Smarkm execvp ("rlogin", argv); 96355682Smarkm err (1, "execvp rlogin"); 96455682Smarkm } 96555682Smarkm 96655682Smarkm local_user = get_default_username (); 96755682Smarkm if (local_user == NULL) 96855682Smarkm errx (1, "who are you?"); 96955682Smarkm 97055682Smarkm if (user == NULL) 97155682Smarkm user = local_user; 97255682Smarkm 973120945Snectar cmd_len = construct_command(&cmd, argc - argindex, argv + argindex); 974233294Sstas 97555682Smarkm /* 97655682Smarkm * Try all different authentication methods 97755682Smarkm */ 97855682Smarkm 979102644Snectar#ifdef KRB5 98055682Smarkm if (ret && use_v5) { 981102644Snectar memset (&hints, 0, sizeof(hints)); 982102644Snectar hints.ai_socktype = SOCK_STREAM; 983102644Snectar hints.ai_protocol = IPPROTO_TCP; 98455682Smarkm 985102644Snectar if(port_str == NULL) { 986102644Snectar error = getaddrinfo(host, "kshell", &hints, &ai); 987102644Snectar if(error == EAI_NONAME) 988102644Snectar error = getaddrinfo(host, "544", &hints, &ai); 989102644Snectar } else 990102644Snectar error = getaddrinfo(host, port_str, &hints, &ai); 99155682Smarkm 992102644Snectar if(error) 993102644Snectar errx (1, "getaddrinfo: %s", gai_strerror(error)); 994102644Snectar 99555682Smarkm auth_method = AUTH_KRB5; 996103423Snectar again: 997102644Snectar ret = doit (host, ai, user, local_user, cmd, cmd_len, 99855682Smarkm send_krb5_auth); 999233294Sstas if(ret != 0 && sendauth_version_error && 1000103423Snectar protocol_version == 2) { 1001103423Snectar protocol_version = 1; 1002103423Snectar goto again; 1003103423Snectar } 1004102644Snectar freeaddrinfo(ai); 100555682Smarkm } 1006102644Snectar#endif 100755682Smarkm if (ret && use_broken) { 1008102644Snectar memset (&hints, 0, sizeof(hints)); 1009102644Snectar hints.ai_socktype = SOCK_STREAM; 1010102644Snectar hints.ai_protocol = IPPROTO_TCP; 101155682Smarkm 1012102644Snectar if(port_str == NULL) { 1013102644Snectar error = getaddrinfo(host, "shell", &hints, &ai); 1014102644Snectar if(error == EAI_NONAME) 1015102644Snectar error = getaddrinfo(host, "514", &hints, &ai); 1016102644Snectar } else 1017102644Snectar error = getaddrinfo(host, port_str, &hints, &ai); 1018102644Snectar 1019102644Snectar if(error) 1020102644Snectar errx (1, "getaddrinfo: %s", gai_strerror(error)); 1021102644Snectar 102255682Smarkm auth_method = AUTH_BROKEN; 1023102644Snectar ret = doit_broken (argc, argv, host_index, ai, 102455682Smarkm user, local_user, 102555682Smarkm priv_socket1, 102655682Smarkm do_errsock ? priv_socket2 : -1, 102755682Smarkm cmd, cmd_len); 1028102644Snectar freeaddrinfo(ai); 102955682Smarkm } 1030103423Snectar free(cmd); 103155682Smarkm return ret; 103255682Smarkm} 1033