#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"
#include "auth.h"
#define X11BASEPORT 6000
#define X11BINDBASE 6010
static
void
x11accept(
struct
Listener* listener,
int
sock);
static
int
bindport(
int
fd);
static
int
send_msg_channel_open_x11(
int
fd,
struct
sockaddr_in* addr);
int
x11req(
struct
ChanSess * chansess) {
int
fd;
if
(!svr_pubkey_allows_x11fwd()) {
return
DROPBEAR_FAILURE;
}
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);
fd = socket(PF_INET, SOCK_STREAM, 0);
if
(fd < 0) {
goto
fail;
}
chansess->x11port = bindport(fd);
if
(chansess->x11port < 0) {
goto
fail;
}
if
(listen(fd, 20) < 0) {
goto
fail;
}
setnonblocking(fd);
chansess->x11listener = new_listener( &fd, 1, 0, chansess, x11accept, NULL);
if
(chansess->x11listener == NULL) {
goto
fail;
}
return
DROPBEAR_SUCCESS;
fail:
m_free(chansess->x11authprot);
m_free(chansess->x11authcookie);
close(fd);
return
DROPBEAR_FAILURE;
}
static
void
x11accept(
struct
Listener* listener,
int
sock) {
int
fd;
struct
sockaddr_in addr;
int
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
(chansess->x11singleconn) {
x11cleanup(chansess);
}
ret = send_msg_channel_open_x11(fd, &addr);
if
(ret == DROPBEAR_FAILURE) {
close(fd);
}
}
void
x11setauth(
struct
ChanSess *chansess) {
char
display[20];
FILE
* authprog = NULL;
int
val;
if
(chansess->x11listener == NULL) {
return
;
}
val = snprintf(display,
sizeof
(display),
"localhost:%d.%d"
,
chansess->x11port - X11BASEPORT, chansess->x11screennum);
if
(val < 0 || val >= (
int
)
sizeof
(display)) {
return
;
}
addnewvar(
"DISPLAY"
, display);
val = snprintf(display,
sizeof
(display),
"unix:%d.%d"
,
chansess->x11port - X11BASEPORT, chansess->x11screennum);
if
(val < 0 || val >= (
int
)
sizeof
(display)) {
return
;
}
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"
, 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 = {
0,
"x11"
,
x11_inithandler,
NULL,
NULL,
NULL
};
static
int
send_msg_channel_open_x11(
int
fd,
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;
}
}
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);
for
(port = X11BINDBASE; port < X11BINDBASE + 2000; port++) {
addr.sin_port = htons(port);
if
(bind(fd, (
struct
sockaddr*)&addr,
sizeof
(
struct
sockaddr_in)) == 0) {
return
port;
}
if
(
errno
== EADDRINUSE) {
continue
;
}
dropbear_log(LOG_DEBUG,
"Failed to bind x11 socket"
);
break
;
}
return
-1;
}
#endif /* DROPBEAR_X11FWD */