|  | Home | Documentation | WS-Security lite updated by Robert van Engelen |

WS-Security lite

# Security Header

The material in this section relates to the WS-Security specification section 5.

To use the wsse lite API:

1. Run wsdl2h -t typemap.dat on a WSDL of a service that requires WS-Security headers. The typemap.dat file is used to recognize and translate Security header blocks.
2. Run soapcpp2 on the header file produced by wsdl2h.
3. Use the wsse lite API functions described below to add time stamp and user name tokens.

If HTTPS is required with OpenSSL then please follow the instructions in Section WS-Security and HTTPS to ensure thread-safety of WS-Security with HTTPS.

The wsse lite API is located in:

- gsoap/plugin/wsseapi-lite.h wsse lite API.
- gsoap/plugin/wsseapi-lite.c wsse lite API for C and C++.

You will also need:

- gsoap/custom/struct_timeval.c compile and link this file (C and C++).
- compile all sources with -DWITH_OPENSSL to enable HTTPS.
- if you have zlib installed, compile all sources also with -DWITH_GZIP.
- link with -lssl -lcrypto -lz -gsoapssl++ (or -lgsoapssl for C, or compile stdsoap2.cpp for C++ and stdsoap2.c for C).

The gSOAP header file for soapcpp2 should import wsse.h (or the older 2002 version wsse2.h):

#import "wsse.h"
wsse.h

The wsdl2h tool adds the necessary imports to the generated header file if the WSDL declares the use of WS-Security. If not, you may have to add the import manually before running soapcpp2.

The wsse lite API consists of a set of functions to populate and verify WS-Security headers and message body content. For more details, we refer to the following sections that correspond to the WS-Security specification sections:

- Section 6 Security Tokens
- Section 10 Security Timestamps
- WS-Security and HTTPS

The basic API is introduced below.

To add an empty Security header block to the SOAP header, use:

soap_wsse_add_Security(soap);
soap_wsse_add_Security
SOAP_FMAC1 struct _wsse__Security *SOAP_FMAC2 soap_wsse_add_Security(struct soap *soap)
Adds Security header element.
Definition wsseapi-lite.c:464

To delete a Security header, use:

soap_wsse_delete_Security(soap);
soap_wsse_delete_Security
SOAP_FMAC1 void SOAP_FMAC2 soap_wsse_delete_Security(struct soap *soap)
Deletes Security header element.
Definition wsseapi-lite.c:513

Adding an empty Security header block is not very useful. In the following, we present the higher-level functions of the wsse lite API to populate and verify Security header content.

Note
  The soap context includes an actor value soap.actor that is populated and rendered as the SOAP-ENV:actor (SOAP 1.1) or SOAP-ENV:role (SOAP 1.2) attribute in XML within the generic SOAP Header. The attribute is optional, but should be used to target a recipient such as an intermediate node to process the SOAP header. In contrast, actor or role attributes within Security header blocks target specific recipients to process the Security header block. The gSOAP implementation does not automate this feature and application should set and check the actor/role attribute when necessary. In addition, the current implementation supports the inclusion of a single Security header block in the SOAP header.

To populate the SOAP-ENV:actor or SOAP-ENV:role attribute within the Security header, use:

soap_wsse_add_Security_actor(soap, "recipient");
soap_wsse_add_Security_actor
SOAP_FMAC1 struct _wsse__Security *SOAP_FMAC2 soap_wsse_add_Security_actor(struct soap *soap, const char *actor)
Adds Security header element with actor or role attribute.
Definition wsseapi-lite.c:492

To obtain the actor or role value (e.g. after receiving a message), use:

_wsse__Security *security = soap_wsse_Security(soap);
if (security)
{
... = security->SOAP_ENV__actor; // SOAP 1.1
... = security->SOAP_ENV__role; // SOAP 1.2
_wsse__Security
This element defines the wsse:Security SOAP header element per Section 4. Imported element _wsse__Sec...
Definition wsse.h:212
_wsse__Security::SOAP_ENV__actor
char * SOAP_ENV__actor
Definition wsse.h:221
_wsse__Security::SOAP_ENV__role
char * SOAP_ENV__role
Definition wsse.h:222
soap_wsse_Security
SOAP_FMAC1 struct _wsse__Security *SOAP_FMAC2 soap_wsse_Security(struct soap *soap)
Returns Security header element if present.
Definition wsseapi-lite.c:531

The SOAP-ENV:mustUnderstand attribute is automatically added and checked by the gSOAP engine. A gSOAP application compiled without Security support will reject Security headers.

Security header blocks are attached to the soap context, which means that the information will be automatically kept to support multiple invocations.

# Security Tokens

The material in this section relates to the WS-Security specification section 6.

## User Name Token

To add a user name token to the Security header block, use:

soap_wsse_add_UsernameTokenText(soap, "Id", "username", NULL);
soap_wsse_add_UsernameTokenText
SOAP_FMAC1 int SOAP_FMAC2 soap_wsse_add_UsernameTokenText(struct soap *soap, const char *id, const char *username, const char *password)
Adds UsernameToken element with optional clear-text password.
Definition wsseapi-lite.c:653

The Id value is optional and not used in the wsse lite API. These Ids are serialized as wsu:Id identifiers for cross-referencing XML elements.

To add a user name token with clear text password, use:

soap_wsse_add_UsernameTokenText(soap, "Id", "username", "password");

It is strongly recommended to use soap_wsse_add_UsernameTokenText only in combination with HTTPS encrypted transmission or not at all. A better alternative is to use password digests (not supported in this wsse lite API).

Clear-text passwords are verified with soap_wsse_verify_Password. To verify a password at the receiving side to authorize a request (e.g. within a Web service operation), use:

int ns__myMethod(struct soap *soap, ...)
{
const char *username = soap_wsse_get_Username(soap);
const char *password;
if (!username)
return soap->error; // no username: return FailedAuthentication (from soap_wsse_get_Username)
password = ...; // lookup password of username
if (soap_wsse_verify_Password(soap, password))
{
int err = soap->error;
soap_wsse_delete_Security(soap); // remove old security headers
return err; // password verification failed: return FailedAuthentication
}
return SOAP_OK;
}
soap_wsse_get_Username
SOAP_FMAC1 const char *SOAP_FMAC2 soap_wsse_get_Username(struct soap *soap)
Returns UsernameToken/username string or wsse:FailedAuthentication fault.
Definition wsseapi-lite.c:723
soap_wsse_verify_Password
SOAP_FMAC1 int SOAP_FMAC2 soap_wsse_verify_Password(struct soap *soap, const char *password)
Verifies the supplied password or sets wsse:FailedAuthentication fault.
Definition wsseapi-lite.c:750

Note that the soap_wsse_get_Username functions sets the wsse:FailedAuthentication fault upon failure. It is common for the API functions functions to return SOAP_OK or a wsse fault that should be passed to the sender by returning soap->error from service operations. The fault is displayed with the soap_print_fault function.

# Security Timestamps

The material in this section relates to the WS-Security specification section 10.

To add a timestamp with the creation time to the Security header, use:

soap_wsse_add_Timestamp(soap, NULL, 0); // no expiration
soap_wsse_add_Timestamp
SOAP_FMAC1 int SOAP_FMAC2 soap_wsse_add_Timestamp(struct soap *soap, const char *id, time_t lifetime)
Adds Timestamp element with optional expiration date+time (lifetime).
Definition wsseapi-lite.c:555

The lifetime of a message (in seconds) is passed as the third argument, which will be displayed as the timestamp expiration time:

soap_wsse_add_Timestamp(soap, NULL, 10); // 10 seconds lifetime

# WS-Security and HTTPS

HTTPS is used at the client side with the usual "https:" URL addressing, shown here:

#include "wsseapi-lite.h"
#include "threads.h"
struct soap *soap;
if (CRYPTO_thread_setup())
... // error
soap = soap_new1(SOAP_XML_CANONICAL | SOAP_XML_INDENT);
if (soap_ssl_client_context(&soap,
SOAP_SSL_DEFAULT, // requires server authentication
NULL, // keyfile for client authentication to server
NULL, // the keyfile password
"cacerts.pem", // cafile CA certificates to authenticate the server
NULL, // capath CA directory path to certificates
NULL
))
... // error
soap->cafile = "cacerts.pem"; // same as above (or overrides the above)
soap->capath = "dir/to/certs"; // and/or point to CA certs
soap->crlfile = "revoked.pem"; // use CRL (optional)
soap_wsse_delete_Security(soap); // remove any previous header content
soap_wsse_add_UsernameTokenText(soap, NULL, "username", "password");
soap_wsse_add_Timestamp(soap, NULL, 10); // 10 seconds lifetime
if (soap_call_ns__myMethod(soap, "https://...", ...))
... // error
... // process response results
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
CRYPTO_thread_cleanup();
wsseapi-lite.h

With OpenSSL, the CRYPTO threads should be set up before any threads are created.

The soap_ssl_client_context only needs to be set up once. Use the following flags:

- SOAP_SSL_DEFAULT requires server authentication, CA certs should be used
- SOAP_SSL_NO_AUTHENTICATION disables server authentication
- SOAP_SSL_SKIP_HOST_CHECK disables server authentication host check
- SOAP_SSL_ALLOW_EXPIRED_CERTIFICATE to accept self-signed certificates, expired certificates, and certificates without CRL.

The server uses the following:

#include "wsseapi-lite.h"
#include "threads.h"
SOAP_SOCKET m, s;
int port = 443;
struct soap *soap;
if (CRYPTO_thread_setup())
... // error
soap = soap_new1(SOAP_XML_CANONICAL | SOAP_XML_INDENT);
if (soap_ssl_server_context(&soap,
SOAP_SSL_DEFAULT, // requires server to authenticate, but not the client
server.pem, // keyfile for authentication to client
"password", // the keyfile password
NULL, // CA certificates to authenticate the client
NULL, // CA directory path to certificates
NULL, // use RSA 2048 bits (or give file name with DH param)
NULL,
NULL
))
... // error
if (!soap_valid_socket(m = soap_bind(soap, NULL, port, 100))
... // error
for (;;)
{
if (!soap_valid_socket(s = soap_accept(soap)))
... // error
else
{
struct soap *tsoap = soap_copy(soap);
while (THREAD_CREATE(&tid, (void*(*)(void*))&process_request, (void*)tsoap))
sleep(1);
}
}
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
CRYPTO_thread_cleanup();

where we define a process_request function that is executed by the thread to process the request (on a copy of the soap context struct):

void *process_request(struct soap *soap)
{
if (soap_ssl_accept(soap)
|| soap_serve(soap))
... // error
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
}

The soap_ssl_server_context only needs to be set up once. Use the following flags:

- SOAP_SSL_DEFAULT requires server authentication, but no client authentication
- SOAP_SSL_REQUIRE_CLIENT_AUTHENTICATION requires client authentication

With OpenSSL, we need to define the thread set up and clean up operations as follows:

struct CRYPTO_dynlock_value
{
MUTEX_TYPE mutex;
};
static MUTEX_TYPE *mutex_buf;
static struct CRYPTO_dynlock_value *dyn_create_function(const char *file, int line)
{
struct CRYPTO_dynlock_value *value;
value = (struct CRYPTO_dynlock_value*)malloc(sizeof(struct CRYPTO_dynlock_value));
if (value)
MUTEX_SETUP(value->mutex);
return value;
}
static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)
{
if (mode & CRYPTO_LOCK)
MUTEX_LOCK(l->mutex);
else
MUTEX_UNLOCK(l->mutex);
}
static void dyn_destroy_function(struct CRYPTO_dynlock_value *l, const char *file, int line)
{
MUTEX_CLEANUP(l->mutex);
free(l);
}
void locking_function(int mode, int n, const char *file, int line)
{
if (mode & CRYPTO_LOCK)
MUTEX_LOCK(mutex_buf[n]);
else
MUTEX_UNLOCK(mutex_buf[n]);
}
unsigned long id_function()
{
return (unsigned long)THREAD_ID;
}
int CRYPTO_thread_setup()
{
int i;
mutex_buf = (MUTEX_TYPE*)malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
if (!mutex_buf)
return SOAP_EOM;
for (i = 0; i < CRYPTO_num_locks(); i++)
MUTEX_SETUP(mutex_buf[i]);
CRYPTO_set_id_callback(id_function);
CRYPTO_set_locking_callback(locking_function);
CRYPTO_set_dynlock_create_callback(dyn_create_function);
CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
return SOAP_OK;
}
void CRYPTO_thread_cleanup()
{
int i;
if (!mutex_buf)
return;
CRYPTO_set_id_callback(NULL);
CRYPTO_set_locking_callback(NULL);
CRYPTO_set_dynlock_create_callback(NULL);
CRYPTO_set_dynlock_lock_callback(NULL);
CRYPTO_set_dynlock_destroy_callback(NULL);
for (i = 0; i < CRYPTO_num_locks(); i++)
MUTEX_CLEANUP(mutex_buf[i]);
free(mutex_buf);
mutex_buf = NULL;
}

For additional details and examples, see the user guide and examples in the gSOAP package directory gsoap/samples/ssl.