/* * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please obtain * a copy of the License at http://www.apple.com/publicsource and read it before * using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the * specific language governing rights and limitations under the License. */ /* ********************************************************************* File: sslAlertMessage.cpp - SSL3 Alert protocol ****************************************************************** */ #include "ssl.h" #include "sslAlertMessage.h" #include "sslMemory.h" #include "sslContext.h" #include "sslSession.h" #include "sslDebug.h" #include "sslUtils.h" #include #ifdef NDEBUG #define SSLLogAlertMsg(msg,sent) #else static void SSLLogAlertMsg(AlertDescription msg, bool sent); #endif static OSStatus SSLEncodeAlert( SSLRecord &rec, AlertLevel level, AlertDescription desc, SSLContext *ctx); /* * If a peer sends us any kind of a bad cert alert, we may need to adjust * ctx->clientCertState accordingly. */ static void SSLDetectCertRejected( SSLContext *ctx, AlertDescription desc) { if(ctx->protocolSide == SSL_ServerSide) { return; } if(ctx->clientCertState != kSSLClientCertSent) { return; } switch(desc) { case SSL_AlertBadCert: case SSL_AlertUnsupportedCert: case SSL_AlertCertRevoked: case SSL_AlertCertExpired: case SSL_AlertCertUnknown: case SSL_AlertUnknownCA: ctx->clientCertState = kSSLClientCertRejected; break; default: break; } } OSStatus SSLProcessAlert(SSLRecord rec, SSLContext *ctx) { OSStatus err = noErr; AlertLevel level; AlertDescription desc; uint8 *charPtr; uint32 remaining; if (rec.contents.length % 2 != 0) { err = SSLFatalSessionAlert(SSL_AlertIllegalParam, ctx); if (!err) { err = errSSLProtocol; } return err; } charPtr = rec.contents.data; remaining = rec.contents.length; bool fatal = false; while (remaining > 0) { level = (AlertLevel)*charPtr++; desc = (AlertDescription)*charPtr++; sslHdskMsgDebug("alert msg recieved level %d desc %d", (int)level, (int)desc); remaining -= 2; SSLLogAlertMsg(desc, false); if (level == SSL_AlertLevelFatal) { /* explicit fatal errror */ fatal = true; sslHdskMsgDebug("***Fatal alert %d received\n", desc); } SSLDetectCertRejected(ctx, desc); switch (desc) { /* A number of these are fatal by implication */ case SSL_AlertUnexpectedMsg: err = errSSLPeerUnexpectedMsg; fatal = true; break; case SSL_AlertBadRecordMac: err = errSSLPeerBadRecordMac; fatal = true; break; case SSL_AlertDecryptionFail: err = errSSLPeerDecryptionFail; fatal = true; break; case SSL_AlertRecordOverflow: err = errSSLPeerRecordOverflow; fatal = true; break; case SSL_AlertDecompressFail: err = errSSLPeerDecompressFail; fatal = true; break; case SSL_AlertHandshakeFail: err = errSSLPeerHandshakeFail; fatal = true; break; case SSL_AlertIllegalParam: err = errSSLIllegalParam; fatal = true; break; case SSL_AlertBadCert: err = errSSLPeerBadCert; break; case SSL_AlertUnsupportedCert: err = errSSLPeerUnsupportedCert; break; case SSL_AlertCertRevoked: err = errSSLPeerCertRevoked; break; case SSL_AlertCertExpired: err = errSSLPeerCertExpired; break; case SSL_AlertCertUnknown: err = errSSLPeerCertUnknown; break; case SSL_AlertUnknownCA: err = errSSLPeerUnknownCA; break; case SSL_AlertAccessDenied: err = errSSLPeerAccessDenied; break; case SSL_AlertDecodeError: err = errSSLPeerDecodeError; break; case SSL_AlertDecryptError: err = errSSLPeerDecryptError; break; case SSL_AlertExportRestriction: err = errSSLPeerExportRestriction; break; case SSL_AlertProtocolVersion: err = errSSLPeerProtocolVersion; break; case SSL_AlertInsufficientSecurity: err = errSSLPeerInsufficientSecurity; break; case SSL_AlertInternalError: err = errSSLPeerInternalError; break; case SSL_AlertUserCancelled: err = errSSLPeerUserCancelled; break; case SSL_AlertNoRenegotiation: err = errSSLPeerNoRenegotiation; break; /* unusual cases.... */ case SSL_AlertCloseNotify: /* the clean "we're done" case */ SSLClose(ctx); err = noErr; break; case SSL_AlertNoCert: if((ctx->state == SSL_HdskStateClientCert) && (ctx->protocolSide == SSL_ServerSide) && (ctx->clientAuth != kAlwaysAuthenticate)) { /* * Tolerate this unless we're configured to * *require* a client cert. If a client cert is * required, we'll catch the error at the next * handshake msg we receive - which will probably * be a client key exchange msg, which is illegal * when we're in state SSL_HdskStateClientCert. * If the client cert is optional, advance to * state ClientKeyExchange by pretending we * just got a client cert msg. */ if ((err = SSLAdvanceHandshake(SSL_HdskCert, ctx)) != 0) { return err; } } break; default: /* Unknown alert, ignore if not fatal */ if(level == SSL_AlertLevelFatal) { err = errSSLFatalAlert; } else { err = noErr; } break; } if(fatal) { /* don't bother processing any more */ break; } } if(fatal) { SSLDeleteSessionData(ctx); } return err; } OSStatus SSLSendAlert(AlertLevel level, AlertDescription desc, SSLContext *ctx) { SSLRecord rec; OSStatus err; switch(ctx->negProtocolVersion) { case SSL_Version_Undetermined: /* Too early in negotiation to send an alert */ return noErr; case SSL_Version_2_0: /* shouldn't be here */ assert(0); return errSSLInternal; default: break; } if(ctx->sentFatalAlert) { /* no more alerts allowed */ return noErr; } if ((err = SSLEncodeAlert(rec, level, desc, ctx)) != 0) return err; assert(ctx->sslTslCalls != NULL); SSLLogAlertMsg(desc, true); if ((err = ctx->sslTslCalls->writeRecord(rec, ctx)) != 0) return err; if ((err = SSLFreeBuffer(rec.contents, ctx)) != 0) return err; if(desc == SSL_AlertCloseNotify) { /* no more alerts allowed */ ctx->sentFatalAlert = true; } return noErr; } static OSStatus SSLEncodeAlert(SSLRecord &rec, AlertLevel level, AlertDescription desc, SSLContext *ctx) { OSStatus err; rec.protocolVersion = ctx->negProtocolVersion; rec.contentType = SSL_RecordTypeAlert; rec.contents.length = 2; if ((err = SSLAllocBuffer(rec.contents, 2, ctx)) != 0) return err; rec.contents.data[0] = level; rec.contents.data[1] = desc; return noErr; } 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: case SSL_AlertRecordOverflow: case SSL_AlertAccessDenied: case SSL_AlertDecodeError: case SSL_AlertDecryptError: case SSL_AlertExportRestriction: 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; } #ifndef NDEBUG /* log alert messages */ static char *alertMsgToStr(AlertDescription msg) { static char badStr[100]; switch(msg) { case SSL_AlertCloseNotify: return "SSL_AlertCloseNotify"; case SSL_AlertUnexpectedMsg: return "SSL_AlertUnexpectedMsg"; case SSL_AlertBadRecordMac: return "SSL_AlertBadRecordMac"; case SSL_AlertDecryptionFail: return "SSL_AlertDecryptionFail"; case SSL_AlertRecordOverflow: return "SSL_AlertRecordOverflow"; case SSL_AlertDecompressFail: return "SSL_AlertDecompressFail"; case SSL_AlertHandshakeFail: return "SSL_AlertHandshakeFail"; case SSL_AlertNoCert: return "SSL_AlertNoCert"; case SSL_AlertBadCert: return "SSL_AlertBadCert"; case SSL_AlertUnsupportedCert: return "SSL_AlertUnsupportedCert"; case SSL_AlertCertRevoked: return "SSL_AlertCertRevoked"; case SSL_AlertCertExpired: return "SSL_AlertCertExpired"; case SSL_AlertCertUnknown: return "SSL_AlertCertUnknown"; case SSL_AlertIllegalParam: return "SSL_AlertIllegalParam"; case SSL_AlertUnknownCA: return "SSL_AlertUnknownCA"; case SSL_AlertAccessDenied: return "SSL_AlertAccessDenied"; case SSL_AlertDecodeError: return "SSL_AlertDecodeError"; case SSL_AlertDecryptError: return "SSL_AlertDecryptError"; case SSL_AlertExportRestriction: return "SSL_AlertExportRestriction"; case SSL_AlertProtocolVersion: return "SSL_AlertProtocolVersion"; case SSL_AlertInsufficientSecurity: return "SSL_AlertInsufficientSecurity"; case SSL_AlertInternalError: return "SSL_AlertInternalError"; case SSL_AlertUserCancelled: return "SSL_AlertUserCancelled"; case SSL_AlertNoRenegotiation: return "SSL_AlertNoRenegotiation"; default: sprintf(badStr, "Unknown state (%d(d)", msg); return badStr; } } static void SSLLogAlertMsg(AlertDescription msg, bool sent) { sslHdskMsgDebug("---%s alert msg %s", alertMsgToStr(msg), (sent ? "sent" : "recv")); } #endif /* NDEBUG */