Security Header
The material in this section relates to the WS-Security specification section 5.
To use the wsse lite API:
- 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.
- Run soapcpp2 on the header file produced by wsdl2h.
- 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:
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):
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:
The basic API is introduced below.
To add an empty Security header block to the SOAP header, use:
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_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_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:
if (security)
{
This element defines the wsse:Security SOAP header element per Section 4. Imported element _wsse__Sec...
Definition: wsse.h:212
char * SOAP_ENV__actor
Definition: wsse.h:221
char * SOAP_ENV__role
Definition: wsse.h:222
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_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 Id
s are serialized as wsu:Id identifiers for cross-referencing XML elements.
To add a user name token with clear text password, use:
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 *password;
if (!username)
return soap->error;
password = ...;
{
int err = soap->error;
return err;
}
return SOAP_OK;
}
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_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_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:
WS-Security and HTTPS
HTTPS is used at the client side with the usual "https:" URL addressing, shown here:
#include "threads.h"
struct soap *soap;
if (CRYPTO_thread_setup())
...
soap = soap_new1(SOAP_XML_CANONICAL | SOAP_XML_INDENT);
if (soap_ssl_client_context(&soap,
SOAP_SSL_DEFAULT,
NULL,
NULL,
"cacerts.pem",
NULL,
NULL
))
...
soap->cafile = "cacerts.pem";
soap->capath = "dir/to/certs";
soap->crlfile = "revoked.pem";
if (soap_call_ns__myMethod(soap, "https://...", ...))
...
...
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
CRYPTO_thread_cleanup();
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 "threads.h"
SOAP_SOCKET m, s;
int port = 443;
struct soap *soap;
if (CRYPTO_thread_setup())
...
soap = soap_new1(SOAP_XML_CANONICAL | SOAP_XML_INDENT);
if (soap_ssl_server_context(&soap,
SOAP_SSL_DEFAULT,
server.pem,
"password",
NULL,
NULL,
NULL,
NULL,
NULL
))
...
if (!soap_valid_socket(m = soap_bind(soap, NULL, port, 100))
...
for (;;)
{
if (!soap_valid_socket(s = soap_accept(soap)))
...
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))
...
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
.