Merge pull request #26 from kasmtech/multiuser_and_new_ui
Packaging changes, multiuser passwd, clipboard (UI) changes
This commit is contained in:
16
builder/build-and-test-deb
Executable file
16
builder/build-and-test-deb
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
os="$1"
|
||||||
|
codename="$2"
|
||||||
|
|
||||||
|
if [[ -z "$os" ]] || [[ -z "$codename" ]]; then
|
||||||
|
echo "Usage: $0 <os> <codename>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
./build-tarball "$os" "$codename" && ./build-deb "$os" "$codename" && \
|
||||||
|
./test-deb "$os" "$codename"
|
||||||
16
builder/build-and-test-rpm
Executable file
16
builder/build-and-test-rpm
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
os="$1"
|
||||||
|
codename="$2"
|
||||||
|
|
||||||
|
if [[ -z "$os" ]] || [[ -z "$codename" ]]; then
|
||||||
|
echo "Usage: $0 <os> <codename>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
./build-tarball "$os" "$codename" && ./build-rpm "$os" "$codename" && \
|
||||||
|
./test-rpm "$os" "$codename"
|
||||||
@@ -34,13 +34,31 @@ detect_cert_location() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add_vnc_user() {
|
||||||
|
local username="$1"
|
||||||
|
local password="$2"
|
||||||
|
local permission_option="$3"
|
||||||
|
|
||||||
|
echo "Adding user $username"
|
||||||
|
echo -e "$password\n$password" | kasmvncpasswd $permission_option \
|
||||||
|
-u "$username" $HOME/.kasmpasswd
|
||||||
|
}
|
||||||
|
|
||||||
## resolve_vnc_connection
|
## resolve_vnc_connection
|
||||||
VNC_IP=$(hostname -i)
|
VNC_IP=$(hostname -i)
|
||||||
|
|
||||||
# first entry is control, second is view (if only one is valid for both)
|
# first entry is control, second is view (if only one is valid for both)
|
||||||
mkdir -p "$HOME/.vnc"
|
mkdir -p "$HOME/.vnc"
|
||||||
PASSWD_PATH="$HOME/.vnc/passwd"
|
PASSWD_PATH="$HOME/.vnc/passwd"
|
||||||
echo "$VNC_PW" | kasmvncpasswd -f > $HOME/.kasmpasswd
|
# echo -e "$VNC_PW\n$VNC_PW" | kasmvncpasswd -w -u $VNC_USER $HOME/.kasmpasswd
|
||||||
|
add_vnc_user "$VNC_USER" "$VNC_PW" "-w"
|
||||||
|
add_vnc_user "$VNC_USER-ro" "$VNC_PW"
|
||||||
|
add_vnc_user "$VNC_USER-owner" "$VNC_PW" "-o"
|
||||||
|
add_vnc_user "$VNC_USER-to-delete" "$VNC_PW"
|
||||||
|
|
||||||
|
kasmvncpasswd -n -u "$VNC_USER-owner" -w $HOME/.kasmpasswd
|
||||||
|
kasmvncpasswd -d -u "$VNC_USER-to-delete" $HOME/.kasmpasswd
|
||||||
|
|
||||||
chmod 0600 $HOME/.kasmpasswd
|
chmod 0600 $HOME/.kasmpasswd
|
||||||
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout $HOME/.vnc/self.pem -out $HOME/.vnc/self.pem -subj "/C=US/ST=VA/L=None/O=None/OU=DoFu/CN=kasm/emailAddress=none@none.none"
|
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout $HOME/.vnc/self.pem -out $HOME/.vnc/self.pem -subj "/C=US/ST=VA/L=None/O=None/OU=DoFu/CN=kasm/emailAddress=none@none.none"
|
||||||
|
|
||||||
@@ -69,9 +87,10 @@ vncserver -kill $DISPLAY &> $HOME/.vnc/vnc_startup.log \
|
|||||||
|
|
||||||
detect_www_dir
|
detect_www_dir
|
||||||
detect_cert_location
|
detect_cert_location
|
||||||
|
[ -n "$KASMVNC_VERBOSE_LOGGING" ] && verbose_logging_option="-log *:stderr:100"
|
||||||
|
|
||||||
echo -e "start vncserver with param: VNC_COL_DEPTH=$VNC_COL_DEPTH, VNC_RESOLUTION=$VNC_RESOLUTION\n..."
|
echo -e "start vncserver with param: VNC_COL_DEPTH=$VNC_COL_DEPTH, VNC_RESOLUTION=$VNC_RESOLUTION\n..."
|
||||||
vncserver $DISPLAY -depth $VNC_COL_DEPTH -geometry $VNC_RESOLUTION -FrameRate=$MAX_FRAME_RATE -websocketPort $VNC_PORT $cert_option -sslOnly -interface 0.0.0.0 $VNCOPTIONS $package_www_dir_option #&> $STARTUPDIR/no_vnc_startup.log
|
vncserver $DISPLAY -depth $VNC_COL_DEPTH -geometry $VNC_RESOLUTION -FrameRate=$MAX_FRAME_RATE -websocketPort $VNC_PORT $cert_option -sslOnly -interface 0.0.0.0 $VNCOPTIONS $package_www_dir_option $verbose_logging_option #&> $STARTUPDIR/no_vnc_startup.log
|
||||||
|
|
||||||
PID_SUN=$!
|
PID_SUN=$!
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,7 @@ cd "$(dirname "$0")"
|
|||||||
docker build --build-arg KASMVNC_PACKAGE_DIR="build/${os_codename}" \
|
docker build --build-arg KASMVNC_PACKAGE_DIR="build/${os_codename}" \
|
||||||
-t kasmvnctester_${os}:$os_codename \
|
-t kasmvnctester_${os}:$os_codename \
|
||||||
-f dockerfile.${os}_${os_codename}.deb.test .
|
-f dockerfile.${os}_${os_codename}.deb.test .
|
||||||
echo docker run -it -p 443:8443 --rm -e "VNC_USER=foo" -e "VNC_PW=bar" \
|
docker run -it -p 443:8443 --rm \
|
||||||
|
-e KASMVNC_VERBOSE_LOGGING=$KASMVNC_VERBOSE_LOGGING \
|
||||||
|
-e "VNC_USER=foo" -e "VNC_PW=foobar" \
|
||||||
kasmvnctester_${os}:$os_codename
|
kasmvnctester_${os}:$os_codename
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ docker build --build-arg KASMVNC_PACKAGE_DIR="build/${os_codename}" \
|
|||||||
-f dockerfile.${os}_${os_codename}.barebones.deb.test .
|
-f dockerfile.${os}_${os_codename}.barebones.deb.test .
|
||||||
echo
|
echo
|
||||||
echo "You will be asked to set password. User name is docker."
|
echo "You will be asked to set password. User name is docker."
|
||||||
docker run -it -p 443:8443 --rm kasmvnctester_barebones_${os}:$os_codename
|
docker run -it -p 443:8443 --rm -e "VNC_USER=foo" -e "VNC_PW=foobar" \
|
||||||
|
kasmvnctester_barebones_${os}:$os_codename
|
||||||
|
|||||||
@@ -10,5 +10,8 @@ docker build --build-arg \
|
|||||||
KASMVNC_PACKAGE_DIR="build/${os}_${os_codename}" \
|
KASMVNC_PACKAGE_DIR="build/${os}_${os_codename}" \
|
||||||
-t kasmvnctester_${os}:$os_codename \
|
-t kasmvnctester_${os}:$os_codename \
|
||||||
-f dockerfile.${os}_${os_codename}.rpm.test .
|
-f dockerfile.${os}_${os_codename}.rpm.test .
|
||||||
echo docker run -it -p 443:8443 --rm -e "VNC_USER=foo" -e "VNC_PW=bar" \
|
|
||||||
|
docker run -it -p 443:8443 --rm \
|
||||||
|
-e KASMVNC_VERBOSE_LOGGING=$KASMVNC_VERBOSE_LOGGING \
|
||||||
|
-e "VNC_USER=foo" -e "VNC_PW=foobar" \
|
||||||
kasmvnctester_${os}:$os_codename
|
kasmvnctester_${os}:$os_codename
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ os_codename="${2:-core}"
|
|||||||
docker build --build-arg KASMVNC_PACKAGE_DIR="build/${os}_${os_codename}" \
|
docker build --build-arg KASMVNC_PACKAGE_DIR="build/${os}_${os_codename}" \
|
||||||
-t kasmvnctester_barebones_${os}:$os_codename \
|
-t kasmvnctester_barebones_${os}:$os_codename \
|
||||||
-f dockerfile.${os}_${os_codename}.barebones.rpm.test .
|
-f dockerfile.${os}_${os_codename}.barebones.rpm.test .
|
||||||
docker run -it -p 443:8443 --rm -e "VNC_USER=foo" -e "VNC_PW=bar" \
|
docker run -it -p 443:8443 --rm -e "VNC_USER=foo" -e "VNC_PW=foobar" \
|
||||||
kasmvnctester_barebones_${os}:$os_codename
|
kasmvnctester_barebones_${os}:$os_codename
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
include_directories(${CMAKE_SOURCE_DIR}/common)
|
include_directories(${CMAKE_SOURCE_DIR}/common ${CMAKE_SOURCE_DIR}/unix/kasmvncpasswd)
|
||||||
|
|
||||||
set(NETWORK_SOURCES
|
set(NETWORK_SOURCES
|
||||||
Socket.cxx
|
Socket.cxx
|
||||||
TcpSocket.cxx
|
TcpSocket.cxx
|
||||||
websocket.c
|
websocket.c
|
||||||
websockify.c)
|
websockify.c
|
||||||
|
${CMAKE_SOURCE_DIR}/unix/kasmvncpasswd/kasmpasswd.c)
|
||||||
|
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
set(NETWORK_SOURCES ${NETWORK_SOURCES} UnixSocket.cxx)
|
set(NETWORK_SOURCES ${NETWORK_SOURCES} UnixSocket.cxx)
|
||||||
|
|||||||
@@ -124,11 +124,20 @@ WebSocket::WebSocket(int sock) : Socket(sock)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char* WebSocket::getPeerAddress() {
|
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() {
|
char* WebSocket::getPeerEndpoint() {
|
||||||
return rfb::strDup("websocket");
|
char buf[1024];
|
||||||
|
sprintf(buf, "%s::websocket", getPeerAddress());
|
||||||
|
|
||||||
|
return rfb::strDup(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -=- TcpSocket
|
// -=- TcpSocket
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <openssl/md5.h> /* md5 hash */
|
#include <openssl/md5.h> /* md5 hash */
|
||||||
#include <openssl/sha.h> /* sha1 hash */
|
#include <openssl/sha.h> /* sha1 hash */
|
||||||
#include "websocket.h"
|
#include "websocket.h"
|
||||||
|
#include "kasmpasswd.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Global state
|
* Global state
|
||||||
@@ -59,28 +60,28 @@ void fatal(char *msg)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* resolve host with also IP address parsing */
|
/* resolve host with also IP address parsing */
|
||||||
int resolve_host(struct in_addr *sin_addr, const char *hostname)
|
int resolve_host(struct in_addr *sin_addr, const char *hostname)
|
||||||
{
|
{
|
||||||
if (!inet_aton(hostname, sin_addr)) {
|
if (!inet_aton(hostname, sin_addr)) {
|
||||||
struct addrinfo *ai, *cur;
|
struct addrinfo *ai, *cur;
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
memset(&hints, 0, sizeof(hints));
|
memset(&hints, 0, sizeof(hints));
|
||||||
hints.ai_family = AF_INET;
|
hints.ai_family = AF_INET;
|
||||||
if (getaddrinfo(hostname, NULL, &hints, &ai))
|
if (getaddrinfo(hostname, NULL, &hints, &ai))
|
||||||
return -1;
|
return -1;
|
||||||
for (cur = ai; cur; cur = cur->ai_next) {
|
for (cur = ai; cur; cur = cur->ai_next) {
|
||||||
if (cur->ai_family == AF_INET) {
|
if (cur->ai_family == AF_INET) {
|
||||||
*sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
|
*sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
|
||||||
freeaddrinfo(ai);
|
freeaddrinfo(ai);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
freeaddrinfo(ai);
|
freeaddrinfo(ai);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -107,7 +108,7 @@ ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len) {
|
|||||||
|
|
||||||
ws_ctx_t *alloc_ws_ctx() {
|
ws_ctx_t *alloc_ws_ctx() {
|
||||||
ws_ctx_t *ctx;
|
ws_ctx_t *ctx;
|
||||||
if (! (ctx = malloc(sizeof(ws_ctx_t))) )
|
if (! (ctx = calloc(sizeof(ws_ctx_t), 1)) )
|
||||||
{ fatal("malloc()"); }
|
{ fatal("malloc()"); }
|
||||||
|
|
||||||
if (! (ctx->cin_buf = malloc(BUFSIZE)) )
|
if (! (ctx->cin_buf = malloc(BUFSIZE)) )
|
||||||
@@ -308,7 +309,7 @@ int decode_hixie(char *src, size_t srclength,
|
|||||||
*left = srclength;
|
*left = srclength;
|
||||||
|
|
||||||
if (srclength == 2 &&
|
if (srclength == 2 &&
|
||||||
(src[0] == '\xff') &&
|
(src[0] == '\xff') &&
|
||||||
(src[1] == '\x00')) {
|
(src[1] == '\x00')) {
|
||||||
// client sent orderly close frame
|
// client sent orderly close frame
|
||||||
*opcode = 0x8; // Close frame
|
*opcode = 0x8; // Close frame
|
||||||
@@ -326,7 +327,7 @@ int decode_hixie(char *src, size_t srclength,
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
retlen += len;
|
retlen += len;
|
||||||
start = end + 2; // Skip '\xff' end and '\x00' start
|
start = end + 2; // Skip '\xff' end and '\x00' start
|
||||||
framecount++;
|
framecount++;
|
||||||
} while (end < (src+srclength-1));
|
} while (end < (src+srclength-1));
|
||||||
if (framecount > 1) {
|
if (framecount > 1) {
|
||||||
@@ -399,7 +400,7 @@ int decode_hybi(unsigned char *src, size_t srclength,
|
|||||||
int i = 0, len, framecount = 0;
|
int i = 0, len, framecount = 0;
|
||||||
size_t remaining = 0;
|
size_t remaining = 0;
|
||||||
unsigned int target_offset = 0, hdr_length = 0, payload_length = 0;
|
unsigned int target_offset = 0, hdr_length = 0, payload_length = 0;
|
||||||
|
|
||||||
*left = srclength;
|
*left = srclength;
|
||||||
frame = src;
|
frame = src;
|
||||||
|
|
||||||
@@ -499,7 +500,7 @@ int decode_hybi(unsigned char *src, size_t srclength,
|
|||||||
snprintf(cntstr, 3, "%d", framecount);
|
snprintf(cntstr, 3, "%d", framecount);
|
||||||
traffic(cntstr);
|
traffic(cntstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
*left = remaining;
|
*left = remaining;
|
||||||
return target_offset;
|
return target_offset;
|
||||||
}
|
}
|
||||||
@@ -543,7 +544,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
|||||||
end = strstr(start, "\r\n");
|
end = strstr(start, "\r\n");
|
||||||
strncpy(headers->origin, start, end-start);
|
strncpy(headers->origin, start, end-start);
|
||||||
headers->origin[end-start] = '\0';
|
headers->origin[end-start] = '\0';
|
||||||
|
|
||||||
start = strstr(handshake, "\r\nSec-WebSocket-Version: ");
|
start = strstr(handshake, "\r\nSec-WebSocket-Version: ");
|
||||||
if (start) {
|
if (start) {
|
||||||
// HyBi/RFC 6455
|
// HyBi/RFC 6455
|
||||||
@@ -560,14 +561,14 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
|||||||
end = strstr(start, "\r\n");
|
end = strstr(start, "\r\n");
|
||||||
strncpy(headers->key1, start, end-start);
|
strncpy(headers->key1, start, end-start);
|
||||||
headers->key1[end-start] = '\0';
|
headers->key1[end-start] = '\0';
|
||||||
|
|
||||||
start = strstr(handshake, "\r\nConnection: ");
|
start = strstr(handshake, "\r\nConnection: ");
|
||||||
if (!start) { return 0; }
|
if (!start) { return 0; }
|
||||||
start += 14;
|
start += 14;
|
||||||
end = strstr(start, "\r\n");
|
end = strstr(start, "\r\n");
|
||||||
strncpy(headers->connection, start, end-start);
|
strncpy(headers->connection, start, end-start);
|
||||||
headers->connection[end-start] = '\0';
|
headers->connection[end-start] = '\0';
|
||||||
|
|
||||||
start = strstr(handshake, "\r\nSec-WebSocket-Protocol: ");
|
start = strstr(handshake, "\r\nSec-WebSocket-Protocol: ");
|
||||||
if (!start) { return 0; }
|
if (!start) { return 0; }
|
||||||
start += 26;
|
start += 26;
|
||||||
@@ -592,7 +593,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
|||||||
end = strstr(start, "\r\n");
|
end = strstr(start, "\r\n");
|
||||||
strncpy(headers->key1, start, end-start);
|
strncpy(headers->key1, start, end-start);
|
||||||
headers->key1[end-start] = '\0';
|
headers->key1[end-start] = '\0';
|
||||||
|
|
||||||
start = strstr(handshake, "\r\nSec-WebSocket-Key2: ");
|
start = strstr(handshake, "\r\nSec-WebSocket-Key2: ");
|
||||||
if (!start) { return 0; }
|
if (!start) { return 0; }
|
||||||
start += 22;
|
start += 22;
|
||||||
@@ -916,34 +917,46 @@ ws_ctx_t *do_handshake(int sock) {
|
|||||||
if (resppw && *resppw)
|
if (resppw && *resppw)
|
||||||
resppw++;
|
resppw++;
|
||||||
if (!colon[1] && settings.passwdfile) {
|
if (!colon[1] && settings.passwdfile) {
|
||||||
if (resppw && *resppw) {
|
if (resppw && *resppw && resppw - response < 32) {
|
||||||
char pwbuf[4096];
|
char pwbuf[4096];
|
||||||
FILE *f = fopen(settings.passwdfile, "r");
|
struct kasmpasswd_t *set = readkasmpasswd(settings.passwdfile);
|
||||||
if (f) {
|
if (!set->num) {
|
||||||
handler_emsg("BasicAuth reading password from %s\n", settings.passwdfile);
|
fprintf(stderr, " websocket %d: Error: BasicAuth configured to read password from file %s, but the file doesn't exist or has no valid users\n",
|
||||||
const unsigned len = fread(pwbuf, 1, 4096, f);
|
|
||||||
fclose(f);
|
|
||||||
pwbuf[4095] = '\0';
|
|
||||||
if (len < 4096)
|
|
||||||
pwbuf[len] = '\0';
|
|
||||||
|
|
||||||
snprintf(authbuf, 4096, "%s%s", settings.basicauth, pwbuf);
|
|
||||||
authbuf[4095] = '\0';
|
|
||||||
|
|
||||||
struct crypt_data cdata;
|
|
||||||
cdata.initialized = 0;
|
|
||||||
|
|
||||||
const char *encrypted = crypt_r(resppw, "$5$kasm$", &cdata);
|
|
||||||
*resppw = '\0';
|
|
||||||
|
|
||||||
snprintf(pwbuf, 4096, "%s%s", response, encrypted);
|
|
||||||
pwbuf[4095] = '\0';
|
|
||||||
strcpy(response, pwbuf);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, " websocket %d: Error: BasicAuth configured to read password from file %s, but the file doesn't exist\n",
|
|
||||||
wsthread_handler_id,
|
wsthread_handler_id,
|
||||||
settings.passwdfile);
|
settings.passwdfile);
|
||||||
|
} else {
|
||||||
|
unsigned i;
|
||||||
|
char inuser[32];
|
||||||
|
unsigned char found = 0;
|
||||||
|
memcpy(inuser, response, resppw - response - 1);
|
||||||
|
inuser[resppw - response - 1] = '\0';
|
||||||
|
|
||||||
|
for (i = 0; i < set->num; i++) {
|
||||||
|
if (!strcmp(set->entries[i].user, inuser)) {
|
||||||
|
found = 1;
|
||||||
|
strcpy(ws_ctx->user, inuser);
|
||||||
|
snprintf(authbuf, 4096, "%s:%s", set->entries[i].user,
|
||||||
|
set->entries[i].password);
|
||||||
|
authbuf[4095] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
handler_emsg("BasicAuth user %s not found\n", inuser);
|
||||||
}
|
}
|
||||||
|
free(set->entries);
|
||||||
|
free(set);
|
||||||
|
|
||||||
|
struct crypt_data cdata;
|
||||||
|
cdata.initialized = 0;
|
||||||
|
|
||||||
|
const char *encrypted = crypt_r(resppw, "$5$kasm$", &cdata);
|
||||||
|
*resppw = '\0';
|
||||||
|
|
||||||
|
snprintf(pwbuf, 4096, "%s%s", response, encrypted);
|
||||||
|
pwbuf[4095] = '\0';
|
||||||
|
strcpy(response, pwbuf);
|
||||||
} else {
|
} else {
|
||||||
// Client tried an empty password, just fail them
|
// Client tried an empty password, just fail them
|
||||||
response[0] = '\0';
|
response[0] = '\0';
|
||||||
@@ -1001,7 +1014,7 @@ ws_ctx_t *do_handshake(int sock) {
|
|||||||
snprintf(response, sizeof(response), SERVER_HANDSHAKE_HIXIE, pre, headers->origin,
|
snprintf(response, sizeof(response), SERVER_HANDSHAKE_HIXIE, pre, headers->origin,
|
||||||
pre, scheme, headers->host, headers->path, pre, "base64", trailer);
|
pre, scheme, headers->host, headers->path, pre, "base64", trailer);
|
||||||
}
|
}
|
||||||
|
|
||||||
//handler_msg("response: %s\n", response);
|
//handler_msg("response: %s\n", response);
|
||||||
ws_send(ws_ctx, response, strlen(response));
|
ws_send(ws_ctx, response, strlen(response));
|
||||||
|
|
||||||
@@ -1018,7 +1031,6 @@ void *subthread(void *ptr) {
|
|||||||
|
|
||||||
const int csock = pass->csock;
|
const int csock = pass->csock;
|
||||||
wsthread_handler_id = pass->id;
|
wsthread_handler_id = pass->id;
|
||||||
free((void *) pass);
|
|
||||||
|
|
||||||
ws_ctx_t *ws_ctx;
|
ws_ctx_t *ws_ctx;
|
||||||
|
|
||||||
@@ -1028,11 +1040,14 @@ void *subthread(void *ptr) {
|
|||||||
goto out; // Child process exits
|
goto out; // Child process exits
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(ws_ctx->ip, pass->ip, sizeof(pass->ip));
|
||||||
|
|
||||||
proxy_handler(ws_ctx);
|
proxy_handler(ws_ctx);
|
||||||
if (pipe_error) {
|
if (pipe_error) {
|
||||||
handler_emsg("Closing due to SIGPIPE\n");
|
handler_emsg("Closing due to SIGPIPE\n");
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
|
free((void *) pass);
|
||||||
|
|
||||||
if (ws_ctx) {
|
if (ws_ctx) {
|
||||||
ws_socket_free(ws_ctx);
|
ws_socket_free(ws_ctx);
|
||||||
@@ -1068,12 +1083,13 @@ void *start_server(void *unused) {
|
|||||||
error("ERROR on accept");
|
error("ERROR on accept");
|
||||||
continue;
|
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",
|
fprintf(stderr, " websocket %d: got client connection from %s\n",
|
||||||
settings.handler_id,
|
settings.handler_id,
|
||||||
inet_ntoa(cli_addr.sin_addr));
|
pass->ip);
|
||||||
|
|
||||||
pthread_t tid;
|
pthread_t tid;
|
||||||
struct wspass_t *pass = calloc(1, sizeof(struct wspass_t));
|
|
||||||
pass->id = settings.handler_id;
|
pass->id = settings.handler_id;
|
||||||
pass->csock = csock;
|
pass->csock = csock;
|
||||||
pthread_create(&tid, NULL, subthread, pass);
|
pthread_create(&tid, NULL, subthread, pass);
|
||||||
|
|||||||
@@ -53,11 +53,15 @@ typedef struct {
|
|||||||
char *cout_buf;
|
char *cout_buf;
|
||||||
char *tin_buf;
|
char *tin_buf;
|
||||||
char *tout_buf;
|
char *tout_buf;
|
||||||
|
|
||||||
|
char user[32];
|
||||||
|
char ip[64];
|
||||||
} ws_ctx_t;
|
} ws_ctx_t;
|
||||||
|
|
||||||
struct wspass_t {
|
struct wspass_t {
|
||||||
int csock;
|
int csock;
|
||||||
unsigned id;
|
unsigned id;
|
||||||
|
char ip[64];
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -227,7 +227,13 @@ void proxy_handler(ws_ctx_t *ws_ctx) {
|
|||||||
strcpy(addr.sun_path, ".KasmVNCSock");
|
strcpy(addr.sun_path, ".KasmVNCSock");
|
||||||
addr.sun_path[0] = '\0';
|
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);
|
int tsock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
bind(tsock, (struct sockaddr *) &myaddr, sizeof(struct sockaddr_un));
|
||||||
|
|
||||||
handler_msg("connecting to VNC target\n");
|
handler_msg("connecting to VNC target\n");
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
set(RFB_SOURCES
|
||||||
Blacklist.cxx
|
Blacklist.cxx
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <rfb/ledStates.h>
|
#include <rfb/ledStates.h>
|
||||||
#include <rfb/ConnParams.h>
|
#include <rfb/ConnParams.h>
|
||||||
#include <rfb/ServerCore.h>
|
#include <rfb/ServerCore.h>
|
||||||
|
#include <rfb/SMsgHandler.h>
|
||||||
#include <rfb/util.h>
|
#include <rfb/util.h>
|
||||||
|
|
||||||
using namespace rfb;
|
using namespace rfb;
|
||||||
@@ -43,7 +44,7 @@ ConnParams::ConnParams()
|
|||||||
supportsContinuousUpdates(false),
|
supportsContinuousUpdates(false),
|
||||||
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
|
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
|
||||||
subsampling(subsampleUndefined), name_(0), verStrPos(0),
|
subsampling(subsampleUndefined), name_(0), verStrPos(0),
|
||||||
ledState_(ledUnknown)
|
ledState_(ledUnknown), shandler(NULL)
|
||||||
{
|
{
|
||||||
memset(kasmPassed, 0, KASM_NUM_SETTINGS);
|
memset(kasmPassed, 0, KASM_NUM_SETTINGS);
|
||||||
setName("");
|
setName("");
|
||||||
@@ -124,6 +125,8 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
|||||||
encodings_.clear();
|
encodings_.clear();
|
||||||
encodings_.insert(encodingRaw);
|
encodings_.insert(encodingRaw);
|
||||||
|
|
||||||
|
bool canChangeSettings = !shandler || shandler->canChangeKasmSettings();
|
||||||
|
|
||||||
for (int i = nEncodings-1; i >= 0; i--) {
|
for (int i = nEncodings-1; i >= 0; i--) {
|
||||||
switch (encodings[i]) {
|
switch (encodings[i]) {
|
||||||
case encodingCopyRect:
|
case encodingCopyRect:
|
||||||
@@ -184,11 +187,11 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
|||||||
subsampling = subsample16X;
|
subsampling = subsample16X;
|
||||||
break;
|
break;
|
||||||
case pseudoEncodingPreferBandwidth:
|
case pseudoEncodingPreferBandwidth:
|
||||||
if (!rfb::Server::ignoreClientSettingsKasm)
|
if (!rfb::Server::ignoreClientSettingsKasm && canChangeSettings)
|
||||||
Server::preferBandwidth.setParam();
|
Server::preferBandwidth.setParam();
|
||||||
break;
|
break;
|
||||||
case pseudoEncodingMaxVideoResolution:
|
case pseudoEncodingMaxVideoResolution:
|
||||||
if (!rfb::Server::ignoreClientSettingsKasm)
|
if (!rfb::Server::ignoreClientSettingsKasm && canChangeSettings)
|
||||||
kasmPassed[KASM_MAX_VIDEO_RESOLUTION] = true;
|
kasmPassed[KASM_MAX_VIDEO_RESOLUTION] = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -205,7 +208,7 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
|||||||
encodings[i] <= pseudoEncodingFineQualityLevel100)
|
encodings[i] <= pseudoEncodingFineQualityLevel100)
|
||||||
fineQualityLevel = encodings[i] - pseudoEncodingFineQualityLevel0;
|
fineQualityLevel = encodings[i] - pseudoEncodingFineQualityLevel0;
|
||||||
|
|
||||||
if (!rfb::Server::ignoreClientSettingsKasm) {
|
if (!rfb::Server::ignoreClientSettingsKasm && canChangeSettings) {
|
||||||
if (encodings[i] >= pseudoEncodingJpegVideoQualityLevel0 &&
|
if (encodings[i] >= pseudoEncodingJpegVideoQualityLevel0 &&
|
||||||
encodings[i] <= pseudoEncodingJpegVideoQualityLevel9)
|
encodings[i] <= pseudoEncodingJpegVideoQualityLevel9)
|
||||||
Server::jpegVideoQuality.setParam(encodings[i] - pseudoEncodingJpegVideoQualityLevel0);
|
Server::jpegVideoQuality.setParam(encodings[i] - pseudoEncodingJpegVideoQualityLevel0);
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ namespace rfb {
|
|||||||
const int subsample8X = 4;
|
const int subsample8X = 4;
|
||||||
const int subsample16X = 5;
|
const int subsample16X = 5;
|
||||||
|
|
||||||
|
class SMsgHandler;
|
||||||
|
|
||||||
class ConnParams {
|
class ConnParams {
|
||||||
public:
|
public:
|
||||||
ConnParams();
|
ConnParams();
|
||||||
@@ -74,6 +76,8 @@ namespace rfb {
|
|||||||
const PixelFormat& pf() const { return pf_; }
|
const PixelFormat& pf() const { return pf_; }
|
||||||
void setPF(const PixelFormat& pf);
|
void setPF(const PixelFormat& pf);
|
||||||
|
|
||||||
|
void setSHandler(SMsgHandler *s) { shandler = s; }
|
||||||
|
|
||||||
const char* name() const { return name_; }
|
const char* name() const { return name_; }
|
||||||
void setName(const char* name);
|
void setName(const char* name);
|
||||||
|
|
||||||
@@ -136,6 +140,7 @@ namespace rfb {
|
|||||||
char verStr[13];
|
char verStr[13];
|
||||||
int verStrPos;
|
int verStrPos;
|
||||||
unsigned int ledState_;
|
unsigned int ledState_;
|
||||||
|
SMsgHandler *shandler;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ using namespace rfb;
|
|||||||
|
|
||||||
SMsgHandler::SMsgHandler()
|
SMsgHandler::SMsgHandler()
|
||||||
{
|
{
|
||||||
|
cp.setSHandler(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
SMsgHandler::~SMsgHandler()
|
SMsgHandler::~SMsgHandler()
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ namespace rfb {
|
|||||||
|
|
||||||
virtual void sendStats() = 0;
|
virtual void sendStats() = 0;
|
||||||
|
|
||||||
|
virtual bool canChangeKasmSettings() const = 0;
|
||||||
|
|
||||||
// InputHandler interface
|
// InputHandler interface
|
||||||
// The InputHandler methods will be called for the corresponding messages.
|
// The InputHandler methods will be called for the corresponding messages.
|
||||||
|
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ void SMsgReader::readSetMaxVideoResolution()
|
|||||||
width = is->readU16();
|
width = is->readU16();
|
||||||
height = is->readU16();
|
height = is->readU16();
|
||||||
|
|
||||||
if (!rfb::Server::ignoreClientSettingsKasm) {
|
if (!rfb::Server::ignoreClientSettingsKasm && handler->canChangeKasmSettings()) {
|
||||||
sprintf(tmp, "%ux%u", width, height);
|
sprintf(tmp, "%ux%u", width, height);
|
||||||
rfb::Server::maxVideoResolution.setParam(tmp);
|
rfb::Server::maxVideoResolution.setParam(tmp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,9 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <wordexp.h>
|
||||||
|
|
||||||
|
#include "kasmpasswd.h"
|
||||||
|
|
||||||
using namespace rfb;
|
using namespace rfb;
|
||||||
|
|
||||||
@@ -45,6 +48,8 @@ static LogWriter vlog("VNCSConnST");
|
|||||||
|
|
||||||
static Cursor emptyCursor(0, 0, Point(0, 0), NULL);
|
static Cursor emptyCursor(0, 0, Point(0, 0), NULL);
|
||||||
|
|
||||||
|
extern rfb::StringParameter basicauth;
|
||||||
|
|
||||||
VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
||||||
bool reverse)
|
bool reverse)
|
||||||
: sock(s), reverseConnection(reverse),
|
: sock(s), reverseConnection(reverse),
|
||||||
@@ -54,7 +59,7 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
|||||||
losslessTimer(this), kbdLogTimer(this), server(server_), updates(false),
|
losslessTimer(this), kbdLogTimer(this), server(server_), updates(false),
|
||||||
updateRenderedCursor(false), removeRenderedCursor(false),
|
updateRenderedCursor(false), removeRenderedCursor(false),
|
||||||
continuousUpdates(false), encodeManager(this, &server_->encCache),
|
continuousUpdates(false), encodeManager(this, &server_->encCache),
|
||||||
pointerEventTime(0),
|
needsPermCheck(false), pointerEventTime(0),
|
||||||
clientHasCursor(false),
|
clientHasCursor(false),
|
||||||
accessRights(AccessDefault), startTime(time(0))
|
accessRights(AccessDefault), startTime(time(0))
|
||||||
{
|
{
|
||||||
@@ -65,6 +70,25 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
|||||||
memset(bstats_total, 0, sizeof(bstats_total));
|
memset(bstats_total, 0, sizeof(bstats_total));
|
||||||
gettimeofday(&connStart, NULL);
|
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 | AccessSetDesktopSize));
|
||||||
|
|
||||||
// Configure the socket
|
// Configure the socket
|
||||||
setSocketTimeouts();
|
setSocketTimeouts();
|
||||||
lastEventTime = time(0);
|
lastEventTime = time(0);
|
||||||
@@ -1001,6 +1025,34 @@ bool VNCSConnectionST::isShiftPressed()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VNCSConnectionST::getPerms(bool &write, bool &owner) const
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
const char *colon = strchr(basicauth, ':');
|
||||||
|
if (!colon || colon[1]) {
|
||||||
|
// We're running without basicauth, or with both user:pass on the command line
|
||||||
|
write = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
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()
|
void VNCSConnectionST::writeRTTPing()
|
||||||
{
|
{
|
||||||
char type;
|
char type;
|
||||||
@@ -1081,6 +1133,22 @@ void VNCSConnectionST::writeFramebufferUpdate()
|
|||||||
if (isCongested())
|
if (isCongested())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Check for permission changes?
|
||||||
|
if (needsPermCheck) {
|
||||||
|
needsPermCheck = false;
|
||||||
|
|
||||||
|
bool write, owner, ret;
|
||||||
|
ret = getPerms(write, owner);
|
||||||
|
if (!ret) {
|
||||||
|
close("User was deleted");
|
||||||
|
return;
|
||||||
|
} else if (!write) {
|
||||||
|
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize));
|
||||||
|
} else {
|
||||||
|
accessRights |= AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Updates often consists of many small writes, and in continuous
|
// Updates often consists of many small writes, and in continuous
|
||||||
// mode, we will also have small fence messages around the update. We
|
// mode, we will also have small fence messages around the update. We
|
||||||
// need to aggregate these in order to not clog up TCP's congestion
|
// need to aggregate these in order to not clog up TCP's congestion
|
||||||
|
|||||||
@@ -102,6 +102,10 @@ namespace rfb {
|
|||||||
// or because the current cursor position has not been set by this client.
|
// or because the current cursor position has not been set by this client.
|
||||||
bool needRenderedCursor();
|
bool needRenderedCursor();
|
||||||
|
|
||||||
|
void recheckPerms() {
|
||||||
|
needsPermCheck = true;
|
||||||
|
}
|
||||||
|
|
||||||
network::Socket* getSock() { return sock; }
|
network::Socket* getSock() { return sock; }
|
||||||
void add_changed(const Region& region) { updates.add_changed(region); }
|
void add_changed(const Region& region) { updates.add_changed(region); }
|
||||||
void add_changed_all() { updates.add_changed(server->pb->getRect()); }
|
void add_changed_all() { updates.add_changed(server->pb->getRect()); }
|
||||||
@@ -179,12 +183,23 @@ namespace rfb {
|
|||||||
virtual void supportsLEDState();
|
virtual void supportsLEDState();
|
||||||
|
|
||||||
virtual void sendStats();
|
virtual void sendStats();
|
||||||
|
virtual bool canChangeKasmSettings() const {
|
||||||
|
return (accessRights & (AccessPtrEvents | AccessKeyEvents)) ==
|
||||||
|
(AccessPtrEvents | AccessKeyEvents);
|
||||||
|
}
|
||||||
|
|
||||||
// setAccessRights() allows a security package to limit the access rights
|
// setAccessRights() allows a security package to limit the access rights
|
||||||
// of a VNCSConnectioST to the server. These access rights are applied
|
// of a VNCSConnectioST to the server. These access rights are applied
|
||||||
// such that the actual rights granted are the minimum of the server's
|
// such that the actual rights granted are the minimum of the server's
|
||||||
// default access settings and the connection's access settings.
|
// 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));
|
||||||
|
needsPermCheck = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Timer callbacks
|
// Timer callbacks
|
||||||
virtual bool handleTimeout(Timer* t);
|
virtual bool handleTimeout(Timer* t);
|
||||||
@@ -193,6 +208,8 @@ namespace rfb {
|
|||||||
|
|
||||||
bool isShiftPressed();
|
bool isShiftPressed();
|
||||||
|
|
||||||
|
bool getPerms(bool &write, bool &owner) const;
|
||||||
|
|
||||||
// Congestion control
|
// Congestion control
|
||||||
void writeRTTPing();
|
void writeRTTPing();
|
||||||
bool isCongested();
|
bool isCongested();
|
||||||
@@ -249,6 +266,10 @@ namespace rfb {
|
|||||||
rdr::U64 bstats_total[BS_NUM];
|
rdr::U64 bstats_total[BS_NUM];
|
||||||
struct timeval connStart;
|
struct timeval connStart;
|
||||||
|
|
||||||
|
char user[32];
|
||||||
|
char kasmpasswdpath[4096];
|
||||||
|
bool needsPermCheck;
|
||||||
|
|
||||||
time_t lastEventTime;
|
time_t lastEventTime;
|
||||||
time_t pointerEventTime;
|
time_t pointerEventTime;
|
||||||
Point pointerEventPos;
|
Point pointerEventPos;
|
||||||
|
|||||||
@@ -63,6 +63,11 @@
|
|||||||
|
|
||||||
#include <rdr/types.h>
|
#include <rdr/types.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wordexp.h>
|
||||||
|
|
||||||
using namespace rfb;
|
using namespace rfb;
|
||||||
|
|
||||||
static LogWriter slog("VNCServerST");
|
static LogWriter slog("VNCServerST");
|
||||||
@@ -73,6 +78,9 @@ EncCache VNCServerST::encCache;
|
|||||||
// -=- VNCServerST Implementation
|
// -=- VNCServerST Implementation
|
||||||
//
|
//
|
||||||
|
|
||||||
|
static char kasmpasswdpath[4096];
|
||||||
|
extern rfb::StringParameter basicauth;
|
||||||
|
|
||||||
// -=- Constructors/Destructor
|
// -=- Constructors/Destructor
|
||||||
|
|
||||||
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||||
@@ -87,6 +95,26 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
|||||||
{
|
{
|
||||||
lastUserInputTime = lastDisconnectTime = time(0);
|
lastUserInputTime = lastDisconnectTime = time(0);
|
||||||
slog.debug("creating single-threaded server %s", name.buf);
|
slog.debug("creating single-threaded server %s", name.buf);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (kasmpasswdpath[0] && access(kasmpasswdpath, R_OK) == 0) {
|
||||||
|
// Set up a watch on the password file
|
||||||
|
inotifyfd = inotify_init();
|
||||||
|
if (inotifyfd < 0)
|
||||||
|
slog.error("Failed to init inotify");
|
||||||
|
|
||||||
|
int flags = fcntl(inotifyfd, F_GETFL, 0);
|
||||||
|
fcntl(inotifyfd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
|
if (inotify_add_watch(inotifyfd, kasmpasswdpath, IN_CLOSE_WRITE | IN_DELETE_SELF) < 0)
|
||||||
|
slog.error("Failed to set watch");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VNCServerST::~VNCServerST()
|
VNCServerST::~VNCServerST()
|
||||||
@@ -659,8 +687,34 @@ void VNCServerST::writeUpdate()
|
|||||||
encCache.clear();
|
encCache.clear();
|
||||||
encCache.enabled = clients.size() > 1;
|
encCache.enabled = clients.size() > 1;
|
||||||
|
|
||||||
|
// Check if the password file was updated
|
||||||
|
bool permcheck = false;
|
||||||
|
if (inotifyfd >= 0) {
|
||||||
|
char buf[256];
|
||||||
|
int ret = read(inotifyfd, buf, 256);
|
||||||
|
int pos = 0;
|
||||||
|
while (ret > 0) {
|
||||||
|
const struct inotify_event * const ev = (struct inotify_event *) &buf[pos];
|
||||||
|
|
||||||
|
if (ev->mask & IN_IGNORED) {
|
||||||
|
// file was deleted, set new watch
|
||||||
|
if (inotify_add_watch(inotifyfd, kasmpasswdpath, IN_CLOSE_WRITE | IN_DELETE_SELF) < 0)
|
||||||
|
slog.error("Failed to set watch");
|
||||||
|
}
|
||||||
|
|
||||||
|
permcheck = true;
|
||||||
|
|
||||||
|
ret -= sizeof(struct inotify_event) - ev->len;
|
||||||
|
pos += sizeof(struct inotify_event) - ev->len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
||||||
ci_next = ci; ci_next++;
|
ci_next = ci; ci_next++;
|
||||||
|
|
||||||
|
if (permcheck)
|
||||||
|
(*ci)->recheckPerms();
|
||||||
|
|
||||||
(*ci)->add_copied(ui.copied, ui.copy_delta);
|
(*ci)->add_copied(ui.copied, ui.copy_delta);
|
||||||
(*ci)->add_copypassed(ui.copypassed);
|
(*ci)->add_copypassed(ui.copypassed);
|
||||||
(*ci)->add_changed(ui.changed);
|
(*ci)->add_changed(ui.changed);
|
||||||
|
|||||||
@@ -249,6 +249,8 @@ namespace rfb {
|
|||||||
bool disableclients;
|
bool disableclients;
|
||||||
|
|
||||||
Timer frameTimer;
|
Timer frameTimer;
|
||||||
|
|
||||||
|
int inotifyfd;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -172,7 +172,6 @@ const UI = {
|
|||||||
UI.initSetting('port', port);
|
UI.initSetting('port', port);
|
||||||
UI.initSetting('encrypt', (window.location.protocol === "https:"));
|
UI.initSetting('encrypt', (window.location.protocol === "https:"));
|
||||||
UI.initSetting('view_clip', false);
|
UI.initSetting('view_clip', false);
|
||||||
UI.initSetting('resize', 'remote');
|
|
||||||
UI.initSetting('shared', true);
|
UI.initSetting('shared', true);
|
||||||
UI.initSetting('view_only', false);
|
UI.initSetting('view_only', false);
|
||||||
UI.initSetting('show_dot', false);
|
UI.initSetting('show_dot', false);
|
||||||
@@ -181,15 +180,26 @@ const UI = {
|
|||||||
UI.initSetting('reconnect', false);
|
UI.initSetting('reconnect', false);
|
||||||
UI.initSetting('reconnect_delay', 5000);
|
UI.initSetting('reconnect_delay', 5000);
|
||||||
UI.initSetting('idle_disconnect', 20);
|
UI.initSetting('idle_disconnect', 20);
|
||||||
UI.initSetting('video_quality', 3);
|
|
||||||
UI.initSetting('clipboard_up', true);
|
|
||||||
UI.initSetting('clipboard_down', true);
|
|
||||||
UI.initSetting('clipboard_seamless', true);
|
|
||||||
UI.initSetting('prefer_local_cursor', true);
|
UI.initSetting('prefer_local_cursor', true);
|
||||||
UI.initSetting('enable_webp', true);
|
|
||||||
UI.initSetting('toggle_control_panel', false);
|
UI.initSetting('toggle_control_panel', false);
|
||||||
UI.initSetting('enable_perf_stats', false);
|
UI.initSetting('enable_perf_stats', false);
|
||||||
|
|
||||||
|
if (WebUtil.isInsideKasmVDI()) {
|
||||||
|
UI.initSetting('video_quality', 1);
|
||||||
|
UI.initSetting('clipboard_up', false);
|
||||||
|
UI.initSetting('clipboard_down', false);
|
||||||
|
UI.initSetting('clipboard_seamless', false);
|
||||||
|
UI.initSetting('enable_webp', false);
|
||||||
|
UI.initSetting('resize', 'off');
|
||||||
|
} else {
|
||||||
|
UI.initSetting('video_quality', 3);
|
||||||
|
UI.initSetting('clipboard_up', true);
|
||||||
|
UI.initSetting('clipboard_down', true);
|
||||||
|
UI.initSetting('clipboard_seamless', true);
|
||||||
|
UI.initSetting('enable_webp', true);
|
||||||
|
UI.initSetting('resize', 'remote');
|
||||||
|
}
|
||||||
|
|
||||||
UI.setupSettingLabels();
|
UI.setupSettingLabels();
|
||||||
},
|
},
|
||||||
// Adds a link to the label elements on the corresponding input elements
|
// Adds a link to the label elements on the corresponding input elements
|
||||||
@@ -416,6 +426,10 @@ const UI = {
|
|||||||
document.documentElement.classList.remove("noVNC_reconnecting");
|
document.documentElement.classList.remove("noVNC_reconnecting");
|
||||||
|
|
||||||
const transition_elem = document.getElementById("noVNC_transition_text");
|
const transition_elem = document.getElementById("noVNC_transition_text");
|
||||||
|
if (WebUtil.isInsideKasmVDI())
|
||||||
|
{
|
||||||
|
parent.postMessage({ action: 'connection_state', value: state}, '*' );
|
||||||
|
}
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case 'init':
|
case 'init':
|
||||||
break;
|
break;
|
||||||
@@ -1254,7 +1268,7 @@ const UI = {
|
|||||||
UI.rfb.addEventListener("securityfailure", UI.securityFailed);
|
UI.rfb.addEventListener("securityfailure", UI.securityFailed);
|
||||||
UI.rfb.addEventListener("capabilities", UI.updatePowerButton);
|
UI.rfb.addEventListener("capabilities", UI.updatePowerButton);
|
||||||
UI.rfb.addEventListener("clipboard", UI.clipboardReceive);
|
UI.rfb.addEventListener("clipboard", UI.clipboardReceive);
|
||||||
UI.rfb.addEventListener("bottleneck_stats", UI.bottleneckStatsRecieve);
|
UI.rfb.addEventListener("bottleneck_stats", UI.bottleneckStatsRecieve);
|
||||||
|
|
||||||
document.addEventListener('mouseenter', UI.enterVNC);
|
document.addEventListener('mouseenter', UI.enterVNC);
|
||||||
document.addEventListener('mouseleave', UI.leaveVNC);
|
document.addEventListener('mouseleave', UI.leaveVNC);
|
||||||
@@ -1295,7 +1309,9 @@ const UI = {
|
|||||||
window.attachEvent('onload', WindowLoad);
|
window.attachEvent('onload', WindowLoad);
|
||||||
window.attachEvent('message', UI.receiveMessage);
|
window.attachEvent('message', UI.receiveMessage);
|
||||||
}
|
}
|
||||||
UI.rfb.addEventListener("clipboard", UI.clipboardRx);
|
if (UI.rfb.clipboardDown){
|
||||||
|
UI.rfb.addEventListener("clipboard", UI.clipboardRx);
|
||||||
|
}
|
||||||
UI.rfb.addEventListener("disconnect", UI.disconnectedRx);
|
UI.rfb.addEventListener("disconnect", UI.disconnectedRx);
|
||||||
document.getElementById('noVNC_control_bar_anchor').setAttribute('style', 'display: none');
|
document.getElementById('noVNC_control_bar_anchor').setAttribute('style', 'display: none');
|
||||||
document.getElementById('noVNC_connect_dlg').innerHTML = '';
|
document.getElementById('noVNC_connect_dlg').innerHTML = '';
|
||||||
@@ -1459,7 +1475,9 @@ const UI = {
|
|||||||
if (event.data && event.data.action) {
|
if (event.data && event.data.action) {
|
||||||
switch (event.data.action) {
|
switch (event.data.action) {
|
||||||
case 'clipboardsnd':
|
case 'clipboardsnd':
|
||||||
UI.rfb.clipboardPasteFrom(event.data.value);
|
if (UI.rfb.clipboardUp) {
|
||||||
|
UI.rfb.clipboardPasteFrom(event.data.value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'setvideoquality':
|
case 'setvideoquality':
|
||||||
UI.rfb.videoQuality = event.data.value;
|
UI.rfb.videoQuality = event.data.value;
|
||||||
|
|||||||
@@ -1068,14 +1068,7 @@ export default class RFB extends EventTargetMixin {
|
|||||||
_negotiate_std_vnc_auth() {
|
_negotiate_std_vnc_auth() {
|
||||||
if (this._sock.rQwait("auth challenge", 16)) { return false; }
|
if (this._sock.rQwait("auth challenge", 16)) { return false; }
|
||||||
|
|
||||||
/* Empty passwords are allowed in VNC and since we use HTTPS basic auth, wich is superior, lets allow the bypass of the vnc password
|
// KasmVNC uses basic Auth, clear the VNC password, which is not used
|
||||||
if (!this._rfb_credentials.password) {
|
|
||||||
this.dispatchEvent(new CustomEvent(
|
|
||||||
"credentialsrequired",
|
|
||||||
{ detail: { types: ["password"] } }));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
this._rfb_credentials.password = "";
|
this._rfb_credentials.password = "";
|
||||||
|
|
||||||
// TODO(directxman12): make genDES not require an Array
|
// TODO(directxman12): make genDES not require an Array
|
||||||
@@ -1353,7 +1346,7 @@ export default class RFB extends EventTargetMixin {
|
|||||||
*/
|
*/
|
||||||
if (!this.enableWebP)
|
if (!this.enableWebP)
|
||||||
return false;
|
return false;
|
||||||
// It's not possible to check for webp synchronously, and hacking promises
|
// It's not possible to check for webp synchronously, and hacking promises
|
||||||
// into everything would be too time-consuming. So test for FF and Chrome.
|
// into everything would be too time-consuming. So test for FF and Chrome.
|
||||||
var uagent = navigator.userAgent.toLowerCase();
|
var uagent = navigator.userAgent.toLowerCase();
|
||||||
var match = uagent.match(/firefox\/([0-9]+)\./);
|
var match = uagent.match(/firefox\/([0-9]+)\./);
|
||||||
|
|||||||
@@ -71,9 +71,19 @@
|
|||||||
loader.src = "vendor/browser-es-module-loader/dist/browser-es-module-loader.js";
|
loader.src = "vendor/browser-es-module-loader/dist/browser-es-module-loader.js";
|
||||||
document.head.appendChild(loader);
|
document.head.appendChild(loader);
|
||||||
});
|
});
|
||||||
window.addEventListener("load", function() {
|
|
||||||
document.getElementById("noVNC_connect_button").click();
|
let isInsideKasmVDI = false;
|
||||||
});
|
try {
|
||||||
|
isInsideKasmVDI = (window.self !== window.top);
|
||||||
|
} catch (e) {
|
||||||
|
isInsideKasmVDI = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isInsideKasmVDI) {
|
||||||
|
window.addEventListener("load", function() {
|
||||||
|
document.getElementById("noVNC_connect_button").click();
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<!-- actual script modules -->
|
<!-- actual script modules -->
|
||||||
<script type="module" crossorigin='use-credentials' src="app/ui.js"></script>
|
<script type="module" crossorigin='use-credentials' src="app/ui.js"></script>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
include_directories(${CMAKE_SOURCE_DIR}/common)
|
include_directories(${CMAKE_SOURCE_DIR}/common)
|
||||||
|
|
||||||
add_executable(kasmvncpasswd
|
add_executable(kasmvncpasswd
|
||||||
kasmvncpasswd.c)
|
kasmvncpasswd.c
|
||||||
|
kasmpasswd.c)
|
||||||
|
|
||||||
target_link_libraries(kasmvncpasswd crypt)
|
target_link_libraries(kasmvncpasswd crypt)
|
||||||
|
|
||||||
|
|||||||
117
unix/kasmvncpasswd/kasmpasswd.c
Normal file
117
unix/kasmvncpasswd/kasmpasswd.c
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "kasmpasswd.h"
|
||||||
|
|
||||||
|
struct kasmpasswd_t *readkasmpasswd(const char path[]) {
|
||||||
|
|
||||||
|
struct kasmpasswd_t *set = calloc(sizeof(struct kasmpasswd_t), 1);
|
||||||
|
FILE *f = fopen(path, "r");
|
||||||
|
if (!f)
|
||||||
|
return set;
|
||||||
|
|
||||||
|
// Count lines
|
||||||
|
unsigned lines = 0;
|
||||||
|
char buf[4096];
|
||||||
|
|
||||||
|
while (fgets(buf, 4096, f)) {
|
||||||
|
lines++;
|
||||||
|
}
|
||||||
|
|
||||||
|
rewind(f);
|
||||||
|
|
||||||
|
set->entries = calloc(sizeof(struct kasmpasswd_entry_t), lines);
|
||||||
|
|
||||||
|
unsigned cur = 0;
|
||||||
|
while (fgets(buf, 4096, f)) {
|
||||||
|
char *lim = strchr(buf, ':');
|
||||||
|
if (!lim)
|
||||||
|
continue;
|
||||||
|
*lim = '\0';
|
||||||
|
lim++;
|
||||||
|
|
||||||
|
const char * const pw = lim;
|
||||||
|
|
||||||
|
lim = strchr(lim, ':');
|
||||||
|
if (!lim)
|
||||||
|
continue;
|
||||||
|
*lim = '\0';
|
||||||
|
lim++;
|
||||||
|
|
||||||
|
const char * const perms = lim;
|
||||||
|
|
||||||
|
lim = strchr(lim, '\n');
|
||||||
|
if (lim)
|
||||||
|
*lim = '\0';
|
||||||
|
|
||||||
|
if (strlen(buf) + 1 > sizeof(((struct kasmpasswd_entry_t *)0)->user)) {
|
||||||
|
fprintf(stderr, "Username %s too long\n", buf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strlen(pw) + 1 > sizeof(((struct kasmpasswd_entry_t *)0)->password)) {
|
||||||
|
fprintf(stderr, "Password for user %s too long\n", buf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(set->entries[cur].user, buf);
|
||||||
|
strcpy(set->entries[cur].password, pw);
|
||||||
|
|
||||||
|
if (strchr(perms, 'w'))
|
||||||
|
set->entries[cur].write = 1;
|
||||||
|
if (strchr(perms, 'o'))
|
||||||
|
set->entries[cur].owner = 1;
|
||||||
|
|
||||||
|
cur++;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
set->num = cur;
|
||||||
|
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writekasmpasswd(const char path[], const struct kasmpasswd_t *set) {
|
||||||
|
char tmpname[PATH_MAX];
|
||||||
|
|
||||||
|
if (!set || !set->entries || !set->num)
|
||||||
|
return;
|
||||||
|
|
||||||
|
snprintf(tmpname, PATH_MAX, "%s.tmp", path);
|
||||||
|
tmpname[PATH_MAX - 1] = '\0';
|
||||||
|
|
||||||
|
FILE *f = fopen(tmpname, "w");
|
||||||
|
if (!f) {
|
||||||
|
fprintf(stderr, "Failed to open temp file %s\n", tmpname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char * const perms[] = {
|
||||||
|
"",
|
||||||
|
"w",
|
||||||
|
"o",
|
||||||
|
"ow"
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < set->num; i++) {
|
||||||
|
if (!set->entries[i].user[0])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fprintf(f, "%s:%s:%s\n",
|
||||||
|
set->entries[i].user,
|
||||||
|
set->entries[i].password,
|
||||||
|
perms[set->entries[i].owner * 2 + set->entries[i].write]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fsync(fileno(f));
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
if (rename(tmpname, path))
|
||||||
|
fprintf(stderr, "Failed writing the password file %s\n", path);
|
||||||
|
chmod(path, S_IRUSR|S_IWUSR);
|
||||||
|
}
|
||||||
27
unix/kasmvncpasswd/kasmpasswd.h
Normal file
27
unix/kasmvncpasswd/kasmpasswd.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef KASMPASSWD_H
|
||||||
|
#define KASMPASSWD_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct kasmpasswd_entry_t {
|
||||||
|
char user[32];
|
||||||
|
char password[128];
|
||||||
|
unsigned char write : 1;
|
||||||
|
unsigned char owner : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kasmpasswd_t {
|
||||||
|
struct kasmpasswd_entry_t *entries;
|
||||||
|
unsigned num;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kasmpasswd_t *readkasmpasswd(const char path[]);
|
||||||
|
void writekasmpasswd(const char path[], const struct kasmpasswd_t *set);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern C
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -29,16 +29,30 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
|
|
||||||
|
#include "kasmpasswd.h"
|
||||||
|
|
||||||
static void usage(const char *prog)
|
static void usage(const char *prog)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Usage: %s [file]\n", prog);
|
fprintf(stderr, "Usage: %s -u username [-wnod] [file]\n"
|
||||||
fprintf(stderr, " %s -f\n", prog);
|
"-w Write permission\n"
|
||||||
|
"-o Owner\n"
|
||||||
|
"-n Don't change password, change permissions only\n"
|
||||||
|
"-d Delete this user\n"
|
||||||
|
"\n"
|
||||||
|
"The file is updated atomically.\n\n"
|
||||||
|
"To pass the password via a pipe, use\n"
|
||||||
|
"echo -e \"password\\npassword\\n\" | %s [-args]\n",
|
||||||
|
prog, prog);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void enableEcho(unsigned char enable) {
|
static void enableEcho(unsigned char enable) {
|
||||||
struct termios attrs;
|
struct termios attrs;
|
||||||
|
|
||||||
|
if (!isatty(fileno(stdin)))
|
||||||
|
return;
|
||||||
|
|
||||||
tcgetattr(fileno(stdin), &attrs);
|
tcgetattr(fileno(stdin), &attrs);
|
||||||
if (enable)
|
if (enable)
|
||||||
attrs.c_lflag |= ECHO;
|
attrs.c_lflag |= ECHO;
|
||||||
@@ -53,7 +67,7 @@ static const char *encryptpw(const char *in) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static char* getpassword(const char* prompt, char *buf) {
|
static char* getpassword(const char* prompt, char *buf) {
|
||||||
if (prompt) fputs(prompt, stdout);
|
if (prompt && isatty(fileno(stdin))) fputs(prompt, stdout);
|
||||||
enableEcho(0);
|
enableEcho(0);
|
||||||
char* result = fgets(buf, 4096, stdin);
|
char* result = fgets(buf, 4096, stdin);
|
||||||
enableEcho(1);
|
enableEcho(1);
|
||||||
@@ -68,18 +82,6 @@ static char* getpassword(const char* prompt, char *buf) {
|
|||||||
static char pw1[4096];
|
static char pw1[4096];
|
||||||
static char pw2[4096];
|
static char pw2[4096];
|
||||||
|
|
||||||
// Reads passwords from stdin and prints encrypted passwords to stdout.
|
|
||||||
static int encrypt_pipe() {
|
|
||||||
char *result = getpassword(NULL, pw1);
|
|
||||||
if (!result)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
printf("%s", encryptpw(result));
|
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *readpassword() {
|
static const char *readpassword() {
|
||||||
while (1) {
|
while (1) {
|
||||||
if (!getpassword("Password:", pw1)) {
|
if (!getpassword("Password:", pw1)) {
|
||||||
@@ -100,6 +102,10 @@ static const char *readpassword() {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (strcmp(pw1, pw2) != 0) {
|
if (strcmp(pw1, pw2) != 0) {
|
||||||
|
if (!isatty(fileno(stdin))) {
|
||||||
|
fprintf(stderr,"Passwords don't match\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
fprintf(stderr,"Passwords don't match - try again\n");
|
fprintf(stderr,"Passwords don't match - try again\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -110,20 +116,49 @@ static const char *readpassword() {
|
|||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
char* fname = 0;
|
const char *fname = NULL;
|
||||||
|
const char *user = NULL;
|
||||||
|
const char args[] = "u:wnod";
|
||||||
|
int opt;
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++) {
|
unsigned char nopass = 0, writer = 0, owner = 0, deleting = 0;
|
||||||
if (strncmp(argv[i], "-f", 2) == 0) {
|
|
||||||
return encrypt_pipe();
|
while ((opt = getopt(argc, argv, args)) != -1) {
|
||||||
} else if (argv[i][0] == '-') {
|
switch (opt) {
|
||||||
usage(argv[0]);
|
case 'u':
|
||||||
} else if (!fname) {
|
user = optarg;
|
||||||
fname = argv[i];
|
if (strlen(user) + 1 > sizeof(((struct kasmpasswd_entry_t *)0)->user)) {
|
||||||
} else {
|
fprintf(stderr, "Username %s too long\n", user);
|
||||||
usage(argv[0]);
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
nopass = 1;
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
writer = 1;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
owner = 1;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
deleting = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (deleting && (nopass || writer || owner))
|
||||||
|
usage(argv[0]);
|
||||||
|
|
||||||
|
if (!user)
|
||||||
|
usage(argv[0]);
|
||||||
|
|
||||||
|
if (optind < argc)
|
||||||
|
fname = argv[optind];
|
||||||
|
|
||||||
if (!fname) {
|
if (!fname) {
|
||||||
wordexp_t wexp;
|
wordexp_t wexp;
|
||||||
if (!wordexp("~/.kasmpasswd", &wexp, WRDE_NOCMD) && wexp.we_wordv[0])
|
if (!wordexp("~/.kasmpasswd", &wexp, WRDE_NOCMD) && wexp.we_wordv[0])
|
||||||
@@ -133,23 +168,54 @@ int main(int argc, char** argv)
|
|||||||
if (!fname)
|
if (!fname)
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
|
|
||||||
while (1) {
|
// Action
|
||||||
|
struct kasmpasswd_t *set = readkasmpasswd(fname);
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if (nopass) {
|
||||||
|
for (i = 0; i < set->num; i++) {
|
||||||
|
if (!strcmp(set->entries[i].user, user)) {
|
||||||
|
set->entries[i].write = writer;
|
||||||
|
set->entries[i].owner = owner;
|
||||||
|
|
||||||
|
writekasmpasswd(fname, set);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, "No user named %s found\n", user);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
} else if (deleting) {
|
||||||
|
for (i = 0; i < set->num; i++) {
|
||||||
|
if (!strcmp(set->entries[i].user, user)) {
|
||||||
|
set->entries[i].user[0] = '\0';
|
||||||
|
|
||||||
|
writekasmpasswd(fname, set);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, "No user named %s found\n", user);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
const char *encrypted = readpassword();
|
const char *encrypted = readpassword();
|
||||||
|
for (i = 0; i < set->num; i++) {
|
||||||
FILE* fp = fopen(fname, "w");
|
if (!strcmp(set->entries[i].user, user))
|
||||||
if (!fp) {
|
break;
|
||||||
fprintf(stderr, "Couldn't open %s for writing\n", fname);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
chmod(fname, S_IRUSR|S_IWUSR);
|
|
||||||
|
|
||||||
if (fwrite(encrypted, strlen(encrypted), 1, fp) != 1) {
|
|
||||||
fprintf(stderr,"Writing to %s failed\n",fname);
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
// No existing user by that name?
|
||||||
|
if (i >= set->num) {
|
||||||
|
i = set->num++;
|
||||||
|
set->entries = realloc(set->entries, set->num * sizeof(struct kasmpasswd_entry_t));
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
strcpy(set->entries[i].user, user);
|
||||||
|
strcpy(set->entries[i].password, encrypted);
|
||||||
|
set->entries[i].write = writer;
|
||||||
|
set->entries[i].owner = owner;
|
||||||
|
|
||||||
|
writekasmpasswd(fname, set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user