Genivia Home Documentation
The gSOAP WS-Trust Extensible Framework

updated Sat Jun 9 2018 by Robert van Engelen
 
The gSOAP WS-Trust Extensible Framework

Table of Contents

The gSOAP WS-Trust Extensible Framework

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

The WS-Trust framework is extensible. New client-side and server-side WS-Trust operations can be added. Several predefined operations are included to get you started. The list of predefined operations will be expanded over time. Please inquire Genivia tech support services.

To use the WS-Trust framework, make sure that the wst.h specification is imported in the .h file for soapcpp2, e.g. after running wsdl2h check the generated .h file:

#import "wst.h"

If the import is not there, add it manually. Then run soapcpp2 as usual with option -Iimport to import wst.h from the import directory.

The wst.h and the WS-Trust-dependent wstx.h and other gSOAP-specific .h header files are located in the import directory of the gSOAP package. These files define the WS-Trust and other WS-* protocol header elements and types. The wstx.h header file defines the WS-Trust RequestSecurityTokenRequest and RequestSecurityTokenRequestCollection operations.

Compile your code with -DWITH_DOM and -DWITH_OPENSSL to enable WS-Security plugin API features.

Compile and link your code with wsseapi.c and wstapi.c, and include wsseapi.h and wstapi.h in your code.

Internally, the wstapi.c code enables SOAP 1.2 messaging. This will not affect your SOAP 1.1 messaging.

WS-Trust Bindings

The WS-Trust bindings in wst.h in the import directory were generated from the WS-Trust schema for you with the wsdl2h tool and WS/WS-typemap.dat as follows:

wsdl2h -cgyex -o wst.h -t WS/WS-typemap.dat WS/WS-Trust.xsd

The following modifications to wst.h are required to be made after generating wst.h with wsdl2h:

Expanding the Current WS-Trust Bindings

To expand or customize the WS-Trust bindings by adding (or removing) content model elements to the RequestSecurityToken and RequestSecurityTokenResponse, edit WS/WS-typemap.dat for the following two definition blocks:

wst__RequestSecurityTokenType = $\
    _wsp__AppliesTo_*                    wsp__AppliesTo;
wst__RequestSecurityTokenType = $\
    char*                                KeyType;
wst__RequestSecurityTokenType = $\
    char*                                RequestType;
wst__RequestSecurityTokenType = $\
    char*                                TokenType;
wst__RequestSecurityTokenType = $\
    wst__EntropyType*                    Entropy;
wst__RequestSecurityTokenType = $\
    char*                                ComputedKeyAlgorithm;
wst__RequestSecurityTokenType = $\
    unsigned int*                        KeySize;
wst__RequestSecurityTokenType = $\
    struct wst__BinaryExchangeType*      BinaryExchange;

wst__RequestSecurityTokenResponseType = $\
    _wsp__AppliesTo_*                    wsp__AppliesTo;
wst__RequestSecurityTokenResponseType = $\
    struct wst__RequestedSecurityTokenType* RequestedSecurityToken;
wst__RequestSecurityTokenResponseType = $\
    struct wst__RequestedReferenceType*  RequestedAttachedReference;
wst__RequestSecurityTokenResponseType = $\
    struct wst__RequestedReferenceType*  RequestedUnattachedReference;
wst__RequestSecurityTokenResponseType = $\
    char*                                KeyType;
wst__RequestSecurityTokenResponseType = $\
    char*                                RequestType;
wst__RequestSecurityTokenResponseType = $\
    char*                                TokenType;
wst__RequestSecurityTokenResponseType = $\
    wst__EntropyType*                    Entropy;
wst__RequestSecurityTokenResponseType = $\
    struct wst__BinaryExchangeType*      BinaryExchange;

For example, to add the wst:Lifetime element to the RequestSecurityTokenResponse add the following two lines:

wst__RequestSecurityTokenResponseType = $\
    wst__LifetimeType*                   Lifetime;

where wst__LifetimeType is declared in wst.h. The pointer makes it optional.

Then follow the instructions in the previous section to regenerate wst.h.

Given the new Lifetime element, the wstapi.c framework can be extended to use this element information as follows:

time_t expires = 0; // no expiration
...
if (soap_call___wst__RequestSecurityToken(soap, endpoint, soap_wst_rst_action, &request, &response))
{
soap_set_namespaces(soap, saved_namespaces);
return soap->error;
}
soap_set_namespaces(soap, saved_namespaces);
if (response.Lifetime && response.Lifetime->wsu__Expires)
soap_s2dateTime(soap, response.Lifetime->wsu__Expires, &expires); // set expiration

Predefined WS-Trust Operations

This section lists the predefined WS-Trust operations implemented in wstapi.c.

wst_soap_wst_request_saml_token

int soap_wst_request_saml_token(struct soap *soap, const char *endpoint, int soapver, const char *applyto, const char *username, const char *password, saml1__AssertionType **saml1, saml2__AssertionType **saml2)

Request SAML 1.0 or SAML 2.0 token, with endpoint service endpoint URL (send to), soapver SOAP version with 1 = SOAP 1.1, 2 = SOAP 1.2 (SOAP 1.2 is recommended), applyto is your service domain, username to authenticate or NULL, password to authenticate or NULL, saml1 if non-NULL, requests SAML 1.0 and upon return points to a pointer that is set to the SAML 1.0 assertion received, saml2 if non-NULL, requests SAML 2.0 and upon return points to a pointer that is set to the SAML 2.0 assertion received.

Returns SOAP_OK on success when the assertion could be verified, with saml1 or saml2 set.

For example:

#include "wstapi.h"
#include "wsaapi.h"
#include "wsseapi.h"
#include "wst.nsmap"
static int ssl_verify(int ok, X509_STORE_CTX *store) { return 1; } // ignore all cert warnings (bad)
...
struct soap *soap = soap_new1(SOAP_XML_INDENT);
int soapver = 2; // SOAP 1.2
const char *to = "https://yourcompany.com/adfs/services/trust/13/UsernameMixed";
const char *applyto = "yourcompany.com";
const char *username = "yourusername";
const char *password = "yourpassword";
// register wsa plugin (optional, only if the client requires WS-Addressing rerouted messaging)
soap_register_plugin(soap, soap_wsa);
// register wsse plugin
soap_register_plugin(soap, soap_wsse);
// HTTPS settings
if (soap_ssl_client_context(soap, SOAP_SSL_DEFAULT, NULL, NULL, "cacerts.pem", NULL, NULL))
{
soap_print_fault(soap, stderr);
exit(1);
}
// HTTPS and SAML certificate verification callback
soap->fsslverify = ssl_verify;
// SAML 2.0 token request
if (soap_wst_request_saml_token(soap, to, soapver, applyto, username, password, NULL, &saml2))
{
soap_print_fault(soap, stderr);
}
else
{
// display subset of the assertion information
if (saml2)
{
int i;
for (i = 0; i < saml2->__size_AssertionType; i++)
{
if (saml2->__union_AssertionType[i].saml2__Statement)
{
// omitted from displaying
}
if (saml2->__union_AssertionType[i].saml2__AuthnStatement)
{
if (saml2->__union_AssertionType[i].saml2__AuthnStatement->saml2__AuthnContext)
{
if (saml2->__union_AssertionType[i].saml2__AuthnStatement->saml2__AuthnContext->saml2__AuthnContextClassRef)
printf("AuthnStatement Context: %s\n", saml2->__union_AssertionType[i].saml2__AuthnStatement->saml2__AuthnContext->saml2__AuthnContextClassRef);
}
}
if (saml2->__union_AssertionType[i].saml2__AuthzDecisionStatement)
{
// omitted from displaying
}
if (saml2->__union_AssertionType[i].saml2__AttributeStatement)
{
int j;
for (j = 0; j < saml2->__union_AssertionType[i].saml2__AttributeStatement->__size_AttributeStatementType; j++)
{
if (saml2->__union_AssertionType[i].saml2__AttributeStatement->__union_AttributeStatementType[j].saml2__Attribute)
{
int k;
for (k = 0; k < saml2->__union_AssertionType[i].saml2__AttributeStatement->__union_AttributeStatementType[j].saml2__Attribute->__sizeAttributeValue; k++)
printf("Type: %s\nValue: %s\n", saml2->__union_AssertionType[i].saml2__AttributeStatement->__union_AttributeStatementType[j].saml2__Attribute->Name, saml2->__union_AssertionType[i].saml2__AttributeStatement->__union_AttributeStatementType[j].saml2__Attribute->saml2__AttributeValue[k]);
}
}
}
}
if (saml2->saml2__Conditions)
{
printf("Not before %s\n", soap_dateTime2s(soap, *saml2->saml2__Conditions->NotBefore));
printf("Not on or after %s\n", soap_dateTime2s(soap, *saml2->saml2__Conditions->NotOnOrAfter));
}
}
else
{
printf("No SAML 2.0 statements!\n");
}
}
soap_destroy(soap);
soap_end(soap);
soap_free(soap);

This prints several of the assertion's properties, including the conditions under which the assertion is valid. The NotBefore and NotOnOrAfter conditions can be checked against the current time as follows:

time_t now = time(NULL);
... error // not valid yet
... error // expired

wst_soap_wst_request_psha1_token

int soap_wst_request_psha1_token(struct soap *soap, const char *endpoint, int soapver, const char *applyto, const char *username, const char *password, char *psha1, size_t psha1len)

Request P_SHA1 token with endpoint service endpoint URL (send to), soapver SOAP version with 1 = SOAP 1.1, 2 = SOAP 1.2 (SOAP 1.2 is recommended), applyto your service domain, username to authenticate or NULL, password to authenticate or NULL, psha1 is filled with the P_SHA1 result token of psa1len bytes.

Returns SOAP_OK on success.

#include "wsaapi.h"
#include "wstapi.h"
#include "wsseapi.h"
#include "wst.nsmap"
static int ssl_verify(int ok, X509_STORE_CTX *store) { return 1; } // ignore all cert warnings (bad)
...
struct soap *soap = soap_new1(SOAP_XML_INDENT);
int soapver = 2; // SOAP 1.2
const char *to = "https://yourcompany.com/adfs/services/trust/13/UsernameMixed";
const char *applyto = "yourcompany.com";
const char *username = "yourusername";
const char *password = "yourpassword";
char psha1[256];
// register wsa plugin (optional, only if the client requires WS-Addressing rerouted messaging)
soap_register_plugin(soap, soap_wsa);
// register wsse plugin
soap_register_plugin(soap, soap_wsse);
// HTTPS settings
if (soap_ssl_client_context(soap, SOAP_SSL_DEFAULT, NULL, NULL, "cacerts.pem", NULL, NULL))
{
soap_print_fault(soap, stderr);
exit(1);
}
// HTTPS certificate verification callback
soap->fsslverify = ssl_verify;
// PSHA1 token request
if (soap_wst_request_psha1_token(soap, to, soapver, applyto, username, password, psha1, 256))
{
soap_print_fault(soap, stderr);
}
else
{
// use psha1[0..255]
}
soap_destroy(soap);
soap_end(soap);
soap_free(soap);

wst_soap_wst_request_psha256_token

Similar to the previous section, request a P_SHA256 token with:

int soap_wst_request_psha256_token(struct soap *soap, const char *endpoint, int soapver, const char *applyto, const char *username, const char *password, char *psha256, size_t psha256len)

Using the wst Plugin for Servers

To implement a WS-Trust server in C, run soapcpp2 as follows:

soapcpp2 -c -L file.h

where file.h has an #import "wst.h". This generates the soapServer.c and soapC.c code you need to compile with wstapi.c, wsaapi.c, wsseapi.c, smdevp.c, and mecevp.c. Link with libgsoapssl.a (or stdsoap2.c and dom.c). Use -DWITH_OPENSSL and -DWITH_DOM to compile the source code.

For C++, use:

soapcpp2 -L file.h

This generates the soapServer.cpp and soapC.cpp code you need to compile with wstapi.c, wsaapi.c, wsseapi.c, smdevp.c, and mecevp.c. Link with libgsoapssl++.a (or stdsoap2.cpp and dom.cpp). Use -DWITH_OPENSSL and -DWITH_DOM to compile the source code.

If you prefer to use soapcpp2 option -j (or -i) to generate C++ server objects, please run soacpp2 again as follows:

soapcpp2 -j -L file.h
soapcpp2 -CL -pwst import/wst.h

This generates wstClient.cpp, which should be compiled together with the rest of your project code.

You should define the following service operations:

int wstService::RequestSecurityToken(wst__RequestSecurityTokenType *request, wst__RequestSecurityTokenResponseType *response)
{
...
}
int wstService::RequestSecurityTokenCollection(struct wst__RequestSecurityTokenCollectionType *request, struct wst__RequestSecurityTokenResponseCollectionType *response)
{
...
}

If you are combinding WS-Trust with other service operations, then you must also chain the service operations at the server side as follows:

if (soap_begin_serve(service.soap) == SOAP_OK)
if (service.dispatch() == SOAP_NO_METHOD)
soap_serve_request(service.soap);

where the service object is an instance of the application services generated by soapcpp2 -j.