From 263d05a296295b2e36ceeca90b3d84999b6c2411 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Mon, 12 Oct 2020 14:45:31 +0300 Subject: [PATCH] Apply read-only perms upon connecting --- common/network/TcpSocket.cxx | 13 +++++++-- common/network/websocket.c | 14 ++++++---- common/network/websocket.h | 4 +++ common/network/websockify.c | 6 +++++ common/rfb/CMakeLists.txt | 3 ++- common/rfb/VNCSConnectionST.cxx | 47 +++++++++++++++++++++++++++++++++ common/rfb/VNCSConnectionST.h | 13 ++++++++- 7 files changed, 91 insertions(+), 9 deletions(-) diff --git a/common/network/TcpSocket.cxx b/common/network/TcpSocket.cxx index f5476e8..c557805 100644 --- a/common/network/TcpSocket.cxx +++ b/common/network/TcpSocket.cxx @@ -123,11 +123,20 @@ WebSocket::WebSocket(int sock) : Socket(sock) } char* WebSocket::getPeerAddress() { - return rfb::strDup("websocket"); + struct sockaddr_un addr; + socklen_t len = sizeof(struct sockaddr_un); + if (getpeername(getFd(), (struct sockaddr *) &addr, &len) != 0) { + vlog.error("unable to get peer name for socket"); + return rfb::strDup("websocket"); + } + return rfb::strDup(addr.sun_path + 1); } char* WebSocket::getPeerEndpoint() { - return rfb::strDup("websocket"); + char buf[1024]; + sprintf(buf, "%s::websocket", getPeerAddress()); + + return rfb::strDup(buf); } // -=- TcpSocket diff --git a/common/network/websocket.c b/common/network/websocket.c index 1471210..0278b76 100644 --- a/common/network/websocket.c +++ b/common/network/websocket.c @@ -107,7 +107,7 @@ ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len) { ws_ctx_t *alloc_ws_ctx() { ws_ctx_t *ctx; - if (! (ctx = malloc(sizeof(ws_ctx_t))) ) + if (! (ctx = calloc(sizeof(ws_ctx_t), 1)) ) { fatal("malloc()"); } if (! (ctx->cin_buf = malloc(BUFSIZE)) ) @@ -930,7 +930,8 @@ ws_ctx_t *do_handshake(int sock) { for (i = 0; i < set->num; i++) { if (!strcmp(set->entries[i].user, inuser)) { - found = 1; // TODO write to wctx + found = 1; + strcpy(ws_ctx->user, inuser); snprintf(authbuf, 4096, "%s:%s", set->entries[i].user, set->entries[i].password); authbuf[4095] = '\0'; @@ -1024,7 +1025,6 @@ void *subthread(void *ptr) { const int csock = pass->csock; wsthread_handler_id = pass->id; - free((void *) pass); ws_ctx_t *ws_ctx; @@ -1034,11 +1034,14 @@ void *subthread(void *ptr) { goto out; // Child process exits } + memcpy(ws_ctx->ip, pass->ip, sizeof(pass->ip)); + proxy_handler(ws_ctx); if (pipe_error) { handler_emsg("Closing due to SIGPIPE\n"); } out: + free((void *) pass); if (ws_ctx) { ws_socket_free(ws_ctx); @@ -1074,12 +1077,13 @@ void *start_server(void *unused) { error("ERROR on accept"); continue; } + struct wspass_t *pass = calloc(1, sizeof(struct wspass_t)); + inet_ntop(cli_addr.sin_family, &cli_addr.sin_addr, pass->ip, sizeof(pass->ip)); fprintf(stderr, " websocket %d: got client connection from %s\n", settings.handler_id, - inet_ntoa(cli_addr.sin_addr)); + pass->ip); pthread_t tid; - struct wspass_t *pass = calloc(1, sizeof(struct wspass_t)); pass->id = settings.handler_id; pass->csock = csock; pthread_create(&tid, NULL, subthread, pass); diff --git a/common/network/websocket.h b/common/network/websocket.h index 18a9b0d..3d757f1 100644 --- a/common/network/websocket.h +++ b/common/network/websocket.h @@ -53,11 +53,15 @@ typedef struct { char *cout_buf; char *tin_buf; char *tout_buf; + + char user[32]; + char ip[64]; } ws_ctx_t; struct wspass_t { int csock; unsigned id; + char ip[64]; }; typedef struct { diff --git a/common/network/websockify.c b/common/network/websockify.c index fe2f018..aaabb9a 100644 --- a/common/network/websockify.c +++ b/common/network/websockify.c @@ -227,7 +227,13 @@ void proxy_handler(ws_ctx_t *ws_ctx) { strcpy(addr.sun_path, ".KasmVNCSock"); addr.sun_path[0] = '\0'; + struct sockaddr_un myaddr; + myaddr.sun_family = AF_UNIX; + sprintf(myaddr.sun_path, ".%s@%s", ws_ctx->user, ws_ctx->ip); + myaddr.sun_path[0] = '\0'; + int tsock = socket(AF_UNIX, SOCK_STREAM, 0); + bind(tsock, (struct sockaddr *) &myaddr, sizeof(struct sockaddr_un)); handler_msg("connecting to VNC target\n"); diff --git a/common/rfb/CMakeLists.txt b/common/rfb/CMakeLists.txt index 67adcf9..38d3472 100644 --- a/common/rfb/CMakeLists.txt +++ b/common/rfb/CMakeLists.txt @@ -1,4 +1,5 @@ -include_directories(${CMAKE_SOURCE_DIR}/common ${JPEG_INCLUDE_DIR}) +include_directories(${CMAKE_SOURCE_DIR}/common ${JPEG_INCLUDE_DIR} + ${CMAKE_SOURCE_DIR}/unix/kasmvncpasswd) set(RFB_SOURCES Blacklist.cxx diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index 18779a5..9b31d6c 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -38,6 +38,9 @@ #include #include #include +#include + +#include "kasmpasswd.h" using namespace rfb; @@ -45,6 +48,8 @@ static LogWriter vlog("VNCSConnST"); static Cursor emptyCursor(0, 0, Point(0, 0), NULL); +extern rfb::StringParameter basicauth; + VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, bool reverse) : sock(s), reverseConnection(reverse), @@ -65,6 +70,25 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, memset(bstats_total, 0, sizeof(bstats_total)); gettimeofday(&connStart, NULL); + // Check their permissions, if applicable + kasmpasswdpath[0] = '\0'; + wordexp_t wexp; + if (!wordexp(rfb::Server::kasmPasswordFile, &wexp, WRDE_NOCMD)) + strncpy(kasmpasswdpath, wexp.we_wordv[0], 4096); + kasmpasswdpath[4095] = '\0'; + wordfree(&wexp); + + user[0] = '\0'; + const char *at = strchr(peerEndpoint.buf, '@'); + if (at && at - peerEndpoint.buf > 1 && at - peerEndpoint.buf < 32) { + memcpy(user, peerEndpoint.buf, at - peerEndpoint.buf); + user[at - peerEndpoint.buf] = '\0'; + } + + bool write, owner; + if (!getPerms(write, owner) || !write) + accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents)); + // Configure the socket setSocketTimeouts(); lastEventTime = time(0); @@ -999,6 +1023,29 @@ bool VNCSConnectionST::isShiftPressed() return false; } +bool VNCSConnectionST::getPerms(bool &write, bool &owner) const +{ + bool found = false; + const char *colon = strchr(basicauth, ':'); + if (colon && !colon[1] && user[0]) { + struct kasmpasswd_t *set = readkasmpasswd(kasmpasswdpath); + unsigned i; + for (i = 0; i < set->num; i++) { + if (!strcmp(set->entries[i].user, user)) { + write = set->entries[i].write; + owner = set->entries[i].owner; + found = true; + break; + } + } + + free(set->entries); + free(set); + } + + return found; +} + void VNCSConnectionST::writeRTTPing() { char type; diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index 76350ef..6c9f3ee 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -184,7 +184,13 @@ namespace rfb { // of a VNCSConnectioST to the server. These access rights are applied // such that the actual rights granted are the minimum of the server's // default access settings and the connection's access settings. - virtual void setAccessRights(AccessRights ar) {accessRights=ar;} + virtual void setAccessRights(AccessRights ar) { + accessRights = ar; + + bool write, owner; + if (!getPerms(write, owner) || !write) + accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents)); + } // Timer callbacks virtual bool handleTimeout(Timer* t); @@ -193,6 +199,8 @@ namespace rfb { bool isShiftPressed(); + bool getPerms(bool &write, bool &owner) const; + // Congestion control void writeRTTPing(); bool isCongested(); @@ -249,6 +257,9 @@ namespace rfb { rdr::U64 bstats_total[BS_NUM]; struct timeval connStart; + char user[32]; + char kasmpasswdpath[4096]; + time_t lastEventTime; time_t pointerEventTime; Point pointerEventPos;