/* $Id: ssh_v1_dispatch_server.c,v 1.5 2001/02/11 03:35:19 tls Exp $ */ /* * Copyright 1999 RedBack Networks, Incorporated. * All rights reserved. * * This software is not in the public domain. It is distributed * under the terms of the license in the file LICENSE in the * same directory as this file. If you have received a copy of this * software without the LICENSE file (which means that whoever gave * you this software violated its license) you may obtain a copy from * http://www.panix.com/~tls/LICENSE.txt */ /* * Copyright (c) 2001 Jason R. Thorpe. * 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 #define SSH_V1_MESSAGE_STRINGS #include "sshd.h" #include "ssh_buffer.h" #include "ssh_transport.h" #include "ssh_v1_child.h" #include "ssh_v1_messages.h" #include "ssh_v1_proto.h" #include "ssh_util.h" #include "ssh_types.h" #include "ssh_authagent.h" #include "crypto/ssh_auth.h" /* * dispatch_v1_server_msg: * * Returns: * 0 All ok. * -1 Fatal error. (shouldn't happen, we exit instead) * * msg is left in an undefined state on exit. It should * be reset before being used again. */ int dispatch_v1_server_msg(ssh_context_t *context, struct ssh_buf *msg, int size) { int retval; struct itimerval itval; u_int8_t msg_type; struct ssh_event ev; /* Grab packet type and trim it (and any left begine padding) */ buf_get_int8(msg, &msg_type); size -= sizeof(u_int8_t); /* * Note: This is the terminal point for any data in msg. This * means that any function that we pass msg to can do whatever * it wants with it. We do not depend on it being in any * particular when we return (other than still existing). */ #ifndef SSH_V1_MESSAGE_STRINGS SSH_DLOG(3, ("received packet type: %d\n", msg_type)); #else SSH_DLOG(3, ("received packet type %s (%d)\n", v1_msg_name[msg_type], msg_type)); #endif /* SSH_V1_MESSAGE_STRINGS */ /* * Handle ignore, debug and disconnect messages. * * These are actually transport layer actions * but it's easier to do them here. */ switch (msg_type) { case SSH_V1_MSG_DEBUG: SSH_DLOG(5, ("DEBUG packet received.\n")); SSH_DLOG(3, ("%s\n", buf_data(msg))); return (0); case SSH_V1_MSG_IGNORE: SSH_DLOG(6, ("IGNORE packet received.\n")); return (0); case SSH_V1_MSG_DISCONNECT: SSH_DLOG(6, ("DISCONNECT packet received.\n")); if (is_debug_level(4)) { u_int8_t *d_str; int len; d_str = NULL; if (buf_get_binstr(msg, &d_str, &len) == 0) { d_str[len] = '\0'; SSH_DLOG(4, ("Received DISCONNECT: %s\n", d_str)); } } ssh_exit(context, 0, EXIT_NOW); } /* * This happens when the command/shell exits and sigchld_handler * reaps it and indicates that we should go to the exiting state. */ if (context->v1_ctx.go_to_exit) context->v1_ctx.state = SSH_V1_STATE_EXITING; switch (context->v1_ctx.state) { case SSH_STATE_NONE: context->v1_ctx.child_gone = 0; if ((retval = (context->transport_ctx.dispatch_transport) (context, msg, size, msg_type)) < 0) { SSH_ERROR("Fatal error in dispatch_transport: %d\n", retval); ssh_exit(context, -retval, EXIT_NOW); } switch (retval) { case RET_OK: /* Nothing */ break; case RET_NEXT_STATE: context->v1_ctx.state = SSH_V1_STATE_GETUSER; break; case RET_FAIL: SSH_DLOG(3, ("dispatch_transport failed: %d\n", retval)); ssh_exit(context, 0, EXIT_FLUSH); break; default: SSH_ERROR("Unknown dispatch_transport return " "value: %d\n", retval); ssh_exit(context, 1, EXIT_NOW); } break; case SSH_V1_STATE_GETUSER: /* Get user name from client. */ if ((retval = get_user(context, msg, size, msg_type)) < 0) { SSH_ERROR("Fatal error in get_user: %d\n", retval); ssh_exit(context, -retval, EXIT_NOW); } switch (retval) { case RET_NEXT_STATE: context->v1_ctx.state = SSH_V1_STATE_AUTHUSER; break; case RET_SKIP_STATE: context->v1_ctx.state = SSH_V1_STATE_PREPOPS; break; case RET_FAIL: SSH_DLOG(3, ("get_user failed: %d\n", retval)); ssh_exit(context, 0, EXIT_FLUSH); break; default: SSH_ERROR("Unknown get_user return value: %d\n", retval); ssh_exit(context, 1, EXIT_NOW); } break; case SSH_V1_STATE_AUTHUSER: /* See if the user is authorized. */ if ((retval = v1_auth_user(context, msg, size, msg_type)) < 0) { SSH_ERROR("v1_auth_user fatal error: %d\n", retval); ssh_exit(context, -retval, EXIT_NOW); } switch (retval) { case RET_FAIL: /* Check for authentication failure */ context->v1_ctx.auth_tries++; if (context->v1_ctx.auth_tries > SSH_V1_MAX_AUTH_TRIES) { EVT_V1_DISCONNECT(ev, context, "Too many authentication tries.\n"); EVT_SEND(&ev, context); ssh_exit(context, 0, EXIT_FLUSH); } break; case RET_NEXT_STATE: context->v1_ctx.state = SSH_V1_STATE_PREPOPS; break; case RET_OK: break; default: SSH_ERROR("Unknown auth_user return value: %d\n", retval); ssh_exit(context, 1, EXIT_NOW); } break; case SSH_V1_STATE_PREPOPS: /* * Do preliminary setup, before executing a shell. * (We should be running as the user now.) */ if ((retval = ssh_v1_prep_server(context, msg, size, msg_type)) < 0) { SSH_ERROR("ssh_v1_prep_server fatal error: %d\n", retval); ssh_exit(context, -retval, EXIT_NOW); } switch (retval) { case RET_OK: /* nothing, continue */ break; case RET_NEXT_STATE: context->v1_ctx.state = SSH_V1_STATE_IACTIVE; break; case RET_FAIL: SSH_DLOG(3, ("ssh_v1_prep_server failed: %d\n", retval)); ssh_exit(context, 0, EXIT_FLUSH); break; default: SSH_ERROR("Unknown ssh_v1_prep_server return " "value: %d\n", retval); ssh_exit(context, 1, EXIT_NOW); } break; case SSH_V1_STATE_IACTIVE: if ((retval = ssh_v1_iactive_server(context, msg, size, msg_type)) < 0) { SSH_ERROR("ssh_v1_iactive_server fatal error: %d\n", retval); ssh_exit(context, -retval, EXIT_NOW); } switch (retval) { case RET_FAIL: SSH_DLOG(3, ("ssh_v1_iactive_server failed")); ssh_exit(context, 0, EXIT_FLUSH); break; case RET_NEXT_STATE: context->v1_ctx.state = SSH_V1_STATE_EXITING; break; case RET_OK: break; default: SSH_ERROR("Unknown ssh_v1_iactive_server return " "value: %d\n", retval); ssh_exit(context, 1, EXIT_NOW); } if (retval != RET_OK) { if (retval == RET_SKIP_STATE || retval == RET_FAIL) { /* * Other side went away, * either by itself or we sent DISCONNECT */ } /* * Get rid of any children, collect exit statuses. */ if (v1_close_children(context) != RET_OK) ssh_exit(context, 1, EXIT_FLUSH); /* * Child is gone and socket is gone. We've * got no one else to talk to, so exit. */ ssh_exit(context, 0, EXIT_NOW); } /* * Transition to EXITING state occurs when the * sigchld handler sets context->v1_ctx.go_to_exit. * It that is set, then we change the state at the * beginning of this function. */ break; case SSH_V1_STATE_EXITING: if ((retval = ssh_v1_exiting_server(context, msg, size, msg_type)) < 0) { SSH_ERROR("ssh_v1_exiting_server fatal error: %d\n", retval); ssh_exit(context, -retval, EXIT_NOW); } switch (retval) { case RET_NEXT_STATE: case RET_FAIL: ssh_exit(context, 0, EXIT_FLUSH); break; } break; default: /* Shouldn't happen, but... */ SSH_ERROR("Fatal programming error: invalid state"); ssh_exit(context, 1, EXIT_NOW); } /* * Do anything to prepare for a new state */ if (retval == RET_NEXT_STATE || retval == RET_SKIP_STATE) { /* State we're going TO next */ switch (context->v1_ctx.state) { case SSH_STATE_NONE: SSH_ERROR("Attempting transition to NONE state"); ssh_exit(context, 1, EXIT_NOW); break; case SSH_V1_STATE_GETUSER: /* Nothing */ break; case SSH_V1_STATE_AUTHUSER: context->v1_ctx.auth_tries = 0; break; case SSH_V1_STATE_PREPOPS: /* * User has authenticated himself, * disable the timer to disconnect him. */ timerclear(&itval.it_value); setitimer(ITIMER_REAL, &itval, NULL); signal(SIGALRM, SIG_IGN); /* Switch to the user. */ if (ssh_sys_setuser(context, context->username) != 0) { EVT_V1_DISCONNECT(ev, context, "Authentication failed"); EVT_SEND(&ev, context); ssh_exit(context, 0, EXIT_FLUSH); } break; case SSH_V1_STATE_IACTIVE: setup_sigchld_handler(context); setup_sigpipe_handler(context); /* Set the type of service. */ set_tos(context->transport_ctx.commsock, context->usepty); break; case SSH_V1_STATE_EXITING: break; } } return (0); }