mirror of
https://github.com/ceph/ceph
synced 2025-01-24 20:13:45 +00:00
doc: extra \ in CEPH\_AUTH\_UNKNOWN
Suppress all \ fixes: #11097 Signed-off-by: DEHU Robin <robindehu@gmail.com>
This commit is contained in:
parent
6e56438b01
commit
38bc2986d8
@ -70,7 +70,7 @@ Getting Started With Authorization
|
||||
|
||||
When the client first needs to get service, it contacts the monitor. At the moment, it has
|
||||
no tickets. Therefore, it uses the "unknown" protocol to talk to the monitor. This protocol
|
||||
is specified as ``CEPH\_AUTH\_UNKNOWN``. The monitor also takes on the authentication server
|
||||
is specified as ``CEPH_AUTH_UNKNOWN``. The monitor also takes on the authentication server
|
||||
role, A. The remainder of the communications will use the cephx protocol (most of whose code
|
||||
will be found in files in ``auth/cephx``). This protocol is responsible for creating and
|
||||
communicating the tickets spoken of above.
|
||||
@ -90,7 +90,7 @@ Phase I:
|
||||
The client is set up to know that it needs certain things, using a variable called ``need``,
|
||||
which is part of the ``AuthClientHandler`` class, which the ``CephxClientHandler`` inherits
|
||||
from. At this point, one thing that's encoded in the ``need`` variable is
|
||||
``CEPH\_ENTITY\_TYPE\_AUTH``, indicating that we need to start the authentication protocol
|
||||
``CEPH_ENTITY_TYPE_AUTH``, indicating that we need to start the authentication protocol
|
||||
from scratch. Since we're always talking to the same authorization server, if we've gone
|
||||
through this step of the protocol before (and the resulting ticket/session hasn't timed out),
|
||||
we can skip this step and just ask for client tickets. But it must be done initially, and
|
||||
@ -106,9 +106,9 @@ in the ``need`` flag as necessary. Then we call ``ticket.get_handler()``. This
|
||||
authorization) in the ticket map, creates a ticket handler object for it, and puts the
|
||||
handler into the right place in the map. Then we hit specialized code to deal with individual
|
||||
cases. The case here is when we still need to authenticate to A (the
|
||||
``if (need & CEPH\_ENTITY\_TYPE\_AUTH)`` branch).
|
||||
``if (need & CEPH_ENTITY_TYPE_AUTH)`` branch).
|
||||
|
||||
We now create a message of type ``CEPHX\_GET\_AUTH\_SESSION\_KEY``. We need to authenticate
|
||||
We now create a message of type ``CEPH_AUTH_UNKNOWN``. We need to authenticate
|
||||
this message with C's secret key, so we fetch that from the local key repository. (It's
|
||||
called a key server in the code, but it's not really a separate machine or processing entity.
|
||||
It's more like the place where locally used keys are kept.) We create a
|
||||
@ -124,12 +124,12 @@ challenges, gets put into the message. Then we return from this function, and t
|
||||
message is sent.
|
||||
|
||||
We now switch over to the authenticator side, A. The server receives the message that was
|
||||
sent, of type ``CEPHX\_GET\_AUTH\_SESSION\_KEY``. The message gets handled in ``prep_auth()``,
|
||||
sent, of type ``CEPH_AUTH_UNKNOWN``. The message gets handled in ``prep_auth()``,
|
||||
in ``mon/AuthMonitor.cc``, which calls ``handle_request()`` is ``CephxServiceHandler.cc`` to
|
||||
do most of the work. This routine, also, handles multiple cases.
|
||||
|
||||
The control flow is determined by the ``request_type`` in the ``cephx_header`` associated
|
||||
with the message. Our case here is ``CEPHX\_GET\_AUTH\_SESSION\_KEY``. We need the
|
||||
with the message. Our case here is ``CEPH_AUTH_UNKNOWN``. We need the
|
||||
secret key A shares with C, so we call ``get_secret()`` from out local key repository to get
|
||||
it. We should have set up a server challenge already with this client, so we make sure
|
||||
we really do have one. (This variable is specific to a ``CephxServiceHandler``, so there
|
||||
@ -153,32 +153,32 @@ If the attempt to decode his old ticket fails (most probably because he didn't h
|
||||
the name of C, the global ID provided in the method call (unless there was an old ticket), and
|
||||
his ``auid``, obtained from the ``eauth`` structure obtained above. We need a new session key
|
||||
to help the client communicate securely with us, not using his permanent key. We set the
|
||||
service ID to ``CEPH\_ENTITY\_TYPE\_AUTH``, which will tell the client C what to do with the
|
||||
service ID to ``CEPH_ENTITY_TYPE_AUTH``, which will tell the client C what to do with the
|
||||
message we send it. We build a cephx response header and call
|
||||
``cephx\_build\_service\_ticket\_reply()``.
|
||||
``cephx_build_service_ticket_reply()``.
|
||||
|
||||
``cephx\_build\_service\_ticket\_reply()`` is in ``auth/cephx/CephxProtocol.cc``. This
|
||||
``cephx_build_service_ticket_reply()`` is in ``auth/cephx/CephxProtocol.cc``. This
|
||||
routine will build up the response message. Much of it copies data from its parameters to
|
||||
a message structure. Part of that information (the session key and the validity period)
|
||||
gets encrypted with C's permanent key. If the ``should\_encrypt\_ticket`` flag is set,
|
||||
gets encrypted with C's permanent key. If the ``should_encrypt_ticket`` flag is set,
|
||||
encrypt it using the old ticket's key. Otherwise, there was no old ticket key, so the
|
||||
new ticket is not encrypted. (It is, of course, already encrypted with A's permanent key.)
|
||||
Presumably the point of this second encryption is to expose less material encrypted with
|
||||
permanent keys.
|
||||
|
||||
Then we call the key server's ``get\_service\_caps()`` routine on the entity name, with a
|
||||
flag ``CEPH\_ENTITY\_TYPE\_MON``, and capabilities, which will be filled in by this routine.
|
||||
Then we call the key server's ``get_service_caps()`` routine on the entity name, with a
|
||||
flag ``CEPH_ENTITY_TYPE_MON``, and capabilities, which will be filled in by this routine.
|
||||
The use of that constant flag means we're going to get the client's caps for A, not for some
|
||||
other data server. The ticket here is to access the authorizer A, not the service S. The
|
||||
result of this call is that the caps variable (a parameter to the routine we're in) is
|
||||
filled in with the monitor capabilities that will allow C to access A's authorization services.
|
||||
|
||||
``handle\_request()`` itself does not send the response message. It builds up the
|
||||
``result\_bl``, which basically holds that message's contents, and the capabilities structure,
|
||||
but it doesn't send the message. We go back to ``prep\_auth()``, in ``mon/AuthMonitor.cc``,
|
||||
``handle_request()`` itself does not send the response message. It builds up the
|
||||
``result_bl``, which basically holds that message's contents, and the capabilities structure,
|
||||
but it doesn't send the message. We go back to ``prep_auth()``, in ``mon/AuthMonitor.cc``,
|
||||
for that. This routine does some fiddling around with the caps structure that just got
|
||||
filled in. There's a global ID that comes up as a result of this fiddling that is put into
|
||||
the reply message. The reply message is built here (mostly from the ``response\_bl`` buffer)
|
||||
the reply message. The reply message is built here (mostly from the ``response_bl`` buffer)
|
||||
and sent off.
|
||||
|
||||
This completes Phase I of the protocol. At this point, C has authenticated himself to A, and A has generated a new session key and ticket allowing C to obtain server tickets from A.
|
||||
@ -190,16 +190,16 @@ This phase starts when C receives the message from A containing a new ticket and
|
||||
The goal of this phase is to provide A with a session key and ticket allowing him to
|
||||
communicate with S.
|
||||
|
||||
The message A sent to C is dispatched to ``build\_request()`` in ``CephxClientHandler.cc``,
|
||||
The message A sent to C is dispatched to ``build_request()`` in ``CephxClientHandler.cc``,
|
||||
the same routine that was used early in Phase I to build the first message in the protocol.
|
||||
This time, when ``validate\_tickets()`` is called, the ``need`` variable will not contain
|
||||
``CEPH\_ENTITY\_TYPE\_AUTH``, so a different branch through the bulk of the routine will be
|
||||
This time, when ``validate_tickets()`` is called, the ``need`` variable will not contain
|
||||
``CEPH_ENTITY_TYPE_AUTH``, so a different branch through the bulk of the routine will be
|
||||
used. This is the branch indicated by ``if (need)``. We have a ticket for the authorizer,
|
||||
but we still need service tickets.
|
||||
|
||||
We must send another message to A to obtain the tickets (and session key) for the server
|
||||
S. We set the ``request\_type`` of the message to ``CEPHX\_GET\_PRINCIPAL\_SESSION\_KEY`` and
|
||||
call ``ticket\_handler.build\_authorizer()`` to obtain an authorizer. This routine is in
|
||||
S. We set the ``request_type`` of the message to ``CEPHX_GET_PRINCIPAL_SESSION_KEY`` and
|
||||
call ``ticket_handler.build_authorizer()`` to obtain an authorizer. This routine is in
|
||||
``CephxProtocol.cc``. We set the key for this authorizer to be the session key we just got
|
||||
from A,and create a new nonce. We put the global ID, the service ID, and the ticket into a
|
||||
message buffer that is part of the authorizer. Then we create a new ``CephXAuthorize``
|
||||
@ -207,20 +207,20 @@ structure. The nonce we just created goes there. We encrypt this ``CephXAuthor
|
||||
structure with the current session key and stuff it into the authorizer's buffer. We
|
||||
return the authorizer.
|
||||
|
||||
Back in ``build\_request()``, we take the part of the authorizer that was just built (its
|
||||
Back in ``build_request()``, we take the part of the authorizer that was just built (its
|
||||
buffer, not the session key or anything else) and shove it into the buffer we're creating
|
||||
for the message that will go to A. Then we delete the authorizer. We put the requirements
|
||||
for what we want in ``req.keys``, and we put ``req`` into the buffer. Then we return, and
|
||||
the message gets sent.
|
||||
|
||||
The authorizer A receives this message which is of type ``CEPHX\_GET\_PRINCIPAL\_SESSION\_KEY``.
|
||||
The authorizer A receives this message which is of type ``CEPHX_GET_PRINCIPAL_SESSION_KEY``.
|
||||
The message gets handled in ``prep_auth()``, in ``mon/AuthMonitor.cc``, which again calls
|
||||
``handle\_request()`` in ``CephxServiceHandler.cc`` to do most of the work.
|
||||
``handle_request()`` in ``CephxServiceHandler.cc`` to do most of the work.
|
||||
|
||||
In this case, ``handle\_request()`` will take the ``CEPHX\_GET\_PRINCIPAL\_SESSION\_KEY`` case.
|
||||
It will call ``cephx\_verify\_authorizer()`` in ``CephxProtocol.cc``. Here, we will grab
|
||||
In this case, ``handle_request()`` will take the ``CEPHX_GET_PRINCIPAL_SESSION_KEY`` case.
|
||||
It will call ``cephx_verify_authorizer()`` in ``CephxProtocol.cc``. Here, we will grab
|
||||
a bunch of data out of the input buffer, including the global and service IDs and the ticket
|
||||
for A. The ticket contains a ``secret\_id``, indicating which key is being used for it.
|
||||
for A. The ticket contains a ``secret_id``, indicating which key is being used for it.
|
||||
If the secret ID pulled out of the ticket was -1, the ticket does not specify which secret
|
||||
key A should use. In this case, A should use the key for the specific entity that C wants
|
||||
to contact, rather than a rotating key shared by all server entities of the same type.
|
||||
@ -236,8 +236,8 @@ this message. Use that session key to decrypt the rest of the message.
|
||||
|
||||
Create a ``CephXAuthorizeReply`` to hold our reply. Extract the nonce (which was in the stuff
|
||||
we just decrypted), add 1 to it, and put the result in the reply. Encrypt the reply and
|
||||
put it in the buffer provided in the call to ``cephx\_verify\_authorizer()`` and return
|
||||
to ``handle\`_request()``. This will be used to prove to C that A (rather than an attacker)
|
||||
put it in the buffer provided in the call to ``cephx_verify_authorizer()`` and return
|
||||
to ``handle`_request()``. This will be used to prove to C that A (rather than an attacker)
|
||||
created this response.
|
||||
|
||||
Having verified that the message is valid and from C, now we need to build him a ticket for S.
|
||||
@ -245,43 +245,43 @@ We need to know what S he wants to communicate with and what services he wants.
|
||||
ticket request that describes those things out of his message. Now run through the ticket
|
||||
request to see what he wanted. (He could potentially be asking for multiple different
|
||||
services in the same request, but we will assume it's just one, for this discussion.) Once we
|
||||
know which service ID he's after, call ``build\_session\_auth\_info()``.
|
||||
know which service ID he's after, call ``build_session_auth_info()``.
|
||||
|
||||
``build\_session\_auth\_info()`` is in ``CephxKeyServer.cc``. It checks to see if the
|
||||
secret for the ``service\_ID`` of S is available and puts it into the subfield of one of
|
||||
the parameters, and calls the similarly named ``\_build\_session\_auth\_info()``, located in
|
||||
the same file. This routine loads up the new ``auth\_info`` structure with the
|
||||
``build_session_auth_info()`` is in ``CephxKeyServer.cc``. It checks to see if the
|
||||
secret for the ``service_ID`` of S is available and puts it into the subfield of one of
|
||||
the parameters, and calls the similarly named ``_build_session_auth_info()``, located in
|
||||
the same file. This routine loads up the new ``auth_info`` structure with the
|
||||
ID of S, a ticket, and some timestamps for that ticket. It generates a new session key
|
||||
and puts it in the structure. It then calls ``get\_caps()`` to fill in the
|
||||
``info.ticket`` caps field. ``get\_caps()`` is also in ``CephxKeyServer.cc``. It fills the
|
||||
``caps\_info`` structure it is provided with caps for S allowed to C.
|
||||
and puts it in the structure. It then calls ``get_caps()`` to fill in the
|
||||
``info.ticket`` caps field. ``get_caps()`` is also in ``CephxKeyServer.cc``. It fills the
|
||||
``caps_info`` structure it is provided with caps for S allowed to C.
|
||||
|
||||
Once ``build\_session\_auth\_info()`` returns, A has a list of the capabilities allowed to
|
||||
Once ``build_session_auth_info()`` returns, A has a list of the capabilities allowed to
|
||||
C for S. We put a validity period based on the current TTL for this context into the info
|
||||
structure, and put it into the ``info\_vec`` structure we are preparing in response to the
|
||||
structure, and put it into the ``info_vec`` structure we are preparing in response to the
|
||||
message.
|
||||
|
||||
Now call ``build\_cephx\_response\_header()``, also in ``CephxServiceHandler.cc``. Fill in
|
||||
the ``request\_type``, which is ``CEPHX\_GET\_PRINCIPAL\_SESSION\_KEY``, a status of 0,
|
||||
Now call ``build_cephx_response_header()``, also in ``CephxServiceHandler.cc``. Fill in
|
||||
the ``request_type``, which is ``CEPHX_GET_PRINCIPAL_SESSION_KEY``, a status of 0,
|
||||
and the result buffer.
|
||||
|
||||
Now call ``cephx\_build\_service\_ticket\_reply()``, which is in ``CephxProtocol.cc``. The
|
||||
Now call ``cephx_build_service_ticket_reply()``, which is in ``CephxProtocol.cc``. The
|
||||
same routine was used towards the end of A's handling of its response in phase I. Here,
|
||||
the session key (now a session key to talk to S, not A) and the validity period for that
|
||||
key will be encrypted with the existing session key shared between C and A.
|
||||
The ``should\_encrypt\_ticket`` parameter is false here, and no key is provided for that
|
||||
The ``should_encrypt_ticket`` parameter is false here, and no key is provided for that
|
||||
encryption. The ticket in question, destined for S once C sends it there, is already
|
||||
encrypted with S's secret. So, essentially, this routine will put ID information,
|
||||
the encrypted session key, and the ticket allowing C to talk to S into the buffer to
|
||||
be sent to C.
|
||||
|
||||
After this routine returns, we exit from ``handle\_request()``, going back to ``prep\_auth()``
|
||||
After this routine returns, we exit from ``handle_request()``, going back to ``prep_auth()``
|
||||
and ultimately to the underlying message send code.
|
||||
|
||||
The client receives this message. The nonce is checked as the message passes through
|
||||
``Pipe::connect()``, which is in ``msg/SimpleMessager.cc``. In a lengthy ``while(1)`` loop in
|
||||
the middle of this routine, it gets an authorizer. If the get was successful, eventually
|
||||
it will call ``verify\_reply()``, which checks the nonce. ``connect()`` never explicitly
|
||||
it will call ``verify_reply()``, which checks the nonce. ``connect()`` never explicitly
|
||||
checks to see if it got an authorizer, which would suggest that failure to provide an
|
||||
authorizer would allow an attacker to skip checking of the nonce. However, in many places,
|
||||
if there is no authorizer, important connection fields will get set to zero, which will
|
||||
@ -289,16 +289,16 @@ ultimately cause the connection to fail to provide data. It would be worth test
|
||||
it looks like failure to provide an authorizer, which contains the nonce, would not be helpful
|
||||
to an attacker.
|
||||
|
||||
The message eventually makes its way through to ``handle\_response()``, in
|
||||
``CephxClientHandler.cc``. In this routine, we call ``get\_handler()`` to get a ticket
|
||||
The message eventually makes its way through to ``handle_response()``, in
|
||||
``CephxClientHandler.cc``. In this routine, we call ``get_handler()`` to get a ticket
|
||||
handler to hold the ticket we have just received. This routine is embedded in the definition
|
||||
for a ``CephXTicketManager`` structure. It takes a type (``CEPH\_ENTITY\_TYPE\_AUTH``, in
|
||||
this case) and looks through the ``tickets\_map`` to find that type. There should be one, and
|
||||
for a ``CephXTicketManager`` structure. It takes a type (``CEPH_ENTITY_TYPE_AUTH``, in
|
||||
this case) and looks through the ``tickets_map`` to find that type. There should be one, and
|
||||
it should have the session key of the session between C and A in its entry. This key will
|
||||
be used to decrypt the information provided by A, particularly the new session key allowing
|
||||
C to talk to S.
|
||||
|
||||
We then call ``verify\_service\_ticket\_reply()``, in ``CephxProtocol.cc``. This routine
|
||||
We then call ``verify_service_ticket_reply()``, in ``CephxProtocol.cc``. This routine
|
||||
needs to determine if the ticket is OK and also obtain the session key associated with this
|
||||
ticket. It decrypts the encrypted portion of the message buffer, using the session key
|
||||
shared with A. This ticket was not encrypted (well, not twice - tickets are always encrypted,
|
||||
@ -309,7 +309,7 @@ The stuff we decrypted with the session key shared between C and A included the
|
||||
key. That's our current session key for this ticket, so set it. Check validity and
|
||||
set the expiration times. Now return true, if we got this far.
|
||||
|
||||
Back in ``handle\_response()``, we now call ``validate\_tickets()`` to adjust what we think
|
||||
Back in ``handle_response()``, we now call ``validate_tickets()`` to adjust what we think
|
||||
we need, since we now have a ticket we didn't have before. If we've taken care of
|
||||
everything we need, we'll return 0.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user