/* Provenance: curl/curl gtls_shutdown (fixed). * repo: curl/curl * commit: f18af4f874cecab82a9797e8c7541e0990c7a64c * parent: 7e92d12b4e6911f424678a133b19de670e183a59 * commit_url: https://github.com/curl/curl/commit/f18af4f874cecab82a9797e8c7541e0990c7a64c * cve: CVE-2022-27782 * license: curl * function: gtls_shutdown * relpath: lib/vtls/gtls.c * provenance: human * mechanism: other */ static int gtls_shutdown(struct Curl_easy *data, struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; int retval = 0; DEBUGASSERT(backend); #ifndef CURL_DISABLE_FTP /* This has only been tested on the proftpd server, and the mod_tls code sends a close notify alert without waiting for a close notify alert in response. Thus we wait for a close notify alert from the server, but we do not send one. Let's hope other servers do the same... */ if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE) gnutls_bye(backend->session, GNUTLS_SHUT_WR); #endif if(backend->session) { ssize_t result; bool done = FALSE; char buf[120]; while(!done) { int what = SOCKET_READABLE(conn->sock[sockindex], SSL_SHUTDOWN_TIMEOUT); if(what > 0) { /* Something to read, let's do it and hope that it is the close notify alert from the server */ result = gnutls_record_recv(backend->session, buf, sizeof(buf)); switch(result) { case 0: /* This is the expected response. There was no data but only the close notify alert */ done = TRUE; break; case GNUTLS_E_AGAIN: case GNUTLS_E_INTERRUPTED: infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED"); break; default: retval = -1; done = TRUE; break; } } else if(0 == what) { /* timeout */ failf(data, "SSL shutdown timeout"); done = TRUE; } else { /* anything that gets here is fatally bad */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); retval = -1; done = TRUE; } } gnutls_deinit(backend->session); } gnutls_certificate_free_credentials(backend->cred); #ifdef USE_GNUTLS_SRP if(SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP && SSL_SET_OPTION(primary.username) != NULL) gnutls_srp_free_client_credentials(backend->srp_client_cred); #endif backend->cred = NULL; backend->session = NULL; return retval; }