/* * Copyright (C) 2001-2012 Free Software Foundation, Inc. * * Author: Nikos Mavrogiannopoulos * * This file is part of GnuTLS. * * The GnuTLS is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * */ /* This file contains certificate authentication functions to be exported in the * API which did not fit elsewhere. */ #include #include #include #include #include #include #include #include #include #include #include #include /** * gnutls_random_art: * @type: The type of the random art * @key_type: The type of the key (RSA, DSA etc.) * @key_size: The size of the key in bits * @fpr: The fingerprint of the key * @fpr_size: The size of the fingerprint * @art: The returned random art * * This function will convert a given fingerprint to an "artistic" * image. The returned image is allocated using gnutls_malloc() * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. * **/ int gnutls_random_art(gnutls_random_art_t type, const char *key_type, unsigned int key_size, void *fpr, size_t fpr_size, gnutls_datum_t * art) { if (type != GNUTLS_RANDOM_ART_OPENSSH) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); art->data = (void *) _gnutls_key_fingerprint_randomart(fpr, fpr_size, key_type, key_size, NULL); if (art->data == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); art->size = strlen((char *) art->data); return 0; } /* ANON & DHE */ #if defined(ENABLE_DHE) || defined(ENABLE_ANON) /** * gnutls_dh_set_prime_bits: * @session: is a #gnutls_session_t structure. * @bits: is the number of bits * * This function sets the number of bits, for use in a Diffie-Hellman * key exchange. This is used both in DH ephemeral and DH anonymous * cipher suites. This will set the minimum size of the prime that * will be used for the handshake. * * In the client side it sets the minimum accepted number of bits. If * a server sends a prime with less bits than that * %GNUTLS_E_DH_PRIME_UNACCEPTABLE will be returned by the handshake. * * Note that values lower than 512 bits may allow decryption of the * exchanged data. * * The function has no effect in server side. * * Note that since 3.1.7 this function is deprecated. The minimum * number of bits is set by the priority string level. * Also this function must be called after gnutls_priority_set_direct() * or the set value may be overridden by the selected priority options. * * **/ void gnutls_dh_set_prime_bits(gnutls_session_t session, unsigned int bits) { if (bits <= 512 && bits != 0) _gnutls_audit_log(session, "Note that the security level of the Diffie-Hellman key exchange has been lowered to %u bits and this may allow decryption of the session data\n", bits); session->internals.priorities.dh_prime_bits = bits; } /** * gnutls_dh_get_group: * @session: is a gnutls session * @raw_gen: will hold the generator. * @raw_prime: will hold the prime. * * This function will return the group parameters used in the last * Diffie-Hellman key exchange with the peer. These are the prime and * the generator used. This function should be used for both * anonymous and ephemeral Diffie-Hellman. The output parameters must * be freed with gnutls_free(). * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. **/ int gnutls_dh_get_group(gnutls_session_t session, gnutls_datum_t * raw_gen, gnutls_datum_t * raw_prime) { dh_info_st *dh; int ret; anon_auth_info_t anon_info; cert_auth_info_t cert_info; psk_auth_info_t psk_info; switch (gnutls_auth_get_type(session)) { case GNUTLS_CRD_ANON: anon_info = _gnutls_get_auth_info(session); if (anon_info == NULL) return GNUTLS_E_INTERNAL_ERROR; dh = &anon_info->dh; break; case GNUTLS_CRD_PSK: psk_info = _gnutls_get_auth_info(session); if (psk_info == NULL) return GNUTLS_E_INTERNAL_ERROR; dh = &psk_info->dh; break; case GNUTLS_CRD_CERTIFICATE: cert_info = _gnutls_get_auth_info(session); if (cert_info == NULL) return GNUTLS_E_INTERNAL_ERROR; dh = &cert_info->dh; break; default: gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } ret = _gnutls_set_datum(raw_prime, dh->prime.data, dh->prime.size); if (ret < 0) { gnutls_assert(); return ret; } ret = _gnutls_set_datum(raw_gen, dh->generator.data, dh->generator.size); if (ret < 0) { gnutls_assert(); _gnutls_free_datum(raw_prime); return ret; } return 0; } /** * gnutls_dh_get_pubkey: * @session: is a gnutls session * @raw_key: will hold the public key. * * This function will return the peer's public key used in the last * Diffie-Hellman key exchange. This function should be used for both * anonymous and ephemeral Diffie-Hellman. The output parameters must * be freed with gnutls_free(). * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. **/ int gnutls_dh_get_pubkey(gnutls_session_t session, gnutls_datum_t * raw_key) { dh_info_st *dh; anon_auth_info_t anon_info; cert_auth_info_t cert_info; cert_auth_info_t psk_info; switch (gnutls_auth_get_type(session)) { case GNUTLS_CRD_ANON: { anon_info = _gnutls_get_auth_info(session); if (anon_info == NULL) return GNUTLS_E_INTERNAL_ERROR; dh = &anon_info->dh; break; } case GNUTLS_CRD_PSK: { psk_info = _gnutls_get_auth_info(session); if (psk_info == NULL) return GNUTLS_E_INTERNAL_ERROR; dh = &psk_info->dh; break; } case GNUTLS_CRD_CERTIFICATE: { cert_info = _gnutls_get_auth_info(session); if (cert_info == NULL) return GNUTLS_E_INTERNAL_ERROR; dh = &cert_info->dh; break; } default: gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } return _gnutls_set_datum(raw_key, dh->public_key.data, dh->public_key.size); } /** * gnutls_dh_get_secret_bits: * @session: is a gnutls session * * This function will return the bits used in the last Diffie-Hellman * key exchange with the peer. Should be used for both anonymous and * ephemeral Diffie-Hellman. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. **/ int gnutls_dh_get_secret_bits(gnutls_session_t session) { switch (gnutls_auth_get_type(session)) { case GNUTLS_CRD_ANON: { anon_auth_info_t info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; return info->dh.secret_bits; } case GNUTLS_CRD_PSK: { psk_auth_info_t info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; return info->dh.secret_bits; } case GNUTLS_CRD_CERTIFICATE: { cert_auth_info_t info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; return info->dh.secret_bits; } default: gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } } static int mpi_buf2bits(gnutls_datum_t * mpi_buf) { bigint_t mpi; int rc; rc = _gnutls_mpi_scan_nz(&mpi, mpi_buf->data, mpi_buf->size); if (rc) { gnutls_assert(); return rc; } rc = _gnutls_mpi_get_nbits(mpi); _gnutls_mpi_release(&mpi); return rc; } /** * gnutls_dh_get_prime_bits: * @session: is a gnutls session * * This function will return the bits of the prime used in the last * Diffie-Hellman key exchange with the peer. Should be used for both * anonymous and ephemeral Diffie-Hellman. Note that some ciphers, * like RSA and DSA without DHE, do not use a Diffie-Hellman key * exchange, and then this function will return 0. * * Returns: The Diffie-Hellman bit strength is returned, or 0 if no * Diffie-Hellman key exchange was done, or a negative error code on * failure. **/ int gnutls_dh_get_prime_bits(gnutls_session_t session) { dh_info_st *dh; switch (gnutls_auth_get_type(session)) { case GNUTLS_CRD_ANON: { anon_auth_info_t info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; dh = &info->dh; break; } case GNUTLS_CRD_PSK: { psk_auth_info_t info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; dh = &info->dh; break; } case GNUTLS_CRD_CERTIFICATE: { cert_auth_info_t info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; dh = &info->dh; break; } default: gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } return mpi_buf2bits(&dh->prime); } /** * gnutls_dh_get_peers_public_bits: * @session: is a gnutls session * * Get the Diffie-Hellman public key bit size. Can be used for both * anonymous and ephemeral Diffie-Hellman. * * Returns: The public key bit size used in the last Diffie-Hellman * key exchange with the peer, or a negative error code in case of error. **/ int gnutls_dh_get_peers_public_bits(gnutls_session_t session) { dh_info_st *dh; switch (gnutls_auth_get_type(session)) { case GNUTLS_CRD_ANON: { anon_auth_info_t info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; dh = &info->dh; break; } case GNUTLS_CRD_PSK: { psk_auth_info_t info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; dh = &info->dh; break; } case GNUTLS_CRD_CERTIFICATE: { cert_auth_info_t info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; dh = &info->dh; break; } default: gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } return mpi_buf2bits(&dh->public_key); } /** * gnutls_certificate_set_dh_params: * @res: is a gnutls_certificate_credentials_t structure * @dh_params: is a structure that holds Diffie-Hellman parameters. * * This function will set the Diffie-Hellman parameters for a * certificate server to use. These parameters will be used in * Ephemeral Diffie-Hellman cipher suites. Note that only a pointer * to the parameters are stored in the certificate handle, so you * must not deallocate the parameters before the certificate is deallocated. * **/ void gnutls_certificate_set_dh_params(gnutls_certificate_credentials_t res, gnutls_dh_params_t dh_params) { res->dh_params = dh_params; } #endif /* DH */ /* CERTIFICATE STUFF */ /** * gnutls_certificate_get_ours: * @session: is a gnutls session * * Gets the certificate as sent to the peer in the last handshake. * The certificate is in raw (DER) format. No certificate * list is being returned. Only the first certificate. * * Returns: a pointer to a #gnutls_datum_t containing our * certificate, or %NULL in case of an error or if no certificate * was used. **/ const gnutls_datum_t *gnutls_certificate_get_ours(gnutls_session_t session) { gnutls_certificate_credentials_t cred; CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, NULL); cred = (gnutls_certificate_credentials_t) _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE, NULL); if (cred == NULL || cred->certs == NULL) { gnutls_assert(); return NULL; } if (session->internals.selected_cert_list == NULL) return NULL; return &session->internals.selected_cert_list[0].cert; } /** * gnutls_certificate_get_peers: * @session: is a gnutls session * @list_size: is the length of the certificate list (may be %NULL) * * Get the peer's raw certificate (chain) as sent by the peer. These * certificates are in raw format (DER encoded for X.509). In case of * a X.509 then a certificate list may be present. The first * certificate in the list is the peer's certificate, following the * issuer's certificate, then the issuer's issuer etc. * * In case of OpenPGP keys a single key will be returned in raw * format. * * Returns: a pointer to a #gnutls_datum_t containing our * certificates, or %NULL in case of an error or if no certificate * was used. **/ const gnutls_datum_t *gnutls_certificate_get_peers(gnutls_session_t session, unsigned int *list_size) { cert_auth_info_t info; CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, NULL); info = _gnutls_get_auth_info(session); if (info == NULL) return NULL; if (list_size) *list_size = info->ncerts; return info->raw_certificate_list; } #ifdef ENABLE_OPENPGP /** * gnutls_certificate_get_peers_subkey_id: * @session: is a gnutls session * @id: will contain the ID * * Get the peer's subkey ID when OpenPGP certificates are * used. The returned @id should be treated as constant. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. * * Since: 3.1.3 **/ int gnutls_certificate_get_peers_subkey_id(gnutls_session_t session, gnutls_datum_t * id) { cert_auth_info_t info; CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST); info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INVALID_REQUEST; id->data = info->subkey_id; id->size = GNUTLS_OPENPGP_KEYID_SIZE; return 0; } #endif /** * gnutls_certificate_client_get_request_status: * @session: is a gnutls session * * Get whether client certificate is requested or not. * * Returns: 0 if the peer (server) did not request client * authentication or 1 otherwise, or a negative error code in case of * error. **/ int gnutls_certificate_client_get_request_status(gnutls_session_t session) { return session->key.crt_requested; } /** * gnutls_fingerprint: * @algo: is a digest algorithm * @data: is the data * @result: is the place where the result will be copied (may be null). * @result_size: should hold the size of the result. The actual size * of the returned result will also be copied there. * * This function will calculate a fingerprint (actually a hash), of * the given data. The result is not printable data. You should * convert it to hex, or to something else printable. * * This is the usual way to calculate a fingerprint of an X.509 DER * encoded certificate. Note however that the fingerprint of an * OpenPGP certificate is not just a hash and cannot be calculated with this * function. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. **/ int gnutls_fingerprint(gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, void *result, size_t * result_size) { int ret; int hash_len = _gnutls_hash_get_algo_len(mac_to_entry(algo)); if (hash_len < 0 || (unsigned) hash_len > *result_size || result == NULL) { *result_size = hash_len; return GNUTLS_E_SHORT_MEMORY_BUFFER; } *result_size = hash_len; if (result) { ret = _gnutls_hash_fast(algo, data->data, data->size, result); if (ret < 0) return gnutls_assert_val(ret); } return 0; } /** * gnutls_certificate_set_params_function: * @res: is a gnutls_certificate_credentials_t structure * @func: is the function to be called * * This function will set a callback in order for the server to get * the Diffie-Hellman or RSA parameters for certificate * authentication. The callback should return %GNUTLS_E_SUCCESS (0) on success. **/ void gnutls_certificate_set_params_function(gnutls_certificate_credentials_t res, gnutls_params_function * func) { res->params_func = func; } /** * gnutls_certificate_set_verify_flags: * @res: is a gnutls_certificate_credentials_t structure * @flags: are the flags * * This function will set the flags to be used for verification * of certificates and override any defaults. The provided flags must be an OR of the * #gnutls_certificate_verify_flags enumerations. * **/ void gnutls_certificate_set_verify_flags(gnutls_certificate_credentials_t res, unsigned int flags) { res->verify_flags = flags; } /** * gnutls_certificate_set_verify_limits: * @res: is a gnutls_certificate_credentials structure * @max_bits: is the number of bits of an acceptable certificate (default 8200) * @max_depth: is maximum depth of the verification of a certificate chain (default 5) * * This function will set some upper limits for the default * verification function, gnutls_certificate_verify_peers2(), to avoid * denial of service attacks. You can set them to zero to disable * limits. **/ void gnutls_certificate_set_verify_limits(gnutls_certificate_credentials_t res, unsigned int max_bits, unsigned int max_depth) { res->verify_depth = max_depth; res->verify_bits = max_bits; } #ifdef ENABLE_PSK /** * gnutls_psk_set_params_function: * @res: is a gnutls_psk_server_credentials_t structure * @func: is the function to be called * * This function will set a callback in order for the server to get * the Diffie-Hellman or RSA parameters for PSK authentication. The * callback should return %GNUTLS_E_SUCCESS (0) on success. **/ void gnutls_psk_set_params_function(gnutls_psk_server_credentials_t res, gnutls_params_function * func) { res->params_func = func; } #endif #ifdef ENABLE_ANON /** * gnutls_anon_set_params_function: * @res: is a gnutls_anon_server_credentials_t structure * @func: is the function to be called * * This function will set a callback in order for the server to get * the Diffie-Hellman or RSA parameters for anonymous authentication. * The callback should return %GNUTLS_E_SUCCESS (0) on success. **/ void gnutls_anon_set_params_function(gnutls_anon_server_credentials_t res, gnutls_params_function * func) { res->params_func = func; } #endif /** * gnutls_load_file: * @filename: the name of the file to load * @data: Where the file will be stored * * This function will load a file into a datum. The data are * zero terminated but the terminating null is not included in length. * The returned data are allocated using gnutls_malloc(). * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. * * Since 3.1.0 **/ int gnutls_load_file(const char *filename, gnutls_datum_t * data) { size_t len; data->data = (void *) read_binary_file(filename, &len); if (data->data == NULL) return GNUTLS_E_FILE_ERROR; if (malloc != gnutls_malloc) { void *tmp = gnutls_malloc(len); memcpy(tmp, data->data, len); free(data->data); data->data = tmp; } data->size = len; return 0; } /** * gnutls_ocsp_status_request_is_checked: * @session: is a gnutls session * @flags: should be zero * * Check whether an OCSP status response was included in the handshake * and whether it was checked and valid (not too old or superseded). * This is a helper function when needing to decide whether to perform an * OCSP validity check on the peer's certificate. Must be called after * gnutls_certificate_verify_peers3() is called. * * Returns: non zero it was valid, or a zero if it wasn't sent, * or sent and was invalid. **/ int gnutls_ocsp_status_request_is_checked(gnutls_session_t session, unsigned int flags) { return session->internals.ocsp_check_ok; } #ifdef ENABLE_RSA_EXPORT /** * gnutls_rsa_export_get_pubkey: * @session: is a gnutls session * @exponent: will hold the exponent. * @modulus: will hold the modulus. * * This function will return the peer's public key exponent and * modulus used in the last RSA-EXPORT authentication. The output * parameters must be freed with gnutls_free(). * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. **/ int gnutls_rsa_export_get_pubkey(gnutls_session_t session, gnutls_datum_t * exponent, gnutls_datum_t * modulus) { return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); } /** * gnutls_rsa_export_get_modulus_bits: * @session: is a gnutls session * * Get the export RSA parameter's modulus size. * * Returns: The bits used in the last RSA-EXPORT key exchange with the * peer, or a negative error code in case of error. **/ int gnutls_rsa_export_get_modulus_bits(gnutls_session_t session) { return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); } /** * gnutls_certificate_set_rsa_export_params: * @res: is a gnutls_certificate_credentials_t structure * @rsa_params: is a structure that holds temporary RSA parameters. * * This function will set the temporary RSA parameters for a * certificate server to use. These parameters will be used in * RSA-EXPORT cipher suites. **/ void gnutls_certificate_set_rsa_export_params(gnutls_certificate_credentials_t res, gnutls_rsa_params_t rsa_params) { return; } #endif #define DESC_SIZE 64 /** * gnutls_session_get_desc: * @session: is a gnutls session * * This function returns a string describing the current session. * The string is null terminated and allocated using gnutls_malloc(). * * Returns: a description of the protocols and algorithms in the current session. * * Since: 3.1.10 **/ char *gnutls_session_get_desc(gnutls_session_t session) { gnutls_kx_algorithm_t kx; unsigned type; char kx_name[32]; char proto_name[32]; const char *curve_name = NULL; unsigned dh_bits = 0; unsigned mac_id; char *desc; kx = session->security_parameters.kx_algorithm; if (kx == GNUTLS_KX_ANON_ECDH || kx == GNUTLS_KX_ECDHE_PSK || kx == GNUTLS_KX_ECDHE_RSA || kx == GNUTLS_KX_ECDHE_ECDSA) { curve_name = gnutls_ecc_curve_get_name(gnutls_ecc_curve_get (session)); } else if (kx == GNUTLS_KX_ANON_DH || kx == GNUTLS_KX_DHE_PSK || kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS) { dh_bits = gnutls_dh_get_prime_bits(session); } if (curve_name != NULL) snprintf(kx_name, sizeof(kx_name), "%s-%s", gnutls_kx_get_name(kx), curve_name); else if (dh_bits != 0) snprintf(kx_name, sizeof(kx_name), "%s-%u", gnutls_kx_get_name(kx), dh_bits); else snprintf(kx_name, sizeof(kx_name), "%s", gnutls_kx_get_name(kx)); type = gnutls_certificate_type_get(session); if (type == GNUTLS_CRT_X509) snprintf(proto_name, sizeof(proto_name), "%s", gnutls_protocol_get_name(get_num_version (session))); else snprintf(proto_name, sizeof(proto_name), "%s-%s", gnutls_protocol_get_name(get_num_version (session)), gnutls_certificate_type_get_name(type)); gnutls_protocol_get_name(get_num_version(session)), desc = gnutls_malloc(DESC_SIZE); if (desc == NULL) return NULL; mac_id = gnutls_mac_get(session); if (mac_id == GNUTLS_MAC_AEAD) { /* no need to print */ snprintf(desc, DESC_SIZE, "(%s)-(%s)-(%s)", proto_name, kx_name, gnutls_cipher_get_name(gnutls_cipher_get(session))); } else { snprintf(desc, DESC_SIZE, "(%s)-(%s)-(%s)-(%s)", proto_name, kx_name, gnutls_cipher_get_name(gnutls_cipher_get(session)), gnutls_mac_get_name(mac_id)); } return desc; } /** * gnutls_session_set_id: * @session: is a #gnutls_session_t structure. * @sid: the session identifier * * This function sets the session ID to be used in a client hello. * This is a function intended for exceptional uses. Do not use this * function unless you are implementing a custom protocol. * * To set session resumption parameters use gnutls_session_set_data() instead. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. **/ int gnutls_session_set_id(gnutls_session_t session, const gnutls_datum_t * sid) { if (session->security_parameters.entity == GNUTLS_SERVER || sid->size > TLS_MAX_SESSION_ID_SIZE) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); memset(&session->internals.resumed_security_parameters, 0, sizeof(session->internals.resumed_security_parameters)); session->internals.resumed_security_parameters.session_id_size = sid->size; memcpy(session->internals.resumed_security_parameters.session_id, sid->data, sid->size); return 0; }