eudora-mac/sslCerts.cp

1 line
23 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. */
/*
File: sslCerts.cp
Purpose: Routines for getting certificates for SSL operations
*/
#define DEBUGASSERT 0
#define DEBUGERR 0
#define DEBUGMESSAGE 0
#define DEBUGDATA 0
#ifndef _SSLCERTS_H_
#include "sslCerts.h"
#endif
#include "EZCrypto.h"
#if TARGET_API_MAC_CARBON
#include <KeychainCore.h>
#include <KeychainHI.h>
#include "MachOWrapper.h"
#endif
#include "md5.h"
#pragma mark -- OS 9 routines ---
extern Boolean gKeychainChanged;
enum { kKCGetRootCertificateKeychain = 14 };
EXTERN_API( OSStatus )
KCDispatch (UInt16 commandID,
void * value);
static OSStatus OS9ReadCertsFromKC ( SSLContext *sslContext, AddCertProc addCerts, KCRef kc ) {
OSStatus err;
KCAttributeList attrList;
KCAttribute attr;
KCItemAttr itemClass;
KCSearchRef search;
KCItemRef item;
Handle certData;
long certBuffSize = 1;
SSLBuffer berCert;
// Allocate a buffer to hold the certs
// Why NuHTempBetter doesn't return a handle is beyond me!
if ( !( certData = (Handle) NuHTempBetter ( certBuffSize )))
return memFullErr;
itemClass = kCertificateKCItemClass;
attrList.count = 1;
attrList.attr = &attr;
attr.tag = kClassKCItemAttr;
attr.length = sizeof(itemClass);
attr.data = &itemClass;
/* Get the first item */
if (( err = KCFindFirstItem ( kc, &attrList, &search, &item )) == noErr ) {
Boolean lastItem = false;
/* The first time, it will be 1 */
berCert.length = certBuffSize;
do { // for each certificate
do { // until the buffer is big enough
/* Is the buffer too small? */
if ( certBuffSize < berCert.length ) {
/* Make it bigger */
certBuffSize = berCert.length;
SetHandleSize ( certData, certBuffSize );
if (( err = MemError ()) != noErr )
break;
}
/* Get the cert data */
HLock ( certData );
err = KCGetData ( item, certBuffSize, *certData, &berCert.length );
HUnlock ( certData );
} while(err == errKCBufferTooSmall);
/* Release the current cert item; we've still got the data in the handle */
KCReleaseItem ( &item );
// To get out of a doubly-nested loop, you have to break twice!
if ( err != noErr )
break;
/* Add the cert to the SSL context */
HLock ( certData );
berCert.data = (unsigned char *) *certData;
err = addCerts ( sslContext, berCert, 1 );
HUnlock ( certData );
} while (err >= noErr && ( err = KCFindNextItem ( search, &item )) == noErr );
KCReleaseSearch(&search);
}
DisposeHandle ( certData );
return (err == errKCItemNotFound) ? noErr : err;
}
static OSStatus OS9ReadCerts ( SSLContext *sslContext, AddCertProc addCerts ) {
KCRef kc;
OSStatus err = noErr;
gKeychainChanged = false;
#if (!TARGET_API_MAC_CARBON)
// Make sure we've got the call to get the root cert keychain
if ((long) KCDispatch == kUnresolvedCFragSymbolAddress )
return errKCWrongKCVersion;
// Open the root cert keychain
if (( err = KCDispatch ( kKCGetRootCertificateKeychain, &kc )) == noErr ) {
// Add all the certs in the root certificate keychain
err = OS9ReadCertsFromKC ( sslContext, addCerts, kc );
KCReleaseKeychain ( &kc );
}
#endif
if ( err == noErr ) {
// Add all the certs in the default keychain, too
if (( err = KCGetDefaultKeychain ( &kc )) == noErr ) {
err = OS9ReadCertsFromKC ( sslContext, addCerts, kc );
KCReleaseKeychain(&kc);
}
}
return err;
}
static Boolean NagHit ( EventRecord *event, DialogPtr dlog, short item, long refcon ) {
AliasHandle alias;
FSSpec kcSpec, spec;
Boolean wasChanged;
CloseMyWindow ( GetDialogWindow ( dlog ));
if( !FSResolveFID ( Root.vRef, refcon, &spec )) {
if ( item != kStdOkItemIndex )
FSpDelete ( &spec );
else { // launch the keychain with the file we just saved
if ( noErr == CreatorToApp ( 'kcmr', &alias )) {
if ( noErr == ResolveAlias ( nil, alias, &kcSpec, &wasChanged ))
OpenDocWith ( &spec, &kcSpec, false );
if ( alias != NULL )
DisposeHandle ((Handle) alias );
}
}
}
return true;
}
static OSStatus OS9AddCert ( SSLCertificateChain *certs ) {
OSStatus err = noErr;
FSSpec certSpec;
// Create a temp file, write the contents of the cert to it and let the user know about it
if ( !NewTempSpec ( Root.vRef, 0, nil, &certSpec )) {
long fid;
err = FSpCreate ( &certSpec, CREATOR, 'cert', smSystemScript );
if ( err == noErr ) {
short certRefNum;
err = AFSpOpenDF ( &certSpec, &certSpec, fsRdWrPerm, &certRefNum );
if ( err == noErr ) {
long count = certs->berCert.length;
AWrite ( certRefNum, &count, certs->berCert.data );
MyFSClose ( certRefNum );
FSMakeFID ( &certSpec, &fid );
Nag ( SSL_CERT_DLOG_9, nil, NagHit, nil, false, fid );
}
}
}
return err;
}
#pragma mark -- OS X routines --
#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;
#define CSSM_CERT_X_509v3 0x03
#define CSSM_CERT_ENCODING_DER 0x03
static OSStatus OSXReadCerts ( SSLContext *sslContext, AddCertProc addCerts ) {
OSStatus err = noErr;
SecKeychainSearchRef search;
SecKeychainItemRef item;
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, SSLBuffer *)>
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 ) {
SSLBuffer berCert;
// Get the data from the certificate item
err = SecCertificateGetData ( item, &berCert );
if ( err == noErr )
err = addCerts ( sslContext, berCert, 1 );
CFRelease ( item );
}
} while ( err == noErr );
CFRelease ( search );
}
return err == errKCItemNotFound ? noErr : err;
}
static OSStatus AppendChar ( Handle h, char c ) {
OSStatus err = noErr;
long oldSize = GetHandleSize ( h );
SetHandleSize ( h, oldSize + 1 );
if ( noErr == ( err = MemError ())) {
(*h) [ oldSize ] = c;
}
return err;
}
static OSStatus AppendToHandle ( Handle h, char *text, long sz ) {
OSStatus err = noErr;
long oldSize = GetHandleSize ( h );
SetHandleSize ( h, oldSize + sz );
if ( noErr == ( err = MemError ()))
BlockMoveData ( text, *h + oldSize, sz );
return err;
}
static OSStatus AppendToHandle ( Handle h, StringPtr text ) {
OSStatus err = noErr;
long oldSize = GetHandleSize ( h );
SetHandleSize ( h, oldSize + text [ 0 ] );
if ( noErr == ( err = MemError ()))
BlockMoveData ( text + 1, *h + oldSize, text [ 0 ] );
return err;
}
static OSStatus AppendToHandle ( Handle h, UInt32 strID ) {
OSStatus err = noErr;
Str255 aString;
GetRString ( aString, strID );
if ( aString [ 0 ] > 0 )
err = AppendToHandle ( h, aString );
return err;
}
static OSStatus AppendDateToHandle ( Handle h, UInt32 date ) {
OSStatus err = noErr;
// convert the date to a string
char *t = ctime ((time_t *) &date );
// cTime appends a '\n' onto the end of the string (which we don't want)
err = AppendToHandle ( h, t, strlen ( t ) - 1 );
return err;
}
static unsigned char NibbleToChar ( int value ) { return "\p0123456789ABCDEF" [ ( value & 0x0F ) + 1 ]; }
static OSStatus AppendHex ( Handle h, unsigned char *data, UInt32 len ) {
OSStatus err = noErr;
// convert the buffer to hex
Ptr p = NewPtr ( len * 3 );
if ( p == NULL )
return MemError ();
Ptr q = p;
for ( int i = 0; i < len; ++i ) {
*q++ = ' ';
*q++ = NibbleToChar ( data [ i ] >> 4 );
*q++ = NibbleToChar ( data [ i ] );
}
err = AppendToHandle ( h, p, q - p );
DisposePtr ( p );
return err;
}
static OSStatus AppendToHandle ( Handle h, const SSLBuffer &buf ) {
return AppendHex ( h, buf.data, buf.length );
}
static OSStatus AppendToHandle ( Handle h, const SSLAVA &ava ) {
OSStatus err = noErr;
if ( ava.avaData.data != NULL ) {
if ( ava.tag == TagPrintableString || ava.tag == TagIA5String )
err = AppendToHandle ( h, (char *) ava.avaData.data, ava.avaData.length );
// else // do something else!!
}
return err;
}
static Boolean FindDN ( SSLContext *sslContext, void *certData, unsigned long oid, SSLAVA &ava ) {
// Yeah, this is compute intensive, but WTF
unsigned long count;
SSLErr sslErr = SSLCountSubjectDNFields ( certData, &count );
if ( sslErr == SSLNoErr ) {
for ( unsigned long i = 0; sslErr == SSLNoErr && i < count; ++i ) {
if ( SSLNoErr != SSLExtractSubjectDNFieldIndex ( certData, i, &ava, sslContext ))
return false;
if ( ava.oid == oid )
return true;
}
}
return false;
}
static OSErr AppendHash ( Handle h, const SSLCertificateChain *certs, EZCryptoType hashAlg ) {
unsigned char digest [ 30 ]; // long enough
UInt32 len;
EZCryptoObj ez;
EZCreateObject ( &ez );
EZInitHash ( ez, hashAlg );
EZUpdateHash ( ez, certs->berCert.data, certs->berCert.length );
EZFinalHash ( ez, digest, sizeof ( digest ), &len );
EZDestroyObject ( &ez );
return AppendHex ( h, digest, len );
}
static Handle GetCertText ( SSLContext *sslContext, const SSLCertificateChain *certs ) {
Handle retVal = NewHandle ( 0 );
SSLBuffer buf;
UInt32 startDate, endDate;
SSLAVA ava;
// HostName:
AppendToHandle ( retVal, SSL_CERTDLG_HOSTNAMAE );
SSLExtractSubjectDNField ( certs->certData, OID_commonName, &ava, sslContext );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
// Expiration date:
AppendToHandle ( retVal, SSL_CERTDLG_EXPIRES );
SSLExtractValidityDates ( certs->certData, &startDate, &endDate );
AppendDateToHandle ( retVal, endDate );
AppendChar ( retVal, '\r' );
// Fingerprints:
AppendToHandle ( retVal, SSL_CERTDLG_FINGERPRINTS );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_SHA1 );
AppendHash ( retVal, certs, ezSHA1 );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_MD5 );
AppendHash ( retVal, certs, ezMD5 );
AppendChar ( retVal, '\r' );
// What kind of cert is it?
// AppendToHandle ( retVal, SSL_CERTDLG_TYPE );
AppendChar ( retVal, '\r' );
// Serial #
AppendToHandle ( retVal, SSL_CERTDLG_SERIAL );
SSLExtractSerialNumber ( certs->certData, &buf, sslContext );
AppendToHandle ( retVal, buf );
SSLFreeSerialNumber ( &buf, sslContext );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_BEFORE );
AppendDateToHandle ( retVal, startDate );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_AFTER );
AppendDateToHandle ( retVal, endDate );
AppendChar ( retVal, '\r' );
AppendChar ( retVal, '\r' );
// Who's that girl...
AppendToHandle ( retVal, SSL_CERTDLG_ISSUER );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_COUNTRY );
FindDN ( sslContext, certs->certData, OID_countryName, ava );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_STATE );
FindDN ( sslContext, certs->certData, OID_stateProvinceName, ava );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_LOCALITY );
FindDN ( sslContext, certs->certData, OID_localityName, ava );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_ORGANIZATION );
FindDN ( sslContext, certs->certData, OID_organizationName, ava );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_ORGUNIT );
FindDN ( sslContext, certs->certData, OID_organizationalUnitName, ava );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_CNAME );
FindDN ( sslContext, certs->certData, OID_commonName, ava );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_EMAIL );
FindDN ( sslContext, certs->certData, OID_emailAddress, ava );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
AppendChar ( retVal, '\r' );
// Running around with you...
AppendToHandle ( retVal, SSL_CERTDLG_SUBJECT );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_COUNTRY );
SSLExtractSubjectDNField ( certs->certData, OID_countryName, &ava, sslContext );
AppendToHandle ( retVal, ava );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_STATE );
SSLExtractSubjectDNField ( certs->certData, OID_stateProvinceName, &ava, sslContext );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_LOCALITY );
SSLExtractSubjectDNField ( certs->certData, OID_localityName, &ava, sslContext );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_ORGANIZATION );
SSLExtractSubjectDNField ( certs->certData, OID_organizationName, &ava, sslContext );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_ORGUNIT );
SSLExtractSubjectDNField ( certs->certData, OID_organizationalUnitName, &ava, sslContext );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_CNAME );
SSLExtractSubjectDNField ( certs->certData, OID_commonName, &ava, sslContext );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
AppendToHandle ( retVal, SSL_CERTDLG_EMAIL );
SSLExtractSubjectDNField ( certs->certData, OID_emailAddress, &ava, sslContext );
AppendToHandle ( retVal, ava );
SSLFreeAVA ( &ava, sslContext );
AppendChar ( retVal, '\r' );
AppendChar ( retVal, '\r' );
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;
}
static OSStatus XShowCertToUser ( SSLContext *sslContext, const SSLCertificateChain *certs ) {
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
Handle certText = GetCertText ( sslContext, certs );
TESetText ( *certText, GetHandleSize ( certText ), GetDialogTextEditHandle ( theDialog ));
DisposeHandle ( certText );
// 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;
}
static OSStatus OSXAddCert ( SSLContext *sslContext, const SSLCertificateChain *certs ) {
OSStatus err = noErr;
SecCertificateRef certRef;
// I've kind of butchered the prototype here; the first param to SecCertificateCreateFromData 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 (*)(const SSLBuffer *, CSSM_CERT_TYPE, CSSM_CERT_ENCODING, SecCertificateRef *)>
SecCertificateCreateFromData ( CFSTR ( "Security.framework" ), CFSTR ( "SecCertificateCreateFromData" ));
// Create the cert from the cert data
err = SecCertificateCreateFromData ( &certs->berCert, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &certRef );
if ( err == noErr ) {
// Ask the user if he wants to approve this cert
if ( noErr == ( err = XShowCertToUser ( sslContext, certs ))) {
MachOWrapper<OSStatus (*)( SecKeychainRef *)>
SecKeychainCopyDefault ( CFSTR ( "Security.framework" ), CFSTR ( "SecKeychainCopyDefault" ));
SecKeychainRef defKC;
if ( noErr == ( err = SecKeychainCopyDefault ( &defKC ))) {
int oldCount, newCount;
oldCount = CountKCCerts ( defKC );
MachOWrapper<OSStatus (*)(SecCertificateRef, SecKeychainRef )>
SecCertificateAddToKeychain ( CFSTR ( "Security.framework" ), CFSTR ( "SecCertificateAddToKeychain" ));
err = SecCertificateAddToKeychain ( certRef, NULL );
newCount = CountKCCerts ( defKC );
ASSERT ( err == noErr && newCount - oldCount == 1 );
CFRelease ( defKC );
}
}
CFRelease ( certRef );
}
return err;
}
#pragma mark -- routines defined in the header --
static Boolean HaveJaguar () {
return GetOSVersion () >= 0x1020;
}
Boolean CanDoSSL () {
return CanReadCertificates ();
}
// General interrogation - can we get certs from somewhere?
Boolean CanReadCertificates () {
// We're OK on MacOS 9
if ( !HaveOSX ()) return true;
// If we've got Jaguar, we're OK
if ( HaveJaguar ()) return true;
// Otherwise, we're screwed
return false;
}
// Get the certificates from somewhere
OSStatus ReadCertsFromStore ( SSLContext *sslContext, AddCertProc addCerts ) {
if ( !CanReadCertificates ())
return unimpErr;
OSStatus err;
if ( HaveOSX ())
err = OSXReadCerts ( sslContext, addCerts );
else
err = OS9ReadCerts ( sslContext, addCerts );
return err;
}
// Add a cert to the cert store
OSStatus AddCertToStore ( SSLContext *sslContext, SSLCertificateChain *certs ) {
if ( !CanReadCertificates ())
return unimpErr;
#ifndef VRELEASE
// Save the cert to a temp file for later examination
FSSpec certFile;
short refNum;
(void) FSMakeFSSpec ( Root.vRef, Root.dirId, "\pmtcTempCert", &certFile );
// Create the file - we don't care if it already exists
(void) FSpCreate ( &certFile, 'cs0m', 'DER ', smRoman );
// Open the file and write the cert to it
if ( noErr == FSpOpenDF ( &certFile, fsRdWrPerm, &refNum )) {
long len = certs->berCert.length;
(void) SetEOF ( refNum, len );
(void) FSWrite ( refNum, &len, certs->berCert.data );
FSClose ( refNum );
}
#endif
OSStatus err;
if ( HaveOSX ())
err = OSXAddCert ( sslContext, certs );
else
err = OS9AddCert ( certs );
return err;
}