On Fri, Nov 02, 2007 at 01:09:26AM +0000, Daniel P. Berrange wrote:
With the TLS socket, all data is encrypted on the wire. The TCP
socket though
is still clear text. Fortunately some SASL authentication mechanism can
also supply encryption capabilities. This is called SSF in SASL terminology.
This patch mandates the use of an SSF capable SASL authentiction mechanism
on the TCP socket. This in effect restricts you to a choise between GSSAPI
and DIGEST-MD5 as your SASL auth methods (the latter is a user/password
based scheme). We also disallow anonymous & plaintext auth methods. If you
really want to run the TCP socket in clear text & with anonymous auth, simply
turn off SASL altogether. Since TLS already provides encryptiuon, if you run
SASL over the TLS socket, we don't place any restrictions on the choice of
SASL auth mechanism.
On the server side I have removed the 'direction' field of the client object.
This was only used on the TLS socket & was intended to track whether the
handshake process was waiting to receive or send. Rather than try to set
this in various places throughout the daemon code, we simply query the
neccessary direction at the one point where we register a FD event handle
with poll(). This makes the code clearer to follow & reduces the chance of
accidentally messing up the state.
The send & receive functions previously would call read vs gnutls_record_recv
and write vs gnutls_record_send depending on the type of socket. If there is
a SASL SSF layer enabled, we have to first pass the outgoing data through
the sasl_encode() API, and pass incoming data through sasl_decode(). So the
send/recive APIs have changed a little, to deal with this codec need and thus
there is also some more state being tracked per connection - we may have to
cache the output for sasl_decode for future calls depending on how much
data we need in short term.
NB, the SSF layer lets you choose a strength factor from 0 -> a large number
and the docs all talk about
* 0 = no protection
* 1 = integrity protection only
* 40 = 40-bit DES or 40-bit RC2/RC4
* 56 = DES
* 112 = triple-DES
* 128 = 128-bit RC2/RC4/BLOWFISH
* 256 = baseline AES
This is incredibly misleading. The GSSAPI mechanism in SASL will never report
a strength of anything other than 56. Even if it is using triple-DES. The
true strength of the GSSAPI/Kerberos impl is impossible to figure out from
the SASL apis - you have to trust that the underlying Kerberos impl was
compiled with suitably strong ciphers - all modern OS use strong ciphers in
Kebreros so I don't think this is a huge issue. If you are truely paranoid
though, you always have the option of using TLS (which gives at least 128
bit ciphers, often 256 bit), and then just using Kerberos for auth and ignore
the SASL SSF layer. This is an site admin config choice.
qemud/internal.h | 17 ++-
qemud/qemud.c | 207 +++++++++++++++++++++++++++++-------
qemud/remote.c | 97 ++++++++++++++++-
src/remote_internal.c | 283 ++++++++++++++++++++++++++++++++++++++++----------
4 files changed, 503 insertions(+), 101 deletions(-)
diff -r 1052e0b6c97b qemud/internal.h
--- a/qemud/internal.h Thu Nov 01 16:28:22 2007 -0400
+++ b/qemud/internal.h Thu Nov 01 16:28:26 2007 -0400
@@ -67,10 +67,11 @@ enum qemud_mode {
QEMUD_MODE_TLS_HANDSHAKE,
};
-/* These have to remain compatible with gnutls_record_get_direction. */
-enum qemud_tls_direction {
- QEMUD_TLS_DIRECTION_READ = 0,
- QEMUD_TLS_DIRECTION_WRITE = 1,
+/* Whether we're passing reads & writes through a sasl SSF */
+enum qemud_sasl_ssf {
+ QEMUD_SASL_SSF_NONE = 0,
+ QEMUD_SASL_SSF_READ = 1,
+ QEMUD_SASL_SSF_WRITE = 2,
};
/* Stores the per-client connection state */
@@ -87,10 +88,16 @@ struct qemud_client {
/* If set, TLS is required on this socket. */
int tls;
gnutls_session_t session;
- enum qemud_tls_direction direction;
int auth;
#if HAVE_SASL
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
unsigned int incomingSerial;
diff -r 1052e0b6c97b qemud/qemud.c
--- a/qemud/qemud.c Thu Nov 01 16:28:22 2007 -0400
+++ b/qemud/qemud.c Thu Nov 01 16:28:26 2007 -0400
@@ -701,7 +701,9 @@ static struct qemud_server *qemudInitial
struct qemud_socket *sock;
char sockname[PATH_MAX];
char roSockname[PATH_MAX];
+#if HAVE_SASL
int err;
+#endif
if (!(server = calloc(1, sizeof(struct qemud_server)))) {
qemudLog(QEMUD_ERR, "Failed to allocate struct qemud_server");
@@ -1029,7 +1031,6 @@ remoteCheckAccess (struct qemud_client *
client->bufferOffset = 0;
client->buffer[0] = '\1';
client->mode = QEMUD_MODE_TX_PACKET;
- client->direction = QEMUD_TLS_DIRECTION_WRITE;
return 0;
}
@@ -1095,7 +1096,6 @@ static int qemudDispatchServer(struct qe
/* Most likely. */
client->mode = QEMUD_MODE_TLS_HANDSHAKE;
client->bufferLength = -1;
- client->direction = gnutls_record_get_direction (client->session);
if (qemudRegisterClientEvent (server, client, 0) < 0)
goto cleanup;
@@ -1153,13 +1153,10 @@ static void qemudDispatchClientFailure(s
-static int qemudClientRead(struct qemud_server *server,
- struct qemud_client *client) {
- int ret, len;
- char *data;
-
- data = client->buffer + client->bufferOffset;
- len = client->bufferLength - client->bufferOffset;
+static int qemudClientReadBuf(struct qemud_server *server,
+ struct qemud_client *client,
+ char *data, unsigned len) {
+ int ret;
/*qemudDebug ("qemudClientRead: len = %d", len);*/
@@ -1174,7 +1171,6 @@ static int qemudClientRead(struct qemud_
}
} else {
ret = gnutls_record_recv (client->session, data, len);
- client->direction = gnutls_record_get_direction (client->session);
if (qemudRegisterClientEvent (server, client, 1) < 0)
qemudDispatchClientFailure (server, client);
else if (ret <= 0) {
@@ -1189,9 +1185,79 @@ static int qemudClientRead(struct qemud_
}
}
+ 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;
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) {
@@ -1235,7 +1301,6 @@ static void qemudDispatchClientRead(stru
client->mode = QEMUD_MODE_RX_PAYLOAD;
client->bufferLength = len - REMOTE_MESSAGE_HEADER_XDR_LEN;
client->bufferOffset = 0;
- if (client->tls) client->direction = QEMUD_TLS_DIRECTION_READ;
if (qemudRegisterClientEvent(server, client, 1) < 0) {
qemudDispatchClientFailure(server, client);
@@ -1275,7 +1340,6 @@ static void qemudDispatchClientRead(stru
gnutls_strerror (ret));
qemudDispatchClientFailure (server, client);
} else {
- client->direction = gnutls_record_get_direction (client->session);
if (qemudRegisterClientEvent (server ,client, 1) < 0)
qemudDispatchClientFailure (server, client);
}
@@ -1290,14 +1354,10 @@ static void qemudDispatchClientRead(stru
}
-static int qemudClientWrite(struct qemud_server *server,
- struct qemud_client *client) {
- int ret, len;
- char *data;
-
- data = client->buffer + client->bufferOffset;
- len = client->bufferLength - client->bufferOffset;
-
+static int qemudClientWriteBuf(struct qemud_server *server,
+ struct qemud_client *client,
+ const char *data, int len) {
+ int ret;
if (!client->tls) {
if ((ret = write(client->fd, data, len)) == -1) {
if (errno != EAGAIN) {
@@ -1308,7 +1368,6 @@ static int qemudClientWrite(struct qemud
}
} else {
ret = gnutls_record_send (client->session, data, len);
- client->direction = gnutls_record_get_direction (client->session);
if (qemudRegisterClientEvent (server, client, 1) < 0)
qemudDispatchClientFailure (server, client);
else if (ret < 0) {
@@ -1320,9 +1379,69 @@ static int qemudClientWrite(struct qemud
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;
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);
}
@@ -1337,7 +1456,6 @@ static void qemudDispatchClientWrite(str
client->mode = QEMUD_MODE_RX_HEADER;
client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN;
client->bufferOffset = 0;
- if (client->tls) client->direction = QEMUD_TLS_DIRECTION_READ;
if (qemudRegisterClientEvent (server, client, 1) < 0)
qemudDispatchClientFailure (server, client);
@@ -1362,7 +1480,6 @@ static void qemudDispatchClientWrite(str
gnutls_strerror (ret));
qemudDispatchClientFailure (server, client);
} else {
- client->direction = gnutls_record_get_direction (client->session);
if (qemudRegisterClientEvent (server, client, 1))
qemudDispatchClientFailure (server, client);
}
@@ -1402,25 +1519,37 @@ static int qemudRegisterClientEvent(stru
static int qemudRegisterClientEvent(struct qemud_server *server,
struct qemud_client *client,
int removeFirst) {
+ int mode;
+ switch (client->mode) {
+ case QEMUD_MODE_TLS_HANDSHAKE:
+ if (gnutls_record_get_direction (client->session) == 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 (virEventRemoveHandleImpl(client->fd) < 0)
return -1;
- if (client->tls) {
- if (virEventAddHandleImpl(client->fd,
- (client->direction ?
- POLLOUT : POLLIN) | POLLERR | POLLHUP,
- qemudDispatchClientEvent,
- server) < 0)
- return -1;
- } else {
- if (virEventAddHandleImpl(client->fd,
- (client->mode == QEMUD_MODE_TX_PACKET ?
- POLLOUT : POLLIN) | POLLERR | POLLHUP,
- qemudDispatchClientEvent,
- server) < 0)
- return -1;
- }
+ if (virEventAddHandleImpl(client->fd,
+ mode | POLLERR | POLLHUP,
+ qemudDispatchClientEvent,
+ server) < 0)
+ return -1;
return 0;
}
diff -r 1052e0b6c97b qemud/remote.c
--- a/qemud/remote.c Thu Nov 01 16:28:22 2007 -0400
+++ b/qemud/remote.c Thu Nov 01 16:32:06 2007 -0400
@@ -283,7 +283,6 @@ remoteDispatchClientRequest (struct qemu
client->mode = QEMUD_MODE_TX_PACKET;
client->bufferLength = len;
client->bufferOffset = 0;
- if (client->tls) client->direction = QEMUD_TLS_DIRECTION_WRITE;
}
/* An error occurred during the dispatching process itself (ie. not
@@ -368,7 +367,6 @@ remoteDispatchSendError (struct qemud_cl
client->mode = QEMUD_MODE_TX_PACKET;
client->bufferLength = len;
client->bufferOffset = 0;
- if (client->tls) client->direction = QEMUD_TLS_DIRECTION_WRITE;
}
static void
@@ -2034,6 +2032,7 @@ remoteDispatchAuthSaslInit (struct qemud
remote_auth_sasl_init_ret *ret)
{
const char *mechlist = NULL;
+ sasl_security_properties_t secprops;
int err;
struct sockaddr_storage sa;
socklen_t salen;
@@ -2089,6 +2088,51 @@ remoteDispatchAuthSaslInit (struct qemud
return -2;
}
+ /* Inform SASL that we've got an external SSF layer from TLS */
+ if (client->tls) {
+ gnutls_cipher_algorithm_t cipher;
+ sasl_ssf_t ssf;
+
+ cipher = gnutls_cipher_get(client->session);
+ 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 we've got TLS, we don't care about SSF */
+ secprops.min_ssf = client->tls ? 0 : 56; /* Good enough to require kerberos */
+ secprops.max_ssf = client->tls ? 0 : 100000; /* Arbitrary big number */
+ secprops.maxbufsize = 8192;
+ /* If we're not TLS, then forbid any anonymous or trivially crackable auth */
+ secprops.security_flags = client->tls ? 0 :
+ 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,
NULL, /* Don't need to set user */
"", /* Prefix */
@@ -2117,6 +2161,45 @@ remoteDispatchAuthSaslInit (struct qemud
return 0;
}
+
+/* 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;
+
+ 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.
@@ -2183,6 +2266,11 @@ remoteDispatchAuthSaslStart (struct qemu
if (err == SASL_CONTINUE) {
ret->complete = 0;
} else {
+ /* If non-TLS, check a suitable SSF was negotiated */
+ if (!client->tls &&
+ remoteSASLCheckSSF(client, req) < 0)
+ return -2;
+
REMOTE_DEBUG("Authentication successful %d", client->fd);
ret->complete = 1;
client->auth = REMOTE_AUTH_NONE;
@@ -2254,6 +2342,11 @@ remoteDispatchAuthSaslStep (struct qemud
if (err == SASL_CONTINUE) {
ret->complete = 0;
} else {
+ /* If non-TLS, check a suitable SSF was negotiated */
+ if (!client->tls &&
+ remoteSASLCheckSSF(client, req) < 0)
+ return -2;
+
REMOTE_DEBUG("Authentication successful %d", client->fd);
ret->complete = 1;
client->auth = REMOTE_AUTH_NONE;
diff -r 1052e0b6c97b src/remote_internal.c
--- a/src/remote_internal.c Thu Nov 01 16:28:22 2007 -0400
+++ b/src/remote_internal.c Thu Nov 01 16:32:48 2007 -0400
@@ -78,6 +78,9 @@ struct private_data {
FILE *debugLog; /* Debug remote protocol */
#if HAVE_SASL
sasl_conn_t *saslconn; /* SASL context */
+ const char *saslDecoded;
+ unsigned int saslDecodedLength;
+ unsigned int saslDecodedOffset;
#endif
};
@@ -145,6 +148,7 @@ remoteStartup(void)
return 0;
}
+#if HAVE_SASL
static void
remoteDebug(struct private_data *priv, const char *msg,...)
{
@@ -157,6 +161,7 @@ remoteDebug(struct private_data *priv, c
va_end(args);
fprintf(priv->debugLog, "\n");
}
+#endif
/**
@@ -2904,15 +2909,14 @@ static char *addrToString(struct sockadd
/* 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 max packet size spec
* XXX better mechanism negotiation ? Ask client app ?
*/
static int
remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open)
{
sasl_conn_t *saslconn;
+ sasl_security_properties_t secprops;
remote_auth_sasl_init_ret iret;
remote_auth_sasl_start_args sargs;
remote_auth_sasl_start_ret sret;
@@ -2926,6 +2930,8 @@ remoteAuthSASL (virConnectPtr conn, stru
struct sockaddr_storage sa;
socklen_t salen;
char *localAddr, *remoteAddr;
+ const void *val;
+ sasl_ssf_t ssf;
remoteDebug(priv, "Client initialize SASL authentication");
/* Sets up the SASL library as a whole */
@@ -2965,7 +2971,7 @@ remoteAuthSASL (virConnectPtr conn, stru
free(localAddr);
return -1;
}
- printf("'%s' '%s' '%s'\n", priv->hostname,
localAddr, remoteAddr);
+
/* Setup a handle for being a client */
err = sasl_client_new("libvirt",
priv->hostname,
@@ -2981,6 +2987,51 @@ remoteAuthSASL (virConnectPtr conn, stru
VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0,
"Failed to create SASL client context: %d (%s)",
err, sasl_errstring(err, NULL, NULL));
+ 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;
}
@@ -3100,9 +3151,30 @@ remoteAuthSASL (virConnectPtr conn, stru
}
}
+ /* 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");
- /* XXX keep this around for wire encoding */
- sasl_dispose(&saslconn);
+ priv->saslconn = saslconn;
return 0;
}
#endif
@@ -3294,11 +3366,11 @@ call (virConnectPtr conn, struct private
}
static int
-really_write (virConnectPtr conn, struct private_data *priv,
- int in_open /* if we are in virConnectOpen */,
- char *bytes, int len)
-{
- char *p;
+really_write_buf (virConnectPtr conn, struct private_data *priv,
+ int in_open /* if we are in virConnectOpen */,
+ const char *bytes, int len)
+{
+ const char *p;
int err;
p = bytes;
@@ -3336,55 +3408,156 @@ really_write (virConnectPtr conn, struct
}
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
really_read (virConnectPtr conn, struct private_data *priv,
int in_open /* if we are in virConnectOpen */,
char *bytes, int len)
{
- char *p;
- int err;
-
- p = bytes;
- if (priv->uses_tls) {
- do {
- 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;
+#if HAVE_SASL
+ if (priv->saslconn)
+ return really_read_sasl (conn, priv, in_open, bytes, len);
+ else
+#endif
+ return really_read_plain (conn, priv, in_open, bytes, len);
}
/* For errors internal to this library. */
--
|=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=|
|=- Perl modules:
http://search.cpan.org/~danberr/ -=|
|=- Projects:
http://freshmeat.net/~danielpb/ -=|
|=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|