1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
33 #include <sys/types.h>
35 #include <glib/gstdio.h>
37 #include "camel-certdb.h"
38 #include "camel-file-utils.h"
39 #include "camel-win32.h"
41 #define CAMEL_CERTDB_VERSION 0x100
43 #define CAMEL_CERTDB_GET_PRIVATE(obj) \
44 (G_TYPE_INSTANCE_GET_PRIVATE \
45 ((obj), CAMEL_TYPE_CERTDB, CamelCertDBPrivate))
47 struct _CamelCertDBPrivate {
48 GMutex *db_lock; /* for the db hashtable/array */
49 GMutex *io_lock; /* load/save lock, for access to saved_count, etc */
50 GMutex *alloc_lock; /* for setting up and using allocators */
51 GMutex *ref_lock; /* for reffing/unreffing certs */
54 static gint certdb_header_load (CamelCertDB *certdb, FILE *istream);
55 static gint certdb_header_save (CamelCertDB *certdb, FILE *ostream);
56 static CamelCert *certdb_cert_load (CamelCertDB *certdb, FILE *istream);
57 static gint certdb_cert_save (CamelCertDB *certdb, CamelCert *cert, FILE *ostream);
58 static CamelCert *certdb_cert_new (CamelCertDB *certdb);
59 static void certdb_cert_free (CamelCertDB *certdb, CamelCert *cert);
61 static const gchar *cert_get_string (CamelCertDB *certdb, CamelCert *cert, gint string);
62 static void cert_set_string (CamelCertDB *certdb, CamelCert *cert, gint string, const gchar *value);
64 G_DEFINE_TYPE (CamelCertDB, camel_certdb, CAMEL_TYPE_OBJECT)
67 certdb_str_equal_casecmp (gconstpointer str1,
73 return g_ascii_strcasecmp (str1, str2) == 0;
77 certdb_finalize (GObject *object)
79 CamelCertDB *certdb = CAMEL_CERTDB (object);
80 CamelCertDBPrivate *priv;
82 priv = CAMEL_CERTDB_GET_PRIVATE (object);
84 if (certdb->flags & CAMEL_CERTDB_DIRTY)
85 camel_certdb_save (certdb);
87 camel_certdb_clear (certdb);
88 g_ptr_array_free (certdb->certs, TRUE);
89 g_hash_table_destroy (certdb->cert_hash);
91 g_free (certdb->filename);
93 if (certdb->cert_chunks)
94 camel_memchunk_destroy (certdb->cert_chunks);
96 g_mutex_free (priv->db_lock);
97 g_mutex_free (priv->io_lock);
98 g_mutex_free (priv->alloc_lock);
99 g_mutex_free (priv->ref_lock);
101 /* Chain up to parent's finalize() method. */
102 G_OBJECT_CLASS (camel_certdb_parent_class)->finalize (object);
106 camel_certdb_class_init (CamelCertDBClass *class)
108 GObjectClass *object_class;
110 g_type_class_add_private (class, sizeof (CamelCertDBPrivate));
112 object_class = G_OBJECT_CLASS (class);
113 object_class->finalize = certdb_finalize;
115 class->header_load = certdb_header_load;
116 class->header_save = certdb_header_save;
118 class->cert_new = certdb_cert_new;
119 class->cert_load = certdb_cert_load;
120 class->cert_save = certdb_cert_save;
121 class->cert_free = certdb_cert_free;
122 class->cert_get_string = cert_get_string;
123 class->cert_set_string = cert_set_string;
127 camel_certdb_init (CamelCertDB *certdb)
129 certdb->priv = CAMEL_CERTDB_GET_PRIVATE (certdb);
131 certdb->filename = NULL;
132 certdb->version = CAMEL_CERTDB_VERSION;
133 certdb->saved_certs = 0;
135 certdb->cert_size = sizeof (CamelCert);
137 certdb->cert_chunks = NULL;
139 certdb->certs = g_ptr_array_new ();
140 certdb->cert_hash = g_hash_table_new (g_str_hash, certdb_str_equal_casecmp);
142 certdb->priv->db_lock = g_mutex_new ();
143 certdb->priv->io_lock = g_mutex_new ();
144 certdb->priv->alloc_lock = g_mutex_new ();
145 certdb->priv->ref_lock = g_mutex_new ();
149 camel_certdb_new (void)
151 return g_object_new (CAMEL_TYPE_CERTDB, NULL);
154 static CamelCertDB *default_certdb = NULL;
155 static GStaticMutex default_certdb_lock = G_STATIC_MUTEX_INIT;
158 camel_certdb_set_default (CamelCertDB *certdb)
160 g_static_mutex_lock (&default_certdb_lock);
163 g_object_unref (default_certdb);
166 g_object_ref (certdb);
168 default_certdb = certdb;
170 g_static_mutex_unlock (&default_certdb_lock);
174 camel_certdb_get_default (void)
178 g_static_mutex_lock (&default_certdb_lock);
181 g_object_ref (default_certdb);
183 certdb = default_certdb;
185 g_static_mutex_unlock (&default_certdb_lock);
191 camel_certdb_set_filename (CamelCertDB *certdb,
192 const gchar *filename)
194 g_return_if_fail (CAMEL_IS_CERTDB (certdb));
195 g_return_if_fail (filename != NULL);
197 camel_certdb_lock (certdb, CAMEL_CERTDB_DB_LOCK);
199 g_free (certdb->filename);
200 certdb->filename = g_strdup (filename);
202 camel_certdb_unlock (certdb, CAMEL_CERTDB_DB_LOCK);
206 certdb_header_load (CamelCertDB *certdb,
209 if (camel_file_util_decode_uint32 (istream, &certdb->version) == -1)
211 if (camel_file_util_decode_uint32 (istream, &certdb->saved_certs) == -1)
218 certdb_cert_load (CamelCertDB *certdb,
223 cert = camel_certdb_cert_new (certdb);
225 if (camel_file_util_decode_string (istream, &cert->issuer) == -1)
227 if (camel_file_util_decode_string (istream, &cert->subject) == -1)
229 if (camel_file_util_decode_string (istream, &cert->hostname) == -1)
231 if (camel_file_util_decode_string (istream, &cert->fingerprint) == -1)
233 if (camel_file_util_decode_uint32 (istream, &cert->trust) == -1)
236 /* unset temporary trusts on load */
237 if (cert->trust == CAMEL_CERT_TRUST_TEMPORARY)
238 cert->trust = CAMEL_CERT_TRUST_UNKNOWN;
244 camel_certdb_cert_unref (certdb, cert);
250 camel_certdb_load (CamelCertDB *certdb)
252 CamelCertDBClass *class;
257 g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), -1);
258 g_return_val_if_fail (certdb->filename, -1);
260 in = g_fopen (certdb->filename, "rb");
264 class = CAMEL_CERTDB_GET_CLASS (certdb);
265 if (!class->header_load || !class->cert_load) {
269 g_return_val_if_fail (class->header_load != NULL, -1);
270 g_return_val_if_fail (class->cert_load != NULL, -1);
272 camel_certdb_lock (certdb, CAMEL_CERTDB_IO_LOCK);
273 if (class->header_load (certdb, in) == -1)
276 for (i = 0; i < certdb->saved_certs; i++) {
277 cert = class->cert_load (certdb, in);
282 /* NOTE: If we are upgrading from an evolution-data-server version
283 * prior to the change to look up certs by hostname (bug 606181),
284 * this "put" will result in duplicate entries for the same
285 * hostname being dropped. The change will become permanent on
286 * disk the next time the certdb is dirtied for some reason and
287 * has to be saved. */
288 camel_certdb_put (certdb, cert);
291 camel_certdb_unlock (certdb, CAMEL_CERTDB_IO_LOCK);
293 if (fclose (in) != 0)
296 certdb->flags &= ~CAMEL_CERTDB_DIRTY;
302 g_warning ("Cannot load certificate database: %s", g_strerror (ferror (in)));
304 camel_certdb_unlock (certdb, CAMEL_CERTDB_IO_LOCK);
312 certdb_header_save (CamelCertDB *certdb,
315 if (camel_file_util_encode_uint32 (ostream, certdb->version) == -1)
317 if (camel_file_util_encode_uint32 (ostream, certdb->saved_certs) == -1)
324 certdb_cert_save (CamelCertDB *certdb,
328 if (camel_file_util_encode_string (ostream, cert->issuer) == -1)
330 if (camel_file_util_encode_string (ostream, cert->subject) == -1)
332 if (camel_file_util_encode_string (ostream, cert->hostname) == -1)
334 if (camel_file_util_encode_string (ostream, cert->fingerprint) == -1)
336 if (camel_file_util_encode_uint32 (ostream, cert->trust) == -1)
343 camel_certdb_save (CamelCertDB *certdb)
345 CamelCertDBClass *class;
351 g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), -1);
352 g_return_val_if_fail (certdb->filename, -1);
354 /* no change, nothing new to save, simply return success */
355 if ((certdb->flags & CAMEL_CERTDB_DIRTY) == 0)
358 filename = alloca (strlen (certdb->filename) + 4);
359 sprintf (filename, "%s~", certdb->filename);
361 fd = g_open (filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
365 out = fdopen (fd, "wb");
374 class = CAMEL_CERTDB_GET_CLASS (certdb);
375 if (!class->header_save || !class->cert_save) {
379 g_return_val_if_fail (class->header_save != NULL, -1);
380 g_return_val_if_fail (class->cert_save != NULL, -1);
382 camel_certdb_lock (certdb, CAMEL_CERTDB_IO_LOCK);
384 certdb->saved_certs = certdb->certs->len;
385 if (class->header_save (certdb, out) == -1)
388 for (i = 0; i < certdb->saved_certs; i++) {
389 cert = (CamelCert *) certdb->certs->pdata[i];
391 if (class->cert_save (certdb, cert, out) == -1)
395 camel_certdb_unlock (certdb, CAMEL_CERTDB_IO_LOCK);
397 if (fflush (out) != 0 || fsync (fileno (out)) == -1) {
405 if (fclose (out) != 0) {
412 if (g_rename (filename, certdb->filename) == -1) {
419 certdb->flags &= ~CAMEL_CERTDB_DIRTY;
425 g_warning ("Cannot save certificate database: %s", g_strerror (ferror (out)));
427 camel_certdb_unlock (certdb, CAMEL_CERTDB_IO_LOCK);
438 camel_certdb_touch (CamelCertDB *certdb)
440 g_return_if_fail (CAMEL_IS_CERTDB (certdb));
442 certdb->flags |= CAMEL_CERTDB_DIRTY;
446 camel_certdb_get_host (CamelCertDB *certdb,
447 const gchar *hostname)
451 g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), NULL);
453 camel_certdb_lock (certdb, CAMEL_CERTDB_DB_LOCK);
455 cert = g_hash_table_lookup (certdb->cert_hash, hostname);
457 camel_certdb_cert_ref (certdb, cert);
459 camel_certdb_unlock (certdb, CAMEL_CERTDB_DB_LOCK);
465 camel_certdb_put (CamelCertDB *certdb,
470 g_return_if_fail (CAMEL_IS_CERTDB (certdb));
472 camel_certdb_lock (certdb, CAMEL_CERTDB_DB_LOCK);
474 /* Replace an existing entry with the same hostname. */
475 old_cert = g_hash_table_lookup (certdb->cert_hash, cert->hostname);
477 g_hash_table_remove (certdb->cert_hash, cert->hostname);
478 g_ptr_array_remove (certdb->certs, old_cert);
479 camel_certdb_cert_unref (certdb, old_cert);
482 camel_certdb_cert_ref (certdb, cert);
483 g_ptr_array_add (certdb->certs, cert);
484 g_hash_table_insert (certdb->cert_hash, cert->hostname, cert);
486 certdb->flags |= CAMEL_CERTDB_DIRTY;
488 camel_certdb_unlock (certdb, CAMEL_CERTDB_DB_LOCK);
492 camel_certdb_remove_host (CamelCertDB *certdb,
493 const gchar *hostname)
497 g_return_if_fail (CAMEL_IS_CERTDB (certdb));
499 camel_certdb_lock (certdb, CAMEL_CERTDB_DB_LOCK);
501 cert = g_hash_table_lookup (certdb->cert_hash, hostname);
503 g_hash_table_remove (certdb->cert_hash, hostname);
504 g_ptr_array_remove (certdb->certs, cert);
505 camel_certdb_cert_unref (certdb, cert);
507 certdb->flags |= CAMEL_CERTDB_DIRTY;
510 camel_certdb_unlock (certdb, CAMEL_CERTDB_DB_LOCK);
514 certdb_cert_new (CamelCertDB *certdb)
518 if (certdb->cert_chunks)
519 cert = camel_memchunk_alloc0 (certdb->cert_chunks);
521 cert = g_malloc0 (certdb->cert_size);
529 camel_certdb_cert_new (CamelCertDB *certdb)
531 CamelCertDBClass *class;
534 g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), NULL);
536 class = CAMEL_CERTDB_GET_CLASS (certdb);
537 g_return_val_if_fail (class->cert_new != NULL, NULL);
539 camel_certdb_lock (certdb, CAMEL_CERTDB_ALLOC_LOCK);
541 cert = class->cert_new (certdb);
543 camel_certdb_unlock (certdb, CAMEL_CERTDB_ALLOC_LOCK);
549 camel_certdb_cert_ref (CamelCertDB *certdb,
552 g_return_if_fail (CAMEL_IS_CERTDB (certdb));
553 g_return_if_fail (cert != NULL);
555 camel_certdb_lock (certdb, CAMEL_CERTDB_REF_LOCK);
557 camel_certdb_unlock (certdb, CAMEL_CERTDB_REF_LOCK);
561 certdb_cert_free (CamelCertDB *certdb,
564 g_free (cert->issuer);
565 g_free (cert->subject);
566 g_free (cert->hostname);
567 g_free (cert->fingerprint);
569 g_byte_array_free (cert->rawcert, TRUE);
573 camel_certdb_cert_unref (CamelCertDB *certdb,
576 CamelCertDBClass *class;
578 g_return_if_fail (CAMEL_IS_CERTDB (certdb));
579 g_return_if_fail (cert != NULL);
581 class = CAMEL_CERTDB_GET_CLASS (certdb);
582 g_return_if_fail (class->cert_free != NULL);
584 camel_certdb_lock (certdb, CAMEL_CERTDB_REF_LOCK);
586 if (cert->refcount <= 1) {
587 class->cert_free (certdb, cert);
588 if (certdb->cert_chunks)
589 camel_memchunk_free (certdb->cert_chunks, cert);
596 camel_certdb_unlock (certdb, CAMEL_CERTDB_REF_LOCK);
600 cert_remove (gpointer key,
608 camel_certdb_clear (CamelCertDB *certdb)
613 g_return_if_fail (CAMEL_IS_CERTDB (certdb));
615 camel_certdb_lock (certdb, CAMEL_CERTDB_DB_LOCK);
617 g_hash_table_foreach_remove (certdb->cert_hash, cert_remove, NULL);
618 for (i = 0; i < certdb->certs->len; i++) {
619 cert = (CamelCert *) certdb->certs->pdata[i];
620 camel_certdb_cert_unref (certdb, cert);
623 certdb->saved_certs = 0;
624 g_ptr_array_set_size (certdb->certs, 0);
625 certdb->flags |= CAMEL_CERTDB_DIRTY;
627 camel_certdb_unlock (certdb, CAMEL_CERTDB_DB_LOCK);
631 cert_get_string (CamelCertDB *certdb,
636 case CAMEL_CERT_STRING_ISSUER:
638 case CAMEL_CERT_STRING_SUBJECT:
639 return cert->subject;
640 case CAMEL_CERT_STRING_HOSTNAME:
641 return cert->hostname;
642 case CAMEL_CERT_STRING_FINGERPRINT:
643 return cert->fingerprint;
650 camel_cert_get_string (CamelCertDB *certdb,
654 CamelCertDBClass *class;
656 g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), NULL);
657 g_return_val_if_fail (cert != NULL, NULL);
659 class = CAMEL_CERTDB_GET_CLASS (certdb);
660 g_return_val_if_fail (class->cert_get_string != NULL, NULL);
662 /* FIXME: do locking? */
664 return class->cert_get_string (certdb, cert, string);
668 cert_set_string (CamelCertDB *certdb,
674 case CAMEL_CERT_STRING_ISSUER:
675 g_free (cert->issuer);
676 cert->issuer = g_strdup (value);
678 case CAMEL_CERT_STRING_SUBJECT:
679 g_free (cert->subject);
680 cert->subject = g_strdup (value);
682 case CAMEL_CERT_STRING_HOSTNAME:
683 g_free (cert->hostname);
684 cert->hostname = g_strdup (value);
686 case CAMEL_CERT_STRING_FINGERPRINT:
687 g_free (cert->fingerprint);
688 cert->fingerprint = g_strdup (value);
696 camel_cert_set_string (CamelCertDB *certdb,
701 CamelCertDBClass *class;
703 g_return_if_fail (CAMEL_IS_CERTDB (certdb));
704 g_return_if_fail (cert != NULL);
706 class = CAMEL_CERTDB_GET_CLASS (certdb);
707 g_return_if_fail (class->cert_set_string != NULL);
709 /* FIXME: do locking? */
711 class->cert_set_string (certdb, cert, string, value);
715 camel_cert_get_trust (CamelCertDB *certdb,
718 g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), CAMEL_CERT_TRUST_UNKNOWN);
719 g_return_val_if_fail (cert != NULL, CAMEL_CERT_TRUST_UNKNOWN);
725 camel_cert_set_trust (CamelCertDB *certdb,
727 CamelCertTrust trust)
729 g_return_if_fail (CAMEL_IS_CERTDB (certdb));
730 g_return_if_fail (cert != NULL);
737 * @certdb: a #CamelCertDB
738 * @lock: lock type to lock
740 * Locks @certdb's @lock. Unlock it with camel_certdb_unlock().
745 camel_certdb_lock (CamelCertDB *certdb,
746 CamelCertDBLock lock)
748 g_return_if_fail (CAMEL_IS_CERTDB (certdb));
751 case CAMEL_CERTDB_DB_LOCK:
752 g_mutex_lock (certdb->priv->db_lock);
754 case CAMEL_CERTDB_IO_LOCK:
755 g_mutex_lock (certdb->priv->io_lock);
757 case CAMEL_CERTDB_ALLOC_LOCK:
758 g_mutex_lock (certdb->priv->alloc_lock);
760 case CAMEL_CERTDB_REF_LOCK:
761 g_mutex_lock (certdb->priv->ref_lock);
764 g_return_if_reached ();
769 * camel_certdb_unlock:
770 * @certdb: a #CamelCertDB
771 * @lock: lock type to unlock
773 * Unlocks @certdb's @lock, previously locked with camel_certdb_lock().
778 camel_certdb_unlock (CamelCertDB *certdb,
779 CamelCertDBLock lock)
781 g_return_if_fail (CAMEL_IS_CERTDB (certdb));
784 case CAMEL_CERTDB_DB_LOCK:
785 g_mutex_unlock (certdb->priv->db_lock);
787 case CAMEL_CERTDB_IO_LOCK:
788 g_mutex_unlock (certdb->priv->io_lock);
790 case CAMEL_CERTDB_ALLOC_LOCK:
791 g_mutex_unlock (certdb->priv->alloc_lock);
793 case CAMEL_CERTDB_REF_LOCK:
794 g_mutex_unlock (certdb->priv->ref_lock);
797 g_return_if_reached ();