]> git.proxmox.com Git - ceph.git/blame - ceph/doc/dev/cephx_protocol.rst
update ceph source to reef 18.2.1
[ceph.git] / ceph / doc / dev / cephx_protocol.rst
CommitLineData
11fdf7f2
TL
1.. _cephx_2012_peter:
2
7c673cae
FG
3============================================================
4A Detailed Description of the Cephx Authentication Protocol
5============================================================
11fdf7f2 6
7c673cae
FG
7Peter Reiher
87/13/12
9
10This document provides deeper detail on the Cephx authorization protocol whose high level flow
11is described in the memo by Yehuda (12/19/09). Because this memo discusses details of
12routines called and variables used, it represents a snapshot. The code might be changed
13subsequent to the creation of this document, and the document is not likely to be updated in
14lockstep. With luck, code comments will indicate major changes in the way the protocol is
15implemented.
16
17Introduction
18-------------
19
20The basic idea of the protocol is based on Kerberos. A client wishes to obtain something from
21a server. The server will only offer the requested service to authorized clients. Rather
22than requiring each server to deal with authentication and authorization issues, the system
23uses an authorization server. Thus, the client must first communicate with the authorization
24server to authenticate itself and to obtain credentials that will grant it access to the
25service it wants.
26
27Authorization is not the same as authentication. Authentication provides evidence that some
28party is who it claims to be. Authorization provides evidence that a particular party is
29allowed to do something. Generally, secure authorization implies secure authentication
30(since without authentication, you may authorize something for an imposter), but the reverse
31is not necessarily true. One can authenticate without authorizing. The purpose
32of this protocol is to authorize.
33
34The basic approach is to use symmetric cryptography throughout. Each client C has its own
35secret key, known only to itself and the authorization server A. Each server S has its own
36secret key, known only to itself and the authorization server A. Authorization information
37will be passed in tickets, encrypted with the secret key of the entity that offers the service.
38There will be a ticket that A gives to C, which permits C to ask A for other tickets. This
39ticket will be encrypted with A's key, since A is the one who needs to check it. There will
40later be tickets that A issues that allow C to communicate with S to ask for service. These
41tickets will be encrypted with S's key, since S needs to check them. Since we wish to provide
42security of the communications, as well, session keys are set up along with the tickets.
43Currently, those session keys are only used for authentication purposes during this protocol
44and the handshake between the client C and the server S, when the client provides its service
45ticket. They could be used for authentication or secrecy throughout, with some changes to
46the system.
47
48Several parties need to prove something to each other if this protocol is to achieve its
49desired security effects.
50
511. The client C must prove to the authenticator A that it really is C. Since everything
52is being done via messages, the client must also prove that the message proving authenticity
53is fresh, and is not being replayed by an attacker.
54
552. The authenticator A must prove to client C that it really is the authenticator. Again,
56proof that replay is not occurring is also required.
57
583. A and C must securely share a session key to be used for distribution of later
59authorization material between them. Again, no replay is allowable, and the key must be
60known only to A and C.
61
624. A must receive evidence from C that allows A to look up C's authorized operations with
63server S.
64
655. C must receive a ticket from A that will prove to S that C can perform its authorized
66operations. This ticket must be usable only by C.
67
686. C must receive from A a session key to protect the communications between C and S. The
69session key must be fresh and not the result of a replay.
70
71Getting Started With Authorization
72-----------------------------------
73
74When the client first needs to get service, it contacts the monitor. At the moment, it has
75no tickets. Therefore, it uses the "unknown" protocol to talk to the monitor. This protocol
76is specified as ``CEPH_AUTH_UNKNOWN``. The monitor also takes on the authentication server
77role, A. The remainder of the communications will use the cephx protocol (most of whose code
78will be found in files in ``auth/cephx``). This protocol is responsible for creating and
79communicating the tickets spoken of above.
80
81Currently, this document does not follow the pre-cephx protocol flow. It starts up at the
82point where the client has contacted the server and is ready to start the cephx protocol itself.
83
84Once we are in the cephx protocol, we can get the tickets. First, C needs a ticket that
85allows secure communications with A. This ticket can then be used to obtain other tickets.
86This is phase I of the protocol, and consists of a send from C to A and a response from A to C.
87Then, C needs a ticket to allow it to talk to S to get services. This is phase II of the
88protocol, and consists of a send from C to A and a response from A to C.
89
90Phase I:
91--------
92
93The client is set up to know that it needs certain things, using a variable called ``need``,
94which is part of the ``AuthClientHandler`` class, which the ``CephxClientHandler`` inherits
95from. At this point, one thing that's encoded in the ``need`` variable is
96``CEPH_ENTITY_TYPE_AUTH``, indicating that we need to start the authentication protocol
97from scratch. Since we're always talking to the same authorization server, if we've gone
98through this step of the protocol before (and the resulting ticket/session hasn't timed out),
99we can skip this step and just ask for client tickets. But it must be done initially, and
100we'll assume that we are in that state.
101
102The message C sends to A in phase I is build in ``CephxClientHandler::build_request()`` (in
103``auth/cephx/CephxClientHandler.cc``). This routine is used for more than one purpose.
104In this case, we first call ``validate_tickets()`` (from routine
1e59de90 105``CephXTicketManager::validate_tickets()`` which lives in ``auth/cephx/CephxProtocol.h``).
7c673cae
FG
106This code runs through the list of possible tickets to determine what we need, setting values
107in the ``need`` flag as necessary. Then we call ``ticket.get_handler()``. This routine
108(in ``CephxProtocol.h``) finds a ticket of the specified type (a ticket to perform
109authorization) in the ticket map, creates a ticket handler object for it, and puts the
110handler into the right place in the map. Then we hit specialized code to deal with individual
111cases. The case here is when we still need to authenticate to A (the
112``if (need & CEPH_ENTITY_TYPE_AUTH)`` branch).
113
11fdf7f2
TL
114We now create a message of type ``CEPHX_GET_AUTH_SESSION_KEY``. We need to authenticate
115this message with C's secret key, so we fetch that from the local key repository. We create
116a random challenge, whose purpose is to prevent replays. We encrypt that challenge using
117``cephx_calc_client_server_challenge()``. We already
7c673cae
FG
118have a server challenge (a similar set of random bytes, but created by the server and sent to
119the client) from our pre-cephx stage. We take both challenges and our secret key and
120produce a combined encrypted challenge value, which goes into ``req.key``.
121
122If we have an old ticket, we store it in ``req.old_ticket``. We're about to get a new one.
123
124The entire ``req`` structure, including the old ticket and the cryptographic hash of the two
125challenges, gets put into the message. Then we return from this function, and the
126message is sent.
127
128We now switch over to the authenticator side, A. The server receives the message that was
11fdf7f2 129sent, of type ``CEPH_GET_AUTH_SESSION_KEY``. The message gets handled in ``prep_auth()``,
7c673cae
FG
130in ``mon/AuthMonitor.cc``, which calls ``handle_request()`` is ``CephxServiceHandler.cc`` to
131do most of the work. This routine, also, handles multiple cases.
132
133The control flow is determined by the ``request_type`` in the ``cephx_header`` associated
11fdf7f2 134with the message. Our case here is ``CEPH_GET_AUTH_SESSION_KEY``. We need the
7c673cae 135secret key A shares with C, so we call ``get_secret()`` from out local key repository to get
11fdf7f2
TL
136it. (It's called a ``key_server`` in the code, but it's not really a separate machine or
137processing entity. It's more like the place where locally used keys are kept.) We should
138have set up a server challenge already with this client, so we make sure
7c673cae
FG
139we really do have one. (This variable is specific to a ``CephxServiceHandler``, so there
140is a different one for each such structure we create, presumably one per client A is
141dealing with.) If there is no challenge, we'll need to start over, since we need to
142check the client's crypto hash, which depends on a server challenge, in part.
143
144We now call the same routine the client used to calculate the hash, based on the same values:
145the client challenge (which is in the incoming message), the server challenge (which we saved),
146and the client's key (which we just obtained). We check to see if the client sent the same
147thing we expected. If so, we know we're talking to the right client. We know the session is
148fresh, because it used the challenge we sent it to calculate its crypto hash. So we can
149give it an authentication ticket.
150
151We fetch C's ``eauth`` structure. This contains an ID, a key, and a set of caps (capabilities).
152
11fdf7f2
TL
153The client sent us its old ticket in the message, if it had one. If
154so, we set a flag, ``should_enc_ticket``, to true and set the global
155ID to the global ID in that old ticket. If the attempt to decode its
156old ticket fails (most probably because it didn't have one),
157``should_enc_ticket`` remains false. Now we set up the new ticket,
158filling in timestamps, the name of C, and the global ID provided in the
159method call (unless there was an old ticket). We need a new session
160key to help the client communicate securely with us, not using its
161permanent key. We set the service ID to ``CEPH_ENTITY_TYPE_AUTH``,
162which will tell the client C what to do with the message we send it.
163We build a cephx response header and call
7c673cae
FG
164``cephx_build_service_ticket_reply()``.
165
166``cephx_build_service_ticket_reply()`` is in ``auth/cephx/CephxProtocol.cc``. This
167routine will build up the response message. Much of it copies data from its parameters to
168a message structure. Part of that information (the session key and the validity period)
169gets encrypted with C's permanent key. If the ``should_encrypt_ticket`` flag is set,
170encrypt it using the old ticket's key. Otherwise, there was no old ticket key, so the
171new ticket is not encrypted. (It is, of course, already encrypted with A's permanent key.)
172Presumably the point of this second encryption is to expose less material encrypted with
173permanent keys.
174
175Then we call the key server's ``get_service_caps()`` routine on the entity name, with a
176flag ``CEPH_ENTITY_TYPE_MON``, and capabilities, which will be filled in by this routine.
177The use of that constant flag means we're going to get the client's caps for A, not for some
178other data server. The ticket here is to access the authorizer A, not the service S. The
179result of this call is that the caps variable (a parameter to the routine we're in) is
180filled in with the monitor capabilities that will allow C to access A's authorization services.
181
182``handle_request()`` itself does not send the response message. It builds up the
183``result_bl``, which basically holds that message's contents, and the capabilities structure,
184but it doesn't send the message. We go back to ``prep_auth()``, in ``mon/AuthMonitor.cc``,
185for that. This routine does some fiddling around with the caps structure that just got
186filled in. There's a global ID that comes up as a result of this fiddling that is put into
187the reply message. The reply message is built here (mostly from the ``response_bl`` buffer)
188and sent off.
189
190This completes Phase I of the protocol. At this point, C has authenticated itself to A, and A has generated a new session key and ticket allowing C to obtain server tickets from A.
191
192Phase II
193--------
194
195This phase starts when C receives the message from A containing a new ticket and session key.
196The goal of this phase is to provide C with a session key and ticket allowing it to
197communicate with S.
198
199The message A sent to C is dispatched to ``build_request()`` in ``CephxClientHandler.cc``,
200the same routine that was used early in Phase I to build the first message in the protocol.
201This time, when ``validate_tickets()`` is called, the ``need`` variable will not contain
202``CEPH_ENTITY_TYPE_AUTH``, so a different branch through the bulk of the routine will be
203used. This is the branch indicated by ``if (need)``. We have a ticket for the authorizer,
204but we still need service tickets.
205
206We must send another message to A to obtain the tickets (and session key) for the server
207S. We set the ``request_type`` of the message to ``CEPHX_GET_PRINCIPAL_SESSION_KEY`` and
208call ``ticket_handler.build_authorizer()`` to obtain an authorizer. This routine is in
209``CephxProtocol.cc``. We set the key for this authorizer to be the session key we just got
210from A,and create a new nonce. We put the global ID, the service ID, and the ticket into a
211message buffer that is part of the authorizer. Then we create a new ``CephXAuthorize``
212structure. The nonce we just created goes there. We encrypt this ``CephXAuthorize``
213structure with the current session key and stuff it into the authorizer's buffer. We
214return the authorizer.
215
216Back in ``build_request()``, we take the part of the authorizer that was just built (its
217buffer, not the session key or anything else) and shove it into the buffer we're creating
218for the message that will go to A. Then we delete the authorizer. We put the requirements
219for what we want in ``req.keys``, and we put ``req`` into the buffer. Then we return, and
220the message gets sent.
221
222The authorizer A receives this message which is of type ``CEPHX_GET_PRINCIPAL_SESSION_KEY``.
223The message gets handled in ``prep_auth()``, in ``mon/AuthMonitor.cc``, which again calls
224``handle_request()`` in ``CephxServiceHandler.cc`` to do most of the work.
225
226In this case, ``handle_request()`` will take the ``CEPHX_GET_PRINCIPAL_SESSION_KEY`` case.
227It will call ``cephx_verify_authorizer()`` in ``CephxProtocol.cc``. Here, we will grab
228a bunch of data out of the input buffer, including the global and service IDs and the ticket
229for A. The ticket contains a ``secret_id``, indicating which key is being used for it.
230If the secret ID pulled out of the ticket was -1, the ticket does not specify which secret
231key A should use. In this case, A should use the key for the specific entity that C wants
232to contact, rather than a rotating key shared by all server entities of the same type.
233To get that key, A must consult the key repository to find the right key. Otherwise,
234there's already a structure obtained from the key repository to hold the necessary secret.
235Server secrets rotate on a time expiration basis (key rotation is not covered in this
236document), so run through that structure to find its current secret. Either way, A now
237knows the secret key used to create this ticket. Now decrypt the encrypted part of the
238ticket, using this key. It should be a ticket for A.
239
240The ticket also contains a session key that C should have used to encrypt other parts of
241this message. Use that session key to decrypt the rest of the message.
242
243Create a ``CephXAuthorizeReply`` to hold our reply. Extract the nonce (which was in the stuff
244we just decrypted), add 1 to it, and put the result in the reply. Encrypt the reply and
245put it in the buffer provided in the call to ``cephx_verify_authorizer()`` and return
246to ``handle_request()``. This will be used to prove to C that A (rather than an attacker)
247created this response.
248
249Having verified that the message is valid and from C, now we need to build it a ticket for S.
250We need to know what S it wants to communicate with and what services it wants. Pull the
251ticket request that describes those things out of its message. Now run through the ticket
252request to see what it wanted. (He could potentially be asking for multiple different
253services in the same request, but we will assume it's just one, for this discussion.) Once we
254know which service ID it's after, call ``build_session_auth_info()``.
255
256``build_session_auth_info()`` is in ``CephxKeyServer.cc``. It checks to see if the
257secret for the ``service_ID`` of S is available and puts it into the subfield of one of
258the parameters, and calls the similarly named ``_build_session_auth_info()``, located in
259the same file. This routine loads up the new ``auth_info`` structure with the
260ID of S, a ticket, and some timestamps for that ticket. It generates a new session key
261and puts it in the structure. It then calls ``get_caps()`` to fill in the
262``info.ticket`` caps field. ``get_caps()`` is also in ``CephxKeyServer.cc``. It fills the
263``caps_info`` structure it is provided with caps for S allowed to C.
264
265Once ``build_session_auth_info()`` returns, A has a list of the capabilities allowed to
266C for S. We put a validity period based on the current TTL for this context into the info
267structure, and put it into the ``info_vec`` structure we are preparing in response to the
268message.
269
270Now call ``build_cephx_response_header()``, also in ``CephxServiceHandler.cc``. Fill in
271the ``request_type``, which is ``CEPHX_GET_PRINCIPAL_SESSION_KEY``, a status of 0,
272and the result buffer.
273
274Now call ``cephx_build_service_ticket_reply()``, which is in ``CephxProtocol.cc``. The
275same routine was used towards the end of A's handling of its response in phase I. Here,
276the session key (now a session key to talk to S, not A) and the validity period for that
277key will be encrypted with the existing session key shared between C and A.
278The ``should_encrypt_ticket`` parameter is false here, and no key is provided for that
279encryption. The ticket in question, destined for S once C sends it there, is already
280encrypted with S's secret. So, essentially, this routine will put ID information,
281the encrypted session key, and the ticket allowing C to talk to S into the buffer to
282be sent to C.
283
284After this routine returns, we exit from ``handle_request()``, going back to ``prep_auth()``
285and ultimately to the underlying message send code.
286
287The client receives this message. The nonce is checked as the message passes through
288``Pipe::connect()``, which is in ``msg/SimpleMessager.cc``. In a lengthy ``while(1)`` loop in
289the middle of this routine, it gets an authorizer. If the get was successful, eventually
290it will call ``verify_reply()``, which checks the nonce. ``connect()`` never explicitly
291checks to see if it got an authorizer, which would suggest that failure to provide an
292authorizer would allow an attacker to skip checking of the nonce. However, in many places,
293if there is no authorizer, important connection fields will get set to zero, which will
294ultimately cause the connection to fail to provide data. It would be worth testing, but
295it looks like failure to provide an authorizer, which contains the nonce, would not be helpful
296to an attacker.
297
298The message eventually makes its way through to ``handle_response()``, in
299``CephxClientHandler.cc``. In this routine, we call ``get_handler()`` to get a ticket
300handler to hold the ticket we have just received. This routine is embedded in the definition
301for a ``CephXTicketManager`` structure. It takes a type (``CEPH_ENTITY_TYPE_AUTH``, in
302this case) and looks through the ``tickets_map`` to find that type. There should be one, and
303it should have the session key of the session between C and A in its entry. This key will
304be used to decrypt the information provided by A, particularly the new session key allowing
305C to talk to S.
306
307We then call ``verify_service_ticket_reply()``, in ``CephxProtocol.cc``. This routine
308needs to determine if the ticket is OK and also obtain the session key associated with this
309ticket. It decrypts the encrypted portion of the message buffer, using the session key
310shared with A. This ticket was not encrypted (well, not twice - tickets are always encrypted,
311but sometimes double encrypted, which this one isn't). So it can be stored in a service
312ticket buffer directly. We now grab the ticket out of that buffer.
313
314The stuff we decrypted with the session key shared between C and A included the new session
315key. That's our current session key for this ticket, so set it. Check validity and
316set the expiration times. Now return true, if we got this far.
317
318Back in ``handle_response()``, we now call ``validate_tickets()`` to adjust what we think
319we need, since we now have a ticket we didn't have before. If we've taken care of
320everything we need, we'll return 0.
321
322This ends phase II of the protocol. We have now successfully set up a ticket and session key
323for client C to talk to server S. S will know that C is who it claims to be, since A will
324verify it. C will know it is S it's talking to, again because A verified it. The only
325copies of the session key for C and S to communicate were sent encrypted under the permanent
326keys of C and S, respectively, so no other party (excepting A, who is trusted by all) knows
327that session key. The ticket will securely indicate to S what C is allowed to do, attested
328to by A. The nonces passed back and forth between A and C ensure that they have not been
329subject to a replay attack. C has not yet actually talked to S, but it is ready to.
330
331Much of the security here falls apart if one of the permanent keys is compromised. Compromise
332of C's key means that the attacker can pose as C and obtain all of C's privileges, and can
333eavesdrop on C's legitimate conversations. He can also pretend to be A, but only in
334conversations with C. Since it does not (by hypothesis) have keys for any services, he
335cannot generate any new tickets for services, though it can replay old tickets and session
336keys until S's permanent key is changed or the old tickets time out.
337
338Compromise of S's key means that the attacker can pose as S to anyone, and can eavesdrop on
339any user's conversation with S. Unless some client's key is also compromised, the attacker
340cannot generate new fake client tickets for S, since doing so requires it to authenticate
341himself as A, using the client key it doesn't know.