diff options
author | Niels Provos <provos@cvs.openbsd.org> | 2002-03-18 17:28:38 +0000 |
---|---|---|
committer | Niels Provos <provos@cvs.openbsd.org> | 2002-03-18 17:28:38 +0000 |
commit | 8aedc22af7fcf8d8a7e340edd4002d6186e8ecbd (patch) | |
tree | a5cd7f1e76d803abf97d875220b026784f4c7c6f /usr.bin | |
parent | ebe979f04c2fde2d7315912f07c85508629cf324 (diff) |
implementation of the interface between privileged and unprivileged process
for ssh-privsep
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/ssh/monitor.c | 1464 | ||||
-rw-r--r-- | usr.bin/ssh/monitor.h | 73 | ||||
-rw-r--r-- | usr.bin/ssh/monitor_wrap.c | 906 | ||||
-rw-r--r-- | usr.bin/ssh/monitor_wrap.h | 87 |
4 files changed, 2530 insertions, 0 deletions
diff --git a/usr.bin/ssh/monitor.c b/usr.bin/ssh/monitor.c new file mode 100644 index 00000000000..4b41da528b6 --- /dev/null +++ b/usr.bin/ssh/monitor.c @@ -0,0 +1,1464 @@ +/* + * 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 + * 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: monitor.c,v 1.1 2002/03/18 17:28:37 provos Exp $"); + +#include <openssl/dh.h> + +#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" + +/* Imports */ +extern ServerOptions options; +extern u_int utmp_len; +extern Newkeys *current_keys[]; +extern z_stream incoming_stream; +extern z_stream outgoing_stream; +extern u_char session_id[]; +extern Buffer input, output; +extern Buffer auth_debug; +extern int auth_debug_init; + +/* State exported from the child */ + +struct { + z_stream incoming; + z_stream outgoing; + u_char *keyin; + u_int keyinlen; + u_char *keyout; + u_int keyoutlen; + u_char *ivin; + u_int ivinlen; + u_char *ivout; + u_int ivoutlen; + int ssh1cipher; + int ssh1protoflags; + u_char *input; + u_int ilen; + u_char *output; + u_int olen; +} child_state; + +/* Prototypes for request sending and receiving */ +void mm_request_send(int, enum monitor_reqtype, Buffer *); +void mm_request_receive(int, Buffer *); +void mm_request_receive_expect(int, enum monitor_reqtype, Buffer *); + +/* Prototypes for authentication functions */ +int bsdauth_query(void *, char **, char **, u_int *, char ***, u_int **); +int bsdauth_respond(void *, u_int, char **); +int skey_query(void *, char **, char **, u_int *, char ***, u_int **); +int skey_respond(void *, u_int, char **); + +int auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, Key *); +int hostbased_key_allowed(struct passwd *, const char *, char *, Key *); +int user_key_allowed(struct passwd *, Key *); +Key *get_hostkey_by_index(int); +Key *get_hostkey_by_type(int); +int get_hostkey_index(Key *); +int ssh1_session_key(BIGNUM *); +int auth_rsa_key_allowed(struct passwd *, BIGNUM *, Key **); +int auth_rsa_verify_response(Key *, BIGNUM *, u_char *); +BIGNUM *auth_rsa_generate_challenge(Key *); + +void session_pty_cleanup2(void *); +Session *session_by_tty(char *); + +/* 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_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 *); + +static Authctxt *authctxt; +static BIGNUM *ssh1_challenge = NULL; /* used for ssh1 rsa auth */ + +/* local state for key verify */ +static u_char *key_blob = NULL; +static u_int key_bloblen = 0; +static int key_blobtype = MM_NOKEY; +static u_char *hostbased_cuser = NULL; +static u_char *hostbased_chost = NULL; +static char *auth_method = "unknown"; + +struct mon_table { + enum monitor_reqtype type; + int flags; + int (*f)(int, Buffer *); +}; + +#define MON_ISAUTH 0x0004 /* Required for Authentication */ +#define MON_AUTHDECIDE 0x0008 /* Decides Authentication */ +#define MON_ONCE 0x0010 /* Disable after calling */ + +#define MON_AUTH (MON_ISAUTH|MON_AUTHDECIDE) + +#define MON_PERMIT 0x1000 /* Request is permitted */ + +struct mon_table mon_dispatch_proto20[] = { + {MONITOR_REQ_MODULI, MON_ONCE, mm_answer_moduli}, + {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, + {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, + {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv}, + {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, +#ifdef BSD_AUTH + {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, + {MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH,mm_answer_bsdauthrespond}, +#endif +#ifdef SKEY + {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery}, + {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond}, +#endif + {MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed}, + {MONITOR_REQ_KEYVERIFY, MON_AUTH, mm_answer_keyverify}, + {0, 0, NULL} +}; + +struct mon_table mon_dispatch_postauth20[] = { + {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, + {MONITOR_REQ_SIGN, 0, mm_answer_sign}, + {MONITOR_REQ_PTY, 0, mm_answer_pty}, + {MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup}, + {MONITOR_REQ_TERM, 0, mm_answer_term}, + {0, 0, NULL} +}; + +struct mon_table mon_dispatch_proto15[] = { + {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, + {MONITOR_REQ_SESSKEY, MON_ONCE, mm_answer_sesskey}, + {MONITOR_REQ_SESSID, MON_ONCE, mm_answer_sessid}, + {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, + {MONITOR_REQ_RSAKEYALLOWED, MON_ISAUTH, mm_answer_rsa_keyallowed}, + {MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed}, + {MONITOR_REQ_RSACHALLENGE, MON_ONCE, mm_answer_rsa_challenge}, + {MONITOR_REQ_RSARESPONSE, MON_ONCE|MON_AUTHDECIDE, mm_answer_rsa_response}, +#ifdef BSD_AUTH + {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, + {MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH,mm_answer_bsdauthrespond}, +#endif +#ifdef SKEY + {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery}, + {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond}, +#endif + {0, 0, NULL} +}; + +struct mon_table mon_dispatch_postauth15[] = { + {MONITOR_REQ_PTY, MON_ONCE, mm_answer_pty}, + {MONITOR_REQ_PTYCLEANUP, MON_ONCE, mm_answer_pty_cleanup}, + {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 +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++; + } +} + +static 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++; + } +} + +Authctxt * +monitor_child_preauth(struct monitor *monitor) +{ + struct mon_table *ent; + int authenticated = 0; + + 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; + + monitor_permit(mon_dispatch, MONITOR_REQ_SESSKEY, 1); + } + + authctxt = authctxt_new(); + + /* The first few requests do not require asynchronous access */ + while (!authenticated) { + authenticated = monitor_read(monitor, mon_dispatch, &ent); + if (authenticated) { + if (!(ent->flags & MON_AUTHDECIDE)) + fatal("%s: unexpected authentication from %d", + __FUNCTION__, ent->type); + if (authctxt->pw->pw_uid == 0 && + !auth_root_allowed(auth_method)) + authenticated = 0; + } + + if (ent->flags & MON_AUTHDECIDE) { + auth_log(authctxt, authenticated, auth_method, + compat20 ? " ssh2" : ""); + if (!authenticated) + authctxt->failures++; + } + } + + if (!authctxt->valid) + fatal("%s: authenticated invalid user", __FUNCTION__); + + debug("%s: %s has been authenticated by privileged process", + __FUNCTION__, authctxt->user); + + mm_get_keystate(monitor); + + return (authctxt); +} + +void +monitor_child_postauth(struct monitor *monitor) +{ + 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); + + } else { + mon_dispatch = mon_dispatch_postauth15; + monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); + } + if (!no_pty_flag) { + monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_PTYCLEANUP, 1); + } + + for (;;) + monitor_read(monitor, mon_dispatch, NULL); +} + +void +monitor_sync(struct monitor *monitor) +{ + /* The member allocation is not visible, so sync it */ + mm_share_sync(&monitor->m_zlib, &monitor->m_zback); +} + +int +monitor_read(struct monitor *monitor, struct mon_table *ent, + struct mon_table **pent) +{ + Buffer m; + int ret; + u_char type; + + buffer_init(&m); + + mm_request_receive(monitor->m_sendfd, &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)(monitor->m_sendfd, &m); + buffer_free(&m); + + /* The child may use this request only once, disable it */ + if (ent->flags & MON_ONCE) { + debug2("%s: %d used once, disabling now", __FUNCTION__, + type); + ent->flags &= ~MON_PERMIT; + } + + if (pent != NULL) + *pent = ent; + + return ret; + } + + fatal("%s: unsupported request: %d\n", __FUNCTION__, type); + + /* NOTREACHED */ + return (-1); +} + +/* allowed key state */ +static int +monitor_allowed_key(u_char *blob, u_int bloblen) +{ + /* make sure key is allowed */ + if (key_blob == NULL || key_bloblen != bloblen || + memcmp(key_blob, blob, key_bloblen)) + return (0); + return (1); +} + +static void +monitor_reset_key_state(void) +{ + /* reset state */ + if (key_blob != NULL) + xfree(key_blob); + if (hostbased_cuser != NULL) + xfree(hostbased_cuser); + if (hostbased_chost != NULL) + xfree(hostbased_chost); + key_blob = NULL; + key_bloblen = 0; + key_blobtype = MM_NOKEY; + hostbased_cuser = NULL; + hostbased_chost = NULL; +} + +int +mm_answer_moduli(int socket, Buffer *m) +{ + DH *dh; + int min, want, max; + + 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 (datlen != 20) + fatal("%s: data length incorrect: %d", __FUNCTION__, 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); + + mm_request_send(socket, MONITOR_ANS_SIGN, m); + + /* Turn on permissions for getpwnam */ + monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); + + 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 = 0; + + debug3("%s", __FUNCTION__); + + if (authctxt->attempt++ != 0) + fatal("%s: multiple attempts for getpwnam", __FUNCTION__); + + login = buffer_get_string(m, NULL); + + pwent = getpwnamallow(login); + + authctxt->user = xstrdup(login); + setproctitle("%s [priv]", pwent ? login : "unknown"); + xfree(login); + + buffer_clear(m); + + if (pwent == NULL) { + buffer_put_char(m, 0); + goto out; + } + + allowed = 1; + authctxt->pw = pwcopy(pwent); + authctxt->valid = 1; + + buffer_put_char(m, 1); + 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); + + out: + debug3("%s: sending MONITOR_ANS_PWNAM: %d", __FUNCTION__, allowed); + mm_request_send(socket, MONITOR_ANS_PWNAM, m); + + /* For SSHv1 allow authentication now */ + if (!compat20) + monitor_permit_authentications(1); + else + /* Allow service/style information on the auth context */ + monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1); + + + return (0); +} + +int +mm_answer_authserv(int socket, Buffer *m) +{ + 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) +{ + static int call_count; + char *passwd; + int authenticated, plen; + + passwd = buffer_get_string(m, &plen); + /* 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); + + call_count++; + if (plen == 0 && call_count == 1) + auth_method = "none"; + else + auth_method = "password"; + + /* Causes monitor loop to terminate if authenticated */ + return (authenticated); +} + +#ifdef BSD_AUTH +int +mm_answer_bsdauthquery(int socket, Buffer *m) +{ + char *name, *infotxt; + u_int numprompts; + u_int *echo_on; + char **prompts; + int res; + + res = bsdauth_query(authctxt, &name, &infotxt, &numprompts, + &prompts, &echo_on); + + buffer_clear(m); + buffer_put_int(m, res); + if (res != -1) + buffer_put_cstring(m, prompts[0]); + + debug3("%s: sending challenge res: %d", __FUNCTION__, res); + mm_request_send(socket, MONITOR_ANS_BSDAUTHQUERY, m); + + if (res != -1) { + xfree(name); + xfree(infotxt); + xfree(prompts); + xfree(echo_on); + } + + return (0); +} + +int +mm_answer_bsdauthrespond(int socket, Buffer *m) +{ + char *response; + int authok; + + if (authctxt->as == 0) + fatal("%s: no bsd auth session", __FUNCTION__); + + response = buffer_get_string(m, NULL); + authok = auth_userresponse(authctxt->as, response, 0); + authctxt->as = NULL; + debug3("%s: <%s> = <%d>", __FUNCTION__, response, authok); + xfree(response); + + buffer_clear(m); + buffer_put_int(m, authok); + + debug3("%s: sending authenticated: %d", __FUNCTION__, authok); + mm_request_send(socket, MONITOR_ANS_BSDAUTHRESPOND, m); + + auth_method = "bsdauth"; + + return (authok != 0); +} +#endif + +#ifdef SKEY +int +mm_answer_skeyquery(int socket, Buffer *m) +{ + struct skey skey; + char challenge[1024]; + int res; + + res = skeychallenge(&skey, authctxt->user, challenge); + + buffer_clear(m); + buffer_put_int(m, res); + if (res != -1) + buffer_put_cstring(m, challenge); + + debug3("%s: sending challenge res: %d", __FUNCTION__, res); + mm_request_send(socket, MONITOR_ANS_SKEYQUERY, m); + + return (0); +} + +int +mm_answer_skeyrespond(int socket, Buffer *m) +{ + char *response; + int authok; + + response = buffer_get_string(m, NULL); + + authok = (authctxt->valid && + skey_haskey(authctxt->pw->pw_name) == 0 && + skey_passcheck(authctxt->pw->pw_name, response) != -1); + + xfree(response); + + buffer_clear(m); + buffer_put_int(m, authok); + + debug3("%s: sending authenticated: %d", __FUNCTION__, authok); + mm_request_send(socket, MONITOR_ANS_SKEYRESPOND, m); + + auth_method = "skey"; + + return (authok != 0); +} +#endif + +void +mm_append_debug(Buffer *m) +{ + if (auth_debug_init && buffer_len(&auth_debug)) { + debug3("%s: Appending debug messages for child", __FUNCTION__); + buffer_append(m, buffer_ptr(&auth_debug), + buffer_len(&auth_debug)); + buffer_clear(&auth_debug); + } +} + +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); + + if ((compat20 && type == MM_RSAHOSTKEY) || + (!compat20 && type != MM_RSAHOSTKEY)) + fatal("%s: key type and protocol mismatch", __FUNCTION__); + + 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; + case MM_RSAHOSTKEY: + key->type = KEY_RSA1; /* XXX */ + allowed = auth_rhosts_rsa_key_allowed(authctxt->pw, + cuser, chost, key); + break; + default: + fatal("%s: unknown key type %d", __FUNCTION__, type); + break; + } + key_free(key); + } + + /* clear temporarily storage (used by verify) */ + monitor_reset_key_state(); + + if (allowed) { + /* Save temporarily for comparison in verify */ + key_blob = blob; + key_bloblen = bloblen; + key_blobtype = type; + hostbased_cuser = cuser; + hostbased_chost = chost; + } + + debug3("%s: key %p is %s", + __FUNCTION__, key, allowed ? "allowed" : "disallowed"); + + buffer_clear(m); + buffer_put_int(m, allowed); + + mm_append_debug(m); + + mm_request_send(socket, MONITOR_ANS_KEYALLOWED, m); + + if (type == MM_RSAHOSTKEY) + monitor_permit(mon_dispatch, MONITOR_REQ_RSACHALLENGE, allowed); + + return (0); +} + +static int +monitor_valid_userblob(u_char *data, u_int datalen) +{ + Buffer b; + u_char *p; + u_int len; + int fail = 0; + int session_id2_len = 20 /*XXX should get from [net] */; + + buffer_init(&b); + buffer_append(&b, data, datalen); + + if (datafellows & SSH_OLD_SESSIONID) { + buffer_consume(&b, session_id2_len); + } else { + xfree(buffer_get_string(&b, &len)); + if (len != session_id2_len) + fail++; + } + if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST) + fail++; + p = buffer_get_string(&b, NULL); + if (strcmp(authctxt->user, p) != 0) { + log("wrong user name passed to monitor: expected %s != %.100s", + authctxt->user, p); + 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 +monitor_valid_hostbasedblob(u_char *data, u_int datalen, u_char *cuser, + u_char *chost) +{ + Buffer b; + u_char *p; + u_int len; + int fail = 0; + int session_id2_len = 20 /*XXX should get from [net] */; + + buffer_init(&b); + buffer_append(&b, data, datalen); + + xfree(buffer_get_string(&b, &len)); + if (len != session_id2_len) + fail++; + if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST) + fail++; + p = buffer_get_string(&b, NULL); + if (strcmp(authctxt->user, p) != 0) { + log("wrong user name passed to monitor: expected %s != %.100s", + authctxt->user, p); + 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); + if (((len = strlen(p)) > 0) && p[len - 1] == '.') + p[len - 1] = '\0'; + if (strcmp(p, chost) != 0) + fail++; + xfree(p); + + /* verify client user */ + p = buffer_get_string(&b, NULL); + if (strcmp(p, cuser) != 0) + fail++; + xfree(p); + + if (buffer_len(&b) != 0) + fail++; + buffer_free(&b); + return (fail == 0); +} + +int +mm_answer_keyverify(int socket, Buffer *m) +{ + Key *key; + u_char *signature, *data, *blob; + u_int signaturelen, datalen, bloblen; + int verified = 0; + int valid_data = 0; + + blob = buffer_get_string(m, &bloblen); + signature = buffer_get_string(m, &signaturelen); + data = buffer_get_string(m, &datalen); + + if (hostbased_cuser == NULL || hostbased_chost == NULL || + monitor_allowed_key(blob, bloblen) == NULL) + fatal("%s: bad key, not previously allowed", __FUNCTION__); + + key = key_from_blob(blob, bloblen); + if (key == NULL) + fatal("%s: bad public key blob", __FUNCTION__); + + switch (key_blobtype) { + case MM_USERKEY: + valid_data = monitor_valid_userblob(data, datalen); + break; + case MM_HOSTKEY: + valid_data = monitor_valid_hostbasedblob(data, datalen, + hostbased_cuser, hostbased_chost); + break; + default: + valid_data = 0; + break; + } + if (!valid_data) + fatal("%s: bad signature data blob", __FUNCTION__); + + verified = key_verify(key, signature, signaturelen, data, datalen); + debug3("%s: key %p signature %s", + __FUNCTION__, key, verified ? "verified" : "unverified"); + + key_free(key); + xfree(blob); + xfree(signature); + xfree(data); + + monitor_reset_key_state(); + + buffer_clear(m); + buffer_put_int(m, verified); + mm_request_send(socket, MONITOR_ANS_KEYVERIFY, m); + + auth_method = "publickey"; + + return (verified); +} + +void +mm_record_login(Session *s, struct passwd *pw) +{ + socklen_t fromlen; + struct sockaddr_storage from; + + /* + * Get IP address of client. If the connection is not a socket, let + * the address be 0.0.0.0. + */ + memset(&from, 0, sizeof(from)); + if (packet_connection_is_on_socket()) { + fromlen = sizeof(from); + if (getpeername(packet_get_connection_in(), + (struct sockaddr *) & from, &fromlen) < 0) { + debug("getpeername: %.100s", strerror(errno)); + fatal_cleanup(); + } + } + /* Record that there was a login on that tty from the remote host. */ + record_login(s->pid, s->tty, pw->pw_name, pw->pw_uid, + get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping), + (struct sockaddr *)&from); +} + +static void +mm_session_close(Session *s) +{ + debug3("%s: session %d pid %d", __FUNCTION__, s->self, s->pid); + if (s->ttyfd != -1) { + debug3("%s: tty %s ptyfd %d", __FUNCTION__, s->tty, s->ptyfd); + fatal_remove_cleanup(session_pty_cleanup2, (void *)s); + session_pty_cleanup2(s); + } + s->used = 0; +} + +int +mm_answer_pty(int socket, Buffer *m) +{ + extern struct monitor *monitor; + Session *s; + int res, fd0; + + debug3("%s entering", __FUNCTION__); + + buffer_clear(m); + s = session_new(); + if (s == NULL) + goto error; + s->authctxt = authctxt; + s->pw = authctxt->pw; + s->pid = monitor->m_pid; + res = pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)); + if (res == 0) + goto error; + fatal_add_cleanup(session_pty_cleanup2, (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(socket, s->ptyfd); + mm_send_fd(socket, s->ttyfd); + + /* We need to trick ttyslot */ + if (dup2(s->ttyfd, 0) == -1) + fatal("%s: dup2", __FUNCTION__); + + mm_record_login(s, authctxt->pw); + + /* Now we can close the file descriptor again */ + close(0); + + /* make sure nothing uses fd 0 */ + if ((fd0 = open(_PATH_DEVNULL, O_RDONLY)) < 0) + fatal("%s: open(/dev/null): %s", __FUNCTION__, strerror(errno)); + if (fd0 != 0) + error("%s: fd0 %d != 0", __FUNCTION__, fd0); + + /* slave is not needed */ + close(s->ttyfd); + s->ttyfd = s->ptyfd; + /* no need to dup() because nobody closes ptyfd */ + s->ptymaster = s->ptyfd; + + debug3("%s: tty %s ptyfd %d", __FUNCTION__, s->tty, s->ttyfd); + + return (0); + + error: + if (s != NULL) + mm_session_close(s); + buffer_put_int(m, 0); + mm_request_send(socket, MONITOR_ANS_PTY, m); + return (0); +} + +int +mm_answer_pty_cleanup(int socket, Buffer *m) +{ + Session *s; + char *tty; + + debug3("%s entering", __FUNCTION__); + + tty = buffer_get_string(m, NULL); + if ((s = session_by_tty(tty)) != NULL) + mm_session_close(s); + buffer_clear(m); + xfree(tty); + return (0); +} + +int +mm_answer_sesskey(int socket, Buffer *m) +{ + BIGNUM *p; + int rsafail; + + /* Turn off permissions */ + monitor_permit(mon_dispatch, MONITOR_REQ_SESSKEY, 1); + + if ((p = BN_new()) == NULL) + fatal("%s: BN_new", __FUNCTION__); + + 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); + + mm_request_send(socket, MONITOR_ANS_SESSKEY, m); + + /* Turn on permissions for sessid passing */ + monitor_permit(mon_dispatch, MONITOR_REQ_SESSID, 1); + + return (0); +} + +int +mm_answer_sessid(int socket, Buffer *m) +{ + int i; + + debug3("%s entering", __FUNCTION__); + + if (buffer_len(m) != 16) + fatal("%s: bad ssh1 session id", __FUNCTION__); + for (i = 0; i < 16; i++) + session_id[i] = buffer_get_char(m); + + /* Turn on permissions for getpwnam */ + monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); + + return (0); +} + +int +mm_answer_rsa_keyallowed(int socket, Buffer *m) +{ + BIGNUM *client_n; + Key *key = NULL; + u_char *blob = NULL; + u_int blen = 0; + int allowed = 0; + + debug3("%s entering", __FUNCTION__); + + if (authctxt->valid) { + if ((client_n = BN_new()) == NULL) + fatal("%s: BN_new", __FUNCTION__); + buffer_get_bignum2(m, client_n); + allowed = auth_rsa_key_allowed(authctxt->pw, client_n, &key); + BN_clear_free(client_n); + } + buffer_clear(m); + buffer_put_int(m, allowed); + + /* clear temporarily storage (used by generate challenge) */ + monitor_reset_key_state(); + + if (allowed && key != NULL) { + key->type = KEY_RSA; /* cheat for key_to_blob */ + if (key_to_blob(key, &blob, &blen) == 0) + fatal("%s: key_to_blob failed", __FUNCTION__); + buffer_put_string(m, blob, blen); + + /* Save temporarily for comparison in verify */ + key_blob = blob; + key_bloblen = blen; + key_blobtype = MM_RSAUSERKEY; + key_free(key); + } + + mm_append_debug(m); + + mm_request_send(socket, MONITOR_ANS_RSAKEYALLOWED, m); + + monitor_permit(mon_dispatch, MONITOR_REQ_RSACHALLENGE, allowed); + monitor_permit(mon_dispatch, MONITOR_REQ_RSARESPONSE, 0); + return (0); +} + +int +mm_answer_rsa_challenge(int socket, Buffer *m) +{ + Key *key = NULL; + u_char *blob; + u_int blen; + + debug3("%s entering", __FUNCTION__); + + if (!authctxt->valid) + fatal("%s: authctxt not valid", __FUNCTION__); + blob = buffer_get_string(m, &blen); + if (!monitor_allowed_key(blob, blen)) + fatal("%s: bad key, not previously allowed", __FUNCTION__); + if (key_blobtype != MM_RSAUSERKEY && key_blobtype != MM_RSAHOSTKEY) + fatal("%s: key type mismatch", __FUNCTION__); + if ((key = key_from_blob(blob, blen)) == NULL) + fatal("%s: received bad key", __FUNCTION__); + + if (ssh1_challenge) + BN_clear_free(ssh1_challenge); + ssh1_challenge = auth_rsa_generate_challenge(key); + + buffer_clear(m); + buffer_put_bignum2(m, ssh1_challenge); + + debug3("%s sending reply", __FUNCTION__); + mm_request_send(socket, MONITOR_ANS_RSACHALLENGE, m); + + + monitor_permit(mon_dispatch, MONITOR_REQ_RSARESPONSE, 1); + return (0); +} + +int +mm_answer_rsa_response(int socket, Buffer *m) +{ + Key *key = NULL; + u_char *blob, *response; + u_int blen, len; + int success; + + debug3("%s entering", __FUNCTION__); + + if (!authctxt->valid) + fatal("%s: authctxt not valid", __FUNCTION__); + if (ssh1_challenge == NULL) + fatal("%s: no ssh1_challenge", __FUNCTION__); + + blob = buffer_get_string(m, &blen); + if (!monitor_allowed_key(blob, blen)) + fatal("%s: bad key, not previously allowed", __FUNCTION__); + if (key_blobtype != MM_RSAUSERKEY && key_blobtype != MM_RSAHOSTKEY) + fatal("%s: key type mismatch: %d", __FUNCTION__, key_blobtype); + if ((key = key_from_blob(blob, blen)) == NULL) + fatal("%s: received bad key", __FUNCTION__); + response = buffer_get_string(m, &len); + if (len != 16) + fatal("%s: received bad response to challenge", __FUNCTION__); + success = auth_rsa_verify_response(key, ssh1_challenge, response); + + key_free(key); + xfree(response); + + auth_method = key_blobtype == MM_RSAUSERKEY ? "rsa" : "rhosts-rsa"; + + /* reset state */ + BN_clear_free(ssh1_challenge); + ssh1_challenge = NULL; + monitor_reset_key_state(); + + buffer_clear(m); + buffer_put_int(m, success); + mm_request_send(socket, MONITOR_ANS_RSARESPONSE, m); + + return (success); +} + +int +mm_answer_term(int socket, Buffer *req) +{ + extern struct monitor *monitor; + int res, status; + + debug3("%s: tearing down sessions", __FUNCTION__); + + /* The child is terminating */ + session_destroy_all(&mm_session_close); + + if (waitpid(monitor->m_pid, &status, 0) == -1) + exit(1); + + res = WIFEXITED(status) ? WEXITSTATUS(status) : 1; + + /* Terminate process */ + exit (res); +} + +void +monitor_apply_keystate(struct monitor *monitor) +{ + if (compat20) { + set_newkeys(MODE_IN); + set_newkeys(MODE_OUT); + } else { + u_char key[256]; + packet_set_protocol_flags(child_state.ssh1protoflags); + packet_set_encryption_key(key, SSH_SESSION_KEY_LENGTH, + child_state.ssh1cipher); + } + + packet_set_keycontext(MODE_OUT, child_state.keyout); + xfree(child_state.keyout); + packet_set_keycontext(MODE_IN, child_state.keyin); + xfree(child_state.keyin); + + if (!compat20) { + packet_set_iv(MODE_OUT, child_state.ivout); + xfree(child_state.ivout); + packet_set_iv(MODE_IN, child_state.ivin); + xfree(child_state.ivin); + } + + 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(monitor->m_zlib); + + /* Network I/O buffers */ + /* XXX inefficient for large buffers, need: buffer_init_from_string */ + buffer_clear(&input); + buffer_append(&input, child_state.input, child_state.ilen); + memset(child_state.input, 0, child_state.ilen); + xfree(child_state.input); + + buffer_clear(&output); + buffer_append(&output, child_state.output, child_state.olen); + memset(child_state.output, 0, child_state.olen); + xfree(child_state.output); +} + +Kex * +mm_get_kex(Buffer *m) +{ + Kex *kex; + void *blob; + u_int bloblen; + + kex = xmalloc(sizeof(*kex)); + memset(kex, 0, sizeof(*kex)); + kex->session_id = buffer_get_string(m, &kex->session_id_len); + kex->we_need = buffer_get_int(m); + kex->server = 1; + kex->hostkey_type = buffer_get_int(m); + kex->kex_type = buffer_get_int(m); + blob = buffer_get_string(m, &bloblen); + buffer_init(&kex->my); + buffer_append(&kex->my, blob, bloblen); + xfree(blob); + blob = buffer_get_string(m, &bloblen); + buffer_init(&kex->peer); + buffer_append(&kex->peer, blob, bloblen); + xfree(blob); + kex->done = 1; + kex->flags = buffer_get_int(m); + kex->client_version_string = buffer_get_string(m, NULL); + kex->server_version_string = buffer_get_string(m, NULL); + kex->load_host_key=&get_hostkey_by_type; + kex->host_key_index=&get_hostkey_index; + + return (kex); +} + +/* This function requries careful sanity checking */ + +void +mm_get_keystate(struct monitor *monitor) +{ + Buffer m; + u_char *blob, *p; + u_int bloblen, plen; + + debug3("%s: Waiting for new keys", __FUNCTION__); + + buffer_init(&m); + mm_request_receive_expect(monitor->m_sendfd, MONITOR_REQ_KEYEXPORT, &m); + if (!compat20) { + child_state.ssh1protoflags = buffer_get_int(&m); + child_state.ssh1cipher = buffer_get_int(&m); + child_state.ivout = buffer_get_string(&m, + &child_state.ivoutlen); + child_state.ivin = buffer_get_string(&m, &child_state.ivinlen); + goto skip; + } else { + /* Get the Kex for rekeying */ + *monitor->m_pkex = mm_get_kex(&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)); + + skip: + /* 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); + + /* Network I/O buffers */ + debug3("%s: Getting Network I/O buffers", __FUNCTION__); + child_state.input = buffer_get_string(&m, &child_state.ilen); + child_state.output = buffer_get_string(&m, &child_state.olen); + + 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; +} + +/* XXX */ + +#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(AF_UNIX, SOCK_STREAM, 0, pair) == -1) + fatal("%s: socketpair", __FUNCTION__); + FD_CLOSEONEXEC(pair[0]); + FD_CLOSEONEXEC(pair[1]); +} + +#define MM_MEMSIZE 65536 + +struct monitor * +monitor_init(void) +{ + struct monitor *mon; + int pair[2]; + + mon = xmalloc(sizeof(*mon)); + + monitor_socketpair(pair); + + mon->m_recvfd = pair[0]; + mon->m_sendfd = pair[1]; + + /* Used to share zlib space across processes */ + mon->m_zback = mm_create(NULL, MM_MEMSIZE); + mon->m_zlib = mm_create(mon->m_zback, 20 * MM_MEMSIZE); + + /* Compression needs to share state across borders */ + mm_init_compression(mon->m_zlib); + + return mon; +} + +void +monitor_reinit(struct monitor *mon) +{ + int pair[2]; + + monitor_socketpair(pair); + + mon->m_recvfd = pair[0]; + mon->m_sendfd = pair[1]; +} diff --git a/usr.bin/ssh/monitor.h b/usr.bin/ssh/monitor.h new file mode 100644 index 00000000000..3dc05cad186 --- /dev/null +++ b/usr.bin/ssh/monitor.h @@ -0,0 +1,73 @@ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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. + */ + +#ifndef _MONITOR_H_ +#define _MONITOR_H_ + +enum monitor_reqtype { + MONITOR_REQ_MODULI, MONITOR_ANS_MODULI, + MONITOR_REQ_FREE, MONITOR_REQ_AUTHSERV, + MONITOR_REQ_SIGN, MONITOR_ANS_SIGN, + MONITOR_REQ_PWNAM, MONITOR_ANS_PWNAM, + MONITOR_REQ_AUTHPASSWORD, MONITOR_ANS_AUTHPASSWORD, + MONITOR_REQ_BSDAUTHQUERY, MONITOR_ANS_BSDAUTHQUERY, + MONITOR_REQ_BSDAUTHRESPOND, MONITOR_ANS_BSDAUTHRESPOND, + MONITOR_REQ_SKEYQUERY, MONITOR_ANS_SKEYQUERY, + MONITOR_REQ_SKEYRESPOND, MONITOR_ANS_SKEYRESPOND, + MONITOR_REQ_KEYALLOWED, MONITOR_ANS_KEYALLOWED, + MONITOR_REQ_KEYVERIFY, MONITOR_ANS_KEYVERIFY, + MONITOR_REQ_KEYEXPORT, + MONITOR_REQ_PTY, MONITOR_ANS_PTY, + MONITOR_REQ_PTYCLEANUP, + MONITOR_REQ_SESSKEY, MONITOR_ANS_SESSKEY, + MONITOR_REQ_SESSID, + MONITOR_REQ_RSAKEYALLOWED, MONITOR_ANS_RSAKEYALLOWED, + MONITOR_REQ_RSACHALLENGE, MONITOR_ANS_RSACHALLENGE, + MONITOR_REQ_RSARESPONSE, MONITOR_ANS_RSARESPONSE, + MONITOR_REQ_TERM, +}; + +struct mm_master; +struct monitor { + int m_recvfd; + int m_sendfd; + struct mm_master *m_zback; + struct mm_master *m_zlib; + struct Kex **m_pkex; + int m_pid; +}; + +struct monitor *monitor_init(void); +void monitor_reinit(struct monitor *); +void monitor_sync(struct monitor *); + +struct Authctxt; +struct Authctxt *monitor_child_preauth(struct monitor *); +void monitor_child_postauth(struct monitor *); + +struct mon_table; +int monitor_read(struct monitor*, struct mon_table *, struct mon_table **); + +#endif /* _MONITOR_H_ */ diff --git a/usr.bin/ssh/monitor_wrap.c b/usr.bin/ssh/monitor_wrap.c new file mode 100644 index 00000000000..f796cee5391 --- /dev/null +++ b/usr.bin/ssh/monitor_wrap.c @@ -0,0 +1,906 @@ +/* + * 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 + * 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: monitor_wrap.c,v 1.1 2002/03/18 17:28:37 provos Exp $"); + +#include <openssl/bn.h> +#include <openssl/dh.h> + +#include "ssh.h" +#include "dh.h" +#include "kex.h" +#include "auth.h" +#include "buffer.h" +#include "bufaux.h" +#include "packet.h" +#include "mac.h" +#include "log.h" +#include "zlib.h" +#include "monitor.h" +#include "monitor_wrap.h" +#include "xmalloc.h" +#include "atomicio.h" +#include "monitor_fdpass.h" +#include "getput.h" + +#include "auth.h" +#include "channels.h" +#include "session.h" + +/* Imports */ +extern int compat20; +extern Newkeys *newkeys[]; +extern z_stream incoming_stream; +extern z_stream outgoing_stream; +extern struct monitor *monitor; +extern Buffer input, output; + +void +mm_request_send(int socket, enum monitor_reqtype type, Buffer *m) +{ + u_char buf[5]; + u_int mlen = buffer_len(m); + + debug3("%s entering: type %d", __FUNCTION__, type); + + PUT_32BIT(buf, mlen + 1); + buf[4] = (u_char) type; /* 1st byte of payload is mesg-type */ + if (atomicio(write, socket, buf, sizeof(buf)) != sizeof(buf)) + fatal("%s: write", __FUNCTION__); + if (atomicio(write, socket, buffer_ptr(m), mlen) != mlen) + fatal("%s: write", __FUNCTION__); +} + +void +mm_request_receive(int socket, Buffer *m) +{ + u_char buf[4]; + ssize_t res; + u_int msg_len; + + debug3("%s entering", __FUNCTION__); + + res = atomicio(read, socket, buf, sizeof(buf)); + if (res != sizeof(buf)) { + if (res == 0) + fatal_cleanup(); + fatal("%s: read: %d", __FUNCTION__, res); + } + msg_len = GET_32BIT(buf); + if (msg_len > 256 * 1024) + fatal("%s: read: bad msg_len %d", __FUNCTION__, msg_len); + buffer_clear(m); + buffer_append_space(m, msg_len); + res = atomicio(read, socket, buffer_ptr(m), msg_len); + if (res != msg_len) + fatal("%s: read: %d != msg_len", __FUNCTION__, res); +} + +void +mm_request_receive_expect(int socket, enum monitor_reqtype type, Buffer *m) +{ + u_char rtype; + + debug3("%s entering: type %d", __FUNCTION__, type); + + mm_request_receive(socket, m); + rtype = buffer_get_char(m); + if (rtype != type) + fatal("%s: read: rtype %d != type %d", __FUNCTION__, + rtype, type); +} + +DH * +mm_choose_dh(int min, int nbits, int max) +{ + BIGNUM *p, *g; + int success = 0; + Buffer m; + + buffer_init(&m); + buffer_put_int(&m, min); + buffer_put_int(&m, nbits); + buffer_put_int(&m, max); + + mm_request_send(monitor->m_recvfd, MONITOR_REQ_MODULI, &m); + + debug3("%s: waiting for MONITOR_ANS_MODULI", __FUNCTION__); + mm_request_receive_expect(monitor->m_recvfd, MONITOR_ANS_MODULI, &m); + + success = buffer_get_char(&m); + if (success == 0) + fatal("%s: MONITOR_ANS_MODULI failed", __FUNCTION__); + + if ((p = BN_new()) == NULL) + fatal("%s: BN_new failed", __FUNCTION__); + if ((g = BN_new()) == NULL) + fatal("%s: BN_new failed", __FUNCTION__); + buffer_get_bignum2(&m, p); + buffer_get_bignum2(&m, g); + + debug3("%s: remaining %d", __FUNCTION__, buffer_len(&m)); + buffer_free(&m); + + return (dh_new_group(g, p)); +} + +int +mm_key_sign(Key *key, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) +{ + Kex *kex = *monitor->m_pkex; + Buffer m; + + debug3("%s entering", __FUNCTION__); + + buffer_init(&m); + buffer_put_int(&m, kex->host_key_index(key)); + buffer_put_string(&m, data, datalen); + + mm_request_send(monitor->m_recvfd, MONITOR_REQ_SIGN, &m); + + debug3("%s: waiting for MONITOR_ANS_SIGN", __FUNCTION__); + mm_request_receive_expect(monitor->m_recvfd, MONITOR_ANS_SIGN, &m); + *sigp = buffer_get_string(&m, lenp); + buffer_free(&m); + + return (0); +} + +struct passwd * +mm_getpwnamallow(const char *login) +{ + Buffer m; + struct passwd *pw; + u_int pwlen; + + debug3("%s entering", __FUNCTION__); + + buffer_init(&m); + buffer_put_cstring(&m, login); + + mm_request_send(monitor->m_recvfd, MONITOR_REQ_PWNAM, &m); + + debug3("%s: waiting for MONITOR_ANS_PWNAM", __FUNCTION__); + mm_request_receive_expect(monitor->m_recvfd, MONITOR_ANS_PWNAM, &m); + + if (buffer_get_char(&m) == 0) { + buffer_free(&m); + return (NULL); + } + pw = buffer_get_string(&m, &pwlen); + if (pwlen != sizeof(struct passwd)) + fatal("%s: struct passwd size mismatch", __FUNCTION__); + pw->pw_name = buffer_get_string(&m, NULL); + pw->pw_passwd = buffer_get_string(&m, NULL); + pw->pw_gecos = buffer_get_string(&m, NULL); + pw->pw_class = buffer_get_string(&m, NULL); + pw->pw_dir = buffer_get_string(&m, NULL); + pw->pw_shell = buffer_get_string(&m, NULL); + buffer_free(&m); + + return (pw); +} + +void +pwfree(struct passwd *pw) +{ + xfree(pw->pw_name); + xfree(pw->pw_passwd); + xfree(pw->pw_gecos); + xfree(pw->pw_class); + xfree(pw->pw_dir); + xfree(pw->pw_shell); + xfree(pw); +} + +/* Inform the privileged process about service and style */ + +void +mm_inform_authserv(char *service, char *style) +{ + Buffer m; + + debug3("%s entering", __FUNCTION__); + + buffer_init(&m); + buffer_put_cstring(&m, service); + buffer_put_cstring(&m, style ? style : ""); + + mm_request_send(monitor->m_recvfd, MONITOR_REQ_AUTHSERV, &m); + + buffer_free(&m); +} + +/* Do the password authentication */ +int +mm_auth_password(Authctxt *authctxt, char *password) +{ + Buffer m; + int authenticated = 0; + + debug3("%s entering", __FUNCTION__); + + buffer_init(&m); + buffer_put_cstring(&m, password); + mm_request_send(monitor->m_recvfd, MONITOR_REQ_AUTHPASSWORD, &m); + + debug3("%s: waiting for MONITOR_ANS_AUTHPASSWORD", __FUNCTION__); + mm_request_receive_expect(monitor->m_recvfd, MONITOR_ANS_AUTHPASSWORD, &m); + + authenticated = buffer_get_int(&m); + + buffer_free(&m); + + debug3("%s: user %sauthenticated", + __FUNCTION__, authenticated ? "" : "not "); + return (authenticated); +} + +int +mm_user_key_allowed(struct passwd *pw, Key *key) +{ + return (mm_key_allowed(MM_USERKEY, NULL, NULL, key)); +} + +int +mm_hostbased_key_allowed(struct passwd *pw, char *user, char *host, + Key *key) +{ + return (mm_key_allowed(MM_HOSTKEY, user, host, key)); +} + +int +mm_auth_rhosts_rsa_key_allowed(struct passwd *pw, char *user, + char *host, Key *key) +{ + int ret; + + key->type = KEY_RSA; /* XXX hack for key_to_blob */ + ret = mm_key_allowed(MM_RSAHOSTKEY, user, host, key); + key->type = KEY_RSA1; + return (ret); +} + +void +mm_send_debug(Buffer *m) +{ + char *msg; + + while (buffer_len(m)) { + msg = buffer_get_string(m, NULL); + debug3("%s: Sending debug: %s", __FUNCTION__, msg); + packet_send_debug("%s", msg); + xfree(msg); + } +} + +int +mm_key_allowed(enum mm_keytype type, char *user, char *host, Key *key) +{ + Buffer m; + u_char *blob; + u_int len; + int allowed = 0; + + debug3("%s entering", __FUNCTION__); + + /* Convert the key to a blob and the pass it over */ + if (!key_to_blob(key, &blob, &len)) + return (0); + + buffer_init(&m); + buffer_put_int(&m, type); + buffer_put_cstring(&m, user ? user : ""); + buffer_put_cstring(&m, host ? host : ""); + buffer_put_string(&m, blob, len); + xfree(blob); + + mm_request_send(monitor->m_recvfd, MONITOR_REQ_KEYALLOWED, &m); + + debug3("%s: waiting for MONITOR_ANS_KEYALLOWED", __FUNCTION__); + mm_request_receive_expect(monitor->m_recvfd, MONITOR_ANS_KEYALLOWED, &m); + + allowed = buffer_get_int(&m); + + /* Send potential debug messages */ + mm_send_debug(&m); + + buffer_free(&m); + + return (allowed); +} + +/* + * This key verify needs to send the key type along, because the + * privileged parent makes the decision if the key is allowed + * for authentication. + */ + +int +mm_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) +{ + Buffer m; + u_char *blob; + u_int len; + int verified = 0; + + debug3("%s entering", __FUNCTION__); + + /* Convert the key to a blob and the pass it over */ + if (!key_to_blob(key, &blob, &len)) + return (0); + + buffer_init(&m); + buffer_put_string(&m, blob, len); + buffer_put_string(&m, sig, siglen); + buffer_put_string(&m, data, datalen); + xfree(blob); + + mm_request_send(monitor->m_recvfd, MONITOR_REQ_KEYVERIFY, &m); + + debug3("%s: waiting for MONITOR_ANS_KEYVERIFY", __FUNCTION__); + mm_request_receive_expect(monitor->m_recvfd, MONITOR_ANS_KEYVERIFY, &m); + + verified = buffer_get_int(&m); + + buffer_free(&m); + + return (verified); +} + +/* Export key state after authentication */ +Newkeys * +mm_newkeys_from_blob(u_char *blob, int blen) +{ + Buffer b; + u_int len; + Newkeys *newkey = NULL; + Enc *enc; + Mac *mac; + Comp *comp; + + debug3("%s: %p(%d)", __FUNCTION__, blob, blen); +#ifdef DEBUG_PK + dump_base64(stderr, blob, blen); +#endif + buffer_init(&b); + buffer_append(&b, blob, blen); + + newkey = xmalloc(sizeof(*newkey)); + enc = &newkey->enc; + mac = &newkey->mac; + comp = &newkey->comp; + + /* Enc structure */ + enc->name = buffer_get_string(&b, NULL); + buffer_get(&b, &enc->cipher, sizeof(enc->cipher)); + enc->enabled = buffer_get_int(&b); + enc->block_size = buffer_get_int(&b); + enc->key = buffer_get_string(&b, &enc->key_len); + enc->iv = buffer_get_string(&b, &len); + if (len != enc->block_size) + fatal("%s: bad ivlen: expected %d != %d", __FUNCTION__, + enc->block_size, len); + + if (enc->name == NULL || cipher_by_name(enc->name) != enc->cipher) + fatal("%s: bad cipher name %s or pointer %p", __FUNCTION__, + enc->name, enc->cipher); + + /* Mac structure */ + mac->name = buffer_get_string(&b, NULL); + if (mac->name == NULL || mac_init(mac, mac->name) == -1) + fatal("%s: can not init mac %s", __FUNCTION__, mac->name); + mac->enabled = buffer_get_int(&b); + mac->key = buffer_get_string(&b, &len); + if (len > mac->key_len) + fatal("%s: bad mac key lenght: %d > %d", __FUNCTION__, len, + mac->key_len); + mac->key_len = len; + + /* Comp structure */ + comp->type = buffer_get_int(&b); + comp->enabled = buffer_get_int(&b); + comp->name = buffer_get_string(&b, NULL); + + len = buffer_len(&b); + if (len != 0) + error("newkeys_from_blob: remaining bytes in blob %d", len); + buffer_free(&b); + return (newkey); +} + +int +mm_newkeys_to_blob(int mode, u_char **blobp, u_int *lenp) +{ + Buffer b; + int len; + u_char *buf; + Enc *enc; + Mac *mac; + Comp *comp; + Newkeys *newkey = newkeys[mode]; + + debug3("%s: converting %p", __FUNCTION__, newkey); + + if (newkey == NULL) { + error("%s: newkey == NULL", __FUNCTION__); + return 0; + } + enc = &newkey->enc; + mac = &newkey->mac; + comp = &newkey->comp; + + buffer_init(&b); + /* Enc structure */ + buffer_put_cstring(&b, enc->name); + /* The cipher struct is constant and shared, you export pointer */ + buffer_append(&b, &enc->cipher, sizeof(enc->cipher)); + buffer_put_int(&b, enc->enabled); + buffer_put_int(&b, enc->block_size); + buffer_put_string(&b, enc->key, enc->key_len); + packet_get_keyiv(mode, enc->iv, enc->block_size); + buffer_put_string(&b, enc->iv, enc->block_size); + + /* Mac structure */ + buffer_put_cstring(&b, mac->name); + buffer_put_int(&b, mac->enabled); + buffer_put_string(&b, mac->key, mac->key_len); + + /* Comp structure */ + buffer_put_int(&b, comp->type); + buffer_put_int(&b, comp->enabled); + buffer_put_cstring(&b, comp->name); + + len = buffer_len(&b); + buf = xmalloc(len); + memcpy(buf, buffer_ptr(&b), len); + memset(buffer_ptr(&b), 0, len); + buffer_free(&b); + if (lenp != NULL) + *lenp = len; + if (blobp != NULL) + *blobp = buf; + return len; +} + +void +mm_send_kex(Buffer *m, Kex *kex) +{ + buffer_put_string(m, kex->session_id, kex->session_id_len); + buffer_put_int(m, kex->we_need); + buffer_put_int(m, kex->hostkey_type); + buffer_put_int(m, kex->kex_type); + buffer_put_string(m, buffer_ptr(&kex->my), buffer_len(&kex->my)); + buffer_put_string(m, buffer_ptr(&kex->peer), buffer_len(&kex->peer)); + buffer_put_int(m, kex->flags); + buffer_put_cstring(m, kex->client_version_string); + buffer_put_cstring(m, kex->server_version_string); +} + +void +mm_send_keystate(struct monitor *monitor) +{ + Buffer m; + u_char *blob, *p; + u_int bloblen, plen; + + buffer_init(&m); + + if (!compat20) { + u_char iv[24]; + int ivlen; + + buffer_put_int(&m, packet_get_protocol_flags()); + + buffer_put_int(&m, packet_get_ssh1_cipher()); + + debug3("%s: Sending ssh1 IV", __FUNCTION__); + ivlen = packet_get_keyiv_len(MODE_OUT); + packet_get_keyiv(MODE_OUT, iv, ivlen); + buffer_put_string(&m, iv, ivlen); + ivlen = packet_get_keyiv_len(MODE_OUT); + packet_get_keyiv(MODE_IN, iv, ivlen); + buffer_put_string(&m, iv, ivlen); + goto skip; + } else { + /* Kex for rekeying */ + mm_send_kex(&m, *monitor->m_pkex); + } + + debug3("%s: Sending new keys: %p %p", + __FUNCTION__, newkeys[MODE_OUT], newkeys[MODE_IN]); + + /* Keys from Kex */ + if (!mm_newkeys_to_blob(MODE_OUT, &blob, &bloblen)) + fatal("%s: conversion of newkeys failed", __FUNCTION__); + + buffer_put_string(&m, blob, bloblen); + xfree(blob); + + if (!mm_newkeys_to_blob(MODE_IN, &blob, &bloblen)) + fatal("%s: conversion of newkeys failed", __FUNCTION__); + + buffer_put_string(&m, blob, bloblen); + xfree(blob); + + buffer_put_int(&m, packet_get_seqnr(MODE_OUT)); + buffer_put_int(&m, packet_get_seqnr(MODE_IN)); + + debug3("%s: New keys have been sent", __FUNCTION__); + skip: + /* More key context */ + plen = packet_get_keycontext(MODE_OUT, NULL); + p = xmalloc(plen+1); + packet_get_keycontext(MODE_OUT, p); + buffer_put_string(&m, p, plen); + xfree(p); + + plen = packet_get_keycontext(MODE_IN, NULL); + p = xmalloc(plen+1); + packet_get_keycontext(MODE_IN, p); + buffer_put_string(&m, p, plen); + xfree(p); + + /* Compression state */ + debug3("%s: Sending compression state", __FUNCTION__); + buffer_put_string(&m, &outgoing_stream, sizeof(outgoing_stream)); + buffer_put_string(&m, &incoming_stream, sizeof(incoming_stream)); + + /* Network I/O buffers */ + buffer_put_string(&m, buffer_ptr(&input), buffer_len(&input)); + buffer_put_string(&m, buffer_ptr(&output), buffer_len(&output)); + + mm_request_send(monitor->m_recvfd, MONITOR_REQ_KEYEXPORT, &m); + debug3("%s: Finished sending state", __FUNCTION__); + + buffer_free(&m); +} + +int +mm_pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen) +{ + Buffer m; + u_char *p; + int success = 0; + + buffer_init(&m); + mm_request_send(monitor->m_recvfd, MONITOR_REQ_PTY, &m); + + debug3("%s: waiting for MONITOR_ANS_PTY", __FUNCTION__); + mm_request_receive_expect(monitor->m_recvfd, MONITOR_ANS_PTY, &m); + + success = buffer_get_int(&m); + if (success == 0) { + debug3("%s: pty alloc failed", __FUNCTION__); + buffer_free(&m); + return (0); + } + p = buffer_get_string(&m, NULL); + buffer_free(&m); + + strlcpy(namebuf, p, namebuflen); /* Possible truncation */ + xfree(p); + + *ptyfd = mm_receive_fd(monitor->m_recvfd); + *ttyfd = mm_receive_fd(monitor->m_recvfd); + + /* Success */ + return (1); +} + +void +mm_session_pty_cleanup2(void *session) +{ + Session *s = session; + Buffer m; + + if (s->ttyfd == -1) + return; + buffer_init(&m); + buffer_put_cstring(&m, s->tty); + mm_request_send(monitor->m_recvfd, MONITOR_REQ_PTYCLEANUP, &m); + buffer_free(&m); + + /* closed dup'ed master */ + if (close(s->ptymaster) < 0) + error("close(s->ptymaster): %s", strerror(errno)); + + /* unlink pty from session */ + s->ttyfd = -1; +} + +/* Request process termination */ + +void +mm_terminate(void) +{ + Buffer m; + + buffer_init(&m); + mm_request_send(monitor->m_recvfd, MONITOR_REQ_TERM, &m); + buffer_free(&m); +} + +int +mm_ssh1_session_key(BIGNUM *num) +{ + int rsafail; + Buffer m; + + buffer_init(&m); + buffer_put_bignum2(&m, num); + mm_request_send(monitor->m_recvfd, MONITOR_REQ_SESSKEY, &m); + + mm_request_receive_expect(monitor->m_recvfd, MONITOR_ANS_SESSKEY, &m); + + rsafail = buffer_get_int(&m); + buffer_get_bignum2(&m, num); + + buffer_free(&m); + + return (rsafail); +} + +void +mm_chall_setup(char **name, char **infotxt, u_int *numprompts, + char ***prompts, u_int **echo_on) +{ + *name = xstrdup(""); + *infotxt = xstrdup(""); + *numprompts = 1; + *prompts = xmalloc(*numprompts * sizeof(char*)); + *echo_on = xmalloc(*numprompts * sizeof(u_int)); + (*echo_on)[0] = 0; +} + +int +mm_bsdauth_query(void *ctx, char **name, char **infotxt, + u_int *numprompts, char ***prompts, u_int **echo_on) +{ + Buffer m; + int res; + char *challenge; + + debug3("%s: entering", __FUNCTION__); + + buffer_init(&m); + mm_request_send(monitor->m_recvfd, MONITOR_REQ_BSDAUTHQUERY, &m); + + mm_request_receive_expect(monitor->m_recvfd, MONITOR_ANS_BSDAUTHQUERY, + &m); + res = buffer_get_int(&m); + if (res == -1) { + debug3("%s: no challenge", __FUNCTION__); + buffer_free(&m); + return (-1); + } + + /* Get the challenge, and format the response */ + challenge = buffer_get_string(&m, NULL); + buffer_free(&m); + + mm_chall_setup(name, infotxt, numprompts, prompts, echo_on); + (*prompts)[0] = challenge; + + debug3("%s: received challenge: %s", __FUNCTION__, challenge); + + return (0); +} + +int +mm_bsdauth_respond(void *ctx, u_int numresponses, char **responses) +{ + Buffer m; + int authok; + + debug3("%s: entering", __FUNCTION__); + if (numresponses != 1) + return (-1); + + buffer_init(&m); + buffer_put_cstring(&m, responses[0]); + mm_request_send(monitor->m_recvfd, MONITOR_REQ_BSDAUTHRESPOND, &m); + + mm_request_receive_expect(monitor->m_recvfd, + MONITOR_ANS_BSDAUTHRESPOND, &m); + + authok = buffer_get_int(&m); + buffer_free(&m); + + return ((authok == 0) ? -1 : 0); +} + +int +mm_skey_query(void *ctx, char **name, char **infotxt, + u_int *numprompts, char ***prompts, u_int **echo_on) +{ + Buffer m; + int len, res; + char *p, *challenge; + + debug3("%s: entering", __FUNCTION__); + + buffer_init(&m); + mm_request_send(monitor->m_recvfd, MONITOR_REQ_SKEYQUERY, &m); + + mm_request_receive_expect(monitor->m_recvfd, MONITOR_ANS_SKEYQUERY, + &m); + res = buffer_get_int(&m); + if (res == -1) { + debug3("%s: no challenge", __FUNCTION__); + buffer_free(&m); + return (-1); + } + + /* Get the challenge, and format the response */ + challenge = buffer_get_string(&m, NULL); + buffer_free(&m); + + debug3("%s: received challenge: %s", __FUNCTION__, challenge); + + mm_chall_setup(name, infotxt, numprompts, prompts, echo_on); + + len = strlen(challenge) + strlen(SKEY_PROMPT) + 1; + p = xmalloc(len); + strlcpy(p, challenge, len); + strlcat(p, SKEY_PROMPT, len); + (*prompts)[0] = p; + xfree(challenge); + + return (0); +} + +int +mm_skey_respond(void *ctx, u_int numresponses, char **responses) +{ + Buffer m; + int authok; + + debug3("%s: entering", __FUNCTION__); + if (numresponses != 1) + return (-1); + + buffer_init(&m); + buffer_put_cstring(&m, responses[0]); + mm_request_send(monitor->m_recvfd, MONITOR_REQ_SKEYRESPOND, &m); + + mm_request_receive_expect(monitor->m_recvfd, + MONITOR_ANS_SKEYRESPOND, &m); + + authok = buffer_get_int(&m); + buffer_free(&m); + + return ((authok == 0) ? -1 : 0); +} + +void +mm_ssh1_session_id(u_char session_id[16]) +{ + Buffer m; + int i; + + debug3("%s entering", __FUNCTION__); + + buffer_init(&m); + for (i = 0; i < 16; i++) + buffer_put_char(&m, session_id[i]); + + mm_request_send(monitor->m_recvfd, MONITOR_REQ_SESSID, &m); + buffer_free(&m); +} + +int +mm_auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) +{ + Buffer m; + Key *key; + u_char *blob; + u_int blen; + int allowed = 0; + + debug3("%s entering", __FUNCTION__); + + buffer_init(&m); + buffer_put_bignum2(&m, client_n); + + mm_request_send(monitor->m_recvfd, MONITOR_REQ_RSAKEYALLOWED, &m); + mm_request_receive_expect(monitor->m_recvfd, MONITOR_ANS_RSAKEYALLOWED, &m); + + allowed = buffer_get_int(&m); + + if (allowed && rkey != NULL) { + blob = buffer_get_string(&m, &blen); + if ((key = key_from_blob(blob, blen)) == NULL) + fatal("%s: key_from_blob failed", __FUNCTION__); + *rkey = key; + xfree(blob); + } + mm_send_debug(&m); + buffer_free(&m); + + return (allowed); +} + +BIGNUM * +mm_auth_rsa_generate_challenge(Key *key) +{ + Buffer m; + BIGNUM *challenge; + u_char *blob; + u_int blen; + + debug3("%s entering", __FUNCTION__); + + if ((challenge = BN_new()) == NULL) + fatal("%s: BN_new failed", __FUNCTION__); + + key->type = KEY_RSA; /* XXX cheat for key_to_blob */ + if (key_to_blob(key, &blob, &blen) == 0) + fatal("%s: key_to_blob failed", __FUNCTION__); + key->type = KEY_RSA1; + + buffer_init(&m); + buffer_put_string(&m, blob, blen); + xfree(blob); + + mm_request_send(monitor->m_recvfd, MONITOR_REQ_RSACHALLENGE, &m); + mm_request_receive_expect(monitor->m_recvfd, MONITOR_ANS_RSACHALLENGE, &m); + + buffer_get_bignum2(&m, challenge); + buffer_free(&m); + + return (challenge); +} + +int +mm_auth_rsa_verify_response(Key *key, BIGNUM *p, u_char response[16]) +{ + Buffer m; + u_char *blob; + u_int blen; + int success = 0; + + debug3("%s entering", __FUNCTION__); + + key->type = KEY_RSA; /* XXX cheat for key_to_blob */ + if (key_to_blob(key, &blob, &blen) == 0) + fatal("%s: key_to_blob failed", __FUNCTION__); + key->type = KEY_RSA1; + + buffer_init(&m); + buffer_put_string(&m, blob, blen); + buffer_put_string(&m, response, 16); + xfree(blob); + + mm_request_send(monitor->m_recvfd, MONITOR_REQ_RSARESPONSE, &m); + mm_request_receive_expect(monitor->m_recvfd, MONITOR_ANS_RSARESPONSE, &m); + + success = buffer_get_int(&m); + buffer_free(&m); + + return (success); +} diff --git a/usr.bin/ssh/monitor_wrap.h b/usr.bin/ssh/monitor_wrap.h new file mode 100644 index 00000000000..24b224bd8fb --- /dev/null +++ b/usr.bin/ssh/monitor_wrap.h @@ -0,0 +1,87 @@ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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. + */ + +#ifndef _MM_WRAP_H_ +#define _MM_WRAP_H_ +#include "key.h" +#include "buffer.h" + +extern int use_privsep; +#define PRIVSEP(x) (use_privsep ? mm_##x : x) + +enum mm_keytype {MM_NOKEY, MM_HOSTKEY, MM_USERKEY, MM_RSAHOSTKEY, MM_RSAUSERKEY}; + +struct monitor; +struct mm_master; +struct passwd; +struct Authctxt; +DH *mm_choose_dh(int, int, int); +int mm_key_sign(Key *, u_char **, u_int *, u_char *, u_int); +void mm_inform_authserv(char *, char *); +struct passwd *mm_getpwnamallow(const char *); +int mm_auth_password(struct Authctxt *, char *); +int mm_key_allowed(enum mm_keytype, char *, char *, Key *); +int mm_user_key_allowed(struct passwd *, Key *); +int mm_hostbased_key_allowed(struct passwd *, char *, char *, Key *); +int mm_auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, Key *); +int mm_key_verify(Key *, u_char *, u_int, u_char *, u_int); +int mm_auth_rsa_key_allowed(struct passwd *, BIGNUM *, Key **); +int mm_auth_rsa_verify_response(Key *, BIGNUM *, u_char *); +BIGNUM *mm_auth_rsa_generate_challenge(Key *); + +void mm_terminate(void); +int mm_pty_allocate(int *, int *, char *, int); +void mm_session_pty_cleanup2(void *); + +/* SSHv1 interfaces */ +void mm_ssh1_session_id(u_char *); +int mm_ssh1_session_key(BIGNUM *); + +/* Key export functions */ +struct Newkeys *mm_newkeys_from_blob(u_char *, int); +int mm_newkeys_to_blob(int, u_char **, u_int *); + +void monitor_apply_keystate(struct monitor *); +void mm_get_keystate(struct monitor *); +void mm_send_keystate(struct monitor*); + +/* bsdauth */ +int mm_bsdauth_query(void *, char **, char **, u_int *, char ***, u_int **); +int mm_bsdauth_respond(void *, u_int, char **); + +/* skey */ +int mm_skey_query(void *, char **, char **, u_int *, char ***, u_int **); +int mm_skey_respond(void *, u_int, char **); + +/* zlib allocation hooks */ + +void *mm_zalloc(struct mm_master *, u_int, u_int); +void mm_zfree(struct mm_master *, void *); +void mm_init_compression(struct mm_master *); + +/* Utility functions */ + +void pwfree(struct passwd *); +#endif /* _MM_H_ */ |