diff --git a/0001-CVE-2023-39325.patch b/0001-CVE-2023-39325.patch new file mode 100644 index 0000000..86d698a --- /dev/null +++ b/0001-CVE-2023-39325.patch @@ -0,0 +1,117 @@ +diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go +index e644d9b..7575bc5 100644 +--- a/vendor/golang.org/x/net/http2/server.go ++++ b/vendor/golang.org/x/net/http2/server.go +@@ -520,9 +520,11 @@ type serverConn struct { + advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client + curClientStreams uint32 // number of open streams initiated by the client + curPushedStreams uint32 // number of open streams initiated by server push ++ curHandlers uint32 // number of running handler goroutines + maxClientStreamID uint32 // max ever seen from client (odd), or 0 if there have been no client requests + maxPushPromiseID uint32 // ID of the last push promise (even), or 0 if there have been no pushes + streams map[uint32]*stream ++ unstartedHandlers []unstartedHandler + initialStreamSendWindowSize int32 + maxFrameSize int32 + headerTableSize uint32 +@@ -909,6 +911,8 @@ func (sc *serverConn) serve() { + return + case gracefulShutdownMsg: + sc.startGracefulShutdownInternal() ++ case handlerDoneMsg: ++ sc.handlerDone() + default: + panic("unknown timer") + } +@@ -954,6 +958,7 @@ var ( + idleTimerMsg = new(serverMessage) + shutdownTimerMsg = new(serverMessage) + gracefulShutdownMsg = new(serverMessage) ++ handlerDoneMsg = new(serverMessage) + ) + + func (sc *serverConn) onSettingsTimer() { sc.sendServeMsg(settingsTimerMsg) } +@@ -1911,8 +1916,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error { + sc.conn.SetReadDeadline(time.Time{}) + } + +- go sc.runHandler(rw, req, handler) +- return nil ++ return sc.scheduleHandler(id, rw, req, handler) + } + + func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error { +@@ -2159,8 +2163,62 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r + return rw, req, nil + } + ++type unstartedHandler struct { ++ streamID uint32 ++ rw *responseWriter ++ req *http.Request ++ handler func(http.ResponseWriter, *http.Request) ++} ++ ++// scheduleHandler starts a handler goroutine, ++// or schedules one to start as soon as an existing handler finishes. ++func (sc *serverConn) scheduleHandler(streamID uint32, rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) error { ++ sc.serveG.check() ++ maxHandlers := sc.advMaxStreams ++ if sc.curHandlers < maxHandlers { ++ sc.curHandlers++ ++ go sc.runHandler(rw, req, handler) ++ return nil ++ } ++ if len(sc.unstartedHandlers) > int(4*sc.advMaxStreams) { ++ return sc.countError("too_many_early_resets", ConnectionError(ErrCodeEnhanceYourCalm)) ++ } ++ sc.unstartedHandlers = append(sc.unstartedHandlers, unstartedHandler{ ++ streamID: streamID, ++ rw: rw, ++ req: req, ++ handler: handler, ++ }) ++ return nil ++} ++ ++func (sc *serverConn) handlerDone() { ++ sc.serveG.check() ++ sc.curHandlers-- ++ i := 0 ++ maxHandlers := sc.advMaxStreams ++ for ; i < len(sc.unstartedHandlers); i++ { ++ u := sc.unstartedHandlers[i] ++ if sc.streams[u.streamID] == nil { ++ // This stream was reset before its goroutine had a chance to start. ++ continue ++ } ++ if sc.curHandlers >= maxHandlers { ++ break ++ } ++ sc.curHandlers++ ++ go sc.runHandler(u.rw, u.req, u.handler) ++ sc.unstartedHandlers[i] = unstartedHandler{} // don't retain references ++ } ++ sc.unstartedHandlers = sc.unstartedHandlers[i:] ++ if len(sc.unstartedHandlers) == 0 { ++ sc.unstartedHandlers = nil ++ } ++} ++ + // Run on its own goroutine. + func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) { ++ defer sc.sendServeMsg(handlerDoneMsg) + didPanic := true + defer func() { + rw.rws.stream.cancelCtx() +@@ -2923,6 +2982,10 @@ func (sc *serverConn) startPush(msg *startPushRequest) { + panic(fmt.Sprintf("newWriterAndRequestNoBody(%+v): %v", msg.url, err)) + } + ++ // This is the first request on the connection, ++ // so start the handler directly rather than going ++ // through scheduleHandler. ++ sc.curHandlers++ + go sc.runHandler(rw, req, sc.handler.ServeHTTP) + return promisedID, nil + } diff --git a/0002-CVE-2023-48795.patch b/0002-CVE-2023-48795.patch new file mode 100644 index 0000000..c836580 --- /dev/null +++ b/0002-CVE-2023-48795.patch @@ -0,0 +1,245 @@ +From 409a35189b23ca1490265c4eeb13ea370d3144ba Mon Sep 17 00:00:00 2001 +From: Jianmin +Date: Mon, 23 Dec 2024 17:47:26 +0800 +Subject: [PATCH] [backport] ssh: implement strict KEX protocol changes + +--- + vendor/golang.org/x/crypto/ssh/handshake.go | 59 +++++++++++++++++++-- + vendor/golang.org/x/crypto/ssh/transport.go | 32 +++++++++-- + 2 files changed, 81 insertions(+), 10 deletions(-) + +diff --git a/vendor/golang.org/x/crypto/ssh/handshake.go b/vendor/golang.org/x/crypto/ssh/handshake.go +index 653dc4d..e7d4545 100644 +--- a/vendor/golang.org/x/crypto/ssh/handshake.go ++++ b/vendor/golang.org/x/crypto/ssh/handshake.go +@@ -34,6 +34,16 @@ type keyingTransport interface { + // direction will be effected if a msgNewKeys message is sent + // or received. + prepareKeyChange(*algorithms, *kexResult) error ++ ++ // setStrictMode sets the strict KEX mode, notably triggering ++ // sequence number resets on sending or receiving msgNewKeys. ++ // If the sequence number is already > 1 when setStrictMode ++ // is called, an error is returned. ++ setStrictMode() error ++ ++ // setInitialKEXDone indicates to the transport that the initial key exchange ++ // was completed ++ setInitialKEXDone() + } + + // handshakeTransport implements rekeying on top of a keyingTransport +@@ -94,6 +104,10 @@ type handshakeTransport struct { + + // The session ID or nil if first kex did not complete yet. + sessionID []byte ++ ++ // strictMode indicates if the other side of the handshake indicated ++ // that we should be following the strict KEX protocol restrictions. ++ strictMode bool + } + + type pendingKex struct { +@@ -201,7 +215,10 @@ func (t *handshakeTransport) readLoop() { + close(t.incoming) + break + } +- if p[0] == msgIgnore || p[0] == msgDebug { ++ // If this is the first kex, and strict KEX mode is enabled, ++ // we don't ignore any messages, as they may be used to manipulate ++ // the packet sequence numbers. ++ if !(t.sessionID == nil && t.strictMode) && (p[0] == msgIgnore || p[0] == msgDebug) { + continue + } + t.incoming <- p +@@ -432,6 +449,11 @@ func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) { + return successPacket, nil + } + ++const ( ++ kexStrictClient = "kex-strict-c-v00@openssh.com" ++ kexStrictServer = "kex-strict-s-v00@openssh.com" ++) ++ + // sendKexInit sends a key change message. + func (t *handshakeTransport) sendKexInit() error { + t.mu.Lock() +@@ -445,7 +467,6 @@ func (t *handshakeTransport) sendKexInit() error { + } + + msg := &kexInitMsg{ +- KexAlgos: t.config.KeyExchanges, + CiphersClientServer: t.config.Ciphers, + CiphersServerClient: t.config.Ciphers, + MACsClientServer: t.config.MACs, +@@ -455,6 +476,13 @@ func (t *handshakeTransport) sendKexInit() error { + } + io.ReadFull(rand.Reader, msg.Cookie[:]) + ++ // We mutate the KexAlgos slice, in order to add the kex-strict extension algorithm, ++ // and possibly to add the ext-info extension algorithm. Since the slice may be the ++ // user owned KeyExchanges, we create our own slice in order to avoid using user ++ // owned memory by mistake. ++ msg.KexAlgos = make([]string, 0, len(t.config.KeyExchanges)+2) // room for kex-strict and ext-info ++ msg.KexAlgos = append(msg.KexAlgos, t.config.KeyExchanges...) ++ + isServer := len(t.hostKeys) > 0 + if isServer { + for _, k := range t.hostKeys { +@@ -474,17 +502,24 @@ func (t *handshakeTransport) sendKexInit() error { + msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, keyFormat) + } + } ++ ++ if t.sessionID == nil { ++ msg.KexAlgos = append(msg.KexAlgos, kexStrictServer) ++ } + } else { + msg.ServerHostKeyAlgos = t.hostKeyAlgorithms + + // As a client we opt in to receiving SSH_MSG_EXT_INFO so we know what + // algorithms the server supports for public key authentication. See RFC + // 8308, Section 2.1. ++ // ++ // We also send the strict KEX mode extension algorithm, in order to opt ++ // into the strict KEX mode. + if firstKeyExchange := t.sessionID == nil; firstKeyExchange { +- msg.KexAlgos = make([]string, 0, len(t.config.KeyExchanges)+1) +- msg.KexAlgos = append(msg.KexAlgos, t.config.KeyExchanges...) + msg.KexAlgos = append(msg.KexAlgos, "ext-info-c") ++ msg.KexAlgos = append(msg.KexAlgos, kexStrictClient) + } ++ + } + + packet := Marshal(msg) +@@ -581,6 +616,13 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { + return err + } + ++ if t.sessionID == nil && ((isClient && contains(serverInit.KexAlgos, kexStrictServer)) || (!isClient && contains(clientInit.KexAlgos, kexStrictClient))) { ++ t.strictMode = true ++ if err := t.conn.setStrictMode(); err != nil { ++ return err ++ } ++ } ++ + // We don't send FirstKexFollows, but we handle receiving it. + // + // RFC 4253 section 7 defines the kex and the agreement method for +@@ -615,7 +657,8 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { + return err + } + +- if t.sessionID == nil { ++ firstKeyExchange := t.sessionID == nil ++ if firstKeyExchange { + t.sessionID = result.H + } + result.SessionID = t.sessionID +@@ -632,6 +675,12 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { + return unexpectedMessageError(msgNewKeys, packet[0]) + } + ++ if firstKeyExchange { ++ // Indicates to the transport that the first key exchange is completed ++ // after receiving SSH_MSG_NEWKEYS. ++ t.conn.setInitialKEXDone() ++ } ++ + return nil + } + +diff --git a/vendor/golang.org/x/crypto/ssh/transport.go b/vendor/golang.org/x/crypto/ssh/transport.go +index acf5a21..4df45fc 100644 +--- a/vendor/golang.org/x/crypto/ssh/transport.go ++++ b/vendor/golang.org/x/crypto/ssh/transport.go +@@ -48,6 +48,9 @@ type transport struct { + rand io.Reader + isClient bool + io.Closer ++ ++ strictMode bool ++ initialKEXDone bool + } + + // packetCipher represents a combination of SSH encryption/MAC +@@ -73,6 +76,18 @@ type connectionState struct { + pendingKeyChange chan packetCipher + } + ++func (t *transport) setStrictMode() error { ++ if t.reader.seqNum != 1 { ++ return errors.New("ssh: sequence number != 1 when strict KEX mode requested") ++ } ++ t.strictMode = true ++ return nil ++} ++ ++func (t *transport) setInitialKEXDone() { ++ t.initialKEXDone = true ++} ++ + // prepareKeyChange sets up key material for a keychange. The key changes in + // both directions are triggered by reading and writing a msgNewKey packet + // respectively. +@@ -111,11 +126,12 @@ func (t *transport) printPacket(p []byte, write bool) { + // Read and decrypt next packet. + func (t *transport) readPacket() (p []byte, err error) { + for { +- p, err = t.reader.readPacket(t.bufReader) ++ p, err = t.reader.readPacket(t.bufReader, t.strictMode) + if err != nil { + break + } +- if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) { ++ // in strict mode we pass through DEBUG and IGNORE packets only during the initial KEX ++ if len(p) == 0 || (t.strictMode && !t.initialKEXDone) || (p[0] != msgIgnore && p[0] != msgDebug) { + break + } + } +@@ -126,7 +142,7 @@ func (t *transport) readPacket() (p []byte, err error) { + return p, err + } + +-func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { ++func (s *connectionState) readPacket(r *bufio.Reader, strictMode bool) ([]byte, error) { + packet, err := s.packetCipher.readCipherPacket(s.seqNum, r) + s.seqNum++ + if err == nil && len(packet) == 0 { +@@ -139,6 +155,9 @@ func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { + select { + case cipher := <-s.pendingKeyChange: + s.packetCipher = cipher ++ if strictMode { ++ s.seqNum = 0 ++ } + default: + return nil, errors.New("ssh: got bogus newkeys message") + } +@@ -169,10 +188,10 @@ func (t *transport) writePacket(packet []byte) error { + if debugTransport { + t.printPacket(packet, true) + } +- return t.writer.writePacket(t.bufWriter, t.rand, packet) ++ return t.writer.writePacket(t.bufWriter, t.rand, packet, t.strictMode) + } + +-func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error { ++func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte, strictMode bool) error { + changeKeys := len(packet) > 0 && packet[0] == msgNewKeys + + err := s.packetCipher.writeCipherPacket(s.seqNum, w, rand, packet) +@@ -187,6 +206,9 @@ func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet [] + select { + case cipher := <-s.pendingKeyChange: + s.packetCipher = cipher ++ if strictMode { ++ s.seqNum = 0 ++ } + default: + panic("ssh: no key material for msgNewKeys") + } +-- +2.39.5 (Apple Git-154) + diff --git a/0003-CVE-2022-41723.patch b/0003-CVE-2022-41723.patch new file mode 100644 index 0000000..f225114 --- /dev/null +++ b/0003-CVE-2022-41723.patch @@ -0,0 +1,158 @@ +From 11bc24fcd1f15f54849c7aebefe1bbdc0d75437b Mon Sep 17 00:00:00 2001 +From: bwzhang +Date: Sun, 7 Apr 2024 17:04:42 +0800 +Subject: [PATCH] fix CVE-2022-41723 + +http2/hpack: avoid quadratic complexity in hpack decoding + +When parsing a field literal containing two Huffman-encoded strings, +don't decode the first string until verifying all data is present. +Avoids forced quadratic complexity when repeatedly parsing a partial +field, repeating the Huffman decoding of the string on each iteration. + +Thanks to Philippe Antoine (Catena cyber) for reporting this issue. + +Fixes golang/go#57855 +Fixes CVE-2022-41723 + +Change-Id: I58a743df450a4a4923dddd5cf6bb0592b0a7bdf3 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1688184 +TryBot-Result: Security TryBots +Reviewed-by: Julie Qiu +Run-TryBot: Damien Neil +Reviewed-by: Roland Shoemaker +Reviewed-on: https://go-review.googlesource.com/c/net/+/468135 +Run-TryBot: Michael Pratt +Reviewed-by: Roland Shoemaker +Reviewed-by: Than McIntosh +Auto-Submit: Michael Pratt +TryBot-Result: Gopher Robot +--- + vendor/golang.org/x/net/http2/hpack/hpack.go | 79 ++++++++++++-------- + 1 file changed, 49 insertions(+), 30 deletions(-) + +diff --git a/vendor/golang.org/x/net/http2/hpack/hpack.go b/vendor/golang.org/x/net/http2/hpack/hpack.go +index 85f18a2..7e7e4cf 100644 +--- a/vendor/golang.org/x/net/http2/hpack/hpack.go ++++ b/vendor/golang.org/x/net/http2/hpack/hpack.go +@@ -359,6 +359,7 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error { + + var hf HeaderField + wantStr := d.emitEnabled || it.indexed() ++ var undecodedName undecodedString + if nameIdx > 0 { + ihf, ok := d.at(nameIdx) + if !ok { +@@ -366,15 +367,27 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error { + } + hf.Name = ihf.Name + } else { +- hf.Name, buf, err = d.readString(buf, wantStr) ++ undecodedName, buf, err = d.readString(buf) + if err != nil { + return err + } + } +- hf.Value, buf, err = d.readString(buf, wantStr) ++ undecodedValue, buf, err := d.readString(buf) + if err != nil { + return err + } ++ if wantStr { ++ if nameIdx <= 0 { ++ hf.Name, err = d.decodeString(undecodedName) ++ if err != nil { ++ return err ++ } ++ } ++ hf.Value, err = d.decodeString(undecodedValue) ++ if err != nil { ++ return err ++ } ++ } + d.buf = buf + if it.indexed() { + d.dynTab.add(hf) +@@ -459,46 +472,52 @@ func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) { + return 0, origP, errNeedMore + } + +-// readString decodes an hpack string from p. ++// readString reads an hpack string from p. + // +-// wantStr is whether s will be used. If false, decompression and +-// []byte->string garbage are skipped if s will be ignored +-// anyway. This does mean that huffman decoding errors for non-indexed +-// strings past the MAX_HEADER_LIST_SIZE are ignored, but the server +-// is returning an error anyway, and because they're not indexed, the error +-// won't affect the decoding state. +-func (d *Decoder) readString(p []byte, wantStr bool) (s string, remain []byte, err error) { ++// It returns a reference to the encoded string data to permit deferring decode costs ++// until after the caller verifies all data is present. ++func (d *Decoder) readString(p []byte) (u undecodedString, remain []byte, err error) { + if len(p) == 0 { +- return "", p, errNeedMore ++ return u, p, errNeedMore + } + isHuff := p[0]&128 != 0 + strLen, p, err := readVarInt(7, p) + if err != nil { +- return "", p, err ++ return u, p, err + } + if d.maxStrLen != 0 && strLen > uint64(d.maxStrLen) { +- return "", nil, ErrStringLength ++ // Returning an error here means Huffman decoding errors ++ // for non-indexed strings past the maximum string length ++ // are ignored, but the server is returning an error anyway ++ // and because the string is not indexed the error will not ++ // affect the decoding state. ++ return u, nil, ErrStringLength + } + if uint64(len(p)) < strLen { +- return "", p, errNeedMore +- } +- if !isHuff { +- if wantStr { +- s = string(p[:strLen]) +- } +- return s, p[strLen:], nil ++ return u, p, errNeedMore + } ++ u.isHuff = isHuff ++ u.b = p[:strLen] ++ return u, p[strLen:], nil ++} + +- if wantStr { +- buf := bufPool.Get().(*bytes.Buffer) +- buf.Reset() // don't trust others +- defer bufPool.Put(buf) +- if err := huffmanDecode(buf, d.maxStrLen, p[:strLen]); err != nil { +- buf.Reset() +- return "", nil, err +- } ++type undecodedString struct { ++ isHuff bool ++ b []byte ++} ++ ++func (d *Decoder) decodeString(u undecodedString) (string, error) { ++ if !u.isHuff { ++ return string(u.b), nil ++ } ++ buf := bufPool.Get().(*bytes.Buffer) ++ buf.Reset() // don't trust others ++ var s string ++ err := huffmanDecode(buf, d.maxStrLen, u.b) ++ if err == nil { + s = buf.String() +- buf.Reset() // be nice to GC + } +- return s, p[strLen:], nil ++ buf.Reset() // be nice to GC ++ bufPool.Put(buf) ++ return s, err + } +-- +2.20.1 diff --git a/buildah.spec b/buildah.spec index c8f1e7a..515d36b 100644 --- a/buildah.spec +++ b/buildah.spec @@ -25,12 +25,15 @@ Name: buildah Version: 1.26.1 -Release: 2 +Release: 3 Summary: A command line tool used for creating OCI Images License: ASL 2.0 and BSD and MIT and MPLv2.0 URL: https://%{name}.io Source: %{git0}/archive/v%{version}.tar.gz Source1: https://github.com/cpuguy83/go-md2man/archive/v1.0.10.tar.gz +Patch1: 0001-CVE-2023-39325.patch +Patch2: 0002-CVE-2023-48795.patch +Patch3: 0003-CVE-2022-41723.patch BuildRequires: device-mapper-devel git-core golang >= 1.17.3-33 glib2-devel glibc-static gpgme-devel libassuan-devel BuildRequires: make ostree-devel btrfs-progs-devel libseccomp-static Requires: containers-common netavark iptables nftables libseccomp >= 2.4.1-0 @@ -136,6 +139,9 @@ make DESTDIR=%{buildroot} PREFIX=%{_prefix} -C docs install %{_datadir}/bash-completion/completions/%{name} %changelog +* Mon Dec 23 2024 Jianmin - 1.26.1-3 +- fix CVE-2023-39325, CVE-2022-41723 and CVE-2023-48795 + * Thu Dec 19 2024 Jianmin - 1.26.1-2 - Rebuild on Go 1.17.3-33 to fix CVE-2023-24534 CVE-2023-24536 CVE-2023-24537 CVE-2023-24538 CVE-2023-29406 CVE-2022-41715 CVE-2024-24791 CVE-2021-34558 and CVE-2022-1962