version 1.290, 2004/03/11 10:21:17 |
version 1.290.2.1, 2004/08/19 04:13:28 |
|
|
#include "rsa.h" |
#include "rsa.h" |
#include "sshpty.h" |
#include "sshpty.h" |
#include "packet.h" |
#include "packet.h" |
#include "mpaux.h" |
|
#include "log.h" |
#include "log.h" |
#include "servconf.h" |
#include "servconf.h" |
#include "uidswap.h" |
#include "uidswap.h" |
#include "compat.h" |
#include "compat.h" |
#include "buffer.h" |
#include "buffer.h" |
|
#include "bufaux.h" |
#include "cipher.h" |
#include "cipher.h" |
#include "kex.h" |
#include "kex.h" |
#include "key.h" |
#include "key.h" |
|
|
#include "canohost.h" |
#include "canohost.h" |
#include "auth.h" |
#include "auth.h" |
#include "misc.h" |
#include "misc.h" |
|
#include "msg.h" |
#include "dispatch.h" |
#include "dispatch.h" |
#include "channels.h" |
#include "channels.h" |
#include "session.h" |
#include "session.h" |
|
|
#define O_NOCTTY 0 |
#define O_NOCTTY 0 |
#endif |
#endif |
|
|
|
/* Re-exec fds */ |
|
#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) |
|
#define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) |
|
#define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3) |
|
#define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4) |
|
|
extern char *__progname; |
extern char *__progname; |
|
|
/* Server configuration options. */ |
/* Server configuration options. */ |
|
|
/* Saved arguments to main(). */ |
/* Saved arguments to main(). */ |
char **saved_argv; |
char **saved_argv; |
|
|
|
/* re-exec */ |
|
int rexeced_flag = 0; |
|
int rexec_flag = 1; |
|
int rexec_argc = 0; |
|
char **rexec_argv; |
|
|
/* |
/* |
* The sockets that the server is listening; this is used in the SIGHUP |
* The sockets that the server is listening; this is used in the SIGHUP |
* signal handler. |
* signal handler. |
|
|
/* global authentication context */ |
/* global authentication context */ |
Authctxt *the_authctxt = NULL; |
Authctxt *the_authctxt = NULL; |
|
|
|
/* message to be displayed after login */ |
|
Buffer loginmsg; |
|
|
/* Prototypes for various functions defined later in this file. */ |
/* Prototypes for various functions defined later in this file. */ |
void destroy_sensitive_data(void); |
void destroy_sensitive_data(void); |
void demote_sensitive_data(void); |
void demote_sensitive_data(void); |
|
|
exit(1); |
exit(1); |
} |
} |
|
|
|
static void |
|
send_rexec_state(int fd, Buffer *conf) |
|
{ |
|
Buffer m; |
|
|
|
debug3("%s: entering fd = %d config len %d", __func__, fd, |
|
buffer_len(conf)); |
|
|
|
/* |
|
* Protocol from reexec master to child: |
|
* string configuration |
|
* u_int ephemeral_key_follows |
|
* bignum e (only if ephemeral_key_follows == 1) |
|
* bignum n " |
|
* bignum d " |
|
* bignum iqmp " |
|
* bignum p " |
|
* bignum q " |
|
*/ |
|
buffer_init(&m); |
|
buffer_put_cstring(&m, buffer_ptr(conf)); |
|
|
|
if (sensitive_data.server_key != NULL && |
|
sensitive_data.server_key->type == KEY_RSA1) { |
|
buffer_put_int(&m, 1); |
|
buffer_put_bignum(&m, sensitive_data.server_key->rsa->e); |
|
buffer_put_bignum(&m, sensitive_data.server_key->rsa->n); |
|
buffer_put_bignum(&m, sensitive_data.server_key->rsa->d); |
|
buffer_put_bignum(&m, sensitive_data.server_key->rsa->iqmp); |
|
buffer_put_bignum(&m, sensitive_data.server_key->rsa->p); |
|
buffer_put_bignum(&m, sensitive_data.server_key->rsa->q); |
|
} else |
|
buffer_put_int(&m, 0); |
|
|
|
if (ssh_msg_send(fd, 0, &m) == -1) |
|
fatal("%s: ssh_msg_send failed", __func__); |
|
|
|
buffer_free(&m); |
|
|
|
debug3("%s: done", __func__); |
|
} |
|
|
|
static void |
|
recv_rexec_state(int fd, Buffer *conf) |
|
{ |
|
Buffer m; |
|
char *cp; |
|
u_int len; |
|
|
|
debug3("%s: entering fd = %d", __func__, fd); |
|
|
|
buffer_init(&m); |
|
|
|
if (ssh_msg_recv(fd, &m) == -1) |
|
fatal("%s: ssh_msg_recv failed", __func__); |
|
if (buffer_get_char(&m) != 0) |
|
fatal("%s: rexec version mismatch", __func__); |
|
|
|
cp = buffer_get_string(&m, &len); |
|
if (conf != NULL) |
|
buffer_append(conf, cp, len + 1); |
|
xfree(cp); |
|
|
|
if (buffer_get_int(&m)) { |
|
if (sensitive_data.server_key != NULL) |
|
key_free(sensitive_data.server_key); |
|
sensitive_data.server_key = key_new_private(KEY_RSA1); |
|
buffer_get_bignum(&m, sensitive_data.server_key->rsa->e); |
|
buffer_get_bignum(&m, sensitive_data.server_key->rsa->n); |
|
buffer_get_bignum(&m, sensitive_data.server_key->rsa->d); |
|
buffer_get_bignum(&m, sensitive_data.server_key->rsa->iqmp); |
|
buffer_get_bignum(&m, sensitive_data.server_key->rsa->p); |
|
buffer_get_bignum(&m, sensitive_data.server_key->rsa->q); |
|
rsa_generate_additional_parameters( |
|
sensitive_data.server_key->rsa); |
|
} |
|
buffer_free(&m); |
|
|
|
debug3("%s: done", __func__); |
|
} |
|
|
/* |
/* |
* Main program for the daemon. |
* Main program for the daemon. |
*/ |
*/ |
|
|
{ |
{ |
extern char *optarg; |
extern char *optarg; |
extern int optind; |
extern int optind; |
int opt, sock_in = 0, sock_out = 0, newsock, j, i, fdsetsz, on = 1; |
int opt, j, i, fdsetsz, on = 1; |
|
int sock_in = -1, sock_out = -1, newsock = -1; |
pid_t pid; |
pid_t pid; |
socklen_t fromlen; |
socklen_t fromlen; |
fd_set *fdset; |
fd_set *fdset; |
|
|
char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
char *line; |
char *line; |
int listen_sock, maxfd; |
int listen_sock, maxfd; |
int startup_p[2]; |
int startup_p[2], config_s[2]; |
int startups = 0; |
int startups = 0; |
Key *key; |
Key *key; |
Authctxt *authctxt; |
Authctxt *authctxt; |
int ret, key_used = 0; |
int ret, key_used = 0; |
|
Buffer cfg; |
|
|
/* Save argv. */ |
/* Save argv. */ |
saved_argv = av; |
saved_argv = av; |
|
rexec_argc = ac; |
|
|
/* Initialize configuration options to their default values. */ |
/* Initialize configuration options to their default values. */ |
initialize_server_options(&options); |
initialize_server_options(&options); |
|
|
/* Parse command-line arguments. */ |
/* Parse command-line arguments. */ |
while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:dDeiqtQ46")) != -1) { |
while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:dDeiqrtQR46")) != -1) { |
switch (opt) { |
switch (opt) { |
case '4': |
case '4': |
IPv4or6 = AF_INET; |
IPv4or6 = AF_INET; |
|
|
case 'i': |
case 'i': |
inetd_flag = 1; |
inetd_flag = 1; |
break; |
break; |
|
case 'r': |
|
rexec_flag = 0; |
|
break; |
|
case 'R': |
|
rexeced_flag = 1; |
|
inetd_flag = 1; |
|
break; |
case 'Q': |
case 'Q': |
/* ignored */ |
/* ignored */ |
break; |
break; |
|
|
break; |
break; |
} |
} |
} |
} |
|
if (rexeced_flag || inetd_flag) |
|
rexec_flag = 0; |
|
if (rexec_flag && (av[0] == NULL || *av[0] != '/')) |
|
fatal("sshd re-exec requires execution with an absolute path"); |
|
if (rexeced_flag) |
|
closefrom(REEXEC_MIN_FREE_FD); |
|
else |
|
closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); |
|
|
SSLeay_add_all_algorithms(); |
SSLeay_add_all_algorithms(); |
channel_set_af(IPv4or6); |
channel_set_af(IPv4or6); |
|
|
|
|
SYSLOG_FACILITY_AUTH : options.log_facility, |
SYSLOG_FACILITY_AUTH : options.log_facility, |
log_stderr || !inetd_flag); |
log_stderr || !inetd_flag); |
|
|
/* Read server configuration options from the configuration file. */ |
sensitive_data.server_key = NULL; |
read_server_config(&options, config_file_name); |
sensitive_data.ssh1_host_key = NULL; |
|
sensitive_data.have_ssh1_key = 0; |
|
sensitive_data.have_ssh2_key = 0; |
|
|
|
/* Fetch our configuration */ |
|
buffer_init(&cfg); |
|
if (rexeced_flag) |
|
recv_rexec_state(REEXEC_CONFIG_PASS_FD, &cfg); |
|
else |
|
load_server_config(config_file_name, &cfg); |
|
|
|
parse_server_config(&options, |
|
rexeced_flag ? "rexec" : config_file_name, &cfg); |
|
|
|
if (!rexec_flag) |
|
buffer_free(&cfg); |
|
|
/* Fill in default values for those options not explicitly set. */ |
/* Fill in default values for those options not explicitly set. */ |
fill_default_server_options(&options); |
fill_default_server_options(&options); |
|
|
|
|
sizeof(Key *)); |
sizeof(Key *)); |
for (i = 0; i < options.num_host_key_files; i++) |
for (i = 0; i < options.num_host_key_files; i++) |
sensitive_data.host_keys[i] = NULL; |
sensitive_data.host_keys[i] = NULL; |
sensitive_data.server_key = NULL; |
|
sensitive_data.ssh1_host_key = NULL; |
|
sensitive_data.have_ssh1_key = 0; |
|
sensitive_data.have_ssh2_key = 0; |
|
|
|
for (i = 0; i < options.num_host_key_files; i++) { |
for (i = 0; i < options.num_host_key_files; i++) { |
key = key_load_private(options.host_key_files[i], "", NULL); |
key = key_load_private(options.host_key_files[i], "", NULL); |
|
|
if (test_flag) |
if (test_flag) |
exit(0); |
exit(0); |
|
|
|
if (rexec_flag) { |
|
rexec_argv = xmalloc(sizeof(char *) * (rexec_argc + 2)); |
|
for (i = 0; i < rexec_argc; i++) { |
|
debug("rexec_argv[%d]='%s'", i, saved_argv[i]); |
|
rexec_argv[i] = saved_argv[i]; |
|
} |
|
rexec_argv[rexec_argc] = "-R"; |
|
rexec_argv[rexec_argc + 1] = NULL; |
|
} |
|
|
/* Initialize the log (it is reinitialized below in case we forked). */ |
/* Initialize the log (it is reinitialized below in case we forked). */ |
if (debug_flag && !inetd_flag) |
if (debug_flag && !inetd_flag) |
log_stderr = 1; |
log_stderr = 1; |
|
|
|
|
/* Start listening for a socket, unless started from inetd. */ |
/* Start listening for a socket, unless started from inetd. */ |
if (inetd_flag) { |
if (inetd_flag) { |
int s1; |
int fd; |
s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ |
|
dup(s1); |
|
sock_in = dup(0); |
|
sock_out = dup(1); |
|
startup_pipe = -1; |
startup_pipe = -1; |
|
if (rexeced_flag) { |
|
close(REEXEC_CONFIG_PASS_FD); |
|
sock_in = sock_out = dup(STDIN_FILENO); |
|
if (!debug_flag) { |
|
startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); |
|
close(REEXEC_STARTUP_PIPE_FD); |
|
} |
|
} else { |
|
sock_in = dup(STDIN_FILENO); |
|
sock_out = dup(STDOUT_FILENO); |
|
} |
/* |
/* |
* We intentionally do not close the descriptors 0, 1, and 2 |
* We intentionally do not close the descriptors 0, 1, and 2 |
* as our code for setting the descriptors won\'t work if |
* as our code for setting the descriptors won't work if |
* ttyfd happens to be one of those. |
* ttyfd happens to be one of those. |
*/ |
*/ |
|
if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { |
|
dup2(fd, STDIN_FILENO); |
|
dup2(fd, STDOUT_FILENO); |
|
if (fd > STDOUT_FILENO) |
|
close(fd); |
|
} |
debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); |
debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); |
if (options.protocol & SSH_PROTO_1) |
if ((options.protocol & SSH_PROTO_1) && |
|
sensitive_data.server_key == NULL) |
generate_ephemeral_server_key(); |
generate_ephemeral_server_key(); |
} else { |
} else { |
for (ai = options.listen_addrs; ai; ai = ai->ai_next) { |
for (ai = options.listen_addrs; ai; ai = ai->ai_next) { |
|
|
verbose("socket: %.100s", strerror(errno)); |
verbose("socket: %.100s", strerror(errno)); |
continue; |
continue; |
} |
} |
if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) < 0) { |
if (set_nonblock(listen_sock) == -1) { |
error("listen_sock O_NONBLOCK: %s", strerror(errno)); |
|
close(listen_sock); |
close(listen_sock); |
continue; |
continue; |
} |
} |
|
|
error("accept: %.100s", strerror(errno)); |
error("accept: %.100s", strerror(errno)); |
continue; |
continue; |
} |
} |
if (fcntl(newsock, F_SETFL, 0) < 0) { |
if (unset_nonblock(newsock) == -1) { |
error("newsock del O_NONBLOCK: %s", strerror(errno)); |
|
close(newsock); |
close(newsock); |
continue; |
continue; |
} |
} |
|
|
continue; |
continue; |
} |
} |
|
|
|
if (rexec_flag && socketpair(AF_UNIX, |
|
SOCK_STREAM, 0, config_s) == -1) { |
|
error("reexec socketpair: %s", |
|
strerror(errno)); |
|
close(newsock); |
|
close(startup_p[0]); |
|
close(startup_p[1]); |
|
continue; |
|
} |
|
|
for (j = 0; j < options.max_startups; j++) |
for (j = 0; j < options.max_startups; j++) |
if (startup_pipes[j] == -1) { |
if (startup_pipes[j] == -1) { |
startup_pipes[j] = startup_p[0]; |
startup_pipes[j] = startup_p[0]; |
|
|
close_listen_socks(); |
close_listen_socks(); |
sock_in = newsock; |
sock_in = newsock; |
sock_out = newsock; |
sock_out = newsock; |
|
close(startup_p[0]); |
|
close(startup_p[1]); |
startup_pipe = -1; |
startup_pipe = -1; |
pid = getpid(); |
pid = getpid(); |
|
if (rexec_flag) { |
|
send_rexec_state(config_s[0], |
|
&cfg); |
|
close(config_s[0]); |
|
} |
break; |
break; |
} else { |
} else { |
/* |
/* |
|
|
sock_in = newsock; |
sock_in = newsock; |
sock_out = newsock; |
sock_out = newsock; |
log_init(__progname, options.log_level, options.log_facility, log_stderr); |
log_init(__progname, options.log_level, options.log_facility, log_stderr); |
|
close(config_s[0]); |
break; |
break; |
} |
} |
} |
} |
|
|
|
|
close(startup_p[1]); |
close(startup_p[1]); |
|
|
|
if (rexec_flag) { |
|
send_rexec_state(config_s[0], &cfg); |
|
close(config_s[0]); |
|
close(config_s[1]); |
|
} |
|
|
/* Mark that the key has been used (it was "given" to the child). */ |
/* Mark that the key has been used (it was "given" to the child). */ |
if ((options.protocol & SSH_PROTO_1) && |
if ((options.protocol & SSH_PROTO_1) && |
key_used == 0) { |
key_used == 0) { |
|
|
if (!debug_flag && !inetd_flag && setsid() < 0) |
if (!debug_flag && !inetd_flag && setsid() < 0) |
error("setsid: %.100s", strerror(errno)); |
error("setsid: %.100s", strerror(errno)); |
|
|
|
if (rexec_flag) { |
|
int fd; |
|
|
|
debug("rexec start in %d out %d newsock %d pipe %d sock %d", |
|
sock_in, sock_out, newsock, startup_pipe, config_s[0]); |
|
dup2(newsock, STDIN_FILENO); |
|
dup2(STDIN_FILENO, STDOUT_FILENO); |
|
if (startup_pipe == -1) |
|
close(REEXEC_STARTUP_PIPE_FD); |
|
else |
|
dup2(startup_pipe, REEXEC_STARTUP_PIPE_FD); |
|
|
|
dup2(config_s[1], REEXEC_CONFIG_PASS_FD); |
|
close(config_s[1]); |
|
if (startup_pipe != -1) |
|
close(startup_pipe); |
|
|
|
execv(rexec_argv[0], rexec_argv); |
|
|
|
/* Reexec has failed, fall back and continue */ |
|
error("rexec of %s failed: %s", rexec_argv[0], strerror(errno)); |
|
recv_rexec_state(REEXEC_CONFIG_PASS_FD, NULL); |
|
log_init(__progname, options.log_level, |
|
options.log_facility, log_stderr); |
|
|
|
/* Clean up fds */ |
|
startup_pipe = REEXEC_STARTUP_PIPE_FD; |
|
close(config_s[1]); |
|
close(REEXEC_CONFIG_PASS_FD); |
|
newsock = sock_out = sock_in = dup(STDIN_FILENO); |
|
if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { |
|
dup2(fd, STDIN_FILENO); |
|
dup2(fd, STDOUT_FILENO); |
|
if (fd > STDERR_FILENO) |
|
close(fd); |
|
} |
|
debug("rexec cleanup in %d out %d newsock %d pipe %d sock %d", |
|
sock_in, sock_out, newsock, startup_pipe, config_s[0]); |
|
} |
|
|
/* |
/* |
* Disable the key regeneration alarm. We will not regenerate the |
* Disable the key regeneration alarm. We will not regenerate the |
* key since we are no longer in a position to give it to anyone. We |
* key since we are no longer in a position to give it to anyone. We |
|
|
|
|
#ifdef LIBWRAP |
#ifdef LIBWRAP |
/* Check whether logins are denied from this host. */ |
/* Check whether logins are denied from this host. */ |
{ |
if (packet_connection_is_on_socket()) { |
struct request_info req; |
struct request_info req; |
|
|
request_init(&req, RQ_DAEMON, __progname, RQ_FILE, sock_in, 0); |
request_init(&req, RQ_DAEMON, __progname, RQ_FILE, sock_in, 0); |
|
|
if (privsep_preauth(authctxt) == 1) |
if (privsep_preauth(authctxt) == 1) |
goto authenticated; |
goto authenticated; |
|
|
|
/* prepare buffer to collect messages to display to user after login */ |
|
buffer_init(&loginmsg); |
|
|
/* perform the key exchange */ |
/* perform the key exchange */ |
/* authenticate user and start session */ |
/* authenticate user and start session */ |
if (compat20) { |
if (compat20) { |
|
|
BN_bn2bin(session_key_int, |
BN_bn2bin(session_key_int, |
session_key + sizeof(session_key) - len); |
session_key + sizeof(session_key) - len); |
|
|
compute_session_id(session_id, cookie, |
derive_ssh1_session_id( |
sensitive_data.ssh1_host_key->rsa->n, |
sensitive_data.ssh1_host_key->rsa->n, |
sensitive_data.server_key->rsa->n); |
sensitive_data.server_key->rsa->n, |
|
cookie, session_id); |
/* |
/* |
* Xor the first 16 bytes of the session key with the |
* Xor the first 16 bytes of the session key with the |
* session id. |
* session id. |
|
|
/* start key exchange */ |
/* start key exchange */ |
kex = kex_setup(myproposal); |
kex = kex_setup(myproposal); |
kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; |
kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; |
|
kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; |
kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; |
kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; |
kex->server = 1; |
kex->server = 1; |
kex->client_version_string=client_version_string; |
kex->client_version_string=client_version_string; |