diff --git a/src/rpc/virnetsaslcontext.c b/src/rpc/virnetsaslcontext.c index 6b2a883e1e28177e8875dc79d3f5e8378c4f0642..71796b9edbb50eceb17c1aa09d8ac1d142e07c55 100644 --- a/src/rpc/virnetsaslcontext.c +++ b/src/rpc/virnetsaslcontext.c @@ -27,6 +27,7 @@ #include "virterror_internal.h" #include "memory.h" +#include "threads.h" #include "logging.h" #define VIR_FROM_THIS VIR_FROM_RPC @@ -36,11 +37,13 @@ struct _virNetSASLContext { + virMutex lock; const char *const*usernameWhitelist; int refs; }; struct _virNetSASLSession { + virMutex lock; sasl_conn_t *conn; int refs; size_t maxbufsize; @@ -65,6 +68,13 @@ virNetSASLContextPtr virNetSASLContextNewClient(void) return NULL; } + if (virMutexInit(&ctxt->lock) < 0) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to initialized mutex")); + VIR_FREE(ctxt); + return NULL; + } + ctxt->refs = 1; return ctxt; @@ -88,6 +98,13 @@ virNetSASLContextPtr virNetSASLContextNewServer(const char *const*usernameWhitel return NULL; } + if (virMutexInit(&ctxt->lock) < 0) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to initialized mutex")); + VIR_FREE(ctxt); + return NULL; + } + ctxt->usernameWhitelist = usernameWhitelist; ctxt->refs = 1; @@ -98,21 +115,28 @@ int virNetSASLContextCheckIdentity(virNetSASLContextPtr ctxt, const char *identity) { const char *const*wildcards; + int ret = -1; + + virMutexLock(&ctxt->lock); /* If the list is not set, allow any DN. */ wildcards = ctxt->usernameWhitelist; - if (!wildcards) - return 1; /* No ACL, allow all */ + if (!wildcards) { + ret = 1; /* No ACL, allow all */ + goto cleanup; + } while (*wildcards) { - int ret = fnmatch (*wildcards, identity, 0); - if (ret == 0) /* Succesful match */ - return 1; + int rv = fnmatch (*wildcards, identity, 0); + if (rv == 0) { + ret = 1; + goto cleanup; /* Succesful match */ + } if (ret != FNM_NOMATCH) { virNetError(VIR_ERR_INTERNAL_ERROR, _("Malformed TLS whitelist regular expression '%s'"), *wildcards); - return -1; + goto cleanup; } wildcards++; @@ -124,13 +148,19 @@ int virNetSASLContextCheckIdentity(virNetSASLContextPtr ctxt, /* This is the most common error: make it informative. */ virNetError(VIR_ERR_SYSTEM_ERROR, "%s", _("Client's username is not on the list of allowed clients")); - return 0; + ret = 0; + +cleanup: + virMutexUnlock(&ctxt->lock); + return ret; } void virNetSASLContextRef(virNetSASLContextPtr ctxt) { + virMutexLock(&ctxt->lock); ctxt->refs++; + virMutexUnlock(&ctxt->lock); } void virNetSASLContextFree(virNetSASLContextPtr ctxt) @@ -138,10 +168,15 @@ void virNetSASLContextFree(virNetSASLContextPtr ctxt) if (!ctxt) return; + virMutexLock(&ctxt->lock); ctxt->refs--; - if (ctxt->refs > 0) + if (ctxt->refs > 0) { + virMutexUnlock(&ctxt->lock); return; + } + virMutexUnlock(&ctxt->lock); + virMutexDestroy(&ctxt->lock); VIR_FREE(ctxt); } @@ -160,6 +195,13 @@ virNetSASLSessionPtr virNetSASLSessionNewClient(virNetSASLContextPtr ctxt ATTRIB goto cleanup; } + if (virMutexInit(&sasl->lock) < 0) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to initialized mutex")); + VIR_FREE(sasl); + return NULL; + } + sasl->refs = 1; /* Arbitrary size for amount of data we can encode in a single block */ sasl->maxbufsize = 1 << 16; @@ -198,6 +240,13 @@ virNetSASLSessionPtr virNetSASLSessionNewServer(virNetSASLContextPtr ctxt ATTRIB goto cleanup; } + if (virMutexInit(&sasl->lock) < 0) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to initialized mutex")); + VIR_FREE(sasl); + return NULL; + } + sasl->refs = 1; /* Arbitrary size for amount of data we can encode in a single block */ sasl->maxbufsize = 1 << 16; @@ -226,43 +275,56 @@ cleanup: void virNetSASLSessionRef(virNetSASLSessionPtr sasl) { + virMutexLock(&sasl->lock); sasl->refs++; + virMutexUnlock(&sasl->lock); } int virNetSASLSessionExtKeySize(virNetSASLSessionPtr sasl, int ssf) { int err; + int ret = -1; + virMutexLock(&sasl->lock); err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf); if (err != SASL_OK) { virNetError(VIR_ERR_INTERNAL_ERROR, _("cannot set external SSF %d (%s)"), err, sasl_errstring(err, NULL, NULL)); - return -1; + goto cleanup; } - return 0; + + ret = 0; + +cleanup: + virMutexLock(&sasl->lock); + return ret; } const char *virNetSASLSessionGetIdentity(virNetSASLSessionPtr sasl) { - const void *val; + const void *val = NULL; int err; + virMutexLock(&sasl->lock); err = sasl_getprop(sasl->conn, SASL_USERNAME, &val); if (err != SASL_OK) { virNetError(VIR_ERR_AUTH_FAILED, _("cannot query SASL username on connection %d (%s)"), err, sasl_errstring(err, NULL, NULL)); - return NULL; + val = NULL; + goto cleanup; } if (val == NULL) { virNetError(VIR_ERR_AUTH_FAILED, _("no client username was found")); - return NULL; + goto cleanup; } VIR_DEBUG("SASL client username %s", (const char *)val); +cleanup: + virMutexUnlock(&sasl->lock); return (const char*)val; } @@ -272,14 +334,20 @@ int virNetSASLSessionGetKeySize(virNetSASLSessionPtr sasl) int err; int ssf; const void *val; + + virMutexLock(&sasl->lock); err = sasl_getprop(sasl->conn, SASL_SSF, &val); if (err != SASL_OK) { virNetError(VIR_ERR_AUTH_FAILED, _("cannot query SASL ssf on connection %d (%s)"), err, sasl_errstring(err, NULL, NULL)); - return -1; + ssf = -1; + goto cleanup; } ssf = *(const int *)val; + +cleanup: + virMutexUnlock(&sasl->lock); return ssf; } @@ -290,10 +358,12 @@ int virNetSASLSessionSecProps(virNetSASLSessionPtr sasl, { sasl_security_properties_t secprops; int err; + int ret = -1; VIR_DEBUG("minSSF=%d maxSSF=%d allowAnonymous=%d maxbufsize=%zu", minSSF, maxSSF, allowAnonymous, sasl->maxbufsize); + virMutexLock(&sasl->lock); memset(&secprops, 0, sizeof secprops); secprops.min_ssf = minSSF; @@ -307,10 +377,14 @@ int virNetSASLSessionSecProps(virNetSASLSessionPtr sasl, virNetError(VIR_ERR_INTERNAL_ERROR, _("cannot set security props %d (%s)"), err, sasl_errstring(err, NULL, NULL)); - return -1; + goto cleanup; } - return 0; + ret = 0; + +cleanup: + virMutexUnlock(&sasl->lock); + return ret; } @@ -336,9 +410,10 @@ static int virNetSASLSessionUpdateBufSize(virNetSASLSessionPtr sasl) char *virNetSASLSessionListMechanisms(virNetSASLSessionPtr sasl) { const char *mechlist; - char *ret; + char *ret = NULL; int err; + virMutexLock(&sasl->lock); err = sasl_listmech(sasl->conn, NULL, /* Don't need to set user */ "", /* Prefix */ @@ -351,12 +426,15 @@ char *virNetSASLSessionListMechanisms(virNetSASLSessionPtr sasl) virNetError(VIR_ERR_INTERNAL_ERROR, _("cannot list SASL mechanisms %d (%s)"), err, sasl_errdetail(sasl->conn)); - return NULL; + goto cleanup; } if (!(ret = strdup(mechlist))) { virReportOOMError(); - return NULL; + goto cleanup; } + +cleanup: + virMutexUnlock(&sasl->lock); return ret; } @@ -369,35 +447,44 @@ int virNetSASLSessionClientStart(virNetSASLSessionPtr sasl, const char **mech) { unsigned outlen = 0; + int err; + int ret = -1; VIR_DEBUG("sasl=%p mechlist=%s prompt_need=%p clientout=%p clientoutlen=%p mech=%p", sasl, mechlist, prompt_need, clientout, clientoutlen, mech); - int err = sasl_client_start(sasl->conn, - mechlist, - prompt_need, - clientout, - &outlen, - mech); + virMutexLock(&sasl->lock); + err = sasl_client_start(sasl->conn, + mechlist, + prompt_need, + clientout, + &outlen, + mech); *clientoutlen = outlen; switch (err) { case SASL_OK: if (virNetSASLSessionUpdateBufSize(sasl) < 0) - return -1; - return VIR_NET_SASL_COMPLETE; + goto cleanup; + ret = VIR_NET_SASL_COMPLETE; + break; case SASL_CONTINUE: - return VIR_NET_SASL_CONTINUE; + ret = VIR_NET_SASL_CONTINUE; + break; case SASL_INTERACT: - return VIR_NET_SASL_INTERACT; - + ret = VIR_NET_SASL_INTERACT; + break; default: virNetError(VIR_ERR_AUTH_FAILED, _("Failed to start SASL negotiation: %d (%s)"), err, sasl_errdetail(sasl->conn)); - return -1; + break; } + +cleanup: + virMutexUnlock(&sasl->lock); + return ret; } @@ -410,34 +497,43 @@ int virNetSASLSessionClientStep(virNetSASLSessionPtr sasl, { unsigned inlen = serverinlen; unsigned outlen = 0; + int err; + int ret = -1; VIR_DEBUG("sasl=%p serverin=%s serverinlen=%zu prompt_need=%p clientout=%p clientoutlen=%p", sasl, serverin, serverinlen, prompt_need, clientout, clientoutlen); - int err = sasl_client_step(sasl->conn, - serverin, - inlen, - prompt_need, - clientout, - &outlen); + virMutexLock(&sasl->lock); + err = sasl_client_step(sasl->conn, + serverin, + inlen, + prompt_need, + clientout, + &outlen); *clientoutlen = outlen; switch (err) { case SASL_OK: if (virNetSASLSessionUpdateBufSize(sasl) < 0) - return -1; - return VIR_NET_SASL_COMPLETE; + goto cleanup; + ret = VIR_NET_SASL_COMPLETE; + break; case SASL_CONTINUE: - return VIR_NET_SASL_CONTINUE; + ret = VIR_NET_SASL_CONTINUE; + break; case SASL_INTERACT: - return VIR_NET_SASL_INTERACT; - + ret = VIR_NET_SASL_INTERACT; + break; default: virNetError(VIR_ERR_AUTH_FAILED, _("Failed to step SASL negotiation: %d (%s)"), err, sasl_errdetail(sasl->conn)); - return -1; + break; } + +cleanup: + virMutexUnlock(&sasl->lock); + return ret; } int virNetSASLSessionServerStart(virNetSASLSessionPtr sasl, @@ -449,31 +545,41 @@ int virNetSASLSessionServerStart(virNetSASLSessionPtr sasl, { unsigned inlen = clientinlen; unsigned outlen = 0; - int err = sasl_server_start(sasl->conn, - mechname, - clientin, - inlen, - serverout, - &outlen); + int err; + int ret = -1; + + virMutexLock(&sasl->lock); + err = sasl_server_start(sasl->conn, + mechname, + clientin, + inlen, + serverout, + &outlen); *serveroutlen = outlen; switch (err) { case SASL_OK: if (virNetSASLSessionUpdateBufSize(sasl) < 0) - return -1; - return VIR_NET_SASL_COMPLETE; + goto cleanup; + ret = VIR_NET_SASL_COMPLETE; + break; case SASL_CONTINUE: - return VIR_NET_SASL_CONTINUE; + ret = VIR_NET_SASL_CONTINUE; + break; case SASL_INTERACT: - return VIR_NET_SASL_INTERACT; - + ret = VIR_NET_SASL_INTERACT; + break; default: virNetError(VIR_ERR_AUTH_FAILED, _("Failed to start SASL negotiation: %d (%s)"), err, sasl_errdetail(sasl->conn)); - return -1; + break; } + +cleanup: + virMutexUnlock(&sasl->lock); + return ret; } @@ -485,36 +591,49 @@ int virNetSASLSessionServerStep(virNetSASLSessionPtr sasl, { unsigned inlen = clientinlen; unsigned outlen = 0; + int err; + int ret = -1; - int err = sasl_server_step(sasl->conn, - clientin, - inlen, - serverout, - &outlen); + virMutexLock(&sasl->lock); + err = sasl_server_step(sasl->conn, + clientin, + inlen, + serverout, + &outlen); *serveroutlen = outlen; switch (err) { case SASL_OK: if (virNetSASLSessionUpdateBufSize(sasl) < 0) - return -1; - return VIR_NET_SASL_COMPLETE; + goto cleanup; + ret = VIR_NET_SASL_COMPLETE; + break; case SASL_CONTINUE: - return VIR_NET_SASL_CONTINUE; + ret = VIR_NET_SASL_CONTINUE; + break; case SASL_INTERACT: - return VIR_NET_SASL_INTERACT; - + ret = VIR_NET_SASL_INTERACT; + break; default: virNetError(VIR_ERR_AUTH_FAILED, _("Failed to start SASL negotiation: %d (%s)"), err, sasl_errdetail(sasl->conn)); - return -1; + break; } + +cleanup: + virMutexUnlock(&sasl->lock); + return ret; } size_t virNetSASLSessionGetMaxBufSize(virNetSASLSessionPtr sasl) { - return sasl->maxbufsize; + size_t ret; + virMutexLock(&sasl->lock); + ret = sasl->maxbufsize; + virMutexUnlock(&sasl->lock); + return ret; } ssize_t virNetSASLSessionEncode(virNetSASLSessionPtr sasl, @@ -526,12 +645,14 @@ ssize_t virNetSASLSessionEncode(virNetSASLSessionPtr sasl, unsigned inlen = inputLen; unsigned outlen = 0; int err; + ssize_t ret = -1; + virMutexLock(&sasl->lock); if (inputLen > sasl->maxbufsize) { virReportSystemError(EINVAL, _("SASL data length %zu too long, max %zu"), inputLen, sasl->maxbufsize); - return -1; + goto cleanup; } err = sasl_encode(sasl->conn, @@ -545,9 +666,13 @@ ssize_t virNetSASLSessionEncode(virNetSASLSessionPtr sasl, virNetError(VIR_ERR_INTERNAL_ERROR, _("failed to encode SASL data: %d (%s)"), err, sasl_errstring(err, NULL, NULL)); - return -1; + goto cleanup; } - return 0; + ret = 0; + +cleanup: + virMutexUnlock(&sasl->lock); + return ret; } ssize_t virNetSASLSessionDecode(virNetSASLSessionPtr sasl, @@ -559,12 +684,14 @@ ssize_t virNetSASLSessionDecode(virNetSASLSessionPtr sasl, unsigned inlen = inputLen; unsigned outlen = 0; int err; + ssize_t ret = -1; + virMutexLock(&sasl->lock); if (inputLen > sasl->maxbufsize) { virReportSystemError(EINVAL, _("SASL data length %zu too long, max %zu"), inputLen, sasl->maxbufsize); - return -1; + goto cleanup; } err = sasl_decode(sasl->conn, @@ -577,9 +704,13 @@ ssize_t virNetSASLSessionDecode(virNetSASLSessionPtr sasl, virNetError(VIR_ERR_INTERNAL_ERROR, _("failed to decode SASL data: %d (%s)"), err, sasl_errstring(err, NULL, NULL)); - return -1; + goto cleanup; } - return 0; + ret = 0; + +cleanup: + virMutexUnlock(&sasl->lock); + return ret; } void virNetSASLSessionFree(virNetSASLSessionPtr sasl) @@ -587,12 +718,17 @@ void virNetSASLSessionFree(virNetSASLSessionPtr sasl) if (!sasl) return; + virMutexLock(&sasl->lock); sasl->refs--; - if (sasl->refs > 0) + if (sasl->refs > 0) { + virMutexUnlock(&sasl->lock); return; + } if (sasl->conn) sasl_dispose(&sasl->conn); + virMutexUnlock(&sasl->lock); + virMutexDestroy(&sasl->lock); VIR_FREE(sasl); } diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c index bde4e7a75f431aaad5f799cda394c4487f802af2..db03669fad331b91b392f64f278d255626f19381 100644 --- a/src/rpc/virnettlscontext.c +++ b/src/rpc/virnettlscontext.c @@ -34,6 +34,7 @@ #include "virterror_internal.h" #include "util.h" #include "logging.h" +#include "threads.h" #include "configmake.h" #define DH_BITS 1024 @@ -52,6 +53,7 @@ __FUNCTION__, __LINE__, __VA_ARGS__) struct _virNetTLSContext { + virMutex lock; int refs; gnutls_certificate_credentials_t x509cred; @@ -63,6 +65,8 @@ struct _virNetTLSContext { }; struct _virNetTLSSession { + virMutex lock; + int refs; bool handshakeComplete; @@ -653,6 +657,13 @@ static virNetTLSContextPtr virNetTLSContextNew(const char *cacert, return NULL; } + if (virMutexInit(&ctxt->lock) < 0) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to initialized mutex")); + VIR_FREE(ctxt); + return NULL; + } + ctxt->refs = 1; /* Initialise GnuTLS. */ @@ -1053,18 +1064,29 @@ authfail: int virNetTLSContextCheckCertificate(virNetTLSContextPtr ctxt, virNetTLSSessionPtr sess) { + int ret = -1; + + virMutexLock(&ctxt->lock); + virMutexLock(&sess->lock); if (virNetTLSContextValidCertificate(ctxt, sess) < 0) { virErrorPtr err = virGetLastError(); VIR_WARN("Certificate check failed %s", err && err->message ? err->message : ""); if (ctxt->requireValidCert) { virNetError(VIR_ERR_AUTH_FAILED, "%s", _("Failed to verify peer's certificate")); - return -1; + goto cleanup; } virResetLastError(); VIR_INFO("Ignoring bad certificate at user request"); } - return 0; + + ret = 0; + +cleanup: + virMutexUnlock(&ctxt->lock); + virMutexUnlock(&sess->lock); + + return ret; } void virNetTLSContextFree(virNetTLSContextPtr ctxt) @@ -1072,12 +1094,17 @@ void virNetTLSContextFree(virNetTLSContextPtr ctxt) if (!ctxt) return; + virMutexLock(&ctxt->lock); ctxt->refs--; - if (ctxt->refs > 0) + if (ctxt->refs > 0) { + virMutexUnlock(&ctxt->lock); return; + } gnutls_dh_params_deinit(ctxt->dhParams); gnutls_certificate_free_credentials(ctxt->x509cred); + virMutexUnlock(&ctxt->lock); + virMutexDestroy(&ctxt->lock); VIR_FREE(ctxt); } @@ -1124,6 +1151,13 @@ virNetTLSSessionPtr virNetTLSSessionNew(virNetTLSContextPtr ctxt, return NULL; } + if (virMutexInit(&sess->lock) < 0) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to initialized mutex")); + VIR_FREE(ctxt); + return NULL; + } + sess->refs = 1; if (hostname && !(sess->hostname = strdup(hostname))) { @@ -1184,7 +1218,9 @@ error: void virNetTLSSessionRef(virNetTLSSessionPtr sess) { + virMutexLock(&sess->lock); sess->refs++; + virMutexUnlock(&sess->lock); } void virNetTLSSessionSetIOCallbacks(virNetTLSSessionPtr sess, @@ -1192,9 +1228,11 @@ void virNetTLSSessionSetIOCallbacks(virNetTLSSessionPtr sess, virNetTLSSessionReadFunc readFunc, void *opaque) { + virMutexLock(&sess->lock); sess->writeFunc = writeFunc; sess->readFunc = readFunc; sess->opaque = opaque; + virMutexUnlock(&sess->lock); } @@ -1202,10 +1240,12 @@ ssize_t virNetTLSSessionWrite(virNetTLSSessionPtr sess, const char *buf, size_t len) { ssize_t ret; + + virMutexLock(&sess->lock); ret = gnutls_record_send(sess->session, buf, len); if (ret >= 0) - return ret; + goto cleanup; switch (ret) { case GNUTLS_E_AGAIN: @@ -1222,7 +1262,11 @@ ssize_t virNetTLSSessionWrite(virNetTLSSessionPtr sess, break; } - return -1; + ret = -1; + +cleanup: + virMutexUnlock(&sess->lock); + return ret; } ssize_t virNetTLSSessionRead(virNetTLSSessionPtr sess, @@ -1230,10 +1274,11 @@ ssize_t virNetTLSSessionRead(virNetTLSSessionPtr sess, { ssize_t ret; + virMutexLock(&sess->lock); ret = gnutls_record_recv(sess->session, buf, len); if (ret >= 0) - return ret; + goto cleanup; switch (ret) { case GNUTLS_E_AGAIN: @@ -1247,21 +1292,29 @@ ssize_t virNetTLSSessionRead(virNetTLSSessionPtr sess, break; } - return -1; + ret = -1; + +cleanup: + virMutexUnlock(&sess->lock); + return ret; } int virNetTLSSessionHandshake(virNetTLSSessionPtr sess) { + int ret; VIR_DEBUG("sess=%p", sess); - int ret = gnutls_handshake(sess->session); + virMutexLock(&sess->lock); + ret = gnutls_handshake(sess->session); VIR_DEBUG("Ret=%d", ret); if (ret == 0) { sess->handshakeComplete = true; VIR_DEBUG("Handshake is complete"); - return 0; + goto cleanup; + } + if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { + ret = 1; + goto cleanup; } - if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) - return 1; #if 0 PROBE(CLIENT_TLS_FAIL, "fd=%d", @@ -1271,32 +1324,43 @@ int virNetTLSSessionHandshake(virNetTLSSessionPtr sess) virNetError(VIR_ERR_AUTH_FAILED, _("TLS handshake failed %s"), gnutls_strerror(ret)); - return -1; + ret = -1; + +cleanup: + virMutexUnlock(&sess->lock); + return ret; } virNetTLSSessionHandshakeStatus virNetTLSSessionGetHandshakeStatus(virNetTLSSessionPtr sess) { + virNetTLSSessionHandshakeStatus ret; + virMutexLock(&sess->lock); if (sess->handshakeComplete) - return VIR_NET_TLS_HANDSHAKE_COMPLETE; + ret = VIR_NET_TLS_HANDSHAKE_COMPLETE; else if (gnutls_record_get_direction(sess->session) == 0) - return VIR_NET_TLS_HANDSHAKE_RECVING; + ret = VIR_NET_TLS_HANDSHAKE_RECVING; else - return VIR_NET_TLS_HANDSHAKE_SENDING; + ret = VIR_NET_TLS_HANDSHAKE_SENDING; + virMutexUnlock(&sess->lock); + return ret; } int virNetTLSSessionGetKeySize(virNetTLSSessionPtr sess) { gnutls_cipher_algorithm_t cipher; int ssf; - + virMutexLock(&sess->lock); cipher = gnutls_cipher_get(sess->session); if (!(ssf = gnutls_cipher_get_key_size(cipher))) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid cipher size for TLS session")); - return -1; + ssf = -1; + goto cleanup; } +cleanup: + virMutexUnlock(&sess->lock); return ssf; } @@ -1306,11 +1370,16 @@ void virNetTLSSessionFree(virNetTLSSessionPtr sess) if (!sess) return; + virMutexLock(&sess->lock); sess->refs--; - if (sess->refs > 0) + if (sess->refs > 0) { + virMutexUnlock(&sess->lock); return; + } VIR_FREE(sess->hostname); gnutls_deinit(sess->session); + virMutexUnlock(&sess->lock); + virMutexDestroy(&sess->lock); VIR_FREE(sess); }