- 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
- (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 $

View File

@ -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 @@ parse_flag:
/* 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);

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>
@ -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 @@ typedef struct {
} 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 */

229
sshd.c
View File

@ -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 @@ RCSID("$OpenBSD: sshd.c,v 1.293 2004/06/14 01:44:39 djm Exp $");
#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 @@ RCSID("$OpenBSD: sshd.c,v 1.293 2004/06/14 01:44:39 djm Exp $");
#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 @@ int log_stderr = 0;
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 @@ usage(void)
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 @@ main(int ac, char **av)
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 @@ main(int ac, char **av)
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 @@ main(int ac, char **av)
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 @@ main(int ac, char **av)
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 @@ main(int ac, char **av)
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 @@ main(int ac, char **av)
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 @@ main(int ac, char **av)
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 @@ main(int ac, char **av)
/* 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 @@ main(int ac, char **av)
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 @@ main(int ac, char **av)
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 @@ main(int ac, char **av)
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 @@ main(int ac, char **av)
/* 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