summaryrefslogtreecommitdiff
path: root/lib/verify-tofu.c
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2012-02-15 08:54:54 +0100
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2012-02-15 08:54:54 +0100
commit1f245ddb9ab672a0b253b711e58c186dd6d1ae4f (patch)
treef6cce0df88ae3090190c96c31107c8139ecd8cd3 /lib/verify-tofu.c
parent0b215d3f27f52808ca7bf2b5a8192fe61ae9203f (diff)
downloadgnutls-1f245ddb9ab672a0b253b711e58c186dd6d1ae4f.tar.gz
The hash in gnutls_store_commitment() is specified in raw format.
Diffstat (limited to 'lib/verify-tofu.c')
-rw-r--r--lib/verify-tofu.c736
1 files changed, 736 insertions, 0 deletions
diff --git a/lib/verify-tofu.c b/lib/verify-tofu.c
new file mode 100644
index 0000000000..d4461eeca4
--- /dev/null
+++ b/lib/verify-tofu.c
@@ -0,0 +1,736 @@
+/*
+ * Copyright (C) 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 3 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <gnutls_int.h>
+#include <gnutls_errors.h>
+#include <libtasn1.h>
+#include <gnutls_global.h>
+#include <gnutls_num.h> /* MAX */
+#include <gnutls_sig.h>
+#include <gnutls_str.h>
+#include <gnutls_datum.h>
+#include <hash.h>
+#include "x509_int.h"
+#include <common.h>
+#include <base64.h>
+#include <gnutls/abstract.h>
+#include <system.h>
+
+static int raw_pubkey_to_base64(const gnutls_datum_t* raw, gnutls_datum_t * b64);
+static int x509_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t *rpubkey);
+static int pgp_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t *rpubkey);
+static int find_stored_pubkey(const char* file,
+ const char* host, const char* service,
+ const gnutls_datum_t* skey);
+
+static
+int store_commitment(const char* db_name, const char* host,
+ const char* service, time_t expiration,
+ gnutls_digest_algorithm_t hash_algo,
+ const gnutls_datum_t* hash);
+static
+int store_pubkey(const char* db_name, const char* host,
+ const char* service, time_t expiration, const gnutls_datum_t* pubkey);
+
+static int find_config_file(char* file, size_t max_size);
+#define MAX_FILENAME 512
+
+static const trust_storage_st default_storage =
+{
+ store_pubkey,
+ store_commitment,
+ find_stored_pubkey
+};
+
+/**
+ * gnutls_verify_stored_pubkey:
+ * @db_name: A file specifying the stored keys (use NULL for the default)
+ * @tdb: A database structure or NULL to use the default
+ * @host: The peer's name
+ * @service: non-NULL if this key is specific to a service (e.g. http)
+ * @cert_type: The type of the certificate
+ * @cert: The raw (der) data of the certificate
+ * @flags: should be 0.
+ *
+ * This function will try to verify the provided certificate using
+ * a list of stored public keys. The @service field if non-NULL should
+ * be a port number.
+ *
+ * The @tdb variable if non-null specifies a custom backend for
+ * the storage and retrieval of entries. If it is NULL then the
+ * default file backend will be used. In POSIX-like systems the
+ * file backend uses the $HOME/.gnutls/known_hosts file.
+ *
+ * Note that if the custom storage backend is provided the
+ * retrieval function should return %GNUTLS_E_CERTIFICATE_KEY_MISMATCH
+ * if the host/service pair is found but key doesn't match,
+ * %GNUTLS_E_NO_CERTIFICATE_FOUND if no such host/service with
+ * the given key is found, and 0 if it was found. The storage
+ * function should return 0 on success.
+ *
+ * Returns: If no associated public key is found
+ * then %GNUTLS_E_NO_CERTIFICATE_FOUND will be returned. If a key
+ * is found but does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH
+ * is returned. On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * or a negative error value on other errors.
+ *
+ * Since: 3.0.0
+ **/
+int
+gnutls_verify_stored_pubkey(const char* db_name,
+ const trust_storage_st *tdb,
+ const char* host,
+ const char* service,
+ gnutls_certificate_type_t cert_type,
+ const gnutls_datum_t * cert, unsigned int flags)
+{
+gnutls_datum_t pubkey = { NULL, 0 };
+int ret;
+char local_file[MAX_FILENAME];
+
+ if (cert_type != GNUTLS_CRT_X509 && cert_type != GNUTLS_CRT_OPENPGP)
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
+
+ if (db_name == NULL && tdb == NULL)
+ {
+ ret = find_config_file(local_file, sizeof(local_file));
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ db_name = local_file;
+ }
+
+ if (tdb == NULL)
+ tdb = &default_storage;
+
+ if (cert_type == GNUTLS_CRT_X509)
+ ret = x509_crt_to_raw_pubkey(cert, &pubkey);
+ else
+ ret = pgp_crt_to_raw_pubkey(cert, &pubkey);
+
+ if (ret < 0)
+ {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = tdb->retrieve(db_name, host, service, &pubkey);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND);
+
+cleanup:
+ gnutls_free(pubkey.data);
+ return ret;
+}
+
+static int parse_commitment_line(char* line,
+ const char* host, size_t host_len,
+ const char* service, size_t service_len,
+ time_t now,
+ const gnutls_datum_t *skey)
+{
+char* p, *kp;
+char* savep = NULL;
+size_t kp_len, phash_size;
+time_t expiration;
+int ret;
+gnutls_digest_algorithm_t hash_algo;
+uint8_t phash[MAX_HASH_SIZE];
+uint8_t hphash[MAX_HASH_SIZE*2+1];
+
+ /* read host */
+ p = strtok_r(line, "|", &savep);
+ if (p == NULL)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ if (p[0] != '*' && strcmp(p, host) != 0)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ /* read service */
+ p = strtok_r(NULL, "|", &savep);
+ if (p == NULL)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ if (p[0] != '*' && strcmp(p, service) != 0)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ /* read expiration */
+ p = strtok_r(NULL, "|", &savep);
+ if (p == NULL)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ expiration = (time_t)atol(p);
+ if (expiration > 0 && now > expiration)
+ return gnutls_assert_val(GNUTLS_E_EXPIRED);
+
+ /* read hash algorithm */
+ p = strtok_r(NULL, "|", &savep);
+ if (p == NULL)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ hash_algo = (time_t)atol(p);
+ if (_gnutls_digest_get_name(hash_algo) == NULL)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ /* read hash */
+ kp = strtok_r(NULL, "|", &savep);
+ if (kp == NULL)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ p = strpbrk(kp, "\n \r\t|");
+ if (p != NULL) *p = 0;
+
+ /* hash and hex encode */
+ ret = _gnutls_hash_fast (hash_algo, skey->data, skey->size, phash);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ phash_size = _gnutls_hash_get_algo_len(hash_algo);
+
+ p = _gnutls_bin2hex (phash, phash_size,(void*) hphash,
+ sizeof(hphash), NULL);
+ if (p == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ kp_len = strlen(kp);
+ if (kp_len != phash_size*2)
+ return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH);
+
+ if (memcmp(kp, hphash, kp_len) != 0)
+ return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH);
+
+ /* key found and matches */
+ return 0;
+}
+
+
+static int parse_line(char* line,
+ const char* host, size_t host_len,
+ const char* service, size_t service_len,
+ time_t now,
+ const gnutls_datum_t *rawkey,
+ const gnutls_datum_t *b64key)
+{
+char* p, *kp;
+char* savep = NULL;
+size_t kp_len;
+time_t expiration;
+
+ /* read version */
+ p = strtok_r(line, "|", &savep);
+ if (p == NULL)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ if (strncmp(p, "c0", 2) == 0)
+ return parse_commitment_line(p+3, host, host_len, service, service_len, now, rawkey);
+
+ if (strncmp(p, "g0", 2) != 0)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ /* read host */
+ p = strtok_r(NULL, "|", &savep);
+ if (p == NULL)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ if (p[0] != '*' && strcmp(p, host) != 0)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ /* read service */
+ p = strtok_r(NULL, "|", &savep);
+ if (p == NULL)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ if (p[0] != '*' && strcmp(p, service) != 0)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ /* read expiration */
+ p = strtok_r(NULL, "|", &savep);
+ if (p == NULL)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ expiration = (time_t)atol(p);
+ if (expiration > 0 && now > expiration)
+ return gnutls_assert_val(GNUTLS_E_EXPIRED);
+
+ /* read key */
+ kp = strtok_r(NULL, "|", &savep);
+ if (kp == NULL)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ p = strpbrk(kp, "\n \r\t|");
+ if (p != NULL) *p = 0;
+
+ kp_len = strlen(kp);
+ if (kp_len != b64key->size)
+ return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH);
+
+ if (memcmp(kp, b64key->data, b64key->size) != 0)
+ return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH);
+
+ /* key found and matches */
+ return 0;
+}
+
+/* Returns the base64 key if found
+ */
+static int find_stored_pubkey(const char* file,
+ const char* host, const char* service,
+ const gnutls_datum_t* pubkey)
+{
+FILE* fd;
+char* line = NULL;
+size_t line_size = 0;
+int ret, l2, mismatch = 0;
+size_t host_len = 0, service_len = 0;
+time_t now = gnutls_time(0);
+gnutls_datum_t b64key = { NULL, 0 };
+
+ ret = raw_pubkey_to_base64(pubkey, &b64key);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (host != NULL) host_len = strlen(host);
+ if (service != NULL) service_len = strlen(service);
+
+ fd = fopen(file, "rb");
+ if (fd == NULL)
+ {
+ ret = gnutls_assert_val(GNUTLS_E_FILE_ERROR);
+ goto cleanup;
+ }
+
+ do
+ {
+ l2 = getline(&line, &line_size, fd);
+ if (l2 > 0)
+ {
+ ret = parse_line(line, host, host_len, service, service_len, now, pubkey, &b64key);
+ if (ret == 0) /* found */
+ {
+ goto cleanup;
+ }
+ else if (ret == GNUTLS_E_CERTIFICATE_KEY_MISMATCH)
+ mismatch = 1;
+ }
+ }
+ while(l2 >= 0);
+
+ if (mismatch)
+ ret = GNUTLS_E_CERTIFICATE_KEY_MISMATCH;
+ else
+ ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
+
+cleanup:
+ free(line);
+ if (fd != NULL)
+ fclose(fd);
+ gnutls_free(b64key.data);
+
+ return ret;
+}
+
+static int raw_pubkey_to_base64(const gnutls_datum_t* raw, gnutls_datum_t * b64)
+{
+ int ret;
+ char* out;
+
+ ret = base64_encode_alloc((void*)raw->data, raw->size, &out);
+ if (ret == 0)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ b64->data = (void*)out;
+ b64->size = ret;
+
+ return 0;
+}
+
+static int x509_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t *rpubkey)
+{
+gnutls_x509_crt_t crt = NULL;
+gnutls_pubkey_t pubkey = NULL;
+size_t size;
+int ret;
+
+ ret = gnutls_x509_crt_init(&crt);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0)
+ {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_crt_import(crt, cert, GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pubkey_import_x509 (pubkey, crt, 0);
+ if (ret < 0)
+ {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ size = 0;
+ ret = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, NULL, &size);
+ if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+ {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ rpubkey->data = gnutls_malloc(size);
+ if (rpubkey->data == NULL)
+ if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+ {
+ ret = GNUTLS_E_MEMORY_ERROR;
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, rpubkey->data, &size);
+ if (ret < 0)
+ {
+ gnutls_free(rpubkey->data);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ rpubkey->size = size;
+ ret = 0;
+
+cleanup:
+ gnutls_x509_crt_deinit(crt);
+ gnutls_pubkey_deinit(pubkey);
+
+ return ret;
+}
+
+static int pgp_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t *rpubkey)
+{
+gnutls_openpgp_crt_t crt = NULL;
+gnutls_pubkey_t pubkey = NULL;
+size_t size;
+int ret;
+
+ ret = gnutls_openpgp_crt_init(&crt);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0)
+ {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_openpgp_crt_import(crt, cert, GNUTLS_OPENPGP_FMT_RAW);
+ if (ret < 0)
+ {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pubkey_import_openpgp (pubkey, crt, 0);
+ if (ret < 0)
+ {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ size = 0;
+ ret = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, NULL, &size);
+ if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+ {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ rpubkey->data = gnutls_malloc(size);
+ if (rpubkey->data == NULL)
+ if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+ {
+ ret = GNUTLS_E_MEMORY_ERROR;
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, rpubkey->data, &size);
+ if (ret < 0)
+ {
+ gnutls_free(rpubkey->data);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ rpubkey->size = size;
+ ret = 0;
+
+cleanup:
+ gnutls_openpgp_crt_deinit(crt);
+ gnutls_pubkey_deinit(pubkey);
+
+ return ret;
+}
+
+static
+int store_pubkey(const char* db_name, const char* host,
+ const char* service, time_t expiration,
+ const gnutls_datum_t* pubkey)
+{
+FILE* fd = NULL;
+gnutls_datum_t b64key;
+int ret;
+
+ ret = raw_pubkey_to_base64(pubkey, &b64key);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ fd = fopen(db_name, "ab+");
+ if (fd == NULL)
+ {
+ ret = gnutls_assert_val(GNUTLS_E_FILE_ERROR);
+ goto cleanup;
+ }
+
+ if (service == NULL) service = "*";
+ if (host == NULL) host = "*";
+
+ fprintf(fd, "|g0|%s|%s|%lu|%.*s\n", host, service, (unsigned long)expiration,
+ pubkey->size, pubkey->data);
+
+ ret = 0;
+
+cleanup:
+ if (fd != NULL)
+ fclose(fd);
+ gnutls_free(b64key.data);
+
+ return ret;
+}
+
+static
+int store_commitment(const char* db_name, const char* host,
+ const char* service, time_t expiration,
+ gnutls_digest_algorithm_t hash_algo,
+ const gnutls_datum_t* hash)
+{
+FILE* fd;
+char buffer[MAX_HASH_SIZE*2+1];
+
+ fd = fopen(db_name, "ab+");
+ if (fd == NULL)
+ return gnutls_assert_val(GNUTLS_E_FILE_ERROR);
+
+ if (service == NULL) service = "*";
+ if (host == NULL) host = "*";
+
+ fprintf(fd, "|c0|%s|%s|%lu|%u|%s\n", host, service, (unsigned long)expiration,
+ (unsigned)hash_algo, _gnutls_bin2hex(hash->data, hash->size, buffer, sizeof(buffer), NULL));
+
+ fclose(fd);
+
+ return 0;
+}
+
+/**
+ * gnutls_store_pubkey:
+ * @db_name: A file specifying the stored keys (use NULL for the default)
+ * @tdb: A database structure or NULL to use the default
+ * @host: The peer's name
+ * @service: non-NULL if this key is specific to a service (e.g. http)
+ * @cert_type: The type of the certificate
+ * @cert: The data of the certificate
+ * @expiration: The expiration time (use 0 to disable expiration)
+ * @flags: should be 0.
+ *
+ * This function will store the provided certificate to
+ * the list of stored public keys. The key will be considered valid until
+ * the provided expiration time.
+ *
+ * The @tdb variable if non-null specifies a custom backend for
+ * the storage and retrieval of entries. If it is NULL then the
+ * default file backend will be used.
+ *
+ * Note that this function is not thread safe with the default backend.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.0.0
+ **/
+int
+gnutls_store_pubkey(const char* db_name,
+ const trust_storage_st* tdb,
+ const char* host,
+ const char* service,
+ gnutls_certificate_type_t cert_type,
+ const gnutls_datum_t * cert,
+ time_t expiration,
+ unsigned int flags)
+{
+FILE* fd = NULL;
+gnutls_datum_t pubkey = { NULL, 0 };
+int ret;
+char local_file[MAX_FILENAME];
+
+ if (cert_type != GNUTLS_CRT_X509 && cert_type != GNUTLS_CRT_OPENPGP)
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
+
+ if (db_name == NULL && tdb == NULL)
+ {
+ ret = _gnutls_find_config_path(local_file, sizeof(local_file));
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ _gnutls_debug_log("Configuration path: %s\n", local_file);
+ mkdir(local_file, 0700);
+
+ ret = find_config_file(local_file, sizeof(local_file));
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ db_name = local_file;
+ }
+
+ if (tdb == NULL)
+ tdb = &default_storage;
+
+ if (cert_type == GNUTLS_CRT_X509)
+ ret = x509_crt_to_raw_pubkey(cert, &pubkey);
+ else
+ ret = pgp_crt_to_raw_pubkey(cert, &pubkey);
+ if (ret < 0)
+ {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_debug_log("Configuration file: %s\n", db_name);
+
+ tdb->store(db_name, host, service, expiration, &pubkey);
+
+ ret = 0;
+
+cleanup:
+ gnutls_free(pubkey.data);
+ if (fd != NULL) fclose(fd);
+
+ return ret;
+}
+
+/**
+ * gnutls_store_commitment:
+ * @db_name: A file specifying the stored keys (use NULL for the default)
+ * @tdb: A database structure or NULL to use the default
+ * @host: The peer's name
+ * @service: non-NULL if this key is specific to a service (e.g. http)
+ * @hash_algo: The hash algorithm type
+ * @hash: The raw hash
+ * @expiration: The expiration time (use 0 to disable expiration)
+ * @flags: should be 0.
+ *
+ * This function will store the provided hash commitment to
+ * the list of stored public keys. The key with the given
+ * hash will be considered valid until the provided expiration time.
+ *
+ * The @tdb variable if non-null specifies a custom backend for
+ * the storage and retrieval of entries. If it is NULL then the
+ * default file backend will be used.
+ *
+ * Note that this function is not thread safe with the default backend.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.0.0
+ **/
+int
+gnutls_store_commitment(const char* db_name,
+ const trust_storage_st* tdb,
+ const char* host,
+ const char* service,
+ gnutls_digest_algorithm_t hash_algo,
+ const gnutls_datum_t* hash,
+ time_t expiration,
+ unsigned int flags)
+{
+FILE* fd = NULL;
+int ret;
+char local_file[MAX_FILENAME];
+
+ if (hash_algo == GNUTLS_DIG_MD5 || hash_algo == GNUTLS_DIG_MD2)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ if (_gnutls_hash_get_algo_len(hash_algo) != hash->size)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (db_name == NULL && tdb == NULL)
+ {
+ ret = _gnutls_find_config_path(local_file, sizeof(local_file));
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ _gnutls_debug_log("Configuration path: %s\n", local_file);
+ mkdir(local_file, 0700);
+
+ ret = find_config_file(local_file, sizeof(local_file));
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ db_name = local_file;
+ }
+
+ if (tdb == NULL)
+ tdb = &default_storage;
+
+ _gnutls_debug_log("Configuration file: %s\n", db_name);
+
+ tdb->cstore(db_name, host, service, expiration, hash_algo, hash);
+
+ ret = 0;
+
+ if (fd != NULL) fclose(fd);
+
+ return ret;
+}
+
+#define CONFIG_FILE "known_hosts"
+
+static int find_config_file(char* file, size_t max_size)
+{
+char path[MAX_FILENAME];
+int ret;
+
+ ret = _gnutls_find_config_path(path, sizeof(path));
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (path[0] == 0)
+ snprintf(file, max_size, "%s", CONFIG_FILE);
+ else
+ snprintf(file, max_size, "%s/%s", path, CONFIG_FILE);
+
+ return 0;
+}