2 * Copyright (C) 2012 Free Software Foundation, Inc.
4 * Author: Nikos Mavrogiannopoulos
6 * This file is part of GnuTLS.
8 * The GnuTLS is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 3 of
11 * the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>
23 #include <gnutls_int.h>
24 #include <gnutls_errors.h>
26 #include <gnutls_global.h>
27 #include <gnutls_num.h> /* MAX */
28 #include <gnutls_sig.h>
29 #include <gnutls_str.h>
30 #include <gnutls_datum.h>
35 #include <gnutls/abstract.h>
39 struct gnutls_tdb_int
{
40 gnutls_tdb_store_func store
;
41 gnutls_tdb_store_commitment_func cstore
;
42 gnutls_tdb_verify_func verify
;
45 static int raw_pubkey_to_base64(const gnutls_datum_t
* raw
, gnutls_datum_t
* b64
);
46 static int x509_crt_to_raw_pubkey(const gnutls_datum_t
* cert
, gnutls_datum_t
*rpubkey
);
47 static int pgp_crt_to_raw_pubkey(const gnutls_datum_t
* cert
, gnutls_datum_t
*rpubkey
);
48 static int verify_pubkey(const char* file
,
49 const char* host
, const char* service
,
50 const gnutls_datum_t
* skey
);
53 int store_commitment(const char* db_name
, const char* host
,
54 const char* service
, time_t expiration
,
55 gnutls_digest_algorithm_t hash_algo
,
56 const gnutls_datum_t
* hash
);
58 int store_pubkey(const char* db_name
, const char* host
,
59 const char* service
, time_t expiration
, const gnutls_datum_t
* pubkey
);
61 static int find_config_file(char* file
, size_t max_size
);
62 #define MAX_FILENAME 512
64 void *_gnutls_file_mutex
;
66 struct gnutls_tdb_int default_tdb
= {
74 * gnutls_verify_stored_pubkey:
75 * @db_name: A file specifying the stored keys (use NULL for the default)
76 * @tdb: A storage structure or NULL to use the default
77 * @host: The peer's name
78 * @service: non-NULL if this key is specific to a service (e.g. http)
79 * @cert_type: The type of the certificate
80 * @cert: The raw (der) data of the certificate
81 * @flags: should be 0.
83 * This function will try to verify the provided certificate using
84 * a list of stored public keys. The @service field if non-NULL should
87 * The @retrieve variable if non-null specifies a custom backend for
88 * the retrieval of entries. If it is NULL then the
89 * default file backend will be used. In POSIX-like systems the
90 * file backend uses the $HOME/.gnutls/known_hosts file.
92 * Note that if the custom storage backend is provided the
93 * retrieval function should return %GNUTLS_E_CERTIFICATE_KEY_MISMATCH
94 * if the host/service pair is found but key doesn't match,
95 * %GNUTLS_E_NO_CERTIFICATE_FOUND if no such host/service with
96 * the given key is found, and 0 if it was found. The storage
97 * function should return 0 on success.
99 * Returns: If no associated public key is found
100 * then %GNUTLS_E_NO_CERTIFICATE_FOUND will be returned. If a key
101 * is found but does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH
102 * is returned. On success, %GNUTLS_E_SUCCESS (0) is returned,
103 * or a negative error value on other errors.
108 gnutls_verify_stored_pubkey(const char* db_name
,
112 gnutls_certificate_type_t cert_type
,
113 const gnutls_datum_t
* cert
, unsigned int flags
)
115 gnutls_datum_t pubkey
= { NULL
, 0 };
117 char local_file
[MAX_FILENAME
];
119 if (cert_type
!= GNUTLS_CRT_X509
&& cert_type
!= GNUTLS_CRT_OPENPGP
)
120 return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE
);
122 if (db_name
== NULL
&& tdb
== NULL
)
124 ret
= find_config_file(local_file
, sizeof(local_file
));
126 return gnutls_assert_val(ret
);
127 db_name
= local_file
;
133 if (cert_type
== GNUTLS_CRT_X509
)
134 ret
= x509_crt_to_raw_pubkey(cert
, &pubkey
);
136 ret
= pgp_crt_to_raw_pubkey(cert
, &pubkey
);
144 ret
= tdb
->verify(db_name
, host
, service
, &pubkey
);
145 if (ret
< 0 && ret
!= GNUTLS_E_CERTIFICATE_KEY_MISMATCH
)
146 ret
= gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND
);
149 gnutls_free(pubkey
.data
);
153 static int parse_commitment_line(char* line
,
154 const char* host
, size_t host_len
,
155 const char* service
, size_t service_len
,
157 const gnutls_datum_t
*skey
)
161 size_t kp_len
, phash_size
;
164 gnutls_digest_algorithm_t hash_algo
;
165 uint8_t phash
[MAX_HASH_SIZE
];
166 uint8_t hphash
[MAX_HASH_SIZE
*2+1];
169 p
= strtok_r(line
, "|", &savep
);
171 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
173 if (p
[0] != '*' && strcmp(p
, host
) != 0)
174 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
177 p
= strtok_r(NULL
, "|", &savep
);
179 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
181 if (p
[0] != '*' && strcmp(p
, service
) != 0)
182 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
184 /* read expiration */
185 p
= strtok_r(NULL
, "|", &savep
);
187 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
189 expiration
= (time_t)atol(p
);
190 if (expiration
> 0 && now
> expiration
)
191 return gnutls_assert_val(GNUTLS_E_EXPIRED
);
193 /* read hash algorithm */
194 p
= strtok_r(NULL
, "|", &savep
);
196 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
198 hash_algo
= (time_t)atol(p
);
199 if (_gnutls_digest_get_name(hash_algo
) == NULL
)
200 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
203 kp
= strtok_r(NULL
, "|", &savep
);
205 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
207 p
= strpbrk(kp
, "\n \r\t|");
208 if (p
!= NULL
) *p
= 0;
210 /* hash and hex encode */
211 ret
= _gnutls_hash_fast (hash_algo
, skey
->data
, skey
->size
, phash
);
213 return gnutls_assert_val(ret
);
215 phash_size
= _gnutls_hash_get_algo_len(hash_algo
);
217 p
= _gnutls_bin2hex (phash
, phash_size
,(void*) hphash
,
218 sizeof(hphash
), NULL
);
220 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR
);
223 if (kp_len
!= phash_size
*2)
224 return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH
);
226 if (memcmp(kp
, hphash
, kp_len
) != 0)
227 return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH
);
229 /* key found and matches */
234 static int parse_line(char* line
,
235 const char* host
, size_t host_len
,
236 const char* service
, size_t service_len
,
238 const gnutls_datum_t
*rawkey
,
239 const gnutls_datum_t
*b64key
)
247 p
= strtok_r(line
, "|", &savep
);
249 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
251 if (strncmp(p
, "c0", 2) == 0)
252 return parse_commitment_line(p
+3, host
, host_len
, service
, service_len
, now
, rawkey
);
254 if (strncmp(p
, "g0", 2) != 0)
255 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
258 p
= strtok_r(NULL
, "|", &savep
);
260 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
262 if (p
[0] != '*' && strcmp(p
, host
) != 0)
263 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
266 p
= strtok_r(NULL
, "|", &savep
);
268 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
270 if (p
[0] != '*' && strcmp(p
, service
) != 0)
271 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
273 /* read expiration */
274 p
= strtok_r(NULL
, "|", &savep
);
276 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
278 expiration
= (time_t)atol(p
);
279 if (expiration
> 0 && now
> expiration
)
280 return gnutls_assert_val(GNUTLS_E_EXPIRED
);
283 kp
= strtok_r(NULL
, "|", &savep
);
285 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
287 p
= strpbrk(kp
, "\n \r\t|");
288 if (p
!= NULL
) *p
= 0;
291 if (kp_len
!= b64key
->size
)
292 return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH
);
294 if (memcmp(kp
, b64key
->data
, b64key
->size
) != 0)
295 return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH
);
297 /* key found and matches */
301 /* Returns the base64 key if found
303 static int verify_pubkey(const char* file
,
304 const char* host
, const char* service
,
305 const gnutls_datum_t
* pubkey
)
309 size_t line_size
= 0;
310 int ret
, l2
, mismatch
= 0;
311 size_t host_len
= 0, service_len
= 0;
312 time_t now
= gnutls_time(0);
313 gnutls_datum_t b64key
= { NULL
, 0 };
315 ret
= raw_pubkey_to_base64(pubkey
, &b64key
);
317 return gnutls_assert_val(ret
);
319 if (host
!= NULL
) host_len
= strlen(host
);
320 if (service
!= NULL
) service_len
= strlen(service
);
322 fd
= fopen(file
, "rb");
325 ret
= gnutls_assert_val(GNUTLS_E_FILE_ERROR
);
331 l2
= getline(&line
, &line_size
, fd
);
334 ret
= parse_line(line
, host
, host_len
, service
, service_len
, now
, pubkey
, &b64key
);
335 if (ret
== 0) /* found */
339 else if (ret
== GNUTLS_E_CERTIFICATE_KEY_MISMATCH
)
346 ret
= GNUTLS_E_CERTIFICATE_KEY_MISMATCH
;
348 ret
= GNUTLS_E_NO_CERTIFICATE_FOUND
;
354 gnutls_free(b64key
.data
);
359 static int raw_pubkey_to_base64(const gnutls_datum_t
* raw
, gnutls_datum_t
* b64
)
364 ret
= base64_encode_alloc((void*)raw
->data
, raw
->size
, &out
);
366 return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR
);
368 b64
->data
= (void*)out
;
374 static int x509_crt_to_raw_pubkey(const gnutls_datum_t
* cert
, gnutls_datum_t
*rpubkey
)
376 gnutls_x509_crt_t crt
= NULL
;
377 gnutls_pubkey_t pubkey
= NULL
;
381 ret
= gnutls_x509_crt_init(&crt
);
383 return gnutls_assert_val(ret
);
385 ret
= gnutls_pubkey_init(&pubkey
);
392 ret
= gnutls_x509_crt_import(crt
, cert
, GNUTLS_X509_FMT_DER
);
399 ret
= gnutls_pubkey_import_x509 (pubkey
, crt
, 0);
407 ret
= gnutls_pubkey_export(pubkey
, GNUTLS_X509_FMT_DER
, NULL
, &size
);
408 if (ret
< 0 && ret
!= GNUTLS_E_SHORT_MEMORY_BUFFER
)
414 rpubkey
->data
= gnutls_malloc(size
);
415 if (rpubkey
->data
== NULL
)
416 if (ret
< 0 && ret
!= GNUTLS_E_SHORT_MEMORY_BUFFER
)
418 ret
= GNUTLS_E_MEMORY_ERROR
;
423 ret
= gnutls_pubkey_export(pubkey
, GNUTLS_X509_FMT_DER
, rpubkey
->data
, &size
);
426 gnutls_free(rpubkey
->data
);
431 rpubkey
->size
= size
;
435 gnutls_x509_crt_deinit(crt
);
436 gnutls_pubkey_deinit(pubkey
);
441 static int pgp_crt_to_raw_pubkey(const gnutls_datum_t
* cert
, gnutls_datum_t
*rpubkey
)
443 #ifdef ENABLE_OPENPGP
444 gnutls_openpgp_crt_t crt
= NULL
;
445 gnutls_pubkey_t pubkey
= NULL
;
449 ret
= gnutls_openpgp_crt_init(&crt
);
451 return gnutls_assert_val(ret
);
453 ret
= gnutls_pubkey_init(&pubkey
);
460 ret
= gnutls_openpgp_crt_import(crt
, cert
, GNUTLS_OPENPGP_FMT_RAW
);
467 ret
= gnutls_pubkey_import_openpgp (pubkey
, crt
, 0);
475 ret
= gnutls_pubkey_export(pubkey
, GNUTLS_X509_FMT_DER
, NULL
, &size
);
476 if (ret
< 0 && ret
!= GNUTLS_E_SHORT_MEMORY_BUFFER
)
482 rpubkey
->data
= gnutls_malloc(size
);
483 if (rpubkey
->data
== NULL
)
484 if (ret
< 0 && ret
!= GNUTLS_E_SHORT_MEMORY_BUFFER
)
486 ret
= GNUTLS_E_MEMORY_ERROR
;
491 ret
= gnutls_pubkey_export(pubkey
, GNUTLS_X509_FMT_DER
, rpubkey
->data
, &size
);
494 gnutls_free(rpubkey
->data
);
499 rpubkey
->size
= size
;
503 gnutls_openpgp_crt_deinit(crt
);
504 gnutls_pubkey_deinit(pubkey
);
508 return GNUTLS_E_UNIMPLEMENTED_FEATURE
;
513 int store_pubkey(const char* db_name
, const char* host
,
514 const char* service
, time_t expiration
,
515 const gnutls_datum_t
* pubkey
)
518 gnutls_datum_t b64key
= { NULL
, 0 };
521 ret
= gnutls_mutex_lock(&_gnutls_file_mutex
);
523 return gnutls_assert_val(GNUTLS_E_LOCKING_ERROR
);
525 ret
= raw_pubkey_to_base64(pubkey
, &b64key
);
532 fd
= fopen(db_name
, "ab+");
535 ret
= gnutls_assert_val(GNUTLS_E_FILE_ERROR
);
539 if (service
== NULL
) service
= "*";
540 if (host
== NULL
) host
= "*";
542 fprintf(fd
, "|g0|%s|%s|%lu|%.*s\n", host
, service
, (unsigned long)expiration
,
543 b64key
.size
, b64key
.data
);
551 gnutls_mutex_unlock(&_gnutls_file_mutex
);
552 gnutls_free(b64key
.data
);
558 int store_commitment(const char* db_name
, const char* host
,
559 const char* service
, time_t expiration
,
560 gnutls_digest_algorithm_t hash_algo
,
561 const gnutls_datum_t
* hash
)
564 char buffer
[MAX_HASH_SIZE
*2+1];
566 fd
= fopen(db_name
, "ab+");
568 return gnutls_assert_val(GNUTLS_E_FILE_ERROR
);
570 if (service
== NULL
) service
= "*";
571 if (host
== NULL
) host
= "*";
573 fprintf(fd
, "|c0|%s|%s|%lu|%u|%s\n", host
, service
, (unsigned long)expiration
,
574 (unsigned)hash_algo
, _gnutls_bin2hex(hash
->data
, hash
->size
, buffer
, sizeof(buffer
), NULL
));
582 * gnutls_store_pubkey:
583 * @db_name: A file specifying the stored keys (use NULL for the default)
584 * @tdb: A storage structure or NULL to use the default
585 * @host: The peer's name
586 * @service: non-NULL if this key is specific to a service (e.g. http)
587 * @cert_type: The type of the certificate
588 * @cert: The data of the certificate
589 * @expiration: The expiration time (use 0 to disable expiration)
590 * @flags: should be 0.
592 * This function will store the provided certificate to
593 * the list of stored public keys. The key will be considered valid until
594 * the provided expiration time.
596 * The @store variable if non-null specifies a custom backend for
597 * the storage of entries. If it is NULL then the
598 * default file backend will be used.
600 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
601 * negative error value.
606 gnutls_store_pubkey(const char* db_name
,
610 gnutls_certificate_type_t cert_type
,
611 const gnutls_datum_t
* cert
,
616 gnutls_datum_t pubkey
= { NULL
, 0 };
618 char local_file
[MAX_FILENAME
];
620 if (cert_type
!= GNUTLS_CRT_X509
&& cert_type
!= GNUTLS_CRT_OPENPGP
)
621 return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE
);
623 if (db_name
== NULL
&& tdb
== NULL
)
625 ret
= _gnutls_find_config_path(local_file
, sizeof(local_file
));
627 return gnutls_assert_val(ret
);
629 _gnutls_debug_log("Configuration path: %s\n", local_file
);
630 mkdir(local_file
, 0700);
632 ret
= find_config_file(local_file
, sizeof(local_file
));
634 return gnutls_assert_val(ret
);
635 db_name
= local_file
;
641 if (cert_type
== GNUTLS_CRT_X509
)
642 ret
= x509_crt_to_raw_pubkey(cert
, &pubkey
);
644 ret
= pgp_crt_to_raw_pubkey(cert
, &pubkey
);
651 _gnutls_debug_log("Configuration file: %s\n", db_name
);
653 tdb
->store(db_name
, host
, service
, expiration
, &pubkey
);
658 gnutls_free(pubkey
.data
);
659 if (fd
!= NULL
) fclose(fd
);
665 * gnutls_store_commitment:
666 * @db_name: A file specifying the stored keys (use NULL for the default)
667 * @tdb: A storage structure or NULL to use the default
668 * @host: The peer's name
669 * @service: non-NULL if this key is specific to a service (e.g. http)
670 * @hash_algo: The hash algorithm type
671 * @hash: The raw hash
672 * @expiration: The expiration time (use 0 to disable expiration)
673 * @flags: should be 0.
675 * This function will store the provided hash commitment to
676 * the list of stored public keys. The key with the given
677 * hash will be considered valid until the provided expiration time.
679 * The @store variable if non-null specifies a custom backend for
680 * the storage of entries. If it is NULL then the
681 * default file backend will be used.
683 * Note that this function is not thread safe with the default backend.
685 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
686 * negative error value.
691 gnutls_store_commitment(const char* db_name
,
695 gnutls_digest_algorithm_t hash_algo
,
696 const gnutls_datum_t
* hash
,
702 char local_file
[MAX_FILENAME
];
704 if (hash_algo
== GNUTLS_DIG_MD5
|| hash_algo
== GNUTLS_DIG_MD2
)
705 return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER
);
707 if (_gnutls_hash_get_algo_len(hash_algo
) != hash
->size
)
708 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST
);
710 if (db_name
== NULL
&& tdb
== NULL
)
712 ret
= _gnutls_find_config_path(local_file
, sizeof(local_file
));
714 return gnutls_assert_val(ret
);
716 _gnutls_debug_log("Configuration path: %s\n", local_file
);
717 mkdir(local_file
, 0700);
719 ret
= find_config_file(local_file
, sizeof(local_file
));
721 return gnutls_assert_val(ret
);
722 db_name
= local_file
;
728 _gnutls_debug_log("Configuration file: %s\n", db_name
);
730 tdb
->cstore(db_name
, host
, service
, expiration
, hash_algo
, hash
);
734 if (fd
!= NULL
) fclose(fd
);
739 #define CONFIG_FILE "known_hosts"
741 static int find_config_file(char* file
, size_t max_size
)
743 char path
[MAX_FILENAME
];
746 ret
= _gnutls_find_config_path(path
, sizeof(path
));
748 return gnutls_assert_val(ret
);
751 snprintf(file
, max_size
, "%s", CONFIG_FILE
);
753 snprintf(file
, max_size
, "%s/%s", path
, CONFIG_FILE
);
760 * @tdb: The structure to be initialized
762 * This function will initialize a public key trust storage structure.
764 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
765 * negative error value.
767 int gnutls_tdb_init(gnutls_tdb_t
* tdb
)
769 *tdb
= gnutls_calloc (1, sizeof (struct gnutls_tdb_int
));
772 return GNUTLS_E_MEMORY_ERROR
;
778 * gnutls_set_store_func:
779 * @tdb: The trust storage
780 * @store: The storage function
782 * This function will associate a storage function with the
783 * trust storage structure. The function is of the following form.
785 * gnutls_tdb_store_func(const char* db_name, const char* host,
786 * const char* service, time_t expiration,
787 * const gnutls_datum_t* pubkey);
790 void gnutls_tdb_set_store_func(gnutls_tdb_t tdb
, gnutls_tdb_store_func store
)
796 * gnutls_set_store_commitment_func:
797 * @tdb: The trust storage
798 * @cstore: The commitment storage function
800 * This function will associate a commitment (hash) storage function with the
801 * trust storage structure. The function is of the following form.
803 * gnutls_tdb_store_commitment_func(const char* db_name, const char* host,
804 * const char* service, time_t expiration,
805 * gnutls_digest_algorithm_t, const gnutls_datum_t* hash);
808 void gnutls_tdb_set_store_commitment_func(gnutls_tdb_t tdb
,
809 gnutls_tdb_store_commitment_func cstore
)
811 tdb
->cstore
= cstore
;
815 * gnutls_set_verify_func:
816 * @tdb: The trust storage
817 * @verify: The verification function
819 * This function will associate a retrieval function with the
820 * trust storage structure. The function is of the following form.
822 * gnutls_tdb_verify_func(const char* db_name, const char* host,
823 * const char* service, const gnutls_datum_t* pubkey);
826 void gnutls_tdb_set_verify_func(gnutls_tdb_t tdb
, gnutls_tdb_verify_func verify
)
828 tdb
->verify
= verify
;
833 * @tdb: The structure to be deinitialized
835 * This function will deinitialize a public key trust storage structure.
837 void gnutls_tdb_deinit(gnutls_tdb_t tdb
)