2 * @file certificate.c Public-Key Certificate API
10 * Purple is the legal property of its developers, whose names are too numerous
11 * to list here. Please refer to the COPYRIGHT file distributed with this
12 * source distribution.
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
30 #include "certificate.h"
31 #include "dbus-maybe.h"
37 /** List holding pointers to all registered certificate schemes */
38 static GList
*cert_schemes
= NULL
;
39 /** List of registered Verifiers */
40 static GList
*cert_verifiers
= NULL
;
41 /** List of registered Pools */
42 static GList
*cert_pools
= NULL
;
45 * TODO: Merge this with PurpleCertificateVerificationStatus for 3.0.0 */
47 PURPLE_CERTIFICATE_UNKNOWN_ERROR
= -1,
50 PURPLE_CERTIFICATE_NO_PROBLEMS
= 0,
53 PURPLE_CERTIFICATE_NON_FATALS_MASK
= 0x0000FFFF,
55 /* The certificate is self-signed. */
56 PURPLE_CERTIFICATE_SELF_SIGNED
= 0x01,
58 /* The CA is not in libpurple's pool of certificates. */
59 PURPLE_CERTIFICATE_CA_UNKNOWN
= 0x02,
61 /* The current time is before the certificate's specified
64 PURPLE_CERTIFICATE_NOT_ACTIVATED
= 0x04,
66 /* The current time is after the certificate's specified expiration time */
67 PURPLE_CERTIFICATE_EXPIRED
= 0x08,
69 /* The certificate's subject name doesn't match the expected */
70 PURPLE_CERTIFICATE_NAME_MISMATCH
= 0x10,
72 /* No CA pool was found. This shouldn't happen... */
73 PURPLE_CERTIFICATE_NO_CA_POOL
= 0x20,
76 PURPLE_CERTIFICATE_FATALS_MASK
= 0xFFFF0000,
78 /* The signature chain could not be validated. Due to limitations in the
79 * the current API, this also indicates one of the CA certificates in the
80 * chain is expired (or not yet activated). FIXME 3.0.0 */
81 PURPLE_CERTIFICATE_INVALID_CHAIN
= 0x10000,
83 /* The signature has been revoked. */
84 PURPLE_CERTIFICATE_REVOKED
= 0x20000,
86 PURPLE_CERTIFICATE_LAST
= 0x40000,
87 } PurpleCertificateInvalidityFlags
;
90 invalidity_reason_to_string(PurpleCertificateInvalidityFlags flag
)
93 case PURPLE_CERTIFICATE_SELF_SIGNED
:
94 return _("The certificate is self-signed and cannot be "
95 "automatically checked.");
97 case PURPLE_CERTIFICATE_CA_UNKNOWN
:
98 return _("The certificate is not trusted because no certificate "
99 "that can verify it is currently trusted.");
101 case PURPLE_CERTIFICATE_NOT_ACTIVATED
:
102 return _("The certificate is not valid yet. Check that your "
103 "computer's date and time are accurate.");
105 case PURPLE_CERTIFICATE_EXPIRED
:
106 return _("The certificate has expired and should not be "
107 "considered valid. Check that your computer's date "
108 "and time are accurate.");
110 case PURPLE_CERTIFICATE_NAME_MISMATCH
:
111 /* Translators: "domain" refers to a DNS domain (e.g. talk.google.com) */
112 return _("The certificate presented is not issued to this domain.");
114 case PURPLE_CERTIFICATE_NO_CA_POOL
:
115 return _("You have no database of root certificates, so "
116 "this certificate cannot be validated.");
118 case PURPLE_CERTIFICATE_INVALID_CHAIN
:
119 return _("The certificate chain presented is invalid.");
121 case PURPLE_CERTIFICATE_REVOKED
:
122 return _("The certificate has been revoked.");
124 case PURPLE_CERTIFICATE_UNKNOWN_ERROR
:
126 return _("An unknown certificate error occurred.");
132 purple_certificate_verify (PurpleCertificateVerifier
*verifier
,
133 const gchar
*subject_name
, GList
*cert_chain
,
134 PurpleCertificateVerifiedCallback cb
,
137 PurpleCertificateVerificationRequest
*vrq
;
138 PurpleCertificateScheme
*scheme
;
140 g_return_if_fail(subject_name
!= NULL
);
141 /* If you don't have a cert to check, why are you requesting that it
143 g_return_if_fail(cert_chain
!= NULL
);
144 g_return_if_fail(cb
!= NULL
);
146 /* Look up the CertificateScheme */
147 scheme
= purple_certificate_find_scheme(verifier
->scheme_name
);
148 g_return_if_fail(scheme
);
150 /* Check that at least the first cert in the chain matches the
152 g_return_if_fail(scheme
==
153 ((PurpleCertificate
*) (cert_chain
->data
))->scheme
);
155 /* Construct and fill in the request fields */
156 vrq
= g_new0(PurpleCertificateVerificationRequest
, 1);
157 vrq
->verifier
= verifier
;
158 vrq
->scheme
= scheme
;
159 vrq
->subject_name
= g_strdup(subject_name
);
160 vrq
->cert_chain
= purple_certificate_copy_list(cert_chain
);
162 vrq
->cb_data
= cb_data
;
164 /* Initiate verification */
165 (verifier
->start_verification
)(vrq
);
169 purple_certificate_verify_complete(PurpleCertificateVerificationRequest
*vrq
,
170 PurpleCertificateVerificationStatus st
)
172 PurpleCertificateVerifier
*vr
;
174 g_return_if_fail(vrq
);
176 if (st
== PURPLE_CERTIFICATE_VALID
) {
177 purple_debug_info("certificate",
178 "Successfully verified certificate for %s\n",
181 purple_debug_error("certificate",
182 "Failed to verify certificate for %s\n",
186 /* Pass the results on to the request's callback */
187 (vrq
->cb
)(st
, vrq
->cb_data
);
189 /* And now to eliminate the request */
190 /* Fetch the Verifier responsible... */
192 /* ...and order it to KILL */
193 (vr
->destroy_request
)(vrq
);
195 /* Now the internals have been cleaned up, so clean up the libpurple-
197 g_free(vrq
->subject_name
);
198 purple_certificate_destroy_list(vrq
->cert_chain
);
202 * and with so much within.
210 purple_certificate_copy(PurpleCertificate
*crt
)
212 g_return_val_if_fail(crt
, NULL
);
213 g_return_val_if_fail(crt
->scheme
, NULL
);
214 g_return_val_if_fail(crt
->scheme
->copy_certificate
, NULL
);
216 return (crt
->scheme
->copy_certificate
)(crt
);
220 purple_certificate_copy_list(GList
*crt_list
)
224 /* First, make a shallow copy of the list */
225 new_l
= g_list_copy(crt_list
);
227 /* Now go through and actually duplicate each certificate */
228 for (l
= new_l
; l
; l
= l
->next
) {
229 l
->data
= purple_certificate_copy(l
->data
);
236 purple_certificate_destroy (PurpleCertificate
*crt
)
238 PurpleCertificateScheme
*scheme
;
240 if (NULL
== crt
) return;
242 scheme
= crt
->scheme
;
244 (scheme
->destroy_certificate
)(crt
);
248 purple_certificate_destroy_list (GList
* crt_list
)
250 PurpleCertificate
*crt
;
253 for (l
=crt_list
; l
; l
= l
->next
) {
254 crt
= (PurpleCertificate
*) l
->data
;
255 purple_certificate_destroy(crt
);
258 g_list_free(crt_list
);
262 purple_certificate_signed_by(PurpleCertificate
*crt
, PurpleCertificate
*issuer
)
264 PurpleCertificateScheme
*scheme
;
266 g_return_val_if_fail(crt
, FALSE
);
267 g_return_val_if_fail(issuer
, FALSE
);
269 scheme
= crt
->scheme
;
270 g_return_val_if_fail(scheme
, FALSE
);
271 /* We can't compare two certs of unrelated schemes, obviously */
272 g_return_val_if_fail(issuer
->scheme
== scheme
, FALSE
);
274 return (scheme
->signed_by
)(crt
, issuer
);
278 purple_certificate_check_signature_chain_with_failing(GList
*chain
,
279 PurpleCertificate
**failing
)
282 PurpleCertificate
*crt
, *issuer
;
284 time_t now
, activation
, expiration
;
287 g_return_val_if_fail(chain
, FALSE
);
292 uid
= purple_certificate_get_unique_id((PurpleCertificate
*) chain
->data
);
293 purple_debug_info("certificate",
294 "Checking signature chain for uid=%s\n",
298 /* If this is a single-certificate chain, say that it is valid */
299 if (chain
->next
== NULL
) {
300 purple_debug_info("certificate",
301 "...Singleton. We'll say it's valid.\n");
307 /* Load crt with the first certificate */
308 crt
= (PurpleCertificate
*)(chain
->data
);
309 /* And start with the second certificate in the chain */
310 for ( cur
= chain
->next
; cur
; cur
= cur
->next
) {
312 issuer
= (PurpleCertificate
*)(cur
->data
);
314 uid
= purple_certificate_get_unique_id(issuer
);
316 ret
= purple_certificate_get_times(issuer
, &activation
, &expiration
);
317 if (!ret
|| now
< activation
|| now
> expiration
) {
319 purple_debug_error("certificate",
320 "...Failed to get validity times for certificate %s\n"
321 "Chain is INVALID\n", uid
);
322 else if (now
> expiration
)
323 purple_debug_error("certificate",
324 "...Issuer %s expired at %s\nChain is INVALID\n",
325 uid
, ctime(&expiration
));
327 purple_debug_error("certificate",
328 "...Not-yet-activated issuer %s will be valid at %s\n"
329 "Chain is INVALID\n", uid
, ctime(&activation
));
338 /* Check the signature for this link */
339 if (! purple_certificate_signed_by(crt
, issuer
) ) {
340 purple_debug_error("certificate",
341 "...Bad or missing signature by %s\nChain is INVALID\n",
351 purple_debug_info("certificate",
352 "...Good signature by %s\n",
356 /* The issuer is now the next crt whose signature is to be
361 /* If control reaches this point, the chain is valid */
362 purple_debug_info("certificate", "Chain is VALID\n");
367 purple_certificate_check_signature_chain(GList
*chain
)
369 return purple_certificate_check_signature_chain_with_failing(chain
, NULL
);
373 purple_certificate_import(PurpleCertificateScheme
*scheme
, const gchar
*filename
)
375 g_return_val_if_fail(scheme
, NULL
);
376 g_return_val_if_fail(scheme
->import_certificate
, NULL
);
377 g_return_val_if_fail(filename
, NULL
);
379 return (scheme
->import_certificate
)(filename
);
383 purple_certificates_import(PurpleCertificateScheme
*scheme
, const gchar
*filename
)
385 g_return_val_if_fail(scheme
, NULL
);
386 g_return_val_if_fail(scheme
->import_certificates
, NULL
);
387 g_return_val_if_fail(filename
, NULL
);
389 return (scheme
->import_certificates
)(filename
);
393 purple_certificate_export(const gchar
*filename
, PurpleCertificate
*crt
)
395 PurpleCertificateScheme
*scheme
;
397 g_return_val_if_fail(filename
, FALSE
);
398 g_return_val_if_fail(crt
, FALSE
);
399 g_return_val_if_fail(crt
->scheme
, FALSE
);
401 scheme
= crt
->scheme
;
402 g_return_val_if_fail(scheme
->export_certificate
, FALSE
);
404 return (scheme
->export_certificate
)(filename
, crt
);
408 byte_arrays_equal(const GByteArray
*array1
, const GByteArray
*array2
)
410 g_return_val_if_fail(array1
!= NULL
, FALSE
);
411 g_return_val_if_fail(array2
!= NULL
, FALSE
);
413 return (array1
->len
== array2
->len
) &&
414 (0 == memcmp(array1
->data
, array2
->data
, array1
->len
));
418 purple_certificate_get_fingerprint_sha1(PurpleCertificate
*crt
)
420 PurpleCertificateScheme
*scheme
;
423 g_return_val_if_fail(crt
, NULL
);
424 g_return_val_if_fail(crt
->scheme
, NULL
);
426 scheme
= crt
->scheme
;
428 g_return_val_if_fail(scheme
->get_fingerprint_sha1
, NULL
);
430 fpr
= (scheme
->get_fingerprint_sha1
)(crt
);
436 purple_certificate_get_unique_id(PurpleCertificate
*crt
)
438 g_return_val_if_fail(crt
, NULL
);
439 g_return_val_if_fail(crt
->scheme
, NULL
);
440 g_return_val_if_fail(crt
->scheme
->get_unique_id
, NULL
);
442 return (crt
->scheme
->get_unique_id
)(crt
);
446 purple_certificate_get_issuer_unique_id(PurpleCertificate
*crt
)
448 g_return_val_if_fail(crt
, NULL
);
449 g_return_val_if_fail(crt
->scheme
, NULL
);
450 g_return_val_if_fail(crt
->scheme
->get_issuer_unique_id
, NULL
);
452 return (crt
->scheme
->get_issuer_unique_id
)(crt
);
456 purple_certificate_get_subject_name(PurpleCertificate
*crt
)
458 PurpleCertificateScheme
*scheme
;
461 g_return_val_if_fail(crt
, NULL
);
462 g_return_val_if_fail(crt
->scheme
, NULL
);
464 scheme
= crt
->scheme
;
466 g_return_val_if_fail(scheme
->get_subject_name
, NULL
);
468 subject_name
= (scheme
->get_subject_name
)(crt
);
474 purple_certificate_check_subject_name(PurpleCertificate
*crt
, const gchar
*name
)
476 PurpleCertificateScheme
*scheme
;
478 g_return_val_if_fail(crt
, FALSE
);
479 g_return_val_if_fail(crt
->scheme
, FALSE
);
480 g_return_val_if_fail(name
, FALSE
);
482 scheme
= crt
->scheme
;
484 g_return_val_if_fail(scheme
->check_subject_name
, FALSE
);
486 return (scheme
->check_subject_name
)(crt
, name
);
490 purple_certificate_get_times(PurpleCertificate
*crt
, time_t *activation
, time_t *expiration
)
492 PurpleCertificateScheme
*scheme
;
494 g_return_val_if_fail(crt
, FALSE
);
496 scheme
= crt
->scheme
;
498 g_return_val_if_fail(scheme
, FALSE
);
500 /* If both provided references are NULL, what are you doing calling
502 g_return_val_if_fail( (activation
!= NULL
) || (expiration
!= NULL
), FALSE
);
504 /* Throw the request on down to the certscheme */
505 return (scheme
->get_times
)(crt
, activation
, expiration
);
509 purple_certificate_pool_mkpath(PurpleCertificatePool
*pool
, const gchar
*id
)
512 gchar
*esc_scheme_name
, *esc_name
, *esc_id
;
514 g_return_val_if_fail(pool
, NULL
);
515 g_return_val_if_fail(pool
->scheme_name
, NULL
);
516 g_return_val_if_fail(pool
->name
, NULL
);
518 /* Escape all the elements for filesystem-friendliness */
519 esc_scheme_name
= pool
? g_strdup(purple_escape_filename(pool
->scheme_name
)) : NULL
;
520 esc_name
= pool
? g_strdup(purple_escape_filename(pool
->name
)) : NULL
;
521 esc_id
= id
? g_strdup(purple_escape_filename(id
)) : NULL
;
523 path
= g_build_filename(purple_user_dir(),
524 "certificates", /* TODO: constantize this? */
530 g_free(esc_scheme_name
);
537 purple_certificate_pool_usable(PurpleCertificatePool
*pool
)
539 g_return_val_if_fail(pool
, FALSE
);
540 g_return_val_if_fail(pool
->scheme_name
, FALSE
);
542 /* Check that the pool's scheme is loaded */
543 if (purple_certificate_find_scheme(pool
->scheme_name
) == NULL
) {
550 PurpleCertificateScheme
*
551 purple_certificate_pool_get_scheme(PurpleCertificatePool
*pool
)
553 g_return_val_if_fail(pool
, NULL
);
554 g_return_val_if_fail(pool
->scheme_name
, NULL
);
556 return purple_certificate_find_scheme(pool
->scheme_name
);
560 purple_certificate_pool_contains(PurpleCertificatePool
*pool
, const gchar
*id
)
562 g_return_val_if_fail(pool
, FALSE
);
563 g_return_val_if_fail(id
, FALSE
);
564 g_return_val_if_fail(pool
->cert_in_pool
, FALSE
);
566 return (pool
->cert_in_pool
)(id
);
570 purple_certificate_pool_retrieve(PurpleCertificatePool
*pool
, const gchar
*id
)
572 g_return_val_if_fail(pool
, NULL
);
573 g_return_val_if_fail(id
, NULL
);
574 g_return_val_if_fail(pool
->get_cert
, NULL
);
576 return (pool
->get_cert
)(id
);
580 purple_certificate_pool_store(PurpleCertificatePool
*pool
, const gchar
*id
, PurpleCertificate
*crt
)
582 gboolean ret
= FALSE
;
584 g_return_val_if_fail(pool
, FALSE
);
585 g_return_val_if_fail(id
, FALSE
);
586 g_return_val_if_fail(pool
->put_cert
, FALSE
);
588 /* Whether crt->scheme matches find_scheme(pool->scheme_name) is not
589 relevant... I think... */
590 g_return_val_if_fail(
591 g_ascii_strcasecmp(pool
->scheme_name
, crt
->scheme
->name
) == 0,
594 ret
= (pool
->put_cert
)(id
, crt
);
596 /* Signal that the certificate was stored if success*/
598 purple_signal_emit(pool
, "certificate-stored",
606 purple_certificate_pool_delete(PurpleCertificatePool
*pool
, const gchar
*id
)
608 gboolean ret
= FALSE
;
610 g_return_val_if_fail(pool
, FALSE
);
611 g_return_val_if_fail(id
, FALSE
);
612 g_return_val_if_fail(pool
->delete_cert
, FALSE
);
614 ret
= (pool
->delete_cert
)(id
);
616 /* Signal that the certificate was deleted if success */
618 purple_signal_emit(pool
, "certificate-deleted",
626 purple_certificate_pool_get_idlist(PurpleCertificatePool
*pool
)
628 g_return_val_if_fail(pool
, NULL
);
629 g_return_val_if_fail(pool
->get_idlist
, NULL
);
631 return (pool
->get_idlist
)();
635 purple_certificate_pool_destroy_idlist(GList
*idlist
)
639 /* Iterate through and free them strings */
640 for ( l
= idlist
; l
; l
= l
->next
) {
648 /****************************************************************************/
649 /* Builtin Verifiers, Pools, etc. */
650 /****************************************************************************/
653 x509_singleuse_verify_cb (PurpleCertificateVerificationRequest
*vrq
, gint id
)
655 g_return_if_fail(vrq
);
657 purple_debug_info("certificate/x509_singleuse",
658 "VRQ on cert from %s gave %d\n",
659 vrq
->subject_name
, id
);
661 /* Signal what happened back to the caller */
664 purple_certificate_verify_complete(vrq
,
665 PURPLE_CERTIFICATE_VALID
);
668 purple_certificate_verify_complete(vrq
,
669 PURPLE_CERTIFICATE_INVALID
);
675 x509_singleuse_start_verify (PurpleCertificateVerificationRequest
*vrq
)
680 const gchar
*cn_match
;
681 gchar
*primary
, *secondary
;
682 PurpleCertificate
*crt
= (PurpleCertificate
*) vrq
->cert_chain
->data
;
684 /* Pull out the SHA1 checksum */
685 sha_bin
= purple_certificate_get_fingerprint_sha1(crt
);
686 /* Now decode it for display */
687 sha_asc
= purple_base16_encode_chunked(sha_bin
->data
,
690 /* Get the cert Common Name */
691 cn
= purple_certificate_get_subject_name(crt
);
693 /* Determine whether the name matches */
694 if (purple_certificate_check_subject_name(crt
, vrq
->subject_name
)) {
697 cn_match
= _("(DOES NOT MATCH)");
701 primary
= g_strdup_printf(_("%s has presented the following certificate for just-this-once use:"), vrq
->subject_name
);
702 secondary
= g_strdup_printf(_("Common name: %s %s\nFingerprint (SHA1): %s"), cn
, cn_match
, sha_asc
);
704 /* Make a semi-pretty display */
705 purple_request_accept_cancel(
706 vrq
->cb_data
, /* TODO: Find what the handle ought to be */
707 _("Single-use Certificate Verification"),
710 0, /* Accept by default */
711 NULL
, /* No account */
712 NULL
, /* No other user */
713 NULL
, /* No associated conversation */
715 x509_singleuse_verify_cb
,
716 x509_singleuse_verify_cb
);
723 g_byte_array_free(sha_bin
, TRUE
);
727 x509_singleuse_destroy_request (PurpleCertificateVerificationRequest
*vrq
)
729 /* I don't do anything! */
732 static PurpleCertificateVerifier x509_singleuse
= {
733 "x509", /* Scheme name */
734 "singleuse", /* Verifier name */
735 x509_singleuse_start_verify
, /* start_verification function */
736 x509_singleuse_destroy_request
, /* Request cleanup operation */
746 /***** X.509 Certificate Authority pool, keyed by Distinguished Name *****/
747 /* This is implemented in what may be the most inefficient and bugprone way
748 possible; however, future optimizations should not be difficult. */
750 static PurpleCertificatePool x509_ca
;
752 /** Holds a key-value pair for quickish certificate lookup */
755 PurpleCertificate
*crt
;
759 x509_ca_element_free(x509_ca_element
*el
)
761 if (NULL
== el
) return;
764 purple_certificate_destroy(el
->crt
);
768 /** System directory to probe for CA certificates */
769 /* This is set in the lazy_init function */
770 static GList
*x509_ca_paths
= NULL
;
772 /** A list of loaded CAs, populated from the above path whenever the lazy_init
773 happens. Contains pointers to x509_ca_elements */
774 static GList
*x509_ca_certs
= NULL
;
776 /** Used for lazy initialization purposes. */
777 static gboolean x509_ca_initialized
= FALSE
;
779 /** Adds a certificate to the in-memory cache, doing nothing else */
781 x509_ca_quiet_put_cert(PurpleCertificate
*crt
)
785 /* lazy_init calls this function, so calling lazy_init here is a
788 g_return_val_if_fail(crt
, FALSE
);
789 g_return_val_if_fail(crt
->scheme
, FALSE
);
790 /* Make sure that this is some kind of X.509 certificate */
791 /* TODO: Perhaps just check crt->scheme->name instead? */
792 g_return_val_if_fail(crt
->scheme
== purple_certificate_find_scheme(x509_ca
.scheme_name
), FALSE
);
794 el
= g_new0(x509_ca_element
, 1);
795 el
->dn
= purple_certificate_get_unique_id(crt
);
796 el
->crt
= purple_certificate_copy(crt
);
797 x509_ca_certs
= g_list_prepend(x509_ca_certs
, el
);
802 /* Since the libpurple CertificatePools get registered before plugins are
803 loaded, an X.509 Scheme is generally not available when x509_ca_init is
804 called, but x509_ca requires X.509 operations in order to properly load.
806 To solve this, I present the lazy_init function. It attempts to finish
807 initialization of the Pool, but it usually fails when it is called from
808 x509_ca_init. However, this is OK; initialization is then simply deferred
809 until someone tries to use functions from the pool. */
811 x509_ca_lazy_init(void)
813 PurpleCertificateScheme
*x509
;
816 GPatternSpec
*pempat
, *crtpat
;
820 if (x509_ca_initialized
) return TRUE
;
822 /* Check that X.509 is registered */
823 x509
= purple_certificate_find_scheme(x509_ca
.scheme_name
);
825 purple_debug_warning("certificate/x509/ca",
826 "Lazy init failed because an X.509 Scheme "
827 "is not yet registered. Maybe it will be "
832 /* Use a glob to only read .pem files */
833 pempat
= g_pattern_spec_new("*.pem");
834 crtpat
= g_pattern_spec_new("*.crt");
836 /* Populate the certificates pool from the search path(s) */
837 for (iter
= x509_ca_paths
; iter
; iter
= iter
->next
) {
838 certdir
= g_dir_open(iter
->data
, 0, NULL
);
840 purple_debug_error("certificate/x509/ca", "Couldn't open location '%s'\n", (const char *)iter
->data
);
844 while ( (entry
= g_dir_read_name(certdir
)) ) {
846 PurpleCertificate
*crt
;
848 if (!g_pattern_match_string(pempat
, entry
) && !g_pattern_match_string(crtpat
, entry
)) {
852 fullpath
= g_build_filename(iter
->data
, entry
, NULL
);
854 /* TODO: Respond to a failure in the following? */
855 crts
= purple_certificates_import(x509
, fullpath
);
857 while (crts
&& crts
->data
) {
859 if (x509_ca_quiet_put_cert(crt
)) {
861 name
= purple_certificate_get_subject_name(crt
);
862 purple_debug_info("certificate/x509/ca",
863 "Loaded %s from %s\n",
864 name
? name
: "(unknown)", fullpath
);
867 purple_debug_error("certificate/x509/ca",
868 "Failed to load certificate from %s\n",
871 purple_certificate_destroy(crt
);
872 crts
= g_slist_delete_link(crts
, crts
);
877 g_dir_close(certdir
);
880 g_pattern_spec_free(pempat
);
881 g_pattern_spec_free(crtpat
);
883 purple_debug_info("certificate/x509/ca",
884 "Lazy init completed.\n");
885 x509_ca_initialized
= TRUE
;
892 /* Attempt to point at the appropriate system path */
893 if (NULL
== x509_ca_paths
) {
895 x509_ca_paths
= g_list_append(NULL
, g_build_filename(DATADIR
,
898 # ifdef SSL_CERTIFICATES_DIR
899 x509_ca_paths
= g_list_append(NULL
, g_strdup(SSL_CERTIFICATES_DIR
));
901 x509_ca_paths
= g_list_append(x509_ca_paths
,
902 g_build_filename(DATADIR
, "purple", "ca-certs", NULL
));
906 /* Attempt to initialize now, but if it doesn't work, that's OK;
907 it will get done later */
908 if ( ! x509_ca_lazy_init()) {
909 purple_debug_info("certificate/x509/ca",
910 "Init failed, probably because a "
911 "dependency is not yet registered. "
912 "It has been deferred to later.\n");
923 for (l
= x509_ca_certs
; l
; l
= l
->next
) {
924 x509_ca_element
*el
= l
->data
;
925 x509_ca_element_free(el
);
927 g_list_free(x509_ca_certs
);
928 x509_ca_certs
= NULL
;
929 x509_ca_initialized
= FALSE
;
930 g_list_foreach(x509_ca_paths
, (GFunc
)g_free
, NULL
);
931 g_list_free(x509_ca_paths
);
932 x509_ca_paths
= NULL
;
935 /** Look up a ca_element by dn */
936 static x509_ca_element
*
937 x509_ca_locate_cert(GList
*lst
, const gchar
*dn
)
941 for (cur
= lst
; cur
; cur
= cur
->next
) {
942 x509_ca_element
*el
= cur
->data
;
943 if (purple_strequal(dn
, el
->dn
)) {
951 x509_ca_locate_certs(GList
*lst
, const gchar
*dn
)
956 for (cur
= lst
; cur
; cur
= cur
->next
) {
957 x509_ca_element
*el
= cur
->data
;
958 if (purple_strequal(dn
, el
->dn
)) {
959 crts
= g_slist_prepend(crts
, el
);
967 x509_ca_cert_in_pool(const gchar
*id
)
969 g_return_val_if_fail(x509_ca_lazy_init(), FALSE
);
970 g_return_val_if_fail(id
, FALSE
);
972 if (x509_ca_locate_cert(x509_ca_certs
, id
) != NULL
) {
981 static PurpleCertificate
*
982 x509_ca_get_cert(const gchar
*id
)
984 PurpleCertificate
*crt
= NULL
;
987 g_return_val_if_fail(x509_ca_lazy_init(), NULL
);
988 g_return_val_if_fail(id
, NULL
);
990 /* Search the memory-cached pool */
991 el
= x509_ca_locate_cert(x509_ca_certs
, id
);
994 /* Make a copy of the memcached one for the function caller
996 crt
= purple_certificate_copy(el
->crt
);
1005 x509_ca_get_certs(const gchar
*id
)
1007 GSList
*crts
= NULL
, *els
= NULL
;
1009 g_return_val_if_fail(x509_ca_lazy_init(), NULL
);
1010 g_return_val_if_fail(id
, NULL
);
1012 /* Search the memory-cached pool */
1013 els
= x509_ca_locate_certs(x509_ca_certs
, id
);
1017 /* Make a copy of the memcached ones for the function caller
1019 for (cur
= els
; cur
; cur
= cur
->next
) {
1020 x509_ca_element
*el
= cur
->data
;
1021 crts
= g_slist_prepend(crts
, purple_certificate_copy(el
->crt
));
1030 x509_ca_put_cert(const gchar
*id
, PurpleCertificate
*crt
)
1032 gboolean ret
= FALSE
;
1034 g_return_val_if_fail(x509_ca_lazy_init(), FALSE
);
1036 /* TODO: This is a quick way of doing this. At some point the change
1037 ought to be flushed to disk somehow. */
1038 ret
= x509_ca_quiet_put_cert(crt
);
1044 x509_ca_delete_cert(const gchar
*id
)
1046 x509_ca_element
*el
;
1048 g_return_val_if_fail(x509_ca_lazy_init(), FALSE
);
1049 g_return_val_if_fail(id
, FALSE
);
1051 /* Is the id even in the pool? */
1052 el
= x509_ca_locate_cert(x509_ca_certs
, id
);
1054 purple_debug_warning("certificate/x509/ca",
1055 "Id %s wasn't in the pool\n",
1060 /* Unlink it from the memory cache and destroy it */
1061 x509_ca_certs
= g_list_remove(x509_ca_certs
, el
);
1062 x509_ca_element_free(el
);
1068 x509_ca_get_idlist(void)
1072 g_return_val_if_fail(x509_ca_lazy_init(), NULL
);
1075 for (l
= x509_ca_certs
; l
; l
= l
->next
) {
1076 x509_ca_element
*el
= l
->data
;
1077 idlist
= g_list_prepend(idlist
, g_strdup(el
->dn
));
1084 static PurpleCertificatePool x509_ca
= {
1085 "x509", /* Scheme name */
1086 "ca", /* Pool name */
1087 N_("Certificate Authorities"),/* User-friendly name */
1088 NULL
, /* Internal data */
1089 x509_ca_init
, /* init */
1090 x509_ca_uninit
, /* uninit */
1091 x509_ca_cert_in_pool
, /* Certificate exists? */
1092 x509_ca_get_cert
, /* Cert retriever */
1093 x509_ca_put_cert
, /* Cert writer */
1094 x509_ca_delete_cert
, /* Cert remover */
1095 x509_ca_get_idlist
, /* idlist retriever */
1106 /***** Cache of certificates given by TLS/SSL peers *****/
1107 static PurpleCertificatePool x509_tls_peers
;
1110 x509_tls_peers_init(void)
1115 /* Set up key cache here if it isn't already done */
1116 poolpath
= purple_certificate_pool_mkpath(&x509_tls_peers
, NULL
);
1117 ret
= purple_build_dir(poolpath
, 0700); /* Make it this user only */
1120 purple_debug_info("certificate/tls_peers",
1121 "Could not create %s. Certificates will not be cached.\n",
1130 x509_tls_peers_cert_in_pool(const gchar
*id
)
1133 gboolean ret
= FALSE
;
1135 g_return_val_if_fail(id
, FALSE
);
1137 keypath
= purple_certificate_pool_mkpath(&x509_tls_peers
, id
);
1139 ret
= g_file_test(keypath
, G_FILE_TEST_IS_REGULAR
);
1145 static PurpleCertificate
*
1146 x509_tls_peers_get_cert(const gchar
*id
)
1148 PurpleCertificateScheme
*x509
;
1149 PurpleCertificate
*crt
;
1152 g_return_val_if_fail(id
, NULL
);
1154 /* Is it in the pool? */
1155 if ( !x509_tls_peers_cert_in_pool(id
) ) {
1159 /* Look up the X.509 scheme */
1160 x509
= purple_certificate_find_scheme("x509");
1161 g_return_val_if_fail(x509
, NULL
);
1163 /* Okay, now find and load that key */
1164 keypath
= purple_certificate_pool_mkpath(&x509_tls_peers
, id
);
1165 crt
= purple_certificate_import(x509
, keypath
);
1173 x509_tls_peers_put_cert(const gchar
*id
, PurpleCertificate
*crt
)
1175 gboolean ret
= FALSE
;
1178 g_return_val_if_fail(crt
, FALSE
);
1179 g_return_val_if_fail(crt
->scheme
, FALSE
);
1180 /* Make sure that this is some kind of X.509 certificate */
1181 /* TODO: Perhaps just check crt->scheme->name instead? */
1182 g_return_val_if_fail(crt
->scheme
== purple_certificate_find_scheme(x509_tls_peers
.scheme_name
), FALSE
);
1184 /* Work out the filename and export */
1185 keypath
= purple_certificate_pool_mkpath(&x509_tls_peers
, id
);
1186 ret
= purple_certificate_export(keypath
, crt
);
1193 x509_tls_peers_delete_cert(const gchar
*id
)
1195 gboolean ret
= FALSE
;
1198 g_return_val_if_fail(id
, FALSE
);
1200 /* Is the id even in the pool? */
1201 if (!x509_tls_peers_cert_in_pool(id
)) {
1202 purple_debug_warning("certificate/tls_peers",
1203 "Id %s wasn't in the pool\n",
1208 /* OK, so work out the keypath and delete the thing */
1209 keypath
= purple_certificate_pool_mkpath(&x509_tls_peers
, id
);
1210 if ( unlink(keypath
) != 0 ) {
1211 purple_debug_error("certificate/tls_peers",
1212 "Unlink of %s failed!\n",
1224 x509_tls_peers_get_idlist(void)
1226 GList
*idlist
= NULL
;
1231 /* Get a handle on the pool directory */
1232 poolpath
= purple_certificate_pool_mkpath(&x509_tls_peers
, NULL
);
1233 dir
= g_dir_open(poolpath
,
1235 NULL
); /* Not interested in what the error is */
1238 g_return_val_if_fail(dir
, NULL
);
1240 /* Traverse the directory listing and create an idlist */
1241 while ( (entry
= g_dir_read_name(dir
)) != NULL
) {
1242 /* Unescape the filename */
1243 const char *unescaped
= purple_unescape_filename(entry
);
1245 /* Copy the entry name into our list (GLib owns the original
1247 idlist
= g_list_prepend(idlist
, g_strdup(unescaped
));
1250 /* Release the directory */
1256 static PurpleCertificatePool x509_tls_peers
= {
1257 "x509", /* Scheme name */
1258 "tls_peers", /* Pool name */
1259 N_("SSL Peers Cache"), /* User-friendly name */
1260 NULL
, /* Internal data */
1261 x509_tls_peers_init
, /* init */
1262 NULL
, /* uninit not required */
1263 x509_tls_peers_cert_in_pool
, /* Certificate exists? */
1264 x509_tls_peers_get_cert
, /* Cert retriever */
1265 x509_tls_peers_put_cert
, /* Cert writer */
1266 x509_tls_peers_delete_cert
, /* Cert remover */
1267 x509_tls_peers_get_idlist
, /* idlist retriever */
1276 /***** A Verifier that uses the tls_peers cache and the CA pool to validate certificates *****/
1277 static PurpleCertificateVerifier x509_tls_cached
;
1280 /* The following is several hacks piled together and needs to be fixed.
1281 * It exists because show_cert (see its comments) needs the original reason
1282 * given to user_auth in order to rebuild the dialog.
1284 /* TODO: This will cause a ua_ctx to become memleaked if the request(s) get
1285 closed by handle or otherwise abnormally. */
1287 PurpleCertificateVerificationRequest
*vrq
;
1289 } x509_tls_cached_ua_ctx
;
1291 static x509_tls_cached_ua_ctx
*
1292 x509_tls_cached_ua_ctx_new(PurpleCertificateVerificationRequest
*vrq
,
1293 const gchar
*reason
)
1295 x509_tls_cached_ua_ctx
*c
;
1297 c
= g_new0(x509_tls_cached_ua_ctx
, 1);
1299 c
->reason
= g_strdup(reason
);
1306 x509_tls_cached_ua_ctx_free(x509_tls_cached_ua_ctx
*c
)
1308 g_return_if_fail(c
);
1314 x509_tls_cached_user_auth(PurpleCertificateVerificationRequest
*vrq
,
1315 const gchar
*reason
);
1318 x509_tls_cached_show_cert(x509_tls_cached_ua_ctx
*c
, gint id
)
1320 PurpleCertificate
*disp_crt
= c
->vrq
->cert_chain
->data
;
1322 /* Since clicking a button closes the request, show it again */
1323 x509_tls_cached_user_auth(c
->vrq
, c
->reason
);
1325 /* Show the certificate AFTER re-opening the dialog so that this
1326 appears above the other */
1327 purple_certificate_display_x509(disp_crt
);
1329 x509_tls_cached_ua_ctx_free(c
);
1333 x509_tls_cached_user_auth_cb (x509_tls_cached_ua_ctx
*c
, gint id
)
1335 PurpleCertificateVerificationRequest
*vrq
;
1336 PurpleCertificatePool
*tls_peers
;
1338 g_return_if_fail(c
);
1339 g_return_if_fail(c
->vrq
);
1343 x509_tls_cached_ua_ctx_free(c
);
1345 tls_peers
= purple_certificate_find_pool("x509","tls_peers");
1348 gchar
*cache_id
= vrq
->subject_name
;
1349 purple_debug_info("certificate/x509/tls_cached",
1350 "User ACCEPTED cert\nCaching first in chain for future use as %s...\n",
1353 purple_certificate_pool_store(tls_peers
, cache_id
,
1354 vrq
->cert_chain
->data
);
1356 purple_certificate_verify_complete(vrq
,
1357 PURPLE_CERTIFICATE_VALID
);
1359 purple_debug_warning("certificate/x509/tls_cached",
1360 "User REJECTED cert\n");
1361 purple_certificate_verify_complete(vrq
,
1362 PURPLE_CERTIFICATE_INVALID
);
1367 x509_tls_cached_user_auth_accept_cb(x509_tls_cached_ua_ctx
*c
, gint ignore
)
1369 x509_tls_cached_user_auth_cb(c
, 2);
1373 x509_tls_cached_user_auth_reject_cb(x509_tls_cached_ua_ctx
*c
, gint ignore
)
1375 x509_tls_cached_user_auth_cb(c
, 1);
1378 /** Validates a certificate by asking the user
1379 * @param reason String to explain why the user needs to accept/refuse the
1381 * @todo Needs a handle argument
1384 x509_tls_cached_user_auth(PurpleCertificateVerificationRequest
*vrq
,
1385 const gchar
*reason
)
1390 primary
= g_strdup_printf(_("Accept certificate for %s?"),
1393 /* Make a semi-pretty display */
1394 purple_request_action(
1395 vrq
->cb_data
, /* TODO: Find what the handle ought to be */
1396 _("SSL Certificate Verification"),
1399 0, /* Accept by default */
1400 NULL
, /* No account */
1401 NULL
, /* No other user */
1402 NULL
, /* No associated conversation */
1403 x509_tls_cached_ua_ctx_new(vrq
, reason
),
1404 3, /* Number of actions */
1405 _("Accept"), x509_tls_cached_user_auth_accept_cb
,
1406 _("Reject"), x509_tls_cached_user_auth_reject_cb
,
1407 _("_View Certificate..."), x509_tls_cached_show_cert
);
1414 x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest
*vrq
,
1415 PurpleCertificateInvalidityFlags flags
);
1418 x509_tls_cached_complete(PurpleCertificateVerificationRequest
*vrq
,
1419 PurpleCertificateInvalidityFlags flags
)
1421 PurpleCertificatePool
*tls_peers
;
1422 PurpleCertificate
*peer_crt
= vrq
->cert_chain
->data
;
1424 if (flags
& PURPLE_CERTIFICATE_FATALS_MASK
) {
1425 /* TODO: Also print any other warnings? */
1427 gchar
*tmp
, *secondary
;
1429 if (flags
& PURPLE_CERTIFICATE_INVALID_CHAIN
)
1430 error
= invalidity_reason_to_string(PURPLE_CERTIFICATE_INVALID_CHAIN
);
1431 else if (flags
& PURPLE_CERTIFICATE_REVOKED
)
1432 error
= invalidity_reason_to_string(PURPLE_CERTIFICATE_REVOKED
);
1434 error
= invalidity_reason_to_string(PURPLE_CERTIFICATE_UNKNOWN_ERROR
);
1436 tmp
= g_strdup_printf(_("The certificate for %s could not be validated."),
1438 secondary
= g_strconcat(tmp
, " ", error
, NULL
);
1441 purple_notify_error(NULL
, /* TODO: Probably wrong. */
1442 _("SSL Certificate Error"),
1443 _("Unable to validate certificate"),
1447 purple_certificate_verify_complete(vrq
, PURPLE_CERTIFICATE_INVALID
);
1449 } else if (flags
& PURPLE_CERTIFICATE_NON_FATALS_MASK
) {
1450 /* Non-fatal error. Prompt the user. */
1455 tmp
= g_strdup_printf(_("The certificate for %s could not be validated."),
1457 errors
= g_string_new(tmp
);
1460 errors
= g_string_append_c(errors
, '\n');
1462 /* Special case a name mismatch because we want to display the two names... */
1463 if (flags
& PURPLE_CERTIFICATE_NAME_MISMATCH
) {
1464 gchar
*sn
= purple_certificate_get_subject_name(peer_crt
);
1467 g_string_append_printf(errors
, _("The certificate claims to be "
1468 "from \"%s\" instead. This could mean that you are "
1469 "not connecting to the service you believe you are."),
1473 flags
&= ~PURPLE_CERTIFICATE_NAME_MISMATCH
;
1477 while (i
!= PURPLE_CERTIFICATE_LAST
) {
1479 errors
= g_string_append_c(errors
, '\n');
1480 g_string_append(errors
, invalidity_reason_to_string(i
));
1486 x509_tls_cached_user_auth(vrq
, errors
->str
);
1487 g_string_free(errors
, TRUE
);
1491 /* If we reach this point, the certificate is good. */
1493 /* Look up the local cache and store it there for future use */
1494 tls_peers
= purple_certificate_find_pool(x509_tls_cached
.scheme_name
,
1497 if (!purple_certificate_pool_store(tls_peers
,vrq
->subject_name
,
1499 purple_debug_error("certificate/x509/tls_cached",
1500 "FAILED to cache peer certificate\n");
1503 purple_debug_error("certificate/x509/tls_cached",
1504 "Unable to locate tls_peers certificate cache.\n");
1507 purple_certificate_verify_complete(vrq
, PURPLE_CERTIFICATE_VALID
);
1511 x509_tls_cached_cert_in_cache(PurpleCertificateVerificationRequest
*vrq
,
1512 PurpleCertificateInvalidityFlags flags
)
1514 /* TODO: Looking this up by name over and over is expensive.
1516 PurpleCertificatePool
*tls_peers
=
1517 purple_certificate_find_pool(x509_tls_cached
.scheme_name
,
1520 /* The peer's certificate should be the first in the list */
1521 PurpleCertificate
*peer_crt
=
1522 (PurpleCertificate
*) vrq
->cert_chain
->data
;
1524 PurpleCertificate
*cached_crt
;
1525 GByteArray
*peer_fpr
, *cached_fpr
;
1527 /* Load up the cached certificate */
1528 cached_crt
= purple_certificate_pool_retrieve(
1529 tls_peers
, vrq
->subject_name
);
1530 if ( !cached_crt
) {
1531 purple_debug_warning("certificate/x509/tls_cached",
1532 "Lookup failed on cached certificate!\n"
1533 "Falling back to full verification.\n");
1534 /* vrq now becomes the problem of unknown_peer */
1535 x509_tls_cached_unknown_peer(vrq
, flags
);
1539 /* Now get SHA1 sums for both and compare them */
1540 /* TODO: This is not an elegant way to compare certs */
1541 peer_fpr
= purple_certificate_get_fingerprint_sha1(peer_crt
);
1542 cached_fpr
= purple_certificate_get_fingerprint_sha1(cached_crt
);
1543 if (!memcmp(peer_fpr
->data
, cached_fpr
->data
, peer_fpr
->len
)) {
1544 purple_debug_info("certificate/x509/tls_cached",
1545 "Peer cert matched cached\n");
1546 x509_tls_cached_complete(vrq
, flags
);
1548 purple_debug_error("certificate/x509/tls_cached",
1549 "Peer cert did NOT match cached\n");
1550 /* vrq now becomes the problem of the user */
1551 x509_tls_cached_unknown_peer(vrq
, flags
);
1554 purple_certificate_destroy(cached_crt
);
1555 g_byte_array_free(peer_fpr
, TRUE
);
1556 g_byte_array_free(cached_fpr
, TRUE
);
1560 * This is called from two points in x509_tls_cached_unknown_peer below
1561 * once we've verified the signature chain is valid. Now we need to verify
1562 * the subject name of the certificate.
1565 x509_tls_cached_check_subject_name(PurpleCertificateVerificationRequest
*vrq
,
1566 PurpleCertificateInvalidityFlags flags
)
1568 PurpleCertificate
*peer_crt
;
1569 GList
*chain
= vrq
->cert_chain
;
1571 peer_crt
= (PurpleCertificate
*) chain
->data
;
1573 /* Last, check that the hostname matches */
1574 if ( ! purple_certificate_check_subject_name(peer_crt
,
1575 vrq
->subject_name
) ) {
1576 gchar
*sn
= purple_certificate_get_subject_name(peer_crt
);
1578 flags
|= PURPLE_CERTIFICATE_NAME_MISMATCH
;
1579 purple_debug_error("certificate/x509/tls_cached",
1580 "Name mismatch: Certificate given for %s "
1581 "has a name of %s\n",
1582 vrq
->subject_name
, sn
);
1586 x509_tls_cached_complete(vrq
, flags
);
1589 /* For when we've never communicated with this party before */
1590 /* TODO: Need ways to specify possibly multiple problems with a cert, or at
1591 least reprioritize them.
1594 x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest
*vrq
,
1595 PurpleCertificateInvalidityFlags flags
)
1597 PurpleCertificatePool
*ca
;
1598 PurpleCertificate
*peer_crt
;
1599 PurpleCertificate
*ca_crt
, *end_crt
;
1600 PurpleCertificate
*failing_crt
;
1601 GList
*chain
= vrq
->cert_chain
;
1602 GSList
*ca_crts
, *cur
;
1603 GByteArray
*last_fpr
, *ca_fpr
;
1604 gboolean valid
= FALSE
;
1605 gchar
*ca_id
, *ca2_id
;
1607 peer_crt
= (PurpleCertificate
*) chain
->data
;
1609 /* TODO: Figure out a way to check for a bad signature, as opposed to
1610 "not self-signed" */
1611 if ( purple_certificate_signed_by(peer_crt
, peer_crt
) ) {
1612 flags
|= PURPLE_CERTIFICATE_SELF_SIGNED
;
1614 purple_debug_info("certificate/x509/tls_cached",
1615 "Certificate for %s is self-signed.\n",
1618 x509_tls_cached_check_subject_name(vrq
, flags
);
1620 } /* if (self signed) */
1622 ca
= purple_certificate_find_pool(x509_tls_cached
.scheme_name
, "ca");
1624 /* Next, check that the certificate chain is valid */
1625 if (!purple_certificate_check_signature_chain_with_failing(chain
,
1628 gboolean chain_validated
= FALSE
;
1630 * Check if the failing certificate is in the CA store. If it is, then
1631 * consider this fully validated. This works around issues with some
1632 * prominent intermediate CAs whose signature is md5WithRSAEncryption.
1633 * I'm looking at CACert Class 3 here. See #4458 for details.
1636 gchar
*uid
= purple_certificate_get_unique_id(failing_crt
);
1637 PurpleCertificate
*ca_crt
= purple_certificate_pool_retrieve(ca
, uid
);
1638 if (ca_crt
!= NULL
) {
1639 GByteArray
*failing_fpr
;
1641 failing_fpr
= purple_certificate_get_fingerprint_sha1(failing_crt
);
1642 ca_fpr
= purple_certificate_get_fingerprint_sha1(ca_crt
);
1643 if (byte_arrays_equal(failing_fpr
, ca_fpr
)) {
1644 purple_debug_info("certificate/x509/tls_cached",
1645 "Full chain verification failed (probably a bad "
1646 "signature algorithm), but found the last "
1647 "certificate %s in the CA pool.\n", uid
);
1648 chain_validated
= TRUE
;
1651 g_byte_array_free(failing_fpr
, TRUE
);
1652 g_byte_array_free(ca_fpr
, TRUE
);
1655 purple_certificate_destroy(ca_crt
);
1660 * If we get here, either the cert matched the stuff right above
1661 * or it didn't, in which case we give up and complain to the user.
1663 if (!chain_validated
)
1664 /* TODO: Tell the user where the chain broke? */
1665 flags
|= PURPLE_CERTIFICATE_INVALID_CHAIN
;
1667 x509_tls_cached_check_subject_name(vrq
, flags
);
1669 } /* if (signature chain not good) */
1671 /* Next, attempt to verify the last certificate is signed by a trusted
1672 * CA, or is a trusted CA (based on fingerprint).
1674 /* If, for whatever reason, there is no Certificate Authority pool
1675 loaded, we'll verify the subject name and then warn about thsi. */
1677 purple_debug_error("certificate/x509/tls_cached",
1678 "No X.509 Certificate Authority pool "
1679 "could be found!\n");
1681 flags
|= PURPLE_CERTIFICATE_NO_CA_POOL
;
1683 x509_tls_cached_check_subject_name(vrq
, flags
);
1687 end_crt
= g_list_last(chain
)->data
;
1689 /* Attempt to look up the last certificate, and the last certificate's
1692 ca_id
= purple_certificate_get_issuer_unique_id(end_crt
);
1693 ca2_id
= purple_certificate_get_unique_id(end_crt
);
1694 purple_debug_info("certificate/x509/tls_cached",
1695 "Checking for a CA with DN=%s\n",
1697 purple_debug_info("certificate/x509/tls_cached",
1698 "Also checking for a CA with DN=%s\n",
1700 ca_crts
= g_slist_concat(x509_ca_get_certs(ca_id
), x509_ca_get_certs(ca2_id
));
1703 if ( NULL
== ca_crts
) {
1704 flags
|= PURPLE_CERTIFICATE_CA_UNKNOWN
;
1706 purple_debug_warning("certificate/x509/tls_cached",
1707 "No Certificate Authorities with either DN found "
1708 "found. I'll prompt the user, I guess.\n");
1710 x509_tls_cached_check_subject_name(vrq
, flags
);
1715 * Check the fingerprints; if they match, then this certificate *is* one
1716 * of the designated "trusted roots", and we don't need to verify the
1717 * signature. This is good because some of the older roots are self-signed
1718 * with bad hash algorithms that we don't want to allow in any other
1719 * circumstances (one of Verisign's root CAs is self-signed with MD2).
1721 * If the fingerprints don't match, we'll fall back to checking the
1724 last_fpr
= purple_certificate_get_fingerprint_sha1(end_crt
);
1725 for (cur
= ca_crts
; cur
; cur
= cur
->next
) {
1727 ca_fpr
= purple_certificate_get_fingerprint_sha1(ca_crt
);
1729 if ( byte_arrays_equal(last_fpr
, ca_fpr
) ||
1730 purple_certificate_signed_by(end_crt
, ca_crt
) )
1732 /* TODO: If signed_by ever returns a reason, maybe mention
1734 /* TODO: Also mention the CA involved. While I could do this
1735 now, a full DN is a little much with which to assault the
1736 user's poor, leaky eyes. */
1738 g_byte_array_free(ca_fpr
, TRUE
);
1742 g_byte_array_free(ca_fpr
, TRUE
);
1746 flags
|= PURPLE_CERTIFICATE_INVALID_CHAIN
;
1748 g_slist_foreach(ca_crts
, (GFunc
)purple_certificate_destroy
, NULL
);
1749 g_slist_free(ca_crts
);
1750 g_byte_array_free(last_fpr
, TRUE
);
1752 x509_tls_cached_check_subject_name(vrq
, flags
);
1756 x509_tls_cached_start_verify(PurpleCertificateVerificationRequest
*vrq
)
1758 const gchar
*tls_peers_name
= "tls_peers"; /* Name of local cache */
1759 PurpleCertificatePool
*tls_peers
;
1760 time_t now
, activation
, expiration
;
1761 PurpleCertificateInvalidityFlags flags
= PURPLE_CERTIFICATE_NO_PROBLEMS
;
1764 g_return_if_fail(vrq
);
1766 purple_debug_info("certificate/x509/tls_cached",
1767 "Starting verify for %s\n",
1771 * Verify the first certificate (the main one) has been activated and
1772 * isn't expired, i.e. activation < now < expiration.
1775 ret
= purple_certificate_get_times(vrq
->cert_chain
->data
, &activation
,
1778 flags
|= PURPLE_CERTIFICATE_EXPIRED
| PURPLE_CERTIFICATE_NOT_ACTIVATED
;
1779 purple_debug_error("certificate/x509/tls_cached",
1780 "Failed to get validity times for certificate %s\n",
1782 } else if (now
> expiration
) {
1783 flags
|= PURPLE_CERTIFICATE_EXPIRED
;
1784 purple_debug_error("certificate/x509/tls_cached",
1785 "Certificate %s expired at %s\n",
1786 vrq
->subject_name
, ctime(&expiration
));
1787 } else if (now
< activation
) {
1788 flags
|= PURPLE_CERTIFICATE_NOT_ACTIVATED
;
1789 purple_debug_error("certificate/x509/tls_cached",
1790 "Certificate %s is not yet valid, will be at %s\n",
1791 vrq
->subject_name
, ctime(&activation
));
1794 tls_peers
= purple_certificate_find_pool(x509_tls_cached
.scheme_name
,tls_peers_name
);
1797 purple_debug_error("certificate/x509/tls_cached",
1798 "Couldn't find local peers cache %s\n",
1801 /* vrq now becomes the problem of unknown_peer */
1802 x509_tls_cached_unknown_peer(vrq
, flags
);
1806 /* Check if the peer has a certificate cached already */
1807 purple_debug_info("certificate/x509/tls_cached",
1808 "Checking for cached cert...\n");
1809 if (purple_certificate_pool_contains(tls_peers
, vrq
->subject_name
)) {
1810 purple_debug_info("certificate/x509/tls_cached",
1811 "...Found cached cert\n");
1812 /* vrq is now the responsibility of cert_in_cache */
1813 x509_tls_cached_cert_in_cache(vrq
, flags
);
1815 purple_debug_warning("certificate/x509/tls_cached",
1816 "...Not in cache\n");
1817 /* vrq now becomes the problem of unknown_peer */
1818 x509_tls_cached_unknown_peer(vrq
, flags
);
1823 x509_tls_cached_destroy_request(PurpleCertificateVerificationRequest
*vrq
)
1825 g_return_if_fail(vrq
);
1828 static PurpleCertificateVerifier x509_tls_cached
= {
1829 "x509", /* Scheme name */
1830 "tls_cached", /* Verifier name */
1831 x509_tls_cached_start_verify
, /* Verification begin */
1832 x509_tls_cached_destroy_request
,/* Request cleanup */
1841 /****************************************************************************/
1843 /****************************************************************************/
1845 purple_certificate_init(void)
1847 /* Register builtins */
1848 purple_certificate_register_verifier(&x509_singleuse
);
1849 purple_certificate_register_pool(&x509_ca
);
1850 purple_certificate_register_pool(&x509_tls_peers
);
1851 purple_certificate_register_verifier(&x509_tls_cached
);
1855 purple_certificate_uninit(void)
1857 /* Unregister all Verifiers */
1858 g_list_foreach(cert_verifiers
, (GFunc
)purple_certificate_unregister_verifier
, NULL
);
1860 /* Unregister all Pools */
1861 g_list_foreach(cert_pools
, (GFunc
)purple_certificate_unregister_pool
, NULL
);
1865 purple_certificate_get_handle(void)
1871 PurpleCertificateScheme
*
1872 purple_certificate_find_scheme(const gchar
*name
)
1874 PurpleCertificateScheme
*scheme
= NULL
;
1877 g_return_val_if_fail(name
, NULL
);
1879 /* Traverse the list of registered schemes and locate the
1880 one whose name matches */
1881 for(l
= cert_schemes
; l
; l
= l
->next
) {
1882 scheme
= (PurpleCertificateScheme
*)(l
->data
);
1884 /* Name matches? that's our man */
1885 if(!g_ascii_strcasecmp(scheme
->name
, name
))
1889 purple_debug_warning("certificate",
1890 "CertificateScheme %s requested but not found.\n",
1893 /* TODO: Signalling and such? */
1899 purple_certificate_get_schemes(void)
1901 return cert_schemes
;
1905 purple_certificate_register_scheme(PurpleCertificateScheme
*scheme
)
1907 g_return_val_if_fail(scheme
!= NULL
, FALSE
);
1909 /* Make sure no scheme is registered with the same name */
1910 if (purple_certificate_find_scheme(scheme
->name
) != NULL
) {
1914 /* Okay, we're golden. Register it. */
1915 cert_schemes
= g_list_prepend(cert_schemes
, scheme
);
1917 /* TODO: Signalling and such? */
1919 purple_debug_info("certificate",
1920 "CertificateScheme %s registered\n",
1927 purple_certificate_unregister_scheme(PurpleCertificateScheme
*scheme
)
1929 if (NULL
== scheme
) {
1930 purple_debug_warning("certificate",
1931 "Attempting to unregister NULL scheme\n");
1935 /* TODO: signalling? */
1937 /* TODO: unregister all CertificateVerifiers for this scheme?*/
1938 /* TODO: unregister all CertificatePools for this scheme? */
1939 /* Neither of the above should be necessary, though */
1940 cert_schemes
= g_list_remove(cert_schemes
, scheme
);
1942 purple_debug_info("certificate",
1943 "CertificateScheme %s unregistered\n",
1950 PurpleCertificateVerifier
*
1951 purple_certificate_find_verifier(const gchar
*scheme_name
, const gchar
*ver_name
)
1953 PurpleCertificateVerifier
*vr
= NULL
;
1956 g_return_val_if_fail(scheme_name
, NULL
);
1957 g_return_val_if_fail(ver_name
, NULL
);
1959 /* Traverse the list of registered verifiers and locate the
1960 one whose name matches */
1961 for(l
= cert_verifiers
; l
; l
= l
->next
) {
1962 vr
= (PurpleCertificateVerifier
*)(l
->data
);
1964 /* Scheme and name match? */
1965 if(!g_ascii_strcasecmp(vr
->scheme_name
, scheme_name
) &&
1966 !g_ascii_strcasecmp(vr
->name
, ver_name
))
1970 purple_debug_warning("certificate",
1971 "CertificateVerifier %s, %s requested but not found.\n",
1972 scheme_name
, ver_name
);
1974 /* TODO: Signalling and such? */
1981 purple_certificate_get_verifiers(void)
1983 return cert_verifiers
;
1987 purple_certificate_register_verifier(PurpleCertificateVerifier
*vr
)
1989 g_return_val_if_fail(vr
!= NULL
, FALSE
);
1991 /* Make sure no verifier is registered with the same scheme/name */
1992 if (purple_certificate_find_verifier(vr
->scheme_name
, vr
->name
) != NULL
) {
1996 /* Okay, we're golden. Register it. */
1997 cert_verifiers
= g_list_prepend(cert_verifiers
, vr
);
1999 /* TODO: Signalling and such? */
2001 purple_debug_info("certificate",
2002 "CertificateVerifier %s registered\n",
2008 purple_certificate_unregister_verifier(PurpleCertificateVerifier
*vr
)
2011 purple_debug_warning("certificate",
2012 "Attempting to unregister NULL verifier\n");
2016 /* TODO: signalling? */
2018 cert_verifiers
= g_list_remove(cert_verifiers
, vr
);
2021 purple_debug_info("certificate",
2022 "CertificateVerifier %s unregistered\n",
2028 PurpleCertificatePool
*
2029 purple_certificate_find_pool(const gchar
*scheme_name
, const gchar
*pool_name
)
2031 PurpleCertificatePool
*pool
= NULL
;
2034 g_return_val_if_fail(scheme_name
, NULL
);
2035 g_return_val_if_fail(pool_name
, NULL
);
2037 /* Traverse the list of registered pools and locate the
2038 one whose name matches */
2039 for(l
= cert_pools
; l
; l
= l
->next
) {
2040 pool
= (PurpleCertificatePool
*)(l
->data
);
2042 /* Scheme and name match? */
2043 if(!g_ascii_strcasecmp(pool
->scheme_name
, scheme_name
) &&
2044 !g_ascii_strcasecmp(pool
->name
, pool_name
))
2048 purple_debug_warning("certificate",
2049 "CertificatePool %s, %s requested but not found.\n",
2050 scheme_name
, pool_name
);
2052 /* TODO: Signalling and such? */
2059 purple_certificate_get_pools(void)
2065 purple_certificate_register_pool(PurpleCertificatePool
*pool
)
2067 g_return_val_if_fail(pool
, FALSE
);
2068 g_return_val_if_fail(pool
->scheme_name
, FALSE
);
2069 g_return_val_if_fail(pool
->name
, FALSE
);
2070 g_return_val_if_fail(pool
->fullname
, FALSE
);
2072 /* Make sure no pools are registered under this name */
2073 if (purple_certificate_find_pool(pool
->scheme_name
, pool
->name
)) {
2077 /* Initialize the pool if needed */
2081 success
= pool
->init();
2086 /* Register the Pool */
2087 cert_pools
= g_list_prepend(cert_pools
, pool
);
2089 /* TODO: Emit a signal that the pool got registered */
2091 PURPLE_DBUS_REGISTER_POINTER(pool
, PurpleCertificatePool
);
2092 purple_signal_register(pool
, /* Signals emitted from pool */
2093 "certificate-stored",
2094 purple_marshal_VOID__POINTER_POINTER
,
2095 NULL
, /* No callback return value */
2096 2, /* Two non-data arguments */
2097 purple_value_new(PURPLE_TYPE_SUBTYPE
,
2098 PURPLE_SUBTYPE_CERTIFICATEPOOL
),
2099 purple_value_new(PURPLE_TYPE_STRING
));
2101 purple_signal_register(pool
, /* Signals emitted from pool */
2102 "certificate-deleted",
2103 purple_marshal_VOID__POINTER_POINTER
,
2104 NULL
, /* No callback return value */
2105 2, /* Two non-data arguments */
2106 purple_value_new(PURPLE_TYPE_SUBTYPE
,
2107 PURPLE_SUBTYPE_CERTIFICATEPOOL
),
2108 purple_value_new(PURPLE_TYPE_STRING
));
2110 purple_debug_info("certificate",
2111 "CertificatePool %s registered\n",
2118 purple_certificate_unregister_pool(PurpleCertificatePool
*pool
)
2121 purple_debug_warning("certificate",
2122 "Attempting to unregister NULL pool\n");
2126 /* Check that the pool is registered */
2127 if (!g_list_find(cert_pools
, pool
)) {
2128 purple_debug_warning("certificate",
2129 "Pool to unregister isn't registered!\n");
2134 /* Uninit the pool if needed */
2135 PURPLE_DBUS_UNREGISTER_POINTER(pool
);
2140 cert_pools
= g_list_remove(cert_pools
, pool
);
2142 /* TODO: Signalling? */
2143 purple_signal_unregister(pool
, "certificate-stored");
2144 purple_signal_unregister(pool
, "certificate-deleted");
2146 purple_debug_info("certificate",
2147 "CertificatePool %s unregistered\n",
2152 /****************************************************************************/
2153 /* Scheme-specific functions */
2154 /****************************************************************************/
2157 purple_certificate_display_x509(PurpleCertificate
*crt
)
2160 GByteArray
*sha_bin
;
2162 time_t activation
, expiration
;
2163 gchar
*activ_str
, *expir_str
;
2166 /* Pull out the SHA1 checksum */
2167 sha_bin
= purple_certificate_get_fingerprint_sha1(crt
);
2168 /* Now decode it for display */
2169 sha_asc
= purple_base16_encode_chunked(sha_bin
->data
,
2172 /* Get the cert Common Name */
2173 /* TODO: Will break on CA certs */
2174 cn
= purple_certificate_get_subject_name(crt
);
2176 /* Get the certificate times */
2177 /* TODO: Check the times against localtime */
2178 /* TODO: errorcheck? */
2179 if (!purple_certificate_get_times(crt
, &activation
, &expiration
)) {
2180 purple_debug_error("certificate",
2181 "Failed to get certificate times!\n");
2182 activation
= expiration
= 0;
2184 activ_str
= g_strdup(ctime(&activation
));
2185 expir_str
= g_strdup(ctime(&expiration
));
2188 secondary
= g_strdup_printf(_("Common name: %s\n\n"
2189 "Fingerprint (SHA1): %s\n\n"
2190 "Activation date: %s\n"
2191 "Expiration date: %s\n"),
2193 sha_asc
? sha_asc
: "(null)",
2194 activ_str
? activ_str
: "(null)",
2195 expir_str
? expir_str
: "(null)");
2197 /* Make a semi-pretty display */
2199 NULL
, /* TODO: Find what the handle ought to be */
2200 _("Certificate Information"),
2210 g_byte_array_free(sha_bin
, TRUE
);
2213 void purple_certificate_add_ca_search_path(const char *path
)
2215 if (g_list_find_custom(x509_ca_paths
, path
, (GCompareFunc
)strcmp
))
2217 x509_ca_paths
= g_list_append(x509_ca_paths
, g_strdup(path
));