]>
Commit | Line | Data |
---|---|---|
5a719ac8 A |
1 | /* |
2 | * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. | |
3 | * | |
4 | * The contents of this file constitute Original Code as defined in and are | |
5 | * subject to the Apple Public Source License Version 1.2 (the 'License'). | |
6 | * You may not use this file except in compliance with the License. Please obtain | |
7 | * a copy of the License at http://www.apple.com/publicsource and read it before | |
8 | * using this file. | |
9 | * | |
10 | * This Original Code and all software distributed under the License are | |
11 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS | |
12 | * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT | |
13 | * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | |
14 | * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the | |
15 | * specific language governing rights and limitations under the License. | |
16 | */ | |
17 | ||
18 | ||
19 | /* ********************************************************************* | |
20 | File: sslAlertMessage.cpp - SSL3 Alert protocol | |
21 | ****************************************************************** */ | |
22 | ||
23 | #include "ssl.h" | |
24 | #include "sslAlertMessage.h" | |
25 | #include "sslMemory.h" | |
26 | #include "sslContext.h" | |
27 | #include "sslSession.h" | |
28 | #include "sslDebug.h" | |
df0e469f | 29 | #include "sslUtils.h" |
5a719ac8 A |
30 | |
31 | #include <assert.h> | |
32 | ||
33 | #ifdef NDEBUG | |
34 | #define SSLLogAlertMsg(msg,sent) | |
35 | #else | |
36 | static void SSLLogAlertMsg(AlertDescription msg, bool sent); | |
37 | #endif | |
38 | ||
df0e469f A |
39 | static OSStatus SSLEncodeAlert( |
40 | SSLRecord &rec, | |
41 | AlertLevel level, | |
42 | AlertDescription desc, | |
43 | SSLContext *ctx); | |
44 | ||
45 | /* | |
46 | * If a peer sends us any kind of a bad cert alert, we may need to adjust | |
47 | * ctx->clientCertState accordingly. | |
48 | */ | |
49 | static void | |
50 | SSLDetectCertRejected( | |
51 | SSLContext *ctx, | |
52 | AlertDescription desc) | |
53 | { | |
54 | if(ctx->protocolSide == SSL_ServerSide) { | |
55 | return; | |
56 | } | |
57 | if(ctx->clientCertState != kSSLClientCertSent) { | |
58 | return; | |
59 | } | |
60 | switch(desc) { | |
61 | case SSL_AlertBadCert: | |
62 | case SSL_AlertUnsupportedCert: | |
63 | case SSL_AlertCertRevoked: | |
64 | case SSL_AlertCertExpired: | |
65 | case SSL_AlertCertUnknown: | |
66 | case SSL_AlertUnknownCA: | |
67 | ctx->clientCertState = kSSLClientCertRejected; | |
68 | break; | |
69 | default: | |
70 | break; | |
71 | } | |
72 | } | |
73 | ||
5a719ac8 A |
74 | OSStatus |
75 | SSLProcessAlert(SSLRecord rec, SSLContext *ctx) | |
76 | { OSStatus err = noErr; | |
77 | AlertLevel level; | |
78 | AlertDescription desc; | |
79 | uint8 *charPtr; | |
80 | uint32 remaining; | |
81 | ||
82 | if (rec.contents.length % 2 != 0) | |
83 | { | |
84 | err = SSLFatalSessionAlert(SSL_AlertIllegalParam, ctx); | |
85 | if (!err) { | |
86 | err = errSSLProtocol; | |
87 | } | |
88 | return err; | |
89 | } | |
90 | ||
91 | charPtr = rec.contents.data; | |
92 | remaining = rec.contents.length; | |
df0e469f A |
93 | bool fatal = false; |
94 | ||
5a719ac8 A |
95 | while (remaining > 0) |
96 | { level = (AlertLevel)*charPtr++; | |
97 | desc = (AlertDescription)*charPtr++; | |
df0e469f | 98 | sslHdskMsgDebug("alert msg recieved level %d desc %d", |
5a719ac8 A |
99 | (int)level, (int)desc); |
100 | remaining -= 2; | |
101 | SSLLogAlertMsg(desc, false); | |
102 | ||
df0e469f A |
103 | if (level == SSL_AlertLevelFatal) { |
104 | /* explicit fatal errror */ | |
105 | fatal = true; | |
106 | sslHdskMsgDebug("***Fatal alert %d received\n", desc); | |
5a719ac8 | 107 | } |
df0e469f A |
108 | SSLDetectCertRejected(ctx, desc); |
109 | ||
110 | switch (desc) { | |
111 | /* A number of these are fatal by implication */ | |
112 | case SSL_AlertUnexpectedMsg: | |
113 | err = errSSLPeerUnexpectedMsg; | |
114 | fatal = true; | |
115 | break; | |
5a719ac8 | 116 | case SSL_AlertBadRecordMac: |
df0e469f A |
117 | err = errSSLPeerBadRecordMac; |
118 | fatal = true; | |
119 | break; | |
120 | case SSL_AlertDecryptionFail: | |
121 | err = errSSLPeerDecryptionFail; | |
122 | fatal = true; | |
123 | break; | |
124 | case SSL_AlertRecordOverflow: | |
125 | err = errSSLPeerRecordOverflow; | |
126 | fatal = true; | |
127 | break; | |
5a719ac8 | 128 | case SSL_AlertDecompressFail: |
df0e469f A |
129 | err = errSSLPeerDecompressFail; |
130 | fatal = true; | |
131 | break; | |
5a719ac8 | 132 | case SSL_AlertHandshakeFail: |
df0e469f A |
133 | err = errSSLPeerHandshakeFail; |
134 | fatal = true; | |
135 | break; | |
5a719ac8 | 136 | case SSL_AlertIllegalParam: |
df0e469f A |
137 | err = errSSLIllegalParam; |
138 | fatal = true; | |
139 | break; | |
140 | case SSL_AlertBadCert: | |
141 | err = errSSLPeerBadCert; | |
142 | break; | |
143 | case SSL_AlertUnsupportedCert: | |
144 | err = errSSLPeerUnsupportedCert; | |
145 | break; | |
146 | case SSL_AlertCertRevoked: | |
147 | err = errSSLPeerCertRevoked; | |
148 | break; | |
149 | case SSL_AlertCertExpired: | |
150 | err = errSSLPeerCertExpired; | |
151 | break; | |
152 | case SSL_AlertCertUnknown: | |
153 | err = errSSLPeerCertUnknown; | |
154 | break; | |
155 | case SSL_AlertUnknownCA: | |
156 | err = errSSLPeerUnknownCA; | |
157 | break; | |
158 | case SSL_AlertAccessDenied: | |
159 | err = errSSLPeerAccessDenied; | |
160 | break; | |
161 | case SSL_AlertDecodeError: | |
162 | err = errSSLPeerDecodeError; | |
163 | break; | |
164 | case SSL_AlertDecryptError: | |
165 | err = errSSLPeerDecryptError; | |
166 | break; | |
167 | case SSL_AlertExportRestriction: | |
168 | err = errSSLPeerExportRestriction; | |
169 | break; | |
170 | case SSL_AlertProtocolVersion: | |
171 | err = errSSLPeerProtocolVersion; | |
172 | break; | |
173 | case SSL_AlertInsufficientSecurity: | |
174 | err = errSSLPeerInsufficientSecurity; | |
175 | break; | |
176 | case SSL_AlertInternalError: | |
177 | err = errSSLPeerInternalError; | |
178 | break; | |
179 | case SSL_AlertUserCancelled: | |
180 | err = errSSLPeerUserCancelled; | |
5a719ac8 | 181 | break; |
df0e469f A |
182 | case SSL_AlertNoRenegotiation: |
183 | err = errSSLPeerNoRenegotiation; | |
184 | break; | |
185 | /* unusual cases.... */ | |
5a719ac8 | 186 | case SSL_AlertCloseNotify: |
df0e469f | 187 | /* the clean "we're done" case */ |
5a719ac8 A |
188 | SSLClose(ctx); |
189 | err = noErr; | |
190 | break; | |
191 | case SSL_AlertNoCert: | |
192 | if((ctx->state == SSL_HdskStateClientCert) && | |
193 | (ctx->protocolSide == SSL_ServerSide) && | |
194 | (ctx->clientAuth != kAlwaysAuthenticate)) { | |
195 | /* | |
196 | * Tolerate this unless we're configured to | |
197 | * *require* a client cert. If a client cert is | |
198 | * required, we'll catch the error at the next | |
199 | * handshake msg we receive - which will probably | |
200 | * be a client key exchange msg, which is illegal | |
201 | * when we're in state SSL_HdskStateClientCert. | |
202 | * If the client cert is optional, advance to | |
203 | * state ClientKeyExchange by pretending we | |
204 | * just got a client cert msg. | |
205 | */ | |
206 | if ((err = SSLAdvanceHandshake(SSL_HdskCert, | |
207 | ctx)) != 0) { | |
208 | return err; | |
209 | } | |
210 | } | |
211 | break; | |
5a719ac8 | 212 | default: |
df0e469f A |
213 | /* Unknown alert, ignore if not fatal */ |
214 | if(level == SSL_AlertLevelFatal) { | |
215 | err = errSSLFatalAlert; | |
216 | } | |
217 | else { | |
218 | err = noErr; | |
219 | } | |
5a719ac8 A |
220 | break; |
221 | } | |
df0e469f A |
222 | if(fatal) { |
223 | /* don't bother processing any more */ | |
224 | break; | |
225 | } | |
5a719ac8 | 226 | } |
df0e469f A |
227 | if(fatal) { |
228 | SSLDeleteSessionData(ctx); | |
229 | } | |
5a719ac8 A |
230 | return err; |
231 | } | |
232 | ||
233 | OSStatus | |
234 | SSLSendAlert(AlertLevel level, AlertDescription desc, SSLContext *ctx) | |
235 | { SSLRecord rec; | |
236 | OSStatus err; | |
237 | ||
df0e469f A |
238 | switch(ctx->negProtocolVersion) { |
239 | case SSL_Version_Undetermined: | |
240 | /* Too early in negotiation to send an alert */ | |
241 | return noErr; | |
242 | case SSL_Version_2_0: | |
243 | /* shouldn't be here */ | |
244 | assert(0); | |
245 | return errSSLInternal; | |
246 | default: | |
247 | break; | |
248 | } | |
249 | if(ctx->sentFatalAlert) { | |
250 | /* no more alerts allowed */ | |
251 | return noErr; | |
252 | } | |
5a719ac8 A |
253 | if ((err = SSLEncodeAlert(rec, level, desc, ctx)) != 0) |
254 | return err; | |
255 | assert(ctx->sslTslCalls != NULL); | |
256 | SSLLogAlertMsg(desc, true); | |
257 | if ((err = ctx->sslTslCalls->writeRecord(rec, ctx)) != 0) | |
258 | return err; | |
259 | if ((err = SSLFreeBuffer(rec.contents, ctx)) != 0) | |
260 | return err; | |
df0e469f A |
261 | if(desc == SSL_AlertCloseNotify) { |
262 | /* no more alerts allowed */ | |
263 | ctx->sentFatalAlert = true; | |
264 | } | |
5a719ac8 A |
265 | return noErr; |
266 | } | |
267 | ||
df0e469f | 268 | static OSStatus |
5a719ac8 A |
269 | SSLEncodeAlert(SSLRecord &rec, AlertLevel level, AlertDescription desc, SSLContext *ctx) |
270 | { OSStatus err; | |
271 | ||
df0e469f A |
272 | rec.protocolVersion = ctx->negProtocolVersion; |
273 | rec.contentType = SSL_RecordTypeAlert; | |
5a719ac8 A |
274 | rec.contents.length = 2; |
275 | if ((err = SSLAllocBuffer(rec.contents, 2, ctx)) != 0) | |
276 | return err; | |
277 | rec.contents.data[0] = level; | |
278 | rec.contents.data[1] = desc; | |
279 | ||
280 | return noErr; | |
281 | } | |
282 | ||
283 | OSStatus | |
284 | SSLFatalSessionAlert(AlertDescription desc, SSLContext *ctx) | |
285 | { OSStatus err1, err2; | |
286 | ||
287 | if(desc != SSL_AlertCloseNotify) { | |
df0e469f | 288 | sslHdskMsgDebug("SSLFatalSessionAlert: desc %d\n", desc); |
5a719ac8 A |
289 | } |
290 | SSLChangeHdskState(ctx, SSL_HdskStateErrorClose); | |
291 | ||
df0e469f A |
292 | if(ctx->negProtocolVersion < TLS_Version_1_0) { |
293 | /* translate to SSL3 if necessary */ | |
294 | switch(desc) { | |
295 | case SSL_AlertDecryptionFail: | |
296 | case SSL_AlertRecordOverflow: | |
297 | case SSL_AlertAccessDenied: | |
298 | case SSL_AlertDecodeError: | |
299 | case SSL_AlertDecryptError: | |
300 | case SSL_AlertExportRestriction: | |
301 | case SSL_AlertProtocolVersion: | |
302 | case SSL_AlertInsufficientSecurity: | |
303 | case SSL_AlertUserCancelled: | |
304 | case SSL_AlertNoRenegotiation: | |
305 | desc = SSL_AlertHandshakeFail; | |
306 | break; | |
307 | case SSL_AlertUnknownCA: | |
308 | desc = SSL_AlertUnsupportedCert; | |
309 | break; | |
310 | case SSL_AlertInternalError: | |
311 | desc = SSL_AlertCloseNotify; | |
312 | break; | |
313 | default: | |
314 | /* send as is */ | |
315 | break; | |
316 | } | |
317 | } | |
5a719ac8 A |
318 | /* Make session unresumable; I'm not stopping if I get an error, |
319 | because I'd like to attempt to send the alert anyway */ | |
320 | err1 = SSLDeleteSessionData(ctx); | |
321 | ||
322 | /* Second, send the alert */ | |
323 | err2 = SSLSendAlert(SSL_AlertLevelFatal, desc, ctx); | |
324 | ||
df0e469f A |
325 | ctx->sentFatalAlert = true; |
326 | ||
5a719ac8 A |
327 | /* If they both returned errors, arbitrarily return the first */ |
328 | return err1 != 0 ? err1 : err2; | |
329 | } | |
330 | ||
331 | #ifndef NDEBUG | |
332 | ||
333 | /* log alert messages */ | |
334 | ||
335 | static char *alertMsgToStr(AlertDescription msg) | |
336 | { | |
337 | static char badStr[100]; | |
338 | ||
339 | switch(msg) { | |
340 | case SSL_AlertCloseNotify: | |
341 | return "SSL_AlertCloseNotify"; | |
342 | case SSL_AlertUnexpectedMsg: | |
343 | return "SSL_AlertUnexpectedMsg"; | |
344 | case SSL_AlertBadRecordMac: | |
345 | return "SSL_AlertBadRecordMac"; | |
346 | case SSL_AlertDecryptionFail: | |
347 | return "SSL_AlertDecryptionFail"; | |
348 | case SSL_AlertRecordOverflow: | |
349 | return "SSL_AlertRecordOverflow"; | |
350 | case SSL_AlertDecompressFail: | |
351 | return "SSL_AlertDecompressFail"; | |
352 | case SSL_AlertHandshakeFail: | |
353 | return "SSL_AlertHandshakeFail"; | |
354 | case SSL_AlertNoCert: | |
355 | return "SSL_AlertNoCert"; | |
356 | case SSL_AlertBadCert: | |
357 | return "SSL_AlertBadCert"; | |
358 | case SSL_AlertUnsupportedCert: | |
359 | return "SSL_AlertUnsupportedCert"; | |
360 | case SSL_AlertCertRevoked: | |
361 | return "SSL_AlertCertRevoked"; | |
362 | ||
363 | case SSL_AlertCertExpired: | |
364 | return "SSL_AlertCertExpired"; | |
365 | case SSL_AlertCertUnknown: | |
366 | return "SSL_AlertCertUnknown"; | |
367 | case SSL_AlertIllegalParam: | |
368 | return "SSL_AlertIllegalParam"; | |
369 | case SSL_AlertUnknownCA: | |
370 | return "SSL_AlertUnknownCA"; | |
371 | case SSL_AlertAccessDenied: | |
372 | return "SSL_AlertAccessDenied"; | |
373 | case SSL_AlertDecodeError: | |
374 | return "SSL_AlertDecodeError"; | |
375 | case SSL_AlertDecryptError: | |
376 | return "SSL_AlertDecryptError"; | |
377 | ||
378 | case SSL_AlertExportRestriction: | |
379 | return "SSL_AlertExportRestriction"; | |
380 | case SSL_AlertProtocolVersion: | |
381 | return "SSL_AlertProtocolVersion"; | |
382 | case SSL_AlertInsufficientSecurity: | |
383 | return "SSL_AlertInsufficientSecurity"; | |
384 | case SSL_AlertInternalError: | |
385 | return "SSL_AlertInternalError"; | |
386 | case SSL_AlertUserCancelled: | |
387 | return "SSL_AlertUserCancelled"; | |
388 | case SSL_AlertNoRenegotiation: | |
389 | return "SSL_AlertNoRenegotiation"; | |
390 | ||
391 | default: | |
392 | sprintf(badStr, "Unknown state (%d(d)", msg); | |
393 | return badStr; | |
394 | } | |
395 | } | |
396 | ||
397 | static void SSLLogAlertMsg(AlertDescription msg, bool sent) | |
398 | { | |
399 | sslHdskMsgDebug("---%s alert msg %s", | |
400 | alertMsgToStr(msg), (sent ? "sent" : "recv")); | |
401 | } | |
402 | ||
403 | #endif /* NDEBUG */ |