From: Dietmar Maurer Date: Wed, 23 Oct 2013 08:57:11 +0000 (+0200) Subject: use SASL for auth, always use TLS X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;h=1631c5a9a7437d68bf427d693fac25105d048fd0;p=spiceterm.git use SASL for auth, always use TLS --- diff --git a/Makefile b/Makefile index 9d610d0..af66df6 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ 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 @@ -14,7 +14,8 @@ spiceterm: ${SOURCES} ${HEADERS} spiceterm.c .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 diff --git a/auth-pve.c b/auth-pve.c new file mode 100644 index 0000000..7dd5953 --- /dev/null +++ b/auth-pve.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/event_loop.c b/event_loop.c index 5235b87..1583a18 100644 --- a/event_loop.c +++ b/event_loop.c @@ -27,9 +27,6 @@ #include #include #include -#include -#include -#include #include @@ -44,10 +41,6 @@ static int debug = 0; } \ } -static char *auth_path = "/"; -static char *auth_perm = "Sys.Console"; -static char clientip[INET6_ADDRSTRLEN]; - static GMainLoop *main_loop; static SpiceCoreInterface core; @@ -229,131 +222,6 @@ static void ignore_sigpipe(void) 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); @@ -370,8 +238,6 @@ SpiceCoreInterface *basic_event_loop_init(void) core.watch_remove = watch_remove; core.channel_event = channel_event; - core.auth_plain_verify_credentials = verify_credentials; - ignore_sigpipe(); return &core; diff --git a/screen.c b/screen.c index 53924e0..b3b8eaf 100644 --- a/screen.c +++ b/screen.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "glyphs.h" @@ -706,12 +707,64 @@ spice_screen_draw_char(SpiceScreen *spice_screen, int x, int y, gunichar2 ch, 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; @@ -728,10 +781,25 @@ spice_screen_new(SpiceCoreInterface *core, uint32_t width, uint32_t height, guin 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) { diff --git a/spiceterm.h b/spiceterm.h index cfa9428..95c9b55 100644 --- a/spiceterm.h +++ b/spiceterm.h @@ -159,4 +159,6 @@ gboolean vdagent_owns_clipboard(spiceTerm *vt); 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); +