📄 sshd.c
字号:
/* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved * This program is the ssh daemon. It listens for connections from clients, * and performs authentication, executes use commands or shell, and forwards * information to/from the application to the user client over an encrypted * connection. This can also handle forwarding of X11, TCP/IP, and * authentication agent connections. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 implementation: * Privilege Separation: * * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. * Copyright (c) 2002 Niels Provos. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include "includes.h"RCSID("$OpenBSD: sshd.c,v 1.263 2003/02/16 17:09:57 markus Exp $");#include <openssl/dh.h>#include <openssl/bn.h>#include <openssl/md5.h>#include <openssl/rand.h>#include "ssh.h"#include "ssh1.h"#include "ssh2.h"#include "xmalloc.h"#include "rsa.h"#include "sshpty.h"#include "packet.h"#include "mpaux.h"#include "log.h"#include "servconf.h"#include "uidswap.h"#include "compat.h"#include "buffer.h"#include "cipher.h"#include "kex.h"#include "key.h"#include "dh.h"#include "myproposal.h"#include "authfile.h"#include "pathnames.h"#include "atomicio.h"#include "canohost.h"#include "auth.h"#include "misc.h"#include "dispatch.h"#include "channels.h"#include "session.h"#include "monitor_mm.h"#include "monitor.h"#include "monitor_wrap.h"#include "monitor_fdpass.h"#ifdef LIBWRAP#include <tcpd.h>#include <syslog.h>int allow_severity = LOG_INFO;int deny_severity = LOG_WARNING;#endif /* LIBWRAP */#ifndef O_NOCTTY#define O_NOCTTY 0#endifextern char *__progname;/* Server configuration options. */ServerOptions options;/* Name of the server configuration file. */char *config_file_name = _PATH_SERVER_CONFIG_FILE;/* * Flag indicating whether IPv4 or IPv6. This can be set on the command line. * Default value is AF_UNSPEC means both IPv4 and IPv6. */int IPv4or6 = AF_UNSPEC;/* * Debug mode flag. This can be set on the command line. If debug * mode is enabled, extra debugging output will be sent to the system * log, the daemon will not go to background, and will exit after processing * the first connection. */int debug_flag = 0;/* Flag indicating that the daemon should only test the configuration and keys. */int test_flag = 0;/* Flag indicating that the daemon is being started from inetd. */int inetd_flag = 0;/* Flag indicating that sshd should not detach and become a daemon. */int no_daemon_flag = 0;/* debug goes to stderr unless inetd_flag is set */int log_stderr = 0;/* Saved arguments to main(). */char **saved_argv;/* * The sockets that the server is listening; this is used in the SIGHUP * signal handler. */#define MAX_LISTEN_SOCKS 16int listen_socks[MAX_LISTEN_SOCKS];int num_listen_socks = 0;/* * the client's version string, passed by sshd2 in compat mode. if != NULL, * sshd will skip the version-number exchange */char *client_version_string = NULL;char *server_version_string = NULL;/* for rekeying XXX fixme */Kex *xxx_kex;/* * Any really sensitive data in the application is contained in this * structure. The idea is that this structure could be locked into memory so * that the pages do not get written into swap. However, there are some * problems. The private key contains BIGNUMs, and we do not (in principle) * have access to the internals of them, and locking just the structure is * not very useful. Currently, memory locking is not implemented. */struct { Key *server_key; /* ephemeral server key */ Key *ssh1_host_key; /* ssh1 host key */ Key **host_keys; /* all private host keys */ int have_ssh1_key; int have_ssh2_key; u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH];} sensitive_data;/* * Flag indicating whether the RSA server key needs to be regenerated. * Is set in the SIGALRM handler and cleared when the key is regenerated. */static volatile sig_atomic_t key_do_regen = 0;/* This is set to true when a signal is received. */static volatile sig_atomic_t received_sighup = 0;static volatile sig_atomic_t received_sigterm = 0;/* session identifier, used by RSA-auth */u_char session_id[16];/* same for ssh2 */u_char *session_id2 = NULL;int session_id2_len = 0;/* record remote hostname or ip */u_int utmp_len = MAXHOSTNAMELEN;/* options.max_startup sized array of fd ints */int *startup_pipes = NULL;int startup_pipe; /* in child *//* variables used for privilege separation */int use_privsep;struct monitor *pmonitor;/* Prototypes for various functions defined later in this file. */void destroy_sensitive_data(void);void demote_sensitive_data(void);static void do_ssh1_kex(void);static void do_ssh2_kex(void);/* * Close all listening sockets */static voidclose_listen_socks(void){ int i; for (i = 0; i < num_listen_socks; i++) close(listen_socks[i]); num_listen_socks = -1;}static voidclose_startup_pipes(void){ int i; if (startup_pipes) for (i = 0; i < options.max_startups; i++) if (startup_pipes[i] != -1) close(startup_pipes[i]);}/* * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; * the effect is to reread the configuration file (and to regenerate * the server key). */static voidsighup_handler(int sig){ int save_errno = errno; received_sighup = 1; signal(SIGHUP, sighup_handler); errno = save_errno;}/* * Called from the main program after receiving SIGHUP. * Restarts the server. */static voidsighup_restart(void){ log("Received SIGHUP; restarting."); close_listen_socks(); close_startup_pipes(); execv(saved_argv[0], saved_argv); log("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], strerror(errno)); exit(1);}/* * Generic signal handler for terminating signals in the master daemon. */static voidsigterm_handler(int sig){ received_sigterm = sig;}/* * SIGCHLD handler. This is called whenever a child dies. This will then * reap any zombies left by exited children. */static voidmain_sigchld_handler(int sig){ int save_errno = errno; pid_t pid; int status; while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid < 0 && errno == EINTR)) ; signal(SIGCHLD, main_sigchld_handler); errno = save_errno;}/* * Signal handler for the alarm after the login grace period has expired. */static voidgrace_alarm_handler(int sig){ /* XXX no idea how fix this signal handler */ /* Log error and exit. */ fatal("Timeout before authentication for %s", get_remote_ipaddr());}/* * Signal handler for the key regeneration alarm. Note that this * alarm only occurs in the daemon waiting for connections, and it does not * do anything with the private key or random state before forking. * Thus there should be no concurrency control/asynchronous execution * problems. */static voidgenerate_ephemeral_server_key(void){ u_int32_t rnd = 0; int i; verbose("Generating %s%d bit RSA key.", sensitive_data.server_key ? "new " : "", options.server_key_bits); if (sensitive_data.server_key != NULL) key_free(sensitive_data.server_key); sensitive_data.server_key = key_generate(KEY_RSA1, options.server_key_bits); verbose("RSA key generation complete."); for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { if (i % 4 == 0) rnd = arc4random(); sensitive_data.ssh1_cookie[i] = rnd & 0xff; rnd >>= 8; } arc4random_stir();}static voidkey_regeneration_alarm(int sig){ int save_errno = errno; signal(SIGALRM, SIG_DFL); errno = save_errno; key_do_regen = 1;}static voidsshd_exchange_identification(int sock_in, int sock_out){ int i, mismatch; int remote_major, remote_minor; int major, minor; char *s; char buf[256]; /* Must not be larger than remote_version. */ char remote_version[256]; /* Must be at least as big as buf. */ if ((options.protocol & SSH_PROTO_1) && (options.protocol & SSH_PROTO_2)) { major = PROTOCOL_MAJOR_1; minor = 99; } else if (options.protocol & SSH_PROTO_2) { major = PROTOCOL_MAJOR_2; minor = PROTOCOL_MINOR_2; } else { major = PROTOCOL_MAJOR_1; minor = PROTOCOL_MINOR_1; } snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION); server_version_string = xstrdup(buf); if (client_version_string == NULL) { /* Send our protocol version identification. */ if (atomicio(write, sock_out, server_version_string, strlen(server_version_string)) != strlen(server_version_string)) { log("Could not write ident string to %s", get_remote_ipaddr()); fatal_cleanup(); } /* Read other sides version identification. */ memset(buf, 0, sizeof(buf)); for (i = 0; i < sizeof(buf) - 1; i++) { if (atomicio(read, sock_in, &buf[i], 1) != 1) { log("Did not receive identification string from %s", get_remote_ipaddr()); fatal_cleanup(); } if (buf[i] == '\r') { buf[i] = 0; /* Kludge for F-Secure Macintosh < 1.0.2 */ if (i == 12 && strncmp(buf, "SSH-1.5-W1.0", 12) == 0) break; continue; } if (buf[i] == '\n') { buf[i] = 0; break; } } buf[sizeof(buf) - 1] = 0; client_version_string = xstrdup(buf); } /* * Check that the versions match. In future this might accept * several versions and set appropriate flags to handle them. */ if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) != 3) { s = "Protocol mismatch.\n"; (void) atomicio(write, sock_out, s, strlen(s)); close(sock_in); close(sock_out); log("Bad protocol version identification '%.100s' from %s", client_version_string, get_remote_ipaddr()); fatal_cleanup(); } debug("Client protocol version %d.%d; client software version %.100s", remote_major, remote_minor, remote_version); compat_datafellows(remote_version); if (datafellows & SSH_BUG_PROBE) { log("probed from %s with %s. Don't panic.", get_remote_ipaddr(), client_version_string); fatal_cleanup(); } if (datafellows & SSH_BUG_SCANNER) { log("scanned from %s with %s. Don't panic.", get_remote_ipaddr(), client_version_string); fatal_cleanup(); } mismatch = 0; switch (remote_major) { case 1: if (remote_minor == 99) { if (options.protocol & SSH_PROTO_2) enable_compat20(); else mismatch = 1; break; } if (!(options.protocol & SSH_PROTO_1)) { mismatch = 1; break; } if (remote_minor < 3) { packet_disconnect("Your ssh version is too old and " "is no longer supported. Please install a newer version."); } else if (remote_minor == 3) { /* note that this disables agent-forwarding */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -