Home | History | Annotate | Download | only in dropbear
      1 /*
      2  * Dropbear - a SSH2 server
      3  *
      4  * Copyright (c) 2002,2003 Matt Johnston
      5  * All rights reserved.
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining a copy
      8  * of this software and associated documentation files (the "Software"), to deal
      9  * in the Software without restriction, including without limitation the rights
     10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     11  * copies of the Software, and to permit persons to whom the Software is
     12  * furnished to do so, subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice shall be included in
     15  * all copies or substantial portions of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     23  * SOFTWARE. */
     24 
     25 #include "includes.h"
     26 
     27 #ifndef DISABLE_X11FWD
     28 #include "x11fwd.h"
     29 #include "session.h"
     30 #include "ssh.h"
     31 #include "dbutil.h"
     32 #include "chansession.h"
     33 #include "channel.h"
     34 #include "packet.h"
     35 #include "buffer.h"
     36 
     37 #define X11BASEPORT 6000
     38 #define X11BINDBASE 6010
     39 
     40 static void x11accept(struct Listener* listener, int sock);
     41 static int bindport(int fd);
     42 static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr);
     43 
     44 /* called as a request for a session channel, sets up listening X11 */
     45 /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
     46 int x11req(struct ChanSess * chansess) {
     47 
     48 	int fd;
     49 
     50 	/* we already have an x11 connection */
     51 	if (chansess->x11listener != NULL) {
     52 		return DROPBEAR_FAILURE;
     53 	}
     54 
     55 	chansess->x11singleconn = buf_getbool(ses.payload);
     56 	chansess->x11authprot = buf_getstring(ses.payload, NULL);
     57 	chansess->x11authcookie = buf_getstring(ses.payload, NULL);
     58 	chansess->x11screennum = buf_getint(ses.payload);
     59 
     60 	/* create listening socket */
     61 	fd = socket(PF_INET, SOCK_STREAM, 0);
     62 	if (fd < 0) {
     63 		goto fail;
     64 	}
     65 
     66 	/* allocate port and bind */
     67 	chansess->x11port = bindport(fd);
     68 	if (chansess->x11port < 0) {
     69 		goto fail;
     70 	}
     71 
     72 	/* listen */
     73 	if (listen(fd, 20) < 0) {
     74 		goto fail;
     75 	}
     76 
     77 	/* set non-blocking */
     78 	setnonblocking(fd);
     79 
     80 	/* listener code will handle the socket now.
     81 	 * No cleanup handler needed, since listener_remove only happens
     82 	 * from our cleanup anyway */
     83 	chansess->x11listener = new_listener( &fd, 1, 0, chansess, x11accept, NULL);
     84 	if (chansess->x11listener == NULL) {
     85 		goto fail;
     86 	}
     87 
     88 	return DROPBEAR_SUCCESS;
     89 
     90 fail:
     91 	/* cleanup */
     92 	m_free(chansess->x11authprot);
     93 	m_free(chansess->x11authcookie);
     94 	close(fd);
     95 
     96 	return DROPBEAR_FAILURE;
     97 }
     98 
     99 /* accepts a new X11 socket */
    100 /* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */
    101 static void x11accept(struct Listener* listener, int sock) {
    102 
    103 	int fd;
    104 	struct sockaddr_in addr;
    105 	int len;
    106 	int ret;
    107 	struct ChanSess * chansess = (struct ChanSess *)(listener->typedata);
    108 
    109 	len = sizeof(addr);
    110 
    111 	fd = accept(sock, (struct sockaddr*)&addr, &len);
    112 	if (fd < 0) {
    113 		return;
    114 	}
    115 
    116 	/* if single-connection we close it up */
    117 	if (chansess->x11singleconn) {
    118 		x11cleanup(chansess);
    119 	}
    120 
    121 	ret = send_msg_channel_open_x11(fd, &addr);
    122 	if (ret == DROPBEAR_FAILURE) {
    123 		close(fd);
    124 	}
    125 }
    126 
    127 /* This is called after switching to the user, and sets up the xauth
    128  * and environment variables.  */
    129 void x11setauth(struct ChanSess *chansess) {
    130 
    131 	char display[20]; /* space for "localhost:12345.123" */
    132 	FILE * authprog = NULL;
    133 	int val;
    134 
    135 	if (chansess->x11listener == NULL) {
    136 		return;
    137 	}
    138 
    139 	/* create the DISPLAY string */
    140 	val = snprintf(display, sizeof(display), "localhost:%d.%d",
    141 			chansess->x11port - X11BASEPORT, chansess->x11screennum);
    142 	if (val < 0 || val >= (int)sizeof(display)) {
    143 		/* string was truncated */
    144 		return;
    145 	}
    146 
    147 	addnewvar("DISPLAY", display);
    148 
    149 	/* create the xauth string */
    150 	val = snprintf(display, sizeof(display), "unix:%d.%d",
    151 			chansess->x11port - X11BASEPORT, chansess->x11screennum);
    152 	if (val < 0 || val >= (int)sizeof(display)) {
    153 		/* string was truncated */
    154 		return;
    155 	}
    156 
    157 	/* popen is a nice function - code is strongly based on OpenSSH's */
    158 	authprog = popen(XAUTH_COMMAND, "w");
    159 	if (authprog) {
    160 		fprintf(authprog, "add %s %s %s\n",
    161 				display, chansess->x11authprot, chansess->x11authcookie);
    162 		pclose(authprog);
    163 	} else {
    164 		fprintf(stderr, "Failed to run %s\n", XAUTH_COMMAND);
    165 	}
    166 }
    167 
    168 void x11cleanup(struct ChanSess *chansess) {
    169 
    170 	m_free(chansess->x11authprot);
    171 	m_free(chansess->x11authcookie);
    172 
    173 	TRACE(("chansess %s", chansess))
    174 	if (chansess->x11listener != NULL) {
    175 		remove_listener(chansess->x11listener);
    176 		chansess->x11listener = NULL;
    177 	}
    178 }
    179 
    180 static const struct ChanType chan_x11 = {
    181 	0, /* sepfds */
    182 	"x11",
    183 	NULL, /* inithandler */
    184 	NULL, /* checkclose */
    185 	NULL, /* reqhandler */
    186 	NULL /* closehandler */
    187 };
    188 
    189 
    190 static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr) {
    191 
    192 	char* ipstring = NULL;
    193 
    194 	if (send_msg_channel_open_init(fd, &chan_x11) == DROPBEAR_SUCCESS) {
    195 		ipstring = inet_ntoa(addr->sin_addr);
    196 		buf_putstring(ses.writepayload, ipstring, strlen(ipstring));
    197 		buf_putint(ses.writepayload, addr->sin_port);
    198 
    199 		encrypt_packet();
    200 		return DROPBEAR_SUCCESS;
    201 	} else {
    202 		return DROPBEAR_FAILURE;
    203 	}
    204 
    205 }
    206 
    207 /* returns the port bound to, or -1 on failure.
    208  * Will attempt to bind to a port X11BINDBASE (6010 usually) or upwards */
    209 static int bindport(int fd) {
    210 
    211 	struct sockaddr_in addr;
    212 	uint16_t port;
    213 
    214 	memset((void*)&addr, 0x0, sizeof(addr));
    215 	addr.sin_family = AF_INET;
    216 	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    217 
    218 	/* if we can't find one in 2000 ports free, something's wrong */
    219 	for (port = X11BINDBASE; port < X11BINDBASE + 2000; port++) {
    220 		addr.sin_port = htons(port);
    221 		if (bind(fd, (struct sockaddr*)&addr,
    222 					sizeof(struct sockaddr_in)) == 0) {
    223 			/* success */
    224 			return port;
    225 		}
    226 		if (errno == EADDRINUSE) {
    227 			/* try the next port */
    228 			continue;
    229 		}
    230 		/* otherwise it was an error we don't know about */
    231 		dropbear_log(LOG_DEBUG, "failed to bind x11 socket");
    232 		break;
    233 	}
    234 	return -1;
    235 }
    236 #endif /* DROPBEAR_X11FWD */
    237