|
1 // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // SSL3.0 and TLS1.0 Alert protocol source file. |
|
15 // Describes the implementation of the Alert protocol (send and receive) |
|
16 // events classes |
|
17 // |
|
18 // |
|
19 |
|
20 /** |
|
21 @file AlertProtocolEvents.cpp |
|
22 */ |
|
23 |
|
24 #include "AlertProtocolEvents.h" |
|
25 #include "tlsrecorditem.h" |
|
26 #include "recordprotocolevents.h" |
|
27 #include "tlshandshake.h" |
|
28 #include "applicationdata.h" |
|
29 #include <sslerr.h> |
|
30 |
|
31 // @todo There must be a means of notifying the TLS Provider when a fatal |
|
32 // alert happens (as this invalidates the session; i.e., no new connections |
|
33 // can subsequently be made with that session id). Can ClearSessionCache be |
|
34 // used but identifying only one session (as opposed to all a client's |
|
35 // sessions)? |
|
36 |
|
37 //TLS specific error <-> alert code mapping |
|
38 struct TTLSErrorAlertCodeMap |
|
39 { |
|
40 TInt iTLSErrorCode; |
|
41 TInt iAlertCode; |
|
42 }; |
|
43 |
|
44 const TUint KWarningAlertsCount = 2; |
|
45 |
|
46 enum ETLSAlertCode { |
|
47 EAlertclose_notify = 0, |
|
48 |
|
49 EAlertunexpected_message = 10, |
|
50 EAlertbad_record_mac = 20, |
|
51 EAlertdecryption_failed = 21, |
|
52 EAlertrecord_overflow = 22, |
|
53 EAlertdecompression_failure = 30, |
|
54 EAlerthandshake_failure = 40, |
|
55 EAlertbad_certificate = 42, |
|
56 EAlertunsupported_certificate = 43, |
|
57 EAlertcertificate_revoked = 44, |
|
58 EAlertcertificate_expired = 45, |
|
59 EAlertcertificate_unknown = 46, |
|
60 EAlertillegal_parameter = 47, |
|
61 EAlertunknown_ca = 48, |
|
62 EAlertaccess_denied = 49, |
|
63 EAlertdecode_error = 50, |
|
64 EAlertdecrypt_error = 51, |
|
65 EAlertexport_restriction = 60, |
|
66 EAlertprotocol_version = 70, |
|
67 EAlertinsufficient_security = 71, |
|
68 EAlertinternal_error = 80, |
|
69 EAlertuser_canceled = 90, |
|
70 EAlertno_renegotiation = 100 |
|
71 }; |
|
72 |
|
73 const TTLSErrorAlertCodeMap glbTLSErrorAlertCodeMap[] = |
|
74 {//the first two alerts are warnings the rest is fatal |
|
75 {KErrSSLAlertCloseNotify, 0}, //close_notify(0), |
|
76 {KErrSSLAlertNoRenegotiation,100}, //no_renegotiation(100), |
|
77 |
|
78 {KErrSSLAlertUnexpectedMessage,10}, //unexpected_message(10), |
|
79 {KErrSSLAlertBadRecordMac,20}, //bad_record_mac(20), |
|
80 {KErrSSLAlertDecryptionFailed,21}, //decryption_failed(21), |
|
81 {KErrSSLAlertRecordOverflow,22}, //record_overflow(22), |
|
82 {KErrSSLAlertDecompressionFailure,30}, //decompression_failure(30), |
|
83 {KErrSSLAlertHandshakeFailure,40}, //handshake_failure(40), |
|
84 {KErrSSLAlertBadCertificate,42}, //bad_certificate(42), |
|
85 {KErrSSLAlertUnsupportedCertificate,43},//unsupported_certificate(43), |
|
86 {KErrSSLAlertCertificateRevoked,44}, //certificate_revoked(44), |
|
87 {KErrSSLAlertCertificateExpired,45}, //certificate_expired(45), |
|
88 {KErrSSLAlertCertificateUnknown,46}, //certificate_unknown(46), |
|
89 {KErrSSLAlertIllegalParameter,47}, //illegal_parameter(47), |
|
90 {KErrSSLAlertUnknownCA,48}, //unknown_ca(48), |
|
91 {KErrSSLAlertAccessDenied,49}, //access_denied(49), |
|
92 {KErrSSLAlertDecodeError,50}, //decode_error(50), |
|
93 {KErrSSLAlertDecryptError,51}, //decrypt_error(51), |
|
94 {KErrSSLAlertExportRestriction,60}, //export_restriction(60), |
|
95 {KErrSSLAlertProtocolVersion,70}, //protocol_version(70), |
|
96 {KErrSSLAlertInsufficientSecurity,71}, //insufficient_security(71), |
|
97 {KErrSSLAlertInternalError,80}, //internal_error(80), |
|
98 {KErrSSLAlertUserCanceled,90} //user_canceled(90), |
|
99 }; |
|
100 //the error which apparently don't map to any alert |
|
101 // KErrSSLAlertAccessDenied: |
|
102 // KErrSSLAlertDecodeError: |
|
103 // KErrSSLAlertDecryptError: |
|
104 |
|
105 CAsynchEvent* CSendAlert::ProcessL( TRequestStatus& aStatus ) |
|
106 { |
|
107 // @todo The processing code can be put into a separate function (code reuse/reduce bloat). |
|
108 // @todo Ensure that alerts are set up correctly (i.e., the code that should cause an |
|
109 // alert to be sent is set up correctly. |
|
110 |
|
111 TInt error = iStateMachine->LastError(); |
|
112 LOG(Log::Printf(_L("CSendAlert::ProcessL(). Error value = %d"), error )); |
|
113 iAlertMsg.SetLength( 0 ); |
|
114 |
|
115 switch ( error ) |
|
116 { |
|
117 case KErrEof: |
|
118 { // CTlsConnection::Recv() completion status when there is no more data to |
|
119 // receive. This is not a SSL/TLS error. |
|
120 TRequestStatus* p=&aStatus; |
|
121 User::RequestComplete( p, KErrNone ); |
|
122 |
|
123 return NULL; //stop the state machine |
|
124 } |
|
125 case KErrSSLAlertCloseNotify: |
|
126 { |
|
127 //Upon sending the close_notify from server report KErrEof to the application |
|
128 //to be intact with existing behaviour. |
|
129 iStateMachine->SetLastError( KErrEof ); |
|
130 iAlertMsg.Append( EAlertWarning ); |
|
131 iAlertMsg.Append( EAlertclose_notify ); |
|
132 iRecordComposer.SetNext( this ); |
|
133 break; |
|
134 } |
|
135 case KErrDisconnected: |
|
136 { |
|
137 /* Fix for the TLS Client hang issue DEF130128. |
|
138 * Server might have disconnected the TLS connection. |
|
139 * Terminate the state machine */ |
|
140 TRequestStatus* p=&aStatus; |
|
141 User::RequestComplete(p, KErrDisconnected); |
|
142 |
|
143 return NULL; //stop the state machine |
|
144 } |
|
145 case KErrArgument: |
|
146 { |
|
147 iStateMachine->SetLastError( KErrSSLAlertIllegalParameter ); |
|
148 iAlertMsg.Append( EAlertFatal ); |
|
149 iAlertMsg.Append( EAlertillegal_parameter ); |
|
150 iRecordComposer.SetNext( NULL ); |
|
151 break; |
|
152 } |
|
153 case KErrCancel: |
|
154 {// A user_canceled alert should be followed by a close_notify alert. So this |
|
155 // event will be the next one to be processed. |
|
156 iRecordComposer.SetNext( NULL ); |
|
157 iAlertMsg.Append( EAlertWarning ); |
|
158 iAlertMsg.Append( EAlertclose_notify ); |
|
159 iRecordComposer.SetNext( NULL ); |
|
160 break; |
|
161 } |
|
162 default: |
|
163 { //find the matching alert code to send |
|
164 TUint nIndex = 0; |
|
165 while ( nIndex < sizeof( glbTLSErrorAlertCodeMap )/sizeof( glbTLSErrorAlertCodeMap[0] ) && |
|
166 glbTLSErrorAlertCodeMap[nIndex].iTLSErrorCode != error ) |
|
167 { |
|
168 nIndex++; |
|
169 } |
|
170 // Set the message content and its record type. |
|
171 iAlertMsg.Append( nIndex >= KWarningAlertsCount ? EAlertFatal : EAlertWarning ); |
|
172 iAlertMsg.Append( nIndex < sizeof( glbTLSErrorAlertCodeMap )/sizeof( glbTLSErrorAlertCodeMap[0] ) ? |
|
173 glbTLSErrorAlertCodeMap[nIndex].iAlertCode : EAlertunexpected_message ); |
|
174 if ( iAlertMsg[0] == EAlertFatal ) |
|
175 { |
|
176 iRecordComposer.SetNext( NULL ); |
|
177 } |
|
178 break; |
|
179 } |
|
180 |
|
181 } //switch |
|
182 |
|
183 iRecordComposer.SetUserData( &iAlertMsg ); |
|
184 iRecordComposer.SetRecordType( ETlsAlertContentType ); |
|
185 iRecordComposer.ResetCurrentPos(); |
|
186 return iRecordComposer.ProcessL( aStatus ); |
|
187 } |
|
188 |
|
189 CAsynchEvent* CRecvAlert::ProcessL( TRequestStatus& aStatus ) |
|
190 { |
|
191 // Get the Alert message contents from the Record Parser's iPtrHBuf (this descriptor is |
|
192 // always set to point to the decrypted (when necessary) data. |
|
193 TPtr8 alertMsg( NULL, 0 ); |
|
194 alertMsg.Set( iRecordParser.PtrHBuf() ); |
|
195 User::LeaveIfError( alertMsg.Length() != KAlertMsgLength ? KErrSSLAlertUnexpectedMessage : KErrNone ); |
|
196 TUint8 alertLevel = alertMsg[0]; |
|
197 TUint8 alertDesc = alertMsg[1]; |
|
198 LOG(Log::Printf(_L("CRecvAlert::ProcessL(). Alert level = %d"), alertLevel )); |
|
199 LOG(Log::Printf(_L("CRecvAlert::ProcessL(). Alert description = %d"), alertDesc )); |
|
200 |
|
201 TRequestStatus* p=&aStatus; |
|
202 User::RequestComplete( p, KErrNone ); |
|
203 if ( alertLevel == EAlertWarning ) |
|
204 {// In all circumstances, when a warning alert is received, we carry on as normal. |
|
205 // There is no need to set the next event as this will be unchanged from normal |
|
206 // operation. For a Close_notify alert, we must send one in response. |
|
207 // So the next event will be CSendAlert sending a close-notify alert. |
|
208 if ( alertDesc == EAlertclose_notify ) |
|
209 { |
|
210 iStateMachine->SetLastError( KErrSSLAlertCloseNotify ); |
|
211 return &iSendAlert; |
|
212 } |
|
213 else if ( alertDesc != EAlertno_renegotiation ) |
|
214 { |
|
215 return &iRecordParser; |
|
216 } |
|
217 else if ( iRecordParser.ReadActive() ) |
|
218 {//we must be in data mode already to receive this alert |
|
219 //if alertDesc == EAlertno_renegotiation for the moment it means that re-negotiation completed successfully |
|
220 return NULL; |
|
221 } |
|
222 alertDesc = EAlertillegal_parameter; |
|
223 } |
|
224 //Complete the request immediately and close the connection. |
|
225 //and set the statemachine error code to return to the client |
|
226 TUint nIndex = 0; |
|
227 while ( nIndex < sizeof( glbTLSErrorAlertCodeMap )/sizeof( glbTLSErrorAlertCodeMap[0] ) && |
|
228 glbTLSErrorAlertCodeMap[nIndex].iAlertCode != alertDesc ) |
|
229 { |
|
230 nIndex++; |
|
231 } |
|
232 iStateMachine->SetLastError( nIndex < sizeof( glbTLSErrorAlertCodeMap )/sizeof( glbTLSErrorAlertCodeMap[0] ) ? |
|
233 glbTLSErrorAlertCodeMap[nIndex].iTLSErrorCode : KErrSSLAlertIllegalParameter ); |
|
234 |
|
235 return NULL; |
|
236 } |
|
237 |
|
238 TBool CRecvAlert::AcceptRecord( TInt aRecordType ) const |
|
239 /** |
|
240 * This method accepts an Alert event. Alerts are always accepted. |
|
241 */ |
|
242 { |
|
243 LOG(Log::Printf(_L("CRecvAlert::AcceptRecord()\n"));) |
|
244 return aRecordType == ETlsAlertContentType; |
|
245 } |
|
246 |