diff options
Diffstat (limited to 'lib/auth.c')
-rw-r--r-- | lib/auth.c | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/lib/auth.c b/lib/auth.c new file mode 100644 index 0000000..7c58bd8 --- /dev/null +++ b/lib/auth.c @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2001-2012 Free Software Foundation, Inc. + * Copyright (C) 2017 Red Hat, 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 <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "auth.h" +#include "auth.h" +#include "algorithms.h" +#include <auth/cert.h> +#include <auth/psk.h> +#include <auth/srp_kx.h> +#include <auth/anon.h> +#include <datum.h> + +/* The functions here are used in order for authentication algorithms + * to be able to retrieve the needed credentials eg public and private + * key etc. + */ + +/** + * gnutls_credentials_clear: + * @session: is a #gnutls_session_t type. + * + * Clears all the credentials previously set in this session. + **/ +void gnutls_credentials_clear(gnutls_session_t session) +{ + if (session->key.cred) { /* beginning of the list */ + auth_cred_st *ccred, *ncred; + ccred = session->key.cred; + while (ccred != NULL) { + ncred = ccred->next; + gnutls_free(ccred); + ccred = ncred; + } + session->key.cred = NULL; + } +} + +/* + * This creates a linked list of the form: + * { algorithm, credentials, pointer to next } + */ +/** + * gnutls_credentials_set: + * @session: is a #gnutls_session_t type. + * @type: is the type of the credentials + * @cred: the credentials to set + * + * Sets the needed credentials for the specified type. E.g. username, + * password - or public and private keys etc. The @cred parameter is + * a structure that depends on the specified type and on the current + * session (client or server). + * + * In order to minimize memory usage, and share credentials between + * several threads gnutls keeps a pointer to cred, and not the whole + * cred structure. Thus you will have to keep the structure allocated + * until you call gnutls_deinit(). + * + * For %GNUTLS_CRD_ANON, @cred should be + * #gnutls_anon_client_credentials_t in case of a client. In case of + * a server it should be #gnutls_anon_server_credentials_t. + * + * For %GNUTLS_CRD_SRP, @cred should be #gnutls_srp_client_credentials_t + * in case of a client, and #gnutls_srp_server_credentials_t, in case + * of a server. + * + * For %GNUTLS_CRD_CERTIFICATE, @cred should be + * #gnutls_certificate_credentials_t. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + **/ +int +gnutls_credentials_set(gnutls_session_t session, + gnutls_credentials_type_t type, void *cred) +{ + auth_cred_st *ccred = NULL, *pcred = NULL; + int exists = 0; + + if (session->key.cred == NULL) { /* beginning of the list */ + + session->key.cred = gnutls_malloc(sizeof(auth_cred_st)); + if (session->key.cred == NULL) + return GNUTLS_E_MEMORY_ERROR; + + /* copy credentials locally */ + session->key.cred->credentials = cred; + + session->key.cred->next = NULL; + session->key.cred->algorithm = type; + } else { + ccred = session->key.cred; + while (ccred != NULL) { + if (ccred->algorithm == type) { + exists = 1; + break; + } + pcred = ccred; + ccred = ccred->next; + } + /* After this, pcred is not null. + */ + + if (exists == 0) { /* new entry */ + pcred->next = gnutls_malloc(sizeof(auth_cred_st)); + if (pcred->next == NULL) + return GNUTLS_E_MEMORY_ERROR; + + ccred = pcred->next; + + /* copy credentials locally */ + ccred->credentials = cred; + + ccred->next = NULL; + ccred->algorithm = type; + } else { /* modify existing entry */ + ccred->credentials = cred; + } + } + + /* sanity tests */ + if (type == GNUTLS_CRD_CERTIFICATE) { + gnutls_certificate_credentials_t c = cred; + unsigned i; + bool allow_tls13 = 0; + unsigned key_usage; + + if (c != NULL && c->ncerts != 0) { + for (i = 0; i < c->ncerts; i++) { + key_usage = get_key_usage(session, c->certs[i].cert_list[0].pubkey); + if (key_usage == 0 || (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) { + allow_tls13 = 1; + break; + } + } + + if (session->security_parameters.entity == GNUTLS_SERVER && + !c->tls13_ok) + allow_tls13 = 0; + + if (!allow_tls13) { + /* to prevent the server random indicate TLS1.3 support */ + session->internals.flags |= INT_FLAG_NO_TLS13; + } + } + } + + return 0; +} + +/** + * gnutls_credentials_get: + * @session: is a #gnutls_session_t type. + * @type: is the type of the credentials to return + * @cred: will contain the credentials. + * + * Returns the previously provided credentials structures. + * + * For %GNUTLS_CRD_ANON, @cred will be + * #gnutls_anon_client_credentials_t in case of a client. In case of + * a server it should be #gnutls_anon_server_credentials_t. + * + * For %GNUTLS_CRD_SRP, @cred will be #gnutls_srp_client_credentials_t + * in case of a client, and #gnutls_srp_server_credentials_t, in case + * of a server. + * + * For %GNUTLS_CRD_CERTIFICATE, @cred will be + * #gnutls_certificate_credentials_t. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + * + * Since: 3.3.3 + **/ +int +gnutls_credentials_get(gnutls_session_t session, + gnutls_credentials_type_t type, void **cred) +{ + const void *_cred; + + _cred = _gnutls_get_cred(session, type); + if (_cred == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if (cred) + *cred = (void*)_cred; + + return 0; +} + +/** + * gnutls_auth_get_type: + * @session: is a #gnutls_session_t type. + * + * Returns type of credentials for the current authentication schema. + * The returned information is to be used to distinguish the function used + * to access authentication data. + * + * Eg. for CERTIFICATE ciphersuites (key exchange algorithms: + * %GNUTLS_KX_RSA, %GNUTLS_KX_DHE_RSA), the same function are to be + * used to access the authentication data. + * + * Note that on resumed sessions, this function returns the schema + * used in the original session authentication. + * + * Returns: The type of credentials for the current authentication + * schema, a #gnutls_credentials_type_t type. + **/ +gnutls_credentials_type_t gnutls_auth_get_type(gnutls_session_t session) +{ + if (session->security_parameters.entity == GNUTLS_SERVER) + return gnutls_auth_client_get_type(session); + else + return gnutls_auth_server_get_type(session); +} + +/** + * gnutls_auth_server_get_type: + * @session: is a #gnutls_session_t type. + * + * Returns the type of credentials that were used for server authentication. + * The returned information is to be used to distinguish the function used + * to access authentication data. + * + * Note that on resumed sessions, this function returns the schema + * used in the original session authentication. + * + * Returns: The type of credentials for the server authentication + * schema, a #gnutls_credentials_type_t type. + **/ +gnutls_credentials_type_t +gnutls_auth_server_get_type(gnutls_session_t session) +{ + return session->security_parameters.server_auth_type; +} + +/** + * gnutls_auth_client_get_type: + * @session: is a #gnutls_session_t type. + * + * Returns the type of credentials that were used for client authentication. + * The returned information is to be used to distinguish the function used + * to access authentication data. + * + * Note that on resumed sessions, this function returns the schema + * used in the original session authentication. + * + * Returns: The type of credentials for the client authentication + * schema, a #gnutls_credentials_type_t type. + **/ +gnutls_credentials_type_t +gnutls_auth_client_get_type(gnutls_session_t session) +{ + return session->security_parameters.client_auth_type; +} + + +/* + * This returns a pointer to the linked list. Don't + * free that!!! + */ +const void *_gnutls_get_kx_cred(gnutls_session_t session, + gnutls_kx_algorithm_t algo) +{ + int server = + session->security_parameters.entity == GNUTLS_SERVER ? 1 : 0; + + return _gnutls_get_cred(session, + _gnutls_map_kx_get_cred(algo, server)); +} + +const void *_gnutls_get_cred(gnutls_session_t session, + gnutls_credentials_type_t type) +{ + auth_cred_st *ccred; + gnutls_key_st *key = &session->key; + + ccred = key->cred; + while (ccred != NULL) { + if (ccred->algorithm == type) { + break; + } + ccred = ccred->next; + } + if (ccred == NULL) + return NULL; + + return ccred->credentials; +} + +/*- + * _gnutls_free_auth_info - Frees the auth info structure + * @session: is a #gnutls_session_t type. + * + * This function frees the auth info structure and sets it to + * null. It must be called since some structures contain malloced + * elements. + -*/ +void _gnutls_free_auth_info(gnutls_session_t session) +{ + dh_info_st *dh_info; + + if (session == NULL) { + gnutls_assert(); + return; + } + + switch (session->key.auth_info_type) { + case GNUTLS_CRD_SRP: + { + srp_server_auth_info_t info = + _gnutls_get_auth_info(session, GNUTLS_CRD_SRP); + + if (info == NULL) + break; + + gnutls_free(info->username); + info->username = NULL; + } + break; +#ifdef ENABLE_ANON + case GNUTLS_CRD_ANON: + { + anon_auth_info_t info = + _gnutls_get_auth_info(session, GNUTLS_CRD_ANON); + + if (info == NULL) + break; + + dh_info = &info->dh; + _gnutls_free_dh_info(dh_info); + } + break; +#endif + case GNUTLS_CRD_PSK: + { + psk_auth_info_t info = + _gnutls_get_auth_info(session, GNUTLS_CRD_PSK); + + if (info == NULL) + break; + + gnutls_free(info->username); + info->username = NULL; + info->username_len = 0; + + gnutls_free(info->hint); + info->hint = NULL; + info->hint_len = 0; + +#ifdef ENABLE_DHE + dh_info = &info->dh; + _gnutls_free_dh_info(dh_info); +#endif + } + break; + case GNUTLS_CRD_CERTIFICATE: + { + unsigned int i; + cert_auth_info_t info = + _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE); + + if (info == NULL) + break; + + dh_info = &info->dh; + for (i = 0; i < info->ncerts; i++) { + _gnutls_free_datum(&info->raw_certificate_list[i]); + } + + for (i = 0; i < info->nocsp; i++) { + _gnutls_free_datum(&info->raw_ocsp_list[i]); + } + + gnutls_free(info->raw_certificate_list); + gnutls_free(info->raw_ocsp_list); + info->ncerts = 0; + info->nocsp = 0; + +#ifdef ENABLE_DHE + _gnutls_free_dh_info(dh_info); +#endif + } + + + break; + default: + return; + + } + + gnutls_free(session->key.auth_info); + session->key.auth_info_size = 0; + session->key.auth_info_type = 0; + +} + +/* This function will create the auth info structure in the key + * structure if needed. + * + * If allow change is !=0 then this will allow changing the auth + * info structure to a different type. + */ +int +_gnutls_auth_info_init(gnutls_session_t session, + gnutls_credentials_type_t type, int size, + int allow_change) +{ + if (session->key.auth_info == NULL) { + session->key.auth_info = gnutls_calloc(1, size); + if (session->key.auth_info == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + session->key.auth_info_type = type; + session->key.auth_info_size = size; + } else { + if (allow_change == 0) { + /* If the credentials for the current authentication scheme, + * are not the one we want to set, then it's an error. + * This may happen if a rehandshake is performed an the + * ciphersuite which is negotiated has different authentication + * schema. + */ + if (type != session->key.auth_info_type) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + } else { + /* The new behaviour: Here we reallocate the auth info structure + * in order to be able to negotiate different authentication + * types. Ie. perform an auth_anon and then authenticate again using a + * certificate (in order to prevent revealing the certificate's contents, + * to passive eavesdropers. + */ + if (type != session->key.auth_info_type) { + + _gnutls_free_auth_info(session); + + session->key.auth_info = gnutls_calloc(1, size); + if (session->key.auth_info == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + session->key.auth_info_type = type; + session->key.auth_info_size = size; + } + } + } + return 0; +} |