Mercurial > pidgin
view src/sslconn.c @ 6703:36897b9e009f
[gaim-migrate @ 7229]
Forgot the SSL wrapper code.
committer: Tailor Script <tailor@pidgin.im>
author | Christian Hammond <chipx86@chipx86.com> |
---|---|
date | Tue, 02 Sep 2003 04:43:28 +0000 |
parents | |
children | b0913ab92893 |
line wrap: on
line source
/** * @file sslconn.c SSL API * @ingroup core * * gaim * * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org> * * 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 "internal.h" #include "debug.h" #include "sslconn.h" #ifdef HAVE_NSS # include <nspr.h> # include <nss.h> # include <pk11func.h> # include <prio.h> # include <secerr.h> # include <secmod.h> # include <ssl.h> # include <sslerr.h> # include <sslproto.h> typedef struct { char *host; int port; void *user_data; GaimSslInputFunction input_func; int fd; int inpa; PRFileDesc *nss_fd; PRFileDesc *nss_in; } GaimSslData; static gboolean _nss_initialized = FALSE; static const PRIOMethods *_nss_methods = NULL; static PRDescIdentity _identity; static void destroy_ssl_data(GaimSslData *data) { if (data->inpa) gaim_input_remove(data->inpa); if (data->nss_in) PR_Close(data->nss_in); if (data->nss_fd) PR_Close(data->nss_fd); if (data->fd) close(data->fd); if (data->host != NULL) g_free(data->host); g_free(data); } static void init_nss(void) { if (_nss_initialized) return; PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); NSS_NoDB_Init(NULL); /* TODO: Fix this so autoconf does the work trying to find this lib. */ SECMOD_AddNewModule("Builtins", LIBDIR "/libnssckbi.so", 0, 0); NSS_SetDomesticPolicy(); _identity = PR_GetUniqueIdentity("Gaim"); _nss_methods = PR_GetDefaultIOMethods(); _nss_initialized = TRUE; } static SECStatus ssl_auth_cert(void *arg, PRFileDesc *socket, PRBool checksig, PRBool is_server) { return SECSuccess; #if 0 CERTCertificate *cert; void *pinArg; SECStatus status; cert = SSL_PeerCertificate(socket); pinArg = SSL_RevealPinArg(socket); status = CERT_VerifyCertNow((CERTCertDBHandle *)arg, cert, checksig, certUsageSSLClient, pinArg); if (status != SECSuccess) { gaim_debug(GAIM_DEBUG_ERROR, "msn", "CERT_VerifyCertNow failed\n"); CERT_DestroyCertificate(cert); return status; } CERT_DestroyCertificate(cert); return SECSuccess; #endif } SECStatus ssl_bad_cert(void *arg, PRFileDesc *socket) { SECStatus status = SECFailure; PRErrorCode err; if (arg == NULL) return status; *(PRErrorCode *)arg = err = PORT_GetError(); switch (err) { case SEC_ERROR_INVALID_AVA: case SEC_ERROR_INVALID_TIME: case SEC_ERROR_BAD_SIGNATURE: case SEC_ERROR_EXPIRED_CERTIFICATE: case SEC_ERROR_UNKNOWN_ISSUER: case SEC_ERROR_UNTRUSTED_CERT: case SEC_ERROR_CERT_VALID: case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: case SEC_ERROR_CRL_EXPIRED: case SEC_ERROR_CRL_BAD_SIGNATURE: case SEC_ERROR_EXTENSION_VALUE_INVALID: case SEC_ERROR_CA_CERT_INVALID: case SEC_ERROR_CERT_USAGES_INVALID: case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION: status = SECSuccess; break; default: status = SECFailure; break; } gaim_debug(GAIM_DEBUG_ERROR, "msn", "Bad certificate: %d\n"); return status; } static void input_func(gpointer data, gint source, GaimInputCondition cond) { GaimSslData *ssl_data = (GaimSslData *)data; char *cp, *ip, *sp; int op, kp0, kp1; int result; result = SSL_SecurityStatus(ssl_data->nss_in, &op, &cp, &kp0, &kp1, &ip, &sp); gaim_debug(GAIM_DEBUG_MISC, "msn", "bulk cipher %s, %d secret key bits, %d key bits, status: %d\n" "subject DN: %s\n" "issuer DN: %s\n", cp, kp1, kp0, op, sp, ip); PR_Free(cp); PR_Free(ip); PR_Free(sp); ssl_data->input_func(ssl_data->user_data, (GaimSslConnection *)ssl_data, cond); } static void ssl_connect_cb(gpointer data, gint source, GaimInputCondition cond) { PRSocketOptionData socket_opt; GaimSslData *ssl_data = (GaimSslData *)data; if (!_nss_initialized) init_nss(); ssl_data->fd = source; ssl_data->nss_fd = PR_ImportTCPSocket(ssl_data->fd); if (ssl_data->nss_fd == NULL) { gaim_debug(GAIM_DEBUG_ERROR, "ssl", "nss_fd == NULL!\n"); destroy_ssl_data(ssl_data); return; } socket_opt.option = PR_SockOpt_Nonblocking; socket_opt.value.non_blocking = PR_FALSE; PR_SetSocketOption(ssl_data->nss_fd, &socket_opt); ssl_data->nss_in = SSL_ImportFD(NULL, ssl_data->nss_fd); if (ssl_data->nss_in == NULL) { gaim_debug(GAIM_DEBUG_ERROR, "ssl", "nss_in == NUL!\n"); destroy_ssl_data(ssl_data); return; } SSL_OptionSet(ssl_data->nss_in, SSL_SECURITY, PR_TRUE); SSL_OptionSet(ssl_data->nss_in, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); SSL_AuthCertificateHook(ssl_data->nss_in, (SSLAuthCertificate)ssl_auth_cert, (void *)CERT_GetDefaultCertDB()); SSL_BadCertHook(ssl_data->nss_in, (SSLBadCertHandler)ssl_bad_cert, NULL); SSL_SetURL(ssl_data->nss_in, ssl_data->host); SSL_ResetHandshake(ssl_data->nss_in, PR_FALSE); if (SSL_ForceHandshake(ssl_data->nss_in)) { gaim_debug(GAIM_DEBUG_ERROR, "ssl", "Handshake failed\n"); destroy_ssl_data(ssl_data); return; } #if 0 ssl_data->input_func(ssl_data->user_data, (GaimSslConnection *)ssl_data, cond); #endif input_func(ssl_data, source, cond); } #endif /* HAVE_NSS */ gboolean gaim_ssl_is_supported(void) { #ifdef HAVE_NSS return TRUE; #else return FALSE; #endif } GaimSslConnection * gaim_ssl_connect(GaimAccount *account, const char *host, int port, GaimSslInputFunction func, void *data) { #ifdef HAVE_NSS int i; GaimSslData *ssl_data; g_return_val_if_fail(host != NULL, NULL); g_return_val_if_fail(port != 0 && port != -1, NULL); g_return_val_if_fail(func != NULL, NULL); g_return_val_if_fail(gaim_ssl_is_supported(), NULL); ssl_data = g_new0(GaimSslData, 1); ssl_data->host = g_strdup(host); ssl_data->port = port; ssl_data->user_data = data; ssl_data->input_func = func; i = gaim_proxy_connect(account, host, port, ssl_connect_cb, ssl_data); if (i < 0) { g_free(ssl_data->host); g_free(ssl_data); return NULL; } return (GaimSslConnection)ssl_data; #else g_return_val_if_fail(gaim_ssl_is_supported(), -1); #endif } void gaim_ssl_close(GaimSslConnection *gsc) { g_return_if_fail(gsc != NULL); #ifdef HAVE_NSS destroy_ssl_data((GaimSslData *)gsc); #endif } size_t gaim_ssl_read(GaimSslConnection *gsc, void *data, size_t len) { #ifdef HAVE_NSS GaimSslData *ssl_data = (GaimSslData *)gsc; g_return_val_if_fail(gsc != NULL, 0); g_return_val_if_fail(data != NULL, 0); g_return_val_if_fail(len > 0, 0); return PR_Read(ssl_data->nss_in, data, len); #else return 0; #endif } size_t gaim_ssl_write(GaimSslConnection *gsc, const void *data, size_t len) { #ifdef HAVE_NSS GaimSslData *ssl_data = (GaimSslData *)gsc; g_return_val_if_fail(gsc != NULL, 0); g_return_val_if_fail(data != NULL, 0); g_return_val_if_fail(len > 0, 0); return PR_Write(ssl_data->nss_in, data, len); #else return 0; #endif } void gaim_ssl_init(void) { } void gaim_ssl_uninit(void) { if (!_nss_initialized) return; #ifdef HAVE_NSS PR_Cleanup(); #endif _nss_initialized = FALSE; _nss_methods = NULL; }