/* Negotiate an SSL connection. */ int ne__negotiate_ssl(ne_session *sess) { ne_ssl_context *const ctx = sess->ssl_context; ne_ssl_certificate *chain; gnutls_session sock; NE_DEBUG(NE_DBG_SSL, "Negotiating SSL connection.\n"); /* Pass through the hostname if SNI is enabled. */ ctx->hostname = sess->flags[NE_SESSFLAG_TLS_SNI] ? sess->server.hostname : NULL; if (ne_sock_connect_ssl(sess->socket, ctx, sess)) { if (sess->ssl_cc_requested) { ne_set_error(sess, _("SSL handshake failed, " "client certificate was requested: %s"), ne_sock_error(sess->socket)); } else { ne_set_error(sess, _("SSL handshake failed: %s"), ne_sock_error(sess->socket)); } return NE_ERROR; } sock = ne__sock_sslsock(sess->socket); chain = make_peers_chain(sock, ctx->cred); if (chain == NULL) { ne_set_error(sess, _("Server did not send certificate chain")); return NE_ERROR; } if (sess->server_cert && ne_ssl_cert_cmp(sess->server_cert, chain) == 0) { /* Same cert as last time; presume OK. This is not optimal as * make_peers_chain() has already gone through and done the * expensive DER parsing stuff for the whole chain by now. */ ne_ssl_cert_free(chain); return NE_OK; } if (check_certificate(sess, sock, chain)) { ne_ssl_cert_free(chain); return NE_ERROR; } sess->server_cert = chain; return NE_OK; }
/* Negotiate an SSL connection. */ int ne__negotiate_ssl(ne_session *sess) { ne_ssl_context *const ctx = sess->ssl_context; ne_ssl_certificate *chain; gnutls_session sock; NE_DEBUG(NE_DBG_SSL, "Negotiating SSL connection.\n"); if (ne_sock_connect_ssl(sess->socket, ctx, sess)) { ne_set_error(sess, _("SSL negotiation failed: %s"), ne_sock_error(sess->socket)); return NE_ERROR; } sock = ne__sock_sslsock(sess->socket); chain = make_peers_chain(sock); if (chain == NULL) { ne_set_error(sess, _("Server did not send certificate chain")); return NE_ERROR; } if (sess->server_cert && ne_ssl_cert_cmp(sess->server_cert, chain) == 0) { /* Same cert as last time; presume OK. This is not optimal as * make_peers_chain() has already gone through and done the * expensive DER parsing stuff for the whole chain by now. */ ne_ssl_cert_free(chain); return NE_OK; } if (check_certificate(sess, sock, chain)) { ne_ssl_cert_free(chain); return NE_ERROR; } sess->server_cert = chain; return NE_OK; }
/* Return the certificate chain sent by the peer, or NULL on error. */ static ne_ssl_certificate *make_peers_chain(gnutls_session sock, gnutls_certificate_credentials crd) { ne_ssl_certificate *current = NULL, *top = NULL; const gnutls_datum *certs; unsigned int n, count; ne_ssl_certificate *cert; certs = gnutls_certificate_get_peers(sock, &count); if (!certs) { return NULL; } NE_DEBUG(NE_DBG_SSL, "ssl: Got %u certs in peer chain.\n", count); for (n = 0; n < count; n++) { gnutls_x509_crt x5; if (gnutls_x509_crt_init(&x5) || gnutls_x509_crt_import(x5, &certs[n], GNUTLS_X509_FMT_DER)) { if (top) { ne_ssl_cert_free(top); } return NULL; } cert = populate_cert(ne_calloc(sizeof *cert), x5); if (top == NULL) { current = top = cert; } else { current->issuer = cert; current = cert; } } #ifdef HAVE_GNUTLS_CERTIFICATE_GET_X509_CAS /* GnuTLS only returns the peers which were *sent* by the server * in the Certificate list during the handshake. Fill in the * complete chain manually against the certs we trust: */ if (current->issuer == NULL) { gnutls_x509_crt issuer; gnutls_x509_crt *ca_list; unsigned int num_cas; gnutls_certificate_get_x509_cas(crd, &ca_list, &num_cas); do { /* Look up the issuer. */ issuer = find_issuer(ca_list, num_cas, current->subject); if (issuer) { issuer = x509_crt_copy(issuer); cert = populate_cert(ne_calloc(sizeof *cert), issuer); /* Check that the issuer does not match the current * cert. */ if (ne_ssl_cert_cmp(current, cert)) { current = current->issuer = cert; } else { ne_ssl_cert_free(cert); issuer = NULL; } } } while (issuer); } #endif return top; }