123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- /*
- * 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"
- #if DROPBEAR_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"
- #include "auth.h"
- #define X11BASEPORT 6000
- #define X11BINDBASE 6010
- static void x11accept(const struct Listener* listener, int sock);
- static int bindport(int fd);
- static int send_msg_channel_open_x11(int fd, const struct sockaddr_in* addr);
- /* Check untrusted xauth strings for metacharacters */
- /* Returns DROPBEAR_SUCCESS/DROPBEAR_FAILURE */
- static int
- xauth_valid_string(const char *s)
- {
- size_t i;
- for (i = 0; s[i] != '\0'; i++) {
- if (!isalnum(s[i]) &&
- s[i] != '.' && s[i] != ':' && s[i] != '/' &&
- s[i] != '-' && s[i] != '_') {
- return DROPBEAR_FAILURE;
- }
- }
- return DROPBEAR_SUCCESS;
- }
- /* 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 = -1;
- if (!svr_pubkey_allows_x11fwd()) {
- return DROPBEAR_FAILURE;
- }
- /* we already have an x11 connection */
- if (chansess->x11listener != NULL) {
- return DROPBEAR_FAILURE;
- }
- chansess->x11singleconn = buf_getbool(ses.payload);
- chansess->x11authprot = buf_getstring(ses.payload, NULL);
- chansess->x11authcookie = buf_getstring(ses.payload, NULL);
- chansess->x11screennum = buf_getint(ses.payload);
- if (xauth_valid_string(chansess->x11authprot) == DROPBEAR_FAILURE ||
- xauth_valid_string(chansess->x11authcookie) == DROPBEAR_FAILURE) {
- dropbear_log(LOG_WARNING, "Bad xauth request");
- goto fail;
- }
- /* 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 */
- setnonblocking(fd);
- /* 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, 1, 0, chansess, x11accept, NULL);
- if (chansess->x11listener == NULL) {
- goto fail;
- }
- return DROPBEAR_SUCCESS;
- fail:
- /* cleanup */
- m_free(chansess->x11authprot);
- m_free(chansess->x11authcookie);
- m_close(fd);
- return DROPBEAR_FAILURE;
- }
- /* accepts a new X11 socket */
- /* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */
- static void x11accept(const struct Listener* listener, int sock) {
- int fd;
- struct sockaddr_in addr;
- socklen_t len;
- int ret;
- struct ChanSess * chansess = (struct ChanSess *)(listener->typedata);
- len = sizeof(addr);
- fd = accept(sock, (struct sockaddr*)&addr, &len);
- if (fd < 0) {
- return;
- }
- /* if single-connection we close it up */
- if (chansess->x11singleconn) {
- x11cleanup(chansess);
- }
- 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(const struct ChanSess *chansess) {
- char display[20]; /* space for "localhost:12345.123" */
- FILE * authprog = NULL;
- int val;
- if (chansess->x11listener == NULL) {
- return;
- }
- /* create the DISPLAY string */
- val = snprintf(display, sizeof(display), "localhost:%d.%u",
- 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.%u",
- chansess->x11port - X11BASEPORT, chansess->x11screennum);
- if (val < 0 || val >= (int)sizeof(display)) {
- /* string was truncated */
- return;
- }
- /* 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 ChanSess *chansess) {
- m_free(chansess->x11authprot);
- m_free(chansess->x11authcookie);
- TRACE(("chansess %p", (void*)chansess))
- if (chansess->x11listener != NULL) {
- remove_listener(chansess->x11listener);
- chansess->x11listener = NULL;
- }
- }
- static int x11_inithandler(struct Channel *channel) {
- channel->prio = DROPBEAR_CHANNEL_PRIO_INTERACTIVE;
- return 0;
- }
- static const struct ChanType chan_x11 = {
- "x11",
- x11_inithandler, /* inithandler */
- NULL, /* checkclose */
- NULL, /* reqhandler */
- NULL, /* closehandler */
- NULL /* cleanup */
- };
- static int send_msg_channel_open_x11(int fd, const struct sockaddr_in* addr) {
- char* ipstring = NULL;
- 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 */
|