/*
* Copyright (C) 2012-2014 Free Software Foundation, Inc.
* Copyright (C) 2014 Nikos Mavrogiannopoulos
*
* 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
*
*/
#include "gnutls_int.h"
#include "errors.h"
#include
#include
#include
#include
#include
#include
#include "x509_int.h"
#include
#include "verify-high.h"
#include "read-file.h"
#include
#include
#if !defined(_DIRENT_HAVE_D_TYPE) && !defined(__native_client__)
# ifdef DT_UNKNOWN
# define _DIRENT_HAVE_D_TYPE
# endif
#endif
/* Convenience functions for verify-high functionality
*/
/**
* gnutls_x509_trust_list_add_trust_mem:
* @list: The list
* @cas: A buffer containing a list of CAs (optional)
* @crls: A buffer containing a list of CRLs (optional)
* @type: The format of the certificates
* @tl_flags: flags from %gnutls_trust_list_flags_t
* @tl_vflags: gnutls_certificate_verify_flags if flags specifies GNUTLS_TL_VERIFY_CRL
*
* This function will add the given certificate authorities
* to the trusted list.
*
* Returns: The number of added elements is returned.
*
* Since: 3.1
**/
int
gnutls_x509_trust_list_add_trust_mem(gnutls_x509_trust_list_t list,
const gnutls_datum_t * cas,
const gnutls_datum_t * crls,
gnutls_x509_crt_fmt_t type,
unsigned int tl_flags,
unsigned int tl_vflags)
{
int ret;
gnutls_x509_crt_t *x509_ca_list = NULL;
gnutls_x509_crl_t *x509_crl_list = NULL;
unsigned int x509_ncas, x509_ncrls;
unsigned int r = 0;
if (cas != NULL && cas->data != NULL) {
ret =
gnutls_x509_crt_list_import2(&x509_ca_list, &x509_ncas,
cas, type, 0);
if (ret < 0)
return gnutls_assert_val(ret);
ret =
gnutls_x509_trust_list_add_cas(list, x509_ca_list,
x509_ncas, tl_flags);
gnutls_free(x509_ca_list);
if (ret < 0)
return gnutls_assert_val(ret);
else
r += ret;
}
if (crls != NULL && crls->data != NULL) {
ret =
gnutls_x509_crl_list_import2(&x509_crl_list,
&x509_ncrls, crls, type,
0);
if (ret < 0)
return gnutls_assert_val(ret);
ret =
gnutls_x509_trust_list_add_crls(list, x509_crl_list,
x509_ncrls, tl_flags|GNUTLS_TL_NO_DUPLICATES,
tl_vflags);
gnutls_free(x509_crl_list);
if (ret < 0)
return gnutls_assert_val(ret);
else
r += ret;
}
return r;
}
/**
* gnutls_x509_trust_list_remove_trust_mem:
* @list: The list
* @cas: A buffer containing a list of CAs (optional)
* @type: The format of the certificates
*
* This function will remove the provided certificate authorities
* from the trusted list, and add them into a black list when needed.
*
* See also gnutls_x509_trust_list_remove_cas().
*
* Returns: The number of removed elements is returned.
*
* Since: 3.1.10
**/
int
gnutls_x509_trust_list_remove_trust_mem(gnutls_x509_trust_list_t list,
const gnutls_datum_t * cas,
gnutls_x509_crt_fmt_t type)
{
int ret;
gnutls_x509_crt_t *x509_ca_list = NULL;
unsigned int x509_ncas;
unsigned int r = 0, i;
if (cas != NULL && cas->data != NULL) {
ret =
gnutls_x509_crt_list_import2(&x509_ca_list, &x509_ncas,
cas, type, 0);
if (ret < 0)
return gnutls_assert_val(ret);
ret =
gnutls_x509_trust_list_remove_cas(list, x509_ca_list,
x509_ncas);
for (i = 0; i < x509_ncas; i++)
gnutls_x509_crt_deinit(x509_ca_list[i]);
gnutls_free(x509_ca_list);
if (ret < 0)
return gnutls_assert_val(ret);
else
r += ret;
}
return r;
}
#ifdef ENABLE_PKCS11
static
int remove_pkcs11_url(gnutls_x509_trust_list_t list, const char *ca_file)
{
if (strcmp(ca_file, list->pkcs11_token) == 0) {
gnutls_free(list->pkcs11_token);
list->pkcs11_token = NULL;
}
return 0;
}
/* This function does add a PKCS #11 object URL into trust list. The
* CA certificates are imported directly, rather than using it as a
* trusted PKCS#11 token.
*/
static
int add_trust_list_pkcs11_object_url(gnutls_x509_trust_list_t list, const char *url, unsigned flags)
{
gnutls_x509_crt_t *xcrt_list = NULL;
gnutls_pkcs11_obj_t *pcrt_list = NULL;
unsigned int pcrt_list_size = 0, i;
int ret;
ret =
gnutls_pkcs11_obj_list_import_url2(&pcrt_list, &pcrt_list_size,
url,
GNUTLS_PKCS11_OBJ_FLAG_CRT|GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED,
0);
if (ret < 0)
return gnutls_assert_val(ret);
if (pcrt_list_size == 0) {
ret = 0;
goto cleanup;
}
xcrt_list = gnutls_malloc(sizeof(gnutls_x509_crt_t) * pcrt_list_size);
if (xcrt_list == NULL) {
ret = GNUTLS_E_MEMORY_ERROR;
goto cleanup;
}
ret =
gnutls_x509_crt_list_import_pkcs11(xcrt_list, pcrt_list_size,
pcrt_list, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret =
gnutls_x509_trust_list_add_cas(list, xcrt_list, pcrt_list_size,
flags);
cleanup:
for (i = 0; i < pcrt_list_size; i++)
gnutls_pkcs11_obj_deinit(pcrt_list[i]);
gnutls_free(pcrt_list);
gnutls_free(xcrt_list);
return ret;
}
static
int remove_pkcs11_object_url(gnutls_x509_trust_list_t list, const char *url)
{
gnutls_x509_crt_t *xcrt_list = NULL;
gnutls_pkcs11_obj_t *pcrt_list = NULL;
unsigned int pcrt_list_size = 0, i;
int ret;
ret =
gnutls_pkcs11_obj_list_import_url2(&pcrt_list, &pcrt_list_size,
url,
GNUTLS_PKCS11_OBJ_FLAG_CRT|GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED,
0);
if (ret < 0)
return gnutls_assert_val(ret);
if (pcrt_list_size == 0) {
ret = 0;
goto cleanup;
}
xcrt_list = gnutls_malloc(sizeof(gnutls_x509_crt_t) * pcrt_list_size);
if (xcrt_list == NULL) {
ret = GNUTLS_E_MEMORY_ERROR;
goto cleanup;
}
ret =
gnutls_x509_crt_list_import_pkcs11(xcrt_list, pcrt_list_size,
pcrt_list, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret =
gnutls_x509_trust_list_remove_cas(list, xcrt_list, pcrt_list_size);
cleanup:
for (i = 0; i < pcrt_list_size; i++) {
gnutls_pkcs11_obj_deinit(pcrt_list[i]);
if (xcrt_list)
gnutls_x509_crt_deinit(xcrt_list[i]);
}
gnutls_free(pcrt_list);
gnutls_free(xcrt_list);
return ret;
}
#endif
/**
* gnutls_x509_trust_list_add_trust_file:
* @list: The list
* @ca_file: A file containing a list of CAs (optional)
* @crl_file: A file containing a list of CRLs (optional)
* @type: The format of the certificates
* @tl_flags: flags from %gnutls_trust_list_flags_t
* @tl_vflags: gnutls_certificate_verify_flags if flags specifies GNUTLS_TL_VERIFY_CRL
*
* This function will add the given certificate authorities
* to the trusted list. PKCS #11 URLs are also accepted, instead
* of files, by this function. A PKCS #11 URL implies a trust
* database (a specially marked module in p11-kit); the URL "pkcs11:"
* implies all trust databases in the system. Only a single URL specifying
* trust databases can be set; they cannot be stacked with multiple calls.
*
* Returns: The number of added elements is returned.
*
* Since: 3.1
**/
int
gnutls_x509_trust_list_add_trust_file(gnutls_x509_trust_list_t list,
const char *ca_file,
const char *crl_file,
gnutls_x509_crt_fmt_t type,
unsigned int tl_flags,
unsigned int tl_vflags)
{
gnutls_datum_t cas = { NULL, 0 };
gnutls_datum_t crls = { NULL, 0 };
size_t size;
int ret;
if (ca_file != NULL) {
#ifdef ENABLE_PKCS11
if (strncmp(ca_file, "pkcs11:", 7) == 0) {
unsigned pcrt_list_size = 0;
/* in case of a token URL import it as a PKCS #11 token,
* otherwise import the individual certificates.
*/
if (is_pkcs11_url_object(ca_file) != 0) {
return add_trust_list_pkcs11_object_url(list, ca_file, tl_flags);
} else { /* token */
if (list->pkcs11_token != NULL)
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
list->pkcs11_token = gnutls_strdup(ca_file);
/* enumerate the certificates */
ret = gnutls_pkcs11_obj_list_import_url(NULL, &pcrt_list_size,
ca_file,
(GNUTLS_PKCS11_OBJ_FLAG_CRT|GNUTLS_PKCS11_OBJ_FLAG_MARK_CA|GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED),
0);
if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
return gnutls_assert_val(ret);
return pcrt_list_size;
}
} else
#endif
{
cas.data = (void *) read_binary_file(ca_file, &size);
if (cas.data == NULL) {
gnutls_assert();
return GNUTLS_E_FILE_ERROR;
}
cas.size = size;
}
}
if (crl_file) {
crls.data = (void *) read_binary_file(crl_file, &size);
if (crls.data == NULL) {
gnutls_assert();
return GNUTLS_E_FILE_ERROR;
}
crls.size = size;
}
ret =
gnutls_x509_trust_list_add_trust_mem(list, &cas, &crls, type,
tl_flags, tl_vflags);
free(crls.data);
free(cas.data);
return ret;
}
static
int load_dir_certs(const char *dirname,
gnutls_x509_trust_list_t list,
unsigned int tl_flags, unsigned int tl_vflags,
unsigned type, unsigned crl)
{
DIR *dirp;
struct dirent *d;
int ret;
int r = 0;
char path[GNUTLS_PATH_MAX];
dirp = opendir(dirname);
if (dirp != NULL) {
do {
d = readdir(dirp);
if (d != NULL
#ifdef _DIRENT_HAVE_D_TYPE
&& (d->d_type == DT_REG || d->d_type == DT_LNK || d->d_type == DT_UNKNOWN)
#endif
) {
snprintf(path, sizeof(path), "%s/%s",
dirname, d->d_name);
if (crl != 0) {
ret =
gnutls_x509_trust_list_add_trust_file
(list, NULL, path, type, tl_flags,
tl_vflags);
} else {
ret =
gnutls_x509_trust_list_add_trust_file
(list, path, NULL, type, tl_flags,
tl_vflags);
}
if (ret >= 0)
r += ret;
}
}
while (d != NULL);
closedir(dirp);
}
return r;
}
/**
* gnutls_x509_trust_list_add_trust_dir:
* @list: The list
* @ca_dir: A directory containing the CAs (optional)
* @crl_dir: A directory containing a list of CRLs (optional)
* @type: The format of the certificates
* @tl_flags: flags from %gnutls_trust_list_flags_t
* @tl_vflags: gnutls_certificate_verify_flags if flags specifies GNUTLS_TL_VERIFY_CRL
*
* This function will add the given certificate authorities
* to the trusted list. Only directories are accepted by
* this function.
*
* Returns: The number of added elements is returned.
*
* Since: 3.3.6
**/
int
gnutls_x509_trust_list_add_trust_dir(gnutls_x509_trust_list_t list,
const char *ca_dir,
const char *crl_dir,
gnutls_x509_crt_fmt_t type,
unsigned int tl_flags,
unsigned int tl_vflags)
{
int ret = 0;
if (ca_dir != NULL) {
int r = 0;
r = load_dir_certs(ca_dir, list, tl_flags, tl_vflags, type, 0);
if (r >= 0)
ret += r;
}
if (crl_dir) {
int r = 0;
r = load_dir_certs(crl_dir, list, tl_flags, tl_vflags, type, 1);
if (r >= 0)
ret += r;
}
return ret;
}
/**
* gnutls_x509_trust_list_remove_trust_file:
* @list: The list
* @ca_file: A file containing a list of CAs
* @type: The format of the certificates
*
* This function will remove the given certificate authorities
* from the trusted list, and add them into a black list when needed.
* PKCS 11 URLs are also accepted, instead
* of files, by this function.
*
* See also gnutls_x509_trust_list_remove_cas().
*
* Returns: The number of added elements is returned.
*
* Since: 3.1.10
**/
int
gnutls_x509_trust_list_remove_trust_file(gnutls_x509_trust_list_t list,
const char *ca_file,
gnutls_x509_crt_fmt_t type)
{
gnutls_datum_t cas = { NULL, 0 };
size_t size;
int ret;
#ifdef ENABLE_PKCS11
if (strncmp(ca_file, "pkcs11:", 7) == 0) {
if (is_pkcs11_url_object(ca_file) != 0) {
return remove_pkcs11_object_url(list, ca_file);
} else { /* token */
return remove_pkcs11_url(list, ca_file);
}
} else
#endif
{
cas.data = (void *) read_binary_file(ca_file, &size);
if (cas.data == NULL) {
gnutls_assert();
return GNUTLS_E_FILE_ERROR;
}
cas.size = size;
}
ret = gnutls_x509_trust_list_remove_trust_mem(list, &cas, type);
free(cas.data);
return ret;
}