@@ -34,14 +34,8 @@ private enum Framing
34
34
// This is set on the first packet to figure out the framing style.
35
35
private Framing _framing = Framing . Unknown ;
36
36
37
- // SSL3/TLS protocol frames definitions.
38
- private enum FrameType : byte
39
- {
40
- ChangeCipherSpec = 20 ,
41
- Alert = 21 ,
42
- Handshake = 22 ,
43
- AppData = 23
44
- }
37
+ private TlsAlertDescription _lastAlertDescription ;
38
+ private TlsFrameHandshakeInfo _lastFrame ;
45
39
46
40
private readonly object _handshakeLock = new object ( ) ;
47
41
private volatile TaskCompletionSource < bool > ? _handshakeWaiter ;
@@ -274,7 +268,6 @@ private async Task ForceAuthenticationAsync<TIOAdapter>(TIOAdapter adapter, bool
274
268
{
275
269
// get ready to receive first frame
276
270
_handshakeBuffer = new ArrayBuffer ( InitialHandshakeBufferSize ) ;
277
- _framing = Framing . Unknown ;
278
271
}
279
272
280
273
while ( ! handshakeCompleted )
@@ -288,6 +281,19 @@ private async Task ForceAuthenticationAsync<TIOAdapter>(TIOAdapter adapter, bool
288
281
289
282
if ( message . Failed )
290
283
{
284
+ if ( _lastFrame . Header . Type == TlsContentType . Handshake && message . Size == 0 )
285
+ {
286
+ // If we failed without OS sending out alert, inject one here to be consistent across platforms.
287
+ byte [ ] alert = TlsFrameHelper . CreateAlertFrame ( _lastFrame . Header . Version , TlsAlertDescription . ProtocolVersion ) ;
288
+ await adapter . WriteAsync ( alert , 0 , alert . Length ) . ConfigureAwait ( false ) ;
289
+ }
290
+ else if ( _lastFrame . Header . Type == TlsContentType . Alert && _lastAlertDescription != TlsAlertDescription . CloseNotify &&
291
+ message . Status . ErrorCode == SecurityStatusPalErrorCode . IllegalMessage )
292
+ {
293
+ // Improve generic message and show details if we failed because of TLS Alert.
294
+ throw new AuthenticationException ( SR . Format ( SR . net_auth_tls_alert , _lastAlertDescription . ToString ( ) ) , message . GetException ( ) ) ;
295
+ }
296
+
291
297
throw new AuthenticationException ( SR . net_auth_SSPI , message . GetException ( ) ) ;
292
298
}
293
299
else if ( message . Status . ErrorCode == SecurityStatusPalErrorCode . OK )
@@ -346,17 +352,49 @@ private async ValueTask<ProtocolToken> ReceiveBlobAsync<TIOAdapter>(TIOAdapter a
346
352
_framing = DetectFraming ( _handshakeBuffer . ActiveReadOnlySpan ) ;
347
353
}
348
354
349
- int frameSize = GetFrameSize ( _handshakeBuffer . ActiveReadOnlySpan ) ;
350
- if ( frameSize < 0 )
355
+ if ( _framing == Framing . BeforeSSL3 )
356
+ {
357
+ #pragma warning disable 0618
358
+ _lastFrame . Header . Version = SslProtocols . Ssl2 ;
359
+ #pragma warning restore 0618
360
+ _lastFrame . Header . Length = GetFrameSize ( _handshakeBuffer . ActiveReadOnlySpan ) ;
361
+ }
362
+ else
363
+ {
364
+ TlsFrameHelper . TryGetFrameHeader ( _handshakeBuffer . ActiveReadOnlySpan , ref _lastFrame . Header ) ;
365
+ }
366
+
367
+ if ( _lastFrame . Header . Length < 0 )
351
368
{
352
369
throw new IOException ( SR . net_frame_read_size ) ;
353
370
}
354
371
372
+ // Header length is content only so we must add header size as well.
373
+ int frameSize = _lastFrame . Header . Length + TlsFrameHelper . HeaderSize ;
355
374
if ( _handshakeBuffer . ActiveLength < frameSize )
356
375
{
357
376
await FillHandshakeBufferAsync ( adapter , frameSize ) . ConfigureAwait ( false ) ;
358
377
}
378
+
359
379
// At this point, we have at least one TLS frame.
380
+ if ( _lastFrame . Header . Type == TlsContentType . Alert )
381
+ {
382
+ TlsAlertLevel level = 0 ;
383
+ if ( TlsFrameHelper . TryGetAlertInfo ( _handshakeBuffer . ActiveReadOnlySpan , ref level , ref _lastAlertDescription ) )
384
+ {
385
+ if ( NetEventSource . IsEnabled && _lastAlertDescription != TlsAlertDescription . CloseNotify ) NetEventSource . Fail ( this , $ "Received TLS alert { _lastAlertDescription } ") ;
386
+ }
387
+ }
388
+ else if ( _lastFrame . Header . Type == TlsContentType . Handshake )
389
+ {
390
+ if ( _handshakeBuffer . ActiveReadOnlySpan [ TlsFrameHelper . HeaderSize ] == ( byte ) TlsHandshakeType . ClientHello &&
391
+ _sslAuthenticationOptions ! . ServerCertSelectionDelegate != null )
392
+ {
393
+ // Process SNI from Client Hello message
394
+ TlsFrameHelper . TryGetHandshakeInfo ( _handshakeBuffer . ActiveReadOnlySpan , ref _lastFrame ) ;
395
+ _sslAuthenticationOptions . TargetHost = _lastFrame . TargetName ;
396
+ }
397
+ }
360
398
361
399
return ProcessBlob ( frameSize ) ;
362
400
}
@@ -372,23 +410,24 @@ private ProtocolToken ProcessBlob(int frameSize)
372
410
_handshakeBuffer . Discard ( frameSize ) ;
373
411
374
412
// Often more TLS messages fit into same packet. Get as many complete frames as we can.
375
- while ( _handshakeBuffer . ActiveLength > SecureChannel . ReadHeaderSize )
413
+ while ( _handshakeBuffer . ActiveLength > TlsFrameHelper . HeaderSize )
376
414
{
377
- ReadOnlySpan < byte > remainingData = _handshakeBuffer . ActiveReadOnlySpan ;
378
- if ( remainingData [ 0 ] >= ( int ) FrameType . AppData )
415
+ TlsFrameHeader nextHeader = default ;
416
+
417
+ if ( ! TlsFrameHelper . TryGetFrameHeader ( _handshakeBuffer . ActiveReadOnlySpan , ref nextHeader ) )
379
418
{
380
419
break ;
381
420
}
382
421
383
- frameSize = GetFrameSize ( remainingData ) ;
384
- if ( _handshakeBuffer . ActiveLength >= frameSize )
422
+ frameSize = nextHeader . Length + TlsFrameHelper . HeaderSize ;
423
+ if ( nextHeader . Type == TlsContentType . AppData || frameSize > _handshakeBuffer . ActiveLength )
385
424
{
386
- chunkSize += frameSize ;
387
- _handshakeBuffer . Discard ( frameSize ) ;
388
- continue ;
425
+ // We don't have full frame left or we already have app data which needs to be processed by decrypt.
426
+ break ;
389
427
}
390
428
391
- break ;
429
+ chunkSize += frameSize ;
430
+ _handshakeBuffer . Discard ( frameSize ) ;
392
431
}
393
432
394
433
return _context ! . NextMessage ( availableData . Slice ( 0 , chunkSize ) ) ;
@@ -645,7 +684,7 @@ private async ValueTask<int> ReadAsyncInternal<TIOAdapter>(TIOAdapter adapter, M
645
684
Debug . Assert ( _internalBufferCount >= SecureChannel . ReadHeaderSize ) ;
646
685
647
686
// Parse the frame header to determine the payload size (which includes the header size).
648
- int payloadBytes = GetFrameSize ( _internalBuffer . AsSpan ( _internalOffset ) ) ;
687
+ int payloadBytes = TlsFrameHelper . GetFrameSize ( _internalBuffer . AsSpan ( _internalOffset ) ) ;
649
688
if ( payloadBytes < 0 )
650
689
{
651
690
throw new IOException ( SR . net_frame_read_size ) ;
@@ -913,6 +952,7 @@ private static byte[] EnsureBufferSize(byte[] buffer, int copyCount, int size)
913
952
Buffer . BlockCopy ( saved , 0 , buffer , 0 , copyCount ) ;
914
953
}
915
954
}
955
+
916
956
return buffer ;
917
957
}
918
958
@@ -1003,8 +1043,8 @@ private Framing DetectFraming(ReadOnlySpan<byte> bytes)
1003
1043
}
1004
1044
1005
1045
// If the first byte is SSL3 HandShake, then check if we have a SSLv3 Type3 client hello.
1006
- if ( bytes [ 0 ] == ( byte ) FrameType . Handshake || bytes [ 0 ] == ( byte ) FrameType . AppData
1007
- || bytes [ 0 ] == ( byte ) FrameType . Alert )
1046
+ if ( bytes [ 0 ] == ( byte ) TlsContentType . Handshake || bytes [ 0 ] == ( byte ) TlsContentType . AppData
1047
+ || bytes [ 0 ] == ( byte ) TlsContentType . Alert )
1008
1048
{
1009
1049
if ( bytes . Length < 3 )
1010
1050
{
0 commit comments