next up previous contents
Next: Bibliography Up: manual Previous: 6. Service packaging and   Contents

Subsections


7. Clarens authentication/login protocol

7.1 Introduction

The Clarens web service layer performs user authentication using X509 certificates issued by a certificate authority. It does so within the confines if the http Basic authentication protocol. This means that authentication information is passed along in the http header information using the AUTHORIZATION field. E.g.: AUTHORIZATION Basic a80a844c376705cd8ecb8debdae0

The string following the Basic keyword is a Base64 encoding of some information that the user wishes to pass to the server, usually the string user:password. In Clarens usernames and password are not used, but since the Basic authentication scheme is known by most http client programs and libraries, it eases the implementation of new clients to Clarens services. In server versions 0.6.3 or greater the cookies named clarens_username and clarens_password may alternatively be used from within clients that are unable to set the authentication headers.

Note that the following assumes some knowledge of encryption using public/private keys. Also note that the authentication can be done without using an encrypted link (i.e. using http instead of https), but https is recommended if security of transferred data is required. For the primary HEP application where Clarens is used http is normally used since the data is not ordinarily confidential, and the encryption/decryption slows down data transfer significantly.

7.2 Two steps

In order to do proper authentication of the client by the server, and vice versa, a challenge-response (or two-step) authentication must be used. The Clarens scheme is based on GSI, with the exception that does it was designed to fit into the http protocol without changes to servers or libraries.

The information interchange is described below, with some implementation examples interspersed written in Python. There is also a C++ implementation available which uses OpenSSL directly, see the Clarens layer for the Root package, which is part of the Clarens package.

  1. Create a session em nonce value to identify the session. This should be as random as possible, but is combined with an IP address at the server to identify the session. E.g.:

    PYTHON
    random.seed()
    ustring_raw="%s_%f_%f"%(os.getpid(),time.time(),random.random())
    sha1=MessageDigest('sha1')
    sha1.update(ustring_raw)  
    ustring=encodestring(sha1.digest())
    


    That is, ustring is the SHA1 digest of ustring_raw.

  2. Load the user's X509 certificate from a text file (in PEM-encoded format):

    PYTHON
    text_file=open(certfile)
    text_ucert=text_file.read()
    text_file.close()
    


  3. Construct the first XML-RPC call to the server, calling the "system.auth" method. In the process the http header must be set as: AUTHORIZATION: Basic <string> where <string> is the Base64 encoding of ustring:text_ucert.

    PYTHON
    h.putheader("AUTHORIZATION", 
                "Basic %s"%encodestring("%s:%s" % (ustring,text_ucert)))
    


    Note that the above also removes any linefeed character from the resultant string to make sure the http server does not get confused, since a CRLF sequence is used to indicate the end of a header line in the http protocol.

    A typical system.auth call would look like this::

    HTTP
    POST /xmlrpc/clarens_server.py HTTP/1.0
    Host: localhost
    User-Agent: xmlrpclib.py/0.9.9 (by www.pythonware.com)
    Content-Type: text/xml
    Content-Length: 105
    AUTHORIZATION: Basic MkhVTm9VazYxbXArVEZLS0dCY2tIRlA3bjVzPQo6RnJvbSBi
    <?xml version='1.0'?>
    <methodCall>
      <methodName>system.auth</methodName>
      <params>
      </params>
    </methodCall>
    


    Most http libraries in one form or another allows you to set the username and password of the http request, and you would not need to do the header construction manually.

    PYTHON
    mtrans=BasicAuthTransport(ustring,text_ucert)
    xmlrpclib.Server.__init__(self,url,transport=mtrans)
    values = self.system.auth()
    


    That is, a transport with the username and password of (ustring,text_ucert) is constructed and the xmlrpc client object is initialized using that transport.

  4. The server will send back three strings in response to the system.auth call above:

    XML-RPC
    <?xml version=1.0?>
    <methodResponse>
      <params>
        <param>
          <value>
            <array>
              <data>
                <value><string>STRING1</string></value>
                <value><string>STRING2</string></value>
                <value><string>STRING3</string></value>
              </data>
            </array>
          </value>
        </param>
      </params>
    </methodResponse>
    


    These three strings are as follows: STRING1: The PEM-encoded server certificate. (PEM-encoding is basically Base64) STRING2: A server 'nonce' value that was encrypted using the client's public key, and then Base64 encoded. STRING3: The client's 'nonce' value encrypted using the server's private key, and then Base64 encoded.

    PYTHON
    text_scert, 
    crypt_server_nonce_64,
    crypt_user_nonce_64 = self.system.auth()
    


  5. Read the server certificate contained in STRING1 and extract the public key contained therein. E.g.:

    PYTHON
    server_cert=X509.load_cert_bio(BIO.MemoryBuffer(text_scert))
    server_pub_key=server_cert.get_pubkey()
    server_pub_rsa=RSA.RSA_pub(m2.rsa_from_pkey(server_pub_key))
    


    Verify that the key is authentic and still valid:

    PYTHON
    server_cert.verify()
    


  6. Reverse the Base64-encoding of STRING2 and decrypt the resultant data to see if it matches the user nonce that we have constructed. This verifies that the server can encrypt data using its private key, and that we in turn can decrypt that data using its public key.

    PYTHON
    server_ustring = 
       server_pub_rsa.public_decrypt(decodestring(crypt_user_nonce_64),
                                                  mpadding)
    


    Then check if server_ustring is equal to ustring

  7. Reverse the Base64-encoding of STRING3 and decrypt the resultant data using the client's private key. The resultant value is a secure session ID provided to us by the server.

    PYTHON
    server_nonce = 
      user_priv_rsa.private_decrypt(decodestring(crypt_server_nonce_64),
                                                 mpadding)
    


  8. Calculate the SHA1 hash of the resultant data, and set the password in subsequent http request headers to the Base64 encoding of this hash value:

    PYTHON
    sha1=MessageDigest('sha1')
    sha1.update(server_nonce) 
    mcrypt_server_nonce=encodestring(sha1.digest())
    mtrans.set_password(mcrypt_server_nonce)
    


    Note that when the http header is constructed the user and password (or in our case the ustring and mcrypt_server_nonce values are again combined into a string (separated by a colon) and Base64 encoded when sent to the server as part of each RPC call.

  9. Finally, to end the session, call the system.logout method of the server, which produces an XML request that looks like this:

    HTTP
    POST /xmlrpc/clarens_server.py HTTP/1.0
    Host: localhost
    User-Agent: xmlrpclib.py/0.9.9 (by www.pythonware.com)
    Content-Type: text/xml
    Content-Length: 107
    AUTHORIZATION: Basic MkhVTm9VazYxbXArVEZLS0dCY2tIRlA3bjVzPQo6UFRHdHcrRVlX
    <?xml version='1.0'?>
    <methodCall>
      <methodName>system.logout</methodName>
      <params></params>
    </methodCall>
    


    The server responds with and integer of value 0 when the call succeeds:

    HTTP
    HTTP/1.1 200 OK
    Server: Sourcelight Technologies py-xmlrpc-0.8.8.2
    Content-Type: text/xml
    Content-length: 220
    <?xml version='1.0'?>
    <methodResponse>
      <params>
        <param>
          <value>
            <array>
              <data>
                <value><int>0</int></value>
              </data>
            </array>
          </value>
        </param>
      </params>
    </methodResponse>
    


7.3 SSL based authentication

Since the SSL key exchange algorithm makes it possible to do certificate-based authentication, some of the above procedure is redundant in that case. Furthermore, some clients may not have access to cryptographic functions, even though they are able to make SSL connections with certificate-based authentication, e.g. a JavaScript client running inside a web browser.

To allow for authentication under these circumstances, the system.auth2 server-side function was created. The function is called without an argument, and returns the server certificate, client certificate and new password to be used as strings. In addition, the user and password combination in the Authentication headers or cookies should be set to a session key as in the non-SSL case and the string BROWSER respectively.

The username and new password should be used in subsequest RPC calls, in either the Authentication headers or cookies in the same way as in the non-SSL case. E.g.:

PYTHON
server_cert, user_cert, new_passwd = self.system.auth2()
mtrans.set_password(new_passwd)


7.3.1 Proxy authentication over SSL

As of version 0.6.3 of the Clarens server, any SSL connections made using an accepted certificate/key pair or a so-called `proxy certificate' will automatically result in a session being created on the server, without the need to call one of the system.auth/auth2 methods.

Proxy authentication requires that the Gridsite Apache module be installed and configured on the server. On an OpenPKG-based installation the command
clump install gridsite
will install and configure this package automatically.

7.4 GET requests

At the moment HTTP GET requests (as opposed to POST requests used for RPC calls) do not result in a session ID being created automatically, except in the case of an authenticated SSL connection. Instead, non-authenticated GET requests are assigned an internal distinguished name of '/' (forward slash) and ACLs are evaluated as if a file.read method was called.

7.5 Other transport protocols

Although web services are, as the name implies, mostly implemented using the HTTP protocol, the stateless nature of this protocol makes it unsuitable for some server-side applications. An example of such an application is a secondary network service such as a storage server, e.g. the Storage Resource Broker (SRB) or dCache where reconnecting to the secondary server upon receipt of each HTTP request would be prohibitively slow.

For this reason, future Clarens services would also be usable from a stateful protocol, e.g. FTP.

7.6 Discussion

The above protocol is designed to prove the user and server identities to both parties without prior knowledge of each other, except through the ability to check certificate signatures against known CA signatures.

Since the http protocol is designed to handle multiple requests from the same client using one or more socket connections, the server and user session ids remain valif between requests, and the server should store those session ids. This makes it possible to have a single sign-on for a client to use different applications, or different instances of the same application to connect to the server and make requests, as long as every application knows the two session ids. Obviously the server should keep track of who makes these reqeusts, and this is done by ensuring that a pair of session ids are unique to a unique IP address.

It is also possible to connect to multiple servers from a single client application, the only requirement is that a session id pair must be negiotiated with every server. Note that most private encryption keys are themselves encrypted, so that a password is usually required to enable the client to perform that negotiation process.

Note that Globus works around this inconvenience by creating a temporary unencrypted public/private keypair through the globus-proxy-init command, which in turn presents its own set of problems. It is also once again recommended that SSL encryption (https) be used if data confidentiality is important.


next up previous contents
Next: Bibliography Up: manual Previous: 6. Service packaging and   Contents
Conrad Steenberg 2005-07-11