diff options
author | sakib.sajal@windriver.com <sakib.sajal@windriver.com> | 2020-04-29 15:36:08 -0700 |
---|---|---|
committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2020-05-02 11:02:58 -0400 |
commit | 3b217da98094569623ccb365ad3c850daee72fda (patch) | |
tree | 614370ad46676ed29b0334d77c51018ae5b9646e /recipes-extended | |
parent | f3faf9f1ad29ae5fa0ffdb0a6e1fd5df38b34921 (diff) | |
download | meta-virtualization-3b217da98094569623ccb365ad3c850daee72fda.tar.gz |
ceph: backport CVE fixes
Fix CVE-2020-1759 and CVE-2020-1760
PR for fix: https://github.com/ceph/ceph/pull/34482
Signed-off-by: Sakib Sajal <sakib.sajal@windriver.com>
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'recipes-extended')
6 files changed, 455 insertions, 0 deletions
diff --git a/recipes-extended/ceph/ceph/0001-msg-async-ProtocolV2-avoid-AES-GCM-nonce-reuse-vulne.patch b/recipes-extended/ceph/ceph/0001-msg-async-ProtocolV2-avoid-AES-GCM-nonce-reuse-vulne.patch new file mode 100644 index 00000000..54156698 --- /dev/null +++ b/recipes-extended/ceph/ceph/0001-msg-async-ProtocolV2-avoid-AES-GCM-nonce-reuse-vulne.patch | |||
@@ -0,0 +1,256 @@ | |||
1 | From 20b7bb685c5ea74c651ca1ea547ac66b0fee7035 Mon Sep 17 00:00:00 2001 | ||
2 | From: Ilya Dryomov <idryomov@gmail.com> | ||
3 | Date: Fri, 6 Mar 2020 20:16:45 +0100 | ||
4 | Subject: [PATCH] msg/async/ProtocolV2: avoid AES-GCM nonce reuse | ||
5 | vulnerabilities | ||
6 | |||
7 | The secure mode uses AES-128-GCM with 96-bit nonces consisting of a | ||
8 | 32-bit counter followed by a 64-bit salt. The counter is incremented | ||
9 | after processing each frame, the salt is fixed for the duration of | ||
10 | the session. Both are initialized from the session key generated | ||
11 | during session negotiation, so the counter starts with essentially | ||
12 | a random value. It is allowed to wrap, and, after 2**32 frames, it | ||
13 | repeats, resulting in nonce reuse (the actual sequence numbers that | ||
14 | the messenger works with are 64-bit, so the session continues on). | ||
15 | |||
16 | Because of how GCM works, this completely breaks both confidentiality | ||
17 | and integrity aspects of the secure mode. A single nonce reuse reveals | ||
18 | the XOR of two plaintexts and almost completely reveals the subkey | ||
19 | used for producing authentication tags. After a few nonces get used | ||
20 | twice, all confidentiality and integrity goes out the window and the | ||
21 | attacker can potentially encrypt-authenticate plaintext of their | ||
22 | choice. | ||
23 | |||
24 | We can't easily change the nonce format to extend the counter to | ||
25 | 64 bits (and possibly XOR it with a longer salt). Instead, just | ||
26 | remember the initial nonce and cut the session before it repeats, | ||
27 | forcing renegotiation. | ||
28 | |||
29 | Signed-off-by: Ilya Dryomov <idryomov@gmail.com> | ||
30 | Reviewed-by: Radoslaw Zarzynski <rzarzyns@redhat.com> | ||
31 | Reviewed-by: Sage Weil <sage@redhat.com> | ||
32 | |||
33 | Conflicts: | ||
34 | src/msg/async/ProtocolV2.h [ context: commit ed3ec4c01d17 | ||
35 | ("msg: Build target 'common' without using namespace in | ||
36 | headers") not in octopus ] | ||
37 | |||
38 | CVE: CVE-2020-1759 | ||
39 | Upstream Status: Backport [20b7bb685c5ea74c651ca1ea547ac66b0fee7035] | ||
40 | |||
41 | Signed-off-by: Sakib Sajal <sakib.sajal@windriver.com> | ||
42 | --- | ||
43 | src/msg/async/ProtocolV2.cc | 62 ++++++++++++++++++++++++---------- | ||
44 | src/msg/async/ProtocolV2.h | 5 +-- | ||
45 | src/msg/async/crypto_onwire.cc | 17 ++++++++-- | ||
46 | src/msg/async/crypto_onwire.h | 5 +++ | ||
47 | 4 files changed, 67 insertions(+), 22 deletions(-) | ||
48 | |||
49 | diff --git a/src/msg/async/ProtocolV2.cc b/src/msg/async/ProtocolV2.cc | ||
50 | index 8fc02db6e5..c69f2ccf79 100644 | ||
51 | --- a/src/msg/async/ProtocolV2.cc | ||
52 | +++ b/src/msg/async/ProtocolV2.cc | ||
53 | @@ -533,7 +533,10 @@ ssize_t ProtocolV2::write_message(Message *m, bool more) { | ||
54 | m->get_payload(), | ||
55 | m->get_middle(), | ||
56 | m->get_data()); | ||
57 | - connection->outgoing_bl.append(message.get_buffer(session_stream_handlers)); | ||
58 | + if (!append_frame(message)) { | ||
59 | + m->put(); | ||
60 | + return -EILSEQ; | ||
61 | + } | ||
62 | |||
63 | ldout(cct, 5) << __func__ << " sending message m=" << m | ||
64 | << " seq=" << m->get_seq() << " " << *m << dendl; | ||
65 | @@ -566,15 +569,17 @@ ssize_t ProtocolV2::write_message(Message *m, bool more) { | ||
66 | return rc; | ||
67 | } | ||
68 | |||
69 | -void ProtocolV2::append_keepalive() { | ||
70 | - ldout(cct, 10) << __func__ << dendl; | ||
71 | - auto keepalive_frame = KeepAliveFrame::Encode(); | ||
72 | - connection->outgoing_bl.append(keepalive_frame.get_buffer(session_stream_handlers)); | ||
73 | -} | ||
74 | - | ||
75 | -void ProtocolV2::append_keepalive_ack(utime_t ×tamp) { | ||
76 | - auto keepalive_ack_frame = KeepAliveFrameAck::Encode(timestamp); | ||
77 | - connection->outgoing_bl.append(keepalive_ack_frame.get_buffer(session_stream_handlers)); | ||
78 | +template <class F> | ||
79 | +bool ProtocolV2::append_frame(F& frame) { | ||
80 | + ceph::bufferlist bl; | ||
81 | + try { | ||
82 | + bl = frame.get_buffer(session_stream_handlers); | ||
83 | + } catch (ceph::crypto::onwire::TxHandlerError &e) { | ||
84 | + ldout(cct, 1) << __func__ << " " << e.what() << dendl; | ||
85 | + return false; | ||
86 | + } | ||
87 | + connection->outgoing_bl.append(bl); | ||
88 | + return true; | ||
89 | } | ||
90 | |||
91 | void ProtocolV2::handle_message_ack(uint64_t seq) { | ||
92 | @@ -612,7 +617,15 @@ void ProtocolV2::write_event() { | ||
93 | connection->write_lock.lock(); | ||
94 | if (can_write) { | ||
95 | if (keepalive) { | ||
96 | - append_keepalive(); | ||
97 | + ldout(cct, 10) << __func__ << " appending keepalive" << dendl; | ||
98 | + auto keepalive_frame = KeepAliveFrame::Encode(); | ||
99 | + if (!append_frame(keepalive_frame)) { | ||
100 | + connection->write_lock.unlock(); | ||
101 | + connection->lock.lock(); | ||
102 | + fault(); | ||
103 | + connection->lock.unlock(); | ||
104 | + return; | ||
105 | + } | ||
106 | keepalive = false; | ||
107 | } | ||
108 | |||
109 | @@ -663,13 +676,16 @@ void ProtocolV2::write_event() { | ||
110 | if (r == 0) { | ||
111 | uint64_t left = ack_left; | ||
112 | if (left) { | ||
113 | - auto ack = AckFrame::Encode(in_seq); | ||
114 | - connection->outgoing_bl.append(ack.get_buffer(session_stream_handlers)); | ||
115 | ldout(cct, 10) << __func__ << " try send msg ack, acked " << left | ||
116 | << " messages" << dendl; | ||
117 | - ack_left -= left; | ||
118 | - left = ack_left; | ||
119 | - r = connection->_try_send(left); | ||
120 | + auto ack_frame = AckFrame::Encode(in_seq); | ||
121 | + if (append_frame(ack_frame)) { | ||
122 | + ack_left -= left; | ||
123 | + left = ack_left; | ||
124 | + r = connection->_try_send(left); | ||
125 | + } else { | ||
126 | + r = -EILSEQ; | ||
127 | + } | ||
128 | } else if (is_queued()) { | ||
129 | r = connection->_try_send(); | ||
130 | } | ||
131 | @@ -769,7 +785,13 @@ template <class F> | ||
132 | CtPtr ProtocolV2::write(const std::string &desc, | ||
133 | CONTINUATION_TYPE<ProtocolV2> &next, | ||
134 | F &frame) { | ||
135 | - ceph::bufferlist bl = frame.get_buffer(session_stream_handlers); | ||
136 | + ceph::bufferlist bl; | ||
137 | + try { | ||
138 | + bl = frame.get_buffer(session_stream_handlers); | ||
139 | + } catch (ceph::crypto::onwire::TxHandlerError &e) { | ||
140 | + ldout(cct, 1) << __func__ << " " << e.what() << dendl; | ||
141 | + return _fault(); | ||
142 | + } | ||
143 | return write(desc, next, bl); | ||
144 | } | ||
145 | |||
146 | @@ -1672,7 +1694,11 @@ CtPtr ProtocolV2::handle_keepalive2(ceph::bufferlist &payload) | ||
147 | ldout(cct, 30) << __func__ << " got KEEPALIVE2 tag ..." << dendl; | ||
148 | |||
149 | connection->write_lock.lock(); | ||
150 | - append_keepalive_ack(keepalive_frame.timestamp()); | ||
151 | + auto keepalive_ack_frame = KeepAliveFrameAck::Encode(keepalive_frame.timestamp()); | ||
152 | + if (!append_frame(keepalive_ack_frame)) { | ||
153 | + connection->write_lock.unlock(); | ||
154 | + return _fault(); | ||
155 | + } | ||
156 | connection->write_lock.unlock(); | ||
157 | |||
158 | ldout(cct, 20) << __func__ << " got KEEPALIVE2 " | ||
159 | diff --git a/src/msg/async/ProtocolV2.h b/src/msg/async/ProtocolV2.h | ||
160 | index 2dbe647ae5..9897d18cf2 100644 | ||
161 | --- a/src/msg/async/ProtocolV2.h | ||
162 | +++ b/src/msg/async/ProtocolV2.h | ||
163 | @@ -129,6 +129,9 @@ private: | ||
164 | CONTINUATION_TYPE<ProtocolV2> &next, | ||
165 | bufferlist &buffer); | ||
166 | |||
167 | + template <class F> | ||
168 | + bool append_frame(F& frame); | ||
169 | + | ||
170 | void requeue_sent(); | ||
171 | uint64_t discard_requeued_up_to(uint64_t out_seq, uint64_t seq); | ||
172 | void reset_recv_state(); | ||
173 | @@ -140,8 +143,6 @@ private: | ||
174 | void prepare_send_message(uint64_t features, Message *m); | ||
175 | out_queue_entry_t _get_next_outgoing(); | ||
176 | ssize_t write_message(Message *m, bool more); | ||
177 | - void append_keepalive(); | ||
178 | - void append_keepalive_ack(utime_t ×tamp); | ||
179 | void handle_message_ack(uint64_t seq); | ||
180 | |||
181 | CONTINUATION_DECL(ProtocolV2, _wait_for_peer_banner); | ||
182 | diff --git a/src/msg/async/crypto_onwire.cc b/src/msg/async/crypto_onwire.cc | ||
183 | index acf3f66689..07e7fe6553 100644 | ||
184 | --- a/src/msg/async/crypto_onwire.cc | ||
185 | +++ b/src/msg/async/crypto_onwire.cc | ||
186 | @@ -22,6 +22,10 @@ static constexpr const std::size_t AESGCM_BLOCK_LEN{16}; | ||
187 | struct nonce_t { | ||
188 | std::uint32_t random_seq; | ||
189 | std::uint64_t random_rest; | ||
190 | + | ||
191 | + bool operator==(const nonce_t& rhs) const { | ||
192 | + return !memcmp(this, &rhs, sizeof(*this)); | ||
193 | + } | ||
194 | } __attribute__((packed)); | ||
195 | static_assert(sizeof(nonce_t) == AESGCM_IV_LEN); | ||
196 | |||
197 | @@ -35,7 +39,8 @@ class AES128GCM_OnWireTxHandler : public ceph::crypto::onwire::TxHandler { | ||
198 | CephContext* const cct; | ||
199 | std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)> ectx; | ||
200 | ceph::bufferlist buffer; | ||
201 | - nonce_t nonce; | ||
202 | + nonce_t nonce, initial_nonce; | ||
203 | + bool used_initial_nonce; | ||
204 | static_assert(sizeof(nonce) == AESGCM_IV_LEN); | ||
205 | |||
206 | public: | ||
207 | @@ -44,7 +49,7 @@ public: | ||
208 | const nonce_t& nonce) | ||
209 | : cct(cct), | ||
210 | ectx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free), | ||
211 | - nonce(nonce) { | ||
212 | + nonce(nonce), initial_nonce(nonce), used_initial_nonce(false) { | ||
213 | ceph_assert_always(ectx); | ||
214 | ceph_assert_always(key.size() * CHAR_BIT == 128); | ||
215 | |||
216 | @@ -61,6 +66,7 @@ public: | ||
217 | |||
218 | ~AES128GCM_OnWireTxHandler() override { | ||
219 | ::ceph::crypto::zeroize_for_security(&nonce, sizeof(nonce)); | ||
220 | + ::ceph::crypto::zeroize_for_security(&initial_nonce, sizeof(initial_nonce)); | ||
221 | } | ||
222 | |||
223 | std::uint32_t calculate_segment_size(std::uint32_t size) override | ||
224 | @@ -78,6 +84,13 @@ public: | ||
225 | void AES128GCM_OnWireTxHandler::reset_tx_handler( | ||
226 | std::initializer_list<std::uint32_t> update_size_sequence) | ||
227 | { | ||
228 | + if (nonce == initial_nonce) { | ||
229 | + if (used_initial_nonce) { | ||
230 | + throw ceph::crypto::onwire::TxHandlerError("out of nonces"); | ||
231 | + } | ||
232 | + used_initial_nonce = true; | ||
233 | + } | ||
234 | + | ||
235 | if(1 != EVP_EncryptInit_ex(ectx.get(), nullptr, nullptr, nullptr, | ||
236 | reinterpret_cast<const unsigned char*>(&nonce))) { | ||
237 | throw std::runtime_error("EVP_EncryptInit_ex failed"); | ||
238 | diff --git a/src/msg/async/crypto_onwire.h b/src/msg/async/crypto_onwire.h | ||
239 | index bd682e8c71..0c544f205a 100644 | ||
240 | --- a/src/msg/async/crypto_onwire.h | ||
241 | +++ b/src/msg/async/crypto_onwire.h | ||
242 | @@ -45,6 +45,11 @@ struct MsgAuthError : public std::runtime_error { | ||
243 | } | ||
244 | }; | ||
245 | |||
246 | +struct TxHandlerError : public std::runtime_error { | ||
247 | + TxHandlerError(const char* what) | ||
248 | + : std::runtime_error(std::string("tx handler error: ") + what) {} | ||
249 | +}; | ||
250 | + | ||
251 | struct TxHandler { | ||
252 | virtual ~TxHandler() = default; | ||
253 | |||
254 | -- | ||
255 | 2.20.1 | ||
256 | |||
diff --git a/recipes-extended/ceph/ceph/0001-msg-async-crypto_onwire-fix-endianness-of-nonce_t.patch b/recipes-extended/ceph/ceph/0001-msg-async-crypto_onwire-fix-endianness-of-nonce_t.patch new file mode 100644 index 00000000..ad8a2055 --- /dev/null +++ b/recipes-extended/ceph/ceph/0001-msg-async-crypto_onwire-fix-endianness-of-nonce_t.patch | |||
@@ -0,0 +1,61 @@ | |||
1 | From dfd1d81cec62e21e21696dc87d4db5f920e51a67 Mon Sep 17 00:00:00 2001 | ||
2 | From: Ilya Dryomov <idryomov@gmail.com> | ||
3 | Date: Fri, 6 Mar 2020 20:16:45 +0100 | ||
4 | Subject: [PATCH] msg/async/crypto_onwire: fix endianness of nonce_t | ||
5 | |||
6 | As a AES-GCM IV, nonce_t is implicitly shared between server and | ||
7 | client. Currently, if their endianness doesn't match, they are unable | ||
8 | to communicate in secure mode because each gets its own idea of what | ||
9 | the next nonce should be after the counter is incremented. | ||
10 | |||
11 | Several RFCs state that the nonce counter should be BE, but since we | ||
12 | use LE for everything on-disk and on-wire, make it LE. | ||
13 | |||
14 | Signed-off-by: Ilya Dryomov <idryomov@gmail.com> | ||
15 | Reviewed-by: Radoslaw Zarzynski <rzarzyns@redhat.com> | ||
16 | Reviewed-by: Sage Weil <sage@redhat.com> | ||
17 | |||
18 | CVE: CVE-2020-1759 | ||
19 | Upstream Status: Backport [dfd1d81cec62e21e21696dc87d4db5f920e51a67] | ||
20 | |||
21 | Signed-off-by: Sakib Sajal <sakib.sajal@windriver.com> | ||
22 | --- | ||
23 | src/msg/async/crypto_onwire.cc | 8 ++++---- | ||
24 | 1 file changed, 4 insertions(+), 4 deletions(-) | ||
25 | |||
26 | diff --git a/src/msg/async/crypto_onwire.cc b/src/msg/async/crypto_onwire.cc | ||
27 | index 07e7fe6553..c39632cbd6 100644 | ||
28 | --- a/src/msg/async/crypto_onwire.cc | ||
29 | +++ b/src/msg/async/crypto_onwire.cc | ||
30 | @@ -20,8 +20,8 @@ static constexpr const std::size_t AESGCM_TAG_LEN{16}; | ||
31 | static constexpr const std::size_t AESGCM_BLOCK_LEN{16}; | ||
32 | |||
33 | struct nonce_t { | ||
34 | - std::uint32_t random_seq; | ||
35 | - std::uint64_t random_rest; | ||
36 | + ceph_le32 random_seq; | ||
37 | + ceph_le64 random_rest; | ||
38 | |||
39 | bool operator==(const nonce_t& rhs) const { | ||
40 | return !memcmp(this, &rhs, sizeof(*this)); | ||
41 | @@ -99,7 +99,7 @@ void AES128GCM_OnWireTxHandler::reset_tx_handler( | ||
42 | buffer.reserve(std::accumulate(std::begin(update_size_sequence), | ||
43 | std::end(update_size_sequence), AESGCM_TAG_LEN)); | ||
44 | |||
45 | - ++nonce.random_seq; | ||
46 | + nonce.random_seq = nonce.random_seq + 1; | ||
47 | } | ||
48 | |||
49 | void AES128GCM_OnWireTxHandler::authenticated_encrypt_update( | ||
50 | @@ -204,7 +204,7 @@ void AES128GCM_OnWireRxHandler::reset_rx_handler() | ||
51 | reinterpret_cast<const unsigned char*>(&nonce))) { | ||
52 | throw std::runtime_error("EVP_DecryptInit_ex failed"); | ||
53 | } | ||
54 | - ++nonce.random_seq; | ||
55 | + nonce.random_seq = nonce.random_seq + 1; | ||
56 | } | ||
57 | |||
58 | ceph::bufferlist AES128GCM_OnWireRxHandler::authenticated_decrypt_update( | ||
59 | -- | ||
60 | 2.20.1 | ||
61 | |||
diff --git a/recipes-extended/ceph/ceph/0001-rgw-EPERM-to-ERR_INVALID_REQUEST.patch b/recipes-extended/ceph/ceph/0001-rgw-EPERM-to-ERR_INVALID_REQUEST.patch new file mode 100644 index 00000000..30906d7c --- /dev/null +++ b/recipes-extended/ceph/ceph/0001-rgw-EPERM-to-ERR_INVALID_REQUEST.patch | |||
@@ -0,0 +1,33 @@ | |||
1 | From 92da834cababc4dddd5dbbab5837310478d1e6d4 Mon Sep 17 00:00:00 2001 | ||
2 | From: Abhishek Lekshmanan <abhishek@suse.com> | ||
3 | Date: Fri, 27 Mar 2020 19:29:01 +0100 | ||
4 | Subject: [PATCH] rgw: EPERM to ERR_INVALID_REQUEST | ||
5 | |||
6 | As per Robin's comments and S3 spec | ||
7 | |||
8 | Signed-off-by: Abhishek Lekshmanan <abhishek@suse.com> | ||
9 | |||
10 | CVE: CVE-2020-1760 | ||
11 | Upstream Status: Backport [92da834cababc4dddd5dbbab5837310478d1e6d4] | ||
12 | |||
13 | Signed-off-by: Sakib Sajal <sakib.sajal@windriver.com> | ||
14 | --- | ||
15 | src/rgw/rgw_rest_s3.cc | 2 +- | ||
16 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
17 | |||
18 | diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc | ||
19 | index 1bfc8312de..f13ae23dd6 100644 | ||
20 | --- a/src/rgw/rgw_rest_s3.cc | ||
21 | +++ b/src/rgw/rgw_rest_s3.cc | ||
22 | @@ -301,7 +301,7 @@ int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs, | ||
23 | /* reject unauthenticated response header manipulation, see | ||
24 | * https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html */ | ||
25 | if (s->auth.identity->is_anonymous()) { | ||
26 | - return -EPERM; | ||
27 | + return -ERR_INVALID_REQUEST; | ||
28 | } | ||
29 | if (strcmp(p->param, "response-content-type") != 0) { | ||
30 | response_attrs[p->http_attr] = val; | ||
31 | -- | ||
32 | 2.20.1 | ||
33 | |||
diff --git a/recipes-extended/ceph/ceph/0001-rgw-reject-control-characters-in-response-header-act.patch b/recipes-extended/ceph/ceph/0001-rgw-reject-control-characters-in-response-header-act.patch new file mode 100644 index 00000000..af0fc79a --- /dev/null +++ b/recipes-extended/ceph/ceph/0001-rgw-reject-control-characters-in-response-header-act.patch | |||
@@ -0,0 +1,64 @@ | |||
1 | From be7679007c3dfab3e19c22c38c36ccac91828e3b Mon Sep 17 00:00:00 2001 | ||
2 | From: "Robin H. Johnson" <rjohnson@digitalocean.com> | ||
3 | Date: Fri, 27 Mar 2020 20:48:13 +0100 | ||
4 | Subject: [PATCH] rgw: reject control characters in response-header actions | ||
5 | |||
6 | S3 GetObject permits overriding response header values, but those inputs | ||
7 | need to be validated to insure only characters that are valid in an HTTP | ||
8 | header value are present. | ||
9 | |||
10 | Credit: Initial vulnerability discovery by William Bowling (@wcbowling) | ||
11 | Credit: Further vulnerability discovery by Robin H. Johnson <rjohnson@digitalocean.com> | ||
12 | Signed-off-by: Robin H. Johnson <rjohnson@digitalocean.com> | ||
13 | |||
14 | CVE: CVE-2020-1760 | ||
15 | Upstream Status: Backport [be7679007c3dfab3e19c22c38c36ccac91828e3b] | ||
16 | |||
17 | Signed-off-by: Sakib Sajal <sakib.sajal@windriver.com> | ||
18 | --- | ||
19 | src/rgw/rgw_rest_s3.cc | 22 ++++++++++++++++++++++ | ||
20 | 1 file changed, 22 insertions(+) | ||
21 | |||
22 | diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc | ||
23 | index f13ae23dd6..0de040968c 100644 | ||
24 | --- a/src/rgw/rgw_rest_s3.cc | ||
25 | +++ b/src/rgw/rgw_rest_s3.cc | ||
26 | @@ -189,6 +189,15 @@ int decode_attr_bl_single_value(map<string, bufferlist>& attrs, const char *attr | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | +inline bool str_has_cntrl(const std::string s) { | ||
31 | + return std::any_of(s.begin(), s.end(), ::iscntrl); | ||
32 | +} | ||
33 | + | ||
34 | +inline bool str_has_cntrl(const char* s) { | ||
35 | + std::string _s(s); | ||
36 | + return str_has_cntrl(_s); | ||
37 | +} | ||
38 | + | ||
39 | int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs, | ||
40 | off_t bl_len) | ||
41 | { | ||
42 | @@ -303,6 +312,19 @@ int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs, | ||
43 | if (s->auth.identity->is_anonymous()) { | ||
44 | return -ERR_INVALID_REQUEST; | ||
45 | } | ||
46 | + /* HTTP specification says no control characters should be present in | ||
47 | + * header values: https://tools.ietf.org/html/rfc7230#section-3.2 | ||
48 | + * field-vchar = VCHAR / obs-text | ||
49 | + * | ||
50 | + * Failure to validate this permits a CRLF injection in HTTP headers, | ||
51 | + * whereas S3 GetObject only permits specific headers. | ||
52 | + */ | ||
53 | + if(str_has_cntrl(val)) { | ||
54 | + /* TODO: return a more distinct error in future; | ||
55 | + * stating what the problem is */ | ||
56 | + return -ERR_INVALID_REQUEST; | ||
57 | + } | ||
58 | + | ||
59 | if (strcmp(p->param, "response-content-type") != 0) { | ||
60 | response_attrs[p->http_attr] = val; | ||
61 | } else { | ||
62 | -- | ||
63 | 2.20.1 | ||
64 | |||
diff --git a/recipes-extended/ceph/ceph/0001-rgw-reject-unauthenticated-response-header-actions.patch b/recipes-extended/ceph/ceph/0001-rgw-reject-unauthenticated-response-header-actions.patch new file mode 100644 index 00000000..ae241473 --- /dev/null +++ b/recipes-extended/ceph/ceph/0001-rgw-reject-unauthenticated-response-header-actions.patch | |||
@@ -0,0 +1,36 @@ | |||
1 | From 8f90658c731499722d5f4393c8ad70b971d05f77 Mon Sep 17 00:00:00 2001 | ||
2 | From: Matt Benjamin <mbenjamin@redhat.com> | ||
3 | Date: Fri, 27 Mar 2020 18:13:48 +0100 | ||
4 | Subject: [PATCH] rgw: reject unauthenticated response-header actions | ||
5 | |||
6 | Signed-off-by: Matt Benjamin <mbenjamin@redhat.com> | ||
7 | Reviewed-by: Casey Bodley <cbodley@redhat.com> | ||
8 | (cherry picked from commit d8dd5e513c0c62bbd7d3044d7e2eddcd897bd400) | ||
9 | |||
10 | CVE: CVE-2020-1760 | ||
11 | Upstream Status: Backport [8f90658c731499722d5f4393c8ad70b971d05f77] | ||
12 | |||
13 | Signed-off-by: Sakib Sajal <sakib.sajal@windriver.com> | ||
14 | --- | ||
15 | src/rgw/rgw_rest_s3.cc | 5 +++++ | ||
16 | 1 file changed, 5 insertions(+) | ||
17 | |||
18 | diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc | ||
19 | index 532d738b58..1bfc8312de 100644 | ||
20 | --- a/src/rgw/rgw_rest_s3.cc | ||
21 | +++ b/src/rgw/rgw_rest_s3.cc | ||
22 | @@ -298,6 +298,11 @@ int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs, | ||
23 | bool exists; | ||
24 | string val = s->info.args.get(p->param, &exists); | ||
25 | if (exists) { | ||
26 | + /* reject unauthenticated response header manipulation, see | ||
27 | + * https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html */ | ||
28 | + if (s->auth.identity->is_anonymous()) { | ||
29 | + return -EPERM; | ||
30 | + } | ||
31 | if (strcmp(p->param, "response-content-type") != 0) { | ||
32 | response_attrs[p->http_attr] = val; | ||
33 | } else { | ||
34 | -- | ||
35 | 2.20.1 | ||
36 | |||
diff --git a/recipes-extended/ceph/ceph_15.2.0.bb b/recipes-extended/ceph/ceph_15.2.0.bb index 8ab58eb9..e41aa2f4 100644 --- a/recipes-extended/ceph/ceph_15.2.0.bb +++ b/recipes-extended/ceph/ceph_15.2.0.bb | |||
@@ -12,6 +12,11 @@ SRC_URI = "http://download.ceph.com/tarballs/ceph-${PV}.tar.gz \ | |||
12 | file://0001-ceph-fix-build-errors-for-cross-compile.patch \ | 12 | file://0001-ceph-fix-build-errors-for-cross-compile.patch \ |
13 | file://0001-fix-host-library-paths-were-used.patch \ | 13 | file://0001-fix-host-library-paths-were-used.patch \ |
14 | file://ceph.conf \ | 14 | file://ceph.conf \ |
15 | file://0001-msg-async-ProtocolV2-avoid-AES-GCM-nonce-reuse-vulne.patch \ | ||
16 | file://0001-msg-async-crypto_onwire-fix-endianness-of-nonce_t.patch \ | ||
17 | file://0001-rgw-reject-unauthenticated-response-header-actions.patch \ | ||
18 | file://0001-rgw-EPERM-to-ERR_INVALID_REQUEST.patch \ | ||
19 | file://0001-rgw-reject-control-characters-in-response-header-act.patch \ | ||
15 | " | 20 | " |
16 | 21 | ||
17 | SRC_URI[md5sum] = "1f9af648b4c6d19975aab2583ab99710" | 22 | SRC_URI[md5sum] = "1f9af648b4c6d19975aab2583ab99710" |