Genivia Home Documentation
The HTTP-DA plugin

updated Tue Apr 19 2016
 
The HTTP-DA plugin

Table of Contents

Introduction

The upgraded HTTP digest authentication plugin for gSOAP adds support for the RFC7616 draft that is backwards compatible with RFC2617. The new plugin adds SHA-256 (and SHA-512/256 when OpenSSL supports it) algorithms, including the -sess variants. To maintain backwards compatibility with RFC2617 the MD5 algorithm is still supported but not recommended.

HTTP digest authentication does not transmit the user id and password for authentication. Instead, a server negotiates credentials (username and/or password) with a client using cryptographic hashing algorithms with nonce values to prevent replay attacks.

By contrast, HTTP basic authentication is not safe over unencrypted channels because the password is transmitted to the server unencrypted. Therefore, this mechanism provides no confidentiality protection for the transmitted credentials. HTTPS is typically preferred over or used in conjunction with HTTP basic authentication.

To support HTTP digest authentication in favor of HTTP basic authentication, you will need to install OpenSSL and follow these steps to build your projects:

The plugin is MT-safe by means of internal mutex locks. Mutex ensures exclusive access and updates of the shared session store with nonces to prevent replay attacks.

Client-side usage

HTTP basic authentication is the default authentication mechanism supported by gSOAP. You can set the basic authentication credentials at the client-side with:

soap.userid = "<userid>";
soap.passed = "<passwd>";
if (soap_call_ns__method(&soap, ...))
... // error

HTTP basic authentication should never be used over plain HTTP, because the credentials (the ID and password) are transmitted in the clear in base64 encoded form which is easily reversible. This mechanism is safer to use over HTTPS, because the HTTP headers and body are encrypted.

This upgraded HTTP digest authentication plugin supports RFC7616 and RFC2617. RFC7616 adds SHA2 and is backwards compatible to clients that use MD5. The MD5 algorithm is not allowed in FIPS making SHA-256 or SHA-512-256 digest algorithms mandatory. The client-side of the plugin handles both RFCs automatically.

To use HTTP digest authentication with gSOAP, register the http_da plugin as follows:

#include "httpda.h"
soap_register_plugin(&soap, http_da);

To make a client-side service call you will need to create a digest store http_da_info. The store holds the digest information locally on your machine to manage repeated authentication challenges from all servers you connect to. Use http_da_save() to add credentials to the store and release the store with http_da_release() when you no longer need the credentials.

The http_da_info store is intended for one thread to issue a sequence of calls that are all authenticated without requiring (re)negotiation. You should not share the http_da_info store with multiple threads, unless you use mutex locks.

Here is an example:

struct http_da_info info;
if (soap_call_ns__method(&soap, ...))
{
if (soap.error == 401) // HTTP authentication is required
{
http_da_save(&soap, &info, "<authrealm>", "<userid>", "<passwd>");
if (soap_call_ns__method(&soap, ...)) // try again
... // error
http_da_release(&soap, &info);
}
else
... // other error
}

The "\<authrealm\>" string is the protected realm of the server that requires authorization. This string can be obtained with the soap.authrealm string after an unsuccessful non-authenticated call so you can use it to save credentials to the digest store:

if (soap_call_ns__method(&soap, ...))
{
if (soap.error == 401) // HTTP authentication is required
{
const char *realm = soap.authrealm;
http_da_save(&soap, &info, realm, "<userid>", "<passwd>");
...
}
else
... // error
}

Before a second call is made to the same endpoint that requires authentication, you must restore the authentication state with http_da_restore(), then use it, and finally release it with http_da_release():

struct http_da_info info;
bool auth = false;
if (soap_call_ns__method(&soap, ...))
{
if (soap.error == 401) // HTTP authentication is required
{
http_da_save(&soap, &info, "<authrealm>", "<userid>", "<passwd>");
auth = true;
}
else
... // other error
}
if (soap_call_ns__method(&soap, ...))
... // error
if (auth)
http_da_restore(&soap, &info);
if (soap_call_ns__method(&soap, ...))
... // error
soap_destroy(&soap); // okay to dealloc data
soap_end(&soap); // okay to dealloc data
if (auth)
http_da_restore(&soap, &info);
if (soap_call_ns__method(&soap, ...))
... // error
if (auth)
http_da_release(&soap, &info);
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);

For HTTP proxies requiring HTTP digest authenticaiton, use the 'proxy' functions of the plugin:

struct http_da_info info;
...
if (soap_call_ns__method(&soap, ...))
{
if (soap.error == 407) // HTTP proxy authentication is required
{
http_da_proxy_save(&soap, &info, "<authrealm>", "<userid>", "<passwd>");
auth = true;
}
else
... // error
}
if (auth)
http_da_proxy_restore(&soap, &info);
if (soap_call_ns__method(&soap, ...))
... // error
http_da_proxy_release(&soap, &info);
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);

Client example

A client authenticating against a server:

soap_register_plugin(&soap, http_da);
// try calling without authenticating
if (soap_call_ns__method(&soap, ...))
{
if (soap.error == 401) // HTTP authentication is required
{
if (!strcmp(soap.authrealm, authrealm)) // check authentication realm
{
struct http_da_info info; // to store userid and passwd
http_da_save(&soap, &info, authrealm, userid, passwd);
// call again, now with credentials
if (soap_call_ns__method(&soap, ...) == SOAP_OK)
{
... // process response data
soap_end(&soap);
... // userid and passwd were deallocated (!)
http_da_restore(&soap, &info); // get userid and passwd after soap_end()
if (!soap_call_ns__method(&soap, ...) == SOAP_OK)
... // error
http_da_release(&soap, &info); // free data and remove userid and passwd

A client authenticating against a proxy:

soap_register_plugin(&soap, http_da);
// try calling without authenticating
if (soap_call_ns__method(&soap, ...))
{
if (soap.error == 407) // HTTP authentication is required
{
if (!strcmp(soap.authrealm, authrealm)) // check authentication realm
{
struct http_da_info info; // to store userid and passwd
// call again, now with credentials
if (soap_call_ns__method(&soap, ...) == SOAP_OK)
{
... // process response data
soap_end(&soap);
... // userid and passwd were deallocated (!)
http_da_proxy_restore(&soap, &info); // get userid and passwd after soap_end()
if (!soap_call_ns__method(&soap, ...) == SOAP_OK)
... // error
http_da_proxy_release(&soap, &info); // free data and remove userid and passwd

Server-side usage

As explained in the gSOAP user guid, server-side HTTP basic authentication is enforced by simply checking the soap.userid and soap.passwd values in a service method that requires client authentication:

soap_register_plugin(&soap, http_da);
...
soap_serve(&soap);
...
int ns__method(struct soap *soap, ...)
{
if (!soap->userid || !soap->passwd || strcmp(soap->userid, "<userid>") || strcmp(soap->passwd, "<passwd>"))
return 401; // HTTP authentication required
...
}

HTTP digest authentication is verified differently, because digests are compared, not passwords:

soap_register_plugin(&soap, http_da);
...
soap_serve(&soap);
...
int ns__method(struct soap *soap, ...)
{
if (soap->authrealm && soap->userid)
{
passwd = ... // database lookup on userid and authrealm to find passwd
if (!strcmp(soap->authrealm, authrealm) && !strcmp(soap->userid, userid))
{
if (!http_da_verify_post(soap, passwd)) // HTTP POST DA verification
{
... // process request and produce response
return SOAP_OK;
}
}
}
soap->authrealm = authrealm; // realm to send to client
return 401; // Not authorized, challenge with digest authentication

The http_da_verify_post() function checks the HTTP POST credentials by computing and comparing a digest of the password. To verify an HTTP GET operation, use http_da_verify_get() instead.

RFC7616 recommends SHA2 over MD5. The MD5 algorithm is not allowed in FIPS and SHA-256 or SHA-512-256 are mandatory. The upgrade plugin uses SHA-256 as the default algorithm and reverts to MD5 only if required by a client that does not support RFC7616.

The default SHA-256 digest algorithm is enabled automatically. However, at the server side you can also use a plugin registry option to set a different algorithm as the default:

soap_register_plugin_arg(&soap, http_da, <option>);

where <option> is one of:

When non-MD5 option is selected, the server will present that digest algorithm together with the MD5 authentication algorithm as challenge to the client. If the client is upgraded to RFC7616 it selects the newer protcol. If the client is not upgraded it will select the older MD5-based protocol.

To revert to RFC2617 use http_da_md5().

Server example

soap_register_plugin(&soap, http_da);
...
soap_serve(&soap);
...
int ns__method(struct soap *soap, ...)
{
if (soap->userid && soap->passwd) // Basic authentication
{
if (!strcmp(soap->userid, userid) && !strcmp(soap->passwd, passwd))
{
... // can also check soap->authrealm
... // process request and produce response
return SOAP_OK;
}
}
else if (soap->authrealm && soap->userid) // Digest authentication
{
passwd = ... // database lookup on userid and authrealm to find passwd
if (!strcmp(soap->authrealm, authrealm) && !strcmp(soap->userid, userid))
{
if (!http_da_verify_post(soap, passwd)) // HTTP POST DA verification
{
... // process request and produce response
return SOAP_OK;
}
}
}
soap->authrealm = authrealm; // realm to send to client
return 401; // Not authorized, challenge with digest authentication
}

Limitations

HTTP digest authentication cannot be used with streaming MTOM/MIME/DIME attachments. Non-streaming MTOM/MIME/DIME attachments are handled just fine. MTOM/MIM/DIME attachment streaming is automatically turned off by the plugin and attachment data is buffered rather than streamed.