Mercurial > dropbear
diff svr-x11fwd.c @ 12:7a37cff27258
merge of a585c2284e9ad17bfe6c6fd8f18b1c5042b2df47
and e3f735bb16fbd5cfb6bcad70885550c1b79b874c
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Thu, 03 Jun 2004 17:29:17 +0000 |
parents | 0f7d69d31b9d |
children | db2c8e6fb284 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/svr-x11fwd.c Thu Jun 03 17:29:17 2004 +0000 @@ -0,0 +1,235 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#include "includes.h" + +#ifndef DISABLE_X11FWD +#include "x11fwd.h" +#include "session.h" +#include "ssh.h" +#include "dbutil.h" +#include "chansession.h" +#include "channel.h" +#include "packet.h" +#include "buffer.h" + +#define X11BASEPORT 6000 +#define X11BINDBASE 6010 + +static void x11accept(struct Listener* listener); +static int bindport(int fd); +static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr); + +/* called as a request for a session channel, sets up listening X11 */ +/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int x11req(struct ChanSess * chansess) { + + int fd; + + /* we already have an x11 connection */ + if (chansess->x11listener != NULL) { + return DROPBEAR_FAILURE; + } + + chansess->x11singleconn = buf_getbyte(ses.payload); + chansess->x11authprot = buf_getstring(ses.payload, NULL); + chansess->x11authcookie = buf_getstring(ses.payload, NULL); + chansess->x11screennum = buf_getint(ses.payload); + + /* create listening socket */ + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) { + goto fail; + } + + /* allocate port and bind */ + chansess->x11port = bindport(fd); + if (chansess->x11port < 0) { + goto fail; + } + + /* listen */ + if (listen(fd, 20) < 0) { + goto fail; + } + + /* set non-blocking */ + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { + goto fail; + } + + /* listener code will handle the socket now. + * No cleanup handler needed, since listener_remove only happens + * from our cleanup anyway */ + chansess->x11listener = new_listener( fd, 0, chansess, x11accept, NULL); + if (chansess->x11listener == NULL) { + goto fail; + } + + return DROPBEAR_SUCCESS; + +fail: + /* cleanup */ + m_free(chansess->x11authprot); + m_free(chansess->x11authcookie); + close(fd); + + return DROPBEAR_FAILURE; +} + +/* accepts a new X11 socket */ +/* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */ +static void x11accept(struct Listener* listener) { + + int fd; + struct sockaddr_in addr; + int len; + int ret; + + len = sizeof(addr); + + fd = accept(listener->sock, (struct sockaddr*)&addr, &len); + if (fd < 0) { + return; + } + + /* if single-connection we close it up */ + if (((struct ChanSess *)(listener->typedata))->x11singleconn) { + x11cleanup(listener); + } + + ret = send_msg_channel_open_x11(fd, &addr); + if (ret == DROPBEAR_FAILURE) { + close(fd); + } +} + +/* This is called after switching to the user, and sets up the xauth + * and environment variables. */ +void x11setauth(struct ChanSess *chansess) { + + char display[20]; /* space for "localhost:12345.123" */ + FILE * authprog; + int val; + + if (chansess->x11listener == NULL) { + return; + } + + /* create the DISPLAY string */ + val = snprintf(display, sizeof(display), "localhost:%d.%d", + chansess->x11port - X11BASEPORT, chansess->x11screennum); + if (val < 0 || val >= (int)sizeof(display)) { + /* string was truncated */ + return; + } + + addnewvar("DISPLAY", display); + + /* create the xauth string */ + val = snprintf(display, sizeof(display), "unix:%d.%d", + chansess->x11port - X11BASEPORT, chansess->x11screennum); + if (val < 0 || val >= (int)sizeof(display)) { + /* string was truncated */ + return; + } + + /* popen is a nice function - code is strongly based on OpenSSH's */ + authprog = popen(XAUTH_COMMAND, "w"); + if (authprog) { + fprintf(authprog, "add %s %s %s\n", + display, chansess->x11authprot, chansess->x11authcookie); + pclose(authprog); + } else { + fprintf(stderr, "Failed to run %s\n", XAUTH_COMMAND); + } +} + +void x11cleanup(struct Listener *listener) { + + struct ChanSess *chansess = (struct ChanSess*)listener->typedata; + + m_free(chansess->x11authprot); + m_free(chansess->x11authcookie); + remove_listener(listener); + chansess->x11listener = NULL; +} + +static const struct ChanType chan_x11 = { + 0, /* sepfds */ + "x11", + NULL, /* inithandler */ + NULL, /* checkclose */ + NULL, /* reqhandler */ + NULL /* closehandler */ +}; + + +static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr) { + + char* ipstring; + + if (send_msg_channel_open_init(fd, &chan_x11) == DROPBEAR_SUCCESS) { + ipstring = inet_ntoa(addr->sin_addr); + buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); + buf_putint(ses.writepayload, addr->sin_port); + + encrypt_packet(); + return DROPBEAR_SUCCESS; + } else { + return DROPBEAR_FAILURE; + } + +} + +/* returns the port bound to, or -1 on failure. + * Will attempt to bind to a port X11BINDBASE (6010 usually) or upwards */ +static int bindport(int fd) { + + struct sockaddr_in addr; + uint16_t port; + + memset((void*)&addr, 0x0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* if we can't find one in 2000 ports free, something's wrong */ + for (port = X11BINDBASE; port < X11BINDBASE + 2000; port++) { + addr.sin_port = htons(port); + if (bind(fd, (struct sockaddr*)&addr, + sizeof(struct sockaddr_in)) == 0) { + /* success */ + return port; + } + if (errno == EADDRINUSE) { + /* try the next port */ + continue; + } + /* otherwise it was an error we don't know about */ + dropbear_log(LOG_DEBUG, "failed to bind x11 socket"); + break; + } + return -1; +} +#endif /* DROPBEAR_X11FWD */