/* $Id: ssh_v1_prep_client.c,v 1.2 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) 2000 Andrew Brown and 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 names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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 "options.h" #include "sshd.h" #include "ssh_buffer.h" #include "ssh_channel.h" #include "ssh_util.h" #include "ssh_v1_messages.h" #include "ssh_v1_proto.h" #include "ssh_transport.h" #include "ssh_types.h" /* * ssh_v1_prep_client: pre-exec state. * We are already authenticated. * This is our last chance to ask for stuff. * * Assume: buf is discarded after we return. * * Returns: * RET_NEXT_STATE Program has been exec'd. * RET_OK Not done yet; This doesn't indicate success of last msg! * RET_FAIL error, disconnect sent. * RET_FATAL fatal error, disconnect sent. */ int ssh_v1_prep_client(ssh_context_t * context, struct ssh_buf * buf, size_t size, int msg_type) { struct ssh_buf *mybuf; struct ssh_event ev; struct ssh_ev_send *evsend; int retval, l; /* * we'll do things in this order (done in three parts): * * REQUEST_COMPRESSION (if needed) * REQUEST_PTY (if needed) * EXEC_SHELL (or) * EXEC_CMD * * these things we don't do (some might come later) * * MAX_PACKET_SIZE * X11_REQUEST_FORWARDING * PORT_FORWARD_REQUEST * AGENT_REQUEST_FORWARDING * * part 1) the switch here takes care of notifications, and * advancing the state. * part 2) a series of if statements takes care of advancing past * states we don't need (eg, compression or a tty). * part 3) another switch generates messages to request the things * we do need. */ /* ***************************** PART 1 ***************************** */ if (msg_type == SSH_V1_SMSG_FAILURE || msg_type == SSH_V1_SMSG_SUCCESS) { retval = RET_OK; switch (context->v1_ctx.auth_tries) { case 0: /* * we just got here...don't have to tell the send * thread nothing. */ context->v1_ctx.auth_tries = SSH_V1_CMSG_REQUEST_COMPRESSION; break; case SSH_V1_CMSG_REQUEST_COMPRESSION: #ifdef WITH_COMPRESSION if (msg_type == SSH_V1_SMSG_SUCCESS) { if (context->client->Verbosity) fprintf(stderr, "%s: Enabling compression at " "level %d.\r\n", context->client->localhost, context->client->CompressionLevel); if (!context->transport_ctx.init_decompress) retval = !RET_OK; else retval = (context->transport_ctx.init_decompress) (context, context->client->CompressionLevel); if (retval != RET_OK) { SSH_DLOG(3, ("init_decompress failed")); return RET_FATAL; } ev.event_type = SSH_EVENT_INIT_COMPRESSION; ((u_int32_t *) ev.event_data)[0] = context->client->CompressionLevel; EVT_SEND(&ev, context); } #endif /* WITH_COMPRESSION */ context->v1_ctx.auth_tries = SSH_V1_CMSG_REQUEST_PTY; break; case SSH_V1_CMSG_REQUEST_PTY: /* we've got a pty now...woo-hoo! */ context->v1_ctx.auth_tries = SSH_V1_CMSG_EXEC_CMD; break; case SSH_V1_CMSG_EXEC_CMD: case SSH_V1_CMSG_EXEC_SHELL: /* * we're done. we asked them for it, and we're outta * here */ retval = RET_NEXT_STATE; break; case SSH_V1_CMSG_MAX_PACKET_SIZE: case SSH_V1_CMSG_X11_REQUEST_FORWARDING: case SSH_V1_CMSG_PORT_FORWARD_REQUEST: case SSH_V1_CMSG_AGENT_REQUEST_FORWARDING: retval = RET_FAIL; /* yeah...whatever */ break; } } else { /* we should only get success/failure here, so die */ retval = RET_FAIL; } /* by now, retval is one of _OK, _NEXT_STATE, or _FAIL */ if (retval != RET_OK) return retval; /* otherwise... */ ev.event_type = SSH_EVENT_SEND; evsend = (struct ssh_ev_send *) ev.event_data; /* ***************************** PART 2 ***************************** */ /* ...about to ask for compression, but we don't need it */ if (context->v1_ctx.auth_tries == SSH_V1_CMSG_REQUEST_COMPRESSION && (!context->client->UseCompression || #ifndef WITH_COMPRESSION 1 || /* now we won't ask...ever */ #endif /* WITH_COMPRESSION */ context->client->CompressionLevel == 0)) { context->v1_ctx.auth_tries = SSH_V1_CMSG_REQUEST_PTY; context->client->UseCompression = 0; } /* ...about to ask for a tty, but we neither want nor need it */ if (context->v1_ctx.auth_tries == SSH_V1_CMSG_REQUEST_PTY && !context->client->UseRemoteTTY) context->v1_ctx.auth_tries = SSH_V1_CMSG_EXEC_CMD; /* ...why ask for a remote command to executed if you don't have one? */ if (context->v1_ctx.auth_tries == SSH_V1_CMSG_EXEC_CMD && !context->client->RemoteCommand) context->v1_ctx.auth_tries = SSH_V1_CMSG_EXEC_SHELL; /* the other things we don't do (yet) */ /* ***************************** PART 3 ***************************** */ switch (context->v1_ctx.auth_tries) { case SSH_V1_CMSG_REQUEST_COMPRESSION: #ifdef WITH_COMPRESSION if (context->client->Verbosity) fprintf(stderr, "%s: Requesting compression at level %d.\r\n", context->client->localhost, context->client->CompressionLevel); SSH_DLOG(4, ("Requesting compression.\n")); mybuf = buf_alloc(NULL, 1 + 4); buf_put_int8(mybuf, SSH_V1_CMSG_REQUEST_COMPRESSION); buf_put_int32(mybuf, context->client->CompressionLevel); evsend->dlen = buf_alllen(mybuf); memcpy(evsend->data, buf_alldata(mybuf), buf_alllen(mybuf)); buf_cleanup(mybuf); free(mybuf); break; #else /* WITH_COMPRESSION */ break; /* um...er...heh (shouldn't get here anyway, * so hey!) */ #endif /* WITH_COMPRESSION */ case SSH_V1_CMSG_REQUEST_PTY: if (context->client->Verbosity) fprintf(stderr, "%s: Requesting pty.\r\n", context->client->localhost); SSH_DLOG(4, ("Requesting a pty.\n")); l = strlen(context->term); /* * string length + string + tty data -- ought to be enough, * yesh? */ mybuf = buf_alloc(NULL, 1 + 4 + l + 109); /* send term type, send y, x, x, y, and then send * everythingelse */ buf_put_int8(mybuf, SSH_V1_CMSG_REQUEST_PTY); buf_put_binstr(mybuf, context->term, l); buf_put_int32(mybuf, context->win.ws_row); buf_put_int32(mybuf, context->win.ws_col); buf_put_int32(mybuf, context->win.ws_xpixel); buf_put_int32(mybuf, context->win.ws_ypixel); ssh_sys_put_tty_modes(mybuf, &context->client->tcap); evsend->dlen = buf_alllen(mybuf); memcpy(evsend->data, buf_alldata(mybuf), buf_alllen(mybuf)); buf_cleanup(mybuf); free(mybuf); break; case SSH_V1_CMSG_EXEC_SHELL: if (context->client->Verbosity) fprintf(stderr, "%s: Requesting shell.\r\n", context->client->localhost); SSH_DLOG(4, ("Requesting shell.")); EVT_BUILD_SEND(ev, SSH_V1_CMSG_EXEC_SHELL); /* since the server doesn't *actually* say this is successful */ retval = RET_NEXT_STATE; break; case SSH_V1_CMSG_EXEC_CMD: if (context->client->Verbosity) fprintf(stderr, "%s: Sending command: %s\r\n", context->client->localhost, context->client->RemoteCommand); SSH_DLOG(4, ("Sending command: %s.", context->client->RemoteCommand)); l = strlen(context->client->RemoteCommand); mybuf = buf_alloc(NULL, 1 + l + 4); buf_put_int8(mybuf, SSH_V1_CMSG_EXEC_CMD); buf_put_binstr(mybuf, context->client->RemoteCommand, l); evsend->dlen = buf_alllen(mybuf); memcpy(evsend->data, buf_alldata(mybuf), buf_alllen(mybuf)); buf_cleanup(mybuf); free(mybuf); /* since the server doesn't *actually* say this is successful */ retval = RET_NEXT_STATE; break; } EVT_SEND(&ev, context); return retval; }