PROGRAMS=spiceterm
HEADERS=translations.h event_loop.h glyphs.h spiceterm.h
-SOURCES=screen.c event_loop.c input.c spiceterm.c
+SOURCES=screen.c event_loop.c input.c spiceterm.c auth-pve.c
#export G_MESSAGES_DEBUG=all
#export SPICE_DEBUG=1
.PHONY: test
test: spiceterm
- ./spiceterm & remote-viewer spice://localhost:5912
+ #./spiceterm & remote-viewer spice://localhost:5912
+ G_MESSAGES_DEBUG=all SPICE_DEBUG=1 ./spiceterm & G_MESSAGES_DEBUG=all SPICE_DEBUG=1 remote-viewer --debug 'spice://localhost?tls-port=5912' --spice-ca-file /etc/pve/pve-root-ca.pem --spice-secure-channels=all
.PHONY: distclean
distclean: clean
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "spiceterm.h"
+
+static char *auth_path = "/";
+static char *auth_perm = "Sys.Console";
+
+static char *
+urlencode(char *buf, const char *value)
+{
+ static const char *hexchar = "0123456789abcdef";
+ char *p = buf;
+ int i;
+ int l = strlen(value);
+ for (i = 0; i < l; i++) {
+ char c = value[i];
+ if (('a' <= c && c <= 'z') ||
+ ('A' <= c && c <= 'Z') ||
+ ('0' <= c && c <= '9')) {
+ *p++ = c;
+ } else if (c == 32) {
+ *p++ = '+';
+ } else {
+ *p++ = '%';
+ *p++ = hexchar[c >> 4];
+ *p++ = hexchar[c & 15];
+ }
+ }
+ *p = 0;
+
+ return p;
+}
+
+int
+pve_auth_verify(const char *clientip, const char *username, const char *passwd)
+{
+ struct sockaddr_in server;
+
+ int sfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sfd == -1) {
+ perror("pve_auth_verify: socket failed");
+ return -1;
+ }
+
+ struct hostent *he;
+ if ((he = gethostbyname("localhost")) == NULL) {
+ fprintf(stderr, "pve_auth_verify: error resolving hostname\n");
+ goto err;
+ }
+
+ memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
+ server.sin_family = AF_INET;
+ server.sin_port = htons(85);
+
+ if (connect(sfd, (struct sockaddr *)&server, sizeof(server))) {
+ perror("pve_auth_verify: error connecting to server");
+ goto err;
+ }
+
+ char buf[8192];
+ char form[8192];
+
+ char *p = form;
+ p = urlencode(p, "username");
+ *p++ = '=';
+ p = urlencode(p, username);
+
+ *p++ = '&';
+ p = urlencode(p, "password");
+ *p++ = '=';
+ p = urlencode(p, passwd);
+
+ *p++ = '&';
+ p = urlencode(p, "path");
+ *p++ = '=';
+ p = urlencode(p, auth_path);
+
+ *p++ = '&';
+ p = urlencode(p, "privs");
+ *p++ = '=';
+ p = urlencode(p, auth_perm);
+
+ sprintf(buf, "POST /api2/json/access/ticket HTTP/1.1\n"
+ "Host: localhost:85\n"
+ "Connection: close\n"
+ "PVEClientIP: %s\n"
+ "Content-Type: application/x-www-form-urlencoded\n"
+ "Content-Length: %zd\n\n%s\n", clientip, strlen(form), form);
+ ssize_t len = strlen(buf);
+ ssize_t sb = send(sfd, buf, len, 0);
+ if (sb < 0) {
+ perror("pve_auth_verify: send failed");
+ goto err;
+ }
+ if (sb != len) {
+ fprintf(stderr, "pve_auth_verify: partial send error\n");
+ goto err;
+ }
+
+ len = recv(sfd, buf, sizeof(buf) - 1, 0);
+ if (len < 0) {
+ perror("pve_auth_verify: recv failed");
+ goto err;
+ }
+
+ buf[len] = 0;
+
+ //printf("DATA:%s\n", buf);
+
+ shutdown(sfd, SHUT_RDWR);
+
+ if (!strncmp(buf, "HTTP/1.1 200 OK", 15)) {
+ return 0;
+ }
+
+ char *firstline = strtok(buf, "\n");
+
+ fprintf(stderr, "auth failed: %s\n", firstline);
+
+ return -1;
+
+err:
+ shutdown(sfd, SHUT_RDWR);
+ return -1;
+}
#include <sys/time.h>
#include <signal.h>
#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
#include <glib.h>
} \
}
-static char *auth_path = "/";
-static char *auth_perm = "Sys.Console";
-static char clientip[INET6_ADDRSTRLEN];
-
static GMainLoop *main_loop;
static SpiceCoreInterface core;
sigaction(SIGPIPE, &act, NULL);
}
-static char *
-urlencode(char *buf, const char *value)
-{
- static const char *hexchar = "0123456789abcdef";
- char *p = buf;
- int i;
- int l = strlen(value);
- for (i = 0; i < l; i++) {
- char c = value[i];
- if (('a' <= c && c <= 'z') ||
- ('A' <= c && c <= 'Z') ||
- ('0' <= c && c <= '9')) {
- *p++ = c;
- } else if (c == 32) {
- *p++ = '+';
- } else {
- *p++ = '%';
- *p++ = hexchar[c >> 4];
- *p++ = hexchar[c & 15];
- }
- }
- *p = 0;
-
- return p;
-}
-
-static int
-pve_auth_verify(const char *clientip, const char *username, const char *passwd)
-{
- struct sockaddr_in server;
-
- int sfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sfd == -1) {
- perror("pve_auth_verify: socket failed");
- return -1;
- }
-
- struct hostent *he;
- if ((he = gethostbyname("localhost")) == NULL) {
- fprintf(stderr, "pve_auth_verify: error resolving hostname\n");
- goto err;
- }
-
- memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
- server.sin_family = AF_INET;
- server.sin_port = htons(85);
-
- if (connect(sfd, (struct sockaddr *)&server, sizeof(server))) {
- perror("pve_auth_verify: error connecting to server");
- goto err;
- }
-
- char buf[8192];
- char form[8192];
-
- char *p = form;
- p = urlencode(p, "username");
- *p++ = '=';
- p = urlencode(p, username);
-
- *p++ = '&';
- p = urlencode(p, "password");
- *p++ = '=';
- p = urlencode(p, passwd);
-
- *p++ = '&';
- p = urlencode(p, "path");
- *p++ = '=';
- p = urlencode(p, auth_path);
-
- *p++ = '&';
- p = urlencode(p, "privs");
- *p++ = '=';
- p = urlencode(p, auth_perm);
-
- sprintf(buf, "POST /api2/json/access/ticket HTTP/1.1\n"
- "Host: localhost:85\n"
- "Connection: close\n"
- "PVEClientIP: %s\n"
- "Content-Type: application/x-www-form-urlencoded\n"
- "Content-Length: %zd\n\n%s\n", clientip, strlen(form), form);
- ssize_t len = strlen(buf);
- ssize_t sb = send(sfd, buf, len, 0);
- if (sb < 0) {
- perror("pve_auth_verify: send failed");
- goto err;
- }
- if (sb != len) {
- fprintf(stderr, "pve_auth_verify: partial send error\n");
- goto err;
- }
-
- len = recv(sfd, buf, sizeof(buf) - 1, 0);
- if (len < 0) {
- perror("pve_auth_verify: recv failed");
- goto err;
- }
-
- buf[len] = 0;
-
- //printf("DATA:%s\n", buf);
-
- shutdown(sfd, SHUT_RDWR);
-
- if (!strncmp(buf, "HTTP/1.1 200 OK", 15)) {
- return 0;
- }
-
- char *firstline = strtok(buf, "\n");
-
- fprintf(stderr, "auth failed: %s\n", firstline);
-
- return -1;
-
-err:
- shutdown(sfd, SHUT_RDWR);
- return -1;
-}
-
-static int
-verify_credentials(const char *username, const char *password)
-{
- return pve_auth_verify(clientip, username, password);
-}
-
SpiceCoreInterface *basic_event_loop_init(void)
{
main_loop = g_main_loop_new(NULL, FALSE);
core.watch_remove = watch_remove;
core.channel_event = channel_event;
- core.auth_plain_verify_credentials = verify_credentials;
-
ignore_sigpipe();
return &core;
#include <spice/macros.h>
#include <spice/qxl_dev.h>
#include <spice/vd_agent.h>
+#include <sasl/sasl.h>
#include "glyphs.h"
push_command(spice_screen, &update->ext);
}
+static int
+sasl_checkpass_cb(sasl_conn_t *conn,
+ void *context,
+ const char *user,
+ const char *pass,
+ unsigned passlen,
+ struct propctx *propctx)
+{
+ const void *remoteport = NULL;
+ char *clientip = NULL;
+ if (sasl_getprop(conn, SASL_IPREMOTEPORT, &remoteport) == SASL_OK) {
+ clientip = strtok(g_strdup(remoteport), ";");
+ } else {
+ clientip = g_strdup("unknown");
+ }
+
+ int res = pve_auth_verify(clientip, user, pass);
+
+ g_free(clientip);
+
+ return (res == 0) ? SASL_OK : SASL_NOAUTHZ;
+}
+
+static int
+sasl_getopt_cb(void *context, const char *plugin_name,
+ const char *option,
+ const char **result, unsigned *len)
+{
+ if (strcmp(option, "mech_list") == 0) {
+ *result = "plain";
+ len = NULL;
+ return SASL_OK;
+ }
+
+ return SASL_FAIL;
+}
+
+typedef int sasl_cb_fn(void);
+static sasl_callback_t sasl_callbacks[] = {
+ { SASL_CB_GETOPT, (sasl_cb_fn *)sasl_getopt_cb, NULL },
+ { SASL_CB_SERVER_USERDB_CHECKPASS, (sasl_cb_fn *)sasl_checkpass_cb, NULL },
+ { SASL_CB_LIST_END, NULL, NULL },
+};
+
SpiceScreen *
spice_screen_new(SpiceCoreInterface *core, uint32_t width, uint32_t height, guint timeout)
{
int port = 5912;
SpiceScreen *spice_screen = g_new0(SpiceScreen, 1);
SpiceServer* server = spice_server_new();
+ char *x509_key_file = "/etc/pve/local/pve-ssl.key";
+ char *x509_cert_file = "/etc/pve/local/pve-ssl.pem";
+ char *x509_cacert_file = "/etc/pve/pve-root-ca.pem";
+ char *x509_key_password = NULL;
+ char *x509_dh_file = NULL;
+ char *tls_ciphers = "DES-CBC3-SHA";
+
+ gboolean use_auth = TRUE;
spice_screen->width = width;
spice_screen->height = height;
spice_screen->core = core;
spice_screen->server = server;
- printf("listening on port %d (unsecure)\n", port);
-
- spice_server_set_port(server, port);
- spice_server_set_noauth(server);
+ printf("listening on port %d (secure)\n", port);
+ // spice_server_set_addr();
+ // spice_server_set_port(spice_server, port);
+ //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
+
+ spice_server_set_tls(server, port,
+ x509_cacert_file,
+ x509_cert_file,
+ x509_key_file,
+ x509_key_password,
+ x509_dh_file,
+ tls_ciphers);
+
+ if (use_auth) {
+ spice_server_set_sasl(server, 1);
+ spice_server_set_sasl_callbacks(server, sasl_callbacks);
+ } else {
+ spice_server_set_noauth(server);
+ }
int res = spice_server_init(server, core);
if (res != 0) {
void vdagent_request_clipboard(spiceTerm *vt);
void vdagent_grab_clipboard(spiceTerm *vt);
+int pve_auth_verify(const char *clientip, const char *username, const char *passwd);
+