From 16cad09e1c534391c6aad5c8f552da6a7e6949fd Mon Sep 17 00:00:00 2001 From: Jacob Champion Date: Tue, 15 Jun 2021 15:59:39 -0700 Subject: [PATCH 3/3] nss: clean up leaked resources The NSPR file descriptor, a handful of certificate references, and session cache entries were preventing NSS from shutting down. --- src/interfaces/libpq/fe-secure-nss.c | 44 +++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/interfaces/libpq/fe-secure-nss.c b/src/interfaces/libpq/fe-secure-nss.c index 90f57e0d99..e9dacdc732 100644 --- a/src/interfaces/libpq/fe-secure-nss.c +++ b/src/interfaces/libpq/fe-secure-nss.c @@ -137,9 +137,32 @@ pgtls_init(PGconn *conn, bool do_ssl, bool do_crypto) void pgtls_close(PGconn *conn) { + /* All NSS references must be cleaned up before we close out the context. */ + if (conn->pr_fd) + { + PRStatus status; + + status = PR_Close(conn->pr_fd); + if (status != PR_SUCCESS) + { + pqInternalNotice(&conn->noticeHooks, + "unable to close NSPR fd: %s", + pg_SSLerrmessage(PR_GetError())); + } + + conn->pr_fd = NULL; + } + if (conn->nss_context) { SECStatus status; + + /* + * The session cache must be cleared, or we'll leak references. + * TODO: refcount maybe? This has a global effect. + */ + SSL_ClearSessionCache(); + status = NSS_ShutdownContext(conn->nss_context); if (status != SECSuccess) @@ -573,16 +596,16 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len) SECOidTag signature_tag; SECOidTag digest_alg; int digest_len; - PLArenaPool *arena; + PLArenaPool *arena = NULL; SECItem digest; - char *ret; + char *ret = NULL; SECStatus status; *len = 0; server_cert = SSL_PeerCertificate(conn->pr_fd); if (!server_cert) - return NULL; + goto cleanup; signature_tag = SECOID_GetAlgorithmTag(&server_cert->signature); if (!pg_find_signature_algorithm(signature_tag, &digest_alg, &digest_len)) @@ -590,7 +613,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len) printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not find digest for OID '%s'\n"), SECOID_FindOIDTagDescription(signature_tag)); - return NULL; + goto cleanup; } arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); @@ -605,14 +628,18 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len) printfPQExpBuffer(&conn->errorMessage, libpq_gettext("unable to generate peer certificate digest: %s"), pg_SSLerrmessage(PR_GetError())); - PORT_FreeArena(arena, PR_TRUE); - return NULL; + goto cleanup; } ret = pg_malloc(digest.len); memcpy(ret, digest.data, digest.len); *len = digest_len; - PORT_FreeArena(arena, PR_TRUE); + +cleanup: + if (arena) + PORT_FreeArena(arena, PR_TRUE); + if (server_cert) + CERT_DestroyCertificate(server_cert); return ret; } @@ -712,6 +739,8 @@ done: /* san_list will be freed by freeing the arena it was allocated in */ if (arena) PORT_FreeArena(arena, PR_TRUE); + if (server_cert) + CERT_DestroyCertificate(server_cert); PR_Free(server_hostname); if (status == SECSuccess) @@ -826,6 +855,7 @@ pg_cert_auth_handler(void *arg, PRFileDesc *fd, PRBool checksig, PRBool isServer pg_SSLerrmessage(PR_GetError())); } + CERT_DestroyCertificate(server_cert); return status; } -- 2.25.1