Add option to pass the password in an encrypted file, kasmvncpasswd utility

This commit is contained in:
Lauri Kasanen 2020-10-01 14:37:51 +03:00
parent ae07707e66
commit 279c41fc32
11 changed files with 226 additions and 3 deletions

View File

@ -38,11 +38,13 @@
#include <sys/un.h> #include <sys/un.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <wordexp.h>
#include "websocket.h" #include "websocket.h"
#include <network/TcpSocket.h> #include <network/TcpSocket.h>
#include <rfb/LogWriter.h> #include <rfb/LogWriter.h>
#include <rfb/Configuration.h> #include <rfb/Configuration.h>
#include <rfb/ServerCore.h>
#ifdef WIN32 #ifdef WIN32
#include <os/winerrno.h> #include <os/winerrno.h>
@ -485,6 +487,13 @@ WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
listen(internalSocket); listen(internalSocket);
settings.passwdfile = NULL;
wordexp_t wexp;
if (!wordexp(rfb::Server::kasmPasswordFile, &wexp, WRDE_NOCMD))
settings.passwdfile = strdup(wexp.we_wordv[0]);
wordfree(&wexp);
settings.basicauth = basicauth; settings.basicauth = basicauth;
settings.cert = cert; settings.cert = cert;
settings.key = ""; settings.key = "";

View File

@ -878,7 +878,8 @@ ws_ctx_t *do_handshake(int sock) {
usleep(10); usleep(10);
} }
if (strchr(settings.basicauth, ':')) { const char *colon;
if ((colon = strchr(settings.basicauth, ':'))) {
const char *hdr = strstr(handshake, "Authorization: Basic "); const char *hdr = strstr(handshake, "Authorization: Basic ");
if (!hdr) { if (!hdr) {
sprintf(response, "HTTP/1.1 401 Unauthorized\r\n" sprintf(response, "HTTP/1.1 401 Unauthorized\r\n"
@ -901,7 +902,42 @@ ws_ctx_t *do_handshake(int sock) {
tmp[len] = '\0'; tmp[len] = '\0';
len = ws_b64_pton(tmp, response, 256); len = ws_b64_pton(tmp, response, 256);
if (len <= 0 || strcmp(settings.basicauth, response)) { char authbuf[4096];
strncpy(authbuf, settings.basicauth, 4096);
authbuf[4095] = '\0';
// Do we need to read it from the file?
char *resppw = strchr(response, ':');
if (resppw && *resppw)
resppw++;
if (!colon[1] && settings.passwdfile) {
if (resppw && *resppw) {
char pwbuf[4096];
FILE *f = fopen(settings.passwdfile, "r");
if (f) {
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';
const char *encrypted = crypt(resppw, "$5$kasm$");
*resppw = '\0';
snprintf(pwbuf, 4096, "%s%s", response, encrypted);
pwbuf[4095] = '\0';
strcpy(response, pwbuf);
}
} else {
// Client tried an empty password, just fail them
response[0] = '\0';
}
}
if (len <= 0 || strcmp(authbuf, response)) {
sprintf(response, "HTTP/1.1 401 Forbidden\r\n" sprintf(response, "HTTP/1.1 401 Forbidden\r\n"
"\r\n"); "\r\n");
ws_send(ws_ctx, response, strlen(response)); ws_send(ws_ctx, response, strlen(response));

View File

@ -67,6 +67,7 @@ typedef struct {
const char *cert; const char *cert;
const char *key; const char *key;
const char *basicauth; const char *basicauth;
const char *passwdfile;
int ssl_only; int ssl_only;
const char *httpdir; const char *httpdir;
} settings_t; } settings_t;

View File

@ -191,6 +191,11 @@ rfb::BoolParameter rfb::Server::printVideoArea
"Print the detected video area % value.", "Print the detected video area % value.",
false); false);
rfb::StringParameter rfb::Server::kasmPasswordFile
("KasmPasswordFile",
"Password file for BasicAuth, created with the kasmvncpasswd utility.",
"~/.kasmpasswd");
static void bandwidthPreset() { static void bandwidthPreset() {
rfb::Server::dynamicQualityMin.setParam(2); rfb::Server::dynamicQualityMin.setParam(2);
rfb::Server::dynamicQualityMax.setParam(9); rfb::Server::dynamicQualityMax.setParam(9);

View File

@ -56,6 +56,7 @@ namespace rfb {
static IntParameter videoOutTime; static IntParameter videoOutTime;
static IntParameter videoArea; static IntParameter videoArea;
static IntParameter videoScaling; static IntParameter videoScaling;
static StringParameter kasmPasswordFile;
static BoolParameter printVideoArea; static BoolParameter printVideoArea;
static BoolParameter protocol3_3; static BoolParameter protocol3_3;
static BoolParameter alwaysShared; static BoolParameter alwaysShared;

View File

@ -2,6 +2,7 @@ add_subdirectory(tx)
add_subdirectory(common) add_subdirectory(common)
add_subdirectory(vncconfig) add_subdirectory(vncconfig)
add_subdirectory(vncpasswd) add_subdirectory(vncpasswd)
add_subdirectory(kasmvncpasswd)
install(PROGRAMS vncserver DESTINATION ${BIN_DIR}) install(PROGRAMS vncserver DESTINATION ${BIN_DIR})
install(FILES vncserver.man DESTINATION ${MAN_DIR}/man1 RENAME vncserver.1) install(FILES vncserver.man DESTINATION ${MAN_DIR}/man1 RENAME vncserver.1)

1
unix/kasmvncpasswd/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
kasmvncpasswd

View File

@ -0,0 +1,8 @@
include_directories(${CMAKE_SOURCE_DIR}/common)
add_executable(kasmvncpasswd
kasmvncpasswd.c)
target_link_libraries(kasmvncpasswd crypt)
install(TARGETS kasmvncpasswd DESTINATION ${BIN_DIR})

View File

@ -0,0 +1,155 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2010 Antoine Martin. All Rights Reserved.
* Copyright (C) 2010 D. R. Commander. All Rights Reserved.
* Copyright (C) 2020 Kasm. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include <crypt.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <termios.h>
#include <unistd.h>
#include <wordexp.h>
static void usage(const char *prog)
{
fprintf(stderr, "Usage: %s [file]\n", prog);
fprintf(stderr, " %s -f\n", prog);
exit(1);
}
static void enableEcho(unsigned char enable) {
struct termios attrs;
tcgetattr(fileno(stdin), &attrs);
if (enable)
attrs.c_lflag |= ECHO;
else
attrs.c_lflag &= ~ECHO;
attrs.c_lflag |= ECHONL;
tcsetattr(fileno(stdin), TCSAFLUSH, &attrs);
}
static const char *encryptpw(const char *in) {
return crypt(in, "$5$kasm$");
}
static char* getpassword(const char* prompt, char *buf) {
if (prompt) fputs(prompt, stdout);
enableEcho(0);
char* result = fgets(buf, 4096, stdin);
enableEcho(1);
if (result) {
if (result[strlen(result)-1] == '\n')
result[strlen(result)-1] = 0;
return buf;
}
return NULL;
}
static char pw1[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() {
while (1) {
if (!getpassword("Password:", pw1)) {
perror("getpassword error");
exit(1);
}
if (strlen(pw1) < 6) {
if (strlen(pw1) == 0) {
fprintf(stderr,"Password not changed\n");
exit(1);
}
fprintf(stderr,"Password must be at least 6 characters - try again\n");
continue;
}
if (!getpassword("Verify:", pw2)) {
perror("getpass error");
exit(1);
}
if (strcmp(pw1, pw2) != 0) {
fprintf(stderr,"Passwords don't match - try again\n");
continue;
}
return encryptpw(pw1);
}
}
int main(int argc, char** argv)
{
char* fname = 0;
for (int i = 1; i < argc; i++) {
if (strncmp(argv[i], "-f", 2) == 0) {
return encrypt_pipe();
} else if (argv[i][0] == '-') {
usage(argv[0]);
} else if (!fname) {
fname = argv[i];
} else {
usage(argv[0]);
}
}
if (!fname) {
wordexp_t wexp;
if (!wordexp("~/.kasmpasswd", &wexp, WRDE_NOCMD) && wexp.we_wordv[0])
fname = strdup(wexp.we_wordv[0]);
wordfree(&wexp);
}
if (!fname)
usage(argv[0]);
while (1) {
const char *encrypted = readpassword();
FILE* fp = fopen(fname, "w");
if (!fp) {
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);
return 0;
}
}

View File

@ -46,7 +46,7 @@ Xvnc_CPPFLAGS = $(XVNC_CPPFLAGS) -DKASMVNC -DNO_MODULE_EXTS \
-I$(top_srcdir)/include ${XSERVERLIBS_CFLAGS} -I$(includedir) -I$(top_srcdir)/include ${XSERVERLIBS_CFLAGS} -I$(includedir)
Xvnc_LDADD = $(XVNC_LIBS) libvnccommon.la $(COMMON_LIBS) \ Xvnc_LDADD = $(XVNC_LIBS) libvnccommon.la $(COMMON_LIBS) \
$(XSERVER_LIBS) $(XSERVER_SYS_LIBS) $(XVNC_SYS_LIBS) -lX11 -lwebp -lssl -lcrypto $(XSERVER_LIBS) $(XSERVER_SYS_LIBS) $(XVNC_SYS_LIBS) -lX11 -lwebp -lssl -lcrypto -lcrypt
Xvnc_LDFLAGS = $(LD_EXPORT_SYMBOLS_FLAG) -fopenmp Xvnc_LDFLAGS = $(LD_EXPORT_SYMBOLS_FLAG) -fopenmp

View File

@ -117,6 +117,11 @@ the \fBvncpasswd\fP(1) utility. The file is accessed each time a connection
comes in, so it can be changed on the fly. comes in, so it can be changed on the fly.
. .
.TP .TP
.B \-KasmPasswordFile \fIpasswd-file\fP
Password file for BasicAuth, created with the \fBkasmvncpasswd\fP utility.
Default \fI~/.kasmpasswd\fP.
.
.TP
.B \-AcceptCutText .B \-AcceptCutText
Accept clipboard updates from clients. Default is on. Accept clipboard updates from clients. Default is on.
. .
@ -316,6 +321,7 @@ Require SSL for websocket connections. Default off, non-SSL allowed.
.TP .TP
.B \-basicAuth \fIuser:pass\fP .B \-basicAuth \fIuser:pass\fP
Username and password for websocket connections. Default empty, no authentication required. Username and password for websocket connections. Default empty, no authentication required.
If the password is empty, read it from the \fB-KasmPasswordFile\fP.
. .
.TP .TP
.B \-SecurityTypes \fIsec-types\fP .B \-SecurityTypes \fIsec-types\fP