/* $Id: ssh_event_dispatch.c,v 1.29 2001/02/11 03:35:12 tls Exp $ */ /* * Copyright (c) 2000 Eric Haszlakiewicz. * 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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 #include #include #include #include #define SSH_EVENT_STRINGS #include "sshd.h" #include "ssh_buffer.h" #include "ssh_cipher.h" #include "ssh_event.h" #include "ssh_client.h" #include "ssh_util.h" #include "ssh_v1_messages.h" /* * ssh_dispatch_event */ int ssh_dispatch_event(ssh_context_t * context, struct ssh_event * evp) { FUNC_DECL(ssh_dispatch_event); int retval; int ret; u_int32_t tmp_int32; struct ssh_buf buf; ssh_fdinfo_t *fdi; ret = RET_OK; fdi = context->fdi; #ifndef SSH_EVENT_STRINGS SSH_DLOG(3, ("Got event: %d\n", evp->event_type)); #else /* SSH_EVENT_STRINGS */ SSH_DLOG(3, ("Got event %s (%d)\n", event_name[evp->event_type], evp->event_type)); #endif /* SSH_EVENT_STRINGS */ switch (evp->event_type) { case SSH_EVENT_EXITTHREAD: /* XXX We don't actually want to exit right away here. */ /* ssh_exit(context, 1, EXIT_NOW); */ break; #ifdef WITH_PROTO_V1 case SSH_EVENT_EXITSTATUS: { struct ssh_ev_send *evsend; evsend = (struct ssh_ev_send *)evp->event_data; /* * This means: close primary channel and send the exitstatus. * If we already read an EOF then send the exitstatus right * away. Otherwise just save it until we do get an EOF. */ context->v1_ctx.exitstatus = buf_alloc(context->v1_ctx.exitstatus, 1 + evsend->dlen); buf_put_nbytes(context->v1_ctx.exitstatus, evsend->dlen, evsend->data); if (context->v1_ctx.primary_eof) { /* Already got eof, sent the exit status */ retval = (context->transport_ctx.xmit_packet) (context, context->v1_ctx.exitstatus); if (retval < 0) { SSH_ERROR("xmit_packet returned fatal error"); ssh_exit(context, 1, EXIT_NOW); } else if (retval == 1) { /* Connection went away. */ ssh_exit(context, 0, EXIT_NOW); } /* * Set flag so we exit if no other channels * are open. */ } context->v1_ctx.have_exitstat = YES; SSH_DLOG(5, ("have_exitstat set to 1")); break; } case SSH_EVENT_MAX_PACKET_SIZE: /* * XXX perhaps pass this to a sendThread aware * dispatch_transport()? */ tmp_int32 = ((u_int32_t *) evp->event_data)[0]; (context->transport_ctx.set_max_packet_size) (&context->transport_ctx, tmp_int32); break; case SSH_EVENT_PASSFD: { struct ssh_ev_passfd *evfd; ssh_fdinfo_t *this_fdi; evfd = (struct ssh_ev_passfd *)evp->event_data; SSH_DLOG(4, ("Got fd %d type %d\n", evfd->fd, evfd->fd_type)); this_fdi = &fdi[context->nfds]; context->nfds++; /* XXX SSH_FD_STDDATA isn't correct for V2 (msg type too) */ if (init_fdi(context, this_fdi, evfd->channel_id, SSH_FD_STDDATA, evfd->fd, (evfd->fd_type == FDTYPE_NORMAL) ? SSH_V1_SMSG_STDOUT_DATA : SSH_V1_SMSG_STDERR_DATA) != RET_OK) { SSH_ERROR("init_fdi failed!"); ssh_exit(context, 1, EXIT_NOW); } if (chan_attach_fd(context, evfd->fd, evfd->fd_type, evfd->channel_id) != RET_OK) { /* All should have been valid by now, so fail fatal */ SSH_ERROR("chan_attach_fd failed!"); ssh_exit(context, 1, EXIT_NOW); } break; } case SSH_EVENT_INIT_COMPRESSION: { u_int32_t c_level; c_level = ((u_int32_t *) evp->event_data)[0]; if ((context->transport_ctx.init_compression)(context, c_level) != RET_OK) { SSH_ERROR("sendThread compression init failed"); ret = RET_FATAL; } break; } #endif /* WITH_PROTO_V1 */ #ifdef WITH_PROTO_V2 case FOO: XAX V2 specific events here. #endif /* WITH_PROTO_V2 */ case SSH_EVENT_SEND: { struct ssh_ev_send *evsend; evsend = (struct ssh_ev_send *)evp->event_data; /* Grab the packet to send, and send it */ buf_setbuf(&buf, (char *)evsend->data, evsend->dlen); retval = (context->transport_ctx.xmit_packet)(context, &buf); if (retval < 0) { SSH_ERROR("xmit_packet returned fatal error"); ret = RET_FATAL; } else if (retval == 1) { /* Connection is gone */ ret = RET_FAIL; } /* we're leaving...so long and thanks for all the fish */ /*XXX XAX split into a different event probably.*/ if (evsend->data[0] == SSH_V1_CMSG_EXIT_CONFIRMATION) { /* both, just to be sure */ shutdown(context->transport_ctx.commsock, SHUT_RDWR); close(context->transport_ctx.commsock); ret = RET_NEXT_STATE; /* caller should exit now */ } break; } case SSH_EVENT_INIT_CRYPT: { struct ssh_ev_init_crypt *evcry; evcry = (struct ssh_ev_init_crypt *) evp->event_data; /* * None of this stuff should fail b/c recvThread * already checked it */ if (set_cipher_type(context->cipher, evcry->cipher_type) != 0) { SSH_ERROR("Unable to set pre-validated cipher " "type:%s\n", cipher_name(evcry->cipher_type)); ret = RET_FATAL; break; } if (cipher_initialize(context->cipher, evcry->cipher_data, SSH_V1_SESSION_KEY_SIZE, context->running_as_server) == NULL) { SSH_ERROR("Unable to initialize cipher in send " "thread:%s\n", cipher_name(evcry->cipher_type)); ret = RET_FATAL; break; } break; } case SSH_EVENT_CHANNEL_OPEN: { struct ssh_ev_channel_open *evch; evch = (struct ssh_ev_channel_open *) evp->event_data; retval = create_channel(context, evch->channel_id, evch->remote_id, evch->initial_window_size, evch->max_packet_size); /* * XXX Should change this to send CONFIRMATION/FAILURE * sometimes */ if (retval != RET_OK) { SSH_ERROR("create_channel failed in sendThread!"); ret = RET_FATAL; break; } break; } case SSH_EVENT_CHANNEL_REQ_SEND: SSH_ERROR("Unimpl REQ_SEND"); ret = RET_FATAL; break; case SSH_EVENT_CHANNEL_CLOSE: SSH_ERROR("Unimpl CHANNEL_CLOSE"); ret = RET_FATAL; break; case SSH_EVENT_CHANNEL_REQ_EXEC: { struct ssh_ev_channel_req *evreq; evreq = (struct ssh_ev_channel_req *)evp->event_data; switch(evreq->req_type) { /* Used to have SSH_CHAN_REQ_pty here */ default: SSH_ERROR("Unknown REQ_EXEC type: %d\n", evreq->req_type); ret = RET_FATAL; break; } break; } case SSH_EVENT_USERAUTH_OK: { struct ssh_ev_ua_ok *evua; evua = (struct ssh_ev_ua_ok *)evp->event_data; if (ssh_sys_setuser(context, evua->username) != 0) { SEND_DISCONNECT(context, "Authentication failed"); ret = RET_FAIL; } /* * Note: this assumes both protocols payload starts with * a 1 byte type */ buf_setbuf(&buf, (char *)&evua->msg_type, 1); retval = (context->transport_ctx.xmit_packet)(context, &buf); if (retval < 0) { SSH_ERROR("xmit_packet (userauth_ok) returned " "fatal error"); ret = RET_FATAL; } else if (retval == 1) { /* Connection is gone */ ret = RET_FAIL; } break; } case SSH_EVENT_START: /* * we don't need to transmit anything here, but we do need to * frob the tty (if we're using one) a little and start * reading stdin. */ start_stdin(context, 1); break; default: /* XAX send disconnect here? (and other places?) */ SSH_ERROR("Unknown event type: %d\n", evp->event_type); ret = RET_FATAL; } return (ret); }