aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl_nss.c
diff options
context:
space:
mode:
authorWilmer van der Gaast <wilmer@gaast.net>2012-08-19 16:33:55 +0100
committerWilmer van der Gaast <wilmer@gaast.net>2012-08-19 16:33:55 +0100
commit68709f5618175d0ecb9b2b160cbb2ce2d4ddc7bc (patch)
treed9e8d6af12812d3d35ec5d2f3765ba4d83a8b804 /lib/ssl_nss.c
parent6ee51a914b26fec1f7036af758be514bbfed9778 (diff)
NSS module fixes from mcepl in #714. This removes des.c since it's no
longer necessary.
Diffstat (limited to 'lib/ssl_nss.c')
-rw-r--r--lib/ssl_nss.c331
1 files changed, 248 insertions, 83 deletions
diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c
index d50620d5..b71d3d2f 100644
--- a/lib/ssl_nss.c
+++ b/lib/ssl_nss.c
@@ -39,39 +39,46 @@
#include <seccomon.h>
#include <secerr.h>
#include <sslerr.h>
+#include <assert.h>
+#include <unistd.h>
int ssl_errno = 0;
static gboolean initialized = FALSE;
-struct scd
-{
+#define SSLDEBUG 0
+
+struct scd {
ssl_input_function func;
gpointer data;
int fd;
+ char *hostname;
PRFileDesc *prfd;
gboolean established;
gboolean verify;
};
-static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
-static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond );
+static gboolean ssl_connected(gpointer data, gint source,
+ b_input_condition cond);
+static gboolean ssl_starttls_real(gpointer data, gint source,
+ b_input_condition cond);
-
-static SECStatus nss_auth_cert (void *arg, PRFileDesc *socket, PRBool checksig, PRBool isserver)
+static SECStatus nss_auth_cert(void *arg, PRFileDesc * socket, PRBool checksig,
+ PRBool isserver)
{
return SECSuccess;
}
-static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket)
+static SECStatus nss_bad_cert(void *arg, PRFileDesc * socket)
{
PRErrorCode err;
- if(!arg) return SECFailure;
+ if (!arg)
+ return SECFailure;
- *(PRErrorCode *)arg = err = PORT_GetError();
+ *(PRErrorCode *) arg = err = PORT_GetError();
- switch(err) {
+ switch (err) {
case SEC_ERROR_INVALID_AVA:
case SEC_ERROR_INVALID_TIME:
case SEC_ERROR_BAD_SIGNATURE:
@@ -93,52 +100,63 @@ static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket)
}
}
-
-void ssl_init( void )
+void ssl_init(void)
{
- PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+ PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+ // https://www.mozilla.org/projects/security/pki/nss/ref/ssl/sslfnc.html#1234224
+ // This NSS function is not intended for use with SSL, which
+ // requires that the certificate and key database files be
+ // opened. Relates to whole non-verification of servers for now.
NSS_NoDB_Init(NULL);
NSS_SetDomesticPolicy();
initialized = TRUE;
}
-void *ssl_connect( char *host, int port, gboolean verify, ssl_input_function func, gpointer data )
+void *ssl_connect(char *host, int port, gboolean verify,
+ ssl_input_function func, gpointer data)
{
- struct scd *conn = g_new0( struct scd, 1 );
-
- conn->fd = proxy_connect( host, port, ssl_connected, conn );
+ struct scd *conn = g_new0(struct scd, 1);
+
+ conn->fd = proxy_connect(host, port, ssl_connected, conn);
conn->func = func;
conn->data = data;
-
- if( conn->fd < 0 )
- {
- g_free( conn );
- return( NULL );
+ conn->hostname = g_strdup(host);
+
+ if (conn->fd < 0) {
+ g_free(conn->hostname);
+ g_free(conn);
+ return (NULL);
}
-
- if( !initialized )
- {
+
+ if (!initialized) {
ssl_init();
}
-
- return( conn );
+ return (conn);
}
-static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond )
+static gboolean ssl_starttls_real(gpointer data, gint source,
+ b_input_condition cond)
{
struct scd *conn = data;
- return ssl_connected( conn, conn->fd, B_EV_IO_WRITE );
+ return ssl_connected(conn, conn->fd, B_EV_IO_WRITE);
}
-void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data )
+void *ssl_starttls(int fd, char *hostname, gboolean verify,
+ ssl_input_function func, gpointer data)
{
- struct scd *conn = g_new0( struct scd, 1 );
+ struct scd *conn = g_new0(struct scd, 1);
conn->fd = fd;
conn->func = func;
conn->data = data;
+ conn->hostname = hostname;
+
+ /* For now, SSL verification is globally enabled by setting the cafile
+ setting in bitlbee.conf. Commented out by default because probably
+ not everyone has this file in the same place and plenty of folks
+ may not have the cert of their private Jabber server in it. */
conn->verify = verify && global.conf->cafile;
/* This function should be called via a (short) timeout instead of
@@ -150,108 +168,255 @@ void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function
In short, doing things like this makes the rest of the code a lot
simpler. */
- b_timeout_add( 1, ssl_starttls_real, conn );
+ b_timeout_add(1, ssl_starttls_real, conn);
return conn;
}
-static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
+static gboolean ssl_connected(gpointer data, gint source,
+ b_input_condition cond)
{
struct scd *conn = data;
-
+
/* Right now we don't have any verification functionality for NSS. */
- if( conn->verify )
- {
- conn->func( conn->data, 1, NULL, cond );
- if( source >= 0 ) closesocket( source );
- g_free( conn );
+ if (conn->verify) {
+ conn->func(conn->data, 1, NULL, cond);
+ if (source >= 0)
+ closesocket(source);
+ g_free(conn->hostname);
+ g_free(conn);
return FALSE;
}
-
- if( source == -1 )
+
+ if (source == -1)
goto ssl_connected_failure;
-
+
/* Until we find out how to handle non-blocking I/O with NSS... */
- sock_make_blocking( conn->fd );
-
+ sock_make_blocking(conn->fd);
+
conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source));
+ if (!conn->prfd)
+ goto ssl_connected_failure;
SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE);
SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
- SSL_BadCertHook(conn->prfd, (SSLBadCertHandler)nss_bad_cert, NULL);
- SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate)nss_auth_cert, (void *)CERT_GetDefaultCertDB());
+ SSL_BadCertHook(conn->prfd, (SSLBadCertHandler) nss_bad_cert, NULL);
+ SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate) nss_auth_cert,
+ (void *)CERT_GetDefaultCertDB());
+ SSL_SetURL(conn->prfd, conn->hostname);
SSL_ResetHandshake(conn->prfd, PR_FALSE);
if (SSL_ForceHandshake(conn->prfd)) {
goto ssl_connected_failure;
}
-
-
+
conn->established = TRUE;
- conn->func( conn->data, 0, conn, cond );
+ conn->func(conn->data, 0, conn, cond);
return FALSE;
-
- ssl_connected_failure:
-
- conn->func( conn->data, 0, NULL, cond );
-
- PR_Close( conn -> prfd );
- if( source >= 0 ) closesocket( source );
- g_free( conn );
-
+
+ ssl_connected_failure:
+
+ conn->func(conn->data, 0, NULL, cond);
+
+ if (conn->prfd)
+ PR_Close(conn->prfd);
+ if (source >= 0)
+ closesocket(source);
+ g_free(conn->hostname);
+ g_free(conn);
+
return FALSE;
}
-int ssl_read( void *conn, char *buf, int len )
+int ssl_read(void *conn, char *buf, int len)
{
- if( !((struct scd*)conn)->established )
- return( 0 );
-
- return( PR_Read( ((struct scd*)conn)->prfd, buf, len ) );
+ int st;
+ PRErrorCode PR_err;
+
+ if (!((struct scd *)conn)->established) {
+ ssl_errno = SSL_NOHANDSHAKE;
+ return -1;
+ }
+
+ st = PR_Read(((struct scd *)conn)->prfd, buf, len);
+ PR_err = PR_GetError();
+
+ ssl_errno = SSL_OK;
+ if (PR_err == PR_WOULD_BLOCK_ERROR)
+ ssl_errno = SSL_AGAIN;
+
+ if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0)
+ len = write(STDERR_FILENO, buf, st);
+
+ return st;
}
-int ssl_write( void *conn, const char *buf, int len )
+int ssl_write(void *conn, const char *buf, int len)
{
- if( !((struct scd*)conn)->established )
- return( 0 );
-
- return( PR_Write ( ((struct scd*)conn)->prfd, buf, len ) );
+ int st;
+ PRErrorCode PR_err;
+
+ if (!((struct scd *)conn)->established) {
+ ssl_errno = SSL_NOHANDSHAKE;
+ return -1;
+ }
+ st = PR_Write(((struct scd *)conn)->prfd, buf, len);
+ PR_err = PR_GetError();
+
+ ssl_errno = SSL_OK;
+ if (PR_err == PR_WOULD_BLOCK_ERROR)
+ ssl_errno = SSL_AGAIN;
+
+ if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0)
+ len = write(2, buf, st);
+
+ return st;
}
-int ssl_pending( void *conn )
+int ssl_pending(void *conn)
{
- struct scd *c = (struct scd *) conn;
+ struct scd *c = (struct scd *)conn;
- if( c == NULL ) {
+ if (c == NULL) {
return 0;
}
- return ( c->established && SSL_DataPending( c->prfd ) > 0 );
+ return (c->established && SSL_DataPending(c->prfd) > 0);
}
-void ssl_disconnect( void *conn_ )
+void ssl_disconnect(void *conn_)
{
struct scd *conn = conn_;
-
- PR_Close( conn->prfd );
- closesocket( conn->fd );
-
- g_free( conn );
+
+ // When we swich to NSS_Init, we should have here
+ // NSS_Shutdown();
+
+ if (conn->prfd)
+ PR_Close(conn->prfd);
+
+ g_free(conn->hostname);
+ g_free(conn);
}
-int ssl_getfd( void *conn )
+int ssl_getfd(void *conn)
{
- return( ((struct scd*)conn)->fd );
+ return (((struct scd *)conn)->fd);
}
-b_input_condition ssl_getdirection( void *conn )
+b_input_condition ssl_getdirection(void *conn)
{
/* Just in case someone calls us, let's return the most likely case: */
return B_EV_IO_READ;
}
-char *ssl_verify_strerror( int code )
+char *ssl_verify_strerror(int code)
+{
+ return
+ g_strdup
+ ("SSL certificate verification not supported by BitlBee NSS code.");
+}
+
+size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len,
+ const unsigned char *input, size_t input_len,
+ const unsigned char *iv, unsigned char **res)
{
- return g_strdup( "SSL certificate verification not supported by BitlBee NSS code." );
+#define CIPHER_MECH CKM_DES3_CBC
+#define MAX_OUTPUT_LEN 72
+
+ int len1;
+ unsigned int len2;
+
+ PK11Context *ctx = NULL;
+ PK11SlotInfo *slot = NULL;
+ SECItem keyItem;
+ SECItem ivItem;
+ SECItem *secParam = NULL;
+ PK11SymKey *symKey = NULL;
+
+ size_t rc;
+ SECStatus rv;
+
+ if (!initialized) {
+ ssl_init();
+ }
+
+ keyItem.data = (unsigned char *)key;
+ keyItem.len = key_len;
+
+ slot = PK11_GetBestSlot(CIPHER_MECH, NULL);
+ if (slot == NULL) {
+ fprintf(stderr, "PK11_GetBestSlot failed (err %d)\n",
+ PR_GetError());
+ rc = 0;
+ goto out;
+ }
+
+ symKey =
+ PK11_ImportSymKey(slot, CIPHER_MECH, PK11_OriginUnwrap, CKA_ENCRYPT,
+ &keyItem, NULL);
+ if (symKey == NULL) {
+ fprintf(stderr, "PK11_ImportSymKey failed (err %d)\n",
+ PR_GetError());
+ rc = 0;
+ goto out;
+ }
+
+ ivItem.data = (unsigned char *)iv;
+ /* See msn_soap_passport_sso_handle_response in protocols/msn/soap.c */
+ ivItem.len = 8;
+
+ secParam = PK11_ParamFromIV(CIPHER_MECH, &ivItem);
+ if (secParam == NULL) {
+ fprintf(stderr, "PK11_ParamFromIV failed (err %d)\n",
+ PR_GetError());
+ rc = 0;
+ goto out;
+ }
+
+ ctx =
+ PK11_CreateContextBySymKey(CIPHER_MECH, CKA_ENCRYPT, symKey,
+ secParam);
+ if (ctx == NULL) {
+ fprintf(stderr, "PK11_CreateContextBySymKey failed (err %d)\n",
+ PR_GetError());
+ rc = 0;
+ goto out;
+ }
+
+ *res = g_new0(unsigned char, MAX_OUTPUT_LEN);
+
+ rv = PK11_CipherOp(ctx, *res, &len1, MAX_OUTPUT_LEN,
+ (unsigned char *)input, input_len);
+ if (rv != SECSuccess) {
+ fprintf(stderr, "PK11_CipherOp failed (err %d)\n",
+ PR_GetError());
+ rc = 0;
+ goto out;
+ }
+
+ assert(len1 <= MAX_OUTPUT_LEN);
+
+ rv = PK11_DigestFinal(ctx, *res + len1, &len2,
+ (unsigned int)MAX_OUTPUT_LEN - len1);
+ if (rv != SECSuccess) {
+ fprintf(stderr, "PK11_DigestFinal failed (err %d)\n",
+ PR_GetError());
+ rc = 0;
+ goto out;
+ }
+
+ rc = len1 + len2;
+
+ out:
+ if (ctx)
+ PK11_DestroyContext(ctx, PR_TRUE);
+ if (symKey)
+ PK11_FreeSymKey(symKey);
+ if (secParam)
+ SECITEM_FreeItem(secParam, PR_TRUE);
+ if (slot)
+ PK11_FreeSlot(slot);
+
+ return rc;
}