Trac is being migrated to new services! Issues can be found in our new YouTrack instance and WIKI pages can be found on our website.

General Description

Pidgin doesn't currently do any certificate verification for SSL. In order to properly do this and ensure security, a certificate manager (something like Mozilla's) needs to be added.

This is William Ehlhardt's project for Summer of Code

Branch

im.pidgin.soc.2007.certmgr

Related Pages

Handy-dandy overview

SSL: The Connection Process

  1. purple_ssl_connect called, and the "tls_peers" CertificateVerifier? is provided to it as the method to use for cert checking
  2. ssl_connect looks up the "x509" CertificateScheme?, making sure that it is loaded
  3. ssl_connect fetches a the host's certificate
  4. ssl_connect asks the "tls_peers" CertificateVerifier? for verification of the certificate.
  5. The tls_peers CertificateVerifier? sees if the get_unique_id(cert) can be found within the pool "tls_servers". If so, the certificate is considered Verified, and control returns to ssl_connect, which proceeds to the Hostname Check.
  6. If the certificate cannot be found in the "tls_servers" pool, the tls_peers Verifier then checks the "tls_ca" pool to see if the certificate chain (using get_signer_unique_id(cert)) leads to any certificates in the "tls_ca" pool. If it does find such a certificate, it uses the check_signature function to verify the signature chain, and if THAT passes, control returns to ssl_connect, which proceeds to the Hostname Check.
  7. If the tls_peers CertVerifier? finds an invalid (apparently attempted forged) signature, the connection fails and the user is given a popup detailing the reason.
  8. If the tls_peers CertVerifier? fails to find any appropriate signers in the "tls_ca" or "tls_servers" pool, the user is prompted with the certificate details given by get_verification_data. If the certificate passes user prompting, tls_peers->bless_certificate is called.
  9. If tls_peers finds an expired certificate or any other problem, the connection is aborted with an error.
  10. We shouldn't get to step 10.

Hostname Check

(some logic needs to be changed here to ensure that we don't prompt every connect for incorrect CN fields)

  1. ssl_connect checks that the certificate Common Name matches the hostname being connected to. If it matches, it completes the connection initialization. The hostname check may not always work, though, so:
  2. If the hostname check fails, ssl_connect will prompt the user with the certificate data and a warning that the hostname check failed.
  3. If the user accepts the certificate anyways, ssl_connect calls bless_certificate in the tls_peers CertificateVerifier?, which adds it to the "tls_peers" pool. The connection initialization is then completed. (The exact action at this step is not really laid in stone, though)

A better-written example

SSL SYSTEM:

  1. Set up connection with gmail.com.
  2. Fetch peer certificate chain.
  3. Pass peer certificate chain to the Verifier, along with a callback that accepts an "approved? yes or no" value.

VERIFIER:

  1. See if the first cert in the chain is in some .ssh/known_hosts equivalent. If it is, trigger the callback with "approved"
  2. See if the certificate chain is valid against a known CA. If it is, trigger the callback with "approved"
  3. Prompt the user with details for approval. If the user approves, trigger the callback with "approved"
  4. Trigger the callback with "not approved"

SSL SYSTEM (via callback):

  1. If the certificate chain was approved, finish connection setup. If it wasn't, abort the connection with an informative error message.

CertificateScheme? API

  • Closely modeled on Cipher API

struct Certificate

struct CertificateScheme?

Will be registered into an internal list of scheme types

  • char * name = "x509", "pgp", etc. This must be internally unique - there must be only ONE CertificateScheme? for each name. This is to pre-empt the possibility of someone creating some Certificate instances with a GnuTLS backend and then feeding them to a libNSS backend.
  • int ref - for reference counting
  • Certificate * import_certificate(file arguments) -
  • export_certificate(filename?)
  • gboolean check_signature(Certificate * certificate, Certificate * signer) - returns whether one certificate is signed by another
  • char * get_unique_id - returns a string (probably something involving a key fingerprint) that can usefully be used to uniquely identify this certificate (for storage purposes)
  • char * get_issuer_unique_id - returns a unique identifier for the key used to sign the certificate
  • destroy_certificate(Certificate * crt) - when a certificate's life has reached its end, call this to get rid of it. Will execute free(crt) for you.
  • get_fingerprint_sha1
  • get_certificate_subject(crt) - retrieves a string representing "who the certificate is for". For SSL X.509 certs, this would be the domain name (NOT fully qualified - no dot at the end! BEWARE!)

Certificate Scheme Requirements

Requirements for implementors of various CertificateSchemes?

X.509
Name"x509"
import_certificate File FormatPEM
unique_id constructionFull DN string
  • Required get_verification_data fields:
    • "fpr_sha1" - SHA1 key fingerprint
    • "common_name" - Certificate "Common Name" field.
    • "expiration" - something like "23:04:20, May 5, 2007"
    • "activation" - see above

struct CertificatePool?

  • char * name - the pool identifier
  • CertificateScheme? * scheme - certificate type this pool is filled with
  • gboolean exists_in_pool(unique_id) - checks for the presence of a given cert in the pool (see get_unique_id above) (may take Certificate instead of unique_id?)
  • Certificate * retrieve_certificate(unique_id)
  • store_certificate(Certificate) - overwrites previous certificate by unique_id if it exists

struct CertificateVerifier?

  • char * name - unique identifier ("tls_peers", "silc_servers", etc.)
  • CertificateScheme? * scheme - certificate type this Verifier works with
  • bless_certificate - forces the certificate's verification (just calls CertificatePool?->store_certificate, most likely)
  • gboolean certificate_is_valid (Certificate) - returns whether a certificate can be verified as trustworthy.
    • Perhaps it should also take an expected_values argument of the same form as the return value from get_verification_data(Certificate) to specify some values to check (such as Common Name, which maybe ought to be optional?)

SslOps? Additions

  • GList * get_peer_certificates - fetches as much of the certificate chain as the peer is willing to provide off of the wire and exports the certificates as several "x509" Certificate instances. The order should probably be "peer's cert, issuer's cert, issuer's issuer's cert, etc.", but this may not be guaranteed. TODO: examine ordering here.

Key Store

Managed by a CertificatePool?.

The API for this hasn't been totally decided yet. However, the organization will look something like this:

~/.purple/certificates/cert_scheme_name/cert_pool/cert_unique_id(.pem?)

So an example X.509 certificate might be stored as:

~/.purple/certificates/x509/tls_servers/gmail_com_1234567890abcdef1234567890abcdef.pem

The keystore logic will be left mostly up to the relevant CertificatePool?; these are only guidelines for how the CertificatePools? should run their backends.

Miscellaneous API changes

  • (Resolved - see below) Jabber (and possibly other protocols) use purple_ssl_connect_fd to create SSL connections, throwing out possibly important data about the other end of the connection (hostname, port number) in the process. In the interests of keeping this data intact, I propose adding a purple_ssl_connect_proxyconn that will create an SSL connection around a previously existing ProxyConnection? instead of ripping out its file descriptor and starting anew.
  • Add strerror-equivalent function for the SSL errors (June 26)

Status

Pool API

pool_usableWritten, untested

x509 CertificateScheme?

MemberBackend (GnuTLS)Backend (NSS)libpurple frontend
import_certificateWritten, Tested, ExposedWritten, untested, ExposedWritten, Tested
export_certificateWritten, Tested, ExposedWritten, Tested, ExposedWritten, Tested
copy_certificateWritten, Tested, ExposedWritten, untested, ExposedWritten, Tested
destroy_certificateWritten, Tested, ExposedWritten, untested, ExposedWritten, Tested
signed_byWritten, Tested, ExposedNot startedWritten, Tested
get_fingerprint_sha1Written, Tested, ExposedWritten, Tested, ExposedWritten, Tested
get_subject_nameWritten, Tested, ExposedWritten, Tested, ExposedWritten, Tested
check_subject_nameWritten, Tested, ExposedWritten, untested, ExposedWritten, Tested
get_timesWritten, Tested, ExposedWritten, Tested, ExposedWritten, Tested
unique_idWritten, Tested, ExposedWritten, Tested, ExposedWritten, Tested
issuer_unique_idWritten, Tested, ExposedWritten, untested, ExposedWritten, Tested

tls_peers CertificatePool?

get_idlistWritten, Tested
put_certWritten, Tested
delete_certWritten, Tested

SslOps? modifications

MemberBackend (GnuTLS)Backend (NSS)libpurple frontend
get_peer_certificatesWritten, Tested, ExposedNot startedpurple_ssl_get_peer_certificates

certificate.h

FunctionPrototypedImplementedTested
find_schemeYesYesYes
register_schemeYesYesYes
unregister_schemeYesYesNo

Signals

Pool Signals

NameEmittedReceived by GUITested
certificate-storedYesYesYes
certificate-deletedYesYesYes

Miscellany

purple_ssl_connect_with_host_fdPulled from soc.2007.xmpp
purple_ssl_strerrorDone
Last modified 16 years ago Last modified on Sep 14, 2007, 5:28:57 AM
All information, including names and email addresses, entered onto this website or sent to mailing lists affiliated with this website will be public. Do not post confidential information, especially passwords!