From 646e7cf3d7e7d4231c2d97d27c09fe5fe1d749e2 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Wed, 13 Mar 2002 12:47:54 +1100 Subject: Import of Niels Provos' 20020312 ssh-complete.diff PAM, Cygwin and OSF SIA will not work for sure --- monitor.c | 656 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 656 insertions(+) create mode 100644 monitor.c (limited to 'monitor.c') diff --git a/monitor.c b/monitor.c new file mode 100644 index 000000000..b85790271 --- /dev/null +++ b/monitor.c @@ -0,0 +1,656 @@ +/* + * Copyright 2001 Niels Provos + * 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 + * documentation and/or other materials provided with the distribution. + * + * 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. + */ + +#include "includes.h" +RCSID("$OpenBSD$"); + +#include + +#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 "log.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" + +/* Imports */ +extern Newkeys *current_keys[]; +extern z_stream incoming_stream; +extern z_stream outgoing_stream; +extern int compat20; +extern int mm_sendfd; + +/* State exported from the child */ + +struct { + z_stream incoming; + z_stream outgoing; + u_char *keyin; + u_int keyinlen; + u_char *keyout; + u_int keyoutlen; +} child_state; + +/* Prototype for authentication functions */ + +int hostbased_key_allowed(struct passwd *, const char *, char *, Key *); +int user_key_allowed(struct passwd *, Key *); +Key *get_hostkey_by_index(int); + +void session_pty_cleanup(void *); + +static Authctxt *authctxt; + +struct mon_table { + enum monitor_reqtype type; + int flags; + int (*f)(int, Buffer *); +}; + +#define MON_PROTOONE 0x0001 /* Used in protocol 1 */ +#define MON_PROTOTWO 0x0002 /* Used in protocol 2 */ +#define MON_AUTH 0x0004 /* Authentication Request */ + +#define MON_BOTH (MON_PROTOONE|MON_PROTOTWO) + +#define MON_PERMIT 0x1000 /* Request is permitted */ + +struct mon_table mon_dispatch_proto20[] = { + {MONITOR_REQ_MODULI, MON_PROTOTWO, mm_answer_moduli}, + {MONITOR_REQ_SIGN, MON_PROTOTWO, mm_answer_sign}, + {MONITOR_REQ_PWNAM, MON_BOTH, mm_answer_pwnamallow}, + {MONITOR_REQ_AUTHSERV, MON_BOTH, mm_answer_authserv}, + {MONITOR_REQ_AUTHPASSWORD, MON_BOTH | MON_AUTH, mm_answer_authpassword}, + {MONITOR_REQ_KEYALLOWED, MON_BOTH | MON_AUTH, mm_answer_keyallowed}, + {MONITOR_REQ_KEYVERIFY, MON_BOTH | MON_AUTH, mm_answer_keyverify}, + {0, 0, NULL} +}; + +struct mon_table mon_dispatch_postauth20[] = { + {MONITOR_REQ_MODULI, MON_PROTOTWO, mm_answer_moduli}, + {MONITOR_REQ_SIGN, MON_PROTOTWO, mm_answer_sign}, + {MONITOR_REQ_PTY, MON_BOTH, mm_answer_pty}, + {MONITOR_REQ_TERM, MON_BOTH, mm_answer_term}, + {0, 0, NULL} +}; + +struct mon_table mon_dispatch_proto15[] = { + {0, 0, NULL} +}; + +struct mon_table *mon_dispatch; + +/* Specifies if a certain message is allowed at the moment */ + +void +monitor_permit(struct mon_table *ent, enum monitor_reqtype type, int permit) +{ + while (ent->f != NULL) { + if (ent->type == type) { + ent->flags &= ~MON_PERMIT; + ent->flags |= permit ? MON_PERMIT : 0; + return; + } + ent++; + } +} + +void +monitor_permit_authentications(int permit) +{ + struct mon_table *ent = mon_dispatch; + + while (ent->f != NULL) { + if (ent->flags & MON_AUTH) { + ent->flags &= ~MON_PERMIT; + ent->flags |= permit ? MON_PERMIT : 0; + } + ent++; + } +} + +#define FD_CLOSEONEXEC(x) do { \ + if (fcntl(x, F_SETFD, 1) == -1) \ + fatal("fcntl(%d, F_SETFD)", x); \ +} while (0) + +void +monitor_socketpair(int *pair) +{ + if (socketpair(PF_LOCAL, SOCK_STREAM, 0, pair) == -1) + fatal("%s: socketpair", __FUNCTION__); + FD_CLOSEONEXEC(pair[0]); + FD_CLOSEONEXEC(pair[1]); +} + +Authctxt * +monitor_child_preauth(int socket) +{ + debug3("preauth child monitor started"); + + if (compat20) { + mon_dispatch = mon_dispatch_proto20; + + /* Permit requests for moduli and signatures */ + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); + } else + mon_dispatch = mon_dispatch_proto15; + + authctxt = authctxt_new(); + + /* The first few requests do not require asynchronous access */ + for (;;) { + if (monitor_read(socket, mon_dispatch)) + break; + } + + debug("%s: %s has been authenticated by privileged process", + __FUNCTION__, authctxt->user); + + if (compat20) { + mm_get_keystate(socket); + } else { + fatal("Use loose"); + } + + return (authctxt); +} + +void +monitor_child_postauth(int socket) +{ + if (compat20) { + mon_dispatch = mon_dispatch_postauth20; + + /* Permit requests for moduli and signatures */ + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); + + if (!no_pty_flag) + monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); + } else + mon_dispatch = mon_dispatch_proto15; + + for (;;) { + if (monitor_read(socket, mon_dispatch)) + break; + } +} + +int +monitor_read(int socket, struct mon_table *ent) +{ + Buffer m; + int ret; + u_char type; + + buffer_init(&m); + + mm_request_receive(socket, &m); + type = buffer_get_char(&m); + + debug3("%s: checking request %d", __FUNCTION__, type); + + while (ent->f != NULL) { + if (ent->type == type) + break; + ent++; + } + + if (ent->f != NULL) { + if (!(ent->flags & MON_PERMIT)) + fatal("%s: unpermitted request %d", __FUNCTION__, + type); + ret = (*ent->f)(socket, &m); + buffer_free(&m); + return ret; + } + + fatal("%s: unsupported request: %d\n", __FUNCTION__, type); + + /* NOTREACHED */ + return (-1); +} + +int +mm_answer_moduli(int socket, Buffer *m) +{ + DH *dh; + int min, want, max; + + /* Turn off requests for moduli */ + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 0); + + min = buffer_get_int(m); + want = buffer_get_int(m); + max = buffer_get_int(m); + + debug3("%s: got parameters: %d %d %d", + __FUNCTION__, min, want, max); + /* We need to check here, too, in case the child got corrupted */ + if (max < min || want < min || max < want) + fatal("%s: bad parameters: %d %d %d", + __FUNCTION__, min, want, max); + + buffer_clear(m); + + dh = choose_dh(min, want, max); + if (dh == NULL) { + 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); + + DH_free(dh); + } + mm_request_send(socket, MONITOR_ANS_MODULI, m); + return (0); +} + +int +mm_answer_sign(int socket, Buffer *m) +{ + Key *key; + u_char *p; + u_char *signature; + u_int siglen, datlen; + int keyid; + + debug3("%s", __FUNCTION__); + + keyid = buffer_get_int(m); + p = buffer_get_string(m, &datlen); + + if ((key = get_hostkey_by_index(keyid)) == NULL) + fatal("%s: no hostkey from index %d", __FUNCTION__, keyid); + if (key_sign(key, &signature, &siglen, p, datlen) < 0) + fatal("%s: key_sign failed", __FUNCTION__); + + debug3("%s: signature %p(%d)", __FUNCTION__, signature, siglen); + + buffer_clear(m); + buffer_put_string(m, signature, siglen); + + xfree(p); + xfree(signature); + + /* Turn on permissions for getpwnam */ + monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); + + mm_request_send(socket, MONITOR_ANS_SIGN, m); + return (0); +} + +/* Retrieves the password entry and also checks if the user is permitted */ + +int +mm_answer_pwnamallow(int socket, Buffer *m) +{ + char *login; + struct passwd *pwent; + int allowed; + + debug3("%s", __FUNCTION__); + + if (authctxt->attempt++ != 0) + fatal("%s: multiple attempts for getpwnam", __FUNCTION__); + + login = buffer_get_string(m, NULL); + + /* XXX - probably latch the username here */ + pwent = getpwnam(login); + authctxt->user = xstrdup(login); + setproctitle("%s [priv]", pwent ? login : "unknown"); + xfree(login); + + /* Allow service/style information on the auth context */ + monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1); + + buffer_clear(m); + + if (pwent == NULL) { + buffer_put_char(m, 0); + mm_request_send(socket, MONITOR_ANS_PWNAM, m); + return (0); + } + + /* Check if we permit this user */ + allowed = allowed_user(pwent); + + if (allowed) { + authctxt->pw = pwcopy(pwent); + authctxt->valid = 1; + } + buffer_put_char(m, allowed); + buffer_put_string(m, pwent, sizeof(struct passwd)); + buffer_put_cstring(m, pwent->pw_name); + buffer_put_cstring(m, "*"); + buffer_put_cstring(m, pwent->pw_gecos); + buffer_put_cstring(m, pwent->pw_class); + buffer_put_cstring(m, pwent->pw_dir); + buffer_put_cstring(m, pwent->pw_shell); + + debug3("%s: sending MONITOR_ANS_PWNAM: %d", __FUNCTION__, allowed); + mm_request_send(socket, MONITOR_ANS_PWNAM, m); + + return (0); +} + +int +mm_answer_authserv(int socket, Buffer *m) +{ + /* Disallow service/style information on the auth context */ + monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 0); + + monitor_permit_authentications(1); + + authctxt->service = buffer_get_string(m, NULL); + authctxt->style = buffer_get_string(m, NULL); + if (strlen(authctxt->style) == 0) { + xfree(authctxt->style); + authctxt->style = NULL; + } + + debug3("%s: service=%s, style=%s", + __FUNCTION__, authctxt->service, authctxt->style); + + return (0); +} + +int +mm_answer_authpassword(int socket, Buffer *m) +{ + char *passwd; + int authenticated; + + passwd = buffer_get_string(m, NULL); + /* Only authenticate if the context is valid */ + authenticated = authctxt->valid && auth_password(authctxt, passwd); + memset(passwd, 0, strlen(passwd)); + xfree(passwd); + + buffer_clear(m); + buffer_put_int(m, authenticated); + + debug3("%s: sending result %d", __FUNCTION__, authenticated); + mm_request_send(socket, MONITOR_ANS_AUTHPASSWORD, m); + + /* Causes monitor loop to terminate if authenticated */ + return (authenticated); +} + +int +mm_answer_keyallowed(int socket, Buffer *m) +{ + Key *key; + u_char *cuser, *chost, *blob; + u_int bloblen; + enum mm_keytype type = 0; + int allowed = 0; + + debug3("%s entering", __FUNCTION__); + + type = buffer_get_int(m); + cuser = buffer_get_string(m, NULL); + chost = buffer_get_string(m, NULL); + blob = buffer_get_string(m, &bloblen); + + key = key_from_blob(blob, bloblen); + + debug3("%s: key_from_blob: %p", __FUNCTION__, key); + + if (key != NULL && authctxt->pw != NULL) { + switch(type) { + case MM_USERKEY: + allowed = user_key_allowed(authctxt->pw, key); + break; + case MM_HOSTKEY: + allowed = hostbased_key_allowed(authctxt->pw, + cuser, chost, key); + break; + default: + fatal("%s: unknown key type %d", __FUNCTION__, + type); + break; + } + key_free(key); + } + xfree(chost); + xfree(cuser); + xfree(blob); + + debug3("%s: key %p is %s", + __FUNCTION__, key, allowed ? "allowed" : "disallowed"); + + buffer_clear(m); + buffer_put_int(m, allowed); + + mm_request_send(socket, MONITOR_ANS_KEYALLOWED, m); + return (0); +} + +int +mm_answer_keyverify(int socket, Buffer *m) +{ + Key *key; + u_char *signature, *data, *cuser, *chost, *blob; + u_int signaturelen, datalen, bloblen; + int type; + int verified = 0; + + type = buffer_get_int(m); + cuser = buffer_get_string(m, NULL); + chost = buffer_get_string(m, NULL); + blob = buffer_get_string(m, &bloblen); + signature = buffer_get_string(m, &signaturelen); + data = buffer_get_string(m, &datalen); + + key = key_from_blob(blob, bloblen); + if (key == NULL) + fatal("%s: bad public key blob", __FUNCTION__); + + if (authctxt->pw == NULL || !user_key_allowed(authctxt->pw, key)) + fatal("%s: user not allowed", __FUNCTION__); + verified = key_verify(key, signature, signaturelen, data, datalen); + debug3("%s: key %p signature %s", + __FUNCTION__, key, verified ? "verified" : "unverified"); + + key_free(key); + xfree(chost); + xfree(cuser); + xfree(blob); + xfree(signature); + xfree(data); + + buffer_clear(m); + buffer_put_int(m, verified); + mm_request_send(socket, MONITOR_ANS_KEYVERIFY, m); + + return (verified); +} + +int +mm_answer_pty(int socket, Buffer *m) +{ + Session *s; + int res; + + debug3("%s entering", __FUNCTION__); + + buffer_clear(m); + s = session_new(); + if (s == NULL) + goto error; + s->authctxt = authctxt; + s->pw = authctxt->pw; + res = pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)); + if (res == 0) + goto error; + fatal_add_cleanup(session_pty_cleanup, (void *)s); + pty_setowner(authctxt->pw, s->tty); + + buffer_put_int(m, 1); + buffer_put_cstring(m, s->tty); + mm_request_send(socket, MONITOR_ANS_PTY, m); + + mm_send_fd(mm_sendfd, s->ptyfd); + mm_send_fd(mm_sendfd, s->ttyfd); + return (0); + + error: + if (s != NULL) + session_close(s); + buffer_put_int(m, 0); + mm_request_send(socket, MONITOR_ANS_PTY, m); + return (0); +} + +int +mm_answer_term(int socket, Buffer *req) +{ + debug3("%s: tearing down sessions", __FUNCTION__); + + /* The child is terminating */ + session_destroy_all(); + + return (1); +} + +void +mm_apply_keystate(struct mm_master *mm) +{ + /* XXX - delegate to child? */ + set_newkeys(MODE_IN); + set_newkeys(MODE_OUT); + + packet_set_keycontext(MODE_OUT, child_state.keyout); + xfree(child_state.keyout); + packet_set_keycontext(MODE_IN, child_state.keyin); + xfree(child_state.keyin); + + memcpy(&incoming_stream, &child_state.incoming, + sizeof(incoming_stream)); + memcpy(&outgoing_stream, &child_state.outgoing, + sizeof(outgoing_stream)); + + /* Update with new address */ + mm_init_compression(mm); +} + +/* This function requries careful sanity checking */ + +void +mm_get_keystate(int socket) +{ + Buffer m; + u_char *blob, *p; + u_int bloblen, plen; + + debug3("%s: Waiting for new keys", __FUNCTION__); + + buffer_init(&m); + mm_request_receive_expect(socket, MONITOR_REQ_KEYEXPORT, &m); + + blob = buffer_get_string(&m, &bloblen); + current_keys[MODE_OUT] = mm_newkeys_from_blob(blob, bloblen); + xfree(blob); + + debug3("%s: Waiting for second key", __FUNCTION__); + blob = buffer_get_string(&m, &bloblen); + current_keys[MODE_IN] = mm_newkeys_from_blob(blob, bloblen); + xfree(blob); + + /* Now get sequence numbers for the packets */ + packet_set_seqnr(MODE_OUT, buffer_get_int(&m)); + packet_set_seqnr(MODE_IN, buffer_get_int(&m)); + + /* Get the key context */ + child_state.keyout = buffer_get_string(&m, &child_state.keyoutlen); + child_state.keyin = buffer_get_string(&m, &child_state.keyinlen); + + debug3("%s: Getting compression state", __FUNCTION__); + /* Get compression state */ + p = buffer_get_string(&m, &plen); + if (plen != sizeof(child_state.outgoing)) + fatal("%s: bad request size", __FUNCTION__); + memcpy(&child_state.outgoing, p, sizeof(child_state.outgoing)); + xfree(p); + + p = buffer_get_string(&m, &plen); + if (plen != sizeof(child_state.incoming)) + fatal("%s: bad request size", __FUNCTION__); + memcpy(&child_state.incoming, p, sizeof(child_state.incoming)); + xfree(p); + + buffer_free(&m); +} + + +/* Allocation functions for zlib */ +void * +mm_zalloc(struct mm_master *mm, u_int ncount, u_int size) +{ + void *address; + + address = mm_malloc(mm, size * ncount); + + return (address); +} + +void +mm_zfree(struct mm_master *mm, void *address) +{ + mm_free(mm, address); +} + +void +mm_init_compression(struct mm_master *mm) +{ + outgoing_stream.zalloc = (alloc_func)mm_zalloc; + outgoing_stream.zfree = (free_func)mm_zfree; + outgoing_stream.opaque = mm; + + incoming_stream.zalloc = (alloc_func)mm_zalloc; + incoming_stream.zfree = (free_func)mm_zfree; + incoming_stream.opaque = mm; +} -- cgit v1.2.3