Exemple #1
0
/* VARARGS */
int
main(int argc, char **argv)
{
    int c, rem;
    char *cmd, *cp, **ap, buf[RSH_BUFSIZ], **argv0, *args, *args_no_x;
    char *host = NULL, *user = NULL;
    int cc;
    boolean_t asrsh = B_FALSE;
    struct passwd *pwd;
    boolean_t readfrom_rem;
    boolean_t readfrom_rfd2;
    int one = 1;
    int omask;
    boolean_t nflag = B_FALSE;
    char *krb_realm = NULL;
    krb5_flags authopts;
    krb5_error_code status;
    enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;
    uid_t uid = getuid();

    c = (argc + 1) * sizeof (char *);
    if ((argv0 = malloc(c)) == NULL) {
        perror("malloc");
        return (EXIT_FAILURE);
    }
    (void) memcpy(argv0, argv, c);

    (void) setlocale(LC_ALL, "");

    (void) textdomain(TEXT_DOMAIN);

    /*
     * Determine command name used to invoke to rlogin(1). Users can
     * create links named by a host pointing to the binary and type
     * "hostname" to log into that host afterwards.
     */
    cmd = strrchr(argv[0], '/');
    cmd = (cmd != NULL) ? (cmd + 1) : argv[0];

    /*
     *	Add "remsh" as an alias for "rsh" (System III, V networking
     *	add-ons often used this name for the remote shell since rsh
     *	was already taken for the restricted shell).  Note that this
     *	usurps the ability to use "remsh" as the name of a host (by
     *	symlinking it to rsh), so we go one step farther:  if the
     *	file "/usr/bin/remsh" does not exist, we behave as if "remsh"
     *	is a host name.  If it does exist, we accept "remsh" as an
     *	"rsh" alias.
     */
    if (strcmp(cmd, "remsh") == 0) {
        struct stat sb;

        if (stat("/usr/bin/remsh", &sb) < 0)
            host = cmd;
    } else if (strcmp(cmd, "rsh") != 0) {
        host = cmd;
    }

    /* Handle legacy synopsis "rsh hostname options [command]". */
    if (host == NULL) {
        if (argc < 2)
            usage();
        if (*argv[1] != '-') {
            host = argv[1];
            argc--;
            argv[1] = argv[0];
            argv++;
            asrsh = B_TRUE;
        }
    }

    while ((c = getopt(argc, argv,
                       DEBUGOPTSTRING "8AFKLP:ade:fk:l:nwx")) != -1) {
        switch (c) {
#ifdef DEBUG
        case 'D':
            portnumber = htons(atoi(optarg));
            krb5auth_flag++;
            break;
#endif /* DEBUG */
        case 'F':
            if (fflag)
                usage_forward();
            Fflag = 1;
            krb5auth_flag++;
            fwdable_done = B_TRUE;
            break;
        case 'f':
            if (Fflag)
                usage_forward();
            fflag = 1;
            krb5auth_flag++;
            fwd_done = B_TRUE;
            break;
        case 'P':
            if (strcmp(optarg, "N") == 0)
                kcmd_proto = KCMD_NEW_PROTOCOL;
            else if (strcmp(optarg, "O") == 0)
                kcmd_proto = KCMD_OLD_PROTOCOL;
            else
                die(gettext("rsh: Only -PN or -PO "
                            "allowed.\n"));
            if (rcmdoption_done)
                die(gettext("rsh: Only one of -PN and -PO "
                            "allowed.\n"));
            rcmdoption_done = B_TRUE;
            krb5auth_flag++;
            break;
        case 'a':
            krb5auth_flag++;
            break;
        case 'K':
            no_krb5auth_flag++;
            break;
        case 'd':
            options |= SO_DEBUG;
            break;
        case 'k':
            krb_realm = optarg;
            krb5auth_flag++;
            break;
        case 'l':
            user = optarg;
            break;
        case 'n':
            if (!nflag) {
                if (close(STDIN_FILENO) < 0) {
                    perror("close");
                    return (EXIT_FAILURE);
                }
                /*
                 * "STDION_FILENO" defined to 0 by POSIX
                 * and hence the lowest file descriptor.
                 * So the open(2) below is guaranteed to
                 * reopen it because we closed it above.
                 */
                if (open("/dev/null", O_RDONLY) < 0) {
                    perror("open");
                    return (EXIT_FAILURE);
                }
                nflag = B_TRUE;
            }
            break;
        case 'x':
            encrypt_flag = 1;
            krb5auth_flag++;
            encrypt_done = B_TRUE;
            break;
        /*
         * Ignore the -L, -w, -e and -8 flags to allow aliases with
         * rlogin to work. Actually rlogin(1) doesn't understand
         * -w either but because "rsh -w hostname command" used
         * to work we still accept it.
         */
        case '8':
        case 'L':
        case 'e':
        case 'w':
        /*
         * On the lines of the -L, -w, -e and -8 options above, we
         * ignore the -A option too, in order to allow aliases with
         * rlogin to work.
         *
         * Mind you !, the -a option to trigger Kerberos authentication
         * in rsh, has a totally different usage in rlogin, its the
         * -A option (in rlogin) which needs to be used to talk
         * Kerberos.
         */
        case 'A':
            break;
        default:
            usage();
        }
    }

    argc -= optind;
    argv += optind;

    if (host == NULL) {
        if (argc == 0)
            usage();
        argc--;
        host = *argv++;
        asrsh = B_TRUE;
    }

    if (argc == 0) {
        (void) setreuid(uid, uid);
        if (nflag)
            usage();
        if (asrsh)
            *argv0 = "rlogin";
        (void) execv(rlogin_path, argv0);
        perror(rlogin_path);

        (void) fprintf(stderr, gettext("No local rlogin "
                                       "program found.\n"));
        return (EXIT_FAILURE);
    }

    if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) {
        (void) fprintf(stderr,
                       gettext("Insufficient privileges, "
                               "rsh must be set-uid root\n"));
        return (EXIT_FAILURE);
    }

    pwd = getpwuid(uid);
    if (pwd == NULL) {
        (void) fprintf(stderr, gettext("who are you?\n"));
        return (EXIT_FAILURE);
    }
    if (user == NULL)
        user = pwd->pw_name;

    /*
     * if the user disables krb5 on the cmdline (-K), then skip
     * all krb5 setup.
     *
     * if the user does not disable krb5 or enable krb5 on the
     * cmdline, check krb5.conf to see if it should be enabled.
     */

    if (no_krb5auth_flag) {
        krb5auth_flag = 0;
        Fflag = fflag = encrypt_flag = 0;
    } else if (!krb5auth_flag) {
        /* is autologin set in krb5.conf? */
        status = krb5_init_context(&bsd_context);
        /* don't sweat failure here */
        if (!status) {
            /*
             * note that the call to profile_get_options_boolean
             * with autologin_option can affect value of
             * krb5auth_flag
             */
            (void) profile_get_options_boolean(bsd_context->profile,
                                               appdef,
                                               autologin_option);
        }
    }

    if (krb5auth_flag) {
        if (!bsd_context) {
            status = krb5_init_context(&bsd_context);
            if (status) {
                com_err("rsh", status,
                        "while initializing krb5");
                return (EXIT_FAILURE);

            }
        }

        /*
         * Get our local realm to look up local realm options.
         */
        status = krb5_get_default_realm(bsd_context, &realmdef[1]);
        if (status) {
            com_err("rsh", status,
                    gettext("while getting default realm"));
            return (EXIT_FAILURE);
        }
        /*
         * Check the realms section in krb5.conf for encryption,
         * forward & forwardable info
         */
        profile_get_options_boolean(bsd_context->profile, realmdef,
                                    option);
        /*
         * Check the appdefaults section
         */
        profile_get_options_boolean(bsd_context->profile, appdef,
                                    option);
        profile_get_options_string(bsd_context->profile, appdef,
                                   rcmdversion);
        /*
         * Set the *_flag variables, if the corresponding *_done are
         * set to 1, because we dont want the config file values
         * overriding the command line options.
         */
        if (encrypt_done)
            encrypt_flag = 1;
        if (fwd_done) {
            fflag = 1;
            Fflag = 0;
        } else if (fwdable_done) {
            Fflag = 1;
            fflag = 0;
        }
        if (!rcmdoption_done && (rcmdproto != NULL)) {
            if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
                kcmd_proto = KCMD_NEW_PROTOCOL;
            } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
                kcmd_proto = KCMD_OLD_PROTOCOL;
            } else {
                (void) fprintf(stderr, gettext("Unrecognized "
                                               "KCMD protocol (%s)"), rcmdproto);
                return (EXIT_FAILURE);
            }
        }


        if (encrypt_flag && (!krb5_privacy_allowed())) {
            (void) fprintf(stderr, gettext("rsh: Encryption not "
                                           "supported.\n"));
            return (EXIT_FAILURE);
        }
    }

    /*
     * Connect with the service (shell/kshell) on the daemon side
     */
    if (portnumber == 0) {
        while (!init_service(krb5auth_flag)) {
            /*
             * Connecting to the 'kshell' service failed,
             * fallback to normal rsh; Reset all KRB5 flags
             * and connect to 'shell' service on the server
             */
            krb5auth_flag = 0;
            encrypt_flag = fflag = Fflag = 0;
        }
    }

    cc = encrypt_flag ? strlen(dash_x) : 0;
    for (ap = argv; *ap != NULL; ap++)
        cc += strlen(*ap) + 1;
    cp = args = malloc(cc);
    if (cp == NULL)
        perror("malloc");
    if (encrypt_flag) {
        int length;

        length = strlcpy(args, dash_x, cc);
        cp += length;
        cc -= length;
    }
    args_no_x = args;

    for (ap = argv; *ap != NULL; ap++) {
        int length;

        length = strlcpy(cp, *ap, cc);
        assert(length < cc);
        cp += length;
        cc -= length;
        if (ap[1] != NULL) {
            *cp++ = ' ';
            cc--;
        }
    }

    if (krb5auth_flag) {
        authopts = AP_OPTS_MUTUAL_REQUIRED;
        /*
         * Piggy-back forwarding flags on top of authopts;
         * they will be reset in kcmd
         */
        if (fflag || Fflag)
            authopts |= OPTS_FORWARD_CREDS;
        if (Fflag)
            authopts |= OPTS_FORWARDABLE_CREDS;

        status = kcmd(&rem, &host, portnumber,
                      pwd->pw_name, user,
                      args, &rfd2, "host", krb_realm,
                      bsd_context, &auth_context, &cred,
                      NULL,	/* No need for sequence number */
                      NULL,	/* No need for server seq # */
                      authopts,
                      1,	/* Always set anyport */
                      &kcmd_proto);
        if (status != 0) {
            /*
             * If new protocol requested, we dont fallback to
             * less secure ones.
             */
            if (kcmd_proto == KCMD_NEW_PROTOCOL) {
                (void) fprintf(stderr, gettext("rsh: kcmdv2 "
                                               "to host %s failed - %s\n"
                                               "Fallback to normal rsh denied."),
                               host, error_message(status));
                return (EXIT_FAILURE);
            }
            /* check NO_TKT_FILE or equivalent... */
            if (status != -1) {
                (void) fprintf(stderr,
                               gettext("rsh: kcmd to host %s failed - %s\n"
                                       "trying normal rsh...\n\n"),
                               host, error_message(status));
            } else {
                (void) fprintf(stderr,
                               gettext("trying normal rsh...\n"));
            }
            /*
             * kcmd() failed, so we now fallback to normal rsh,
             * after resetting the KRB5 flags and the 'args' array
             */
            krb5auth_flag = 0;
            encrypt_flag = fflag = Fflag = 0;
            args = args_no_x;
            (void) init_service(B_FALSE);
        } else {
            /*
             * Set up buffers for desread and deswrite.
             */
            desinbuf.data = des_inbuf;
            desoutbuf.data = des_outbuf;
            desinbuf.length = sizeof (des_inbuf);
            desoutbuf.length = sizeof (des_outbuf);

            session_key = &cred->keyblock;

            if (kcmd_proto == KCMD_NEW_PROTOCOL) {
                status = krb5_auth_con_getlocalsubkey(
                             bsd_context,
                             auth_context,
                             &session_key);
                if (status) {
                    com_err("rsh", status,
                            "determining subkey for session");
                    return (EXIT_FAILURE);
                }
                if (session_key == NULL) {
                    com_err("rsh", 0, "no subkey "
                            "negotiated for connection");
                    return (EXIT_FAILURE);
                }
            }

            eblock.crypto_entry = session_key->enctype;
            eblock.key = (krb5_keyblock *)session_key;

            init_encrypt(encrypt_flag, bsd_context, kcmd_proto,
                         &desinbuf, &desoutbuf, CLIENT, &eblock);
            if (encrypt_flag) {
                char *s = gettext("This rsh session is using "
                                  "encryption for all data transmissions.");
                (void) write(STDERR_FILENO, s, strlen(s));
                (void) write(STDERR_FILENO, "\r\n", 2);
            }
        }
    }

    /*
     * Don't merge this with the "if" statement above because
     * "krb5auth_flag" might be set to false inside it.
     */
    if (!krb5auth_flag) {
        rem = rcmd_af(&host, portnumber, pwd->pw_name, user, args,
                      &rfd2, AF_INET6);
        if (rem < 0)
            return (EXIT_FAILURE);
    }
    __priv_relinquish();

    if (rfd2 < 0) {
        (void) fprintf(stderr, gettext("rsh: can't establish "
                                       "stderr\n"));
        return (EXIT_FAILURE);
    }
    if (options & SO_DEBUG) {
        if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, (char *)&one,
                       sizeof (one)) < 0)
            perror("rsh: setsockopt (stdin)");
        if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, (char *)&one,
                       sizeof (one)) < 0)
            perror("rsh: setsockopt (stderr)");
    }
    omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));

    if (sigdisp(SIGINT) != SIG_IGN)
        (void) sigset(SIGINT, sendsig);
    if (sigdisp(SIGQUIT) != SIG_IGN)
        (void) sigset(SIGQUIT, sendsig);
    if (sigdisp(SIGTERM) != SIG_IGN)
        (void) sigset(SIGTERM, sendsig);

    if (nflag) {
        (void) shutdown(rem, SHUT_WR);
    } else {
        child_pid = fork();
        if (child_pid < 0) {
            perror("rsh: fork");
            return (EXIT_FAILURE);
        }

        if (!encrypt_flag) {
            (void) ioctl(rfd2, FIONBIO, &one);
            (void) ioctl(rem, FIONBIO, &one);
        }

        if (child_pid == 0) {
            /* Child */
            fd_set remset;
            char *bp;
            int  wc;
            (void) close(rfd2);
reread:
            errno = 0;
            cc = read(0, buf, sizeof (buf));
            if (cc <= 0)
                goto done;
            bp = buf;
rewrite:
            FD_ZERO(&remset);
            FD_SET(rem, &remset);
            if (select(rem + 1, NULL, &remset, NULL, NULL) < 0) {
                if (errno != EINTR) {
                    perror("rsh: select");
                    return (EXIT_FAILURE);
                }
                goto rewrite;
            }
            if (!FD_ISSET(rem, &remset))
                goto rewrite;
            writeiv = B_FALSE;
            wc = desrshwrite(rem, bp, cc);
            if (wc < 0) {
                if (errno == EWOULDBLOCK)
                    goto rewrite;
                goto done;
            }
            cc -= wc;
            bp += wc;
            if (cc == 0)
                goto reread;
            goto rewrite;
done:
            (void) shutdown(rem, SHUT_WR);
            return (EXIT_SUCCESS);
        }
    }

#define	MAX(a, b)	(((a) > (b)) ? (a) : (b))

    sigsetmask(omask);
    readfrom_rem = B_TRUE;
    readfrom_rfd2 = B_TRUE;
    (void) sigset(SIGPIPE, sigpipehandler);
    do {
        fd_set readyset;

        FD_ZERO(&readyset);
        if (readfrom_rem)
            FD_SET(rem, &readyset);
        if (readfrom_rfd2)
            FD_SET(rfd2, &readyset);
        if (select(MAX(rem, rfd2) + 1, &readyset, NULL, NULL,
                   NULL) < 0) {
            if (errno != EINTR) {
                perror("rsh: select");
                return (EXIT_FAILURE);
            }
            continue;
        }
        if (FD_ISSET(rfd2, &readyset)) {
            errno = 0;
            readiv = B_TRUE;
            cc = desrshread(rfd2, buf, sizeof (buf));
            if (cc <= 0) {
                if (errno != EWOULDBLOCK)
                    readfrom_rfd2 = B_FALSE;
            } else {
                (void) write(STDERR_FILENO, buf, cc);
            }
        }
        if (FD_ISSET(rem, &readyset)) {
            errno = 0;
            readiv = B_FALSE;
            cc = desrshread(rem, buf, sizeof (buf));
            if (cc <= 0) {
                if (errno != EWOULDBLOCK)
                    readfrom_rem = B_FALSE;
            } else
                (void) write(STDOUT_FILENO, buf, cc);
        }
    } while (readfrom_rem || readfrom_rfd2);

    if (!nflag)
        (void) kill(child_pid, SIGKILL);
    return (EXIT_SUCCESS);
}
Exemple #2
0
int
main(int argc, char **argv)
{
	int c;
	char *cp, *cmd, *name = NULL;
	struct passwd *pwd;
	uid_t uid;
	int options = 0, oldmask;
	int on = 1;
	speed_t speed = 0;
	int getattr_ret;
	char *tmp;
	int sock;
	krb5_flags authopts;
	krb5_error_code status;
	enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;

	(void) setlocale(LC_ALL, "");

#if !defined(TEXT_DOMAIN)
#define	TEXT_DOMAIN "SYS_TEST"
#endif
	(void) textdomain(TEXT_DOMAIN);

	if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) {
		(void) fprintf(stderr,
		    gettext("Insufficient privileges, "
			"rlogin must be set-uid root\n"));
		exit(1);
	}

	{
		int it;

		if ((getattr_ret = tcgetattr(STDIN_FILENO, &savetty)) < 0)
			perror("tcgetattr");
		it = ioctl(STDIN_FILENO, I_FIND, "ttcompat");
		if (it < 0) {
			perror("ioctl I_FIND ttcompat");
			return (EXIT_FAILURE);
		}
		if (it == 0) {
			if (ioctl(STDIN_FILENO, I_PUSH, "ttcompat") < 0) {
				perror("ioctl I_PUSH ttcompat");
				exit(EXIT_FAILURE);
			}
			ttcompat = B_TRUE;
		}
	}

	/*
	 * Determine command name used to invoke to rlogin(1). Users can
	 * create links named by a host pointing to the binary and type
	 * "hostname" to log into that host afterwards.
	 */
	cmd = strrchr(argv[0], '/');
	cmd = (cmd != NULL) ? (cmd + 1) : argv[0];

	if (strcmp(cmd, rlogin) == 0) {
		if (argc < 2)
			usage();
		if (*argv[1] != '-') {
			host = argv[1];
			argc--;
			argv[1] = argv[0];
			argv++;
		}
	} else {
		host = cmd;
	}

	while ((c = getopt(argc, argv,
	    DEBUGOPTSTRING "8AEFLP:ade:fk:l:x")) != -1) {
		switch (c) {
		case '8':
			eight = B_TRUE;
			break;
		case 'A':
			krb5auth_flag = B_TRUE;
			break;
#ifdef DEBUG
		case 'D':
			portnumber = htons(atoi(optarg));
			krb5auth_flag = B_TRUE;
			break;
#endif /* DEBUG */
		case 'E':
			nocmdchar = B_TRUE;
			break;
		case 'F':
			if (fflag)
				usage_forward();
			Fflag = 1;
			krb5auth_flag = B_TRUE;
			fwdable_done = B_TRUE;
			break;
		case 'f':
			if (Fflag)
				usage_forward();
			fflag = 1;
			krb5auth_flag = B_TRUE;
			fwd_done = B_TRUE;
			break;
		case 'L':
			litout = B_TRUE;
			break;
		case 'P':
			if (strcmp(optarg, "N") == 0)
				kcmd_proto = KCMD_NEW_PROTOCOL;
			else if (strcmp(optarg, "O") == 0)
				kcmd_proto = KCMD_OLD_PROTOCOL;
			else
				die(gettext("rlogin: Only -PN or -PO "
				    "allowed.\n"));
			if (rcmdoption_done)
				die(gettext("rlogin: Only one of -PN and -PO "
				    "allowed.\n"));
			rcmdoption_done = B_TRUE;
			krb5auth_flag = B_TRUE;
			break;
		case 'a':
		/*
		 * Force the remote host to prompt for a password by sending
		 * a NULL username. This option is mutually exclusive with
		 * the -A, -x, -f, -F, -k <realm> options.
		 */
			null_local_username = B_TRUE;
			break;
		case 'd':
			options |= SO_DEBUG;
			break;
		case 'e': {
			int c;

			cp = optarg;

			if ((c = *cp) != '\\') {
				cmdchar = c;
			} else {
				c = cp[1];
				if (c == '\0' || c == '\\') {
					cmdchar = '\\';
				} else if (c >= '0' && c <= '7') {
					long lc;

					lc = strtol(&cp[1], NULL, 8);
					if (lc < 0 || lc > 255)
						die(gettext("rlogin: octal "
						    "escape character %s too "
						    "large.\n"), cp);
					cmdchar = (char)lc;
				} else {
					die(gettext("rlogin: unrecognized "
					    "escape character option %s.\n"),
					    cp);
				}
			}
			break;
		}
		case 'k':
			krb_realm = optarg;
			krb5auth_flag = B_TRUE;
			break;
		case 'l':
			name = optarg;
			break;
		case 'x':
			encrypt_flag = 1;
			krb5auth_flag = B_TRUE;
			encrypt_done = B_TRUE;
			break;
		default:
			usage();
		}
	}

	argc -= optind;
	argv += optind;

	if (host == NULL) {
		if (argc == 0)
			usage();
		argc--;
		host = *argv++;
	}

	if (argc > 0)
		usage();

	pwd = getpwuid(uid = getuid());
	if (pwd == NULL) {
		(void) fprintf(stderr, gettext("getpwuid(): can not find "
			"password entry for user id %d."), uid);
		return (EXIT_FAILURE);
	}
	if (name == NULL)
		name = pwd->pw_name;

	/*
	 * If the `-a' option is issued on the cmd line, we reset all
	 * flags associated with other KRB5 specific options, since
	 * the -a option is mutually exclusive with the rest.
	 */
	if (null_local_username) {
		krb5auth_flag = B_FALSE;
		fflag = Fflag = encrypt_flag = 0;
		(void) fprintf(stderr, gettext("Note: The -a option nullifies "
					"all other Kerberos-specific\noptions "
					"you may have used.\n"));
	}

	if (krb5auth_flag) {
		status = krb5_init_context(&bsd_context);
		if (status) {
			com_err(rlogin, status, gettext("while initializing"
					" krb5"));
			return (EXIT_FAILURE);
		}
		/*
		 * Set up buffers for desread and deswrite.
		 */
		desinbuf.data = des_inbuf;
		desoutbuf.data = des_outbuf;
		desinbuf.length = sizeof (des_inbuf);
		desoutbuf.length = sizeof (des_outbuf);

		/*
		 * Get our local realm to look up local realm options.
		 */
		status = krb5_get_default_realm(bsd_context, &realmdef[1]);
		if (status) {
			com_err(rlogin, status,
				gettext("while getting default realm"));
			return (EXIT_FAILURE);
		}
		/*
		 * Check the realms section in krb5.conf for encryption,
		 * forward & forwardable info
		 */
		profile_get_options_boolean(bsd_context->profile, realmdef,
						option);
		/*
		 * Check the appdefaults section
		 */
		profile_get_options_boolean(bsd_context->profile, appdef,
						option);
		profile_get_options_string(bsd_context->profile, appdef,
						rcmdversion);

		/*
		 * Set the *_flag variables, if the corresponding *_done are
		 * set to 1, because we dont want the config file values
		 * overriding the command line options.
		 */
		if (encrypt_done)
			encrypt_flag = 1;
		if (fwd_done) {
			fflag = 1;
			Fflag = 0;
		} else if (fwdable_done) {
			Fflag = 1;
			fflag = 0;
		}
		if (!rcmdoption_done && (rcmdproto != NULL)) {
			if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
				kcmd_proto = KCMD_NEW_PROTOCOL;
			} else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
				kcmd_proto = KCMD_OLD_PROTOCOL;
			} else {
				(void) fprintf(stderr, gettext("Unrecognized "
					"KCMD protocol (%s)"), rcmdproto);
				return (EXIT_FAILURE);
			}
		}

		if (encrypt_flag && (!krb5_privacy_allowed())) {
			(void) fprintf(stderr, gettext("rlogin: "******"Encryption not supported.\n"));
			return (EXIT_FAILURE);
		}
	}

	if (port_number == 0) {
		if (krb5auth_flag) {
			struct servent *sp;

			/*
			 * If the krb5auth_flag is set (via -A, -f, -F, -k) &
			 * if there is an entry in /etc/services for Kerberos
			 * login, attempt to login with Kerberos. If we fail
			 * at any step,  use the standard rlogin
			 */
			sp = getservbyname(encrypt_flag ?
			    "eklogin" : "klogin", "tcp");
			if (sp == NULL) {
				port_number = encrypt_flag ?
				    htons(2105) : htons(543);
			} else {
				port_number = sp->s_port;
			}
		} else {
			port_number = htons(IPPORT_LOGINSERVER);
		}
	}

	cp = getenv("TERM");
	if (cp) {
		(void) strncpy(term, cp, sizeof (term));
		term[sizeof (term) - 1] = '\0';
	}
	if (getattr_ret == 0) {
		speed = cfgetospeed(&savetty);
		/*
		 * "Be conservative in what we send" -- Only send baud rates
		 * which at least all 4.x BSD derivatives are known to handle
		 * correctly.
		 * NOTE:  This code assumes new termios speed values will
		 * be "higher" speeds.
		 */
		if (speed > B38400)
			speed = B38400;
	}

	/*
	 * Only put the terminal speed info in if we have room
	 * so we don't overflow the buffer, and only if we have
	 * a speed we recognize.
	 */
	if (speed > 0 && speed < sizeof (speeds)/sizeof (char *) &&
	    strlen(term) + strlen("/") + strlen(speeds[speed]) + 1 <
	    sizeof (term)) {
		(void) strcat(term, "/");
		(void) strcat(term, speeds[speed]);
	}
	(void) sigset(SIGPIPE, (sigdisp_t)lostpeer);
	/* will use SIGUSR1 for window size hack, so hold it off */
	oldmask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));

	/*
	 * Determine if v4 literal address and if so store it to one
	 * side. This is to correct the undesired behaviour of rcmd_af
	 * which converts a passed in v4 literal address to a v4 mapped
	 * v6 literal address. If it was a v4 literal we then re-assign
	 * it to host.
	 */
	tmp = NULL;
	if (inet_addr(host) != (in_addr_t)-1)
		tmp = host;

	if (krb5auth_flag) {
		authopts = AP_OPTS_MUTUAL_REQUIRED;

		/* Piggy-back forwarding flags on top of authopts; */
		/* they will be reset in kcmd */
		if (fflag || Fflag)
			authopts |= OPTS_FORWARD_CREDS;
		if (Fflag)
			authopts |= OPTS_FORWARDABLE_CREDS;

		status = kcmd(&sock, &host, port_number,
			null_local_username ? "" : pwd->pw_name,
			name, term, NULL,
			"host", krb_realm, bsd_context, &auth_context,
			&cred,
			NULL,		/* No need for sequence number */
			NULL,		/* No need for server seq # */
			authopts,
			0,		/* Not any port # */
			&kcmd_proto);

		if (status != 0) {
			/*
			 * If new protocol requested, we dont fallback to
			 * less secure ones.
			 */
			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
				(void) fprintf(stderr, gettext("rlogin: kcmdv2 "
					"to host %s failed - %s\n"
					"Fallback to normal rlogin denied."),
					host, error_message(status));
				return (EXIT_FAILURE);
			}
			if (status != -1) {
				(void) fprintf(stderr, gettext("rlogin: kcmd "
						"to host %s failed - %s,\n"
						"trying normal rlogin...\n\n"),
						host, error_message(status));
			} else {
				(void) fprintf(stderr,
					gettext("trying normal rlogin...\n"));
			}
			/*
			 * kcmd() failed, so we have to
			 * fallback to normal rlogin
			 */
			port_number = htons(IPPORT_LOGINSERVER);
			krb5auth_flag = B_FALSE;
			fflag = Fflag = encrypt_flag = 0;
			null_local_username = B_FALSE;
		} else {
			(void) fprintf(stderr,
			    gettext("connected with Kerberos V5\n"));

			/*
			 * Setup eblock for desread and deswrite.
			 */
			session_key = &cred->keyblock;

			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
				status = krb5_auth_con_getlocalsubkey(
				    bsd_context,
				    auth_context,
				    &session_key);
				if (status) {
					com_err(rlogin, status,
					    "determining subkey for session");
					return (EXIT_FAILURE);
				}
				if (session_key == NULL) {
					com_err(rlogin, 0,
					    "no subkey negotiated for "
					    "connection");
					return (EXIT_FAILURE);
				}
			}

			eblock.crypto_entry = session_key->enctype;
			eblock.key = (krb5_keyblock *)session_key;

			init_encrypt(encrypt_flag, bsd_context, kcmd_proto,
			    &desinbuf, &desoutbuf, CLIENT, &eblock);

			rem = sock;
			if (rem < 0)
				pop(EXIT_FAILURE);
		}
	}

	/*
	 * Don't merge this with the "if" statement above because
	 * "krb5auth_flag" might be set to false inside it.
	 */
	if (!krb5auth_flag) {
		rem = rcmd_af(&host, port_number,
			null_local_username ? "" : pwd->pw_name,
			name, term, NULL, AF_INET6);
		if (rem < 0)
			pop(EXIT_FAILURE);
	}

	/* Never need our privilege again */
	__priv_relinquish();

	if (tmp != NULL)
		host = tmp;

	if (options & SO_DEBUG &&
	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, (char *)&on,
			    sizeof (on)) < 0)
		perror("rlogin: setsockopt (SO_DEBUG)");

	{
		int bufsize = 8192;

		(void) setsockopt(rem, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize,
			sizeof (int));
	}

	doit(oldmask);
	return (0);
}