diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2012-02-10 12:30:23 +0100 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2012-02-10 12:31:40 +0100 |
commit | 5196d292991628627e4ef7cc977b84f1273fe233 (patch) | |
tree | a96dee61a78ee9c160083b6a152574101ca0f20e | |
parent | 38f9998e9aaee0e8a486cad3f715fad4558a88df (diff) | |
download | gnutls-5196d292991628627e4ef7cc977b84f1273fe233.tar.gz |
gnutls_verify_stored_pubkey() and gnutls_store_pubkey() allow for alternative storage back-end.
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | doc/examples/ex-verify-ssh.c | 5 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 29 | ||||
-rw-r--r-- | lib/verify-ssh.c | 93 | ||||
-rw-r--r-- | src/cli-args.def | 6 | ||||
-rw-r--r-- | src/cli.c | 10 |
6 files changed, 107 insertions, 38 deletions
@@ -7,7 +7,7 @@ See the end for copying conditions. ** gnutls-cli: added the --ocsp option which will verify the peer's certificate with OCSP. -** gnutls-cli: added the --ssh and if specified, gnutls-cli +** gnutls-cli: added the --tofu and if specified, gnutls-cli will use an ssh-style authentication method. ** gnutls-cli: if no --x509cafile is provided a default is diff --git a/doc/examples/ex-verify-ssh.c b/doc/examples/ex-verify-ssh.c index 4816a21c91..dd983ce5fb 100644 --- a/doc/examples/ex-verify-ssh.c +++ b/doc/examples/ex-verify-ssh.c @@ -93,7 +93,7 @@ _ssh_verify_certificate_callback (gnutls_session_t session) gnutls_x509_crt_deinit (cert); /* service may be obtained alternatively using getservbyport() */ - ret = gnutls_verify_stored_pubkey(NULL, hostname, "https", + ret = gnutls_verify_stored_pubkey(NULL, NULL, hostname, "https", GNUTLS_CRT_X509, &cert_list[0], 0); if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND) { @@ -129,7 +129,8 @@ _ssh_verify_certificate_callback (gnutls_session_t session) /* user trusts the key -> store it */ if (ret != 0) { - ret = gnutls_store_pubkey(NULL, hostname, "https", GNUTLS_CRT_X509, &cert_list[0], + ret = gnutls_store_pubkey(NULL, NULL, hostname, "https", + GNUTLS_CRT_X509, &cert_list[0], 0, 0); if (ret < 0) fprintf(stderr, "gnutls_store_pubkey: %s\n", gnutls_strerror(ret)); diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index f5adbf5863..183ac946bd 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -1656,14 +1656,37 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get(gnutls_session_t session); int gnutls_hex2bin (const char *hex_data, size_t hex_size, void *bin_data, size_t * bin_size); - /* ssh style functions */ - int gnutls_verify_stored_pubkey(const char* file, + /* Trust on first use (or ssh like) functions */ + + /* stores the provided information to a database + */ + typedef int (*gnutls_trust_db_store_func) (const char* db_name, + const char* host, + const char* service, + time_t expiration, + const gnutls_datum_t* pubkey); + + /* searches for the provided host/service pair that match the + * provided public key in the database. */ + typedef int (*gnutls_trust_db_retr_func) (const char* db_name, + const char* host, + const char* service, + const gnutls_datum_t *pubkey); + + typedef struct { + gnutls_trust_db_store_func store; + gnutls_trust_db_retr_func retrieve; + } trust_storage_st; + + 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); - int gnutls_store_pubkey(const char* file, + 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, diff --git a/lib/verify-ssh.c b/lib/verify-ssh.c index 23ac819e62..4d085a18d5 100644 --- a/lib/verify-ssh.c +++ b/lib/verify-ssh.c @@ -41,12 +41,23 @@ static int pgp_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t *rp static int find_stored_pubkey(const char* file, const char* host, const char* service, const gnutls_datum_t* skey); +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, + find_stored_pubkey +}; + /** * gnutls_verify_stored_pubkey: - * @file: A file specifying the stored keys (use NULL for the default) + * @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 @@ -57,6 +68,17 @@ static int find_config_file(char* file, size_t max_size); * 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 back-end for + * the storage and retrieval of entries. If it is NULL then the + * default file back-end will be used. + * + * Note that if the custom storage back-end 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 @@ -66,7 +88,8 @@ static int find_config_file(char* file, size_t max_size); * Since: 3.0.0 **/ int -gnutls_verify_stored_pubkey(const char* file, +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, @@ -79,14 +102,17 @@ 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 (file == NULL) + if (db_name == NULL && tdb == NULL) { ret = find_config_file(local_file, sizeof(local_file)); if (ret < 0) return gnutls_assert_val(ret); - file = local_file; + 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 @@ -105,10 +131,9 @@ char local_file[MAX_FILENAME]; goto cleanup; } - ret = find_stored_pubkey(file, host, service, &pubkey); + 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); @@ -376,9 +401,32 @@ cleanup: 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; + + 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, "|g0|%s|%s|%lu|%.*s\n", host, service, (unsigned long)expiration, + pubkey->size, pubkey->data); + + fclose(fd); + + return 0; +} + /** * gnutls_store_pubkey: - * @file: A file specifying the stored keys (use NULL for the default) + * @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 @@ -390,7 +438,11 @@ cleanup: * the list of stored public keys. The key will be considered valid until * the provided expiration time. * - * Note that this function is not thread safe. + * The @tdb variable if non-null specifies a custom back-end for + * the storage and retrieval of entries. If it is NULL then the + * default file back-end 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. @@ -398,7 +450,8 @@ cleanup: * Since: 3.0.0 **/ int -gnutls_store_pubkey(const char* file, +gnutls_store_pubkey(const char* db_name, + const trust_storage_st* tdb, const char* host, const char* service, gnutls_certificate_type_t cert_type, @@ -414,7 +467,7 @@ 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 (file == NULL) + if (db_name == NULL && tdb == NULL) { ret = _gnutls_find_config_path(local_file, sizeof(local_file)); if (ret < 0) @@ -426,8 +479,11 @@ char local_file[MAX_FILENAME]; ret = find_config_file(local_file, sizeof(local_file)); if (ret < 0) return gnutls_assert_val(ret); - file = local_file; + db_name = local_file; } + + if (tdb == NULL) + tdb = &default_storage; if (cert_type == GNUTLS_CRT_X509) ret = x509_crt_to_raw_pubkey(cert, &pubkey); @@ -446,20 +502,9 @@ char local_file[MAX_FILENAME]; goto cleanup; } - _gnutls_debug_log("Configuration file: %s\n", file); + _gnutls_debug_log("Configuration file: %s\n", db_name); - fd = fopen(file, "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); + tdb->store(db_name, host, service, expiration, &pubkey); ret = 0; diff --git a/src/cli-args.def b/src/cli-args.def index 7f18026315..c0d29a6b9a 100644 --- a/src/cli-args.def +++ b/src/cli-args.def @@ -37,11 +37,11 @@ flag = { }; flag = { - name = ssh; - descrip = "Enable SSH-style authentication"; + name = tofu; + descrip = "Enable trust on first use (SSH-style) authentication"; disabled; disable = "no"; - doc = "This option will, in addition to certificate authentication, perform authentication based on stored public keys."; + doc = "This option will, in addition to certificate authentication, perform authentication based on previously seen public keys."; }; flag = { @@ -475,7 +475,7 @@ cert_verify_callback (gnutls_session_t session) { int rc; unsigned int status = 0; - int ssh = ENABLED_OPT(SSH); + int ssh = ENABLED_OPT(TOFU); const char* txt_service; if (!x509_cafile && !pgp_keyring) @@ -515,8 +515,8 @@ cert_verify_callback (gnutls_session_t session) return -1; } - rc = gnutls_verify_stored_pubkey(NULL, hostname, txt_service, GNUTLS_CRT_X509, - cert, 0); + rc = gnutls_verify_stored_pubkey(NULL, NULL, hostname, txt_service, + GNUTLS_CRT_X509, cert, 0); if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND) { print_cert_info_compact(session); @@ -548,8 +548,8 @@ cert_verify_callback (gnutls_session_t session) if (rc != 0) { - rc = gnutls_store_pubkey(NULL, hostname, txt_service, GNUTLS_CRT_X509, - cert, 0, 0); + rc = gnutls_store_pubkey(NULL, NULL, hostname, txt_service, + GNUTLS_CRT_X509, cert, 0, 0); if (rc < 0) fprintf(stderr, "Could not store key: %s\n", gnutls_strerror(rc)); } |