Skip to content

Commit

Permalink
Added Support for Reverse VNC
Browse files Browse the repository at this point in the history
Added Support for Password Authentication
Using a Single Tile in Tight, Raw, and ZRLE Encodings
  • Loading branch information
rakesh-mandal-lg committed Jan 2, 2025
1 parent 4c37ae9 commit 4d30b8c
Show file tree
Hide file tree
Showing 15 changed files with 661 additions and 122 deletions.
9 changes: 9 additions & 0 deletions include/auth/vncauth.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#define CHALLENGESIZE 16
#define KEYSIZE 8

struct nvnc_client;

int vnc_auth_handle_message(struct nvnc_client* client);
void genRandomBytes(unsigned char *bytes);
20 changes: 19 additions & 1 deletion include/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,14 @@
#define MAX_CUT_TEXT_SIZE 10000000
#define MAX_CLIENT_UNSOLICITED_TEXT_SIZE 20971520
#define MAX_SECURITY_TYPES 32
#define MAX_CHALLENGESIZE 32

enum nvnc_client_state {
VNC_CLIENT_STATE_ERROR = -1,
VNC_CLIENT_STATE_WAITING_FOR_VERSION = 0,
VNC_CLIENT_STATE_WAITING_FOR_SECURITY,
VNC_CLIENT_STATE_WAITING_FOR_CHALLENGE,

#ifdef ENABLE_TLS
VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_VERSION,
VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_SUBTYPE,
Expand Down Expand Up @@ -135,14 +138,27 @@ struct nvnc_client {
struct bwe* bwe;
int32_t inflight_bytes;

struct {
size_t challenge_len;
uint8_t challenge[MAX_CHALLENGESIZE];
} vnc_auth;

struct vncAuthData
{
char* password;
bool authEnabled;
bool VNCmode;
};
struct vncAuthData* auth;

#ifdef HAVE_CRYPTO
struct crypto_key* apple_dh_secret;

struct {
enum crypto_hash_type hash_type;
enum crypto_cipher_type cipher_type;
size_t challenge_len;
uint8_t challenge[32];
uint8_t challenge[MAX_CHALLENGESIZE];
struct crypto_rsa_pub_key* pub;
} rsa;
#endif
Expand Down Expand Up @@ -170,6 +186,7 @@ struct nvnc {
nvnc_key_fn key_code_fn;
nvnc_pointer_fn pointer_fn;
nvnc_fb_req_fn fb_req_fn;
nvnc_on_encoder_fn on_encode_fn;
nvnc_client_fn new_client_fn;
nvnc_cut_text_fn cut_text_fn;
struct cut_text ext_clipboard_provide_msg;
Expand All @@ -185,6 +202,7 @@ struct nvnc {
enum nvnc_auth_flags auth_flags;
nvnc_auth_fn auth_fn;
void* auth_ud;
nvnc_notifyserverReady_fn notify_fn;

#ifdef ENABLE_TLS
gnutls_certificate_credentials_t tls_creds;
Expand Down
2 changes: 2 additions & 0 deletions include/fb.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct nvnc_fb {
enum nvnc_fb_type type;
int ref;
int hold_count;
pthread_mutex_t lock;
nvnc_fb_release_fn on_release;
void* release_context;
bool is_external;
Expand All @@ -53,3 +54,4 @@ void nvnc_fb_hold(struct nvnc_fb* fb);
void nvnc_fb_release(struct nvnc_fb* fb);
int nvnc_fb_map(struct nvnc_fb* fb);
void nvnc_fb_unmap(struct nvnc_fb* fb);
int nvnc_ref_count(struct nvnc_fb *fb);
10 changes: 10 additions & 0 deletions include/neatvnc.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ enum nvnc_log_level {
enum nvnc_auth_flags {
NVNC_AUTH_REQUIRE_AUTH = 1 << 0,
NVNC_AUTH_REQUIRE_ENCRYPTION = 1 << 1,
NVNC_ENABLE_PASSWORD_AUTH = 1 << 2,
};

struct nvnc_log_data {
Expand All @@ -119,10 +120,12 @@ typedef void (*nvnc_pointer_fn)(struct nvnc_client*, uint16_t x, uint16_t y,
typedef void (*nvnc_fb_req_fn)(struct nvnc_client*, bool is_incremental,
uint16_t x, uint16_t y, uint16_t width,
uint16_t height);
typedef void (*nvnc_on_encoder_fn)(struct nvnc_client*, const char* rfb_encoding);
typedef void (*nvnc_client_fn)(struct nvnc_client*);
typedef void (*nvnc_damage_fn)(struct pixman_region16* damage, void* userdata);
typedef bool (*nvnc_auth_fn)(const char* username, const char* password,
void* userdata);
typedef void (*nvnc_get_client_password_fn)(void *password);
typedef void (*nvnc_cut_text_fn)(struct nvnc_client*, const char* text,
uint32_t len);
typedef void (*nvnc_fb_release_fn)(struct nvnc_fb*, void* context);
Expand All @@ -132,13 +135,16 @@ typedef void (*nvnc_cleanup_fn)(void* userdata);
typedef void (*nvnc_log_fn)(const struct nvnc_log_data*, const char* message);
typedef bool (*nvnc_desktop_layout_fn)(
struct nvnc_client*, const struct nvnc_desktop_layout*);
typedef void (*nvnc_notifyserverReady_fn)(bool res);

extern const char nvnc_version[];

struct nvnc* nvnc_open(const char* addr, uint16_t port);
struct nvnc* nvnc_open_unix(const char *addr);
struct nvnc* nvnc_open_websocket(const char* addr, uint16_t port);
struct nvnc* nvnc_open_from_fd(int fd);
struct nvnc* nvnc_reverse_open(const char* addr, uint16_t port);
void start_tcp_connection(struct nvnc* server);
void nvnc_close(struct nvnc* self);

void nvnc_add_display(struct nvnc*, struct nvnc_display*);
Expand Down Expand Up @@ -166,14 +172,17 @@ void nvnc_set_key_fn(struct nvnc* self, nvnc_key_fn);
void nvnc_set_key_code_fn(struct nvnc* self, nvnc_key_fn);
void nvnc_set_pointer_fn(struct nvnc* self, nvnc_pointer_fn);
void nvnc_set_fb_req_fn(struct nvnc* self, nvnc_fb_req_fn);
void nvnc_set_encode_event_fn(struct nvnc* self, nvnc_on_encoder_fn);
void nvnc_set_new_client_fn(struct nvnc* self, nvnc_client_fn);
void nvnc_set_client_cleanup_fn(struct nvnc_client* self, nvnc_client_fn fn);
void nvnc_set_cut_text_fn(struct nvnc*, nvnc_cut_text_fn fn);
void nvnc_set_desktop_layout_fn(struct nvnc* self, nvnc_desktop_layout_fn);
void nvnc_set_notifyServerReady_fn(struct nvnc* self, nvnc_notifyserverReady_fn);

bool nvnc_has_auth(void);
int nvnc_enable_auth(struct nvnc* self, enum nvnc_auth_flags flags,
nvnc_auth_fn, void* userdata);
void nvnc_enable_vnc_auth(struct nvnc* self, nvnc_get_client_password_fn);
int nvnc_set_tls_creds(struct nvnc* self, const char* privkey_path,
const char* cert_path);
int nvnc_set_rsa_creds(struct nvnc* self, const char* private_key_path);
Expand Down Expand Up @@ -222,6 +231,7 @@ struct nvnc_display* nvnc_display_new(uint16_t x_pos, uint16_t y_pos);
void nvnc_display_ref(struct nvnc_display*);
void nvnc_display_unref(struct nvnc_display*);


struct nvnc* nvnc_display_get_server(const struct nvnc_display*);

void nvnc_display_feed_buffer(struct nvnc_display*, struct nvnc_fb*,
Expand Down
5 changes: 5 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ gmp = dependency('gmp', required: get_option('nettle'))
zlib = dependency('zlib')
gbm = dependency('gbm', required: get_option('gbm'))
libdrm = dependency('libdrm', required: get_option('h264'))
openssl_dep = dependency('openssl', required: true)

libavcodec = dependency('libavcodec', required: get_option('h264'))
libavfilter = dependency('libavfilter', required: get_option('h264'))
Expand All @@ -75,6 +76,7 @@ inc = include_directories('include')

sources = [
'src/auth/common.c',
'src/auth/vncauth.c',
'src/server.c',
'src/vec.c',
'src/enc/zrle.c',
Expand Down Expand Up @@ -107,6 +109,7 @@ dependencies = [
aml,
zlib,
libdrm_inc,
openssl_dep
]

enable_websocket = false
Expand Down Expand Up @@ -143,6 +146,8 @@ if nettle.found() and hogweed.found() and gmp.found()
]
endif

config.set('ENABLE_PASSWORD_AUTH', true)

if host_system == 'linux' and get_option('systemtap') and cc.has_header('sys/sdt.h')
config.set('HAVE_USDT', true)
endif
Expand Down
3 changes: 3 additions & 0 deletions src/auth/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ int security_handshake_failed(struct nvnc_client* client, const char* username,

char buffer[256];

if(client->server->notify_fn) //if reverse is set this code hits in the auth failure case
client->server->notify_fn(false);

client->state = VNC_CLIENT_STATE_ERROR;

uint32_t* result = (uint32_t*)buffer;
Expand Down
193 changes: 193 additions & 0 deletions src/auth/vncauth.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include "common.h"
#include "stream/stream.h"
#include "auth/auth.h"
#include "auth/vncauth.h"
#include <openssl/evp.h>


static unsigned char reverseByte(unsigned char b)
{
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}

int encrypt_des(void *encrypted, int *encrypted_len, const unsigned char deskey[8], const void *plain, const size_t plain_len)
{
int res = 0;
EVP_CIPHER_CTX *ctx;
unsigned char reversedKey[8];

for (int i = 0; i < 8; i++)
{
reversedKey[i] = reverseByte(deskey[i]);
}
if(!(ctx = EVP_CIPHER_CTX_new()))
{
nvnc_log(NVNC_LOG_ERROR, "EVP_CIPHER_CTX_new() failed");
goto onFailure;
}

if(!EVP_EncryptInit_ex(ctx, EVP_des_ecb(), NULL, reversedKey, NULL))
{
nvnc_log(NVNC_LOG_ERROR, "EVP_EncryptInit_ex() failed");
goto onFailure;
}
if(!EVP_EncryptUpdate(ctx, encrypted, encrypted_len, plain, plain_len))
{
nvnc_log(NVNC_LOG_ERROR, "EVP_EncryptUpdate() failed");
goto onFailure;
}
res = 1;

onFailure:
EVP_CIPHER_CTX_free(ctx);
return res;
}

void genRandomBytes(unsigned char *bytes)
{
if (!bytes)
return;

static bool s_srandom_called = false;

if (!s_srandom_called)
{
srandom((unsigned int)time(NULL) ^ (unsigned int)getpid());
s_srandom_called = true;
}

int i = 0;
for (i = 0; i < CHALLENGESIZE; i++)
{
bytes[i] = (unsigned char)(random() & 255);
nvnc_log(NVNC_LOG_DEBUG, "genRandomBytes: %u", bytes[i]);
}
}

void encryptBytes(uint8_t *bytes, char *passwd)
{
if (!bytes)
return;
if (!passwd)
return;
unsigned char key[8];
unsigned int i;
int out_len;

/* key is simply password padded with nulls */
for (i = 0; i < 8; i++)
{
if (i < strlen(passwd))
{
key[i] = passwd[i];
}
else
{
key[i] = 0;
}
}

uint8_t newin[16];
unsigned char encryptedPasswd[8];
memcpy(newin, bytes, 16);

uint8_t outBuf[CHALLENGESIZE];
if(encrypt_des(bytes,&out_len, key, newin, CHALLENGESIZE ) != 1)
{
nvnc_log(NVNC_LOG_DEBUG, "encryptBytes failed");
}
}

bool compare_challenges(struct nvnc_client *cl, const char *response, int len)
{
nvnc_log(NVNC_LOG_DEBUG, "compare_challenges called");

if (!cl)
return false;
if (!response)
return false;

char **passwds;
int i = 0;
uint8_t auth_tmp[CHALLENGESIZE];
memcpy((char *)auth_tmp, (char *)cl->vnc_auth.challenge, CHALLENGESIZE);

for (int i = 0; i < CHALLENGESIZE; i++)
{
nvnc_log(NVNC_LOG_DEBUG, "0x%02X ", auth_tmp[i]);
}
nvnc_log(NVNC_LOG_DEBUG, " password is: %s\n", cl->auth->password);

encryptBytes(auth_tmp, cl->auth->password);

if (memcmp(auth_tmp, response, len) == 0)
{
nvnc_log(NVNC_LOG_DEBUG, "Password comparision passed");
return true;
}

nvnc_log(NVNC_LOG_ERROR, "Password comparision failed");
return false;
}

static int on_vnc_auth_des_challenge_message(struct nvnc_client *client)
{
if (!client)
{
nvnc_log(NVNC_LOG_ERROR, "Client is NULL");
return 0;
}

if (!client->msg_buffer)
{
nvnc_log(NVNC_LOG_ERROR, "Message buffer is NULL");
return 0;
}

if (client->buffer_len < client->buffer_index + CHALLENGESIZE)
{
nvnc_log(NVNC_LOG_WARNING, "Buffer index out of bounds");
return 0;
}

nvnc_log(NVNC_LOG_DEBUG, "on_vnc_auth_des_challenge_message called");

uint8_t response[CHALLENGESIZE];
memcpy(response, client->msg_buffer + client->buffer_index, CHALLENGESIZE);
response[CHALLENGESIZE] = '\0';

if (!compare_challenges(client, (const char *)response, CHALLENGESIZE))
{
security_handshake_failed(client, "", "Invalid password");
}
else
{
security_handshake_ok(client, "");
client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT;
}

return CHALLENGESIZE;
}

int vnc_auth_handle_message(struct nvnc_client *client)
{
nvnc_log(NVNC_LOG_DEBUG, "vnc_auth_handle_message called");
if (!client)
return 0;

switch (client->state)
{
case VNC_CLIENT_STATE_WAITING_FOR_CHALLENGE:
return on_vnc_auth_des_challenge_message(client);
default:;
}

nvnc_log(NVNC_LOG_DEBUG, "Unhandled client state: %d", client->state);
return 0;
}
2 changes: 1 addition & 1 deletion src/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ static void nvnc_display__on_resampler_done(struct nvnc_fb* fb,

DTRACE_PROBE2(neatvnc, nvnc_display__on_resampler_done, self, fb->pts);

if (self->buffer) {
if (self->buffer && self->buffer != fb) {
nvnc_fb_release(self->buffer);
nvnc_fb_unref(self->buffer);
}
Expand Down
Loading

0 comments on commit 4d30b8c

Please sign in to comment.