# HG changeset patch # User William Ehlhardt # Date 1182529830 0 # Node ID f46ec4d6a38580432435caae2a1d2e4578d22f95 # Parent a573a67c80a4f333861f9585c37cc355b7ad7c8a# Parent f2ddc4b10d72cd0b3a51b0e4e588686cd426495a propagate from branch 'im.pidgin.pidgin' (head d2f50519c5ed668dd980277afdc25d71ccb8a852) to branch 'im.pidgin.soc.2007.certmgr' (head ed3c7d774326b4d168945508111f230f60c79c37) diff -r a573a67c80a4 -r f46ec4d6a385 libpurple/Makefile.am --- a/libpurple/Makefile.am Thu Jun 21 18:08:48 2007 +0000 +++ b/libpurple/Makefile.am Fri Jun 22 16:30:30 2007 +0000 @@ -36,6 +36,7 @@ accountopt.c \ blist.c \ buddyicon.c \ + certificate.c \ cipher.c \ circbuffer.c \ cmds.c \ @@ -85,6 +86,7 @@ accountopt.h \ blist.h \ buddyicon.h \ + certificate.h \ cipher.h \ circbuffer.h \ cmds.h \ diff -r a573a67c80a4 -r f46ec4d6a385 libpurple/certificate.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/certificate.c Fri Jun 22 16:30:30 2007 +0000 @@ -0,0 +1,95 @@ +/** + * @file certificate.h Public-Key Certificate API + * @ingroup core + */ + +/* + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "certificate.h" +#include "debug.h" + +/** List holding pointers to all registered certificate schemes */ +static GList *cert_schemes = NULL; + +PurpleCertificateScheme * +purple_certificate_find_scheme(const gchar *name) +{ + PurpleCertificateScheme *scheme = NULL; + GList *l; + + g_return_val_if_fail(name, NULL); + + /* Traverse the list of registered schemes and locate the + one whose name matches */ + for(l = cert_schemes; l; l = l->next) { + scheme = (PurpleCertificateScheme *)(l->data); + + /* Name matches? that's our man */ + if(!g_ascii_strcasecmp(scheme->name, name)) + return scheme; + } + + purple_debug_warning("certificate", + "CertificateScheme %s requested but not found.\n", + name); + + /* TODO: Signalling and such? */ + + return NULL; +} + +gboolean +purple_certificate_register_scheme(PurpleCertificateScheme *scheme) +{ + g_return_val_if_fail(scheme != NULL, FALSE); + + /* Make sure no scheme is registered with the same name */ + if (purple_certificate_find_scheme(scheme->name) != NULL) { + return FALSE; + } + + /* Okay, we're golden. Register it. */ + cert_schemes = g_list_append(cert_schemes, scheme); + + /* TODO: Signalling and such? */ + return TRUE; +} + +gboolean +purple_certificate_unregister_scheme(PurpleCertificateScheme *scheme) +{ + if (NULL == scheme) { + purple_debug_warning("certificate", + "Attempting to unregister NULL scheme"); + } + + /* TODO: signalling? */ + + /* TODO: unregister all CertificatePools for this scheme! */ + cert_schemes = g_list_remove(cert_schemes, scheme); + + return TRUE; +} diff -r a573a67c80a4 -r f46ec4d6a385 libpurple/certificate.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/certificate.h Fri Jun 22 16:30:30 2007 +0000 @@ -0,0 +1,233 @@ +/** + * @file certificate.h Public-Key Certificate API + * @ingroup core + */ + +/* + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PURPLE_CERTIFICATE_H +#define _PURPLE_CERTIFICATE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +typedef enum +{ + PURPLE_CERTIFICATE_INVALID = 0, + PURPLE_CERTIFICATE_VALID = 1 +} PurpleCertificateVerificationStatus; + +typedef struct _PurpleCertificate PurpleCertificate; +typedef struct _PurpleCertificateScheme PurpleCertificateScheme; +typedef struct _PurpleCertificateVerifier PurpleCertificateVerifier; +typedef struct _PurpleCertificateVerificationRequest PurpleCertificateVerificationRequest; + +/** A certificate instance + * + * An opaque data structure representing a single certificate under some + * CertificateScheme + */ +struct _PurpleCertificate +{ + /** Scheme this certificate is under */ + PurpleCertificateScheme * scheme; + /** Opaque pointer to internal data */ + gpointer data; +}; + +/** A certificate type + * + * A CertificateScheme must implement all of the fields in the structure, + * and register it using purple_certificate_register_scheme() + * + * There may be only ONE CertificateScheme provided for each certificate + * type, as specified by the "name" field. + */ +struct _PurpleCertificateScheme +{ + /** Name of the certificate type + * ex: "x509", "pgp", etc. + * This must be globally unique - you may not register more than one + * CertificateScheme of the same name at a time. + */ + gchar * name; + + /** User-friendly name for this type + * ex: N_("X.509 Certificates") + * When this is displayed anywhere, it should be i18ned + * ex: _(scheme->name) + */ + gchar * fullname; + + /** Imports a certificate from a file + * + * @param filename File to import the certificate from + * @return Pointer to the newly allocated Certificate struct + * or NULL on failure. + */ + PurpleCertificate * (* import_certificate)(const gchar * filename); + + /** Destroys and frees a Certificate structure + * + * Destroys a Certificate's internal data structures and calls + * free(crt) + * + * @param crt Certificate instance to be destroyed. It WILL NOT be + * destroyed if it is not of the correct + * CertificateScheme. Can be NULL + */ + void (* destroy_certificate)(PurpleCertificate * crt); + + /** + * Retrieves the certificate public key fingerprint using SHA1 + * + * @param crt Certificate instance + * @return Binary representation of SHA1 hash - must be freed using + * g_byte_array_free() + */ + GByteArray * (* get_fingerprint_sha1)(PurpleCertificate *crt); + + /** + * Reads "who the certificate is assigned to" + * + * For SSL X.509 certificates, this is something like + * "gmail.com" or "jabber.org" + * + * @param crt Certificate instance + * @return Newly allocated string specifying "whose certificate this + * is" + */ + gchar * (* get_certificate_subject)(PurpleCertificate *crt); + + /** + * Retrieves a unique certificate identifier + * + * @param crt Certificate instance + * @return Newly allocated string that can be used to uniquely + * identify the certificate. + */ + gchar * (* get_unique_id)(PurpleCertificate *crt); + + /** + * Retrieves a unique identifier for the certificate's issuer + * + * @param crt Certificate instance + * @return Newly allocated string that can be used to uniquely + * identify the issuer's certificate. + */ + gchar * (* get_issuer_unique_id)(PurpleCertificate *crt); + + + /* TODO: Fill out this structure */ +}; + +/** A set of operations used to provide logic for verifying a Certificate's + * authenticity. + */ +struct _PurpleCertificateVerifier +{ + /** Scheme this Verifier operates on */ + PurpleCertificateScheme *scheme; + + /** Internal name used for lookups + * + * Case insensitive + */ + gchar * name; +}; + +/** Structure for a single certificate request + * + * Useful for keeping track of the state of a verification that involves + * several steps + */ +struct _PurpleCertificateVerificationRequest +{ + /** Reference to the verification logic used */ + PurpleCertificateVerifier *verifier; + + /** Certificate subject's name. + * + * For X.509 certificates, this is the Common Name + */ + gchar *subject_name; + + /** List of certificates in the chain to be verified (such as that returned by purple_ssl_get_peer_certificates ) + * + * This is most relevant for X.509 certificates used in SSL sessions. + * The list order should be: certificate, issuer, issuer's issuer, etc. + */ + GList *cert_chain; + + /** Internal data used by the Verifier code */ + gpointer *data; +}; + +/*****************************************************************************/ +/** @name PurpleCertificate Subsystem API */ +/*****************************************************************************/ +/*@{*/ + +/** Look up a registered CertificateScheme by name + * @param name The scheme name. Case insensitive. + * @return Pointer to the located Scheme, or NULL if it isn't found. + */ +PurpleCertificateScheme * +purple_certificate_find_scheme(const gchar *name); + +/** Register a CertificateScheme with libpurple + * + * No two schemes can be registered with the same name; this function enforces + * that. + * + * @param scheme Pointer to the scheme to register. + * @return TRUE if the scheme was successfully added, otherwise FALSE + */ +gboolean +purple_certificate_register_scheme(PurpleCertificateScheme *scheme); + +/** Unregister a CertificateScheme from libpurple + * + * @param scheme Scheme to unregister. + * If the scheme is not registered, this is a no-op. + * + * @return TRUE if the unregister completed successfully + */ +gboolean +purple_certificate_unregister_scheme(PurpleCertificateScheme *scheme); + +/* TODO: ADD STUFF HERE */ + +/*@}*/ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _PURPLE_CERTIFICATE_H */ diff -r a573a67c80a4 -r f46ec4d6a385 libpurple/plugins/ssl/ssl-gnutls.c --- a/libpurple/plugins/ssl/ssl-gnutls.c Thu Jun 21 18:08:48 2007 +0000 +++ b/libpurple/plugins/ssl/ssl-gnutls.c Fri Jun 22 16:30:30 2007 +0000 @@ -21,15 +21,18 @@ */ #include "internal.h" #include "debug.h" +#include "certificate.h" #include "plugin.h" #include "sslconn.h" #include "version.h" +#include "util.h" #define SSL_GNUTLS_PLUGIN_ID "ssl-gnutls" #ifdef HAVE_GNUTLS #include +#include typedef struct { @@ -44,9 +47,25 @@ static void ssl_gnutls_init_gnutls(void) { + /* Configure GnuTLS to use glib memory management */ + /* I expect that this isn't really necessary, but it may prevent + some bugs */ + /* TODO: It may be necessary to wrap this allocators for GnuTLS. + If there are strange bugs, perhaps look here (yes, I am a + hypocrite) */ + gnutls_global_set_mem_functions( + (gnutls_alloc_function) g_malloc0, /* malloc */ + (gnutls_alloc_function) g_malloc0, /* secure malloc */ + NULL, /* mem_is_secure */ + (gnutls_realloc_function) g_realloc, /* realloc */ + (gnutls_free_function) g_free /* free */ + ); + gnutls_global_init(); gnutls_certificate_allocate_credentials(&xcred); + + /* TODO: I can likely remove this */ gnutls_certificate_set_x509_trust_file(xcred, "ca.pem", GNUTLS_X509_FMT_PEM); } @@ -73,7 +92,7 @@ PurpleSslGnutlsData *gnutls_data = PURPLE_SSL_GNUTLS_DATA(gsc); ssize_t ret; - purple_debug_info("gnutls", "Handshaking\n"); + purple_debug_info("gnutls", "Handshaking with %s\n", gsc->host); ret = gnutls_handshake(gnutls_data->session); if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) @@ -94,6 +113,96 @@ } else { purple_debug_info("gnutls", "Handshake complete\n"); + /* TODO: Remove all this debugging babble */ + /* Now we are cooking with gas! */ + PurpleSslOps *ops = purple_ssl_get_ops(); + GList * peers = ops->get_peer_certificates(gsc); + + PurpleCertificateScheme *x509 = + purple_certificate_find_scheme("x509"); + + GList * l; + for (l=peers; l; l = l->next) { + PurpleCertificate *crt = l->data; + GByteArray *z = + x509->get_fingerprint_sha1(crt); + gchar * fpr = + purple_base16_encode_chunked(z->data, + z->len); + + purple_debug_info("gnutls/x509", + "Key print: %s\n", + fpr); + + /* Kill the cert! */ + x509->destroy_certificate(crt); + + g_free(fpr); + g_byte_array_free(z, TRUE); + } + g_list_free(peers); + + { + const gnutls_datum_t *cert_list; + unsigned int cert_list_size = 0; + gnutls_session_t session=gnutls_data->session; + + cert_list = + gnutls_certificate_get_peers(session, &cert_list_size); + + purple_debug_info("gnutls", + "Peer provided %d certs\n", + cert_list_size); + int i; + for (i=0; iconnect_cb(gsc->connect_cb_data, gsc, cond); } @@ -213,6 +322,275 @@ return s; } +/* Forward declarations are fun! + TODO: This is a stupid place for this */ +static PurpleCertificate * +x509_import_from_datum(const gnutls_datum_t dt, gnutls_x509_crt_fmt_t mode); + +static GList * +ssl_gnutls_get_peer_certificates(PurpleSslConnection * gsc) +{ + PurpleSslGnutlsData *gnutls_data = PURPLE_SSL_GNUTLS_DATA(gsc); + + /* List of Certificate instances to return */ + GList * peer_certs = NULL; + + /* List of raw certificates as given by GnuTLS */ + const gnutls_datum_t *cert_list; + unsigned int cert_list_size = 0; + + unsigned int i; + + /* This should never, ever happen. */ + g_return_val_if_fail( gnutls_certificate_type_get (gnutls_data->session) == GNUTLS_CRT_X509, NULL); + + /* Get the certificate list from GnuTLS */ + /* TODO: I am _pretty sure_ this doesn't block or do other exciting things */ + cert_list = gnutls_certificate_get_peers(gnutls_data->session, + &cert_list_size); + + /* Convert each certificate to a Certificate and append it to the list */ + for (i = 0; i < cert_list_size; i++) { + PurpleCertificate * newcrt = x509_import_from_datum(cert_list[i], + GNUTLS_X509_FMT_DER); + /* Append is somewhat inefficient on linked lists, but is easy + to read. If someone complains, I'll change it. + TODO: Is anyone complaining? (Maybe elb?) */ + peer_certs = g_list_append(peer_certs, newcrt); + } + + /* cert_list shouldn't need free()-ing */ + /* TODO: double-check this */ + + return peer_certs; +} + +/************************************************************************/ +/* X.509 functionality */ +/************************************************************************/ +const gchar * SCHEME_NAME = "x509"; + +static PurpleCertificateScheme x509_gnutls; + +/** Transforms a gnutls_datum_t containing an X.509 certificate into a Certificate instance under the x509_gnutls scheme + * + * @param dt Datum to transform + * @param mode GnuTLS certificate format specifier (GNUTLS_X509_FMT_PEM for + * reading from files, and GNUTLS_X509_FMT_DER for converting + * "over the wire" certs for SSL) + * + * @return A newly allocated Certificate structure of the x509_gnutls scheme + */ +static PurpleCertificate * +x509_import_from_datum(const gnutls_datum_t dt, gnutls_x509_crt_fmt_t mode) +{ + /* Internal certificate data structure */ + gnutls_x509_crt_t *certdat; + /* New certificate to return */ + PurpleCertificate * crt; + + /* Allocate and prepare the internal certificate data */ + certdat = g_new(gnutls_x509_crt_t, 1); + gnutls_x509_crt_init(certdat); + + /* Perform the actual certificate parse */ + /* Yes, certdat SHOULD be dereferenced */ + gnutls_x509_crt_import(*certdat, &dt, mode); + + /* Allocate the certificate and load it with data */ + crt = g_new(PurpleCertificate, 1); + crt->scheme = &x509_gnutls; + crt->data = certdat; + + return crt; +} + +/** Imports a PEM-formatted X.509 certificate from the specified file. + * @param filename Filename to import from. Format is PEM + * + * @return A newly allocated Certificate structure of the x509_gnutls scheme + */ +static PurpleCertificate * +x509_import_from_file(const gchar * filename) +{ + PurpleCertificate *crt; /* Certificate being constructed */ + gchar *buf; /* Used to load the raw file data */ + gsize buf_sz; /* Size of the above */ + gnutls_datum_t dt; /* Struct to pass down to GnuTLS */ + + purple_debug_info("gnutls", + "Attempting to load X.509 certificate from %s\n", + filename); + + /* Next, we'll simply yank the entire contents of the file + into memory */ + /* TODO: Should I worry about very large files here? */ + /* TODO: Error checking */ + g_file_get_contents(filename, + &buf, + &buf_sz, + NULL /* No error checking for now */ + ); + + /* Load the datum struct */ + dt.data = (unsigned char *) buf; + dt.size = buf_sz; + + /* Perform the conversion */ + crt = x509_import_from_datum(dt, + GNUTLS_X509_FMT_PEM); // files should be in PEM format + + /* Cleanup */ + g_free(buf); + + return crt; +} + +/** Frees a Certificate + * + * Destroys a Certificate's internal data structures and frees the pointer + * given. + * @param crt Certificate instance to be destroyed. It WILL NOT be destroyed + * if it is not of the correct CertificateScheme. Can be NULL + * + */ +static void +x509_destroy_certificate(PurpleCertificate * crt) +{ + /* TODO: Issue a warning here? */ + if (NULL == crt) return; + + /* Check that the scheme is x509_gnutls */ + if ( crt->scheme != &x509_gnutls ) { + purple_debug_error("gnutls", + "destroy_certificate attempted on certificate of wrong scheme (scheme was %s, expected %s)\n", + crt->scheme->name, + SCHEME_NAME); + return; + } + + /* TODO: Different error checking? */ + g_return_if_fail(crt->data != NULL); + g_return_if_fail(crt->scheme != NULL); + + /* Destroy the GnuTLS-specific data */ + gnutls_x509_crt_deinit( *( (gnutls_x509_crt_t *) crt->data ) ); + g_free(crt->data); + + /* TODO: Reference counting here? */ + + /* Kill the structure itself */ + g_free(crt); +} + +/** Determines whether one certificate has been issued and signed by another + * + * @param crt Certificate to check the signature of + * @param issuer Issuer's certificate + * + * @return TRUE if crt was signed and issued by issuer, otherwise FALSE + * @TODO Modify this function to return a reason for invalidity? + */ +static gboolean +x509_certificate_signed_by(PurpleCertificate * crt, + PurpleCertificate * issuer) +{ + gnutls_x509_crt_t crt_dat; + gnutls_x509_crt_t issuer_dat; + unsigned int verify; /* used to store details from GnuTLS verifier */ + int ret; + + /* TODO: Change this error checking? */ + g_return_val_if_fail(crt, FALSE); + g_return_val_if_fail(issuer, FALSE); + + /* Verify that both certs are the correct scheme */ + g_return_val_if_fail(crt->scheme != &x509_gnutls, FALSE); + g_return_val_if_fail(issuer->scheme != &x509_gnutls, FALSE); + + /* TODO: check for more nullness? */ + + crt_dat = *((gnutls_x509_crt_t *) crt->data); + issuer_dat = *((gnutls_x509_crt_t *) issuer->data); + + /* First, let's check that crt.issuer is actually issuer */ + ret = gnutls_x509_crt_check_issuer(crt_dat, issuer_dat); + if (ret <= 0) { + + if (ret < 0) { + purple_debug_error("gnutls/x509", + "GnuTLS error %d while checking certificate issuer match.", + ret); + } + + /* The issuer is not correct, or there were errors */ + return FALSE; + } + + /* Now, check the signature */ + /* The second argument is a ptr to an array of "trusted" issuer certs, + but we're only using one trusted one */ + ret = gnutls_x509_crt_verify(crt_dat, &issuer_dat, 1, 0, &verify); + + if (ret > 0) { + /* The certificate is good. */ + return TRUE; + } + else if (ret < 0) { + purple_debug_error("gnutls/x509", + "Attempted certificate verification caused a GnuTLS error code %d. I will just say the signature is bad, but you should look into this.\n", ret); + return FALSE; + } + else { + /* Signature didn't check out, but at least + there were no errors*/ + return FALSE; + } /* if (ret, etc.) */ + + /* Control does not reach this point */ +} + +static GByteArray * +x509_sha1sum(PurpleCertificate *crt) +{ + size_t hashlen = 20; /* SHA1 hashes are 20 bytes */ + size_t tmpsz = hashlen; /* Throw-away variable for GnuTLS to stomp on*/ + gnutls_x509_crt_t crt_dat; + GByteArray *hash; /**< Final hash container */ + guchar hashbuf[hashlen]; /**< Temporary buffer to contain hash */ + + g_return_val_if_fail(crt, NULL); + + crt_dat = *( (gnutls_x509_crt_t *) crt->data ); + + /* Extract the fingerprint */ + /* TODO: Errorcheck? */ + gnutls_x509_crt_get_fingerprint(crt_dat, GNUTLS_MAC_SHA, + hashbuf, &tmpsz); + + /* This shouldn't happen */ + g_return_val_if_fail(tmpsz == hashlen, NULL); + + /* Okay, now create and fill hash array */ + hash = g_byte_array_new(); + g_byte_array_append(hash, hashbuf, hashlen); + + return hash; +} + +/* X.509 certificate operations provided by this plugin */ +/* TODO: Flesh this out! */ +static PurpleCertificateScheme x509_gnutls = { + "x509", /* Scheme name */ + N_("X.509 Certificates"), /* User-visible scheme name */ + x509_import_from_file, /* Certificate import function */ + x509_destroy_certificate, /* Destroy cert */ + x509_sha1sum, /* SHA1 fingerprint */ + NULL, /* Subject */ + NULL, /* Unique ID */ + NULL /* Issuer Unique ID */ +}; + static PurpleSslOps ssl_ops = { ssl_gnutls_init, @@ -221,11 +599,11 @@ ssl_gnutls_close, ssl_gnutls_read, ssl_gnutls_write, + ssl_gnutls_get_peer_certificates, /* padding */ NULL, NULL, - NULL, NULL }; @@ -235,6 +613,10 @@ plugin_load(PurplePlugin *plugin) { #ifdef HAVE_GNUTLS + /* Register that we're providing an X.509 CertScheme */ + /* @TODO : error checking */ + purple_certificate_register_scheme( &x509_gnutls ); + if(!purple_ssl_get_ops()) { purple_ssl_set_ops(&ssl_ops); } @@ -255,6 +637,8 @@ if(purple_ssl_get_ops() == &ssl_ops) { purple_ssl_set_ops(NULL); } + + purple_certificate_unregister_scheme( &x509_gnutls ); #endif return TRUE; diff -r a573a67c80a4 -r f46ec4d6a385 libpurple/prefs.h --- a/libpurple/prefs.h Thu Jun 21 18:08:48 2007 +0000 +++ b/libpurple/prefs.h Fri Jun 22 16:30:30 2007 +0000 @@ -55,7 +55,9 @@ #endif /**************************************************************************/ -/** @name Prefs API */ +/** @name Prefs API + Preferences are named according to a directory-like structure. + Example: "/plugins/core/potato/is_from_idaho" (probably a boolean) */ /**************************************************************************/ /*@{*/ diff -r a573a67c80a4 -r f46ec4d6a385 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Thu Jun 21 18:08:48 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.c Fri Jun 22 16:30:30 2007 +0000 @@ -508,15 +508,13 @@ { purple_input_remove(js->gc->inpa); js->gc->inpa = 0; - js->gsc = purple_ssl_connect_fd(js->gc->account, js->fd, - jabber_login_callback_ssl, jabber_ssl_connect_failure, js->gc); + js->gsc = purple_ssl_connect_with_host_fd(js->gc->account, js->fd, + jabber_login_callback_ssl, jabber_ssl_connect_failure, js->serverFQDN, js->gc); } static void jabber_login_connect(JabberStream *js, const char *fqdn, const char *host, int port) { -#ifdef HAVE_CYRUS_SASL js->serverFQDN = g_strdup(fqdn); -#endif if (purple_proxy_connect(js->gc, js->gc->account, host, port, jabber_login_callback, js->gc) == NULL) @@ -1025,9 +1023,9 @@ g_string_free(js->sasl_mechs, TRUE); if(js->sasl_cb) g_free(js->sasl_cb); +#endif if(js->serverFQDN) g_free(js->serverFQDN); -#endif g_free(js->server_name); g_free(js->gmail_last_time); g_free(js->gmail_last_tid); diff -r a573a67c80a4 -r f46ec4d6a385 libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Thu Jun 21 18:08:48 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.h Fri Jun 22 16:30:30 2007 +0000 @@ -136,6 +136,8 @@ char *gmail_last_time; char *gmail_last_tid; + char *serverFQDN; + /* OK, this stays at the end of the struct, so plugins can depend * on the rest of the stuff being in the right place */ @@ -150,7 +152,6 @@ int sasl_state; int sasl_maxbuf; GString *sasl_mechs; - char *serverFQDN; gboolean vcard_fetched; diff -r a573a67c80a4 -r f46ec4d6a385 libpurple/sslconn.c --- a/libpurple/sslconn.c Thu Jun 21 18:08:48 2007 +0000 +++ b/libpurple/sslconn.c Fri Jun 22 16:30:30 2007 +0000 @@ -154,7 +154,18 @@ PurpleSslConnection * purple_ssl_connect_fd(PurpleAccount *account, int fd, PurpleSslInputFunction func, - PurpleSslErrorFunction error_func, void *data) + PurpleSslErrorFunction error_func, + void *data) +{ + return purple_ssl_connect_with_host_fd(account, fd, func, error_func, NULL, data); +} + +PurpleSslConnection * +purple_ssl_connect_with_host_fd(PurpleAccount *account, int fd, + PurpleSslInputFunction func, + PurpleSslErrorFunction error_func, + const char *host, + void *data) { PurpleSslConnection *gsc; PurpleSslOps *ops; @@ -175,6 +186,8 @@ gsc->connect_cb = func; gsc->error_cb = error_func; gsc->fd = fd; + if(host) + gsc->host = g_strdup(host); ops = purple_ssl_get_ops(); ops->connectfunc(gsc); @@ -231,6 +244,17 @@ return (ops->write)(gsc, data, len); } +GList * +purple_ssl_get_peer_certificates(PurpleSslConnection *gsc) +{ + PurpleSslOps *ops; + + g_return_val_if_fail(gsc != NULL, NULL); + + ops = purple_ssl_get_ops(); + return (ops->get_peer_certificates)(gsc); +} + void purple_ssl_set_ops(PurpleSslOps *ops) { diff -r a573a67c80a4 -r f46ec4d6a385 libpurple/sslconn.h --- a/libpurple/sslconn.h Thu Jun 21 18:08:48 2007 +0000 +++ b/libpurple/sslconn.h Fri Jun 22 16:30:30 2007 +0000 @@ -107,8 +107,16 @@ * @return The number of bytes written (may be less than len) or <0 on error */ size_t (*write)(PurpleSslConnection *gsc, const void *data, size_t len); - - void (*_purple_reserved1)(void); + /** Obtains the certificate chain provided by the peer + * + * @param gsc Connection context + * @return A newly allocated list containing the certificates + * the peer provided. + * @todo Decide whether the ordering of certificates in this + * list can be guaranteed. + */ + GList * (* get_peer_certificates)(PurpleSslConnection * gsc); + void (*_purple_reserved2)(void); void (*_purple_reserved3)(void); void (*_purple_reserved4)(void); @@ -154,6 +162,7 @@ /** * Makes a SSL connection using an already open file descriptor. + * DEPRECATED. Use purple_ssl_connect_with_host_fd instead. * * @param account The account making the connection. * @param fd The file descriptor. @@ -166,7 +175,25 @@ PurpleSslConnection *purple_ssl_connect_fd(PurpleAccount *account, int fd, PurpleSslInputFunction func, PurpleSslErrorFunction error_func, - void *data); + void *data); + +/** + * Makes a SSL connection using an already open file descriptor. + * + * @param account The account making the connection. + * @param fd The file descriptor. + * @param func The SSL input handler function. + * @param error_func The SSL error handler function. + * @param host The hostname of the other peer (to verify the CN) + * @param data User-defined data. + * + * @return The SSL connection handle. + */ +PurpleSslConnection *purple_ssl_connect_with_host_fd(PurpleAccount *account, int fd, + PurpleSslInputFunction func, + PurpleSslErrorFunction error_func, + const char *host, + void *data); /** * Adds an input watcher for the specified SSL connection. @@ -208,6 +235,16 @@ */ size_t purple_ssl_write(PurpleSslConnection *gsc, const void *buffer, size_t len); +/** + * Obtains the peer's presented certificates + * + * @param gsc The SSL connection handle + * + * @return The peer certificate chain, in the order of certificate, issuer, + * issuer's issuer, etc. NULL if no certificates have been provided, + */ +GList * purple_ssl_get_peer_certificates(PurpleSslConnection *gsc); + /*@}*/ /**************************************************************************/