static void tls_handshake_ready_callback(tls_handshake_ctx_t ctx, bool write, bool ready) { SSLContext *myCtx = (SSLContext *)ctx; sslDebugLog("%p %s ready=%d\n", myCtx, write?"write":"read", ready); if(write) { myCtx->writeCipher_ready=ready?1:0; } else { myCtx->readCipher_ready=ready?1:0; if(ready) { SSLChangeHdskState(myCtx, SSL_HdskStateReady); } else { SSLChangeHdskState(myCtx, SSL_HdskStatePending); } } }
int tls_handshake_close(tls_handshake_t filter) { int err = errSSLSuccess; assert(filter); err = SSLSendAlert(SSL_AlertLevelWarning, SSL_AlertCloseNotify, filter); SSLChangeHdskState(filter, SSL_HdskStateGracefulClose); return err; }
OSStatus SSLFatalSessionAlert(AlertDescription desc, SSLContext *ctx) { OSStatus err1, err2; if(desc != SSL_AlertCloseNotify) { sslHdskMsgDebug("SSLFatalSessionAlert: desc %d\n", desc); } SSLChangeHdskState(ctx, SSL_HdskStateErrorClose); if(ctx->negProtocolVersion < TLS_Version_1_0) { /* translate to SSL3 if necessary */ switch(desc) { //case SSL_AlertDecryptionFail_RESERVED: case SSL_AlertRecordOverflow: case SSL_AlertAccessDenied: case SSL_AlertDecodeError: case SSL_AlertDecryptError: //case SSL_AlertExportRestriction_RESERVED: case SSL_AlertProtocolVersion: case SSL_AlertInsufficientSecurity: case SSL_AlertUserCancelled: case SSL_AlertNoRenegotiation: desc = SSL_AlertHandshakeFail; break; case SSL_AlertUnknownCA: desc = SSL_AlertUnsupportedCert; break; case SSL_AlertInternalError: desc = SSL_AlertCloseNotify; break; default: /* send as is */ break; } } /* Make session unresumable; I'm not stopping if I get an error, because I'd like to attempt to send the alert anyway */ err1 = SSLDeleteSessionData(ctx); /* Second, send the alert */ err2 = SSLSendAlert(SSL_AlertLevelFatal, desc, ctx); ctx->sentFatalAlert = true; /* If they both returned errors, arbitrarily return the first */ return err1 != 0 ? err1 : err2; }
static int SSLRecordReadInternal(SSLRecordContextRef ref, SSLRecord *rec) { int err; size_t len, contentLen; uint8_t *charPtr; SSLBuffer readData, cipherFragment; size_t head=5; int skipit=0; struct SSLRecordInternalContext *ctx = ref; if(ctx->isDTLS) head+=8; if (!ctx->partialReadBuffer.data || ctx->partialReadBuffer.length < head) { if (ctx->partialReadBuffer.data) if ((err = SSLFreeBuffer(&ctx->partialReadBuffer)) != 0) { return err; } if ((err = SSLAllocBuffer(&ctx->partialReadBuffer, DEFAULT_BUFFER_SIZE)) != 0) { return err; } } if (ctx->negProtocolVersion == SSL_Version_Undetermined) { if (ctx->amountRead < 1) { readData.length = 1 - ctx->amountRead; readData.data = ctx->partialReadBuffer.data + ctx->amountRead; len = readData.length; err = sslIoRead(readData, &len, ctx); if(err != 0) { if (err == errSSLRecordWouldBlock) { ctx->amountRead += len; return err; } else { /* abort */ err = errSSLRecordClosedAbort; #if 0 // TODO: revisit this in the transport layer if((ctx->protocolSide == kSSLClientSide) && (ctx->amountRead == 0) && (len == 0)) { /* * Detect "server refused to even try to negotiate" * error, when the server drops the connection before * sending a single byte. */ switch(ctx->state) { case SSL_HdskStateServerHello: sslHdskStateDebug("Server dropped initial connection\n"); err = errSSLConnectionRefused; break; default: break; } } #endif return err; } } ctx->amountRead += len; } } if (ctx->amountRead < head) { readData.length = head - ctx->amountRead; readData.data = ctx->partialReadBuffer.data + ctx->amountRead; len = readData.length; err = sslIoRead(readData, &len, ctx); if(err != 0) { switch(err) { case errSSLRecordWouldBlock: ctx->amountRead += len; break; #if SSL_ALLOW_UNNOTICED_DISCONNECT case errSSLClosedGraceful: /* legal if we're on record boundary and we've gotten past * the handshake */ if((ctx->amountRead == 0) && /* nothing pending */ (len == 0) && /* nothing new */ (ctx->state == SSL_HdskStateClientReady)) { /* handshake done */ /* * This means that the server has disconnected without * sending a closure alert notice. This is technically * illegal per the SSL3 spec, but about half of the * servers out there do it, so we report it as a separate * error which most clients - including (currently) * URLAccess - ignore by treating it the same as * a errSSLClosedGraceful error. Paranoid * clients can detect it and handle it however they * want to. */ SSLChangeHdskState(ctx, SSL_HdskStateNoNotifyClose); err = errSSLClosedNoNotify; break; } else { /* illegal disconnect */ err = errSSLClosedAbort; /* and drop thru to default: fatal alert */ } #endif /* SSL_ALLOW_UNNOTICED_DISCONNECT */ default: break; } return err; } ctx->amountRead += len; } check(ctx->amountRead >= head); charPtr = ctx->partialReadBuffer.data; rec->contentType = *charPtr++; if (rec->contentType < SSL_RecordTypeV3_Smallest || rec->contentType > SSL_RecordTypeV3_Largest) return errSSLRecordProtocol; rec->protocolVersion = (SSLProtocolVersion)SSLDecodeInt(charPtr, 2); charPtr += 2; if(rec->protocolVersion == DTLS_Version_1_0) { sslUint64 seqNum; SSLDecodeUInt64(charPtr, 8, &seqNum); charPtr += 8; sslLogRecordIo("Read DTLS Record %016llx (seq is: %016llx)", seqNum, ctx->readCipher.sequenceNum); /* if the epoch of the record is different of current read cipher, just drop it */ if((seqNum>>48)!=(ctx->readCipher.sequenceNum>>48)) { skipit=1; } else {
OSStatus SSLProcessChangeCipherSpec(SSLRecord rec, SSLContext *ctx) { OSStatus err; if (rec.contents.length != 1 || rec.contents.data[0] != 1) { if(ctx->isDTLS) return errSSLWouldBlock; SSLFatalSessionAlert(SSL_AlertUnexpectedMsg, ctx); sslErrorLog("***bad changeCipherSpec msg: length %d data 0x%x\n", (unsigned)rec.contents.length, (unsigned)rec.contents.data[0]); return errSSLProtocol; } /* * Handle PAC-style session resumption, client side only. * In that case, the handshake state was left in either KeyExchange or * Cert. */ if((ctx->protocolSide == kSSLClientSide) && (ctx->sessionTicket.length != 0) && ((ctx->state == SSL_HdskStateKeyExchange) || (ctx->state == SSL_HdskStateCert)) && (ctx->masterSecretCallback != NULL)) { size_t secretLen = SSL_MASTER_SECRET_SIZE; sslEapDebug("Client side resuming based on masterSecretCallback"); ctx->masterSecretCallback(ctx, ctx->masterSecretArg, ctx->masterSecret, &secretLen); ctx->sessionMatch = 1; /* set up selectedCipherSpec */ if ((err = FindCipherSpec(ctx)) != 0) { return err; } if((err = SSLInitPendingCiphers(ctx)) != 0) { SSLFatalSessionAlert(SSL_AlertInternalError, ctx); return err; } SSLChangeHdskState(ctx, SSL_HdskStateChangeCipherSpec); } if (!ctx->readPending_ready || ctx->state != SSL_HdskStateChangeCipherSpec) { if(ctx->isDTLS) return errSSLWouldBlock; SSLFatalSessionAlert(SSL_AlertUnexpectedMsg, ctx); sslErrorLog("***bad changeCipherSpec msg: readPending.ready %d state %d\n", (unsigned)ctx->readPending_ready, (unsigned)ctx->state); return errSSLProtocol; } sslLogNegotiateDebug("===Processing changeCipherSpec msg"); /* Install new cipher spec on read side */ if ((err = ctx->recFuncs->advanceReadCipher(ctx->recCtx)) != 0) { SSLFatalSessionAlert(SSL_AlertInternalError, ctx); return err; } ctx->readCipher_ready = 0; /* Can't send data until Finished is sent */ SSLChangeHdskState(ctx, SSL_HdskStateFinished); return errSecSuccess; }
tls_handshake_t tls_handshake_create(bool dtls, bool server) { tls_handshake_t ctx; ctx=(tls_handshake_t)malloc(sizeof(struct _tls_handshake_s)); if(ctx == NULL) { return NULL; } memset(ctx, 0, sizeof(struct _tls_handshake_s)); ctx->state = SSL_HdskStateUninit; ctx->retransmit_attempt = 0; ctx->clientCertState = kSSLClientCertNone; ctx->isDTLS = dtls; ctx->mtu = DEFAULT_DTLS_MTU; ctx->isServer = server; /* Default value so we can send and receive hello msgs */ ctx->sslTslCalls = &Ssl3Callouts; /* Initialize the cipher state to NULL_WITH_NULL_NULL */ ctx->selectedCipher = TLS_NULL_WITH_NULL_NULL; InitCipherSpecParams(ctx); /* * Default configuration */ tls_handshake_set_config(ctx, tls_handshake_config_default); /* * Enabled SCT and OCSP extension for client */ if(!ctx->isServer) { tls_handshake_set_sct_enable(ctx, true); tls_handshake_set_ocsp_enable(ctx, true); } tls_handshake_set_ems_enable(ctx, true); /* * Initial/default set of ECDH curves */ tls_handshake_set_curves(ctx, KnownCurves, CurvesCount); ctx->ecdhPeerCurve = tls_curve_none; /* until we negotiate one */ ctx->negAuthType = tls_client_auth_type_None; /* ditto */ /* * Initial/default set of SigAlgs */ tls_handshake_set_sigalgs(ctx, KnownSigAlgs, SigAlgsCount); if (ctx->isServer) { SSLChangeHdskState(ctx, SSL_HdskStateServerUninit); } else { SSLChangeHdskState(ctx, SSL_HdskStateClientUninit); } return ctx; }