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>
34 #include <gnutls/abstract.h>
38 struct gnutls_tdb_int
{
39 gnutls_tdb_store_func store
;
40 gnutls_tdb_store_commitment_func cstore
;
41 gnutls_tdb_verify_func verify
;
44 static int raw_pubkey_to_base64(const gnutls_datum_t
* raw
, gnutls_datum_t
* b64
);
45 static int x509_crt_to_raw_pubkey(const gnutls_datum_t
* cert
, gnutls_datum_t
*rpubkey
);
46 static int pgp_crt_to_raw_pubkey(const gnutls_datum_t
* cert
, gnutls_datum_t
*rpubkey
);
47 static int verify_pubkey(const char* file
,
48 const char* host
, const char* service
,
49 const gnutls_datum_t
* skey
);
52 int store_commitment(const char* db_name
, const char* host
,
53 const char* service
, time_t expiration
,
54 gnutls_digest_algorithm_t hash_algo
,
55 const gnutls_datum_t
* hash
);
57 int store_pubkey(const char* db_name
, const char* host
,
58 const char* service
, time_t expiration
, const gnutls_datum_t
* pubkey
);
60 static int find_config_file(char* file
, size_t max_size
);
61 #define MAX_FILENAME 512
63 void *_gnutls_file_mutex
;
65 struct gnutls_tdb_int default_tdb
= {
73 * gnutls_verify_stored_pubkey:
74 * @db_name: A file specifying the stored keys (use NULL for the default)
75 * @tdb: A storage structure or NULL to use the default
76 * @host: The peer's name
77 * @service: non-NULL if this key is specific to a service (e.g. http)
78 * @cert_type: The type of the certificate
79 * @cert: The raw (der) data of the certificate
80 * @flags: should be 0.
82 * This function will try to verify the provided certificate using
83 * a list of stored public keys. The @service field if non-NULL should
86 * The @retrieve variable if non-null specifies a custom backend for
87 * the retrieval of entries. If it is NULL then the
88 * default file backend will be used. In POSIX-like systems the
89 * file backend uses the $HOME/.gnutls/known_hosts file.
91 * Note that if the custom storage backend is provided the
92 * retrieval function should return %GNUTLS_E_CERTIFICATE_KEY_MISMATCH
93 * if the host/service pair is found but key doesn't match,
94 * %GNUTLS_E_NO_CERTIFICATE_FOUND if no such host/service with
95 * the given key is found, and 0 if it was found. The storage
96 * function should return 0 on success.
98 * Returns: If no associated public key is found
99 * then %GNUTLS_E_NO_CERTIFICATE_FOUND will be returned. If a key
100 * is found but does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH
101 * is returned. On success, %GNUTLS_E_SUCCESS (0) is returned,
102 * or a negative error value on other errors.
107 gnutls_verify_stored_pubkey(const char* db_name
,
111 gnutls_certificate_type_t cert_type
,
112 const gnutls_datum_t
* cert
, unsigned int flags
)
114 gnutls_datum_t pubkey
= { NULL
, 0 };
116 char local_file
[MAX_FILENAME
];
118 if (cert_type
!= GNUTLS_CRT_X509
&& cert_type
!= GNUTLS_CRT_OPENPGP
)
119 return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE
);
121 if (db_name
== NULL
&& tdb
== NULL
)
123 ret
= find_config_file(local_file
, sizeof(local_file
));
125 return gnutls_assert_val(ret
);
126 db_name
= local_file
;
132 if (cert_type
== GNUTLS_CRT_X509
)
133 ret
= x509_crt_to_raw_pubkey(cert
, &pubkey
);
135 ret
= pgp_crt_to_raw_pubkey(cert
, &pubkey
);
143 ret
= tdb
->verify(db_name
, host
, service
, &pubkey
);
144 if (ret
< 0 && ret
!= GNUTLS_E_CERTIFICATE_KEY_MISMATCH
)
145 ret
= gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND
);
148 gnutls_free(pubkey
.data
);
152 static int parse_commitment_line(char* line
,
153 const char* host
, size_t host_len
,
154 const char* service
, size_t service_len
,
156 const gnutls_datum_t
*skey
)
160 size_t kp_len
, phash_size
;
163 gnutls_digest_algorithm_t hash_algo
;
164 uint8_t phash
[MAX_HASH_SIZE
];
165 uint8_t hphash
[MAX_HASH_SIZE
*2+1];
168 p
= strtok_r(line
, "|", &savep
);
170 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
172 if (p
[0] != '*' && strcmp(p
, host
) != 0)
173 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
176 p
= strtok_r(NULL
, "|", &savep
);
178 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
180 if (p
[0] != '*' && strcmp(p
, service
) != 0)
181 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
183 /* read expiration */
184 p
= strtok_r(NULL
, "|", &savep
);
186 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
188 expiration
= (time_t)atol(p
);
189 if (expiration
> 0 && now
> expiration
)
190 return gnutls_assert_val(GNUTLS_E_EXPIRED
);
192 /* read hash algorithm */
193 p
= strtok_r(NULL
, "|", &savep
);
195 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
197 hash_algo
= (time_t)atol(p
);
198 if (_gnutls_digest_get_name(hash_algo
) == NULL
)
199 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
202 kp
= strtok_r(NULL
, "|", &savep
);
204 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
206 p
= strpbrk(kp
, "\n \r\t|");
207 if (p
!= NULL
) *p
= 0;
209 /* hash and hex encode */
210 ret
= _gnutls_hash_fast (hash_algo
, skey
->data
, skey
->size
, phash
);
212 return gnutls_assert_val(ret
);
214 phash_size
= _gnutls_hash_get_algo_len(hash_algo
);
216 p
= _gnutls_bin2hex (phash
, phash_size
,(void*) hphash
,
217 sizeof(hphash
), NULL
);
219 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR
);
222 if (kp_len
!= phash_size
*2)
223 return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH
);
225 if (memcmp(kp
, hphash
, kp_len
) != 0)
226 return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH
);
228 /* key found and matches */
233 static int parse_line(char* line
,
234 const char* host
, size_t host_len
,
235 const char* service
, size_t service_len
,
237 const gnutls_datum_t
*rawkey
,
238 const gnutls_datum_t
*b64key
)
246 p
= strtok_r(line
, "|", &savep
);
248 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
250 if (strncmp(p
, "c0", 2) == 0)
251 return parse_commitment_line(p
+3, host
, host_len
, service
, service_len
, now
, rawkey
);
253 if (strncmp(p
, "g0", 2) != 0)
254 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
257 p
= strtok_r(NULL
, "|", &savep
);
259 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
261 if (p
[0] != '*' && strcmp(p
, host
) != 0)
262 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
265 p
= strtok_r(NULL
, "|", &savep
);
267 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
269 if (p
[0] != '*' && strcmp(p
, service
) != 0)
270 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
272 /* read expiration */
273 p
= strtok_r(NULL
, "|", &savep
);
275 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
277 expiration
= (time_t)atol(p
);
278 if (expiration
> 0 && now
> expiration
)
279 return gnutls_assert_val(GNUTLS_E_EXPIRED
);
282 kp
= strtok_r(NULL
, "|", &savep
);
284 return gnutls_assert_val(GNUTLS_E_PARSING_ERROR
);
286 p
= strpbrk(kp
, "\n \r\t|");
287 if (p
!= NULL
) *p
= 0;
290 if (kp_len
!= b64key
->size
)
291 return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH
);
293 if (memcmp(kp
, b64key
->data
, b64key
->size
) != 0)
294 return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH
);
296 /* key found and matches */
300 /* Returns the base64 key if found
302 static int verify_pubkey(const char* file
,
303 const char* host
, const char* service
,
304 const gnutls_datum_t
* pubkey
)
308 size_t line_size
= 0;
309 int ret
, l2
, mismatch
= 0;
310 size_t host_len
= 0, service_len
= 0;
311 time_t now
= gnutls_time(0);
312 gnutls_datum_t b64key
= { NULL
, 0 };
314 ret
= raw_pubkey_to_base64(pubkey
, &b64key
);
316 return gnutls_assert_val(ret
);
318 if (host
!= NULL
) host_len
= strlen(host
);
319 if (service
!= NULL
) service_len
= strlen(service
);
321 fd
= fopen(file
, "rb");
324 ret
= gnutls_assert_val(GNUTLS_E_FILE_ERROR
);
330 l2
= getline(&line
, &line_size
, fd
);
333 ret
= parse_line(line
, host
, host_len
, service
, service_len
, now
, pubkey
, &b64key
);
334 if (ret
== 0) /* found */
338 else if (ret
== GNUTLS_E_CERTIFICATE_KEY_MISMATCH
)
345 ret
= GNUTLS_E_CERTIFICATE_KEY_MISMATCH
;
347 ret
= GNUTLS_E_NO_CERTIFICATE_FOUND
;
353 gnutls_free(b64key
.data
);
358 static int raw_pubkey_to_base64(const gnutls_datum_t
* raw
, gnutls_datum_t
* b64
)
363 ret
= base64_encode_alloc((void*)raw
->data
, raw
->size
, &out
);
365 return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR
);
367 b64
->data
= (void*)out
;
373 static int x509_crt_to_raw_pubkey(const gnutls_datum_t
* cert
, gnutls_datum_t
*rpubkey
)
375 gnutls_x509_crt_t crt
= NULL
;
376 gnutls_pubkey_t pubkey
= NULL
;
380 ret
= gnutls_x509_crt_init(&crt
);
382 return gnutls_assert_val(ret
);
384 ret
= gnutls_pubkey_init(&pubkey
);
391 ret
= gnutls_x509_crt_import(crt
, cert
, GNUTLS_X509_FMT_DER
);
398 ret
= gnutls_pubkey_import_x509 (pubkey
, crt
, 0);
406 ret
= gnutls_pubkey_export(pubkey
, GNUTLS_X509_FMT_DER
, NULL
, &size
);
407 if (ret
< 0 && ret
!= GNUTLS_E_SHORT_MEMORY_BUFFER
)
413 rpubkey
->data
= gnutls_malloc(size
);
414 if (rpubkey
->data
== NULL
)
415 if (ret
< 0 && ret
!= GNUTLS_E_SHORT_MEMORY_BUFFER
)
417 ret
= GNUTLS_E_MEMORY_ERROR
;
422 ret
= gnutls_pubkey_export(pubkey
, GNUTLS_X509_FMT_DER
, rpubkey
->data
, &size
);
425 gnutls_free(rpubkey
->data
);
430 rpubkey
->size
= size
;
434 gnutls_x509_crt_deinit(crt
);
435 gnutls_pubkey_deinit(pubkey
);
440 static int pgp_crt_to_raw_pubkey(const gnutls_datum_t
* cert
, gnutls_datum_t
*rpubkey
)
442 #ifdef ENABLE_OPENPGP
443 gnutls_openpgp_crt_t crt
= NULL
;
444 gnutls_pubkey_t pubkey
= NULL
;
448 ret
= gnutls_openpgp_crt_init(&crt
);
450 return gnutls_assert_val(ret
);
452 ret
= gnutls_pubkey_init(&pubkey
);
459 ret
= gnutls_openpgp_crt_import(crt
, cert
, GNUTLS_OPENPGP_FMT_RAW
);
466 ret
= gnutls_pubkey_import_openpgp (pubkey
, crt
, 0);
474 ret
= gnutls_pubkey_export(pubkey
, GNUTLS_X509_FMT_DER
, NULL
, &size
);
475 if (ret
< 0 && ret
!= GNUTLS_E_SHORT_MEMORY_BUFFER
)
481 rpubkey
->data
= gnutls_malloc(size
);
482 if (rpubkey
->data
== NULL
)
483 if (ret
< 0 && ret
!= GNUTLS_E_SHORT_MEMORY_BUFFER
)
485 ret
= GNUTLS_E_MEMORY_ERROR
;
490 ret
= gnutls_pubkey_export(pubkey
, GNUTLS_X509_FMT_DER
, rpubkey
->data
, &size
);
493 gnutls_free(rpubkey
->data
);
498 rpubkey
->size
= size
;
502 gnutls_openpgp_crt_deinit(crt
);
503 gnutls_pubkey_deinit(pubkey
);
507 return GNUTLS_E_UNIMPLEMENTED_FEATURE
;
512 int store_pubkey(const char* db_name
, const char* host
,
513 const char* service
, time_t expiration
,
514 const gnutls_datum_t
* pubkey
)
517 gnutls_datum_t b64key
= { NULL
, 0 };
520 ret
= gnutls_mutex_lock(&_gnutls_file_mutex
);
522 return gnutls_assert_val(GNUTLS_E_LOCKING_ERROR
);
524 ret
= raw_pubkey_to_base64(pubkey
, &b64key
);
531 fd
= fopen(db_name
, "ab+");
534 ret
= gnutls_assert_val(GNUTLS_E_FILE_ERROR
);
538 if (service
== NULL
) service
= "*";
539 if (host
== NULL
) host
= "*";
541 fprintf(fd
, "|g0|%s|%s|%lu|%.*s\n", host
, service
, (unsigned long)expiration
,
542 b64key
.size
, b64key
.data
);
550 gnutls_mutex_unlock(&_gnutls_file_mutex
);
551 gnutls_free(b64key
.data
);
557 int store_commitment(const char* db_name
, const char* host
,
558 const char* service
, time_t expiration
,
559 gnutls_digest_algorithm_t hash_algo
,
560 const gnutls_datum_t
* hash
)
563 char buffer
[MAX_HASH_SIZE
*2+1];
565 fd
= fopen(db_name
, "ab+");
567 return gnutls_assert_val(GNUTLS_E_FILE_ERROR
);
569 if (service
== NULL
) service
= "*";
570 if (host
== NULL
) host
= "*";
572 fprintf(fd
, "|c0|%s|%s|%lu|%u|%s\n", host
, service
, (unsigned long)expiration
,
573 (unsigned)hash_algo
, _gnutls_bin2hex(hash
->data
, hash
->size
, buffer
, sizeof(buffer
), NULL
));
581 * gnutls_store_pubkey:
582 * @db_name: A file specifying the stored keys (use NULL for the default)
583 * @tdb: A storage structure or NULL to use the default
584 * @host: The peer's name
585 * @service: non-NULL if this key is specific to a service (e.g. http)
586 * @cert_type: The type of the certificate
587 * @cert: The data of the certificate
588 * @expiration: The expiration time (use 0 to disable expiration)
589 * @flags: should be 0.
591 * This function will store the provided certificate to
592 * the list of stored public keys. The key will be considered valid until
593 * the provided expiration time.
595 * The @store variable if non-null specifies a custom backend for
596 * the storage of entries. If it is NULL then the
597 * default file backend will be used.
599 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
600 * negative error value.
605 gnutls_store_pubkey(const char* db_name
,
609 gnutls_certificate_type_t cert_type
,
610 const gnutls_datum_t
* cert
,
615 gnutls_datum_t pubkey
= { NULL
, 0 };
617 char local_file
[MAX_FILENAME
];
619 if (cert_type
!= GNUTLS_CRT_X509
&& cert_type
!= GNUTLS_CRT_OPENPGP
)
620 return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE
);
622 if (db_name
== NULL
&& tdb
== NULL
)
624 ret
= _gnutls_find_config_path(local_file
, sizeof(local_file
));
626 return gnutls_assert_val(ret
);
628 _gnutls_debug_log("Configuration path: %s\n", local_file
);
629 mkdir(local_file
, 0700);
631 ret
= find_config_file(local_file
, sizeof(local_file
));
633 return gnutls_assert_val(ret
);
634 db_name
= local_file
;
640 if (cert_type
== GNUTLS_CRT_X509
)
641 ret
= x509_crt_to_raw_pubkey(cert
, &pubkey
);
643 ret
= pgp_crt_to_raw_pubkey(cert
, &pubkey
);
650 _gnutls_debug_log("Configuration file: %s\n", db_name
);
652 tdb
->store(db_name
, host
, service
, expiration
, &pubkey
);
657 gnutls_free(pubkey
.data
);
658 if (fd
!= NULL
) fclose(fd
);
664 * gnutls_store_commitment:
665 * @db_name: A file specifying the stored keys (use NULL for the default)
666 * @tdb: A storage structure or NULL to use the default
667 * @host: The peer's name
668 * @service: non-NULL if this key is specific to a service (e.g. http)
669 * @hash_algo: The hash algorithm type
670 * @hash: The raw hash
671 * @expiration: The expiration time (use 0 to disable expiration)
672 * @flags: should be 0.
674 * This function will store the provided hash commitment to
675 * the list of stored public keys. The key with the given
676 * hash will be considered valid until the provided expiration time.
678 * The @store variable if non-null specifies a custom backend for
679 * the storage of entries. If it is NULL then the
680 * default file backend will be used.
682 * Note that this function is not thread safe with the default backend.
684 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
685 * negative error value.
690 gnutls_store_commitment(const char* db_name
,
694 gnutls_digest_algorithm_t hash_algo
,
695 const gnutls_datum_t
* hash
,
701 char local_file
[MAX_FILENAME
];
703 if (hash_algo
== GNUTLS_DIG_MD5
|| hash_algo
== GNUTLS_DIG_MD2
)
704 return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER
);
706 if (_gnutls_hash_get_algo_len(hash_algo
) != hash
->size
)
707 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST
);
709 if (db_name
== NULL
&& tdb
== NULL
)
711 ret
= _gnutls_find_config_path(local_file
, sizeof(local_file
));
713 return gnutls_assert_val(ret
);
715 _gnutls_debug_log("Configuration path: %s\n", local_file
);
716 mkdir(local_file
, 0700);
718 ret
= find_config_file(local_file
, sizeof(local_file
));
720 return gnutls_assert_val(ret
);
721 db_name
= local_file
;
727 _gnutls_debug_log("Configuration file: %s\n", db_name
);
729 tdb
->cstore(db_name
, host
, service
, expiration
, hash_algo
, hash
);
733 if (fd
!= NULL
) fclose(fd
);
738 #define CONFIG_FILE "known_hosts"
740 static int find_config_file(char* file
, size_t max_size
)
742 char path
[MAX_FILENAME
];
745 ret
= _gnutls_find_config_path(path
, sizeof(path
));
747 return gnutls_assert_val(ret
);
750 snprintf(file
, max_size
, "%s", CONFIG_FILE
);
752 snprintf(file
, max_size
, "%s/%s", path
, CONFIG_FILE
);
759 * @tdb: The structure to be initialized
761 * This function will initialize a public key trust storage structure.
763 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
764 * negative error value.
766 int gnutls_tdb_init(gnutls_tdb_t
* tdb
)
768 *tdb
= gnutls_calloc (1, sizeof (struct gnutls_tdb_int
));
771 return GNUTLS_E_MEMORY_ERROR
;
777 * gnutls_set_store_func:
778 * @tdb: The trust storage
779 * @store: The storage function
781 * This function will associate a storage function with the
782 * trust storage structure. The function is of the following form.
784 * gnutls_tdb_store_func(const char* db_name, const char* host,
785 * const char* service, time_t expiration,
786 * const gnutls_datum_t* pubkey);
789 void gnutls_tdb_set_store_func(gnutls_tdb_t tdb
, gnutls_tdb_store_func store
)
795 * gnutls_set_store_commitment_func:
796 * @tdb: The trust storage
797 * @cstore: The commitment storage function
799 * This function will associate a commitment (hash) storage function with the
800 * trust storage structure. The function is of the following form.
802 * gnutls_tdb_store_commitment_func(const char* db_name, const char* host,
803 * const char* service, time_t expiration,
804 * gnutls_digest_algorithm_t, const gnutls_datum_t* hash);
807 void gnutls_tdb_set_store_commitment_func(gnutls_tdb_t tdb
,
808 gnutls_tdb_store_commitment_func cstore
)
810 tdb
->cstore
= cstore
;
814 * gnutls_set_verify_func:
815 * @tdb: The trust storage
816 * @verify: The verification function
818 * This function will associate a retrieval function with the
819 * trust storage structure. The function is of the following form.
821 * gnutls_tdb_verify_func(const char* db_name, const char* host,
822 * const char* service, const gnutls_datum_t* pubkey);
825 void gnutls_tdb_set_verify_func(gnutls_tdb_t tdb
, gnutls_tdb_verify_func verify
)
827 tdb
->verify
= verify
;
832 * @tdb: The structure to be deinitialized
834 * This function will deinitialize a public key trust storage structure.
836 void gnutls_tdb_deinit(gnutls_tdb_t tdb
)