Skip to content

Commit e684676

Browse files
authored
fix: (*UConn).Read() and Secure Renegotiation (#292)
* fix: UConn Read does not trigger correct Handshake Copy `(*Conn).Read` to `(*UConn).Read` and force it use `(*UConn).Handshake`. Same for `handleRenegotiation` and `handlePostHandshakeMessage`. Signed-off-by: Gaukas Wang <[email protected]> * update: use VerifyData in RenegotiationInfoExt This make sure the renegotiation would work in certain scenarios instead of no scenarios. Signed-off-by: Gaukas Wang <[email protected]> --------- Signed-off-by: Gaukas Wang <[email protected]>
1 parent e2bc5b1 commit e684676

File tree

2 files changed

+146
-8
lines changed

2 files changed

+146
-8
lines changed

u_conn.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,3 +889,134 @@ type utlsConnExtraFields struct {
889889

890890
sessionController *sessionController
891891
}
892+
893+
// Read reads data from the connection.
894+
//
895+
// As Read calls [Conn.Handshake], in order to prevent indefinite blocking a deadline
896+
// must be set for both Read and [Conn.Write] before Read is called when the handshake
897+
// has not yet completed. See [Conn.SetDeadline], [Conn.SetReadDeadline], and
898+
// [Conn.SetWriteDeadline].
899+
func (c *UConn) Read(b []byte) (int, error) {
900+
if err := c.Handshake(); err != nil {
901+
return 0, err
902+
}
903+
if len(b) == 0 {
904+
// Put this after Handshake, in case people were calling
905+
// Read(nil) for the side effect of the Handshake.
906+
return 0, nil
907+
}
908+
909+
c.in.Lock()
910+
defer c.in.Unlock()
911+
912+
for c.input.Len() == 0 {
913+
if err := c.readRecord(); err != nil {
914+
return 0, err
915+
}
916+
for c.hand.Len() > 0 {
917+
if err := c.handlePostHandshakeMessage(); err != nil {
918+
return 0, err
919+
}
920+
}
921+
}
922+
923+
n, _ := c.input.Read(b)
924+
925+
// If a close-notify alert is waiting, read it so that we can return (n,
926+
// EOF) instead of (n, nil), to signal to the HTTP response reading
927+
// goroutine that the connection is now closed. This eliminates a race
928+
// where the HTTP response reading goroutine would otherwise not observe
929+
// the EOF until its next read, by which time a client goroutine might
930+
// have already tried to reuse the HTTP connection for a new request.
931+
// See https://golang.org/cl/76400046 and https://golang.org/issue/3514
932+
if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 &&
933+
recordType(c.rawInput.Bytes()[0]) == recordTypeAlert {
934+
if err := c.readRecord(); err != nil {
935+
return n, err // will be io.EOF on closeNotify
936+
}
937+
}
938+
939+
return n, nil
940+
}
941+
942+
// handleRenegotiation processes a HelloRequest handshake message.
943+
func (c *UConn) handleRenegotiation() error {
944+
if c.vers == VersionTLS13 {
945+
return errors.New("tls: internal error: unexpected renegotiation")
946+
}
947+
948+
msg, err := c.readHandshake(nil)
949+
if err != nil {
950+
return err
951+
}
952+
953+
helloReq, ok := msg.(*helloRequestMsg)
954+
if !ok {
955+
c.sendAlert(alertUnexpectedMessage)
956+
return unexpectedMessageError(helloReq, msg)
957+
}
958+
959+
if !c.isClient {
960+
return c.sendAlert(alertNoRenegotiation)
961+
}
962+
963+
switch c.config.Renegotiation {
964+
case RenegotiateNever:
965+
return c.sendAlert(alertNoRenegotiation)
966+
case RenegotiateOnceAsClient:
967+
if c.handshakes > 1 {
968+
return c.sendAlert(alertNoRenegotiation)
969+
}
970+
case RenegotiateFreelyAsClient:
971+
// Ok.
972+
default:
973+
c.sendAlert(alertInternalError)
974+
return errors.New("tls: unknown Renegotiation value")
975+
}
976+
977+
c.handshakeMutex.Lock()
978+
defer c.handshakeMutex.Unlock()
979+
980+
c.isHandshakeComplete.Store(false)
981+
982+
// [uTLS section begins]
983+
if err = c.BuildHandshakeState(); err != nil {
984+
return err
985+
}
986+
// [uTLS section ends]
987+
if c.handshakeErr = c.clientHandshake(context.Background()); c.handshakeErr == nil {
988+
c.handshakes++
989+
}
990+
return c.handshakeErr
991+
}
992+
993+
// handlePostHandshakeMessage processes a handshake message arrived after the
994+
// handshake is complete. Up to TLS 1.2, it indicates the start of a renegotiation.
995+
func (c *UConn) handlePostHandshakeMessage() error {
996+
if c.vers != VersionTLS13 {
997+
return c.handleRenegotiation()
998+
}
999+
1000+
msg, err := c.readHandshake(nil)
1001+
if err != nil {
1002+
return err
1003+
}
1004+
c.retryCount++
1005+
if c.retryCount > maxUselessRecords {
1006+
c.sendAlert(alertUnexpectedMessage)
1007+
return c.in.setErrorLocked(errors.New("tls: too many non-advancing records"))
1008+
}
1009+
1010+
switch msg := msg.(type) {
1011+
case *newSessionTicketMsgTLS13:
1012+
return c.handleNewSessionTicket(msg)
1013+
case *keyUpdateMsg:
1014+
return c.handleKeyUpdate(msg)
1015+
}
1016+
// The QUIC layer is supposed to treat an unexpected post-handshake CertificateRequest
1017+
// as a QUIC-level PROTOCOL_VIOLATION error (RFC 9001, Section 4.4). Returning an
1018+
// unexpected_message alert here doesn't provide it with enough information to distinguish
1019+
// this condition from other unexpected messages. This is probably fine.
1020+
c.sendAlert(alertUnexpectedMessage)
1021+
return fmt.Errorf("tls: received unexpected handshake message of type %T", msg)
1022+
}

u_tls_extensions.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,27 +1563,27 @@ type RenegotiationInfoExtension struct {
15631563
// If this is the initial handshake for a connection, then the
15641564
// "renegotiated_connection" field is of zero length in both the
15651565
// ClientHello and the ServerHello.
1566-
// RenegotiatedConnection []byte
1566+
RenegotiatedConnection []byte
15671567
}
15681568

15691569
func (e *RenegotiationInfoExtension) Len() int {
1570-
return 5 // + len(e.RenegotiatedConnection)
1570+
return 5 + len(e.RenegotiatedConnection)
15711571
}
15721572

15731573
func (e *RenegotiationInfoExtension) Read(b []byte) (int, error) {
15741574
if len(b) < e.Len() {
15751575
return 0, io.ErrShortBuffer
15761576
}
15771577

1578-
// dataLen := len(e.RenegotiatedConnection)
1579-
extBodyLen := 1 // + len(dataLen)
1578+
dataLen := len(e.RenegotiatedConnection)
1579+
extBodyLen := 1 + dataLen
15801580

15811581
b[0] = byte(extensionRenegotiationInfo >> 8)
15821582
b[1] = byte(extensionRenegotiationInfo & 0xff)
15831583
b[2] = byte(extBodyLen >> 8)
15841584
b[3] = byte(extBodyLen)
1585-
// b[4] = byte(dataLen)
1586-
// copy(b[5:], e.RenegotiatedConnection)
1585+
b[4] = byte(dataLen)
1586+
copy(b[5:], e.RenegotiatedConnection)
15871587

15881588
return e.Len(), io.EOF
15891589
}
@@ -1593,7 +1593,7 @@ func (e *RenegotiationInfoExtension) UnmarshalJSON(_ []byte) error {
15931593
return nil
15941594
}
15951595

1596-
func (e *RenegotiationInfoExtension) Write(_ []byte) (int, error) {
1596+
func (e *RenegotiationInfoExtension) Write(b []byte) (int, error) {
15971597
e.Renegotiation = RenegotiateOnceAsClient // none empty or other modes are unsupported
15981598
// extData := cryptobyte.String(b)
15991599
// var renegotiatedConnection cryptobyte.String
@@ -1602,7 +1602,10 @@ func (e *RenegotiationInfoExtension) Write(_ []byte) (int, error) {
16021602
// }
16031603
// e.RenegotiatedConnection = make([]byte, len(renegotiatedConnection))
16041604
// copy(e.RenegotiatedConnection, renegotiatedConnection)
1605-
return 0, nil
1605+
1606+
// we don't really want to parse it at all.
1607+
1608+
return len(b), nil
16061609
}
16071610

16081611
func (e *RenegotiationInfoExtension) writeToUConn(uc *UConn) error {
@@ -1612,6 +1615,10 @@ func (e *RenegotiationInfoExtension) writeToUConn(uc *UConn) error {
16121615
fallthrough
16131616
case RenegotiateFreelyAsClient:
16141617
uc.HandshakeState.Hello.SecureRenegotiationSupported = true
1618+
// TODO: don't do backward propagation here
1619+
if uc.handshakes > 0 {
1620+
e.RenegotiatedConnection = uc.clientFinished[:]
1621+
}
16151622
case RenegotiateNever:
16161623
default:
16171624
}

0 commit comments

Comments
 (0)