diff --git a/internal/conf/conf_test.go b/internal/conf/conf_test.go index a9e53829..9c6908f0 100644 --- a/internal/conf/conf_test.go +++ b/internal/conf/conf_test.go @@ -177,9 +177,8 @@ func TestConfEncryption(t *testing.T) { copy(secretKey[:], key) var nonce [24]byte - if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { - panic(err) - } + _, err := io.ReadFull(rand.Reader, nonce[:]) + require.NoError(t, err) encrypted := secretbox.Seal(nonce[:], []byte(plaintext), &nonce, &secretKey) return base64.StdEncoding.EncodeToString(encrypted) diff --git a/internal/core/webrtc_http_server.go b/internal/core/webrtc_http_server.go index 136a4359..de67b39a 100644 --- a/internal/core/webrtc_http_server.go +++ b/internal/core/webrtc_http_server.go @@ -160,7 +160,7 @@ func marshalICEFragment(offer *webrtc.SessionDescription, candidates []*webrtc.I type webRTCHTTPServerParent interface { logger.Writer - generateICEServers() []webrtc.ICEServer + generateICEServers() ([]webrtc.ICEServer, error) sessionNew(req webRTCSessionNewReq) webRTCSessionNewRes sessionAddCandidates(req webRTCSessionAddCandidatesReq) webRTCSessionAddCandidatesRes } @@ -346,9 +346,15 @@ func (s *webRTCHTTPServer) onRequest(ctx *gin.Context) { case "whip", "whep": switch ctx.Request.Method { case http.MethodOptions: + servers, err := s.parent.generateICEServers() + if err != nil { + ctx.Writer.WriteHeader(http.StatusInternalServerError) + return + } + ctx.Writer.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PATCH") ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type, If-Match") - ctx.Writer.Header()["Link"] = iceServersToLinkHeader(s.parent.generateICEServers()) + ctx.Writer.Header()["Link"] = iceServersToLinkHeader(servers) ctx.Writer.WriteHeader(http.StatusOK) case http.MethodPost: @@ -376,12 +382,18 @@ func (s *webRTCHTTPServer) onRequest(ctx *gin.Context) { return } + servers, err := s.parent.generateICEServers() + if err != nil { + ctx.Writer.WriteHeader(http.StatusInternalServerError) + return + } + ctx.Writer.Header().Set("Content-Type", "application/sdp") ctx.Writer.Header().Set("Access-Control-Expose-Headers", "E-Tag, Accept-Patch, Link") ctx.Writer.Header().Set("E-Tag", res.sx.secret.String()) ctx.Writer.Header().Set("ID", res.sx.uuid.String()) ctx.Writer.Header().Set("Accept-Patch", "application/trickle-ice-sdpfrag") - ctx.Writer.Header()["Link"] = iceServersToLinkHeader(s.parent.generateICEServers()) + ctx.Writer.Header()["Link"] = iceServersToLinkHeader(servers) ctx.Writer.Header().Set("Location", ctx.Request.URL.String()) ctx.Writer.WriteHeader(http.StatusCreated) ctx.Writer.Write(res.answer) diff --git a/internal/core/webrtc_manager.go b/internal/core/webrtc_manager.go index b7f309ff..34ccf2ff 100644 --- a/internal/core/webrtc_manager.go +++ b/internal/core/webrtc_manager.go @@ -31,33 +31,57 @@ const ( webrtcTurnSecretExpiration = 24 * 3600 * time.Second ) -func randInt63() int64 { +func randInt63() (int64, error) { var b [8]byte - rand.Read(b[:]) + _, err := rand.Read(b[:]) + if err != nil { + return 0, err + } + return int64(uint64(b[0]&0b01111111)<<56 | uint64(b[1])<<48 | uint64(b[2])<<40 | uint64(b[3])<<32 | - uint64(b[4])<<24 | uint64(b[5])<<16 | uint64(b[6])<<8 | uint64(b[7])) + uint64(b[4])<<24 | uint64(b[5])<<16 | uint64(b[6])<<8 | uint64(b[7])), nil } // https://cs.opensource.google/go/go/+/refs/tags/go1.20.4:src/math/rand/rand.go;l=119 -func randInt63n(n int64) int64 { +func randInt63n(n int64) (int64, error) { if n&(n-1) == 0 { // n is power of two, can mask - return randInt63() & (n - 1) + r, err := randInt63() + if err != nil { + return 0, err + } + return r & (n - 1), nil } + max := int64((1 << 63) - 1 - (1<<63)%uint64(n)) - v := randInt63() - for v > max { - v = randInt63() + + v, err := randInt63() + if err != nil { + return 0, err } - return v % n + + for v > max { + v, err = randInt63() + if err != nil { + return 0, err + } + } + + return v % n, nil } -func randomTurnUser() string { +func randomTurnUser() (string, error) { const charset = "abcdefghijklmnopqrstuvwxyz1234567890" b := make([]byte, 20) for i := range b { - b[i] = charset[int(randInt63n(int64(len(charset))))] + j, err := randInt63n(int64(len(charset))) + if err != nil { + return "", err + } + + b[i] = charset[int(j)] } - return string(b) + + return string(b), nil } type webRTCManagerAPISessionsListRes struct { @@ -363,14 +387,23 @@ func (m *webRTCManager) findSessionByUUID(uuid uuid.UUID) *webRTCSession { return nil } -func (m *webRTCManager) generateICEServers() []webrtc.ICEServer { +func (m *webRTCManager) generateICEServers() ([]webrtc.ICEServer, error) { ret := make([]webrtc.ICEServer, len(m.iceServers)) + for i, server := range m.iceServers { if server.Username == "AUTH_SECRET" { expireDate := time.Now().Add(webrtcTurnSecretExpiration).Unix() - server.Username = strconv.FormatInt(expireDate, 10) + ":" + randomTurnUser() + + user, err := randomTurnUser() + if err != nil { + return nil, err + } + + server.Username = strconv.FormatInt(expireDate, 10) + ":" + user + h := hmac.New(sha1.New, []byte(server.Password)) h.Write([]byte(server.Username)) + server.Password = base64.StdEncoding.EncodeToString(h.Sum(nil)) } @@ -380,7 +413,8 @@ func (m *webRTCManager) generateICEServers() []webrtc.ICEServer { Credential: server.Password, } } - return ret + + return ret, nil } // sessionNew is called by webRTCHTTPServer. diff --git a/internal/core/webrtc_session.go b/internal/core/webrtc_session.go index 16f6e4ce..e5d3d7f9 100644 --- a/internal/core/webrtc_session.go +++ b/internal/core/webrtc_session.go @@ -274,8 +274,13 @@ func (s *webRTCSession) runPublish() (int, error) { defer res.path.publisherRemove(pathPublisherRemoveReq{author: s}) + servers, err := s.parent.generateICEServers() + if err != nil { + return http.StatusInternalServerError, err + } + pc, err := newPeerConnection( - s.parent.generateICEServers(), + servers, s.iceHostNAT1To1IPs, s.iceUDPMux, s.iceTCPMux, @@ -446,8 +451,13 @@ func (s *webRTCSession) runRead() (int, error) { return http.StatusBadRequest, err } + servers, err := s.parent.generateICEServers() + if err != nil { + return http.StatusInternalServerError, err + } + pc, err := newPeerConnection( - s.parent.generateICEServers(), + servers, s.iceHostNAT1To1IPs, s.iceUDPMux, s.iceTCPMux, diff --git a/internal/rtmp/handshake/c1s1.go b/internal/rtmp/handshake/c1s1.go index 9df338d4..4bfe7585 100644 --- a/internal/rtmp/handshake/c1s1.go +++ b/internal/rtmp/handshake/c1s1.go @@ -119,7 +119,10 @@ func (c *C1S1) Write(w io.Writer, isC1 bool) error { copy(buf[4:], []byte{0, 0, 0, 0}) if c.Random == nil { - rand.Read(buf[8:]) + _, err := rand.Read(buf[8:]) + if err != nil { + return err + } c.Random = buf[8:] } else { copy(buf[8:], c.Random) diff --git a/internal/rtmp/handshake/handshake_test.go b/internal/rtmp/handshake/handshake_test.go index da72611b..744854d7 100644 --- a/internal/rtmp/handshake/handshake_test.go +++ b/internal/rtmp/handshake/handshake_test.go @@ -63,7 +63,8 @@ func TestHandshakeFallback(t *testing.T) { require.NoError(t, err) c1 := make([]byte, 1536) - rand.Read(c1[8:]) + _, err = rand.Read(c1[8:]) + require.NoError(t, err) _, err = conn.Write(c1) require.NoError(t, err)