By Malinda Kaushalye
- 18 Sep, 2006
Rampart/C is the security module for Apache Axis2/C. Kaushalye Kapuruge in his article gives a detailed introduction to Username Token based Authentication and Timestamps provided by Rampart for Apache Axis2/C.
Rampart/C serves as the security module for C language implementation of Apache Axis2/C SOAP engine. It provides a set of mechanisms to protect SOAP messages that are exchanged among different entities. Such mechanisms include message confidentiality and message authentication. The initial release of Rampart/C (in Apache Axis2/C version 0.93) supports Username token based authentication and Timestamps. Future enhancements include features such as encryption/decryption and signature/verification of messages. We assume that readers have a moderate knowledge of Web services and some experience in C language. Knowledge in security concepts would be an advantage.
Rampart/C with Axis2/C
Axis2/C is an open source SOAP message processing engine that has a very modular and scalable architecture. When designing the Axis2 architecture, the concept of module was added specifically to support the WS-* specifications that are often in flux. (e.g. WS-Security, WS- Addressing). A module, which is a very siginificant entity in Axis2 architecture is a logically grouped set of handlers and related classes that extends the SOAP preocessing model. Modules makes life very easy for WS-* developers by allowing them to implement specifications without touching the core engine.
Gaining the advantage of the Axis2 architecture, Rampart is deployed as a module. Rampart works transparent to message exchanging parties. The module consists of two handlers that resides both in IN and OUT message flows. The OUT handler will add user credentials to the security header while the IN handler verifies credentials present in the SOAP header.
Username token provides a way to uniquely identify a user. The username along with the password in the token provides a way to authenticate the user who sent the message. The password (or the shared secret) which is used to establish a proof-of-possession can be sent in two forms.
1. Password as a Plain Text :
This is not a secured way to send the password as any one can see it. Even for a simple scenario it is not recommended to use this mechanism as many people used to have the same password for many Web portals and applications. But if the transport protocol is secure or the message is encrypted then it is much more easier and efficient to use a plain text password.
<wsse:Password Type = ...>MySecretPassword</wsse:Password>
When a message arrives Rampart will compare the password or the shared secret with the password in the message. If comparison is not successful an error will be thrown back to the user.
2. A Digest of the Actual Password :
The password digest is a one time hash that uniquely represents the actual password. Here the actual password is concatenated with a random set of bytes and a string that carries information about the creation time.
Password_Digest = Base64 ( SHA-1 (nonce + created + password ) )
Nonce is the random set of bytes. The objective of adding a nonce is to prevent replay attacks.
Digest is a bit complex but a more effective way to use the Username token. When a message arrives, the authentication module in Rampart will generate the hash again with the password provided in the database, the nonce and created values available in the message. Then that hash value will be compared with the digest available in the token. If the two matches the user will be recognized as a valid user. Otherwise an error is thrown back.
Username Token Verification
The verification process consists of a series of checks depending on the security header elements and the Username token. The verification can be configured by specifying UsernameToken in the action items in the inflow parameter set. If the verification is enabled, Rampart will check the security header of the SOAP message for a Username token. If there is no such token, an error will be thrown back to the sender. When the token is present, following verifications will be carried out. If requirements are not met, an error will be thrown.
- The Username token should not contain more than one password.
- The password type should be specified using either
- If a password digest is used, Username token should not contain more than one nonce value.
- If a password digest is used, Username token should not contain more than one created value.
In password digest verification it is possible to have a message without a nonce and/or a created value. The hash is generated by omitting missing parts. For example, if the nonce is not present the hash will be generated by concatenating only the password and the created value.
How to Configure Axis2 to Engage Rampart
Rampart/C works in Axis2/C as a module. Rampart can be engaged in three levels with Axis2 engine.
- Global level : Rampart is engaged with all the services available.
- Service level : Rampart is engaged with a specific service of interest.
- Operation level : Rampart is engaged with a specific operation of a service.
To engage a module in Axis2 you need to follow two easy steps:
Step1: Build the module and copy it to AXIS2C_HOME/modules Step2: Engage the module in the descriptor file (axis2.xml or services.xml)
Once the Rampart build completes a new directory(called rampart) is created under AXIS2C_HOME. The rampart module is located in AXIS2_HOME/rampart/rampart. Copy that directory to the AXIS2C_HOME/modules directory.
Then Add following line to the axis2.xml under axisconfig. This will engage Rampart module globally.
So far we configured the Axis2 engine to work with Rampart. Next step is to set configurations for the Rampart module. Axis2 provides a way to specify parameters. We use two parameter namely InFlowSecurity and OutFlowSecurity to configure Rampart. InFlowSecurity parameter specifies what's to be done when a message arrives while the OutFlowParameter specifies what needs to be done when a message is sent. Add following to the descriptor file:
Above example shows how the configuration looks like when a Usernametoken needs to be attached to an outgoing message. It says add a username token with 'Gajaba' as the username. The password has to be the digest of the actual password and it should be retrieved using the password module specified under passwordCallbackClass.
Let's see how to configure the in flow:
The incoming message MUST contain a Usernametoken. If not it will be an error. It also specifies the location of the password call back module.
We've seen that in the parameter list we had to specify the user name. But we cannot do the same for the password as anybody (has access to the file) would be able to see it. Again, the passwords can be stored in different ways. For instance it can be in a flat file or in a very secure database. Or may be in a platform specific storage.
The way to overcome these limitations is to get the password dynamically. For this we use the password callback mechanism. In Rampart parameters we have to specify the passwordCallbackClass. As the word sounds it is not exactly a class. It can be a .so module under unix or a DLL under windows. But for the compatibility with the Java counterpart we keep the name untempered.
Writing a callback module is easy. The sample coming under rampart/samples/callback is a very good place to start with. If you see the get_sample_password function in the sample implementation, it consists of few hard-coded username/password pairs. Instead of hard coding, you may retrieve passwords from a database, flat file etc.
We will assign our function to the function pointer as below:
rcb->ops->callback_password = get_sample_password;
callback_password is a function pointer to any function which has the following signature:
axis2_char_t * your_function(rampart_callback_t *rcb,
const axis2_env_t *env, const axis2_char_t *username)
/*Your code to supply the password*/
The function you provide should return the password as axis2_char_t* for the username specified, if any. If the user doesn't exist the function should return NULL.
Timestamp is a way to specify the validity period of the message sent. Rampart will reject messages either expired or out of the validity period. Timestamps are in UTC time. Following is an example of a valid Timestamp token:
It is possible to specify the time period, which the message is valid in the OutFlowSecurity parameter list as below:
Here the time is in seconds. So the generated Timestamp would be valid for six minutes(60 x 6 = 360) from the created time. If timeToLive is not given, the default, i.e. five minutes (or 300 seconds) would be taken.
Timestamp Token Verification
The time stamp verification can be configured using inflow security parameters. Simply add Timestamp to the action item list. If the verification is enabled, Rampart will do following checks for the incoming message. If any of the following requirements are not fulfilled the verification will fail, thus an error will be thrown back to the sender.
- Timestamp must contain exactly one Created element.
- Expires element is optional. But there should not be more than one Expires element.
- The Expires element must always come after the Created element.
- There should not be any sub elements of Timestamp token other than Expires and Created.
- All the values in Created and Expires elements must be in UTC format as specified by the XML Schema type (dateTime).
Even if the Timestamp token verification is not configured, Rampart processes the security header to check whether there are multiple Timestamp tokens available. According to the WS-Security specification there should not be more than one Timestamp token in the security header.
The idea of using Rampart/C is to provide security for the messages exchanged using Web services technology. In Apache Axis2/C version 0.93, Rampart module provides Usernamtoken and Timestamp as per WS-Security specification. It is planned to support message encryption and signing in future releases.
Malinda Kaushalye Kapuruge is Software Engineer, WSO2 Inc. kaushalye at wso2 dot com