- 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@
diff --git a/ChangeLog b/ChangeLog
index 023fde0..4367699 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
- (dtucker) [auth1.c] Ensure do_pam_account is called for Protocol 1
connections with empty passwords. Patch from davidwu at nbttech.com,
@@ -1399,4 +1405,4 @@
- (djm) Trim deprecated options from INSTALL. Mention UsePAM
- (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 $
diff --git a/servconf.c b/servconf.c
index ef86516..ea67f62 100644
--- a/servconf.c
+++ b/servconf.c
@@ -10,7 +10,7 @@
*/
#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 "log.h"
@@ -942,26 +942,50 @@
/* Reads the server configuration file. */
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];
+ char line[1024], *cp;
FILE *f;
- debug2("read_server_config: filename %s", filename);
- f = fopen(filename, "r");
- if (!f) {
+ debug2("%s: filename %s", __func__, filename);
+ if ((f = fopen(filename, "r")) == NULL) {
perror(filename);
exit(1);
}
- linenum = 0;
+ buffer_clear(conf);
while (fgets(line, sizeof(line), f)) {
- /* Update line number counter. */
- linenum++;
- if (process_server_config_line(options, line, filename, linenum) != 0)
+ /*
+ * Trim out comments and strip whitespace
+ * 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++;
}
- fclose(f);
+ free(cbuf);
if (bad_options > 0)
fatal("%s: terminating, %d bad configuration options",
filename, bad_options);
diff --git a/servconf.h b/servconf.h
index 36d2e5c..ebd0568 100644
--- a/servconf.h
+++ b/servconf.h
@@ -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>
@@ -16,6 +16,8 @@
#ifndef SERVCONF_H
#define SERVCONF_H
+#include "buffer.h"
+
#define MAX_PORTS 256 /* Max # ports. */
#define MAX_ALLOW_USERS 256 /* Max # users on allow list. */
@@ -134,9 +136,9 @@
} ServerOptions;
void initialize_server_options(ServerOptions *);
-void read_server_config(ServerOptions *, const char *);
void fill_default_server_options(ServerOptions *);
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 */
diff --git a/sshd.c b/sshd.c
index 3437917..98e90b5 100644
--- a/sshd.c
+++ b/sshd.c
@@ -42,7 +42,7 @@
*/
#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/bn.h>
@@ -65,6 +65,7 @@
#include "uidswap.h"
#include "compat.h"
#include "buffer.h"
+#include "bufaux.h"
#include "cipher.h"
#include "kex.h"
#include "key.h"
@@ -76,6 +77,7 @@
#include "canohost.h"
#include "auth.h"
#include "misc.h"
+#include "msg.h"
#include "dispatch.h"
#include "channels.h"
#include "session.h"
@@ -137,6 +139,12 @@
char **saved_argv;
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
* signal handler.
@@ -771,6 +779,87 @@
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.
*/
@@ -791,11 +880,12 @@
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
char *line;
int listen_sock, maxfd;
- int startup_p[2];
+ int startup_p[2], config_s[2];
int startups = 0;
Key *key;
Authctxt *authctxt;
int ret, key_used = 0;
+ Buffer cfg;
#ifdef HAVE_SECUREWARE
(void)set_auth_parameters(ac, av);
@@ -823,7 +913,7 @@
initialize_server_options(&options);
/* 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) {
case '4':
IPv4or6 = AF_INET;
@@ -850,6 +940,13 @@
case 'i':
inetd_flag = 1;
break;
+ case 'r':
+ rexec_flag = 0;
+ break;
+ case 'R':
+ rexeced_flag = 1;
+ inetd_flag = 1;
+ break;
case 'Q':
/* ignored */
break;
@@ -913,6 +1010,13 @@
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();
channel_set_af(IPv4or6);
@@ -943,8 +1047,23 @@
seed_rng();
- /* Read server configuration options from the configuration file. */
- read_server_config(&options, config_file_name);
+ sensitive_data.server_key = NULL;
+ 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_default_server_options(&options);
@@ -962,10 +1081,6 @@
sizeof(Key *));
for (i = 0; i < options.num_host_key_files; i++)
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++) {
key = key_load_private(options.host_key_files[i], "", NULL);
@@ -1064,6 +1179,16 @@
if (setgroups(0, NULL) < 0)
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). */
if (debug_flag && !inetd_flag)
log_stderr = 1;
@@ -1105,19 +1230,34 @@
/* Start listening for a socket, unless started from inetd. */
if (inetd_flag) {
- int s1;
- s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */
- dup(s1);
- sock_in = dup(0);
- sock_out = dup(1);
+ int fd;
+
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
- * 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.
*/
+ 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);
- if (options.protocol & SSH_PROTO_1)
+ if ((options.protocol & SSH_PROTO_1) &&
+ sensitive_data.server_key == NULL)
generate_ephemeral_server_key();
} else {
for (ai = options.listen_addrs; ai; ai = ai->ai_next) {
@@ -1297,6 +1437,16 @@
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++)
if (startup_pipes[j] == -1) {
startup_pipes[j] = startup_p[0];
@@ -1320,8 +1470,15 @@
close_listen_socks();
sock_in = newsock;
sock_out = newsock;
+ close(startup_p[0]);
+ close(startup_p[1]);
startup_pipe = -1;
pid = getpid();
+ if (rexec_flag) {
+ send_rexec_state(config_s[0],
+ &cfg);
+ close(config_s[0]);
+ }
break;
} else {
/*
@@ -1355,6 +1512,12 @@
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). */
if ((options.protocol & SSH_PROTO_1) &&
key_used == 0) {
@@ -1378,6 +1541,40 @@
/* This is the child processing a new connection. */
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
* setlogin() affects the entire process group. We don't