X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_ssl/lib/sslAlertMessage.c diff --git a/libsecurity_ssl/lib/sslAlertMessage.c b/libsecurity_ssl/lib/sslAlertMessage.c new file mode 100644 index 00000000..f2b4689c --- /dev/null +++ b/libsecurity_ssl/lib/sslAlertMessage.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2000-2001,2005-2007,2010-2012 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * sslAlertMessage.c - 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 == kSSLServerSide) { + 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_t *charPtr; + size_t 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 received 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_RESERVED: + 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_RESERVED: + 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_RESERVED: + if((ctx->state == SSL_HdskStateClientCert) && + (ctx->protocolSide == kSSLServerSide) && + (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; + case SSL_AlertUnsupportedExtension: + err = errSSLFatalAlert; + fatal = true; + 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_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; +} + +#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_RESERVED: + return "SSL_AlertDecryptionFail"; + case SSL_AlertRecordOverflow: + return "SSL_AlertRecordOverflow"; + case SSL_AlertDecompressFail: + return "SSL_AlertDecompressFail"; + case SSL_AlertHandshakeFail: + return "SSL_AlertHandshakeFail"; + case SSL_AlertNoCert_RESERVED: + 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_RESERVED: + 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 */