2 * @file ssl-nss.c Mozilla NSS SSL plugin.
6 * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 #include "certificate.h"
30 #define SSL_NSS_PLUGIN_ID "ssl-nss"
32 #undef HAVE_LONG_LONG /* Make Mozilla less angry. If angry, Mozilla SMASH! */
45 /* This is defined in NSPR's <private/pprio.h>, but to avoid including a
46 * private header we duplicate the prototype here */
47 NSPR_API(PRFileDesc*) PR_ImportTCPSocket(PRInt32 osfd);
53 guint handshake_handler;
54 guint handshake_timer;
57 #define PURPLE_SSL_NSS_DATA(gsc) ((PurpleSslNssData *)gsc->private_data)
59 static const PRIOMethods *_nss_methods = NULL;
60 static PRDescIdentity _identity;
61 static PurpleCertificateScheme x509_nss;
63 /* Thank you, Evolution */
67 /* FIXME: this should handle more. */
69 case PR_INVALID_ARGUMENT_ERROR:
72 case PR_PENDING_INTERRUPT_ERROR:
75 case PR_IO_PENDING_ERROR:
78 case PR_WOULD_BLOCK_ERROR:
80 /*errno = EWOULDBLOCK; */
82 case PR_IN_PROGRESS_ERROR:
85 case PR_ALREADY_INITIATED_ERROR:
88 case PR_NETWORK_UNREACHABLE_ERROR:
91 case PR_CONNECT_REFUSED_ERROR:
94 case PR_CONNECT_TIMEOUT_ERROR:
95 case PR_IO_TIMEOUT_ERROR:
98 case PR_NOT_CONNECTED_ERROR:
101 case PR_CONNECT_RESET_ERROR:
111 static gchar *get_error_text(void)
113 PRInt32 len = PR_GetErrorTextLength();
117 ret = g_malloc(len + 1);
118 len = PR_GetErrorText(ret);
126 ssl_nss_init_nss(void)
129 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
132 /* TODO: Fix this so autoconf does the work trying to find this lib. */
134 lib = g_strdup(LIBDIR "/libnssckbi.so");
136 lib = g_strdup("nssckbi.dll");
138 SECMOD_AddNewModule("Builtins", lib, 0, 0);
140 NSS_SetDomesticPolicy();
142 SSL_CipherPrefSetDefault(TLS_DHE_RSA_WITH_AES_256_CBC_SHA, 1);
143 SSL_CipherPrefSetDefault(TLS_DHE_DSS_WITH_AES_256_CBC_SHA, 1);
144 SSL_CipherPrefSetDefault(TLS_RSA_WITH_AES_256_CBC_SHA, 1);
145 SSL_CipherPrefSetDefault(TLS_DHE_DSS_WITH_RC4_128_SHA, 1);
146 SSL_CipherPrefSetDefault(TLS_DHE_RSA_WITH_AES_128_CBC_SHA, 1);
147 SSL_CipherPrefSetDefault(TLS_DHE_DSS_WITH_AES_128_CBC_SHA, 1);
148 SSL_CipherPrefSetDefault(SSL_RSA_WITH_RC4_128_SHA, 1);
149 SSL_CipherPrefSetDefault(TLS_RSA_WITH_AES_128_CBC_SHA, 1);
150 SSL_CipherPrefSetDefault(SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 1);
151 SSL_CipherPrefSetDefault(SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, 1);
152 SSL_CipherPrefSetDefault(SSL_DHE_RSA_WITH_DES_CBC_SHA, 1);
153 SSL_CipherPrefSetDefault(SSL_DHE_DSS_WITH_DES_CBC_SHA, 1);
155 _identity = PR_GetUniqueIdentity("Purple");
156 _nss_methods = PR_GetDefaultIOMethods();
160 ssl_auth_cert(void *arg, PRFileDesc *socket, PRBool checksig,
166 CERTCertificate *cert;
170 cert = SSL_PeerCertificate(socket);
171 pinArg = SSL_RevealPinArg(socket);
173 status = CERT_VerifyCertNow((CERTCertDBHandle *)arg, cert, checksig,
174 certUsageSSLClient, pinArg);
176 if (status != SECSuccess) {
177 purple_debug_error("nss", "CERT_VerifyCertNow failed\n");
178 CERT_DestroyCertificate(cert);
182 CERT_DestroyCertificate(cert);
189 ssl_bad_cert(void *arg, PRFileDesc *socket)
191 SECStatus status = SECFailure;
197 *(PRErrorCode *)arg = err = PORT_GetError();
201 case SEC_ERROR_INVALID_AVA:
202 case SEC_ERROR_INVALID_TIME:
203 case SEC_ERROR_BAD_SIGNATURE:
204 case SEC_ERROR_EXPIRED_CERTIFICATE:
205 case SEC_ERROR_UNKNOWN_ISSUER:
206 case SEC_ERROR_UNTRUSTED_CERT:
207 case SEC_ERROR_CERT_VALID:
208 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
209 case SEC_ERROR_CRL_EXPIRED:
210 case SEC_ERROR_CRL_BAD_SIGNATURE:
211 case SEC_ERROR_EXTENSION_VALUE_INVALID:
212 case SEC_ERROR_CA_CERT_INVALID:
213 case SEC_ERROR_CERT_USAGES_INVALID:
214 case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
223 purple_debug_error("nss", "Bad certificate: %d\n", err);
245 ssl_nss_verified_cb(PurpleCertificateVerificationStatus st,
248 PurpleSslConnection *gsc = (PurpleSslConnection *) userdata;
250 if (st == PURPLE_CERTIFICATE_VALID) {
251 /* Certificate valid? Good! Do the connection! */
252 gsc->connect_cb(gsc->connect_cb_data, gsc, PURPLE_INPUT_READ);
254 /* Otherwise, signal an error */
255 if(gsc->error_cb != NULL)
256 gsc->error_cb(gsc, PURPLE_SSL_CERTIFICATE_INVALID,
257 gsc->connect_cb_data);
258 purple_ssl_close(gsc);
262 /** Transforms an NSS containing an X.509 certificate into a Certificate instance
264 * @param cert Certificate to transform
265 * @return A newly allocated Certificate
267 static PurpleCertificate *
268 x509_import_from_nss(CERTCertificate* cert)
270 /* New certificate to return */
271 PurpleCertificate * crt;
273 /* Allocate the certificate and load it with data */
274 crt = g_new0(PurpleCertificate, 1);
275 crt->scheme = &x509_nss;
276 crt->data = CERT_DupCertificate(cert);
282 ssl_nss_get_peer_certificates(PRFileDesc *socket, PurpleSslConnection * gsc)
284 CERTCertificate *curcert;
285 CERTCertificate *issuerCert;
286 PurpleCertificate * newcrt;
288 /* List of Certificate instances to return */
289 GList * peer_certs = NULL;
291 int64 now = PR_Now();
293 curcert = SSL_PeerCertificate(socket);
294 if (curcert == NULL) {
295 purple_debug_error("nss", "could not DupCertificate\n");
299 for (count = 0 ; count < CERT_MAX_CERT_CHAIN ; count++) {
300 purple_debug_info("nss", "subject=%s issuer=%s\n", curcert->subjectName,
301 curcert->issuerName ? curcert->issuerName : "(null)");
302 newcrt = x509_import_from_nss(curcert);
303 peer_certs = g_list_append(peer_certs, newcrt);
305 if (curcert->isRoot) {
308 issuerCert = CERT_FindCertIssuer(curcert, now, certUsageSSLServer);
310 purple_debug_error("nss", "partial certificate chain\n");
313 CERT_DestroyCertificate(curcert);
314 curcert = issuerCert;
316 CERT_DestroyCertificate(curcert);
322 ssl_nss_handshake_cb(gpointer data, int fd, PurpleInputCondition cond)
324 PurpleSslConnection *gsc = (PurpleSslConnection *)data;
325 PurpleSslNssData *nss_data = gsc->private_data;
327 /* I don't think this the best way to do this...
328 * It seems to work because it'll eventually use the cached value
330 if(SSL_ForceHandshake(nss_data->in) != SECSuccess) {
332 set_errno(PR_GetError());
333 if (errno == EAGAIN || errno == EWOULDBLOCK)
336 error_txt = get_error_text();
337 purple_debug_error("nss", "Handshake failed %s (%d)\n", error_txt ? error_txt : "", PR_GetError());
340 if (gsc->error_cb != NULL)
341 gsc->error_cb(gsc, PURPLE_SSL_HANDSHAKE_FAILED, gsc->connect_cb_data);
343 purple_ssl_close(gsc);
348 purple_input_remove(nss_data->handshake_handler);
349 nss_data->handshake_handler = 0;
351 /* If a Verifier was given, hand control over to it */
354 /* First, get the peer cert chain */
355 peers = ssl_nss_get_peer_certificates(nss_data->in, gsc);
357 /* Now kick off the verification process */
358 purple_certificate_verify(gsc->verifier,
364 purple_certificate_destroy_list(peers);
366 /* Otherwise, just call the "connection complete"
368 gsc->connect_cb(gsc->connect_cb_data, gsc, cond);
373 start_handshake_cb(gpointer data)
375 PurpleSslConnection *gsc = data;
376 PurpleSslNssData *nss_data = PURPLE_SSL_NSS_DATA(gsc);
378 nss_data->handshake_timer = 0;
380 ssl_nss_handshake_cb(gsc, gsc->fd, PURPLE_INPUT_READ);
385 ssl_nss_connect(PurpleSslConnection *gsc)
387 PurpleSslNssData *nss_data = g_new0(PurpleSslNssData, 1);
388 PRSocketOptionData socket_opt;
390 gsc->private_data = nss_data;
392 nss_data->fd = PR_ImportTCPSocket(gsc->fd);
394 if (nss_data->fd == NULL)
396 purple_debug_error("nss", "nss_data->fd == NULL!\n");
398 if (gsc->error_cb != NULL)
399 gsc->error_cb(gsc, PURPLE_SSL_CONNECT_FAILED, gsc->connect_cb_data);
401 purple_ssl_close((PurpleSslConnection *)gsc);
406 socket_opt.option = PR_SockOpt_Nonblocking;
407 socket_opt.value.non_blocking = PR_TRUE;
409 if (PR_SetSocketOption(nss_data->fd, &socket_opt) != PR_SUCCESS) {
410 gchar *error_txt = get_error_text();
411 purple_debug_warning("nss", "unable to set socket into non-blocking mode: %s (%d)\n", error_txt ? error_txt : "", PR_GetError());
415 nss_data->in = SSL_ImportFD(NULL, nss_data->fd);
417 if (nss_data->in == NULL)
419 purple_debug_error("nss", "nss_data->in == NUL!\n");
421 if (gsc->error_cb != NULL)
422 gsc->error_cb(gsc, PURPLE_SSL_CONNECT_FAILED, gsc->connect_cb_data);
424 purple_ssl_close((PurpleSslConnection *)gsc);
429 SSL_OptionSet(nss_data->in, SSL_SECURITY, PR_TRUE);
430 SSL_OptionSet(nss_data->in, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
432 SSL_AuthCertificateHook(nss_data->in,
433 (SSLAuthCertificate)ssl_auth_cert,
434 (void *)CERT_GetDefaultCertDB());
436 /* No point in hooking BadCert, since ssl_auth_cert always succeeds */
437 SSL_BadCertHook(nss_data->in, (SSLBadCertHandler)ssl_bad_cert, NULL);
441 SSL_SetURL(nss_data->in, gsc->host);
444 /* This seems like it'd the be the correct way to implement the
445 nonblocking stuff, but it doesn't seem to work */
446 SSL_HandshakeCallback(nss_data->in,
447 (SSLHandshakeCallback) ssl_nss_handshake_cb, gsc);
449 SSL_ResetHandshake(nss_data->in, PR_FALSE);
451 nss_data->handshake_handler = purple_input_add(gsc->fd,
452 PURPLE_INPUT_READ, ssl_nss_handshake_cb, gsc);
454 nss_data->handshake_timer = purple_timeout_add(0, start_handshake_cb, gsc);
458 ssl_nss_close(PurpleSslConnection *gsc)
460 PurpleSslNssData *nss_data = PURPLE_SSL_NSS_DATA(gsc);
466 PR_Close(nss_data->in);
468 } else if (nss_data->fd) {
469 PR_Close(nss_data->fd);
473 if (nss_data->handshake_handler)
474 purple_input_remove(nss_data->handshake_handler);
476 if (nss_data->handshake_timer)
477 purple_timeout_remove(nss_data->handshake_timer);
480 gsc->private_data = NULL;
484 ssl_nss_read(PurpleSslConnection *gsc, void *data, size_t len)
487 PurpleSslNssData *nss_data = PURPLE_SSL_NSS_DATA(gsc);
489 ret = PR_Read(nss_data->in, data, len);
492 set_errno(PR_GetError());
498 ssl_nss_write(PurpleSslConnection *gsc, const void *data, size_t len)
501 PurpleSslNssData *nss_data = PURPLE_SSL_NSS_DATA(gsc);
506 ret = PR_Write(nss_data->in, data, len);
509 set_errno(PR_GetError());
515 ssl_nss_peer_certs(PurpleSslConnection *gsc)
518 PurpleSslNssData *nss_data = PURPLE_SSL_NSS_DATA(gsc);
519 CERTCertificate *cert;
526 /* TODO: this is a blind guess */
527 cert = SSL_PeerCertificate(nss_data->fd);
530 CERT_DestroyCertificate(cert);
538 /************************************************************************/
539 /* X.509 functionality */
540 /************************************************************************/
541 static PurpleCertificateScheme x509_nss;
543 /** Helpr macro to retrieve the NSS certdata from a PurpleCertificate */
544 #define X509_NSS_DATA(pcrt) ( (CERTCertificate * ) (pcrt->data) )
546 /** Imports a PEM-formatted X.509 certificate from the specified file.
547 * @param filename Filename to import from. Format is PEM
549 * @return A newly allocated Certificate structure of the x509_nss scheme
551 static PurpleCertificate *
552 x509_import_from_file(const gchar *filename)
556 CERTCertificate *crt_dat;
557 PurpleCertificate *crt;
559 g_return_val_if_fail(filename != NULL, NULL);
561 purple_debug_info("nss/x509",
562 "Loading certificate from %s\n",
565 /* Load the raw data up */
566 if (!g_file_get_contents(filename,
569 purple_debug_error("nss/x509", "Unable to read certificate file.\n");
574 purple_debug_error("nss/x509",
575 "Certificate file has no contents!\n");
581 /* Decode the certificate */
582 crt_dat = CERT_DecodeCertFromPackage(rawcert, len);
585 g_return_val_if_fail(crt_dat != NULL, NULL);
587 crt = g_new0(PurpleCertificate, 1);
588 crt->scheme = &x509_nss;
594 /** Imports a number of PEM-formatted X.509 certificates from the specified file.
595 * @param filename Filename to import from. Format is PEM
597 * @return A GSList of newly allocated Certificate structures of the x509_nss scheme
600 x509_importcerts_from_file(const gchar *filename)
602 gchar *rawcert, *begin, *end;
605 CERTCertificate *crt_dat;
606 PurpleCertificate *crt;
608 g_return_val_if_fail(filename != NULL, NULL);
610 purple_debug_info("nss/x509",
611 "Loading certificate from %s\n",
614 /* Load the raw data up */
615 if (!g_file_get_contents(filename,
618 purple_debug_error("nss/x509", "Unable to read certificate file.\n");
623 purple_debug_error("nss/x509",
624 "Certificate file has no contents!\n");
631 while((end = strstr(begin, "-----END CERTIFICATE-----")) != NULL) {
632 end += sizeof("-----END CERTIFICATE-----")-1;
633 /* Decode the certificate */
634 crt_dat = CERT_DecodeCertFromPackage(begin, (end-begin));
636 g_return_val_if_fail(crt_dat != NULL, NULL);
638 crt = g_new0(PurpleCertificate, 1);
639 crt->scheme = &x509_nss;
641 crts = g_slist_prepend(crts, crt);
649 * Exports a PEM-formatted X.509 certificate to the specified file.
650 * @param filename Filename to export to. Format will be PEM
651 * @param crt Certificate to export
653 * @return TRUE if success, otherwise FALSE
655 /* This function should not be so complicated, but NSS doesn't seem to have a
656 "convert yon certificate to PEM format" function. */
658 x509_export_certificate(const gchar *filename, PurpleCertificate *crt)
660 CERTCertificate *crt_dat;
664 gboolean ret = FALSE;
666 g_return_val_if_fail(filename, FALSE);
667 g_return_val_if_fail(crt, FALSE);
668 g_return_val_if_fail(crt->scheme == &x509_nss, FALSE);
670 crt_dat = X509_NSS_DATA(crt);
671 g_return_val_if_fail(crt_dat, FALSE);
673 purple_debug_info("nss/x509",
674 "Exporting certificate to %s\n", filename);
676 /* First, use NSS voodoo to create a DER-formatted certificate */
677 dercrt = SEC_ASN1EncodeItem(NULL, NULL, crt_dat,
678 SEC_ASN1_GET(SEC_SignedCertificateTemplate));
679 g_return_val_if_fail(dercrt != NULL, FALSE);
681 /* Now encode it to b64 */
682 b64crt = NSSBase64_EncodeItem(NULL, NULL, 0, dercrt);
683 SECITEM_FreeItem(dercrt, PR_TRUE);
684 g_return_val_if_fail(b64crt, FALSE);
686 /* Wrap it in nice PEM header things */
687 pemcrt = g_strdup_printf("-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----\n", b64crt);
688 PORT_Free(b64crt); /* Notice that b64crt was allocated by an NSS
689 function; hence, we'll let NSPR free it. */
691 /* Finally, dump the silly thing to a file. */
692 ret = purple_util_write_data_to_file_absolute(filename, pemcrt, -1);
699 static PurpleCertificate *
700 x509_copy_certificate(PurpleCertificate *crt)
702 CERTCertificate *crt_dat;
703 PurpleCertificate *newcrt;
705 g_return_val_if_fail(crt, NULL);
706 g_return_val_if_fail(crt->scheme == &x509_nss, NULL);
708 crt_dat = X509_NSS_DATA(crt);
709 g_return_val_if_fail(crt_dat, NULL);
711 /* Create the certificate copy */
712 newcrt = g_new0(PurpleCertificate, 1);
713 newcrt->scheme = &x509_nss;
714 /* NSS does refcounting automatically */
715 newcrt->data = CERT_DupCertificate(crt_dat);
720 /** Frees a Certificate
722 * Destroys a Certificate's internal data structures and frees the pointer
724 * @param crt Certificate instance to be destroyed. It WILL NOT be destroyed
725 * if it is not of the correct CertificateScheme. Can be NULL
729 x509_destroy_certificate(PurpleCertificate * crt)
731 CERTCertificate *crt_dat;
733 g_return_if_fail(crt);
734 g_return_if_fail(crt->scheme == &x509_nss);
736 crt_dat = X509_NSS_DATA(crt);
737 g_return_if_fail(crt_dat);
739 /* Finally we have the certificate. So let's kill it */
740 /* NSS does refcounting automatically */
741 CERT_DestroyCertificate(crt_dat);
743 /* Delete the PurpleCertificate as well */
747 /** Determines whether one certificate has been issued and signed by another
749 * @param crt Certificate to check the signature of
750 * @param issuer Issuer's certificate
752 * @return TRUE if crt was signed and issued by issuer, otherwise FALSE
753 * @TODO Modify this function to return a reason for invalidity?
756 x509_signed_by(PurpleCertificate * crt,
757 PurpleCertificate * issuer)
759 CERTCertificate *subjectCert;
760 CERTCertificate *issuerCert;
763 issuerCert = X509_NSS_DATA(issuer);
764 g_return_val_if_fail(issuerCert, FALSE);
766 subjectCert = X509_NSS_DATA(crt);
767 g_return_val_if_fail(subjectCert, FALSE);
769 if (subjectCert->issuerName == NULL
770 || PORT_Strcmp(subjectCert->issuerName, issuerCert->subjectName) != 0)
772 st = CERT_VerifySignedData(&subjectCert->signatureWrap, issuerCert, PR_Now(), NULL);
773 return st == SECSuccess;
777 x509_sha1sum(PurpleCertificate *crt)
779 CERTCertificate *crt_dat;
780 size_t hashlen = 20; /* Size of an sha1sum */
782 SECItem *derCert; /* DER representation of the cert */
785 g_return_val_if_fail(crt, NULL);
786 g_return_val_if_fail(crt->scheme == &x509_nss, NULL);
788 crt_dat = X509_NSS_DATA(crt);
789 g_return_val_if_fail(crt_dat, NULL);
791 /* Get the certificate DER representation */
792 derCert = &(crt_dat->derCert);
795 sha1sum = g_byte_array_sized_new(hashlen);
796 /* glib leaves the size as 0 by default */
797 sha1sum->len = hashlen;
799 st = PK11_HashBuf(SEC_OID_SHA1, sha1sum->data,
800 derCert->data, derCert->len);
802 /* Check for errors */
803 if (st != SECSuccess) {
804 g_byte_array_free(sha1sum, TRUE);
805 purple_debug_error("nss/x509",
806 "Error: hashing failed!\n");
814 x509_dn (PurpleCertificate *crt)
816 CERTCertificate *crt_dat;
818 g_return_val_if_fail(crt, NULL);
819 g_return_val_if_fail(crt->scheme == &x509_nss, NULL);
821 crt_dat = X509_NSS_DATA(crt);
822 g_return_val_if_fail(crt_dat, NULL);
824 return g_strdup(crt_dat->subjectName);
828 x509_issuer_dn (PurpleCertificate *crt)
830 CERTCertificate *crt_dat;
832 g_return_val_if_fail(crt, NULL);
833 g_return_val_if_fail(crt->scheme == &x509_nss, NULL);
835 crt_dat = X509_NSS_DATA(crt);
836 g_return_val_if_fail(crt_dat, NULL);
838 return g_strdup(crt_dat->issuerName);
842 x509_common_name (PurpleCertificate *crt)
844 CERTCertificate *crt_dat;
848 g_return_val_if_fail(crt, NULL);
849 g_return_val_if_fail(crt->scheme == &x509_nss, NULL);
851 crt_dat = X509_NSS_DATA(crt);
852 g_return_val_if_fail(crt_dat, NULL);
855 Why get a newly allocated string out of NSS, strdup it, and then
859 The NSS LXR docs state that I should use the NSPR free functions on
860 the strings that the NSS cert functions return. Since the libpurple
861 API expects a g_free()-able string, we make our own copy and return
864 NSPR is something of a prima donna. */
866 nss_cn = CERT_GetCommonName( &(crt_dat->subject) );
867 ret_cn = g_strdup(nss_cn);
874 x509_check_name (PurpleCertificate *crt, const gchar *name)
876 CERTCertificate *crt_dat;
879 g_return_val_if_fail(crt, FALSE);
880 g_return_val_if_fail(crt->scheme == &x509_nss, FALSE);
882 crt_dat = X509_NSS_DATA(crt);
883 g_return_val_if_fail(crt_dat, FALSE);
885 st = CERT_VerifyCertName(crt_dat, name);
887 if (st == SECSuccess) {
890 else if (st == SECFailure) {
894 /* If we get here...bad things! */
895 purple_debug_error("nss/x509",
896 "x509_check_name fell through where it shouldn't "
902 x509_times (PurpleCertificate *crt, time_t *activation, time_t *expiration)
904 CERTCertificate *crt_dat;
905 PRTime nss_activ, nss_expir;
907 g_return_val_if_fail(crt, FALSE);
908 g_return_val_if_fail(crt->scheme == &x509_nss, FALSE);
910 crt_dat = X509_NSS_DATA(crt);
911 g_return_val_if_fail(crt_dat, FALSE);
913 /* Extract the times into ugly PRTime thingies */
914 /* TODO: Maybe this shouldn't throw an error? */
915 g_return_val_if_fail(
916 SECSuccess == CERT_GetCertTimes(crt_dat,
917 &nss_activ, &nss_expir),
920 /* NSS's native PRTime type *almost* corresponds to time_t; however,
921 it measures *microseconds* since the epoch, not seconds. Hence
922 the funny conversion. */
924 *activation = nss_activ / 1000000;
927 *expiration = nss_expir / 1000000;
934 x509_get_der_data(PurpleCertificate *crt)
936 CERTCertificate *crt_dat;
940 crt_dat = X509_NSS_DATA(crt);
941 g_return_val_if_fail(crt_dat, NULL);
943 dercrt = SEC_ASN1EncodeItem(NULL, NULL, crt_dat,
944 SEC_ASN1_GET(SEC_SignedCertificateTemplate));
945 g_return_val_if_fail(dercrt != NULL, FALSE);
947 data = g_byte_array_sized_new(dercrt->len);
948 memcpy(data->data, dercrt->data, dercrt->len);
949 data->len = dercrt->len;
951 SECITEM_FreeItem(dercrt, PR_TRUE);
957 x509_display_string(PurpleCertificate *crt)
962 time_t activation, expiration;
963 gchar *activ_str, *expir_str;
966 /* Pull out the SHA1 checksum */
967 sha_bin = x509_sha1sum(crt);
968 sha_asc = purple_base16_encode_chunked(sha_bin->data, sha_bin->len);
970 /* Get the cert Common Name */
971 /* TODO: Will break on CA certs */
972 cn = x509_common_name(crt);
974 /* Get the certificate times */
975 /* TODO: Check the times against localtime */
976 /* TODO: errorcheck? */
977 if (!x509_times(crt, &activation, &expiration)) {
978 purple_debug_error("certificate",
979 "Failed to get certificate times!\n");
980 activation = expiration = 0;
982 activ_str = g_strdup(ctime(&activation));
983 expir_str = g_strdup(ctime(&expiration));
986 text = g_strdup_printf(_("Common name: %s\n\n"
987 "Fingerprint (SHA1): %s\n\n"
988 "Activation date: %s\n"
989 "Expiration date: %s\n"),
991 sha_asc ? sha_asc : "(null)",
992 activ_str ? activ_str : "(null)",
993 expir_str ? expir_str : "(null)");
1000 g_byte_array_free(sha_bin, TRUE);
1005 static PurpleCertificateScheme x509_nss = {
1006 "x509", /* Scheme name */
1007 N_("X.509 Certificates"), /* User-visible scheme name */
1008 x509_import_from_file, /* Certificate import function */
1009 x509_export_certificate, /* Certificate export function */
1010 x509_copy_certificate, /* Copy */
1011 x509_destroy_certificate, /* Destroy cert */
1012 x509_signed_by, /* Signed-by */
1013 x509_sha1sum, /* SHA1 fingerprint */
1014 x509_dn, /* Unique ID */
1015 x509_issuer_dn, /* Issuer Unique ID */
1016 x509_common_name, /* Subject name */
1017 x509_check_name, /* Check subject name */
1018 x509_times, /* Activation/Expiration time */
1019 x509_importcerts_from_file, /* Multiple certificate import function */
1020 x509_get_der_data, /* Binary DER data */
1021 x509_display_string, /* Display representation */
1026 static PurpleSslOps ssl_ops =
1044 plugin_load(PurplePlugin *plugin)
1046 if (!purple_ssl_get_ops()) {
1047 purple_ssl_set_ops(&ssl_ops);
1050 /* Init NSS now, so others can use it even if sslconn never does */
1053 /* Register the X.509 functions we provide */
1054 purple_certificate_register_scheme(&x509_nss);
1060 plugin_unload(PurplePlugin *plugin)
1062 if (purple_ssl_get_ops() == &ssl_ops) {
1063 purple_ssl_set_ops(NULL);
1066 /* Unregister our X.509 functions */
1067 purple_certificate_unregister_scheme(&x509_nss);
1072 static PurplePluginInfo info =
1074 PURPLE_PLUGIN_MAGIC,
1075 PURPLE_MAJOR_VERSION,
1076 PURPLE_MINOR_VERSION,
1077 PURPLE_PLUGIN_STANDARD, /**< type */
1078 NULL, /**< ui_requirement */
1079 PURPLE_PLUGIN_FLAG_INVISIBLE, /**< flags */
1080 NULL, /**< dependencies */
1081 PURPLE_PRIORITY_DEFAULT, /**< priority */
1083 SSL_NSS_PLUGIN_ID, /**< id */
1084 N_("NSS"), /**< name */
1085 DISPLAY_VERSION, /**< version */
1087 N_("Provides SSL support through Mozilla NSS."),
1089 N_("Provides SSL support through Mozilla NSS."),
1090 "Christian Hammond <chipx86@gnupdate.org>",
1091 PURPLE_WEBSITE, /**< homepage */
1093 plugin_load, /**< load */
1094 plugin_unload, /**< unload */
1095 NULL, /**< destroy */
1097 NULL, /**< ui_info */
1098 NULL, /**< extra_info */
1099 NULL, /**< prefs_info */
1100 NULL, /**< actions */
1110 init_plugin(PurplePlugin *plugin)
1114 PURPLE_INIT_PLUGIN(ssl_nss, init_plugin, info)