#include "ssl.h"
#include "sslContext.h"
#include "sslSession.h"
#include "sslMemory.h"
#include "sslUtils.h"
#include "sslDebug.h"
#include "cipherSpecs.h"
#include "appleSession.h"
#include <assert.h>
#include <string.h>
#include <stddef.h>
#include <Security/SecCertificate.h>
#include <Security/SecCertificatePriv.h>
#include <Security/SecInternal.h>
typedef struct
{ size_t sessionIDLen;
UInt8 sessionID[32];
SSLProtocolVersion protocolVersion;
UInt16 cipherSuite;
UInt16 padding;
UInt8 masterSecret[48];
size_t certCount;
UInt8 certs[1];
} ResumableSession;
OSStatus
SSLAddSessionData(const SSLContext *ctx)
{ OSStatus err;
size_t sessionIDLen;
SSLBuffer sessionID;
ResumableSession *session;
size_t certCount;
#ifdef USE_SSLCERTIFICATE
SSLCertificate *cert;
#else
CFArrayRef certChain;
size_t ix;
#endif
uint8_t *certDest;
if (ctx->peerID.data == 0)
return errSSLSessionNotFound;
sessionIDLen = offsetof(ResumableSession, certs);
#ifdef USE_SSLCERTIFICATE
cert = ctx->peerCert;
certCount = 0;
while (cert)
{ ++certCount;
sessionIDLen += 4 + cert->derCert.length;
cert = cert->next;
}
#else
certChain = ctx->peerCert;
certCount = certChain ? CFArrayGetCount(certChain) : 0;
for (ix = 0; ix < certCount; ++ix) {
SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, ix);
#if SSL_DEBUG
sslDebugLog("SSLAddSessionData: got cert %d of %d\n", ix+1, certCount);
if (!cert || CFGetTypeID(cert) != SecCertificateGetTypeID()) {
sslErrorLog("SSLAddSessionData: non-cert in peerCert array!\n");
}
#endif
sessionIDLen += 4 + (size_t)SecCertificateGetLength(cert);
}
#endif
if ((err = SSLAllocBuffer(&sessionID, sessionIDLen, ctx)) != 0)
return err;
session = (ResumableSession*)sessionID.data;
session->sessionIDLen = ctx->sessionID.length;
memcpy(session->sessionID, ctx->sessionID.data, session->sessionIDLen);
session->protocolVersion = ctx->negProtocolVersion;
session->cipherSuite = ctx->selectedCipher;
memcpy(session->masterSecret, ctx->masterSecret, 48);
session->certCount = certCount;
session->padding = 0;
certDest = session->certs;
#ifdef USE_SSLCERTIFICATE
cert = ctx->peerCert;
while (cert)
{ certDest = SSLEncodeInt(certDest, cert->derCert.length, 4);
memcpy(certDest, cert->derCert.data, cert->derCert.length);
certDest += cert->derCert.length;
cert = cert->next;
}
#else
for (ix = 0; ix < certCount; ++ix) {
SecCertificateRef certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, ix);
size_t certLength = (size_t)SecCertificateGetLength(certRef);
const uint8_t *certBytes = SecCertificateGetBytePtr(certRef);
#if SSL_DEBUG
CFStringRef certName = NULL;
OSStatus status = SecCertificateInferLabel(certRef, &certName);
char buf[1024];
if (!certName || !CFStringGetCString(certName, buf, 1024-1, kCFStringEncodingUTF8)) { buf[0]=0; }
sslDebugLog("SSLAddSessionData: flattening \"%s\" (%ld bytes)\n", buf, certLength);
CFReleaseSafe(certName);
#endif
if (!certBytes || !certLength) {
sslErrorLog("SSLAddSessionData: invalid certificate at index %d of %d (length=%ld, data=%p)\n",
ix, certCount-1, certLength, (const uintptr_t)certBytes);
err = paramErr;
}
else {
certDest = SSLEncodeSize(certDest, certLength, 4);
memcpy(certDest, certBytes, certLength);
certDest += certLength;
}
}
#endif
if (!err) {
err = sslAddSession(ctx->peerID, sessionID, ctx->sessionCacheTimeout);
}
SSLFreeBuffer(&sessionID, ctx);
return err;
}
OSStatus
SSLGetSessionData(SSLBuffer *sessionData, const SSLContext *ctx)
{ OSStatus err;
if (ctx->peerID.data == 0)
return errSSLSessionNotFound;
sessionData->data = 0;
err = sslGetSession(ctx->peerID, sessionData);
if (sessionData->data == 0)
return errSSLSessionNotFound;
return err;
}
OSStatus
SSLDeleteSessionData(const SSLContext *ctx)
{ OSStatus err;
if (ctx->peerID.data == 0)
return errSSLSessionNotFound;
err = sslDeleteSession(ctx->peerID);
return err;
}
OSStatus
SSLRetrieveSessionID(
const SSLBuffer sessionData,
SSLBuffer *identifier,
const SSLContext *ctx)
{ OSStatus err;
ResumableSession *session;
session = (ResumableSession*) sessionData.data;
if ((err = SSLAllocBuffer(identifier, session->sessionIDLen, ctx)) != 0)
return err;
memcpy(identifier->data, session->sessionID, session->sessionIDLen);
return noErr;
}
OSStatus
SSLRetrieveSessionProtocolVersion(
const SSLBuffer sessionData,
SSLProtocolVersion *version,
const SSLContext *ctx)
{ ResumableSession *session;
session = (ResumableSession*) sessionData.data;
*version = session->protocolVersion;
return noErr;
}
#define ALLOW_CIPHERSPEC_CHANGE 1
OSStatus
SSLInstallSessionFromData(const SSLBuffer sessionData, SSLContext *ctx)
{ OSStatus err;
ResumableSession *session;
uint8_t *storedCertProgress;
#ifdef USE_SSLCERTIFICATE
SSLCertificate *cert;
SSLCertificate *lastCert = NULL;
#else
SecCertificateRef cert;
CFMutableArrayRef certChain = NULL;
#endif
size_t certCount;
size_t certLen;
session = (ResumableSession*)sessionData.data;
if(ctx->negProtocolVersion == SSL_Version_2_0) {
if(ctx->protocolSide == kSSLClientSide) {
assert(ctx->selectedCipher == 0);
ctx->selectedCipher = session->cipherSuite;
}
else {
if(ctx->selectedCipher != session->cipherSuite) {
sslErrorLog("+++SSL2: CipherSpec change from %d to %d on session "
"resume\n",
session->cipherSuite, ctx->selectedCipher);
return errSSLProtocol;
}
}
}
else {
assert(ctx->selectedCipher != 0);
if(ctx->selectedCipher != session->cipherSuite) {
#if ALLOW_CIPHERSPEC_CHANGE
sslErrorLog("+++WARNING: CipherSpec change from %d to %d "
"on session resume\n",
session->cipherSuite, ctx->selectedCipher);
#else
sslErrorLog("+++SSL: CipherSpec change from %d to %d on session resume\n",
session->cipherSuite, ctx->selectedCipher);
return errSSLProtocol;
#endif
}
}
if ((err = FindCipherSpec(ctx)) != 0) {
return err;
}
memcpy(ctx->masterSecret, session->masterSecret, 48);
storedCertProgress = session->certs;
certCount = session->certCount;
while (certCount--)
{
#ifdef USE_SSLCERTIFICATE
cert = (SSLCertificate *)sslMalloc(sizeof(SSLCertificate));
if(cert == NULL) {
return memFullErr;
}
cert->next = 0;
certLen = SSLDecodeInt(storedCertProgress, 4);
storedCertProgress += 4;
if ((err = SSLAllocBuffer(&cert->derCert, certLen, ctx)) != 0)
{
sslFree(cert);
return err;
}
memcpy(cert->derCert.data, storedCertProgress, certLen);
storedCertProgress += certLen;
if (lastCert == 0)
ctx->peerCert = cert;
else
lastCert->next = cert;
lastCert = cert;
#else
certLen = SSLDecodeInt(storedCertProgress, 4);
storedCertProgress += 4;
cert = SecCertificateCreateWithBytes(NULL, storedCertProgress, certLen);
#if SSL_DEBUG
sslDebugLog("SSLInstallSessionFromData: creating cert with bytes=%p len=%lu\n",
(uintptr_t)storedCertProgress, certLen);
if (!cert || CFGetTypeID(cert) != SecCertificateGetTypeID()) {
sslErrorLog("SSLInstallSessionFromData: SecCertificateCreateWithBytes failed\n");
}
#endif
if(cert == NULL) {
return memFullErr;
}
storedCertProgress += certLen;
if (!certChain) {
certChain = CFArrayCreateMutable(kCFAllocatorDefault,
session->certCount, &kCFTypeArrayCallBacks);
if (!certChain) {
CFRelease(cert);
return memFullErr;
}
ctx->peerCert = certChain;
}
CFArrayAppendValue(certChain, cert);
CFRelease(cert);
#endif
}
return noErr;
}