Merge pull request #33 from kasmtech/vncserver-remove-basicauth
Vncserver remove basicauthpull/34/head
						commit
						0638245ff9
					
				@ -0,0 +1,75 @@
 | 
			
		||||
/* Copyright (C) 2021 Kasm
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __NETWORK_GET_API_H__
 | 
			
		||||
#define __NETWORK_GET_API_H__
 | 
			
		||||
 | 
			
		||||
#include <kasmpasswd.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <rfb/PixelBuffer.h>
 | 
			
		||||
#include <rfb/PixelFormat.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace network {
 | 
			
		||||
 | 
			
		||||
  class GetAPIMessager {
 | 
			
		||||
  public:
 | 
			
		||||
    GetAPIMessager(const char *passwdfile_);
 | 
			
		||||
 | 
			
		||||
    // from main thread
 | 
			
		||||
    void mainUpdateScreen(rfb::PixelBuffer *pb);
 | 
			
		||||
 | 
			
		||||
    // from network threads
 | 
			
		||||
    uint8_t *netGetScreenshot(uint16_t w, uint16_t h,
 | 
			
		||||
                              const uint8_t q, const bool dedup,
 | 
			
		||||
                              uint32_t &len, uint8_t *staging);
 | 
			
		||||
    uint8_t netAddUser(const char name[], const char pw[], const bool write);
 | 
			
		||||
    uint8_t netRemoveUser(const char name[]);
 | 
			
		||||
    uint8_t netGiveControlTo(const char name[]);
 | 
			
		||||
 | 
			
		||||
    enum USER_ACTION {
 | 
			
		||||
      //USER_ADD, - handled locally for interactivity
 | 
			
		||||
      USER_REMOVE,
 | 
			
		||||
      USER_GIVE_CONTROL,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct action_data {
 | 
			
		||||
      enum USER_ACTION action;
 | 
			
		||||
      kasmpasswd_entry_t data;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_t userMutex;
 | 
			
		||||
    std::vector<action_data> actionQueue;
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    const char *passwdfile;
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_t screenMutex;
 | 
			
		||||
    rfb::ManagedPixelBuffer screenPb;
 | 
			
		||||
    uint16_t screenW, screenH;
 | 
			
		||||
    uint64_t screenHash;
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> cachedJpeg;
 | 
			
		||||
    uint16_t cachedW, cachedH;
 | 
			
		||||
    uint8_t cachedQ;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // __NETWORK_GET_API_H__
 | 
			
		||||
@ -0,0 +1,288 @@
 | 
			
		||||
/* Copyright (C) 2021 Kasm
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define __STDC_FORMAT_MACROS
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <network/GetAPI.h>
 | 
			
		||||
#include <rfb/ConnParams.h>
 | 
			
		||||
#include <rfb/LogWriter.h>
 | 
			
		||||
#include <rfb/JpegCompressor.h>
 | 
			
		||||
#include <rfb/xxhash.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
using namespace network;
 | 
			
		||||
using namespace rfb;
 | 
			
		||||
 | 
			
		||||
static LogWriter vlog("GetAPIMessager");
 | 
			
		||||
 | 
			
		||||
PixelBuffer *progressiveBilinearScale(const PixelBuffer *pb,
 | 
			
		||||
					const uint16_t tgtw, const uint16_t tgth,
 | 
			
		||||
					const float tgtdiff);
 | 
			
		||||
 | 
			
		||||
struct TightJPEGConfiguration {
 | 
			
		||||
    int quality;
 | 
			
		||||
    int subsampling;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct TightJPEGConfiguration conf[10] = {
 | 
			
		||||
  {  15, subsample4X }, // 0
 | 
			
		||||
  {  29, subsample4X }, // 1
 | 
			
		||||
  {  41, subsample4X }, // 2
 | 
			
		||||
  {  42, subsample2X }, // 3
 | 
			
		||||
  {  62, subsample2X }, // 4
 | 
			
		||||
  {  77, subsample2X }, // 5
 | 
			
		||||
  {  79, subsampleNone }, // 6
 | 
			
		||||
  {  86, subsampleNone }, // 7
 | 
			
		||||
  {  92, subsampleNone }, // 8
 | 
			
		||||
  { 100, subsampleNone }  // 9
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GetAPIMessager::GetAPIMessager(const char *passwdfile_): passwdfile(passwdfile_),
 | 
			
		||||
					screenW(0), screenH(0), screenHash(0),
 | 
			
		||||
					cachedW(0), cachedH(0), cachedQ(0) {
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_init(&screenMutex, NULL);
 | 
			
		||||
	pthread_mutex_init(&userMutex, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// from main thread
 | 
			
		||||
void GetAPIMessager::mainUpdateScreen(rfb::PixelBuffer *pb) {
 | 
			
		||||
	if (pthread_mutex_trylock(&screenMutex))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	int stride;
 | 
			
		||||
	const rdr::U8 * const buf = pb->getBuffer(pb->getRect(), &stride);
 | 
			
		||||
 | 
			
		||||
	if (pb->width() != screenW || pb->height() != screenH) {
 | 
			
		||||
		screenHash = 0;
 | 
			
		||||
		screenW = pb->width();
 | 
			
		||||
		screenH = pb->height();
 | 
			
		||||
		screenPb.setPF(pb->getPF());
 | 
			
		||||
		screenPb.setSize(screenW, screenH);
 | 
			
		||||
 | 
			
		||||
		cachedW = cachedH = cachedQ = 0;
 | 
			
		||||
		cachedJpeg.clear();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const uint64_t newHash = XXH64(buf, pb->area() * 4, 0);
 | 
			
		||||
	if (newHash != screenHash) {
 | 
			
		||||
		cachedW = cachedH = cachedQ = 0;
 | 
			
		||||
		cachedJpeg.clear();
 | 
			
		||||
 | 
			
		||||
		screenHash = newHash;
 | 
			
		||||
		rdr::U8 *rw = screenPb.getBufferRW(screenPb.getRect(), &stride);
 | 
			
		||||
		memcpy(rw, buf, screenW * screenH * 4);
 | 
			
		||||
		screenPb.commitBufferRW(screenPb.getRect());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_unlock(&screenMutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// from network threads
 | 
			
		||||
uint8_t *GetAPIMessager::netGetScreenshot(uint16_t w, uint16_t h,
 | 
			
		||||
	const uint8_t q, const bool dedup,
 | 
			
		||||
	uint32_t &len, uint8_t *staging) {
 | 
			
		||||
 | 
			
		||||
	uint8_t *ret = NULL;
 | 
			
		||||
	len = 0;
 | 
			
		||||
 | 
			
		||||
	if (w > screenW)
 | 
			
		||||
		w = screenW;
 | 
			
		||||
	if (h > screenH)
 | 
			
		||||
		h = screenH;
 | 
			
		||||
 | 
			
		||||
	if (!screenW || !screenH)
 | 
			
		||||
		vlog.error("Screenshot requested but no screenshot exists (screen hasn't been viewed)");
 | 
			
		||||
 | 
			
		||||
	if (!w || !h || q > 9 || !staging)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (pthread_mutex_lock(&screenMutex))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (w == cachedW && h == cachedH && q == cachedQ) {
 | 
			
		||||
		if (dedup) {
 | 
			
		||||
			// Return the hash of the unchanged image
 | 
			
		||||
			sprintf((char *) staging, "%" PRIx64, screenHash);
 | 
			
		||||
			ret = staging;
 | 
			
		||||
			len = 16;
 | 
			
		||||
		} else {
 | 
			
		||||
			// Return the cached image
 | 
			
		||||
			len = cachedJpeg.size();
 | 
			
		||||
			ret = staging;
 | 
			
		||||
			memcpy(ret, &cachedJpeg[0], len);
 | 
			
		||||
 | 
			
		||||
			vlog.info("Returning cached screenshot");
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		// Encode the new JPEG, cache it
 | 
			
		||||
		JpegCompressor jc;
 | 
			
		||||
		int quality, subsampling;
 | 
			
		||||
 | 
			
		||||
		quality = conf[q].quality;
 | 
			
		||||
		subsampling = conf[q].subsampling;
 | 
			
		||||
 | 
			
		||||
		jc.clear();
 | 
			
		||||
		int stride;
 | 
			
		||||
 | 
			
		||||
		if (w != screenW || h != screenH) {
 | 
			
		||||
			float xdiff = w / (float) screenW;
 | 
			
		||||
			float ydiff = h / (float) screenH;
 | 
			
		||||
			const float diff = xdiff < ydiff ? xdiff : ydiff;
 | 
			
		||||
 | 
			
		||||
			const uint16_t neww = screenW * diff;
 | 
			
		||||
			const uint16_t newh = screenH * diff;
 | 
			
		||||
 | 
			
		||||
			const PixelBuffer *scaled = progressiveBilinearScale(&screenPb, neww, newh, diff);
 | 
			
		||||
			const rdr::U8 * const buf = scaled->getBuffer(scaled->getRect(), &stride);
 | 
			
		||||
 | 
			
		||||
			jc.compress(buf, stride, scaled->getRect(),
 | 
			
		||||
					scaled->getPF(), quality, subsampling);
 | 
			
		||||
 | 
			
		||||
			cachedJpeg.resize(jc.length());
 | 
			
		||||
			memcpy(&cachedJpeg[0], jc.data(), jc.length());
 | 
			
		||||
 | 
			
		||||
			delete scaled;
 | 
			
		||||
 | 
			
		||||
			vlog.info("Returning scaled screenshot");
 | 
			
		||||
		} else {
 | 
			
		||||
			const rdr::U8 * const buf = screenPb.getBuffer(screenPb.getRect(), &stride);
 | 
			
		||||
 | 
			
		||||
			jc.compress(buf, stride, screenPb.getRect(),
 | 
			
		||||
					screenPb.getPF(), quality, subsampling);
 | 
			
		||||
 | 
			
		||||
			cachedJpeg.resize(jc.length());
 | 
			
		||||
			memcpy(&cachedJpeg[0], jc.data(), jc.length());
 | 
			
		||||
 | 
			
		||||
			vlog.info("Returning normal screenshot");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cachedQ = q;
 | 
			
		||||
		cachedW = w;
 | 
			
		||||
		cachedH = h;
 | 
			
		||||
 | 
			
		||||
		len = cachedJpeg.size();
 | 
			
		||||
		ret = staging;
 | 
			
		||||
		memcpy(ret, &cachedJpeg[0], len);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_unlock(&screenMutex);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define USERNAME_LEN sizeof(((struct kasmpasswd_entry_t *)0)->user)
 | 
			
		||||
#define PASSWORD_LEN sizeof(((struct kasmpasswd_entry_t *)0)->password)
 | 
			
		||||
 | 
			
		||||
uint8_t GetAPIMessager::netAddUser(const char name[], const char pw[], const bool write) {
 | 
			
		||||
	if (strlen(name) >= USERNAME_LEN) {
 | 
			
		||||
		vlog.error("Username too long");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strlen(pw) >= PASSWORD_LEN) {
 | 
			
		||||
		vlog.error("Password too long");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!passwdfile)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	action_data act;
 | 
			
		||||
 | 
			
		||||
	memcpy(act.data.user, name, USERNAME_LEN);
 | 
			
		||||
	act.data.user[USERNAME_LEN - 1] = '\0';
 | 
			
		||||
	memcpy(act.data.password, pw, PASSWORD_LEN);
 | 
			
		||||
	act.data.password[PASSWORD_LEN - 1] = '\0';
 | 
			
		||||
	act.data.owner = 0;
 | 
			
		||||
	act.data.write = write;
 | 
			
		||||
 | 
			
		||||
	if (pthread_mutex_lock(&userMutex))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
        // This needs to be handled locally for proper interactivity
 | 
			
		||||
        // (consider adding users when nobody is connected).
 | 
			
		||||
        // The mutex and atomic rename keep things in sync.
 | 
			
		||||
 | 
			
		||||
        struct kasmpasswd_t *set = readkasmpasswd(passwdfile);
 | 
			
		||||
        unsigned s;
 | 
			
		||||
        for (s = 0; s < set->num; s++) {
 | 
			
		||||
          if (!strcmp(set->entries[s].user, act.data.user)) {
 | 
			
		||||
            vlog.error("Can't create user %s, already exists", act.data.user);
 | 
			
		||||
            goto out;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        s = set->num++;
 | 
			
		||||
        set->entries = (struct kasmpasswd_entry_t *) realloc(set->entries,
 | 
			
		||||
                                                                 set->num * sizeof(struct kasmpasswd_entry_t));
 | 
			
		||||
        set->entries[s] = act.data;
 | 
			
		||||
 | 
			
		||||
        writekasmpasswd(passwdfile, set);
 | 
			
		||||
        vlog.info("User %s created", act.data.user);
 | 
			
		||||
out:
 | 
			
		||||
	pthread_mutex_unlock(&userMutex);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GetAPIMessager::netRemoveUser(const char name[]) {
 | 
			
		||||
	if (strlen(name) >= USERNAME_LEN) {
 | 
			
		||||
		vlog.error("Username too long");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	action_data act;
 | 
			
		||||
	act.action = USER_REMOVE;
 | 
			
		||||
 | 
			
		||||
	memcpy(act.data.user, name, USERNAME_LEN);
 | 
			
		||||
	act.data.user[USERNAME_LEN - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
	if (pthread_mutex_lock(&userMutex))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	actionQueue.push_back(act);
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_unlock(&userMutex);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GetAPIMessager::netGiveControlTo(const char name[]) {
 | 
			
		||||
	if (strlen(name) >= USERNAME_LEN) {
 | 
			
		||||
		vlog.error("Username too long");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	action_data act;
 | 
			
		||||
	act.action = USER_GIVE_CONTROL;
 | 
			
		||||
 | 
			
		||||
	memcpy(act.data.user, name, USERNAME_LEN);
 | 
			
		||||
	act.data.user[USERNAME_LEN - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
	if (pthread_mutex_lock(&userMutex))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	actionQueue.push_back(act);
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_unlock(&userMutex);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue