+static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
+{
+ static unsigned long remote_winsize;
+ static unsigned long remote_maxpkt;
+
+ crBegin;
+
+ /*
+ * Request userauth protocol, and await a response to it.
+ */
+ ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);
+ ssh2_pkt_addstring("ssh-userauth");
+ ssh2_pkt_send();
+ crWaitUntilV(ispkt);
+ if (pktin.type != SSH2_MSG_SERVICE_ACCEPT) {
+ bombout(("Server refused user authentication protocol"));
+ crReturnV;
+ }
+
+ /*
+ * FIXME: currently we support only password authentication.
+ * (This places us technically in violation of the SSH2 spec.
+ * We must fix this.)
+ */
+ while (1) {
+ /*
+ * Get a username and a password.
+ */
+ static char username[100];
+ static char password[100];
+ static int pos = 0;
+ static char c;
+
+ if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
+ c_write("login as: ", 10);
+ ssh_send_ok = 1;
+ while (pos >= 0) {
+ crWaitUntilV(!ispkt);
+ while (inlen--) switch (c = *in++) {
+ case 10: case 13:
+ username[pos] = 0;
+ pos = -1;
+ break;
+ case 8: case 127:
+ if (pos > 0) {
+ c_write("\b \b", 3);
+ pos--;
+ }
+ break;
+ case 21: case 27:
+ while (pos > 0) {
+ c_write("\b \b", 3);
+ pos--;
+ }
+ break;
+ case 3: case 4:
+ random_save_seed();
+ exit(0);
+ break;
+ default:
+ if (((c >= ' ' && c <= '~') ||
+ ((unsigned char)c >= 160)) && pos < 40) {
+ username[pos++] = c;
+ c_write(&c, 1);
+ }
+ break;
+ }
+ }
+ c_write("\r\n", 2);
+ username[strcspn(username, "\n\r")] = '\0';
+ } else {
+ char stuff[200];
+ strncpy(username, cfg.username, 99);
+ username[99] = '\0';
+ if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
+ sprintf(stuff, "Using username \"%s\".\r\n", username);
+ c_write(stuff, strlen(stuff));
+ }
+ }
+
+ if (ssh_get_password) {
+ char prompt[200];
+ sprintf(prompt, "%.90s@%.90s's password: ", username, savedhost);
+ if (!ssh_get_password(prompt, password, sizeof(password))) {
+ /*
+ * get_password failed to get a password (for
+ * example because one was supplied on the command
+ * line which has already failed to work).
+ * Terminate.
+ */
+ logevent("No more passwords to try");
+ ssh_state = SSH_STATE_CLOSED;
+ crReturnV;
+ }
+ } else {
+ c_write("password: ", 10);
+ ssh_send_ok = 1;
+
+ pos = 0;
+ while (pos >= 0) {
+ crWaitUntilV(!ispkt);
+ while (inlen--) switch (c = *in++) {
+ case 10: case 13:
+ password[pos] = 0;
+ pos = -1;
+ break;
+ case 8: case 127:
+ if (pos > 0)
+ pos--;
+ break;
+ case 21: case 27:
+ pos = 0;
+ break;
+ case 3: case 4:
+ random_save_seed();
+ exit(0);
+ break;
+ default:
+ if (((c >= ' ' && c <= '~') ||
+ ((unsigned char)c >= 160)) && pos < 40)
+ password[pos++] = c;
+ break;
+ }
+ }
+ c_write("\r\n", 2);
+ }
+
+ ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ ssh2_pkt_addstring(username);
+ ssh2_pkt_addstring("ssh-connection"); /* service requested */
+ ssh2_pkt_addstring("password");
+ ssh2_pkt_addbool(FALSE);
+ ssh2_pkt_addstring(password);
+ ssh2_pkt_send();
+
+ crWaitUntilV(ispkt);
+ if (pktin.type != SSH2_MSG_USERAUTH_SUCCESS) {
+ c_write("Access denied\r\n", 15);
+ logevent("Authentication refused");
+ } else
+ break;
+ }
+
+ /*
+ * Now we're authenticated for the connection protocol. The
+ * connection protocol will automatically have started at this
+ * point; there's no need to send SERVICE_REQUEST.
+ */
+
+ /*
+ * So now create a channel with a session in it.
+ */
+ mainchan = malloc(sizeof(struct ssh_channel));
+ mainchan->localid = 100; /* as good as any */
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
+ ssh2_pkt_addstring("session");
+ ssh2_pkt_adduint32(mainchan->localid);
+ ssh2_pkt_adduint32(0x8000UL); /* our window size */
+ ssh2_pkt_adduint32(0x4000UL); /* our max pkt size */
+ ssh2_pkt_send();
+ crWaitUntilV(ispkt);
+ if (pktin.type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
+ bombout(("Server refused to open a session"));
+ crReturnV;
+ /* FIXME: error data comes back in FAILURE packet */
+ }
+ if (ssh2_pkt_getuint32() != mainchan->localid) {
+ bombout(("Server's channel confirmation cited wrong channel"));
+ crReturnV;
+ }
+ mainchan->remoteid = ssh2_pkt_getuint32();
+ mainchan->u.v2.remwindow = ssh2_pkt_getuint32();
+ mainchan->u.v2.remmaxpkt = ssh2_pkt_getuint32();
+ mainchan->u.v2.outbuffer = NULL;
+ mainchan->u.v2.outbuflen = mainchan->u.v2.outbufsize = 0;
+ logevent("Opened channel for session");
+
+ /*
+ * Now allocate a pty for the session.
+ */
+ if (!cfg.nopty) {
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+ ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
+ ssh2_pkt_addstring("pty-req");
+ ssh2_pkt_addbool(1); /* want reply */
+ ssh2_pkt_addstring(cfg.termtype);
+ ssh2_pkt_adduint32(cols);
+ ssh2_pkt_adduint32(rows);
+ ssh2_pkt_adduint32(0); /* pixel width */
+ ssh2_pkt_adduint32(0); /* pixel height */
+ ssh2_pkt_addstring_start();
+ ssh2_pkt_addstring_data("\0", 1);/* TTY_OP_END, no special options */
+ ssh2_pkt_send();
+ ssh_state = SSH_STATE_INTERMED;
+
+ do {
+ crWaitUntilV(ispkt);
+ if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
+ continue; /* wrong channel */
+ mainchan->u.v2.remwindow += ssh2_pkt_getuint32();
+ }
+ } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+
+ if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
+ if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
+ bombout(("Server got confused by pty request"));
+ crReturnV;
+ }
+ c_write("Server refused to allocate pty\r\n", 32);
+ } else {
+ logevent("Allocated pty");
+ }
+ }
+
+ /*
+ * Start a shell or a remote command.
+ */
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+ ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
+ if (*cfg.remote_cmd) {
+ ssh2_pkt_addstring("exec");
+ ssh2_pkt_addbool(1); /* want reply */
+ ssh2_pkt_addstring(cfg.remote_cmd);
+ } else {
+ ssh2_pkt_addstring("shell");
+ ssh2_pkt_addbool(1); /* want reply */
+ }
+ ssh2_pkt_send();
+ do {
+ crWaitUntilV(ispkt);
+ if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
+ continue; /* wrong channel */
+ mainchan->u.v2.remwindow += ssh2_pkt_getuint32();
+ }
+ } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+ if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
+ if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
+ bombout(("Server got confused by shell/command request"));
+ crReturnV;
+ }
+ bombout(("Server refused to start a shell/command"));
+ crReturnV;
+ } else {
+ logevent("Started a shell/command");
+ }
+
+ ssh_state = SSH_STATE_SESSION;
+ if (size_needed)
+ ssh_size();
+
+ /*
+ * Transfer data!
+ */
+ ssh_send_ok = 1;
+ begin_session();
+ while (1) {
+ static int try_send;
+ crReturnV;
+ try_send = FALSE;
+ if (ispkt) {
+ if (pktin.type == SSH2_MSG_CHANNEL_DATA ||
+ pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
+ char *data;
+ int length;
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
+ continue; /* wrong channel */
+ if (pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&
+ ssh2_pkt_getuint32() != SSH2_EXTENDED_DATA_STDERR)
+ continue; /* extended but not stderr */
+ ssh2_pkt_getstring(&data, &length);
+ if (data) {
+ from_backend(pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA,
+ data, length);
+ /*
+ * Enlarge the window again at the remote side,
+ * just in case it ever runs down and they fail
+ * to send us any more data.
+ */
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+ ssh2_pkt_adduint32(mainchan->remoteid);
+ ssh2_pkt_adduint32(length);
+ ssh2_pkt_send();
+ }
+ } else if (pktin.type == SSH2_MSG_DISCONNECT) {
+ ssh_state = SSH_STATE_CLOSED;
+ logevent("Received disconnect message");
+ crReturnV;
+ } else if (pktin.type == SSH2_MSG_CHANNEL_REQUEST) {
+ continue; /* exit status et al; ignore (FIXME?) */
+ } else if (pktin.type == SSH2_MSG_CHANNEL_EOF) {
+ continue; /* remote sends EOF; ignore */
+ } else if (pktin.type == SSH2_MSG_CHANNEL_CLOSE) {
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
+ continue; /* wrong channel */
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(mainchan->remoteid);
+ ssh2_pkt_send();
+ /* FIXME: mark the channel as closed */
+ if (1 /* FIXME: "all channels are closed" */) {
+ logevent("All channels closed. Disconnecting");
+ ssh2_pkt_init(SSH2_MSG_DISCONNECT);
+ ssh2_pkt_adduint32(SSH2_DISCONNECT_BY_APPLICATION);
+ ssh2_pkt_addstring("All open channels closed");
+ ssh2_pkt_addstring("en"); /* language tag */
+ ssh2_pkt_send();
+ ssh_state = SSH_STATE_CLOSED;
+ crReturnV;
+ }
+ continue; /* remote sends close; ignore (FIXME) */
+ } else if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
+ continue; /* wrong channel */
+ mainchan->u.v2.remwindow += ssh2_pkt_getuint32();
+ try_send = TRUE;
+ } else {
+ bombout(("Strange packet received: type %d", pktin.type));
+ crReturnV;
+ }
+ } else {
+ /*
+ * We have spare data. Add it to the channel buffer.
+ */
+ if (mainchan->u.v2.outbufsize <
+ mainchan->u.v2.outbuflen + inlen) {
+ mainchan->u.v2.outbufsize =
+ mainchan->u.v2.outbuflen + inlen + 1024;
+ mainchan->u.v2.outbuffer = srealloc(mainchan->u.v2.outbuffer,
+ mainchan->u.v2.outbufsize);
+ }
+ memcpy(mainchan->u.v2.outbuffer + mainchan->u.v2.outbuflen,
+ in, inlen);
+ mainchan->u.v2.outbuflen += inlen;
+ try_send = TRUE;