Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [day] [month] [year] [list]
Date: Wed, 14 Apr 2021 13:22:11 -0400
From: Ana McTaggart <>
To:, Ilya Dryomov <>, 
	Sage Weil <>
Subject: CVE-2021-20288 Ceph: Unauthorized global_id reuse in cephx

Hello all,

An authentication flaw was found in ceph. When the monitor handles
CEPHX_GET_AUTH_SESSION_KEY requests, it doesn't sanitize other_keys,
allowing key reuse. An attacker who can request a global_id can exploit the
ability of any user to request a global_id previously associated with
another user, as ceph does not force the reuse of old keys to generate new
ones. The highest threat from this vulnerability is to data confidentiality
and integrity as well as system availability.

CVE-2021-20288 has been assigned for this flaw.
Upstream patches: (commits on top of 14.2.19) (commits on top of 15.2.10) (commits on top of 16.2.0)

The main patches (i.e. ones that actually close the holes) are
"auth/cephx: option to disallow unauthorized global_id (re)use" and
"auth/cephx: ignore CEPH_ENTITY_TYPE_AUTH in requested keys".

Including the original reporting email below for context, as reported by

In nautilus+, when the monitor handles CEPHX_GET_AUTH_SESSION_KEY
requests, it doesn't sanitize other_keys.  If other_keys contains
CEPH_ENTITY_TYPE_AUTH, it ends up encoding two auth tickets: the
first (possibly encrypted with the old session key) in response to
CEPHX_GET_AUTH_SESSION_KEY itself [1] and the second in response to
CEPH_ENTITY_TYPE_AUTH in other_keys [2].  The second ticket takes
effect but a) it isn't encrypted with the old session key even if
needed and b) its validity is wrong, namely auth_service_ticket_ttl
instead of auth_mon_ticket_ttl.  Unfortunately our client always sets
CEPH_ENTITY_TYPE_AUTH in other_keys [3].

CEPHX_GET_PRINCIPAL_SESSION_KEY arm has the same issue, but at least
our client doesn't set CEPH_ENTITY_TYPE_AUTH in keys in that case.

Attached is the patch I made.

Trying to assess the security impact, I realized that I don't follow
the outer logic and that there may be another much larger bug lurking
there.  I have always thought that the purpose of encrypting the new
auth ticket with the old session key was to ensure that the client is
authentic (vs someone who just snooped the old ticket when the monitor
shared it with the client or any time the client provided it back to
the monitor as part of requesting service tickets) before allowing
reuse of global_id from the old ticket.  However, after looking at the
code, I'm having doubts because we are very lax about giving out
global_ids: it looks like a client can assign itself any global_id of
its choosing by simply passing something other than 0 in MAuth message
[4,5] or AUTH_REQUEST frame [6,7] and not even bother supplying an old
ticket.  This includes obtaining a global_id currently in use by
another client, which can screw with a lot of things: idempotence
logic, out of order op detection, possibly lock ownerships and anything
else that relies on global_id being a cluster-wide unique id.

I dug into history and found that both the reuse via an old ticket,
the encryption safeguard on the new ticket and the negotiation change
that allowed clients to pass an arbitrary global_id came alongside when
cephx was being implemented in 2009, committed within a couple of days:

  "auth: reuse global_id when requesting tickets" on Nov 17, 2009

  "auth: change server side negotiation a bit" on Nov 18, 2009

  "auth: when renewing session, encrypt ticket" on Nov 19, 2009

With that negotiation change in place, the whole reuse via an old
ticket scheme together with the encryption safeguard seems rather
meaningless to me.  Am I missing something obvious?

Sage, Yehuda, do you remember the details by any chance?  The commit
messages are empty and neither of the two cephx write-ups we have shed
any light.  In fact, quite the opposite:

  "... If old_ticket is present, verify it is valid, and we
   can reuse the same global_id.  (Otherwise, a new global_id is
   assigned by the monitor.)"

But as it is (and apparently always has been), a new global_id is
assigned only if global_id == 0 in the initial handshake message/frame.
Then, the value from the handshake is potentially overridden by the
value from the old ticket, and if the old ticket isn't present, the
monitor just continues on [8]...

What was (and still is, since this aspect of auth negotiation got
carried over to msgr2) the use case for allowing a client to provide
a global_id in the initial handshake message/frame?

Should a client be allowed to assume the provided global_id if it
doesn't follow through with a ticket proving previous possession of
that global_id?


Ana McTaggart

Red Hat Product Security

Red Hat Remote <> for urgent response

M: +1 (774)279-0791 <7742790791>     IM: amctagga


Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.