mirror of
http://galexander.org/git/simplesshd.git
synced 2024-12-28 17:08:08 +00:00
271 lines
6.9 KiB
C
271 lines
6.9 KiB
C
/*
|
|
* 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 = {
|
|
0, /* sepfds */
|
|
"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 */
|