Session Authentication for the Cephx Protocol

Peter Reiher7/30/12

The original Cephx protocol authenticated the client to the authenticator and set up a sessionkey used to authenticate the client to the server it needs to talk to. It did not, however,authenticate the ongoing messages between the client and server. Based on the fact that theyshare a secret key, these ongoing session messages can be easily authenticated by using thekey to sign the messages.

This document describes changes to the code that allow such ongoing session authentication.The changes allow for future changes that permit other authentication protocols (and theexisting null NONE and UNKNOWN protocols) to handle signatures, but the only protocol thatactually does signatures, at the time of the writing, is the Cephx protocol.

Introduction

This code comes into play after the Cephx protocol has completed. At this point, the client andserver share a secret key. This key will be used for authentication. For other protocols, theremay or may not be such a key in place, and perhaps the actual procedures used to performsigning will be different, so the code is written to be general.

The “session” here is represented by an established pipe. For such pipes, there should be asession_security structure attached to the pipe. Whenever a message is to be sent on thepipe, code that handles the signature for this kind of session security will be called. On theother end of the pipe, code that checks this kind of session security’s message signatures willbe called. Messages that fail the signature check will not be processed further. That impliesthat the sender had better be in agreement with the receiver on the session security being used,since otherwise messages will be uniformly dropped between them.

The code is also prepared to handle encryption and decryption of session messages, which wouldadd secrecy to the integrity provided by the signatures. No protocol currently implementedencrypts the ongoing session messages, though.

For this functionality to work, several steps are required. First, the sender and receiver must havea successful run of the cephx protocol to establish a shared key. They must store that key somewherethat the pipe can get at later, to permit messages to be signed with it. Sent messages must besigned, and received messages must have their signatures checked.

The signature could be computed in a variety of ways, but currently its size is limited to 64 bits.A message’s signature is placed in its footer, in a field called sig.

The signature code in Cephx can be turned on and off at runtime, using a Ceph boolean option calledcephx_sign_messages. It is currently set to false, by default, so no messages will be signed. Itmust be changed to true to cause signatures to be calculated and checked.

Storing the Key

The key is needed to create signatures on the sending end and check signatures on the receiving end.In the future, if asymmetric crypto is an option, it’s possible that two keys (a private one forthis end of the pipe and a public one for the other end) would need to be stored. At this time,messages going in both directions will be signed with the same key, so only that key needs to besaved.

The key is saved when the pipe is established. On the client side, this happens in connect(),which is located in msg/Pipe.cc. The key is obtained from a run of the Cephx protocol,which results in a successfully checked authorizer structure. If there is such an authorizeravailable, the code calls get_auth_session_handler() to create a new authentication session handlerand stores it in the pipe data structure. On the server side, a similar thing is done inaccept() after the authorizer provided by the client has been verified.

Once these things are done on either end of the connection, session authentication can start.

These routines (connect() and accept()) are also used to handle situations where a newsession is being set up. At this stage, no authorizer has been created yet, so there’s no key.Special cases in the code that calls the signature code skip these calls when theCEPH_AUTH_UNKNOWN protocol is in use. This protocol label is on the pre-authorizermessages in a session, indicating that negotiation on an authentication protocol is ongoing andthus signature is not possible. There will be a reliable authentication operation later in thissession before anything sensitive should be passed, so this is not a security problem.

Signing Messages

Messages are signed in the write_message call located in msg/Pipe.cc. The actualsignature process is to encrypt the CRCs for the message using the shared key. Thus, we mustdefer signing until all CRCs have been computed. The header CRC is computed last, so wecall sign_message() as soon as we’ve calculated that CRC.

sign_message() is a virtual function defined in auth/AuthSessionHandler.h. Thus,a specific version of it must be written for each authentication protocol supported. Currently,only UNKNOWN, NONE and CEPHX are supported. So there is a separate version of sign_message() inauth/unknown/AuthUnknownSessionHandler.h, auth/none/AuthNoneSessionHandler.h andauth/cephx/CephxSessionHandler.cc. The UNKNOWN and NONE versions simply return 0, indicatingsuccess.

The CEPHX version is more extensive. It is found in auth/cephx/CephxSessionHandler.cc.The first thing done is to determine if the run time option to handle signatures (see above) is on.If not, the Cephx version of sign_message() simply returns success without actually calculatinga signature or inserting it into the message.

If the run time option is enabled, sign_message() copies all of the message’s CRCs (one from theheader and three from the footer) into a buffer. It calls encode_encrypt() on the buffer,using the key obtained from the pipe’s session_security structure. 64 bits of the encryptedresult are put into the message footer’s signature field and a footer flag is set to indicate thatthe message was signed. (This flag is a sanity check. It is not regarded as definitiveevidence that the message was signed. The presence of a session_security structure at thereceiving end requires a signature regardless of the value of this flag.) If this all goes well,sign_message() returns 0. If there is a problem anywhere along the line and no signaturewas computed, it returns SESSION_SIGNATURE_FAILURE.

Checking Signatures

The signature is checked by a routine called check_message_signature(). This is also avirtual function, defined in auth/AuthSessionHandler.h. So again there are specific versionsfor supported authentication protocols, such as UNKNOWN, NONE and CEPHX. Again, the UNKNOWN andNONE versions are stored in auth/unknown/AuthUnknownSessionHandler.h andauth/none/AuthNoneSessionHandler.h, respectively, and again they simply return 0, indicatingsuccess.

The CEPHX version of check_message_signature() performs a real signature check. This routine(stored in auth/cephx/CephxSessionHandler.cc) exits with success if the run time option hasdisabled signatures. Otherwise, it takes the CRCs from the header and footer, encrypts the result,and compares it to the signature stored in the footer. Since an earlier routine has checked thatthe CRCs actually match the contents of the message, it is unnecessary to recompute the CRCson the raw data in the message. The encryption is performed with the same encode_encrypt()routine used on the sending end, using the key stored in the local session_securitydata structure.

If everything checks out, the CEPHX routine returns 0, indicating success. If there is aproblem, the routine returns SESSION_SIGNATURE_FAILURE.

Adding New Session Authentication Methods

For the purpose of session authentication only (not the basic authentication of client andserver currently performed by the Cephx protocol), in addition to adding a new protocol, thatprotocol must have a sign_message() routine and a check_message_signature routine.These routines will take a message pointer as a parameter and return 0 on success. The procedureused to sign and check will be specific to the new method, but probably there will be asession_security structure attached to the pipe that contains a cryptographic key. Thisstructure will be either an AuthSessionHandler (found in auth/AuthSessionHandler.h)or a structure derived from that type.

Adding Encryption to Sessions

The existing code is partially, but not fully, set up to allow sessions to have their packetsencrypted. Part of adding encryption would be similar to adding a new authentication method.But one would also need to add calls to the encryption and decryption routines in write_message()and read_message(). These calls would probably go near where the current calls forauthentication are made. You should consider whether you want to replace the existing callswith something more general that does whatever the chosen form of session security requires,rather than explicitly saying sign or encrypt.

Session Security Statistics

The existing Cephx authentication code keeps statistics on how many messages were signed, howmany message signature were checked, and how many checks succeeded and failed. It is preparedto keep similar statistics on encryption and decryption. These statistics can be accessed throughthe call printAuthSessionHandlerStats in auth/AuthSessionHandler.cc.

If new authentication or encryption methods are added, they should include code that keeps thesestatistics.