Blame lib/auth.c

Packit 549fdc
/*
Packit 549fdc
 * Copyright (C) 2001-2012 Free Software Foundation, Inc.
Packit 549fdc
 * Copyright (C) 2017 Red Hat, Inc.
Packit 549fdc
 *
Packit 549fdc
 * Author: Nikos Mavrogiannopoulos
Packit 549fdc
 *
Packit 549fdc
 * This file is part of GnuTLS.
Packit 549fdc
 *
Packit 549fdc
 * The GnuTLS is free software; you can redistribute it and/or
Packit 549fdc
 * modify it under the terms of the GNU Lesser General Public License
Packit 549fdc
 * as published by the Free Software Foundation; either version 2.1 of
Packit 549fdc
 * the License, or (at your option) any later version.
Packit 549fdc
 *
Packit 549fdc
 * This library is distributed in the hope that it will be useful, but
Packit 549fdc
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 549fdc
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 549fdc
 * Lesser General Public License for more details.
Packit 549fdc
 *
Packit 549fdc
 * You should have received a copy of the GNU Lesser General Public License
Packit 549fdc
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
Packit 549fdc
 *
Packit 549fdc
 */
Packit 549fdc
Packit 549fdc
#include "gnutls_int.h"
Packit 549fdc
#include "errors.h"
Packit 549fdc
#include "auth.h"
Packit 549fdc
#include "auth.h"
Packit 549fdc
#include "algorithms.h"
Packit 549fdc
#include <auth/cert.h>
Packit 549fdc
#include <auth/psk.h>
Packit 549fdc
#include <auth/anon.h>
Packit 549fdc
#include <datum.h>
Packit 549fdc
Packit 549fdc
/* The functions here are used in order for authentication algorithms
Packit 549fdc
 * to be able to retrieve the needed credentials eg public and private
Packit 549fdc
 * key etc.
Packit 549fdc
 */
Packit 549fdc
Packit 549fdc
/**
Packit 549fdc
 * gnutls_credentials_clear:
Packit 549fdc
 * @session: is a #gnutls_session_t type.
Packit 549fdc
 *
Packit 549fdc
 * Clears all the credentials previously set in this session.
Packit 549fdc
 **/
Packit 549fdc
void gnutls_credentials_clear(gnutls_session_t session)
Packit 549fdc
{
Packit 549fdc
	if (session->key.cred) {	/* beginning of the list */
Packit 549fdc
		auth_cred_st *ccred, *ncred;
Packit 549fdc
		ccred = session->key.cred;
Packit 549fdc
		while (ccred != NULL) {
Packit 549fdc
			ncred = ccred->next;
Packit 549fdc
			gnutls_free(ccred);
Packit 549fdc
			ccred = ncred;
Packit 549fdc
		}
Packit 549fdc
		session->key.cred = NULL;
Packit 549fdc
	}
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/* 
Packit 549fdc
 * This creates a linked list of the form:
Packit 549fdc
 * { algorithm, credentials, pointer to next }
Packit 549fdc
 */
Packit 549fdc
/**
Packit 549fdc
 * gnutls_credentials_set:
Packit 549fdc
 * @session: is a #gnutls_session_t type.
Packit 549fdc
 * @type: is the type of the credentials
Packit 549fdc
 * @cred: the credentials to set
Packit 549fdc
 *
Packit 549fdc
 * Sets the needed credentials for the specified type.  E.g. username,
Packit 549fdc
 * password - or public and private keys etc.  The @cred parameter is
Packit 549fdc
 * a structure that depends on the specified type and on the current
Packit 549fdc
 * session (client or server).
Packit 549fdc
 *
Packit 549fdc
 * In order to minimize memory usage, and share credentials between
Packit 549fdc
 * several threads gnutls keeps a pointer to cred, and not the whole
Packit 549fdc
 * cred structure.  Thus you will have to keep the structure allocated
Packit 549fdc
 * until you call gnutls_deinit().
Packit 549fdc
 *
Packit 549fdc
 * For %GNUTLS_CRD_ANON, @cred should be
Packit 549fdc
 * #gnutls_anon_client_credentials_t in case of a client.  In case of
Packit 549fdc
 * a server it should be #gnutls_anon_server_credentials_t.
Packit 549fdc
 *
Packit 549fdc
 * For %GNUTLS_CRD_SRP, @cred should be #gnutls_srp_client_credentials_t
Packit 549fdc
 * in case of a client, and #gnutls_srp_server_credentials_t, in case
Packit 549fdc
 * of a server.
Packit 549fdc
 *
Packit 549fdc
 * For %GNUTLS_CRD_CERTIFICATE, @cred should be
Packit 549fdc
 * #gnutls_certificate_credentials_t.
Packit 549fdc
 *
Packit 549fdc
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
Packit 549fdc
 *   otherwise a negative error code is returned.
Packit 549fdc
 **/
Packit 549fdc
int
Packit 549fdc
gnutls_credentials_set(gnutls_session_t session,
Packit 549fdc
		       gnutls_credentials_type_t type, void *cred)
Packit 549fdc
{
Packit 549fdc
	auth_cred_st *ccred = NULL, *pcred = NULL;
Packit 549fdc
	int exists = 0;
Packit 549fdc
Packit 549fdc
	if (session->key.cred == NULL) {	/* beginning of the list */
Packit 549fdc
Packit 549fdc
		session->key.cred = gnutls_malloc(sizeof(auth_cred_st));
Packit 549fdc
		if (session->key.cred == NULL)
Packit 549fdc
			return GNUTLS_E_MEMORY_ERROR;
Packit 549fdc
Packit 549fdc
		/* copy credentials locally */
Packit 549fdc
		session->key.cred->credentials = cred;
Packit 549fdc
Packit 549fdc
		session->key.cred->next = NULL;
Packit 549fdc
		session->key.cred->algorithm = type;
Packit 549fdc
	} else {
Packit 549fdc
		ccred = session->key.cred;
Packit 549fdc
		while (ccred != NULL) {
Packit 549fdc
			if (ccred->algorithm == type) {
Packit 549fdc
				exists = 1;
Packit 549fdc
				break;
Packit 549fdc
			}
Packit 549fdc
			pcred = ccred;
Packit 549fdc
			ccred = ccred->next;
Packit 549fdc
		}
Packit 549fdc
		/* After this, pcred is not null.
Packit 549fdc
		 */
Packit 549fdc
Packit 549fdc
		if (exists == 0) {	/* new entry */
Packit 549fdc
			pcred->next = gnutls_malloc(sizeof(auth_cred_st));
Packit 549fdc
			if (pcred->next == NULL)
Packit 549fdc
				return GNUTLS_E_MEMORY_ERROR;
Packit 549fdc
Packit 549fdc
			ccred = pcred->next;
Packit 549fdc
Packit 549fdc
			/* copy credentials locally */
Packit 549fdc
			ccred->credentials = cred;
Packit 549fdc
Packit 549fdc
			ccred->next = NULL;
Packit 549fdc
			ccred->algorithm = type;
Packit 549fdc
		} else {	/* modify existing entry */
Packit 549fdc
			ccred->credentials = cred;
Packit 549fdc
		}
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	return 0;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/**
Packit 549fdc
 * gnutls_credentials_get:
Packit 549fdc
 * @session: is a #gnutls_session_t type.
Packit 549fdc
 * @type: is the type of the credentials to return
Packit 549fdc
 * @cred: will contain the credentials.
Packit 549fdc
 *
Packit 549fdc
 * Returns the previously provided credentials structures.
Packit 549fdc
 *
Packit 549fdc
 * For %GNUTLS_CRD_ANON, @cred will be
Packit 549fdc
 * #gnutls_anon_client_credentials_t in case of a client.  In case of
Packit 549fdc
 * a server it should be #gnutls_anon_server_credentials_t.
Packit 549fdc
 *
Packit 549fdc
 * For %GNUTLS_CRD_SRP, @cred will be #gnutls_srp_client_credentials_t
Packit 549fdc
 * in case of a client, and #gnutls_srp_server_credentials_t, in case
Packit 549fdc
 * of a server.
Packit 549fdc
 *
Packit 549fdc
 * For %GNUTLS_CRD_CERTIFICATE, @cred will be
Packit 549fdc
 * #gnutls_certificate_credentials_t.
Packit 549fdc
 *
Packit 549fdc
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
Packit 549fdc
 *   otherwise a negative error code is returned.
Packit 549fdc
 *
Packit 549fdc
 * Since: 3.3.3
Packit 549fdc
 **/
Packit 549fdc
int
Packit 549fdc
gnutls_credentials_get(gnutls_session_t session,
Packit 549fdc
		       gnutls_credentials_type_t type, void **cred)
Packit 549fdc
{
Packit 549fdc
	const void *_cred;
Packit 549fdc
Packit 549fdc
	_cred = _gnutls_get_cred(session, type);
Packit 549fdc
	if (_cred == NULL)
Packit 549fdc
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
Packit 549fdc
Packit 549fdc
	if (cred)
Packit 549fdc
		*cred = (void*)_cred;
Packit 549fdc
Packit 549fdc
	return 0;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/**
Packit 549fdc
 * gnutls_auth_get_type:
Packit 549fdc
 * @session: is a #gnutls_session_t type.
Packit 549fdc
 *
Packit 549fdc
 * Returns type of credentials for the current authentication schema.
Packit 549fdc
 * The returned information is to be used to distinguish the function used
Packit 549fdc
 * to access authentication data.
Packit 549fdc
 *
Packit 549fdc
 * Eg. for CERTIFICATE ciphersuites (key exchange algorithms:
Packit 549fdc
 * %GNUTLS_KX_RSA, %GNUTLS_KX_DHE_RSA), the same function are to be
Packit 549fdc
 * used to access the authentication data.
Packit 549fdc
 *
Packit 549fdc
 * Returns: The type of credentials for the current authentication
Packit 549fdc
 *   schema, a #gnutls_credentials_type_t type.
Packit 549fdc
 **/
Packit 549fdc
gnutls_credentials_type_t gnutls_auth_get_type(gnutls_session_t session)
Packit 549fdc
{
Packit 549fdc
/* This is not the credentials we must set, but the authentication data
Packit 549fdc
 * we get by the peer, so it should be reversed.
Packit 549fdc
 */
Packit 549fdc
	int server =
Packit 549fdc
	    session->security_parameters.entity == GNUTLS_SERVER ? 0 : 1;
Packit 549fdc
Packit 549fdc
	if (!session->security_parameters.cs) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		return 0;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	return
Packit 549fdc
	    _gnutls_map_kx_get_cred(session->security_parameters.
Packit 549fdc
				     cs->kx_algorithm, server);
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/**
Packit 549fdc
 * gnutls_auth_server_get_type:
Packit 549fdc
 * @session: is a #gnutls_session_t type.
Packit 549fdc
 *
Packit 549fdc
 * Returns the type of credentials that were used for server authentication.
Packit 549fdc
 * The returned information is to be used to distinguish the function used
Packit 549fdc
 * to access authentication data.
Packit 549fdc
 *
Packit 549fdc
 * Returns: The type of credentials for the server authentication
Packit 549fdc
 *   schema, a #gnutls_credentials_type_t type.
Packit 549fdc
 **/
Packit 549fdc
gnutls_credentials_type_t
Packit 549fdc
gnutls_auth_server_get_type(gnutls_session_t session)
Packit 549fdc
{
Packit 549fdc
	if (!session->security_parameters.cs) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		return 0;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	return
Packit 549fdc
	    _gnutls_map_kx_get_cred(session->security_parameters.
Packit 549fdc
				     cs->kx_algorithm, 1);
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/**
Packit 549fdc
 * gnutls_auth_client_get_type:
Packit 549fdc
 * @session: is a #gnutls_session_t type.
Packit 549fdc
 *
Packit 549fdc
 * Returns the type of credentials that were used for client authentication.
Packit 549fdc
 * The returned information is to be used to distinguish the function used
Packit 549fdc
 * to access authentication data.
Packit 549fdc
 *
Packit 549fdc
 * Returns: The type of credentials for the client authentication
Packit 549fdc
 *   schema, a #gnutls_credentials_type_t type.
Packit 549fdc
 **/
Packit 549fdc
gnutls_credentials_type_t
Packit 549fdc
gnutls_auth_client_get_type(gnutls_session_t session)
Packit 549fdc
{
Packit 549fdc
	if (!session->security_parameters.cs) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		return 0;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	return
Packit 549fdc
	    _gnutls_map_kx_get_cred(session->security_parameters.
Packit 549fdc
				     cs->kx_algorithm, 0);
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
Packit 549fdc
/* 
Packit 549fdc
 * This returns a pointer to the linked list. Don't
Packit 549fdc
 * free that!!!
Packit 549fdc
 */
Packit 549fdc
const void *_gnutls_get_kx_cred(gnutls_session_t session,
Packit 549fdc
				gnutls_kx_algorithm_t algo)
Packit 549fdc
{
Packit 549fdc
	int server =
Packit 549fdc
	    session->security_parameters.entity == GNUTLS_SERVER ? 1 : 0;
Packit 549fdc
Packit 549fdc
	return _gnutls_get_cred(session,
Packit 549fdc
				_gnutls_map_kx_get_cred(algo, server));
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
const void *_gnutls_get_cred(gnutls_session_t session,
Packit 549fdc
			     gnutls_credentials_type_t type)
Packit 549fdc
{
Packit 549fdc
	auth_cred_st *ccred;
Packit 549fdc
	gnutls_key_st *key = &session->key;
Packit 549fdc
Packit 549fdc
	ccred = key->cred;
Packit 549fdc
	while (ccred != NULL) {
Packit 549fdc
		if (ccred->algorithm == type) {
Packit 549fdc
			break;
Packit 549fdc
		}
Packit 549fdc
		ccred = ccred->next;
Packit 549fdc
	}
Packit 549fdc
	if (ccred == NULL)
Packit 549fdc
		return NULL;
Packit 549fdc
Packit 549fdc
	return ccred->credentials;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/*-
Packit 549fdc
 * _gnutls_free_auth_info - Frees the auth info structure
Packit 549fdc
 * @session: is a #gnutls_session_t type.
Packit 549fdc
 *
Packit 549fdc
 * This function frees the auth info structure and sets it to
Packit 549fdc
 * null. It must be called since some structures contain malloced
Packit 549fdc
 * elements.
Packit 549fdc
 -*/
Packit 549fdc
void _gnutls_free_auth_info(gnutls_session_t session)
Packit 549fdc
{
Packit 549fdc
	dh_info_st *dh_info;
Packit 549fdc
Packit 549fdc
	if (session == NULL) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		return;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	switch (session->key.auth_info_type) {
Packit 549fdc
	case GNUTLS_CRD_SRP:
Packit 549fdc
		break;
Packit 549fdc
#ifdef ENABLE_ANON
Packit 549fdc
	case GNUTLS_CRD_ANON:
Packit 549fdc
		{
Packit 549fdc
			anon_auth_info_t info =
Packit 549fdc
			    _gnutls_get_auth_info(session, GNUTLS_CRD_ANON);
Packit 549fdc
Packit 549fdc
			if (info == NULL)
Packit 549fdc
				break;
Packit 549fdc
Packit 549fdc
			dh_info = &info->dh;
Packit 549fdc
			_gnutls_free_dh_info(dh_info);
Packit 549fdc
		}
Packit 549fdc
		break;
Packit 549fdc
#endif
Packit 549fdc
	case GNUTLS_CRD_PSK:
Packit 549fdc
		{
Packit 549fdc
			psk_auth_info_t info =
Packit 549fdc
			    _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
Packit 549fdc
Packit 549fdc
			if (info == NULL)
Packit 549fdc
				break;
Packit 549fdc
Packit 549fdc
#ifdef ENABLE_DHE
Packit 549fdc
			dh_info = &info->dh;
Packit 549fdc
			_gnutls_free_dh_info(dh_info);
Packit 549fdc
#endif
Packit 549fdc
		}
Packit 549fdc
		break;
Packit 549fdc
	case GNUTLS_CRD_CERTIFICATE:
Packit 549fdc
		{
Packit 549fdc
			unsigned int i;
Packit 549fdc
			cert_auth_info_t info =
Packit 549fdc
			    _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
Packit 549fdc
Packit 549fdc
			if (info == NULL)
Packit 549fdc
				break;
Packit 549fdc
Packit 549fdc
			dh_info = &info->dh;
Packit 549fdc
			for (i = 0; i < info->ncerts; i++) {
Packit 549fdc
				_gnutls_free_datum(&info->
Packit 549fdc
						   raw_certificate_list
Packit 549fdc
						   [i]);
Packit 549fdc
			}
Packit 549fdc
Packit 549fdc
			gnutls_free(info->raw_certificate_list);
Packit 549fdc
			info->raw_certificate_list = NULL;
Packit 549fdc
			info->ncerts = 0;
Packit 549fdc
Packit 549fdc
#ifdef ENABLE_DHE
Packit 549fdc
			_gnutls_free_dh_info(dh_info);
Packit 549fdc
#endif
Packit 549fdc
		}
Packit 549fdc
Packit 549fdc
Packit 549fdc
		break;
Packit 549fdc
	default:
Packit 549fdc
		return;
Packit 549fdc
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	gnutls_free(session->key.auth_info);
Packit 549fdc
	session->key.auth_info = NULL;
Packit 549fdc
	session->key.auth_info_size = 0;
Packit 549fdc
	session->key.auth_info_type = 0;
Packit 549fdc
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/* This function will create the auth info structure in the key
Packit 549fdc
 * structure if needed.
Packit 549fdc
 *
Packit 549fdc
 * If allow change is !=0 then this will allow changing the auth
Packit 549fdc
 * info structure to a different type.
Packit 549fdc
 */
Packit 549fdc
int
Packit 549fdc
_gnutls_auth_info_set(gnutls_session_t session,
Packit 549fdc
		      gnutls_credentials_type_t type, int size,
Packit 549fdc
		      int allow_change)
Packit 549fdc
{
Packit 549fdc
	if (session->key.auth_info == NULL) {
Packit 549fdc
		session->key.auth_info = gnutls_calloc(1, size);
Packit 549fdc
		if (session->key.auth_info == NULL) {
Packit 549fdc
			gnutls_assert();
Packit 549fdc
			return GNUTLS_E_MEMORY_ERROR;
Packit 549fdc
		}
Packit 549fdc
		session->key.auth_info_type = type;
Packit 549fdc
		session->key.auth_info_size = size;
Packit 549fdc
	} else {
Packit 549fdc
		if (allow_change == 0) {
Packit 549fdc
			/* If the credentials for the current authentication scheme,
Packit 549fdc
			 * are not the one we want to set, then it's an error.
Packit 549fdc
			 * This may happen if a rehandshake is performed an the
Packit 549fdc
			 * ciphersuite which is negotiated has different authentication
Packit 549fdc
			 * schema.
Packit 549fdc
			 */
Packit 549fdc
			if (type != session->key.auth_info_type) {
Packit 549fdc
				gnutls_assert();
Packit 549fdc
				return GNUTLS_E_INVALID_REQUEST;
Packit 549fdc
			}
Packit 549fdc
		} else {
Packit 549fdc
			/* The new behaviour: Here we reallocate the auth info structure
Packit 549fdc
			 * in order to be able to negotiate different authentication
Packit 549fdc
			 * types. Ie. perform an auth_anon and then authenticate again using a
Packit 549fdc
			 * certificate (in order to prevent revealing the certificate's contents,
Packit 549fdc
			 * to passive eavesdropers.
Packit 549fdc
			 */
Packit 549fdc
			if (type != session->key.auth_info_type) {
Packit 549fdc
Packit 549fdc
				_gnutls_free_auth_info(session);
Packit 549fdc
Packit 549fdc
				session->key.auth_info = calloc(1, size);
Packit 549fdc
				if (session->key.auth_info == NULL) {
Packit 549fdc
					gnutls_assert();
Packit 549fdc
					return GNUTLS_E_MEMORY_ERROR;
Packit 549fdc
				}
Packit 549fdc
Packit 549fdc
				session->key.auth_info_type = type;
Packit 549fdc
				session->key.auth_info_size = size;
Packit 549fdc
			}
Packit 549fdc
		}
Packit 549fdc
	}
Packit 549fdc
	return 0;
Packit 549fdc
}