/* $Id: ssh_v1_prep_server.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_server: pre-exec state. * User is already authenticated. * We are running as the user now. * * 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_server(ssh_context_t * context, struct ssh_buf * buf, size_t size, int msg_type) { u_int32_t tmp_int32; struct ssh_event ev; int retval; int ret; ret = RET_OK; switch (msg_type) { case SSH_V1_CMSG_MAX_PACKET_SIZE: buf_get_int32(buf, &tmp_int32); SSH_DLOG(4, ("Client requested max packet size:%d\n", tmp_int32)); if (tmp_int32 < 4096 || tmp_int32 > (1024 * 1024 * 1024)) { SSH_DLOG(3, ("Invalid max packet size:%d\n", tmp_int32)); EVT_V1_DISCONNECT(ev, context, "Invalid max packet size"); ret = RET_FAIL; break; } (context->transport_ctx.set_max_packet_size) (&context->transport_ctx, tmp_int32); /* Not necessary? */ ev.event_type = SSH_EVENT_MAX_PACKET_SIZE; ((u_int32_t *) ev.event_data)[0] = tmp_int32; break; case SSH_V1_CMSG_REQUEST_PTY: { u_int8_t *term, *modes; int y_row, x_col, x_pix, y_pix; int msize; modes = term = NULL; SSH_DLOG(4, ("Client requested a pty.\n")); if (context->usepty) { EVT_V1_DISCONNECT(ev, context, "You already requested a pty."); ret = RET_FAIL; break; } /* V1 and V2 share this layout */ buf_get_binstr(buf, &term, NULL); buf_get_int32(buf, &y_row); buf_get_int32(buf, &x_col); buf_get_int32(buf, &x_pix); buf_get_int32(buf, &y_pix); buf_get_unk_bytes(buf, &modes, &msize); if (ssh_sys_allocpty(context, term, y_row, x_col, x_pix, y_pix, modes, msize) != 0) { SSH_DLOG(1, ("Failed to allocate pty: %s\n", strerror(errno))); EVT_V1_FAILURE(ev); } else { EVT_V1_SUCCESS(ev); } free(term); free(modes); break; } case SSH_V1_CMSG_REQUEST_COMPRESSION: SSH_DLOG(4, ("Client requested compression.\n")); buf_get_int32(buf, &tmp_int32); /* * Init compression */ if (context->transport_ctx.init_decompress == NULL) { /* Shouldn't happen... */ SSH_ERROR("init_decompress function pointer is NULL!"); EVT_V1_DISCONNECT(ev, context, "init_decompress ptr NULL!"); ret = RET_FATAL; break; } retval = (context->transport_ctx.init_decompress) (context, tmp_int32); if (retval != RET_OK) { SSH_DLOG(3, ("init_decompress failed")); EVT_V1_FAILURE(ev); break; } /* * XXX It is only safe to send this here b/c we know the * XXX sendThread has no sources of data to send yet. If * XXX this fact changes then the INIT_COMPRESSION event will * XXX need to imply the sending of a success message. */ EVT_V1_SUCCESS(ev); EVT_SEND(&ev, context); ev.event_type = SSH_EVENT_INIT_COMPRESSION; ((u_int32_t *) ev.event_data)[0] = tmp_int32; break; case SSH_V1_CMSG_EXEC_SHELL: case SSH_V1_CMSG_EXEC_CMD: { struct ssh_ev_channel_open *evch; u_int8_t *cmd; int fds[2]; cmd = NULL; if (msg_type == SSH_V1_CMSG_EXEC_SHELL) { SSH_DLOG(4, ("Client requests shell.\n")); cmd = strdup(""); } else { SSH_DLOG(4, ("Client requested command exec.\n")); buf_get_binstr(buf, &cmd, NULL); } if (ssh_sys_execcmd(context, cmd, fds) != 0) { EVT_V1_DISCONNECT(ev, context, "Command execution failed"); free(cmd); ret = RET_FAIL; break; } free(cmd); /* * Send the fd's of the child process to the sendThread * If stderr and stdout are the same only send one. * * First open a session type channel in both threads. * Then send the FDs attached to this channel. * * Define channel 0 to always be the one used for this. */ ev.event_type = SSH_EVENT_CHANNEL_OPEN; evch = (struct ssh_ev_channel_open *) ev.event_data; evch->channel_type = SSH_CHANNEL_SESSION; evch->channel_id = 0; evch->remote_id = 0; evch->initial_window_size = (u_int32_t) - 1; evch->max_packet_size = (context->transport_ctx.get_sendsize) (&context->transport_ctx); EVT_SEND(&ev, context); retval = create_channel(context, evch->channel_id, evch->remote_id, evch->initial_window_size, evch->max_packet_size); if (retval != RET_OK) { if (retval == RET_FAIL) { EVT_V1_DISCONNECT(ev, context, "Unable to create channel"); ret = RET_FAIL; break; } else { SSH_ERROR("create_channel fatal error"); EVT_V1_DISCONNECT(ev, context, "Unable to create channel"); ret = RET_FATAL; break; } } if (fds[0] != fds[1]) { if (ssh_sys_send_fd(context, fds[1], FDTYPE_EXT_STDERR, 0) < 0) { SSH_ERROR("ssh_sys_send_fd failed!"); ssh_exit(context, 1, EXIT_NOW); } /* * Don't need stderr in the recv thread. */ close(fds[1]); } if (ssh_sys_send_fd(context, fds[0], FDTYPE_NORMAL, 0) < 0) { SSH_ERROR("ssh_sys_send_fd(2) failed!"); ssh_exit(context, 1, EXIT_NOW); } /* * don't close fds[0]: it's stdout and stdin. * Instead attach it to channel 0. */ if (chan_attach_fd(context, fds[0], FDTYPE_NORMAL, 0) < 0) { SSH_ERROR("chan_attach_fd failed!"); ssh_exit(context, 1, EXIT_NOW); } ret = RET_NEXT_STATE; /* * No response to send, just return now. */ return (ret); break; } case SSH_V1_CMSG_X11_REQUEST_FORWARDING: SSH_DLOG(4, ("Client requested X11 forwarding.\n")); EVT_V1_DEBUG(ev, context, "X11 forwarding unimplemented."); EVT_SEND(&ev, context); EVT_V1_FAILURE(ev); break; case SSH_V1_CMSG_PORT_FORWARD_REQUEST: SSH_DLOG(4, ("Client requested port forwarding.\n")); EVT_V1_DEBUG(ev, context, "Port forwarding unimplemented."); EVT_SEND(&ev, context); EVT_V1_FAILURE(ev); break; case SSH_V1_CMSG_AGENT_REQUEST_FORWARDING: SSH_DLOG(4, ("Client requested agent forwarding.\n")); EVT_V1_DEBUG(ev, context, "Agent forwarding unimplemented."); EVT_SEND(&ev, context); EVT_V1_FAILURE(ev); break; default: SSH_DLOG(2, ("Unknown client request:%d\n", msg_type)); EVT_V1_FAILURE(ev); break; } EVT_SEND(&ev, context); return (ret); }