/* $Id: ssh_v1_dispatch_client.c,v 1.4 2001/02/11 03:35:18 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" int dispatch_v1_client_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); #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: { u_int8_t *msgstr = NULL; u_int32_t msglen; SSH_DLOG(5, ("DEBUG packet received.\n")); buf_use_binstr(msg, &msgstr, &msglen); SSH_DLOG(3, ("%.*s\n", msglen, msgstr)); if (context->client->Verbosity) fprintf(stderr, "%s: Remote: %.*s\r\n", context->client->localhost, (int) msglen, msgstr); return (0); } case SSH_V1_MSG_IGNORE: SSH_DLOG(6, ("IGNORE packet received.\n")); return (0); case SSH_V1_MSG_DISCONNECT: { u_int8_t *msgstr = NULL; u_int32_t msglen; SSH_DLOG(6, ("DISCONNECT packet received.\n")); buf_use_binstr(msg, &msgstr, &msglen); SSH_DLOG(4, ("Received DISCONNECT: %.*s\n", msglen, msgstr)); 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) { SSH_ERROR("we are exiting now"); 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.kexinit) (context, msg)) < 0) { SSH_ERROR("Fatal error in kexinit: %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: /* * the client now expects to receive a * SSH_V1_SMSG_SUCCESS message (for the session key), * responding with a SSH_CMSG_USER message (to log * in). */ if ((retval = send_user(context, msg_type)) < 0) { SSH_ERROR("Fatal error in send_user: %d\n", retval); ssh_exit(context, -retval, EXIT_NOW); } switch (retval) { case RET_OK: break; case RET_NEXT_STATE: context->v1_ctx.state = SSH_V1_STATE_AUTHUSER; break; case RET_SKIP_STATE: case RET_FAIL: SSH_DLOG(3, ("send_user failed: %d\n", retval)); ssh_exit(context, 0, EXIT_FLUSH); break; default: SSH_ERROR("Unknown send_user return value: %d\n", retval); ssh_exit(context, 1, EXIT_NOW); } break; case SSH_V1_STATE_AUTHUSER: if (msg_type == SSH_V1_SMSG_SUCCESS) { if (context->client->Verbosity) { switch (context->v1_ctx.auth_tries) { case 0: case SSH_AUTH_RHOSTS: fprintf(stderr, "%s: Rhosts authentication " "accepted by server.\r\n", context->client->localhost); break; case SSH_AUTH_RSA: fprintf(stderr, "%s: RSA authentication " "accepted by server.\r\n", context->client->localhost); /* * If we have an auth. agent * session open, close it. */ if (context->v1_ctx.authagent_session) { ssh_authagent_closesession( context, context->v1_ctx.authagent_session); context->v1_ctx.authagent_session = NULL; } break; case SSH_AUTH_PASSWORD: fprintf(stderr, "%s: Password authentication " "accepted by server.\r\n", context->client->localhost); break; case SSH_AUTH_RHOSTS_RSA: fprintf(stderr, "%s: Rhosts or /etc/hosts.equiv " "with RSA host authentication " "accepted by server.\r\n", context->client->localhost); break; default: fprintf(stderr, "%s: I have no idea why, but " "the server seems to be letting " "us in. :)\r\n", context->client->localhost); break; } } /* we're done authenticating */ context->v1_ctx.state = SSH_V1_STATE_PREPOPS; /* we'll use this since it's handy, but not needed */ context->v1_ctx.auth_tries = 0; /* * need to use goto since we're still leading the * dance */ goto clientpreops; } /* Try to authenticate myself. */ if ((retval = v1_user_try_auth(context, msg, size, msg_type)) < 0) { SSH_ERROR("v1_user_try_auth fatal error: %d\n", retval); ssh_exit(context, -retval, EXIT_NOW); } switch (retval) { case RET_FAIL: fprintf(stderr, "Permission denied.\r\n"); ssh_exit(context, 1, EXIT_NOW); 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; clientpreops: case SSH_V1_STATE_PREPOPS: if ((retval = ssh_v1_prep_client(context, msg, size, msg_type)) < 0) { SSH_ERROR("ssh_v1_prep_client fatal error: %d\n", retval); ssh_exit(context, -retval, EXIT_NOW); } switch (retval) { case RET_OK: /* nothing, server will respond, continue */ break; case RET_NEXT_STATE: ev.event_type = SSH_EVENT_START; /* * since the server and i are now "equal", i will * wait for him and he will probably say something */ if (context->client->Verbosity) fprintf(stderr, "%s: Entering interactive session.\r\n", context->client->localhost); EVT_SEND(&ev, context); context->v1_ctx.state = SSH_V1_STATE_IACTIVE; break; case RET_FAIL: SSH_DLOG(3, ("ssh_v1_pre_client failed: %d\n", retval)); ssh_exit(context, 0, EXIT_FLUSH); break; default: SSH_ERROR("Unknown ssh_v1_prep_client return " "value: %d\n", retval); ssh_exit(context, 1, EXIT_NOW); } break; case SSH_V1_STATE_IACTIVE: if ((retval = ssh_v1_iactive_client(context, msg, size, msg_type)) < 0) { SSH_ERROR("ssh_v1_iactive_client fatal error: %d\n", retval); ssh_exit(context, -retval, EXIT_NOW); } switch (retval) { case RET_FAIL: SSH_DLOG(3, ("ssh_v1_iactive_client failed")); ssh_exit(context, 0, EXIT_FLUSH); break; case RET_NEXT_STATE: context->v1_ctx.state = SSH_V1_STATE_EXITING; /* * yes, we're still leading the dance. */ goto clientdoexiting; break; case RET_OK: break; default: SSH_ERROR("Unknown ssh_v1_iactive_client return " "value: %d\n", retval); ssh_exit(context, 1, 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; clientdoexiting: case SSH_V1_STATE_EXITING: if ((retval = ssh_v1_exiting_client(context, msg, size, msg_type)) < 0) { SSH_ERROR("ssh_v1_exiting_client fatal error: %d\n", retval); ssh_exit(context, -retval, EXIT_NOW); } switch (retval) { case RET_NEXT_STATE: return RET_NEXT_STATE; /* YAY! */ 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); }