mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-08-27 02:51:16 +00:00
Add data encryption using SASL SSF layer
This commit is contained in:
parent
1b1d647439
commit
bb5371f668
@ -1,3 +1,9 @@
|
|||||||
|
Wed Dec 5 10:25:00 EST 2007 Daniel P. Berrange <berrange@redhat.com>
|
||||||
|
|
||||||
|
* src/remote_internal.c, qemud/qemud.c, qemud/internal.h,
|
||||||
|
qemud/remote.c: Add support for SASL SSF layer providing
|
||||||
|
data encryption of the connection post-authentication.
|
||||||
|
|
||||||
Wed Dec 5 10:20:00 EST 2007 Daniel P. Berrange <berrange@redhat.com>
|
Wed Dec 5 10:20:00 EST 2007 Daniel P. Berrange <berrange@redhat.com>
|
||||||
|
|
||||||
* configure.in: Add checks for SASL library
|
* configure.in: Add checks for SASL library
|
||||||
|
@ -73,10 +73,17 @@ enum qemud_mode {
|
|||||||
QEMUD_MODE_TLS_HANDSHAKE,
|
QEMUD_MODE_TLS_HANDSHAKE,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* These have to remain compatible with gnutls_record_get_direction. */
|
/* Whether we're passing reads & writes through a sasl SSF */
|
||||||
enum qemud_tls_direction {
|
enum qemud_sasl_ssf {
|
||||||
QEMUD_TLS_DIRECTION_READ = 0,
|
QEMUD_SASL_SSF_NONE = 0,
|
||||||
QEMUD_TLS_DIRECTION_WRITE = 1,
|
QEMUD_SASL_SSF_READ = 1,
|
||||||
|
QEMUD_SASL_SSF_WRITE = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum qemud_sock_type {
|
||||||
|
QEMUD_SOCK_TYPE_UNIX = 0,
|
||||||
|
QEMUD_SOCK_TYPE_TCP = 1,
|
||||||
|
QEMUD_SOCK_TYPE_TLS = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Stores the per-client connection state */
|
/* Stores the per-client connection state */
|
||||||
@ -90,13 +97,18 @@ struct qemud_client {
|
|||||||
struct sockaddr_storage addr;
|
struct sockaddr_storage addr;
|
||||||
socklen_t addrlen;
|
socklen_t addrlen;
|
||||||
|
|
||||||
/* If set, TLS is required on this socket. */
|
int type; /* qemud_sock_type */
|
||||||
int tls;
|
gnutls_session_t tlssession;
|
||||||
gnutls_session_t session;
|
|
||||||
enum qemud_tls_direction direction;
|
|
||||||
int auth;
|
int auth;
|
||||||
#if HAVE_SASL
|
#if HAVE_SASL
|
||||||
sasl_conn_t *saslconn;
|
sasl_conn_t *saslconn;
|
||||||
|
int saslSSF;
|
||||||
|
const char *saslDecoded;
|
||||||
|
unsigned int saslDecodedLength;
|
||||||
|
unsigned int saslDecodedOffset;
|
||||||
|
const char *saslEncoded;
|
||||||
|
unsigned int saslEncodedLength;
|
||||||
|
unsigned int saslEncodedOffset;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
unsigned int incomingSerial;
|
unsigned int incomingSerial;
|
||||||
@ -121,8 +133,7 @@ struct qemud_client {
|
|||||||
struct qemud_socket {
|
struct qemud_socket {
|
||||||
int fd;
|
int fd;
|
||||||
int readonly;
|
int readonly;
|
||||||
/* If set, TLS is required on this socket. */
|
int type; /* qemud_sock_type */
|
||||||
int tls;
|
|
||||||
int auth;
|
int auth;
|
||||||
int port;
|
int port;
|
||||||
struct qemud_socket *next;
|
struct qemud_socket *next;
|
||||||
|
244
qemud/qemud.c
244
qemud/qemud.c
@ -461,6 +461,7 @@ static int qemudListenUnix(struct qemud_server *server,
|
|||||||
|
|
||||||
sock->readonly = readonly;
|
sock->readonly = readonly;
|
||||||
sock->port = -1;
|
sock->port = -1;
|
||||||
|
sock->type = QEMUD_SOCK_TYPE_UNIX;
|
||||||
|
|
||||||
if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
|
if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||||||
qemudLog(QEMUD_ERR, "Failed to create socket: %s",
|
qemudLog(QEMUD_ERR, "Failed to create socket: %s",
|
||||||
@ -575,7 +576,7 @@ remoteMakeSockets (int *fds, int max_fds, int *nfds_r, const char *service)
|
|||||||
static int
|
static int
|
||||||
remoteListenTCP (struct qemud_server *server,
|
remoteListenTCP (struct qemud_server *server,
|
||||||
const char *port,
|
const char *port,
|
||||||
int tls,
|
int type,
|
||||||
int auth)
|
int auth)
|
||||||
{
|
{
|
||||||
int fds[2];
|
int fds[2];
|
||||||
@ -604,7 +605,7 @@ remoteListenTCP (struct qemud_server *server,
|
|||||||
server->nsockets++;
|
server->nsockets++;
|
||||||
|
|
||||||
sock->fd = fds[i];
|
sock->fd = fds[i];
|
||||||
sock->tls = tls;
|
sock->type = type;
|
||||||
sock->auth = auth;
|
sock->auth = auth;
|
||||||
|
|
||||||
if (getsockname(sock->fd, (struct sockaddr *)(&sa), &salen) < 0)
|
if (getsockname(sock->fd, (struct sockaddr *)(&sa), &salen) < 0)
|
||||||
@ -743,10 +744,10 @@ static struct qemud_server *qemudInitialize(int sigread) {
|
|||||||
|
|
||||||
if (ipsock) {
|
if (ipsock) {
|
||||||
#if HAVE_SASL
|
#if HAVE_SASL
|
||||||
if (listen_tcp && remoteListenTCP (server, tcp_port, 0, REMOTE_AUTH_SASL) < 0)
|
if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, REMOTE_AUTH_SASL) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
#else
|
#else
|
||||||
if (listen_tcp && remoteListenTCP (server, tcp_port, 0, REMOTE_AUTH_NONE) < 0)
|
if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, REMOTE_AUTH_NONE) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -754,7 +755,7 @@ static struct qemud_server *qemudInitialize(int sigread) {
|
|||||||
if (remoteInitializeGnuTLS () < 0)
|
if (remoteInitializeGnuTLS () < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (remoteListenTCP (server, tls_port, 1, REMOTE_AUTH_NONE) < 0)
|
if (remoteListenTCP (server, tls_port, QEMUD_SOCK_TYPE_TLS, REMOTE_AUTH_NONE) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -787,7 +788,7 @@ static struct qemud_server *qemudInitialize(int sigread) {
|
|||||||
*/
|
*/
|
||||||
sock = server->sockets;
|
sock = server->sockets;
|
||||||
while (sock) {
|
while (sock) {
|
||||||
if (sock->port != -1 && sock->tls) {
|
if (sock->port != -1 && sock->type == QEMUD_SOCK_TYPE_TLS) {
|
||||||
port = sock->port;
|
port = sock->port;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -979,7 +980,7 @@ remoteCheckAccess (struct qemud_client *client)
|
|||||||
int found, err;
|
int found, err;
|
||||||
|
|
||||||
/* Verify client certificate. */
|
/* Verify client certificate. */
|
||||||
if (remoteCheckCertificate (client->session) == -1) {
|
if (remoteCheckCertificate (client->tlssession) == -1) {
|
||||||
qemudLog (QEMUD_ERR, "remoteCheckCertificate: failed to verify client's certificate");
|
qemudLog (QEMUD_ERR, "remoteCheckCertificate: failed to verify client's certificate");
|
||||||
if (!tls_no_verify_certificate) return -1;
|
if (!tls_no_verify_certificate) return -1;
|
||||||
else qemudLog (QEMUD_INFO, "remoteCheckCertificate: tls_no_verify_certificate is set so the bad certificate is ignored");
|
else qemudLog (QEMUD_INFO, "remoteCheckCertificate: tls_no_verify_certificate is set so the bad certificate is ignored");
|
||||||
@ -1031,7 +1032,6 @@ remoteCheckAccess (struct qemud_client *client)
|
|||||||
client->bufferOffset = 0;
|
client->bufferOffset = 0;
|
||||||
client->buffer[0] = '\1';
|
client->buffer[0] = '\1';
|
||||||
client->mode = QEMUD_MODE_TX_PACKET;
|
client->mode = QEMUD_MODE_TX_PACKET;
|
||||||
client->direction = QEMUD_TLS_DIRECTION_WRITE;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1065,12 +1065,12 @@ static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket
|
|||||||
client->magic = QEMUD_CLIENT_MAGIC;
|
client->magic = QEMUD_CLIENT_MAGIC;
|
||||||
client->fd = fd;
|
client->fd = fd;
|
||||||
client->readonly = sock->readonly;
|
client->readonly = sock->readonly;
|
||||||
client->tls = sock->tls;
|
client->type = sock->type;
|
||||||
client->auth = sock->auth;
|
client->auth = sock->auth;
|
||||||
memcpy (&client->addr, &addr, sizeof addr);
|
memcpy (&client->addr, &addr, sizeof addr);
|
||||||
client->addrlen = addrlen;
|
client->addrlen = addrlen;
|
||||||
|
|
||||||
if (!client->tls) {
|
if (client->type != QEMUD_SOCK_TYPE_TLS) {
|
||||||
client->mode = QEMUD_MODE_RX_HEADER;
|
client->mode = QEMUD_MODE_RX_HEADER;
|
||||||
client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN;
|
client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN;
|
||||||
|
|
||||||
@ -1079,15 +1079,15 @@ static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket
|
|||||||
} else {
|
} else {
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
client->session = remoteInitializeTLSSession ();
|
client->tlssession = remoteInitializeTLSSession ();
|
||||||
if (client->session == NULL)
|
if (client->tlssession == NULL)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
gnutls_transport_set_ptr (client->session,
|
gnutls_transport_set_ptr (client->tlssession,
|
||||||
(gnutls_transport_ptr_t) (long) fd);
|
(gnutls_transport_ptr_t) (long) fd);
|
||||||
|
|
||||||
/* Begin the TLS handshake. */
|
/* Begin the TLS handshake. */
|
||||||
ret = gnutls_handshake (client->session);
|
ret = gnutls_handshake (client->tlssession);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
/* Unlikely, but ... Next step is to check the certificate. */
|
/* Unlikely, but ... Next step is to check the certificate. */
|
||||||
if (remoteCheckAccess (client) == -1)
|
if (remoteCheckAccess (client) == -1)
|
||||||
@ -1099,7 +1099,6 @@ static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket
|
|||||||
/* Most likely. */
|
/* Most likely. */
|
||||||
client->mode = QEMUD_MODE_TLS_HANDSHAKE;
|
client->mode = QEMUD_MODE_TLS_HANDSHAKE;
|
||||||
client->bufferLength = -1;
|
client->bufferLength = -1;
|
||||||
client->direction = gnutls_record_get_direction (client->session);
|
|
||||||
|
|
||||||
if (qemudRegisterClientEvent (server, client, 0) < 0)
|
if (qemudRegisterClientEvent (server, client, 0) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -1117,7 +1116,7 @@ static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (client->session) gnutls_deinit (client->session);
|
if (client->tlssession) gnutls_deinit (client->tlssession);
|
||||||
close (fd);
|
close (fd);
|
||||||
free (client);
|
free (client);
|
||||||
return -1;
|
return -1;
|
||||||
@ -1150,24 +1149,21 @@ static void qemudDispatchClientFailure(struct qemud_server *server, struct qemud
|
|||||||
#if HAVE_SASL
|
#if HAVE_SASL
|
||||||
if (client->saslconn) sasl_dispose(&client->saslconn);
|
if (client->saslconn) sasl_dispose(&client->saslconn);
|
||||||
#endif
|
#endif
|
||||||
if (client->tls && client->session) gnutls_deinit (client->session);
|
if (client->tlssession) gnutls_deinit (client->tlssession);
|
||||||
close(client->fd);
|
close(client->fd);
|
||||||
free(client);
|
free(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int qemudClientRead(struct qemud_server *server,
|
static int qemudClientReadBuf(struct qemud_server *server,
|
||||||
struct qemud_client *client) {
|
struct qemud_client *client,
|
||||||
int ret, len;
|
char *data, unsigned len) {
|
||||||
char *data;
|
int ret;
|
||||||
|
|
||||||
data = client->buffer + client->bufferOffset;
|
|
||||||
len = client->bufferLength - client->bufferOffset;
|
|
||||||
|
|
||||||
/*qemudDebug ("qemudClientRead: len = %d", len);*/
|
/*qemudDebug ("qemudClientRead: len = %d", len);*/
|
||||||
|
|
||||||
if (!client->tls) {
|
if (!client->tlssession) {
|
||||||
if ((ret = read (client->fd, data, len)) <= 0) {
|
if ((ret = read (client->fd, data, len)) <= 0) {
|
||||||
if (ret == 0 || errno != EAGAIN) {
|
if (ret == 0 || errno != EAGAIN) {
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
@ -1177,8 +1173,7 @@ static int qemudClientRead(struct qemud_server *server,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = gnutls_record_recv (client->session, data, len);
|
ret = gnutls_record_recv (client->tlssession, data, len);
|
||||||
client->direction = gnutls_record_get_direction (client->session);
|
|
||||||
if (qemudRegisterClientEvent (server, client, 1) < 0)
|
if (qemudRegisterClientEvent (server, client, 1) < 0)
|
||||||
qemudDispatchClientFailure (server, client);
|
qemudDispatchClientFailure (server, client);
|
||||||
else if (ret <= 0) {
|
else if (ret <= 0) {
|
||||||
@ -1193,10 +1188,80 @@ static int qemudClientRead(struct qemud_server *server,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qemudClientReadPlain(struct qemud_server *server,
|
||||||
|
struct qemud_client *client) {
|
||||||
|
int ret;
|
||||||
|
ret = qemudClientReadBuf(server, client,
|
||||||
|
client->buffer + client->bufferOffset,
|
||||||
|
client->bufferLength - client->bufferOffset);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
client->bufferOffset += ret;
|
client->bufferOffset += ret;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAVE_SASL
|
||||||
|
static int qemudClientReadSASL(struct qemud_server *server,
|
||||||
|
struct qemud_client *client) {
|
||||||
|
int got, want;
|
||||||
|
|
||||||
|
/* We're doing a SSF data read, so now its times to ensure
|
||||||
|
* future writes are under SSF too.
|
||||||
|
*
|
||||||
|
* cf remoteSASLCheckSSF in remote.c
|
||||||
|
*/
|
||||||
|
client->saslSSF |= QEMUD_SASL_SSF_WRITE;
|
||||||
|
|
||||||
|
/* Need to read some more data off the wire */
|
||||||
|
if (client->saslDecoded == NULL) {
|
||||||
|
char encoded[8192];
|
||||||
|
int encodedLen = sizeof(encoded);
|
||||||
|
encodedLen = qemudClientReadBuf(server, client, encoded, encodedLen);
|
||||||
|
|
||||||
|
if (encodedLen < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
sasl_decode(client->saslconn, encoded, encodedLen,
|
||||||
|
&client->saslDecoded, &client->saslDecodedLength);
|
||||||
|
|
||||||
|
client->saslDecodedOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Some buffered decoded data to return now */
|
||||||
|
got = client->saslDecodedLength - client->saslDecodedOffset;
|
||||||
|
want = client->bufferLength - client->bufferOffset;
|
||||||
|
|
||||||
|
if (want > got)
|
||||||
|
want = got;
|
||||||
|
|
||||||
|
memcpy(client->buffer + client->bufferOffset,
|
||||||
|
client->saslDecoded + client->saslDecodedOffset, want);
|
||||||
|
client->saslDecodedOffset += want;
|
||||||
|
client->bufferOffset += want;
|
||||||
|
|
||||||
|
if (client->saslDecodedOffset == client->saslDecodedLength) {
|
||||||
|
client->saslDecoded = NULL;
|
||||||
|
client->saslDecodedOffset = client->saslDecodedLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int qemudClientRead(struct qemud_server *server,
|
||||||
|
struct qemud_client *client) {
|
||||||
|
#if HAVE_SASL
|
||||||
|
if (client->saslSSF & QEMUD_SASL_SSF_READ)
|
||||||
|
return qemudClientReadSASL(server, client);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
return qemudClientReadPlain(server, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_client *client) {
|
static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_client *client) {
|
||||||
|
|
||||||
/*qemudDebug ("qemudDispatchClientRead: mode = %d", client->mode);*/
|
/*qemudDebug ("qemudDispatchClientRead: mode = %d", client->mode);*/
|
||||||
@ -1239,7 +1304,6 @@ static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_cl
|
|||||||
client->mode = QEMUD_MODE_RX_PAYLOAD;
|
client->mode = QEMUD_MODE_RX_PAYLOAD;
|
||||||
client->bufferLength = len - REMOTE_MESSAGE_HEADER_XDR_LEN;
|
client->bufferLength = len - REMOTE_MESSAGE_HEADER_XDR_LEN;
|
||||||
client->bufferOffset = 0;
|
client->bufferOffset = 0;
|
||||||
if (client->tls) client->direction = QEMUD_TLS_DIRECTION_READ;
|
|
||||||
|
|
||||||
if (qemudRegisterClientEvent(server, client, 1) < 0) {
|
if (qemudRegisterClientEvent(server, client, 1) < 0) {
|
||||||
qemudDispatchClientFailure(server, client);
|
qemudDispatchClientFailure(server, client);
|
||||||
@ -1267,7 +1331,7 @@ static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_cl
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Continue the handshake. */
|
/* Continue the handshake. */
|
||||||
ret = gnutls_handshake (client->session);
|
ret = gnutls_handshake (client->tlssession);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
/* Finished. Next step is to check the certificate. */
|
/* Finished. Next step is to check the certificate. */
|
||||||
if (remoteCheckAccess (client) == -1)
|
if (remoteCheckAccess (client) == -1)
|
||||||
@ -1279,7 +1343,6 @@ static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_cl
|
|||||||
gnutls_strerror (ret));
|
gnutls_strerror (ret));
|
||||||
qemudDispatchClientFailure (server, client);
|
qemudDispatchClientFailure (server, client);
|
||||||
} else {
|
} else {
|
||||||
client->direction = gnutls_record_get_direction (client->session);
|
|
||||||
if (qemudRegisterClientEvent (server ,client, 1) < 0)
|
if (qemudRegisterClientEvent (server ,client, 1) < 0)
|
||||||
qemudDispatchClientFailure (server, client);
|
qemudDispatchClientFailure (server, client);
|
||||||
}
|
}
|
||||||
@ -1294,15 +1357,11 @@ static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_cl
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int qemudClientWrite(struct qemud_server *server,
|
static int qemudClientWriteBuf(struct qemud_server *server,
|
||||||
struct qemud_client *client) {
|
struct qemud_client *client,
|
||||||
int ret, len;
|
const char *data, int len) {
|
||||||
char *data;
|
int ret;
|
||||||
|
if (!client->tlssession) {
|
||||||
data = client->buffer + client->bufferOffset;
|
|
||||||
len = client->bufferLength - client->bufferOffset;
|
|
||||||
|
|
||||||
if (!client->tls) {
|
|
||||||
if ((ret = write(client->fd, data, len)) == -1) {
|
if ((ret = write(client->fd, data, len)) == -1) {
|
||||||
if (errno != EAGAIN) {
|
if (errno != EAGAIN) {
|
||||||
qemudLog (QEMUD_ERR, "write: %s", strerror (errno));
|
qemudLog (QEMUD_ERR, "write: %s", strerror (errno));
|
||||||
@ -1311,8 +1370,7 @@ static int qemudClientWrite(struct qemud_server *server,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = gnutls_record_send (client->session, data, len);
|
ret = gnutls_record_send (client->tlssession, data, len);
|
||||||
client->direction = gnutls_record_get_direction (client->session);
|
|
||||||
if (qemudRegisterClientEvent (server, client, 1) < 0)
|
if (qemudRegisterClientEvent (server, client, 1) < 0)
|
||||||
qemudDispatchClientFailure (server, client);
|
qemudDispatchClientFailure (server, client);
|
||||||
else if (ret < 0) {
|
else if (ret < 0) {
|
||||||
@ -1324,12 +1382,72 @@ static int qemudClientWrite(struct qemud_server *server,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int qemudClientWritePlain(struct qemud_server *server,
|
||||||
|
struct qemud_client *client) {
|
||||||
|
int ret = qemudClientWriteBuf(server, client,
|
||||||
|
client->buffer + client->bufferOffset,
|
||||||
|
client->bufferLength - client->bufferOffset);
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
client->bufferOffset += ret;
|
client->bufferOffset += ret;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if HAVE_SASL
|
||||||
|
static int qemudClientWriteSASL(struct qemud_server *server,
|
||||||
|
struct qemud_client *client) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Not got any pending encoded data, so we need to encode raw stuff */
|
||||||
|
if (client->saslEncoded == NULL) {
|
||||||
|
int err;
|
||||||
|
err = sasl_encode(client->saslconn,
|
||||||
|
client->buffer + client->bufferOffset,
|
||||||
|
client->bufferLength - client->bufferOffset,
|
||||||
|
&client->saslEncoded,
|
||||||
|
&client->saslEncodedLength);
|
||||||
|
|
||||||
|
client->saslEncodedOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send some of the encoded stuff out on the wire */
|
||||||
|
ret = qemudClientWriteBuf(server, client,
|
||||||
|
client->saslEncoded + client->saslEncodedOffset,
|
||||||
|
client->saslEncodedLength - client->saslEncodedOffset);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Note how much we sent */
|
||||||
|
client->saslEncodedOffset += ret;
|
||||||
|
|
||||||
|
/* Sent all encoded, so update raw buffer to indicate completion */
|
||||||
|
if (client->saslEncodedOffset == client->saslEncodedLength) {
|
||||||
|
client->saslEncoded = NULL;
|
||||||
|
client->saslEncodedOffset = client->saslEncodedLength = 0;
|
||||||
|
client->bufferOffset = client->bufferLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int qemudClientWrite(struct qemud_server *server,
|
||||||
|
struct qemud_client *client) {
|
||||||
|
#if HAVE_SASL
|
||||||
|
if (client->saslSSF & QEMUD_SASL_SSF_WRITE)
|
||||||
|
return qemudClientWriteSASL(server, client);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
return qemudClientWritePlain(server, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void qemudDispatchClientWrite(struct qemud_server *server, struct qemud_client *client) {
|
static void qemudDispatchClientWrite(struct qemud_server *server, struct qemud_client *client) {
|
||||||
switch (client->mode) {
|
switch (client->mode) {
|
||||||
case QEMUD_MODE_TX_PACKET: {
|
case QEMUD_MODE_TX_PACKET: {
|
||||||
@ -1341,7 +1459,6 @@ static void qemudDispatchClientWrite(struct qemud_server *server, struct qemud_c
|
|||||||
client->mode = QEMUD_MODE_RX_HEADER;
|
client->mode = QEMUD_MODE_RX_HEADER;
|
||||||
client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN;
|
client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN;
|
||||||
client->bufferOffset = 0;
|
client->bufferOffset = 0;
|
||||||
if (client->tls) client->direction = QEMUD_TLS_DIRECTION_READ;
|
|
||||||
|
|
||||||
if (qemudRegisterClientEvent (server, client, 1) < 0)
|
if (qemudRegisterClientEvent (server, client, 1) < 0)
|
||||||
qemudDispatchClientFailure (server, client);
|
qemudDispatchClientFailure (server, client);
|
||||||
@ -1354,7 +1471,7 @@ static void qemudDispatchClientWrite(struct qemud_server *server, struct qemud_c
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Continue the handshake. */
|
/* Continue the handshake. */
|
||||||
ret = gnutls_handshake (client->session);
|
ret = gnutls_handshake (client->tlssession);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
/* Finished. Next step is to check the certificate. */
|
/* Finished. Next step is to check the certificate. */
|
||||||
if (remoteCheckAccess (client) == -1)
|
if (remoteCheckAccess (client) == -1)
|
||||||
@ -1366,7 +1483,6 @@ static void qemudDispatchClientWrite(struct qemud_server *server, struct qemud_c
|
|||||||
gnutls_strerror (ret));
|
gnutls_strerror (ret));
|
||||||
qemudDispatchClientFailure (server, client);
|
qemudDispatchClientFailure (server, client);
|
||||||
} else {
|
} else {
|
||||||
client->direction = gnutls_record_get_direction (client->session);
|
|
||||||
if (qemudRegisterClientEvent (server, client, 1))
|
if (qemudRegisterClientEvent (server, client, 1))
|
||||||
qemudDispatchClientFailure (server, client);
|
qemudDispatchClientFailure (server, client);
|
||||||
}
|
}
|
||||||
@ -1406,25 +1522,37 @@ static void qemudDispatchClientEvent(int fd, int events, void *opaque) {
|
|||||||
static int qemudRegisterClientEvent(struct qemud_server *server,
|
static int qemudRegisterClientEvent(struct qemud_server *server,
|
||||||
struct qemud_client *client,
|
struct qemud_client *client,
|
||||||
int removeFirst) {
|
int removeFirst) {
|
||||||
|
int mode;
|
||||||
|
switch (client->mode) {
|
||||||
|
case QEMUD_MODE_TLS_HANDSHAKE:
|
||||||
|
if (gnutls_record_get_direction (client->tlssession) == 0)
|
||||||
|
mode = POLLIN;
|
||||||
|
else
|
||||||
|
mode = POLLOUT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QEMUD_MODE_RX_HEADER:
|
||||||
|
case QEMUD_MODE_RX_PAYLOAD:
|
||||||
|
mode = POLLIN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QEMUD_MODE_TX_PACKET:
|
||||||
|
mode = POLLOUT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (removeFirst)
|
if (removeFirst)
|
||||||
if (virEventRemoveHandleImpl(client->fd) < 0)
|
if (virEventRemoveHandleImpl(client->fd) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (client->tls) {
|
if (virEventAddHandleImpl(client->fd,
|
||||||
if (virEventAddHandleImpl(client->fd,
|
mode | POLLERR | POLLHUP,
|
||||||
(client->direction ?
|
qemudDispatchClientEvent,
|
||||||
POLLOUT : POLLIN) | POLLERR | POLLHUP,
|
server) < 0)
|
||||||
qemudDispatchClientEvent,
|
|
||||||
server) < 0)
|
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
|
||||||
if (virEventAddHandleImpl(client->fd,
|
|
||||||
(client->mode == QEMUD_MODE_TX_PACKET ?
|
|
||||||
POLLOUT : POLLIN) | POLLERR | POLLHUP,
|
|
||||||
qemudDispatchClientEvent,
|
|
||||||
server) < 0)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
106
qemud/remote.c
106
qemud/remote.c
@ -284,7 +284,6 @@ remoteDispatchClientRequest (struct qemud_server *server ATTRIBUTE_UNUSED,
|
|||||||
client->mode = QEMUD_MODE_TX_PACKET;
|
client->mode = QEMUD_MODE_TX_PACKET;
|
||||||
client->bufferLength = len;
|
client->bufferLength = len;
|
||||||
client->bufferOffset = 0;
|
client->bufferOffset = 0;
|
||||||
if (client->tls) client->direction = QEMUD_TLS_DIRECTION_WRITE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* An error occurred during the dispatching process itself (ie. not
|
/* An error occurred during the dispatching process itself (ie. not
|
||||||
@ -369,7 +368,6 @@ remoteDispatchSendError (struct qemud_client *client,
|
|||||||
client->mode = QEMUD_MODE_TX_PACKET;
|
client->mode = QEMUD_MODE_TX_PACKET;
|
||||||
client->bufferLength = len;
|
client->bufferLength = len;
|
||||||
client->bufferOffset = 0;
|
client->bufferOffset = 0;
|
||||||
if (client->tls) client->direction = QEMUD_TLS_DIRECTION_WRITE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -2042,6 +2040,7 @@ remoteDispatchAuthSaslInit (struct qemud_client *client,
|
|||||||
remote_auth_sasl_init_ret *ret)
|
remote_auth_sasl_init_ret *ret)
|
||||||
{
|
{
|
||||||
const char *mechlist = NULL;
|
const char *mechlist = NULL;
|
||||||
|
sasl_security_properties_t secprops;
|
||||||
int err;
|
int err;
|
||||||
struct sockaddr_storage sa;
|
struct sockaddr_storage sa;
|
||||||
socklen_t salen;
|
socklen_t salen;
|
||||||
@ -2097,6 +2096,60 @@ remoteDispatchAuthSaslInit (struct qemud_client *client,
|
|||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Inform SASL that we've got an external SSF layer from TLS */
|
||||||
|
if (client->type == QEMUD_SOCK_TYPE_TLS) {
|
||||||
|
gnutls_cipher_algorithm_t cipher;
|
||||||
|
sasl_ssf_t ssf;
|
||||||
|
|
||||||
|
cipher = gnutls_cipher_get(client->tlssession);
|
||||||
|
if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
|
||||||
|
qemudLog(QEMUD_ERR, "cannot TLS get cipher size");
|
||||||
|
remoteDispatchFailAuth(client, req);
|
||||||
|
sasl_dispose(&client->saslconn);
|
||||||
|
client->saslconn = NULL;
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
ssf *= 8; /* tls key size is bytes, sasl wants bits */
|
||||||
|
|
||||||
|
err = sasl_setprop(client->saslconn, SASL_SSF_EXTERNAL, &ssf);
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
qemudLog(QEMUD_ERR, "cannot set SASL external SSF %d (%s)",
|
||||||
|
err, sasl_errstring(err, NULL, NULL));
|
||||||
|
remoteDispatchFailAuth(client, req);
|
||||||
|
sasl_dispose(&client->saslconn);
|
||||||
|
client->saslconn = NULL;
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memset (&secprops, 0, sizeof secprops);
|
||||||
|
if (client->type == QEMUD_SOCK_TYPE_TLS ||
|
||||||
|
client->type == QEMUD_SOCK_TYPE_UNIX) {
|
||||||
|
/* If we've got TLS or UNIX domain sock, we don't care about SSF */
|
||||||
|
secprops.min_ssf = 0;
|
||||||
|
secprops.max_ssf = 0;
|
||||||
|
secprops.maxbufsize = 8192;
|
||||||
|
secprops.security_flags = 0;
|
||||||
|
} else {
|
||||||
|
/* Plain TCP, better get an SSF layer */
|
||||||
|
secprops.min_ssf = 56; /* Good enough to require kerberos */
|
||||||
|
secprops.max_ssf = 100000; /* Arbitrary big number */
|
||||||
|
secprops.maxbufsize = 8192;
|
||||||
|
/* Forbid any anonymous or trivially crackable auth */
|
||||||
|
secprops.security_flags =
|
||||||
|
SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sasl_setprop(client->saslconn, SASL_SEC_PROPS, &secprops);
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
qemudLog(QEMUD_ERR, "cannot set SASL security props %d (%s)",
|
||||||
|
err, sasl_errstring(err, NULL, NULL));
|
||||||
|
remoteDispatchFailAuth(client, req);
|
||||||
|
sasl_dispose(&client->saslconn);
|
||||||
|
client->saslconn = NULL;
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
err = sasl_listmech(client->saslconn,
|
err = sasl_listmech(client->saslconn,
|
||||||
NULL, /* Don't need to set user */
|
NULL, /* Don't need to set user */
|
||||||
"", /* Prefix */
|
"", /* Prefix */
|
||||||
@ -2127,6 +2180,49 @@ remoteDispatchAuthSaslInit (struct qemud_client *client,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* We asked for an SSF layer, so sanity check that we actaully
|
||||||
|
* got what we asked for */
|
||||||
|
static int
|
||||||
|
remoteSASLCheckSSF (struct qemud_client *client,
|
||||||
|
remote_message_header *req) {
|
||||||
|
const void *val;
|
||||||
|
int err, ssf;
|
||||||
|
|
||||||
|
if (client->type == QEMUD_SOCK_TYPE_TLS ||
|
||||||
|
client->type == QEMUD_SOCK_TYPE_UNIX)
|
||||||
|
return 0; /* TLS or UNIX domain sockets trivially OK */
|
||||||
|
|
||||||
|
err = sasl_getprop(client->saslconn, SASL_SSF, &val);
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
qemudLog(QEMUD_ERR, "cannot query SASL ssf on connection %d (%s)",
|
||||||
|
err, sasl_errstring(err, NULL, NULL));
|
||||||
|
remoteDispatchFailAuth(client, req);
|
||||||
|
sasl_dispose(&client->saslconn);
|
||||||
|
client->saslconn = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ssf = *(const int *)val;
|
||||||
|
REMOTE_DEBUG("negotiated an SSF of %d", ssf);
|
||||||
|
if (ssf < 56) { /* 56 is good for Kerberos */
|
||||||
|
qemudLog(QEMUD_ERR, "negotiated SSF %d was not strong enough", ssf);
|
||||||
|
remoteDispatchFailAuth(client, req);
|
||||||
|
sasl_dispose(&client->saslconn);
|
||||||
|
client->saslconn = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only setup for read initially, because we're about to send an RPC
|
||||||
|
* reply which must be in plain text. When the next incoming RPC
|
||||||
|
* arrives, we'll switch on writes too
|
||||||
|
*
|
||||||
|
* cf qemudClientReadSASL in qemud.c
|
||||||
|
*/
|
||||||
|
client->saslSSF = QEMUD_SASL_SSF_READ;
|
||||||
|
|
||||||
|
/* We have a SSF !*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This starts the SASL authentication negotiation.
|
* This starts the SASL authentication negotiation.
|
||||||
*/
|
*/
|
||||||
@ -2192,6 +2288,9 @@ remoteDispatchAuthSaslStart (struct qemud_client *client,
|
|||||||
if (err == SASL_CONTINUE) {
|
if (err == SASL_CONTINUE) {
|
||||||
ret->complete = 0;
|
ret->complete = 0;
|
||||||
} else {
|
} else {
|
||||||
|
if (remoteSASLCheckSSF(client, req) < 0)
|
||||||
|
return -2;
|
||||||
|
|
||||||
REMOTE_DEBUG("Authentication successful %d", client->fd);
|
REMOTE_DEBUG("Authentication successful %d", client->fd);
|
||||||
ret->complete = 1;
|
ret->complete = 1;
|
||||||
client->auth = REMOTE_AUTH_NONE;
|
client->auth = REMOTE_AUTH_NONE;
|
||||||
@ -2263,6 +2362,9 @@ remoteDispatchAuthSaslStep (struct qemud_client *client,
|
|||||||
if (err == SASL_CONTINUE) {
|
if (err == SASL_CONTINUE) {
|
||||||
ret->complete = 0;
|
ret->complete = 0;
|
||||||
} else {
|
} else {
|
||||||
|
if (remoteSASLCheckSSF(client, req) < 0)
|
||||||
|
return -2;
|
||||||
|
|
||||||
REMOTE_DEBUG("Authentication successful %d", client->fd);
|
REMOTE_DEBUG("Authentication successful %d", client->fd);
|
||||||
ret->complete = 1;
|
ret->complete = 1;
|
||||||
client->auth = REMOTE_AUTH_NONE;
|
client->auth = REMOTE_AUTH_NONE;
|
||||||
|
@ -79,6 +79,9 @@ struct private_data {
|
|||||||
FILE *debugLog; /* Debug remote protocol */
|
FILE *debugLog; /* Debug remote protocol */
|
||||||
#if HAVE_SASL
|
#if HAVE_SASL
|
||||||
sasl_conn_t *saslconn; /* SASL context */
|
sasl_conn_t *saslconn; /* SASL context */
|
||||||
|
const char *saslDecoded;
|
||||||
|
unsigned int saslDecodedLength;
|
||||||
|
unsigned int saslDecodedOffset;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2907,15 +2910,14 @@ static char *addrToString(struct sockaddr_storage *sa, socklen_t salen)
|
|||||||
|
|
||||||
/* Perform the SASL authentication process
|
/* Perform the SASL authentication process
|
||||||
*
|
*
|
||||||
* XXX negotiate a session encryption layer for non-TLS sockets
|
|
||||||
* XXX fetch credentials from a libvirt client app callback
|
* XXX fetch credentials from a libvirt client app callback
|
||||||
* XXX max packet size spec
|
|
||||||
* XXX better mechanism negotiation ? Ask client app ?
|
* XXX better mechanism negotiation ? Ask client app ?
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open)
|
remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open)
|
||||||
{
|
{
|
||||||
sasl_conn_t *saslconn = NULL;
|
sasl_conn_t *saslconn = NULL;
|
||||||
|
sasl_security_properties_t secprops;
|
||||||
remote_auth_sasl_init_ret iret;
|
remote_auth_sasl_init_ret iret;
|
||||||
remote_auth_sasl_start_args sargs;
|
remote_auth_sasl_start_args sargs;
|
||||||
remote_auth_sasl_start_ret sret;
|
remote_auth_sasl_start_ret sret;
|
||||||
@ -2929,6 +2931,8 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open)
|
|||||||
struct sockaddr_storage sa;
|
struct sockaddr_storage sa;
|
||||||
socklen_t salen;
|
socklen_t salen;
|
||||||
char *localAddr, *remoteAddr;
|
char *localAddr, *remoteAddr;
|
||||||
|
const void *val;
|
||||||
|
sasl_ssf_t ssf;
|
||||||
|
|
||||||
remoteDebug(priv, "Client initialize SASL authentication");
|
remoteDebug(priv, "Client initialize SASL authentication");
|
||||||
/* Sets up the SASL library as a whole */
|
/* Sets up the SASL library as a whole */
|
||||||
@ -2987,6 +2991,51 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Initialize some connection props we care about */
|
||||||
|
if (priv->uses_tls) {
|
||||||
|
gnutls_cipher_algorithm_t cipher;
|
||||||
|
|
||||||
|
cipher = gnutls_cipher_get(priv->session);
|
||||||
|
if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
|
||||||
|
__virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE,
|
||||||
|
VIR_ERR_INTERNAL_ERROR, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0,
|
||||||
|
"invalid cipher size for TLS session");
|
||||||
|
sasl_dispose(&saslconn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ssf *= 8; /* key size is bytes, sasl wants bits */
|
||||||
|
|
||||||
|
remoteDebug(priv, "Setting external SSF %d", ssf);
|
||||||
|
err = sasl_setprop(saslconn, SASL_SSF_EXTERNAL, &ssf);
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
__virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE,
|
||||||
|
VIR_ERR_INTERNAL_ERROR, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0,
|
||||||
|
"cannot set external SSF %d (%s)",
|
||||||
|
err, sasl_errstring(err, NULL, NULL));
|
||||||
|
sasl_dispose(&saslconn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memset (&secprops, 0, sizeof secprops);
|
||||||
|
/* If we've got TLS, we don't care about SSF */
|
||||||
|
secprops.min_ssf = priv->uses_tls ? 0 : 56; /* Equiv to DES supported by all Kerberos */
|
||||||
|
secprops.max_ssf = priv->uses_tls ? 0 : 100000; /* Very strong ! AES == 256 */
|
||||||
|
secprops.maxbufsize = 100000;
|
||||||
|
/* If we're not TLS, then forbid any anonymous or trivially crackable auth */
|
||||||
|
secprops.security_flags = priv->uses_tls ? 0 :
|
||||||
|
SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
|
||||||
|
|
||||||
|
err = sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops);
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
__virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE,
|
||||||
|
VIR_ERR_INTERNAL_ERROR, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0,
|
||||||
|
"cannot set security props %d (%s)",
|
||||||
|
err, sasl_errstring(err, NULL, NULL));
|
||||||
|
sasl_dispose(&saslconn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* First call is to inquire about supported mechanisms in the server */
|
/* First call is to inquire about supported mechanisms in the server */
|
||||||
memset (&iret, 0, sizeof iret);
|
memset (&iret, 0, sizeof iret);
|
||||||
if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_INIT,
|
if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_INIT,
|
||||||
@ -3103,9 +3152,30 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check for suitable SSF if non-TLS */
|
||||||
|
if (!priv->uses_tls) {
|
||||||
|
err = sasl_getprop(saslconn, SASL_SSF, &val);
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
__virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE,
|
||||||
|
VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0,
|
||||||
|
"cannot query SASL ssf on connection %d (%s)",
|
||||||
|
err, sasl_errstring(err, NULL, NULL));
|
||||||
|
sasl_dispose(&saslconn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ssf = *(const int *)val;
|
||||||
|
remoteDebug(priv, "SASL SSF value %d", ssf);
|
||||||
|
if (ssf < 56) { /* 56 == DES level, good for Kerberos */
|
||||||
|
__virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE,
|
||||||
|
VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0,
|
||||||
|
"negotiation SSF %d was not strong enough", ssf);
|
||||||
|
sasl_dispose(&saslconn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
remoteDebug(priv, "SASL authentication complete");
|
remoteDebug(priv, "SASL authentication complete");
|
||||||
/* XXX keep this around for wire encoding */
|
priv->saslconn = saslconn;
|
||||||
sasl_dispose(&saslconn);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* HAVE_SASL */
|
#endif /* HAVE_SASL */
|
||||||
@ -3306,11 +3376,11 @@ call (virConnectPtr conn, struct private_data *priv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
really_write (virConnectPtr conn, struct private_data *priv,
|
really_write_buf (virConnectPtr conn, struct private_data *priv,
|
||||||
int in_open /* if we are in virConnectOpen */,
|
int in_open /* if we are in virConnectOpen */,
|
||||||
char *bytes, int len)
|
const char *bytes, int len)
|
||||||
{
|
{
|
||||||
char *p;
|
const char *p;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
p = bytes;
|
p = bytes;
|
||||||
@ -3347,56 +3417,157 @@ really_write (virConnectPtr conn, struct private_data *priv,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
really_write_plain (virConnectPtr conn, struct private_data *priv,
|
||||||
|
int in_open /* if we are in virConnectOpen */,
|
||||||
|
char *bytes, int len)
|
||||||
|
{
|
||||||
|
return really_write_buf(conn, priv, in_open, bytes, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAVE_SASL
|
||||||
|
static int
|
||||||
|
really_write_sasl (virConnectPtr conn, struct private_data *priv,
|
||||||
|
int in_open /* if we are in virConnectOpen */,
|
||||||
|
char *bytes, int len)
|
||||||
|
{
|
||||||
|
const char *output;
|
||||||
|
unsigned int outputlen;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = sasl_encode(priv->saslconn, bytes, len, &output, &outputlen);
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return really_write_buf(conn, priv, in_open, output, outputlen);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int
|
||||||
|
really_write (virConnectPtr conn, struct private_data *priv,
|
||||||
|
int in_open /* if we are in virConnectOpen */,
|
||||||
|
char *bytes, int len)
|
||||||
|
{
|
||||||
|
#if HAVE_SASL
|
||||||
|
if (priv->saslconn)
|
||||||
|
return really_write_sasl(conn, priv, in_open, bytes, len);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
return really_write_plain(conn, priv, in_open, bytes, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
really_read_buf (virConnectPtr conn, struct private_data *priv,
|
||||||
|
int in_open /* if we are in virConnectOpen */,
|
||||||
|
char *bytes, int len)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (priv->uses_tls) {
|
||||||
|
tlsreread:
|
||||||
|
err = gnutls_record_recv (priv->session, bytes, len);
|
||||||
|
if (err < 0) {
|
||||||
|
if (err == GNUTLS_E_INTERRUPTED)
|
||||||
|
goto tlsreread;
|
||||||
|
error (in_open ? NULL : conn,
|
||||||
|
VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (err == 0) {
|
||||||
|
error (in_open ? NULL : conn,
|
||||||
|
VIR_ERR_RPC, "socket closed unexpectedly");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
} else {
|
||||||
|
reread:
|
||||||
|
err = read (priv->sock, bytes, len);
|
||||||
|
if (err == -1) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
goto reread;
|
||||||
|
error (in_open ? NULL : conn,
|
||||||
|
VIR_ERR_SYSTEM_ERROR, strerror (errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (err == 0) {
|
||||||
|
error (in_open ? NULL : conn,
|
||||||
|
VIR_ERR_RPC, "socket closed unexpectedly");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
really_read_plain (virConnectPtr conn, struct private_data *priv,
|
||||||
|
int in_open /* if we are in virConnectOpen */,
|
||||||
|
char *bytes, int len)
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
int ret = really_read_buf (conn, priv, in_open, bytes, len);
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
len -= ret;
|
||||||
|
bytes += ret;
|
||||||
|
} while (len > 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAVE_SASL
|
||||||
|
static int
|
||||||
|
really_read_sasl (virConnectPtr conn, struct private_data *priv,
|
||||||
|
int in_open /* if we are in virConnectOpen */,
|
||||||
|
char *bytes, int len)
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
int want, got;
|
||||||
|
if (priv->saslDecoded == NULL) {
|
||||||
|
char encoded[8192];
|
||||||
|
int encodedLen = sizeof(encoded);
|
||||||
|
int err, ret;
|
||||||
|
ret = really_read_buf (conn, priv, in_open, encoded, encodedLen);
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
err = sasl_decode(priv->saslconn, encoded, ret,
|
||||||
|
&priv->saslDecoded, &priv->saslDecodedLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
got = priv->saslDecodedLength - priv->saslDecodedOffset;
|
||||||
|
want = len;
|
||||||
|
if (want > got)
|
||||||
|
want = got;
|
||||||
|
|
||||||
|
memcpy(bytes, priv->saslDecoded + priv->saslDecodedOffset, want);
|
||||||
|
priv->saslDecodedOffset += want;
|
||||||
|
if (priv->saslDecodedOffset == priv->saslDecodedLength) {
|
||||||
|
priv->saslDecoded = NULL;
|
||||||
|
priv->saslDecodedOffset = priv->saslDecodedLength = 0;
|
||||||
|
}
|
||||||
|
bytes += want;
|
||||||
|
len -= want;
|
||||||
|
} while (len > 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
really_read (virConnectPtr conn, struct private_data *priv,
|
really_read (virConnectPtr conn, struct private_data *priv,
|
||||||
int in_open /* if we are in virConnectOpen */,
|
int in_open /* if we are in virConnectOpen */,
|
||||||
char *bytes, int len)
|
char *bytes, int len)
|
||||||
{
|
{
|
||||||
char *p;
|
#if HAVE_SASL
|
||||||
int err;
|
if (priv->saslconn)
|
||||||
|
return really_read_sasl (conn, priv, in_open, bytes, len);
|
||||||
p = bytes;
|
else
|
||||||
if (priv->uses_tls) {
|
#endif
|
||||||
do {
|
return really_read_plain (conn, priv, in_open, bytes, len);
|
||||||
err = gnutls_record_recv (priv->session, p, len);
|
|
||||||
if (err < 0) {
|
|
||||||
if (err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN)
|
|
||||||
continue;
|
|
||||||
error (in_open ? NULL : conn,
|
|
||||||
VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (err == 0) {
|
|
||||||
error (in_open ? NULL : conn,
|
|
||||||
VIR_ERR_RPC, "socket closed unexpectedly");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len -= err;
|
|
||||||
p += err;
|
|
||||||
}
|
|
||||||
while (len > 0);
|
|
||||||
} else {
|
|
||||||
do {
|
|
||||||
err = read (priv->sock, p, len);
|
|
||||||
if (err == -1) {
|
|
||||||
if (errno == EINTR || errno == EAGAIN)
|
|
||||||
continue;
|
|
||||||
error (in_open ? NULL : conn,
|
|
||||||
VIR_ERR_SYSTEM_ERROR, strerror (errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (err == 0) {
|
|
||||||
error (in_open ? NULL : conn,
|
|
||||||
VIR_ERR_RPC, "socket closed unexpectedly");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len -= err;
|
|
||||||
p += err;
|
|
||||||
}
|
|
||||||
while (len > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For errors internal to this library. */
|
/* For errors internal to this library. */
|
||||||
|
Loading…
Reference in New Issue
Block a user