monitor.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org>
* 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
*
* 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.
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "includes.h"
#pragma ident "%Z%%M% %I% %E% SMI"
#ifdef SKEY
#include <skey.h>
#endif
#include "ssh.h"
#include "auth.h"
#include "kex.h"
#include "dh.h"
#include "zlib.h"
#include "packet.h"
#include "auth-options.h"
#include "sshpty.h"
#include "channels.h"
#include "session.h"
#include "sshlogin.h"
#include "canohost.h"
#include "log.h"
#include "servconf.h"
#include "monitor.h"
#include "monitor_mm.h"
#include "monitor_wrap.h"
#include "monitor_fdpass.h"
#include "xmalloc.h"
#include "misc.h"
#include "buffer.h"
#include "bufaux.h"
#include "compat.h"
#include "ssh2.h"
#include "mpaux.h"
#ifndef lint
#ifdef GSSAPI
#include "ssh-gss.h"
#endif
/* Imports */
extern ServerOptions options;
extern Newkeys *current_keys[];
extern z_stream incoming_stream;
extern z_stream outgoing_stream;
extern u_char session_id[];
extern Buffer auth_debug;
extern int auth_debug_init;
/* State exported from the child */
struct {
int ssh1cipher;
int ssh1protoflags;
} child_state;
/* Functions on the montior that answer unprivileged requests */
int mm_answer_moduli(int, Buffer *);
int mm_answer_sign(int, Buffer *);
int mm_answer_pwnamallow(int, Buffer *);
int mm_answer_auth2_read_banner(int, Buffer *);
int mm_answer_authserv(int, Buffer *);
int mm_answer_authpassword(int, Buffer *);
int mm_answer_bsdauthquery(int, Buffer *);
int mm_answer_bsdauthrespond(int, Buffer *);
int mm_answer_skeyquery(int, Buffer *);
int mm_answer_skeyrespond(int, Buffer *);
int mm_answer_keyallowed(int, Buffer *);
int mm_answer_keyverify(int, Buffer *);
int mm_answer_pty(int, Buffer *);
int mm_answer_pty_cleanup(int, Buffer *);
int mm_answer_term(int, Buffer *);
int mm_answer_rsa_keyallowed(int, Buffer *);
int mm_answer_rsa_challenge(int, Buffer *);
int mm_answer_rsa_response(int, Buffer *);
int mm_answer_sesskey(int, Buffer *);
int mm_answer_sessid(int, Buffer *);
#ifdef USE_PAM
int mm_answer_pam_start(int, Buffer *);
#endif
#ifdef KRB4
int mm_answer_krb4(int, Buffer *);
#endif
#ifdef KRB5
int mm_answer_krb5(int, Buffer *);
#endif
#ifdef GSSAPI
int mm_answer_gss_setup_ctx(int, Buffer *);
int mm_answer_gss_accept_ctx(int, Buffer *);
int mm_answer_gss_userok(int, Buffer *);
int mm_answer_gss_sign(int, Buffer *);
int mm_answer_gss_error(int, Buffer *);
int mm_answer_gss_indicate_mechs(int, Buffer *);
int mm_answer_gss_localname(int, Buffer *);
#endif
/* local state for key verify */
static u_int key_bloblen = 0;
static int key_blobtype = MM_NOKEY;
static char *hostbased_cuser = NULL;
static char *hostbased_chost = NULL;
static char *auth_method = "unknown";
static int session_id2_len = 0;
struct mon_table {
enum monitor_reqtype type;
int flags;
int (*f)(int, Buffer *);
};
struct mon_table mon_dispatch_proto20[] = {
#ifdef USE_PAM
#endif
#ifdef BSD_AUTH
#endif
#ifdef SKEY
#endif
#ifdef GSSAPI
/* Turn this off until we use it */
#if 0
#endif
#endif
{0, 0, NULL}
};
struct mon_table mon_dispatch_postauth20[] = {
#ifdef GSSAPI
#endif
{MONITOR_REQ_SIGN, 0, mm_answer_sign},
{MONITOR_REQ_PTY, 0, mm_answer_pty},
{MONITOR_REQ_TERM, 0, mm_answer_term},
{0, 0, NULL}
};
struct mon_table mon_dispatch_proto15[] = {
#ifdef BSD_AUTH
#endif
#ifdef SKEY
#endif
#ifdef USE_PAM
#endif
#ifdef KRB4
#endif
#ifdef KRB5
#endif
{0, 0, NULL}
};
struct mon_table mon_dispatch_postauth15[] = {
{MONITOR_REQ_TERM, 0, mm_answer_term},
{0, 0, NULL}
};
struct mon_table *mon_dispatch;
/* Specifies if a certain message is allowed at the moment */
static void
{
return;
}
ent++;
}
}
static void
{
}
ent++;
}
}
Authctxt *
{
int authenticated = 0;
debug3("preauth child monitor started");
if (compat20) {
/* Permit requests for moduli and signatures */
#ifdef GSSAPI
/* and for the GSSAPI key exchange */
#endif
} else {
}
authctxt = authctxt_new();
/* The first few requests do not require asynchronous access */
while (!authenticated) {
if (authenticated) {
fatal("%s: unexpected authentication from %d",
authenticated = 0;
#ifdef USE_PAM
/*if (!do_pam_account(authctxt->pw->pw_name, * NULL))*/
authenticated = 0;
#endif
}
if (!authenticated)
}
}
debug("%s: %s has been authenticated by privileged process",
return (authctxt);
}
void
{
if (compat20) {
/* Permit requests for moduli and signatures */
#ifdef GSSAPI
/* and for the GSSAPI key exchange */
#endif
} else {
}
if (!no_pty_flag) {
}
for (;;)
}
void
{
if (options.compression) {
/* The member allocation is not visible, so sync it */
}
}
int
{
Buffer m;
int ret;
buffer_init(&m);
type = buffer_get_char(&m);
break;
ent++;
}
type);
buffer_free(&m);
/* The child may use this request only once, disable it */
type);
}
return ret;
}
/* NOTREACHED */
return (-1);
}
/* allowed key state */
static int
{
/* make sure key is allowed */
return (0);
return (1);
}
static void
monitor_reset_key_state(void)
{
/* reset state */
if (hostbased_cuser != NULL)
if (hostbased_chost != NULL)
key_bloblen = 0;
}
int
{
min = buffer_get_int(m);
want = buffer_get_int(m);
max = buffer_get_int(m);
debug3("%s: got parameters: %d %d %d",
/* We need to check here, too, in case the child got corrupted */
fatal("%s: bad parameters: %d %d %d",
buffer_clear(m);
buffer_put_char(m, 0);
return (0);
} else {
/* Send first bignum */
buffer_put_char(m, 1);
buffer_put_bignum2(m, dh->p);
buffer_put_bignum2(m, dh->g);
}
return (0);
}
int
{
u_char *p;
int keyid;
keyid = buffer_get_int(m);
p = buffer_get_string(m, &datlen);
if (datlen != 20)
/* save session id, it will be passed on the first call */
if (session_id2_len == 0) {
}
buffer_clear(m);
xfree(p);
/* Turn on permissions for getpwnam */
return (0);
}
/* Retrieves the password entry and also checks if the user is permitted */
int
{
char *login;
int allowed = 0;
buffer_clear(m);
buffer_put_char(m, 0);
goto out;
}
allowed = 1;
buffer_put_char(m, 1);
buffer_put_cstring(m, "*");
#ifdef HAVE_PW_CLASS_IN_PASSWD
#endif
out:
/* For SSHv1 allow authentication now */
if (!compat20)
else {
}
#ifdef USE_PAM
#endif
return (0);
}
{
char *banner;
buffer_clear(m);
banner = auth2_read_banner();
return (0);
}
int
{
debug3("%s: service=%s, style=%s",
}
return (0);
}
int
{
static int call_count;
char *passwd;
int authenticated;
/* Only authenticate if the context is valid */
buffer_clear(m);
call_count++;
auth_method = "none";
else
auth_method = "password";
/* Causes monitor loop to terminate if authenticated */
return (authenticated);
}
#ifdef BSD_AUTH
int
{
char **prompts;
int res;
buffer_clear(m);
buffer_put_int(m, res);
if (res != -1)
buffer_put_cstring(m, prompts[0]);
if (res != -1) {
}
return (0);
}
int
{
char *response;
int authok;
buffer_clear(m);
buffer_put_int(m, authok);
auth_method = "bsdauth";
return (authok != 0);
}
#endif
#ifdef SKEY
int
{
char challenge[1024];
int res;
buffer_clear(m);
buffer_put_int(m, res);
if (res != -1)
return (0);
}
int
{
char *response;
int authok;
buffer_clear(m);
buffer_put_int(m, authok);
auth_method = "skey";
return (authok != 0);
}
#endif
#ifdef USE_PAM
int
{
char *user;
return (0);
}
#endif
static void
{
buffer_len(&auth_debug));
}
}
int
{
enum mm_keytype type = 0;
int allowed = 0;
type = buffer_get_int(m);
switch(type) {
case MM_USERKEY:
break;
case MM_HOSTKEY:
break;
case MM_RSAHOSTKEY:
break;
default:
break;
}
}
/* clear temporarily storage (used by verify) */
if (allowed) {
/* Save temporarily for comparison in verify */
key_blobtype = type;
}
debug3("%s: key %p is %s",
buffer_clear(m);
buffer_put_int(m, allowed);
mm_append_debug(m);
if (type == MM_RSAHOSTKEY)
return (0);
}
static int
{
Buffer b;
char *p;
int fail = 0;
buffer_init(&b);
if (datafellows & SSH_OLD_SESSIONID) {
p = buffer_ptr(&b);
len = buffer_len(&b);
if ((session_id2 == NULL) ||
(len < session_id2_len) ||
fail++;
} else {
p = buffer_get_string(&b, &len);
if ((session_id2 == NULL) ||
(len != session_id2_len) ||
fail++;
xfree(p);
}
if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST)
fail++;
p = buffer_get_string(&b, NULL);
log("wrong user name passed to monitor: expected %s != %.100s",
fail++;
}
xfree(p);
buffer_skip_string(&b);
if (datafellows & SSH_BUG_PKAUTH) {
if (!buffer_get_char(&b))
fail++;
} else {
p = buffer_get_string(&b, NULL);
if (strcmp("publickey", p) != 0)
fail++;
xfree(p);
if (!buffer_get_char(&b))
fail++;
buffer_skip_string(&b);
}
buffer_skip_string(&b);
if (buffer_len(&b) != 0)
fail++;
buffer_free(&b);
return (fail == 0);
}
static int
char *chost)
{
Buffer b;
char *p;
int fail = 0;
buffer_init(&b);
p = buffer_get_string(&b, &len);
if ((session_id2 == NULL) ||
(len != session_id2_len) ||
fail++;
xfree(p);
if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST)
fail++;
p = buffer_get_string(&b, NULL);
log("wrong user name passed to monitor: expected %s != %.100s",
fail++;
}
xfree(p);
buffer_skip_string(&b); /* service */
p = buffer_get_string(&b, NULL);
if (strcmp(p, "hostbased") != 0)
fail++;
xfree(p);
buffer_skip_string(&b); /* pkalg */
buffer_skip_string(&b); /* pkblob */
/* verify client host, strip trailing dot if necessary */
p = buffer_get_string(&b, NULL);
fail++;
xfree(p);
/* verify client user */
p = buffer_get_string(&b, NULL);
fail++;
xfree(p);
if (buffer_len(&b) != 0)
fail++;
buffer_free(&b);
return (fail == 0);
}
int
{
int verified = 0;
int valid_data = 0;
switch (key_blobtype) {
case MM_USERKEY:
break;
case MM_HOSTKEY:
break;
default:
valid_data = 0;
break;
}
if (!valid_data)
debug3("%s: key %p signature %s",
buffer_clear(m);
buffer_put_int(m, verified);
return (verified);
}
static void
{
/* Record that there was a login on that tty from the remote host. */
}
static void
{
if (s->ttyfd != -1) {
fatal_remove_cleanup(session_pty_cleanup2, (void *)s);
}
s->used = 0;
}
int
{
Session *s;
buffer_clear(m);
s = session_new();
if (s == NULL)
goto error;
if (res == 0)
goto error;
fatal_add_cleanup(session_pty_cleanup2, (void *)s);
buffer_put_int(m, 1);
buffer_put_cstring(m, s->tty);
/* We need to trick ttyslot */
/* Now we can close the file descriptor again */
close(0);
/* make sure nothing uses fd 0 */
if (fd0 != 0)
/* slave is not needed */
/* no need to dup() because nobody closes ptyfd */
return (0);
if (s != NULL)
mm_session_close(s);
buffer_put_int(m, 0);
return (0);
}
int
{
Session *s;
char *tty;
mm_session_close(s);
buffer_clear(m);
return (0);
}
int
{
BIGNUM *p;
int rsafail;
/* Turn off permissions */
buffer_get_bignum2(m, p);
rsafail = ssh1_session_key(p);
buffer_clear(m);
buffer_put_int(m, rsafail);
buffer_put_bignum2(m, p);
BN_clear_free(p);
/* Turn on permissions for sessid passing */
return (0);
}
int
{
int i;
if (buffer_len(m) != 16)
for (i = 0; i < 16; i++)
session_id[i] = buffer_get_char(m);
/* Turn on permissions for getpwnam */
return (0);
}
int
{
int allowed = 0;
}
buffer_clear(m);
buffer_put_int(m, allowed);
/* clear temporarily storage (used by generate challenge) */
/* Save temporarily for comparison in verify */
key_bloblen = blen;
}
mm_append_debug(m);
return (0);
}
int
{
if (ssh1_challenge)
buffer_clear(m);
return (0);
}
int
{
int success;
if (ssh1_challenge == NULL)
if (len != 16)
/* reset state */
buffer_clear(m);
buffer_put_int(m, success);
return (success);
}
#ifdef KRB4
int
{
char *client, *p;
int success;
p = buffer_get_string(m, &alen);
if (alen >= MAX_KTXT_LEN)
xfree(p);
buffer_clear(m);
buffer_put_int(m, success);
if (success) {
buffer_put_cstring(m, client);
if (client)
}
auth_method = "kerberos";
/* Causes monitor loop to terminate if authenticated */
return (success);
}
#endif
#ifdef KRB5
int
{
char *client_user;
int success;
/* use temporary var to avoid size issues on 64bit arch */
buffer_clear(m);
buffer_put_int(m, success);
if (success) {
if (client_user)
}
return success;
}
#endif
int
{
/* The child is terminating */
exit(1);
/* Terminate process */
return (res);
/* NOTREACHED */
}
void
{
if (compat20) {
} else {
}
/* for rc4 and other stateful ciphers */
if (!compat20) {
}
sizeof(incoming_stream));
sizeof(outgoing_stream));
/* Update with new address */
if (options.compression)
/* Network I/O buffers */
/* XXX inefficient for large buffers, need: buffer_init_from_string */
}
static Kex *
mm_get_kex(Buffer *m)
{
void *blob;
if ((session_id2 == NULL) ||
fatal("mm_get_get: internal error: bad session id");
#ifdef GSSAPI
#endif
return (kex);
}
/* This function requries careful sanity checking */
void
{
Buffer m;
buffer_init(&m);
if (!compat20) {
goto skip;
} else {
/* Get the Kex for rekeying */
}
/* Now get sequence numbers for the packets */
skip:
/* Get the key context */
/* Get compression state */
p = buffer_get_string(&m, &plen);
xfree(p);
p = buffer_get_string(&m, &plen);
xfree(p);
/* Network I/O buffers */
buffer_free(&m);
}
/* Allocation functions for zlib */
void *
{
void *address;
return (address);
}
void
{
}
void
{
}
/* XXX */
#define FD_CLOSEONEXEC(x) do { \
fatal("fcntl(%d, F_SETFD)", x); \
} while (0)
static void
monitor_socketpair(int *pair)
{
#ifdef HAVE_SOCKETPAIR
#else
fatal("%s: UsePrivilegeSeparation=yes not supported",
__func__);
#endif
FD_CLOSEONEXEC(pair[0]);
}
#define MM_MEMSIZE 65536
struct monitor *
monitor_init(void)
{
int pair[2];
/* Used to share zlib space across processes */
if (options.compression) {
/* Compression needs to share state across borders */
}
return mon;
}
void
{
int pair[2];
}
#ifdef GSSAPI
int
/* XXX */
buffer_clear(m);
buffer_put_int(m,major);
/* Now we have a context, enable the step and sign */
return(0);
}
int
buffer_clear(m);
buffer_put_int(m, major);
buffer_put_int(m, flags);
/* Complete - now we can do signing */
if (major==GSS_S_COMPLETE) {
}
return(0);
}
int
int authenticated;
buffer_clear(m);
/* XXX - auth method could also be 'external' */
auth_method="gssapi";
/* Monitor loop will terminate if authenticated */
return(authenticated);
}
int
/* Save the session ID - only first time round */
if (session_id2_len == 0) {
}
buffer_clear(m);
buffer_put_int(m, major);
/* Turn on permissions for getpwnam */
return(0);
}
int
char *msg;
buffer_clear(m);
buffer_put_int(m,major);
buffer_put_int(m,minor);
return(0);
}
int
int i;
buffer_clear(m);
buffer_put_int(m, major);
}
return(0);
}
int
char *name;
buffer_clear(m);
if (name) {
buffer_put_cstring(m, name);
} else {
buffer_put_cstring(m, "");
}
return(0);
}
#endif /* GSSAPI */
#endif /* lint */