Genivia - The gSOAP WS-Trust Extensible Framework

|  | Home | Documentation | The gSOAP WS-Trust Extensible Framework updated Thu Apr 15 2021 by Robert van Engelen |

- Main Page
- Related Pages
- Classes
- Files

The gSOAP WS-Trust Extensible Framework

# 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:

- Remove `//gsoapopt`
- Change `http://docs.oasis-open.org/ws-sx/ws-trust/200512` to remove the trailing `/` (slash)
- Change `//gsoap wst schema namespace` directive to `//gsoap wst schema import` directive
- Add `#import "wsp_appliesto.h"`
- Add `#import "wstx.h"` at the end of the definitions in wst.h

# 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__RequestSecurityTokenType = $\
        struct wst__AuthenticatorType*       Authenticator;

    wst__RequestSecurityTokenResponseType = $\
        struct wst__RequestedSecurityTokenType* RequestedSecurityToken;
    wst__RequestSecurityTokenResponseType = $\
        struct wst__RequestedReferenceType*  RequestedAttachedReference;
    wst__RequestSecurityTokenResponseType = $\
        struct wst__RequestedReferenceType*  RequestedUnattachedReference;
    wst__RequestSecurityTokenResponseType = $\
        struct wst__RequestedProofTokenType* RequestedProofToken;
    wst__RequestSecurityTokenResponseType = $\
        struct wst__RequestedTokenCancelledType* RequestedTokenCancelled;
    wst__RequestSecurityTokenResponseType = $\
        char*                                KeyType;
    wst__RequestSecurityTokenResponseType = $\
        char*                                RequestType;
    wst__RequestSecurityTokenResponseType = $\
        char*                                TokenType;
    wst__RequestSecurityTokenResponseType = $\
        wst__EntropyType*                    Entropy;
    wst__RequestSecurityTokenResponseType = $\
        struct wst__BinaryExchangeType*      BinaryExchange;
    wst__RequestSecurityTokenResponseType = $\
        struct wst__AuthenticatorType*       Authenticator;

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:

struct wst__RequestSecurityTokenResponseType response;
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";
saml2__AssertionType *saml2;
// 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)
{
if (saml2->saml2__Conditions->NotBefore)
printf("Not before %s\n", soap_dateTime2s(soap, *saml2->saml2__Conditions->NotBefore));
if (saml2->saml2__Conditions->NotOnOrAfter)
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);
if (saml2->saml2__Conditions->NotBefore && *saml2->saml2__Conditions->NotBefore > now)
... error // not valid yet
if (saml2->saml2__Conditions->NotOnOrAfter && *saml2->saml2__Conditions->NotOnOrAfter <= now)
... 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`.