eudora-mac/OpenSSL.cp

1 line
41 KiB
C++
Executable File

/* Copyright (c) 2017, Computer History Museum
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to
the limitations in the disclaimer below) provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE. */
/*
OpenSSL.cp
OpenSSL bits for Eudora. Match up the OpenSSL interfaces to the Eudora ones
Basic data structures:
SSL_CTX -- an SSL context. Has an connection method (client/server),
(SSL2/SSL3/TLS1) and some certs assoctiate with it. SSL_CTX_new/SSL_CTX_free.
SSL -- a SSL connection object. Manages a single SSL connection. Has an
associated SSL_CTX. SSL_new/SSL_free.
BIO - Basic I/O -- an object to run the IO. Talks to a file/socket/whatever.
There are filter bios, too. Some handle the encryption and decryption. Another
might handle logging, etc.
We define a new BIO class, that talks to an OpenTransport socket via an Eudora TransStream;
since OpenSSL doesn't know OpenTransport from Adam.
Gotchas -
1. Since the SSL_CTX knows the connection method (v2, v3, TLSV1), we can't
share the SSL_CTX between all the connections. Rats.
2. Since OpenSSL is Mach-O, but Eudora is (currently) PEF, we can't call
OpenSSL functions directly, but will have to bridge them. Also, we have to
make the function pointers that OpenSSL calls into Mach-O function pointers,
since it doesn't (again) know PEF from Adam. In particular, the BIO is a table
of function pointers.
3. OpenSSL wants the certificates in PEM format; we've got them in BER.
*/
#include "OpenSSL.h"
#include "MachOWrapper.h"
/* NO MORE "relaxed pointer rules"!!! */
#pragma mpwc_relax off
static void *MachOFunctionPointerForCFMFunctionPointer( void *cfmfp );
extern TransVector ESSLSubTrans;
#define SSL_CTRL_OPTIONS 32
#define SSL_CTX_set_options(ctx,op) \
SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL)
/* Maybe.. */
int BIO_ot_should_retry ( OTResult err );
int BIO_ot_non_fatal_error ( OTResult err );
BIO * BIO_new ( BIO_METHOD *type );
long BIO_int_ctrl(BIO *bp,int cmd,long larg,int iarg);
long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg);
/* These are the MachO function pointers for the BIO */
void *gOTWrite = NULL;
void *gOTRead = NULL;
void *gOTPuts = NULL;
void *gOTCtrl = NULL;
void *gOTNew = NULL;
void *gOTFree = NULL;
CFBundleRef gSSLBundle = NULL;
#define BIO_TYPE_OT_SOCKET (25|0x0400|0x0100)
/* use either SSL_VERIFY_NONE or SSL_VERIFY_PEER, the last 2 options
* are 'ored' with SSL_VERIFY_PEER if they are desired */
#define SSL_VERIFY_NONE 0x00
#define SSL_VERIFY_PEER 0x01
#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02
#define SSL_VERIFY_CLIENT_ONCE 0x04
#pragma mark -- OT & OpenSSL setup --
/* Private functions */
extern "C" void InitMachOFunctions ();
OSStatus OpenSSLReadCerts ( SSL *ssl, ConstStr255Param hostName );
/* We have to add these! */
X509 *d2i_X509(X509 **px, unsigned char **in, int len);
int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x);
void SSL_CTX_set_verify_depth(SSL_CTX *ctx,int depth);
BIO_METHOD *BIO_s_otsocket ( void ) {
if ( NULL == gOTWrite )
InitMachOFunctions ();
static BIO_METHOD methods_otp = {
BIO_TYPE_OT_SOCKET,
"ot_socket_in_Eudora",
( int (*) ( bio_st *, const char *, int )) gOTWrite,
( int (*) ( bio_st *, char *, int )) gOTRead,
( int (*) ( bio_st *, const char * )) gOTPuts,
NULL, /* sock_gets, */
( long (*) ( bio_st *, int, long, void * )) gOTCtrl,
( int (*) ( bio_st * )) gOTNew,
( int (*) ( bio_st * )) gOTFree,
NULL,
};
return &methods_otp;
}
static int ot_write(BIO *h, const char *buf, int num);
static int ot_read(BIO *h, char *buf, int size);
static int ot_puts(BIO *h, const char *str);
static long ot_ctrl(BIO *h, int cmd, long arg1, void *arg2);
static int ot_new(BIO *h);
static int ot_free(BIO *data);
BIO *BIO_new_ot ( TransStream ts, int close_flag ) {
BIO *ret;
ret= BIO_new ( BIO_s_otsocket ());
if ( NULL == ret )
return NULL;
BIO_set_fd ( ret, (int) ts, close_flag );
return ret;
}
static int ot_new ( BIO *bi ) {
bi->init=0;
bi->num=0;
bi->ptr=NULL;
bi->flags=0;
return(1);
}
static int ot_free ( BIO *a ) {
if ( NULL == a )
return 0;
if (a->shutdown) { // Should we close?
if (a->init) { // Did we init??
// Close the socket
// SHUTDOWN2(a->num);
}
a->init=0; // no longer opened
a->flags=0;
}
return 1;
}
// See <http://www.freebsdchina.org/utils/phpMan.php/man/BIO_read/3> for more info here
static int ot_read ( BIO *b, char *out, int outl ) {
long err = 0;
OTFlags flags = 0;
ASSERT ( outl > 0 );
if ( outl <= 0 )
return 0;
if ( out != NULL ) {
// Read some bytes from the connection
TransStream stream = (TransStream) b->num;
SInt32 processed;
processed = outl;
err = (*ESSLSubTrans.vRecvTrans) ( stream, (unsigned char *) out, &processed );
if ( noErr == err )
err = processed;
// if ( err > 0 )
// print_data ( out, err );
BIO_clear_retry_flags(b);
if ( err <= noErr ) { // we didn't read any bytes
if ( BIO_ot_should_retry ( err )) {
BIO_set_retry_read ( b );
err = -1; // retry me!
}
else
err = 0; // general purpose error
}
}
return err;
}
static int ot_write(BIO *b, const char *in, int inl) {
long err = 0;
ASSERT ( NULL != in );
ASSERT ( inl > 0 );
if ( NULL == in || inl <= 0 )
return 0;
// Instead of pushing the bytes directly, we should call via Eudora's TransVector.
err = (*ESSLSubTrans.vSendTrans)((TransStream) b->num, (unsigned char *) in, inl, nil);
if ( noErr == err )
err = inl;
// if ( err > 0 )
// print_data ( in, err );
BIO_clear_retry_flags ( b );
if ( err < 0 ) { // we didn't write any bytes
if ( BIO_ot_should_retry ( err )) {
BIO_set_retry_write ( b );
err = -1; // retry me!
}
else
err = 0; // general purpose error
}
return err;
}
// See <http://www.freebsdchina.org/utils/phpMan.php?parameter=BIO_ctrl&mode=man>
static long ot_ctrl ( BIO *b, int cmd, long num, void *ptr ) {
long ret=1;
// printf ( "ot_ctrl: %d\n", cmd );
switch (cmd) {
case BIO_CTRL_RESET: num=0; // fall into seek code
case BIO_C_FILE_SEEK:
return 0;
case BIO_C_FILE_TELL:
case BIO_CTRL_INFO:
return 0;
case BIO_C_SET_FD: // Set the endpoint for the BIO
ot_free(b); // Close whatever's already there
b->num= *((int *)ptr); // Set the new endpoint
b->shutdown=(int)num; // Set the close mode
b->init=1; // We've been initialized
break;
case BIO_C_GET_FD: // Return the endpoint for the BIO
if ( b->init ) {
if ( NULL != ptr )
* ((int *) ptr ) = ret = b->num;
}
else
ret= -1;
break;
case BIO_CTRL_GET_CLOSE: // Get the close flag
ret = b->shutdown;
break;
case BIO_CTRL_SET_CLOSE:
b->shutdown = (int)num; // Set the close flag
break;
case BIO_CTRL_PENDING:
case BIO_CTRL_WPENDING:
ret=0;
break;
case BIO_CTRL_DUP:
case BIO_CTRL_FLUSH:
ret=1;
break;
default:
ret=0;
break;
}
return(ret);
}
static int ot_puts ( BIO *bp, const char *str ) {
ASSERT ( NULL != str );
return ot_write ( bp, str, strlen ( str ));
}
// Given an OTResult, decide if it's an error that we should retry on
int BIO_ot_should_retry ( OTResult err ) {
ASSERT ( err <= 0 );
// printf ( "BIO_ot_should_retry: %d\n", err );
return BIO_ot_non_fatal_error ( err );
}
// Is this an error that we should abort on?
int BIO_ot_non_fatal_error ( OTResult err ) {
ASSERT ( err <= 0 );
// printf ( "BIO_ot_non_fatal_error: %d\n", err );
switch ( err ) {
case noErr: // no problems, just no data right now
case kOTNoDataErr: // no data to read right now
case kOTFlowErr: // can't send data right now
case kOTStateChangeErr: // state is changing
// More cases to go here
// printf ( "BIO_ot_non_fatal_error returning 1\n" );
return 1; // non-fatal errors
default:
// printf ( "BIO_ot_non_fatal_error returning 0\n" );
return 0; // fatal errors
}
// printf ( "BIO_ot_non_fatal_error returning 0 (2)\n" );
return 0; // belt and suspenders (and to keep the compiler quiet)
}
#pragma mark --Combo Routines--
OSStatus SetupSSLConnection ( TransStream ep, long method, CertVerifyProc callback ) {
ASSERT ( ep->ctx == NULL );
ASSERT ( ep->ssl == NULL );
ASSERT ( ep->bio == NULL );
if ( NULL == gSSLBundle ) {
OSStatus err = InitOpenSSL ();
if ( noErr != err )
return err;
}
SSL_METHOD *meth = NULL;
// These are SSL-plus numbers, but they're enshrined in Eudora's settings
switch ( method ) {
// SSL_Version_3_0_With_2_0_Hello
case 100: meth = SSLv23_client_method (); ASSERT ( meth != NULL ); break;
// SSL_Version_2_0
case 2: meth = SSLv2_client_method (); ASSERT ( meth != NULL ); break;
// SSL_Version_3_0_Only and SSL_Version_3_0
case 0x0300:
case 101: meth = SSLv3_client_method (); ASSERT ( meth != NULL ); break;
// TLS_Version_1_0 and TLS_Version_1_0_Only
case 0x0301:
case 102: meth = TLSv1_client_method (); ASSERT ( meth != NULL ); break;
// If something else, give it our best shot
default:
meth = SSLv23_client_method (); ASSERT ( meth != NULL );
break;
}
/*
On Mac OS X the trusted root certificates are stored in a keychain at:
/System/Library/Keychains/X509Anchors
*/
ep->ctx = SSL_CTX_new ( meth ); ASSERT ( ep->ctx != NULL );
SSL_CTX_set_options ( ep->ctx, 0 );
if ( NULL == callback )
SSL_CTX_set_verify ( ep->ctx, SSL_VERIFY_NONE, NULL );
else
SSL_CTX_set_verify ( ep->ctx, SSL_VERIFY_PEER, callback );
// Read the CA certs
OpenSSLReadCerts ( ep->ctx, ep->serverName );
SSL_CTX_set_verify_depth ( ep->ctx, 4 );
ep->ssl = SSL_new ( ep->ctx ); ASSERT ( ep->ssl != NULL );
ep->bio = BIO_new_ot ( ep, BIO_NOCLOSE ); ASSERT ( ep->bio != NULL );
SSL_set_bio ( ep->ssl, ep->bio, ep->bio ); // Connect the bio to the SSL engine
SSL_set_connect_state ( ep->ssl ); // Tell it that it's a client
return noErr;
}
OSStatus CleanupSSLConnection ( TransStream ep ) {
if ( NULL != ep->ssl ) { SSL_free ( ep->ssl ); ep->ssl = NULL; }
if ( NULL != ep->ctx ) { SSL_CTX_free ( ep->ctx ); ep->ctx = NULL; }
// We don't have to do this, because according to "Programming with OpenSSL,
// page 126, 'SSL_free automatically frees the SSL object's underlying BIOs for us.'
// if ( NULL != bio ) SSL_BIO_free ( bio );
ep->bio = NULL;
return noErr;
}
#pragma mark --Bridge Routines--
SSL * SSL_new ( SSL_CTX *ctx ) {
static MachOWrapper < SSL * (*) ( SSL_CTX * ) > gSSL_new ( gSSLBundle, CFSTR ( "SSL_new" ));
ASSERT ( gSSL_new.IsValid ());
return gSSL_new ( ctx );
}
void SSL_free ( SSL *ssl ) {
static MachOWrapper < void (*) ( SSL * ) > gSSL_free ( gSSLBundle, CFSTR ( "SSL_free" ));
ASSERT ( gSSL_free.IsValid ());
gSSL_free ( ssl );
}
void SSL_set_bio ( SSL *ssl, BIO *rbio, BIO *wbio ) {
static MachOWrapper < void (*) ( SSL *, BIO *, BIO * ) > gSSL_set_bio ( gSSLBundle, CFSTR ( "SSL_set_bio" ));
ASSERT ( gSSL_set_bio.IsValid ());
gSSL_set_bio ( ssl, rbio, wbio );
}
SSL_METHOD *SSLv2_client_method(void) {
static MachOWrapper < SSL_METHOD * (*) ( void ) > gSSLv2_client_method ( gSSLBundle, CFSTR ( "SSLv2_client_method" ));
ASSERT ( gSSLv2_client_method.IsValid ());
return gSSLv2_client_method ();
}
SSL_METHOD *SSLv3_client_method(void) {
static MachOWrapper < SSL_METHOD * (*) ( void ) > gSSLv3_client_method ( gSSLBundle, CFSTR ( "SSLv3_client_method" ));
ASSERT ( gSSLv3_client_method.IsValid ());
return gSSLv3_client_method ();
}
SSL_METHOD *SSLv23_client_method(void) {
static MachOWrapper < SSL_METHOD * (*) ( void ) > gSSLv23_client_method ( gSSLBundle, CFSTR ( "SSLv23_client_method" ));
ASSERT ( gSSLv23_client_method.IsValid ());
return gSSLv23_client_method ();
}
SSL_METHOD *TLSv1_client_method(void) {
static MachOWrapper < SSL_METHOD * (*) ( void ) > gTLSv1_client_method ( gSSLBundle, CFSTR ( "TLSv1_client_method" ));
ASSERT ( gTLSv1_client_method.IsValid ());
return gTLSv1_client_method ();
}
SSL_CTX * SSL_CTX_new ( SSL_METHOD *meth ) {
static MachOWrapper < SSL_CTX * (*) ( SSL_METHOD * ) > gSSL_CTX_new ( gSSLBundle, CFSTR ( "SSL_CTX_new" ));
ASSERT ( gSSL_CTX_new.IsValid ());
return gSSL_CTX_new ( meth );
}
void SSL_CTX_set_verify ( SSL_CTX *ctx,int mode, CertVerifyProc callback ) {
static MachOWrapper < void (*) ( SSL_CTX *,int , CertVerifyProc ) > gSSL_CTX_set_verify ( gSSLBundle, CFSTR ( "SSL_CTX_set_verify" ));
ASSERT ( gSSL_CTX_set_verify.IsValid ());
// We just leak this small block of memory
void *cb = NULL != callback ? MachOFunctionPointerForCFMFunctionPointer ( callback ) : NULL;
gSSL_CTX_set_verify ( ctx, mode, (CertVerifyProc) cb );
}
void SSL_CTX_free ( SSL_CTX * ctx ) {
static MachOWrapper < void (*) ( SSL_CTX * ) > gSSL_CTX_free ( gSSLBundle, CFSTR ( "SSL_CTX_free" ));
ASSERT ( gSSL_CTX_free.IsValid ());
gSSL_CTX_free ( ctx );
}
long SSL_CTX_ctrl ( SSL_CTX *ctx, int cmd, long larg, void *parg ) {
static MachOWrapper < long (*) ( SSL_CTX *ctx, int, long, void * ) > gSSL_CTX_ctrl ( gSSLBundle, CFSTR ( "SSL_CTX_ctrl" ));
ASSERT ( gSSL_CTX_ctrl.IsValid ());
return gSSL_CTX_ctrl ( ctx, cmd, larg, parg );
}
void SSL_set_connect_state ( SSL *ssl ) {
static MachOWrapper < void (*) ( SSL * ) > gSSL_set_connect_state ( gSSLBundle, CFSTR ( "SSL_set_connect_state" ));
ASSERT ( gSSL_set_connect_state.IsValid ());
gSSL_set_connect_state ( ssl );
}
int SSL_do_handshake ( SSL * ssl ) {
static MachOWrapper < int (*) ( SSL * ) > gSSL_do_handshake ( gSSLBundle, CFSTR ( "SSL_do_handshake" ));
ASSERT ( gSSL_do_handshake.IsValid ());
return gSSL_do_handshake ( ssl );
}
int SSL_write ( SSL * ssl, const void *buffer, int len ) {
static MachOWrapper < int (*) ( SSL *, const void *, int ) > gSSL_write ( gSSLBundle, CFSTR ( "SSL_write" ));
ASSERT ( gSSL_write.IsValid ());
return gSSL_write ( ssl, buffer, len );
}
int SSL_read ( SSL * ssl, void *buffer, int len ) {
static MachOWrapper < int (*) ( SSL *, void *, int ) > gSSL_read ( gSSLBundle, CFSTR ( "SSL_read" ));
ASSERT ( gSSL_read.IsValid ());
return gSSL_read ( ssl, buffer, len );
}
int SSL_get_error ( SSL *ssl, int ret ) {
static MachOWrapper < int (*) ( SSL *, int ) > gSSL_get_error ( gSSLBundle, CFSTR ( "SSL_get_error" ));
ASSERT ( gSSL_get_error.IsValid ());
return gSSL_get_error ( ssl, ret );
}
BIO * BIO_new ( BIO_METHOD *type ) {
static MachOWrapper < BIO * (*) ( BIO_METHOD * ) > gBIO_new ( gSSLBundle, CFSTR ( "BIO_new" ));
ASSERT ( gBIO_new.IsValid ());
return gBIO_new ( type );
}
long BIO_int_ctrl ( BIO *bp,int cmd,long larg,int iarg ) {
static MachOWrapper < long (*) ( BIO *, int, long, int ) > gBIO_int_ctrl ( gSSLBundle, CFSTR ( "BIO_int_ctrl" ));
ASSERT ( gBIO_int_ctrl.IsValid ());
return gBIO_int_ctrl ( bp, cmd, larg, iarg );
}
X509 * d2i_X509 ( X509 **px, unsigned char **in, int len ) {
static MachOWrapper < X509 * (*) ( X509 **, unsigned char **, int ) > gd2i_X509 ( gSSLBundle, CFSTR ( "d2i_X509" ));
ASSERT ( gd2i_X509.IsValid ());
return gd2i_X509 ( px, in, len );
}
int SSL_CTX_use_certificate ( SSL_CTX *ctx, X509 *x ) {
static MachOWrapper < int (*) ( SSL_CTX *, X509 * ) > gSSL_CTX_use_certificate ( gSSLBundle, CFSTR ( "SSL_CTX_use_certificate" ));
ASSERT ( gSSL_CTX_use_certificate.IsValid ());
return gSSL_CTX_use_certificate ( ctx, x );
}
X509 * X509_STORE_CTX_get_current_cert ( X509_STORE_CTX *ctx ) {
static MachOWrapper < X509 * (*) ( X509_STORE_CTX * ) > gX509_STORE_CTX_get_current_cert ( gSSLBundle, CFSTR ( "X509_STORE_CTX_get_current_cert" ));
ASSERT ( gX509_STORE_CTX_get_current_cert.IsValid ());
return gX509_STORE_CTX_get_current_cert ( ctx );
}
int X509_STORE_CTX_get_error_depth ( X509_STORE_CTX *ctx ) {
static MachOWrapper < int (*) ( X509_STORE_CTX * ) > gX509_STORE_CTX_get_error_depth ( gSSLBundle, CFSTR ( "X509_STORE_CTX_get_error_depth" ));
ASSERT ( gX509_STORE_CTX_get_error_depth.IsValid ());
return gX509_STORE_CTX_get_error_depth ( ctx );
}
int X509_STORE_CTX_get_error ( X509_STORE_CTX *ctx ) {
static MachOWrapper < int (*) ( X509_STORE_CTX * ) > gX509_STORE_CTX_get_error ( gSSLBundle, CFSTR ( "X509_STORE_CTX_get_error" ));
ASSERT ( gX509_STORE_CTX_get_error.IsValid ());
return gX509_STORE_CTX_get_error ( ctx );
}
X509_NAME * X509_get_subject_name ( X509 *a ) {
static MachOWrapper < X509_NAME * (*) ( X509 * ) > gX509_get_subject_name ( gSSLBundle, CFSTR ( "X509_get_subject_name" ));
ASSERT ( gX509_get_subject_name.IsValid ());
return gX509_get_subject_name ( a );
}
char * X509_NAME_oneline ( X509_NAME *a, char *buf, int size ) {
static MachOWrapper < char * (*) ( X509_NAME*, char *, int ) > gX509_NAME_oneline ( gSSLBundle, CFSTR ( "X509_NAME_oneline" ));
ASSERT ( gX509_NAME_oneline.IsValid ());
return gX509_NAME_oneline ( a, buf, size );
}
int X509_NAME_get_text_by_NID ( X509_NAME *name, int nid, char *buf,int len ) {
static MachOWrapper < int (*) ( X509_NAME *, int, char *, int ) > gX509_NAME_get_text_by_NID ( gSSLBundle, CFSTR ( "X509_NAME_get_text_by_NID" ));
ASSERT ( gX509_NAME_get_text_by_NID.IsValid ());
return gX509_NAME_get_text_by_NID ( name, nid, buf, len );
}
int X509_STORE_add_cert ( X509_STORE *store, X509 *x ) {
static MachOWrapper < int (*) ( X509_STORE *, X509 * ) > gX509_STORE_add_cert ( gSSLBundle, CFSTR ( "X509_STORE_add_cert" ));
ASSERT ( gX509_STORE_add_cert.IsValid ());
return gX509_STORE_add_cert ( store, x );
}
X509_STORE *SSL_CTX_get_cert_store ( SSL_CTX *ctx ) {
static MachOWrapper < X509_STORE * (*) ( SSL_CTX * ) > gSSL_CTX_get_cert_store ( gSSLBundle, CFSTR ( "SSL_CTX_get_cert_store" ));
ASSERT ( gSSL_CTX_get_cert_store.IsValid ());
return gSSL_CTX_get_cert_store ( ctx );
}
static void X509_free ( X509 *x ) {
static MachOWrapper < void (*) ( X509 * ) > gX509_free ( gSSLBundle, CFSTR ( "X509_free" ));
ASSERT ( gX509_free.IsValid ());
return gX509_free ( x );
}
static int X509_cmp ( const X509 *a, const X509 *b ) {
static MachOWrapper < int (*) ( const X509 *, const X509 * ) > gX509_cmp ( gSSLBundle, CFSTR ( "X509_cmp" ));
ASSERT ( gX509_cmp.IsValid ());
return gX509_cmp ( a, b );
}
void SSL_CTX_set_verify_depth ( SSL_CTX *ctx,int depth ) {
static MachOWrapper < void (*) ( SSL_CTX *, int ) > gSSL_CTX_set_verify_depth ( gSSLBundle, CFSTR ( "SSL_CTX_set_verify_depth" ));
ASSERT ( gSSL_CTX_set_verify_depth.IsValid ());
return gSSL_CTX_set_verify_depth ( ctx, depth );
}
void *CRYPTO_malloc ( int num, const char *file, int line ) {
static MachOWrapper < void * (*) ( int, const char *, int ) > gCRYPTO_malloc ( gSSLBundle, CFSTR ( "CRYPTO_malloc" ));
ASSERT ( gCRYPTO_malloc.IsValid ());
return gCRYPTO_malloc ( num, file, line );
}
/*static*/ void CRYPTO_free ( void *p ) {
static MachOWrapper < void (*) ( void * ) > gCRYPTO_free ( gSSLBundle, CFSTR ( "CRYPTO_free" ));
ASSERT ( gCRYPTO_free.IsValid ());
return gCRYPTO_free ( p );
}
static int i2d_X509 ( X509 *x, unsigned char **out ) {
static MachOWrapper < int (*) ( X509 *, unsigned char ** ) > gi2d_X509 ( gSSLBundle, CFSTR ( "i2d_X509" ));
ASSERT ( gi2d_X509.IsValid ());
return gi2d_X509 ( x, out );
}
static int X509_print ( BIO *bp,X509 *x ) {
static MachOWrapper < int (*) ( BIO *, X509 * ) > gX509_print ( gSSLBundle, CFSTR ( "X509_print" ));
ASSERT ( gX509_print.IsValid ());
return gX509_print ( bp, x );
}
BIO_METHOD * BIO_s_mem ( void ) {
static MachOWrapper < BIO_METHOD * (*) () > gBIO_s_mem ( gSSLBundle, CFSTR ( "BIO_s_mem" ));
ASSERT ( gBIO_s_mem.IsValid ());
return gBIO_s_mem ();
}
int BIO_free ( BIO *a ) {
static MachOWrapper < int (*) ( BIO * ) > gBIO_free ( gSSLBundle, CFSTR ( "BIO_free" ));
ASSERT ( gBIO_free.IsValid ());
return gBIO_free ( a );
}
int BIO_gets ( BIO *bp, char *buf, int size ) {
static MachOWrapper < int (*) ( BIO *, char *, int ) > gBIO_gets ( gSSLBundle, CFSTR ( "BIO_gets" ));
ASSERT ( gBIO_gets.IsValid ());
return gBIO_gets ( bp, buf, size );
}
unsigned long SSLeay ( void ) {
static MachOWrapper < int (*) () > gSSLeay ( gSSLBundle, CFSTR ( "SSLeay" ));
ASSERT ( gSSLeay.IsValid ());
return gSSLeay ();
}
#pragma mark --Glue Routines--
/************************************************************************
* MachOFunctionPointerForCFMFunctionPointer - This function allocates a
* block of MachO glue code which contains the instructions to call CFM
* routines. Thanks, Marshall! :)
************************************************************************/
UInt32 gMachOTempl[6] = {0x3D800000, 0x618C0000, 0x800C0000, 0x804C0004, 0x7C0903A6, 0x4E800420};
static void *MachOFunctionPointerForCFMFunctionPointer( void *cfmfp ) {
UInt32 *mfp = (UInt32*) NewPtr( sizeof(gMachOTempl) ); // Must later dispose of allocated memory
if (mfp) {
mfp[0] = gMachOTempl [0] | ((UInt32)cfmfp >> 16);
mfp[1] = gMachOTempl [1] | ((UInt32)cfmfp & 0xFFFF);
mfp[2] = gMachOTempl [2];
mfp[3] = gMachOTempl [3];
mfp[4] = gMachOTempl [4];
mfp[5] = gMachOTempl [5];
MakeDataExecutable( mfp, sizeof(gMachOTempl) );
}
return( mfp );
}
/* See <http://www.freebsdchina.org/utils/phpMan.php?parameter=i2d_X509&mode=man>
i2d_X509() encodes the structure pointed to by x into DER format. If
out is not NULL is writes the DER encoded data to the buffer at *out,
and increments it to point after the data just written. If the return
value is negative an error occurred, otherwise it returns the length of
the encoded data.
For OpenSSL 0.9.7 and later if *out is NULL memory will be allocated
for a buffer and the encoded data written to it. In this case *out is
not incremented and it points to the start of the data just written.
*/
static int i2d_X509_compat ( X509 *x, unsigned char **out ) {
ASSERT ( NULL == *out );
unsigned long vers = SSLeay ();
if ( vers >= 0x00907000 ) // OpenSSL 0.9.7 has "the fix"
return i2d_X509 ( x, out );
else {
int len = i2d_X509 ( x, NULL );
unsigned char *ptr = *out = (unsigned char *) OPENSSL_malloc ( len );
return i2d_X509 ( x, &ptr );
}
ASSERT ( 0 );
return 0;
}
// Create the SSL Bundle on the fly
static void CreateSSLBundle () {
MachOWrapper < int (*) ( const char * ) > lSystem ( CFSTR ( "System.framework" ), CFSTR ( "system" ));
ASSERT ( lSystem.IsValid ());
// Make a fake framework that we can call
// Note that in the center of the framework, where the shared library is supposed to be,
// we have a symlink to the openSSL shared library, which lives in a "well known place"
// (and in fact, is a symlink to somewhere else.
//
// Yes, this is a horrible hack. Marshall - 08-18-2004
lSystem ( "mkdir ~/Library/Application\\ Support/Eudora" );
lSystem ( "mkdir ~/Library/Application\\ Support/Eudora/OpenSSL.framework" );
lSystem ( "mkdir ~/Library/Application\\ Support/Eudora/OpenSSL.framework/Contents" );
lSystem ( "mkdir ~/Library/Application\\ Support/Eudora/OpenSSL.framework/Contents/Resources" );
lSystem ( "mkdir ~/Library/Application\\ Support/Eudora/OpenSSL.framework/Contents/Versions" );
lSystem ( "mkdir ~/Library/Application\\ Support/Eudora/OpenSSL.framework/Contents/Versions/A" );
lSystem ( "ln -s /usr/lib/libssl.dylib ~/Library/Application\\ Support/Eudora/OpenSSL.framework/Contents/Versions/A/OpenSSL" );
lSystem ( "ln -s Contents/Versions/A/OpenSSL ~/Library/Application\\ Support/Eudora/OpenSSL.framework/OpenSSL" );
lSystem ( "ln -s A ~/Library/Application\\ Support/Eudora/OpenSSL.framework/Contents/Versions/Current" );
}
OSStatus InitOpenSSL () {
OSStatus err = noErr;
FSRef fsRef;
// The library should exist at "~/Library/Application Support/Eudora/OpenSSL.framework"
err = FSFindFolder ( kUserDomain, kApplicationSupportFolderType, kCreateFolder, &fsRef );
if ( noErr == err ) {
CFURLRef asURL = CFURLCreateFromFSRef ( NULL, &fsRef );
ASSERT ( asURL != NULL );
if ( NULL != asURL ) {
// CFShow ( asURL );
CFURLRef sslLibURL = CFURLCreateCopyAppendingPathComponent ( NULL, asURL, CFSTR ( "Eudora/OpenSSL.framework" ), true );
ASSERT ( sslLibURL != NULL );
if ( NULL != sslLibURL ) {
// CFShow ( sslLibURL );
gSSLBundle = CFBundleCreate ( NULL, sslLibURL );
// If the framework isn't there, create it and try again....
if ( NULL == gSSLBundle ) {
CreateSSLBundle ();
gSSLBundle = CFBundleCreate ( NULL, sslLibURL );
}
if ( NULL == gSSLBundle || !CFBundleLoadExecutable ( gSSLBundle )) {
err = coreFoundationUnknownErr;
ASSERT ( false );
}
CFRelease ( sslLibURL );
}
CFRelease ( asURL );
}
if ( noErr == err ) {
ASSERT ( gSSLBundle != NULL );
// int SSL_library_init(void );
MachOWrapper < int (*) ( void ) > lSSL_library_init ( gSSLBundle, CFSTR ( "SSL_library_init" ));
ASSERT ( lSSL_library_init.IsValid ());
lSSL_library_init ();
// void SSL_load_error_strings(void );
MachOWrapper < void (*) ( void ) > lSSL_load_error_strings ( gSSLBundle, CFSTR ( "SSL_load_error_strings" ));
ASSERT ( lSSL_load_error_strings.IsValid ());
lSSL_load_error_strings ();
}
}
return err;
}
// Type safety!
// template < typename FP >
// inline FP MakeMachOFunctionPointer ( FP cfmProc ) { return (FP) MachOFunctionPointerForCFMFunctionPointer ( cfmProc ); }
void InitMachOFunctions () {
gOTWrite = MachOFunctionPointerForCFMFunctionPointer ( ot_write );
gOTRead = MachOFunctionPointerForCFMFunctionPointer ( ot_read );
gOTPuts = MachOFunctionPointerForCFMFunctionPointer ( ot_puts );
gOTCtrl = MachOFunctionPointerForCFMFunctionPointer ( ot_ctrl );
gOTNew = MachOFunctionPointerForCFMFunctionPointer ( ot_new );
gOTFree = MachOFunctionPointerForCFMFunctionPointer ( ot_free );
}
#include <KeychainCore.h>
#include <KeychainHI.h>
#define kCDSACertClass (0x80000000 + 0x1000)
typedef struct OpaqueSecCertificateRef *SecCertificateRef;
typedef struct OpaqueSecTrustRef *SecTrustRef;
typedef struct OpaquePolicySearchRef *SecPolicySearchRef;
typedef struct OpaqueSecPolicyRef *SecPolicyRef;
typedef UInt32 CSSM_CERT_TYPE;
typedef UInt32 CSSM_CERT_ENCODING;
typedef struct cssm_data {
UInt32 Length; /* in bytes */
UInt8 *Data;
} CSSM_DATA, *CSSM_DATA_PTR;
#define CSSM_CERT_X_509v3 0x03
#define CSSM_CERT_ENCODING_DER 0x03
#define NID_commonName 13
static OSErr OpenSSLAddCertToStore ( SSL_CTX *ctx, CSSM_DATA_PTR cert, const char *hostName ) {
X509 *X509Ptr = d2i_X509 ( NULL, &cert->Data, cert->Length );
if ( NULL != X509Ptr ) {
X509_STORE *store = SSL_CTX_get_cert_store ( ctx );
if ( 0 == X509_STORE_add_cert ( store, X509Ptr ))
ASSERT ( false );
X509_free ( X509Ptr );
}
return noErr;
}
/* This is used to hold everything. It is used for all certificate
* validation. Once we have a certificate chain, the 'verify'
* function is then called to actually check the cert chain. */
struct my_x509_store_st {
/* The following is a cache of trusted certs */
int cache; /* if true, stash any hits */
STACK_OF(X509_OBJECT) *objs; /* Cache of all objects */
/* These are external lookup methods */
STACK_OF(X509_LOOKUP) *get_cert_methods;
/* The following fields are not used by X509_STORE but are
* inherited by X509_STORE_CTX when it is initialised.
*/
unsigned long flags; /* Various verify flags */
int purpose;
int trust;
/* Callbacks for various operations */
int (*verify)(X509_STORE_CTX *ctx); /* called to verify a certificate */
int (*verify_cb)(int ok,X509_STORE_CTX *ctx); /* error callback */
int (*get_issuer)(X509 **issuer, X509_STORE_CTX *ctx, X509 *x); /* get issuers cert from ctx */
int (*check_issued)(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); /* check issued */
int (*check_revocation)(X509_STORE_CTX *ctx); /* Check revocation status of chain */
int (*get_crl)(X509_STORE_CTX *ctx, void **crl, X509 *x); /* retrieve CRL */
int (*check_crl)(X509_STORE_CTX *ctx, void *crl); /* Check CRL validity */
int (*cert_crl)(X509_STORE_CTX *ctx, void *crl, X509 *x); /* Check certificate against CRL */
int (*cleanup)(X509_STORE_CTX *ctx);
CRYPTO_EX_DATA ex_data;
int references;
int depth; /* how deep to look (still unused -- X509_STORE_CTX's depth is used) */
} /* X509_STORE */;
typedef my_x509_store_st MyX509_STORE;
struct my_x509_object_st {
/* one of the above types */
int type;
union {
char *ptr;
X509 *x509;
void *crl;
void *pkey;
} data;
};
typedef my_x509_object_st MyX509_OBJECT;
void DumpCertStore ( X509_STORE *store ) {
ASSERT ( NULL != store );
#if 1
MyX509_STORE *myStore = (MyX509_STORE *) store;
int numCerts = myStore->objs->num;
/*
void X509_STORE_set_flags(X509_STORE *ctx, long flags);
int X509_STORE_set_purpose(X509_STORE *ctx, int purpose);
int X509_STORE_set_trust(X509_STORE *ctx, int trust);
*/
fprintf ( stderr, "Cert Store contains %d objects\n", numCerts );
for ( int i = 0; i < numCerts; ++i ) {
MyX509_OBJECT *anObj = (MyX509_OBJECT *) myStore->objs->data [ i ];
if ( 1 == anObj->type ) { // it's a cert!
X509 *aCert = anObj->data.x509;
char peer_CN[256];
X509_NAME_get_text_by_NID ( X509_get_subject_name ( aCert ),
NID_commonName, peer_CN, sizeof ( peer_CN ));
fprintf ( stderr, "Cert %d: '%s'\n", i, peer_CN );
}
}
fprintf ( stderr, "\n" );
fflush ( stderr );
#endif
}
OSStatus OpenSSLReadCerts ( SSL_CTX *ctx, StringPtr hostName ) {
OSStatus err = noErr;
SecKeychainItemRef item = NULL;
CFArrayRef searchList = NULL;
SecKeychainSearchRef search = NULL;
MachOWrapper<OSStatus (*)(const char *, SecKeychainRef *)>
SecKeychainOpen ( CFSTR ( "Security.framework" ), CFSTR ( "SecKeychainOpen" ));
MachOWrapper<OSStatus (*)(CFArrayRef *)>
SecKeychainCopySearchList ( CFSTR ( "Security.framework" ), CFSTR ( "SecKeychainCopySearchList" ));
MachOWrapper<OSStatus (*)(CFTypeRef, KCItemClass, const SecKeychainAttributeList *, SecKeychainSearchRef *)>
SecKeychainSearchCreateFromAttributes ( CFSTR ( "Security.framework" ), CFSTR ( "SecKeychainSearchCreateFromAttributes" ));
MachOWrapper<OSStatus (*)(SecKeychainSearchRef, SecKeychainItemRef *)>
SecKeychainSearchCopyNext ( CFSTR ( "Security.framework" ), CFSTR ( "SecKeychainSearchCopyNext" ));
// I've kind of butchered the prototype here; the second param to SecCertificateGetData is supposed to
// be a CSSM_DATA record, but that has the same structure as an SSLBuffer, and that's what I need here.
MachOWrapper<OSStatus (*)(SecKeychainItemRef, CSSM_DATA *)>
SecCertificateGetData ( CFSTR ( "Security.framework" ), CFSTR ( "SecCertificateGetData" ));
// DumpCertStore ( SSL_CTX_get_cert_store ( ctx ));
#if 0
// Make a list of keychains to search
err = SecKeychainCopySearchList ( &searchList );
if ( noErr == err ) {
SecKeychainRef anchors = NULL;
if ( noErr == SecKeychainOpen ( "/System/Library/Keychains/X509Anchors", &anchors )) {
CFMutableArrayRef mute = CFArrayCreateMutableCopy ( NULL, CFArrayGetCount ( searchList ) + 1, searchList );
CFArrayAppendValue ( mute, anchors );
CFRelease ( anchors );
CFRelease ( searchList );
searchList = mute;
}
}
if ( noErr == err )
err = SecKeychainSearchCreateFromAttributes ( searchList, kCDSACertClass, NULL, &search );
#else
SecKeychainRef anchors = NULL;
err = SecKeychainOpen ( "/System/Library/Keychains/X509Anchors", &anchors );
if ( noErr == err )
err = SecKeychainSearchCreateFromAttributes ( anchors, kCDSACertClass, NULL, &search );
#endif
if ( err == noErr ) {
do {
item = NULL;
err = SecKeychainSearchCopyNext ( search, &item );
// On a brand new keychain in 10.3, this routine returns a whacked out error code
// (0x80011109 to be precise) instead of 'errKCItemNotFound' - we ignore this.
if ( err != noErr )
err = errKCItemNotFound;
if ( err == noErr ) {
CSSM_DATA derCert;
// Get the data from the certificate item [ Certs are always stored as DER ]
err = SecCertificateGetData ( item, &derCert );
if ( err == noErr )
err = OpenSSLAddCertToStore ( ctx, &derCert, (const char *) hostName );
CFRelease ( item );
}
} while ( err == noErr );
CFRelease ( search );
}
if ( NULL != anchors ) CFRelease ( anchors );
if ( NULL != searchList ) CFRelease ( searchList );
// DumpCertStore ( SSL_CTX_get_cert_store ( ctx ));
return err;
}
Boolean IsCertInKeychain ( X509 *theCert ) {
Boolean retVal = false;
OSStatus err = noErr;
SecKeychainItemRef item = NULL;
SecKeychainSearchRef search = NULL;
MachOWrapper<OSStatus (*)(CFTypeRef, KCItemClass, const SecKeychainAttributeList *, SecKeychainSearchRef *)>
SecKeychainSearchCreateFromAttributes ( CFSTR ( "Security.framework" ), CFSTR ( "SecKeychainSearchCreateFromAttributes" ));
MachOWrapper<OSStatus (*)(SecKeychainSearchRef, SecKeychainItemRef *)>
SecKeychainSearchCopyNext ( CFSTR ( "Security.framework" ), CFSTR ( "SecKeychainSearchCopyNext" ));
MachOWrapper<OSStatus (*)(SecKeychainItemRef, CSSM_DATA *)>
SecCertificateGetData ( CFSTR ( "Security.framework" ), CFSTR ( "SecCertificateGetData" ));
err = SecKeychainSearchCreateFromAttributes ( NULL, kCDSACertClass, NULL, &search );
if ( err == noErr ) {
do {
item = NULL;
err = SecKeychainSearchCopyNext ( search, &item );
// On a brand new keychain in 10.3, this routine returns a whacked out error code
// (0x80011109 to be precise) instead of 'errKCItemNotFound' - we ignore this.
if ( err != noErr )
err = errKCItemNotFound;
if ( err == noErr ) {
CSSM_DATA derCert;
// Get the data from the certificate item [ Certs are always stored as DER ]
err = SecCertificateGetData ( item, &derCert );
if ( err == noErr ) {
X509 *X509Ptr = d2i_X509 ( NULL, &derCert.Data, derCert.Length );
if ( NULL != X509Ptr ) {
retVal = 0 == X509_cmp ( theCert, X509Ptr );
X509_free ( X509Ptr );
}
}
CFRelease ( item );
}
} while ( !retVal && err == noErr );
CFRelease ( search );
}
ASSERT ( retVal || errKCItemNotFound == err );
return retVal;
}
static Handle GetDialogItemHandle ( DialogPtr theDialog, short itemNo ) {
DialogItemType itemType;
Handle item;
Rect box;
GetDialogItem ( theDialog, itemNo, &itemType, &item, &box );
return item;
}
// All we want to do is to prevent people from typing chars into the edit-text item
static pascal Boolean XShowFilterProc ( DialogRef theDialog, EventRecord *theEvent, DialogItemIndex *itemHit ) {
// All non-key events are handled by the standard filter
if ( theEvent->what != keyDown && theEvent->what != autoKey )
return StdFilterProc ( theDialog, theEvent, itemHit );
// Command keys are fine, too
if ( theEvent->modifiers & cmdKey )
return StdFilterProc ( theDialog, theEvent, itemHit );
// OK, cancel
char key = (char) ( theEvent->message & charCodeMask );
switch ( key ) {
case kHomeCharCode:
case kEndCharCode:
case kPageUpCharCode:
case kPageDownCharCode:
case kLeftArrowCharCode:
case kRightArrowCharCode:
case kUpArrowCharCode:
case kDownArrowCharCode:
return StdFilterProc ( theDialog, theEvent, itemHit );
// For some reason, if I try to let the standard filter proc
// handle these three keys, then they go into the TextEdit field,
// instead of doing to the dialog
case kReturnCharCode:
case kEnterCharCode:
*itemHit = kStdOkItemIndex;
return true;
case kEscapeCharCode:
*itemHit = kStdCancelItemIndex;
return true;
default:
*itemHit = -1;
SysBeep ( 1 );
return true;
}
// Shouldn't ever get here
return false;
}
OSStatus ShowOpenSSLCertToUser ( X509 *theCert ) {
OSStatus err = noErr;
// Show the alert with the text
MyWindowPtr theWin = GetNewMyDialog ( SSL_CERT_DLOG_X, NULL, NULL, (WindowPtr) InFront );
if ( theWin == NULL )
return memFullErr;
DialogPtr theDialog = GetMyWindowDialogPtr ( theWin );
// Set the dialog prompt
Str255 aString;
GetRString ( aString, SSL_CERT_PROMPT );
SetDialogItemText ( GetDialogItemHandle ( theDialog, 4 ), aString );
// Set the certificate information
#if 1
Handle certText = GetOpenSSLCertText ( theCert );
TESetText ( *certText, GetHandleSize ( certText ), GetDialogTextEditHandle ( theDialog ));
DisposeHandle ( certText );
#endif
// Set the ok and cancel buttons
SetDialogDefaultItem ( theDialog, kStdOkItemIndex );
SetDialogCancelItem ( theDialog, kStdCancelItemIndex );
// Display the dialog
ModalFilterUPP filterProc = NewModalFilterUPP ( XShowFilterProc );
short dItem;
Boolean done = false;
StartMovableModal (theDialog );
ShowWindow ( GetDialogWindow ( theDialog ));
HiliteButtonOne ( theDialog );
while ( !done ) {
MovableModalDialog ( theDialog, filterProc, &dItem );
switch ( dItem ) {
case kStdOkItemIndex:
case kStdCancelItemIndex:
done = true;
break;
}
}
EndMovableModal ( theDialog );
MyDisposeDialog ( theDialog );
DisposeModalFilterUPP ( filterProc );
return dItem == kStdOkItemIndex ? noErr : userCanceledErr ;
}
// How many certs are there in the default keychain?
static int CountKCCerts ( SecKeychainRef theKC ) {
int retVal = 0;
OSErr err = noErr;
MachOWrapper<OSStatus (*)( CFTypeRef keychainOrArray, unsigned long itemClass, const SecKeychainAttributeList *attrList, SecKeychainSearchRef *searchRef )>
SecKeychainSearchCreateFromAttributes ( CFSTR ( "Security.framework" ), CFSTR ( "SecKeychainSearchCreateFromAttributes" ));
SecKeychainSearchRef searchRef;
if ( noErr == ( err = SecKeychainSearchCreateFromAttributes ( theKC, kCDSACertClass, NULL, &searchRef ))) {
MachOWrapper<OSStatus (*)( SecKeychainSearchRef searchRef, SecKeychainItemRef *itemRef )>
SecKeychainSearchCopyNext ( CFSTR ( "Security.framework" ), CFSTR ( "SecKeychainSearchCopyNext" ));
SecKeychainItemRef itemRef;
while ( noErr == ( err = SecKeychainSearchCopyNext ( searchRef, &itemRef ))) {
retVal++;
CFRelease ( itemRef );
}
CFRelease ( searchRef );
}
return retVal;
}
OSStatus AddOpenSSLCertToKeychain ( X509 *theCert ) {
OSStatus err = noErr;
SecCertificateRef certRef;
MachOWrapper<OSStatus (*)(const CSSM_DATA *, CSSM_CERT_TYPE, CSSM_CERT_ENCODING, SecCertificateRef *)>
SecCertificateCreateFromData ( CFSTR ( "Security.framework" ), CFSTR ( "SecCertificateCreateFromData" ));
// Create DER data from the X509 *
CSSM_DATA cert;
cert.Data = NULL;
cert.Length = i2d_X509_compat ( theCert, &cert.Data );
// Create the cert from the cert data
err = SecCertificateCreateFromData ( &cert, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &certRef );
if ( err == noErr ) {
MachOWrapper<OSStatus (*)( SecKeychainRef *)>
SecKeychainCopyDefault ( CFSTR ( "Security.framework" ), CFSTR ( "SecKeychainCopyDefault" ));
SecKeychainRef defKC;
if ( noErr == ( err = SecKeychainCopyDefault ( &defKC ))) {
MachOWrapper<OSStatus (*)(SecCertificateRef, SecKeychainRef )>
SecCertificateAddToKeychain ( CFSTR ( "Security.framework" ), CFSTR ( "SecCertificateAddToKeychain" ));
int oldCount = CountKCCerts ( defKC );
int newCount;
err = SecCertificateAddToKeychain ( certRef, NULL );
newCount = CountKCCerts ( defKC );
ASSERT ( noErr == err && newCount == oldCount + 1 );
CFRelease ( defKC );
}
CFRelease ( certRef );
}
OPENSSL_free ( cert.Data );
return err;
}
static OSStatus AppendHandleNL ( Handle h, char *text ) {
OSStatus err = noErr;
long oldSize = GetHandleSize ( h );
long len = strlen ( text );
text [ len - 1 ] = '\r'; // replace the '\n' that's already there.
SetHandleSize ( h, oldSize + len );
if ( noErr == ( err = MemError ()))
BlockMoveData ( text, *h + oldSize, len );
return err;
}
Handle GetOpenSSLCertText ( X509 *theCert ) {
BIO * bMem = BIO_new ( BIO_s_mem ());
X509_print ( bMem, theCert );
Handle retVal = NewHandle ( 0 );
char line [ 256 ];
while ( BIO_gets ( bMem, line, sizeof ( line ))) {
(void) AppendHandleNL ( retVal, line );
}
BIO_free ( bMem );
return retVal;
}