/* * Checks if the user has an authentication agent, and if so, tries to * authenticate using the agent. */ static int try_agent_authentication(struct ssh *ssh) { int r, type, agent_fd, ret = 0; u_char response[16]; size_t i; BIGNUM *challenge; struct ssh_identitylist *idlist = NULL; /* Get connection to the agent. */ if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) { if (r != SSH_ERR_AGENT_NOT_PRESENT) debug("%s: ssh_get_authentication_socket: %s", __func__, ssh_err(r)); return 0; } if ((challenge = BN_new()) == NULL) fatal("try_agent_authentication: BN_new failed"); /* Loop through identities served by the agent. */ if ((r = ssh_fetch_identitylist(agent_fd, 1, &idlist)) != 0) { if (r != SSH_ERR_AGENT_NO_IDENTITIES) debug("%s: ssh_fetch_identitylist: %s", __func__, ssh_err(r)); goto out; } for (i = 0; i < idlist->nkeys; i++) { /* Try this identity. */ debug("Trying RSA authentication via agent with '%.100s'", idlist->comments[i]); /* * Tell the server that we are willing to authenticate * using this key. */ if ((r = sshpkt_start(ssh, SSH_CMSG_AUTH_RSA)) != 0 || (r = sshpkt_put_bignum1(ssh, idlist->keys[i]->rsa->n)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); ssh_packet_write_wait(ssh); /* Wait for server's response. */ type = ssh_packet_read(ssh); /* The server sends failure if it doesn't like our key or does not support RSA authentication. */ if (type == SSH_SMSG_FAILURE) { debug("Server refused our key."); continue; } /* Otherwise it should have sent a challenge. */ if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) ssh_packet_disconnect(ssh, "Protocol error during RSA " "authentication: %d", type); if ((r = sshpkt_get_bignum1(ssh, challenge)) != 0 || (r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); debug("Received RSA challenge from server."); /* Ask the agent to decrypt the challenge. */ if ((r = ssh_decrypt_challenge(agent_fd, idlist->keys[i], challenge, session_id, response)) != 0) { /* * The agent failed to authenticate this identifier * although it advertised it supports this. Just * return a wrong value. */ logit("Authentication agent failed to decrypt " "challenge: %s", ssh_err(r)); memset(response, 0, sizeof(response)); } debug("Sending response to RSA challenge."); /* Send the decrypted challenge back to the server. */ if ((r = sshpkt_start(ssh, SSH_CMSG_AUTH_RSA_RESPONSE)) != 0 || (r = sshpkt_put(ssh, &response, sizeof(response))) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); ssh_packet_write_wait(ssh); /* Wait for response from the server. */ type = ssh_packet_read(ssh); /* * The server returns success if it accepted the * authentication. */ if (type == SSH_SMSG_SUCCESS) { debug("RSA authentication accepted by server."); ret = 1; break; } else if (type != SSH_SMSG_FAILURE) ssh_packet_disconnect(ssh, "Protocol error waiting RSA auth " "response: %d", type); } if (ret != 1) debug("RSA authentication using agent refused."); out: ssh_free_identitylist(idlist); ssh_close_authentication_socket(agent_fd); BN_clear_free(challenge); return ret; }
/* * Performs the interactive session. This handles data transmission between * the client and the program. Note that the notion of stdin, stdout, and * stderr in this function is sort of reversed: this function writes to * stdin (of the child program), and reads from stdout and stderr (of the * child program). */ void server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) { struct ssh *ssh = active_state; /* XXX */ fd_set *readset = NULL, *writeset = NULL; int max_fd = 0; u_int nalloc = 0; int wait_status; /* Status returned by wait(). */ pid_t wait_pid; /* pid returned by wait(). */ int waiting_termination = 0; /* Have displayed waiting close message. */ u_int64_t max_time_milliseconds; u_int previous_stdout_buffer_bytes; u_int stdout_buffer_bytes; int r, type; debug("Entering interactive session."); /* Initialize the SIGCHLD kludge. */ child_terminated = 0; signal(SIGCHLD, sigchld_handler); if (!use_privsep) { signal(SIGTERM, sigterm_handler); signal(SIGINT, sigterm_handler); signal(SIGQUIT, sigterm_handler); } /* Initialize our global variables. */ fdin = fdin_arg; fdout = fdout_arg; fderr = fderr_arg; /* nonblocking IO */ set_nonblock(fdin); set_nonblock(fdout); /* we don't have stderr for interactive terminal sessions, see below */ if (fderr != -1) set_nonblock(fderr); if (!(ssh->compat & SSH_BUG_IGNOREMSG) && isatty(fdin)) fdin_is_tty = 1; connection_in = ssh_packet_get_connection_in(ssh); connection_out = ssh_packet_get_connection_out(ssh); notify_setup(); previous_stdout_buffer_bytes = 0; /* Set approximate I/O buffer size. */ if (ssh_packet_is_interactive(ssh)) buffer_high = 4096; else buffer_high = 64 * 1024; #if 0 /* Initialize max_fd to the maximum of the known file descriptors. */ max_fd = MAX(connection_in, connection_out); max_fd = MAX(max_fd, fdin); max_fd = MAX(max_fd, fdout); if (fderr != -1) max_fd = MAX(max_fd, fderr); #endif /* Initialize Initialize buffers. */ if ((stdin_buffer = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((stdout_buffer = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((stderr_buffer = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); /* * If we have no separate fderr (which is the case when we have a pty * - there we cannot make difference between data sent to stdout and * stderr), indicate that we have seen an EOF from stderr. This way * we don't need to check the descriptor everywhere. */ if (fderr == -1) fderr_eof = 1; server_init_dispatch(ssh); /* Main loop of the server for the interactive session mode. */ for (;;) { /* Process buffered packets from the client. */ process_buffered_input_packets(ssh); /* * If we have received eof, and there is no more pending * input data, cause a real eof by closing fdin. */ if (stdin_eof && fdin != -1 && sshbuf_len(stdin_buffer) == 0) { if (fdin != fdout) close(fdin); else shutdown(fdin, SHUT_WR); /* We will no longer send. */ fdin = -1; } /* Make packets from buffered stderr data to send to the client. */ make_packets_from_stderr_data(ssh); /* * Make packets from buffered stdout data to send to the * client. If there is very little to send, this arranges to * not send them now, but to wait a short while to see if we * are getting more data. This is necessary, as some systems * wake up readers from a pty after each separate character. */ max_time_milliseconds = 0; stdout_buffer_bytes = sshbuf_len(stdout_buffer); if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 && stdout_buffer_bytes != previous_stdout_buffer_bytes) { /* try again after a while */ max_time_milliseconds = 10; } else { /* Send it now. */ make_packets_from_stdout_data(ssh); } previous_stdout_buffer_bytes = sshbuf_len(stdout_buffer); /* Send channel data to the client. */ if (ssh_packet_not_very_much_data_to_write(ssh)) channel_output_poll(); /* * Bail out of the loop if the program has closed its output * descriptors, and we have no more data to send to the * client, and there is no pending buffered data. */ if (fdout_eof && fderr_eof && !ssh_packet_have_data_to_write(ssh) && sshbuf_len(stdout_buffer) == 0 && sshbuf_len(stderr_buffer) == 0) { if (!channel_still_open()) break; if (!waiting_termination) { const char *s = "Waiting for forwarded connections to terminate...\r\n"; char *cp = channel_open_message(); waiting_termination = 1; /* Display list of open channels. */ if ((r = sshbuf_put(stderr_buffer, s, strlen(s))) != 0 || (r = sshbuf_put(stderr_buffer, s, strlen(s))) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); free(cp); } } max_fd = MAX(connection_in, connection_out); max_fd = MAX(max_fd, fdin); max_fd = MAX(max_fd, fdout); max_fd = MAX(max_fd, fderr); max_fd = MAX(max_fd, notify_pipe[0]); /* Sleep in select() until we can do something. */ wait_until_can_do_something(ssh, &readset, &writeset, &max_fd, &nalloc, max_time_milliseconds); if (received_sigterm) { logit("Exiting on signal %d", (int)received_sigterm); /* Clean up sessions, utmp, etc. */ cleanup_exit(255); } /* Process any channel events. */ channel_after_select(readset, writeset); /* Process input from the client and from program stdout/stderr. */ process_input(ssh, readset); /* Process output to the client and to program stdin. */ process_output(ssh, writeset); } free(readset); free(writeset); /* Cleanup and termination code. */ /* Wait until all output has been sent to the client. */ drain_output(ssh); debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); /* Free and clear the buffers. */ sshbuf_free(stdin_buffer); sshbuf_free(stdout_buffer); sshbuf_free(stderr_buffer); /* Close the file descriptors. */ if (fdout != -1) close(fdout); fdout = -1; fdout_eof = 1; if (fderr != -1) close(fderr); fderr = -1; fderr_eof = 1; if (fdin != -1) close(fdin); fdin = -1; channel_free_all(); /* We no longer want our SIGCHLD handler to be called. */ signal(SIGCHLD, SIG_DFL); while ((wait_pid = waitpid(-1, &wait_status, 0)) < 0) if (errno != EINTR) ssh_packet_disconnect(ssh, "wait: %.100s", strerror(errno)); if (wait_pid != pid) error("Strange, wait returned pid %ld, expected %ld", (long)wait_pid, (long)pid); /* Check if it exited normally. */ if (WIFEXITED(wait_status)) { /* Yes, normal exit. Get exit status and send it to the client. */ debug("Command exited with status %d.", WEXITSTATUS(wait_status)); if ((r = sshpkt_start(ssh, SSH_SMSG_EXITSTATUS)) != 0 || (r = sshpkt_put_u32(ssh, WEXITSTATUS(wait_status))) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); ssh_packet_write_wait(ssh); /* * Wait for exit confirmation. Note that there might be * other packets coming before it; however, the program has * already died so we just ignore them. The client is * supposed to respond with the confirmation when it receives * the exit status. */ do { type = ssh_packet_read(ssh); } while (type != SSH_CMSG_EXIT_CONFIRMATION); debug("Received exit confirmation."); return; } /* Check if the program terminated due to a signal. */ if (WIFSIGNALED(wait_status)) ssh_packet_disconnect(ssh, "Command terminated on signal %d.", WTERMSIG(wait_status)); /* Some weird exit cause. Just exit. */ ssh_packet_disconnect(ssh, "wait returned status %04x.", wait_status); /* NOTREACHED */ }