|
Packit |
aea12f |
/*
|
|
Packit |
aea12f |
* Copyright (C) 2017 Red Hat, Inc.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* Author: Nikos Mavrogiannopoulos
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* This file is part of GnuTLS.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* The GnuTLS is free software; you can redistribute it and/or
|
|
Packit |
aea12f |
* modify it under the terms of the GNU Lesser General Public License
|
|
Packit |
aea12f |
* as published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
aea12f |
* the License, or (at your option) any later version.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* This library is distributed in the hope that it will be useful, but
|
|
Packit |
aea12f |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
aea12f |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
aea12f |
* Lesser General Public License for more details.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* You should have received a copy of the GNU Lesser General Public License
|
|
Packit |
aea12f |
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
*/
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#include "gnutls_int.h"
|
|
Packit |
aea12f |
#include "errors.h"
|
|
Packit |
aea12f |
#include "handshake.h"
|
|
Packit |
aea12f |
#include "auth/cert.h"
|
|
Packit |
aea12f |
#include "ext/signature.h"
|
|
Packit |
aea12f |
#include "algorithms.h"
|
|
Packit |
aea12f |
#include "tls13-sig.h"
|
|
Packit |
aea12f |
#include "mbuffers.h"
|
|
Packit |
aea12f |
#include "tls13/certificate_verify.h"
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#define SRV_CTX "TLS 1.3, server CertificateVerify"
|
|
Packit |
aea12f |
static const gnutls_datum_t srv_ctx = {
|
|
Packit |
aea12f |
(void*)SRV_CTX, sizeof(SRV_CTX)-1
|
|
Packit |
aea12f |
};
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#define CLI_CTX "TLS 1.3, client CertificateVerify"
|
|
Packit |
aea12f |
static const gnutls_datum_t cli_ctx = {
|
|
Packit |
aea12f |
(void*)CLI_CTX, sizeof(CLI_CTX)-1
|
|
Packit |
aea12f |
};
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
int _gnutls13_recv_certificate_verify(gnutls_session_t session)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
gnutls_buffer_st buf;
|
|
Packit |
aea12f |
const gnutls_sign_entry_st *se;
|
|
Packit |
aea12f |
gnutls_datum_t sig_data;
|
|
Packit |
aea12f |
gnutls_certificate_credentials_t cred;
|
|
Packit |
aea12f |
unsigned vflags;
|
|
Packit |
aea12f |
gnutls_pcert_st peer_cert;
|
|
Packit |
aea12f |
cert_auth_info_t info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
|
|
Packit |
aea12f |
bool server = 0;
|
|
Packit |
aea12f |
gnutls_certificate_type_t cert_type;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
memset(&peer_cert, 0, sizeof(peer_cert));
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* this message is only expected if we have received
|
|
Packit |
aea12f |
* a certificate message */
|
|
Packit |
aea12f |
if (!(session->internals.hsk_flags & HSK_CRT_VRFY_EXPECTED))
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (session->security_parameters.entity == GNUTLS_SERVER)
|
|
Packit |
aea12f |
server = 1;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
cred = (gnutls_certificate_credentials_t)
|
|
Packit |
aea12f |
_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
|
|
Packit |
aea12f |
if (unlikely(cred == NULL))
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
|
|
Packit |
aea12f |
if (unlikely(info == NULL))
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY, 0, &buf;;
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
_gnutls_handshake_log("HSK[%p]: Parsing certificate verify\n", session);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (buf.length < 2) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
se = _gnutls_tls_aid_to_sign_entry(buf.data[0], buf.data[1], get_version(session));
|
|
Packit |
aea12f |
if (se == NULL) {
|
|
Packit |
aea12f |
_gnutls_handshake_log("Found unsupported signature (%d.%d)\n", (int)buf.data[0], (int)buf.data[1]);
|
|
Packit Service |
991b93 |
ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (server)
|
|
Packit |
aea12f |
gnutls_sign_algorithm_set_client(session, se->id);
|
|
Packit |
aea12f |
else
|
|
Packit |
aea12f |
gnutls_sign_algorithm_set_server(session, se->id);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
buf.data+=2;
|
|
Packit |
aea12f |
buf.length-=2;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* we check during verification whether the algorithm is enabled */
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_buffer_pop_datum_prefix16(&buf, &sig_data);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (sig_data.size == 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* We verify the certificate of the peer. Therefore we need to
|
|
Packit |
aea12f |
* retrieve the negotiated certificate type for the peer. */
|
|
Packit |
aea12f |
cert_type = get_certificate_type(session, GNUTLS_CTYPE_PEERS);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* Verify the signature */
|
|
Packit |
aea12f |
ret = _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
vflags = cred->verify_flags | session->internals.additional_verify_flags;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls13_handshake_verify_data(session, vflags, &peer_cert,
|
|
Packit |
aea12f |
server?(&cli_ctx):(&srv_ctx),
|
|
Packit |
aea12f |
&sig_data, se);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (buf.length > 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = 0;
|
|
Packit |
aea12f |
cleanup:
|
|
Packit |
aea12f |
gnutls_pcert_deinit(&peer_cert);
|
|
Packit |
aea12f |
_gnutls_buffer_clear(&buf;;
|
|
Packit |
aea12f |
return ret;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
int _gnutls13_send_certificate_verify(gnutls_session_t session, unsigned again)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
gnutls_pcert_st *apr_cert_list;
|
|
Packit |
aea12f |
gnutls_privkey_t apr_pkey;
|
|
Packit |
aea12f |
int apr_cert_list_length;
|
|
Packit |
aea12f |
mbuffer_st *bufel = NULL;
|
|
Packit |
aea12f |
gnutls_buffer_st buf;
|
|
Packit |
aea12f |
gnutls_datum_t sig = {NULL, 0};
|
|
Packit |
aea12f |
gnutls_sign_algorithm_t algo;
|
|
Packit |
aea12f |
const gnutls_sign_entry_st *se;
|
|
Packit |
aea12f |
bool server = 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (again == 0) {
|
|
Packit |
aea12f |
if (!session->internals.initial_negotiation_completed &&
|
|
Packit |
aea12f |
session->internals.hsk_flags & HSK_PSK_SELECTED)
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (session->security_parameters.entity == GNUTLS_SERVER &&
|
|
Packit |
aea12f |
session->internals.resumed)
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (session->security_parameters.entity == GNUTLS_SERVER)
|
|
Packit |
aea12f |
server = 1;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_get_selected_cert(session, &apr_cert_list,
|
|
Packit |
aea12f |
&apr_cert_list_length, &apr_pkey);
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (apr_cert_list_length == 0) {
|
|
Packit |
aea12f |
if (server) {
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
|
|
Packit |
aea12f |
} else {
|
|
Packit |
aea12f |
/* for client, this means either we
|
|
Packit |
aea12f |
* didn't get a cert request or we are
|
|
Packit |
aea12f |
* declining authentication; in either
|
|
Packit |
aea12f |
* case we don't send a cert verify */
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (server) {
|
|
Packit Service |
991b93 |
algo = _gnutls_session_get_sign_algo(session, &apr_cert_list[0], apr_pkey, 0, GNUTLS_KX_UNKNOWN);
|
|
Packit |
aea12f |
if (algo == GNUTLS_SIGN_UNKNOWN)
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_INCOMPATIBLE_SIG_WITH_KEY);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
gnutls_sign_algorithm_set_server(session, algo);
|
|
Packit |
aea12f |
} else {
|
|
Packit |
aea12f |
/* for client, signature algorithm is already
|
|
Packit |
aea12f |
* determined from Certificate Request */
|
|
Packit |
aea12f |
algo = gnutls_sign_algorithm_get_client(session);
|
|
Packit |
aea12f |
if (unlikely(algo == GNUTLS_SIGN_UNKNOWN))
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
se = _gnutls_sign_to_entry(algo);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls13_handshake_sign_data(session, &apr_cert_list[0], apr_pkey,
|
|
Packit |
aea12f |
server?(&srv_ctx):(&cli_ctx),
|
|
Packit |
aea12f |
&sig, se);
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_buffer_init_handshake_mbuffer(&buf;;
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_buffer_append_data(&buf, se->aid.id, 2);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_buffer_append_data_prefix(&buf, 16, sig.data, sig.size);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
bufel = _gnutls_buffer_to_mbuffer(&buf;;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
gnutls_free(sig.data);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
cleanup:
|
|
Packit |
aea12f |
gnutls_free(sig.data);
|
|
Packit |
aea12f |
_gnutls_buffer_clear(&buf;;
|
|
Packit |
aea12f |
return ret;
|
|
Packit |
aea12f |
}
|