- djm@cvs.openbsd.org 2004/06/24 19:30:54

[servconf.c servconf.h sshd.c]
     re-exec sshd on accept(); initial work, final debugging and ok markus@
This commit is contained in:
Darren Tucker 2004-06-25 13:33:20 +10:00
parent b5bc1a6393
commit 645ab757bd
4 changed files with 261 additions and 32 deletions

View File

@ -1,3 +1,9 @@
20040625
- (dtucker) OpenBSD CVS Sync
- djm@cvs.openbsd.org 2004/06/24 19:30:54
[servconf.c servconf.h sshd.c]
re-exec sshd on accept(); initial work, final debugging and ok markus@
20040623 20040623
- (dtucker) [auth1.c] Ensure do_pam_account is called for Protocol 1 - (dtucker) [auth1.c] Ensure do_pam_account is called for Protocol 1
connections with empty passwords. Patch from davidwu at nbttech.com, connections with empty passwords. Patch from davidwu at nbttech.com,
@ -1399,4 +1405,4 @@
- (djm) Trim deprecated options from INSTALL. Mention UsePAM - (djm) Trim deprecated options from INSTALL. Mention UsePAM
- (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu
$Id: ChangeLog,v 1.3443 2004/06/23 14:34:53 dtucker Exp $ $Id: ChangeLog,v 1.3444 2004/06/25 03:33:20 dtucker Exp $

View File

@ -10,7 +10,7 @@
*/ */
#include "includes.h" #include "includes.h"
RCSID("$OpenBSD: servconf.c,v 1.133 2004/05/23 23:59:53 dtucker Exp $"); RCSID("$OpenBSD: servconf.c,v 1.134 2004/06/24 19:30:54 djm Exp $");
#include "ssh.h" #include "ssh.h"
#include "log.h" #include "log.h"
@ -942,26 +942,50 @@ parse_flag:
/* Reads the server configuration file. */ /* Reads the server configuration file. */
void void
read_server_config(ServerOptions *options, const char *filename) load_server_config(const char *filename, Buffer *conf)
{ {
int linenum, bad_options = 0; char line[1024], *cp;
char line[1024];
FILE *f; FILE *f;
debug2("read_server_config: filename %s", filename); debug2("%s: filename %s", __func__, filename);
f = fopen(filename, "r"); if ((f = fopen(filename, "r")) == NULL) {
if (!f) {
perror(filename); perror(filename);
exit(1); exit(1);
} }
linenum = 0; buffer_clear(conf);
while (fgets(line, sizeof(line), f)) { while (fgets(line, sizeof(line), f)) {
/* Update line number counter. */ /*
linenum++; * Trim out comments and strip whitespace
if (process_server_config_line(options, line, filename, linenum) != 0) * NB - preserve newlines, they are needed to reproduce
* line numbers later for error messages
*/
if ((cp = strchr(line, '#')) != NULL)
memcpy(cp, "\n", 2);
cp = line + strspn(line, " \t\r");
buffer_append(conf, cp, strlen(cp));
}
buffer_append(conf, "\0", 1);
fclose(f);
debug2("%s: done config len = %d", __func__, buffer_len(conf));
}
void
parse_server_config(ServerOptions *options, const char *filename, Buffer *conf)
{
int linenum, bad_options = 0;
char *cp, *cbuf;
debug2("%s: config %s len %d", __func__, filename, buffer_len(conf));
cbuf = xstrdup(buffer_ptr(conf));
linenum = 0;
while((cp = strsep(&cbuf, "\n")) != NULL) {
if (process_server_config_line(options, cp, filename,
linenum++) != 0)
bad_options++; bad_options++;
} }
fclose(f); free(cbuf);
if (bad_options > 0) if (bad_options > 0)
fatal("%s: terminating, %d bad configuration options", fatal("%s: terminating, %d bad configuration options",
filename, bad_options); filename, bad_options);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: servconf.h,v 1.69 2004/05/23 23:59:53 dtucker Exp $ */ /* $OpenBSD: servconf.h,v 1.70 2004/06/24 19:30:54 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -16,6 +16,8 @@
#ifndef SERVCONF_H #ifndef SERVCONF_H
#define SERVCONF_H #define SERVCONF_H
#include "buffer.h"
#define MAX_PORTS 256 /* Max # ports. */ #define MAX_PORTS 256 /* Max # ports. */
#define MAX_ALLOW_USERS 256 /* Max # users on allow list. */ #define MAX_ALLOW_USERS 256 /* Max # users on allow list. */
@ -134,9 +136,9 @@ typedef struct {
} ServerOptions; } ServerOptions;
void initialize_server_options(ServerOptions *); void initialize_server_options(ServerOptions *);
void read_server_config(ServerOptions *, const char *);
void fill_default_server_options(ServerOptions *); void fill_default_server_options(ServerOptions *);
int process_server_config_line(ServerOptions *, char *, const char *, int); int process_server_config_line(ServerOptions *, char *, const char *, int);
void load_server_config(const char *, Buffer *);
void parse_server_config(ServerOptions *, const char *, Buffer *);
#endif /* SERVCONF_H */ #endif /* SERVCONF_H */

229
sshd.c
View File

@ -42,7 +42,7 @@
*/ */
#include "includes.h" #include "includes.h"
RCSID("$OpenBSD: sshd.c,v 1.293 2004/06/14 01:44:39 djm Exp $"); RCSID("$OpenBSD: sshd.c,v 1.294 2004/06/24 19:30:54 djm Exp $");
#include <openssl/dh.h> #include <openssl/dh.h>
#include <openssl/bn.h> #include <openssl/bn.h>
@ -65,6 +65,7 @@ RCSID("$OpenBSD: sshd.c,v 1.293 2004/06/14 01:44:39 djm Exp $");
#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"
@ -76,6 +77,7 @@ RCSID("$OpenBSD: sshd.c,v 1.293 2004/06/14 01:44:39 djm Exp $");
#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"
@ -137,6 +139,12 @@ int log_stderr = 0;
char **saved_argv; char **saved_argv;
int saved_argc; int saved_argc;
/* 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.
@ -771,6 +779,87 @@ usage(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.
*/ */
@ -791,11 +880,12 @@ main(int ac, char **av)
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;
#ifdef HAVE_SECUREWARE #ifdef HAVE_SECUREWARE
(void)set_auth_parameters(ac, av); (void)set_auth_parameters(ac, av);
@ -823,7 +913,7 @@ main(int ac, char **av)
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;
@ -850,6 +940,13 @@ main(int ac, char **av)
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;
@ -913,6 +1010,13 @@ main(int ac, char **av)
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(STDERR_FILENO + 3);
SSLeay_add_all_algorithms(); SSLeay_add_all_algorithms();
channel_set_af(IPv4or6); channel_set_af(IPv4or6);
@ -943,8 +1047,23 @@ main(int ac, char **av)
seed_rng(); seed_rng();
/* 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(STDERR_FILENO + 2, &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);
@ -962,10 +1081,6 @@ main(int ac, char **av)
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);
@ -1064,6 +1179,16 @@ main(int ac, char **av)
if (setgroups(0, NULL) < 0) if (setgroups(0, NULL) < 0)
debug("setgroups() failed: %.200s", strerror(errno)); debug("setgroups() failed: %.200s", strerror(errno));
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;
@ -1105,19 +1230,34 @@ main(int ac, char **av)
/* 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(STDERR_FILENO + 2);
sock_in = sock_out = dup(STDIN_FILENO);
if (!debug_flag) {
startup_pipe = dup(STDERR_FILENO + 1);
close(STDERR_FILENO + 1);
}
} 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) {
@ -1297,6 +1437,16 @@ main(int ac, char **av)
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];
@ -1320,8 +1470,15 @@ main(int ac, char **av)
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 {
/* /*
@ -1355,6 +1512,12 @@ main(int ac, char **av)
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) {
@ -1378,6 +1541,40 @@ main(int ac, char **av)
/* This is the child processing a new connection. */ /* This is the child processing a new connection. */
setproctitle("%s", "[accepted]"); setproctitle("%s", "[accepted]");
if (rexec_flag) {
int fd;
debug("rexec newsock %d pipe %d sock %d", newsock,
startup_pipe, config_s[0]);
dup2(newsock, STDIN_FILENO);
dup2(STDIN_FILENO, STDOUT_FILENO);
if (startup_pipe == -1)
close(STDERR_FILENO + 1);
else
dup2(startup_pipe, STDERR_FILENO + 1);
dup2(config_s[1], STDERR_FILENO + 2);
close(config_s[1]);
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(STDERR_FILENO + 2, NULL);
log_init(__progname, options.log_level,
options.log_facility, log_stderr);
/* Clean up fds */
close(config_s[1]);
close(STDERR_FILENO + 1);
close(STDERR_FILENO + 2);
if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
if (fd > STDERR_FILENO)
close(fd);
}
}
/* /*
* Create a new session and process group since the 4.4BSD * Create a new session and process group since the 4.4BSD
* setlogin() affects the entire process group. We don't * setlogin() affects the entire process group. We don't