Jeff Sanders Technical Blog

I am a Microsoft employee that has worked on all aspects of the Web Stack for a long time. I hope these blogs are useful to you! Use this information at your own risk.


<< Go Back

Wwsapi Windows Web Services Api Http Kerberos Client

- 31 Mar 2009

Further adventures with the WWSAPI Beta.  If you find this useful please take the time to Jot me a quick comment or note!

I decided to investigate these API’s and make a simple call to the same WebService in my previous blog post using Kerberos.  I first copied the WebService inside my network so I could use Kerberos and configured the IIS 6 WebSite to use Windows Integrated authentication only.

There are several examples of using Negotiate with SSL but I did not want to use SSL.  I figured all I needed to do was remove the SSL bindings from the samples…  WRONG!

Here are the other steps I took and the challenges I faced:

</p>

</font><font color=#008000 size=2><font color=#008000 size=2>// use the default client credential if the thread opening the channel/proxy is impersonating,
</font></font><font color=#008000 size=2><font color=#008000 size=2>// the thread token will be used; otherwise, the process token will be used
</font></font>WS_DEFAULT_WINDOWS_INTEGRATED_AUTH_CREDENTIAL defaultCred = {}; <font color=#008000 size=2><font color=#008000 size=2>// zero out the struct

</font></font> </p>

defaultCred.credential.credentialType = WS_DEFAULT_WINDOWS_INTEGRATED_AUTH_CREDENTIAL_TYPE;

</font><font color=#008000 size=2><font color=#008000 size=2>// declare and initialize properties to set the authentication scheme to Negotiate
</font></font><font color=#008000 size=2><font color=#008000 size=2>// Since the default scheme for WWSAPI header authentication is Negotiate, this property
</font></font><font color=#008000 size=2><font color=#008000 size=2>// may be omitted but included for clarity.
</font></font>ULONG scheme = WS_HTTP_HEADER_AUTH_SCHEME_NEGOTIATE;

  </p>

WS_SECURITY_BINDING_PROPERTY headerAuthBindingProperties [1] =
{
    { WS_SECURITY_BINDING_PROPERTY_HTTP_HEADER_AUTH_SCHEME, &scheme, </font><font color=#0000ff size=2><font color=#0000ff size=2>sizeof</font></font>(scheme) }
}; </p>

</font><font color=#008000 size=2><font color=#008000 size=2>// declare and initialize an header authentication security binding
</font></font>WS_HTTP_HEADER_AUTH_SECURITY_BINDING headerAuthBinding = {}; <font color=#008000 size=2><font color=#008000 size=2>// zero out the struct

</font></font> </p>

headerAuthBinding.binding.bindingType = WS_HTTP_HEADER_AUTH_SECURITY_BINDING_TYPE;
headerAuthBinding.binding.properties = headerAuthBindingProperties;
headerAuthBinding.binding.propertyCount = WsCountOf(headerAuthBindingProperties);
headerAuthBinding.clientCredential = &defaultCred.credential;

</p>

</font><font color=#008000 size=2><font color=#008000 size=2>// declare and initialize the array of all security bindings
</font></font>WS_SECURITY_BINDING* securityBindings[1] = { &headerAuthBinding.binding };

</p>

</font><font color=#008000 size=2><font color=#008000 size=2>// declare and initialize the security description
</font></font>WS_SECURITY_DESCRIPTION securityDescription = {}; <font color=#008000 size=2><font color=#008000 size=2>// zero out the struct
</font></font>securityDescription.securityBindings = securityBindings;
securityDescription.securityBindingCount = WsCountOf(securityBindings);
securityDescription.properties = securityProperties;
securityDescription.propertyCount = WsCountOf(securityProperties);

<font color=#008000 size=2><font color=#008000 size=2> </p>

// Create the proxy
</font></font>hr = WsCreateServiceProxy(
WS_CHANNEL_TYPE_REQUEST,
WS_HTTP_CHANNEL_BINDING,
&securityDescription,
<font color=#008000 size=2><font color=#008000 size=2>// added for Negotiate
</font></font>NULL,
0,
NULL,
,
&serviceProxy,
error);

</div> This looked good but failed with an error when I called the WebService method: E_INVALIDARG.

 

Further investigation revealed that I needed to set the WS\_PROTECTION\_LEVEL appropriately.  Since the message is NOT using SSL we need to set this property as follows:

<font color=#008000 size=2><font color=#008000 size=2>// set the protection level to none – you have to do this to avoid the error: E_INVALIDARG when you call a SOAP method
</font></font>WS_PROTECTION_LEVEL protectionLevel = WS_PROTECTION_LEVEL_NONE;
WS_SECURITY_PROPERTY securityProperties[1] = {
{WS_SECURITY_PROPERTY_TRANSPORT_PROTECTION_LEVEL, &protectionLevel,
<font color=#0000ff size=2><font color=#0000ff size=2>sizeof</font></font>(protectionLevel)}
};
</p> </div> and then set these on the securityDescription like this:

securityDescription.properties = securityProperties;
securityDescription.propertyCount = WsCountOf(securityProperties);

OK, home free right?  No…  Now I was getting this error from the call to _HelloWorld.   hr 0x803d0000 The input data was not in the expected format or did not have the expected value.   Since the ‘input data’ seemed to be the issue I took a Network Monitor trace and found that the difference between my anonymous test and this new Kerberos Test (other than the Negotiate headers etc…) was that the SOAP envelope looked totally different!  In the Kerberos case I found this:

<s:Envelope xmlns:a=”<font color=#0000ff size=3 face=Calibri>http://www.w3.org/2005/08/addressing</font>” xmlns:s=”<font color=#0000ff size=3 face=Calibri>http://www.w3.org/2003/05/soap-envelope</font>“><s:Header ><a:Action s:mustUnderstand=”1″><font color=#0000ff size=3 face=Calibri>http://tempuri.org/HelloWorld</a:Action>urn:uuid:ba48938e-97a2-4dba-9e3f-cf9378533f8f<a:To</font> s:mustUnderstand=”1″><font color=#0000ff size=3 face=Calibri>http://jsandershv2003/WebSite/MyWebService.asmx</a:To></s:Header><HelloWorld</font></a> xmlns=”<font color=#0000ff size=3 face=Calibri>http://tempuri.org/</font>“/></s:Body></s:Envelope></p>

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 

Error:

 

System.Web.Services.Protocols.SoapHeaderException: SOAP header Action was not understood…  

at System.Web.Services.Protocols.SoapHeaderHandling.SetHeaderMembers(SoapHeaderCollection headers,

Object target,

SoapHeaderMapping[] mappings,

SoapHeaderDirection direction, Boolean client)..  

at System.Web.Services.Protocols.SoapServerProtocol.CreateServerInstance()..  

at System.Web.Services.Protocols.WebServiceHandler.Invoke().. 

 at System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()

 

After investigation I found that to get rid of the Messaging headers I needed to set some channel properties:

WS_CHANNEL_PROPERTY channelProperties[1];
ULONG channelPropertyCount = 0;
WS_ADDRESSING_VERSION addressingVersion = WS_ADDRESSING_VERSION_TRANSPORT;
channelProperties[channelPropertyCount].id = WS_CHANNEL_PROPERTY_ADDRESSING_VERSION;
channelProperties[channelPropertyCount].value = &addressingVersion ;
channelProperties[channelPropertyCount].valueSize =
<font color=#0000ff size=2><font color=#0000ff size=2>sizeof</font></font>(addressingVersion );
channelPropertyCount++; // in this case only one property

</p>

// Then you pass the channelProperties and channelPropertyCount to WsCreateServiceProxy (or WsCreateChannel if you are working at channel layer).

hr = WsCreateServiceProxy(
WS_CHANNEL_TYPE_REQUEST,
WS_HTTP_CHANNEL_BINDING,
&securityDescription, </font><font color=#008000 size=2><font color=#008000 size=2>// added for Negotiate
</font></font>NULL,
0,
channelProperties,
<font color=#008000 size=2><font color=#008000 size=2>// channel properties
</font></font>channelPropertyCount, <font color=#008000 size=2><font color=#008000 size=2>// channel property count
</font></font>&serviceProxy,
error);

</div> **Success!** **Now the complete code listing for your enjoyment ([Copy Code](javascript:CopyCode('wwsapikerbcodesection1');)):**
<font color=#008000 size=2><font color=#008000 size=2></p> // WWSAPITest.cpp : Defines the entry point for the console application. // </font></font><font color=#0000ff size=2><font color=#0000ff size=2> #include</font></font> <font color=#a31515 size=2><font color=#a31515 size=2>“stdafx.h” </font></font><font color=#0000ff size=2><font color=#0000ff size=2>#include</font></font> <font color=#a31515 size=2><font color=#a31515 size=2> </font></font><font color=#0000ff size=2><font color=#0000ff size=2>#include</font></font> <font color=#a31515 size=2><font color=#a31515 size=2>“.\wsdl\MyWebService.wsdl.h” </font></font><font color=#0000ff size=2><font color=#0000ff size=2>int</font></font> mainHttpKerb(<font color=#0000ff size=2><font color=#0000ff size=2>int</font></font> argc, _TCHAR* argv[]) {     HRESULT hr;     WS_ERROR* error;     WS_HEAP* heap;     WS\_SERVICE\_PROXY* serviceProxy;     hr = S_OK;     error = NULL;     heap = NULL;     serviceProxy = NULL; <font color=#008000 size=2><font color=#008000 size=2>    // Creating error object </font></font>    hr = WsCreateError(NULL, 0, &error); <font color=#0000ff size=2><font color=#0000ff size=2>    if</font></font> (FAILED(hr))     {         wprintf (L<font color=#a31515 size=2><font color=#a31515 size=2>“Failed to create Error object\n”</font></font>);         <font color=#0000ff size=2><font color=#0000ff size=2>return</font></font> -1;      } <font color=#008000 size=2><font color=#008000 size=2>    // Creating heap handle </font></font>    hr = WsCreateHeap(10000000, 0, NULL, 0, &heap, error); <font color=#0000ff size=2><font color=#0000ff size=2>    if</font></font> (FAILED(hr))      {         wprintf (L<font color=#a31515 size=2><font color=#a31515 size=2>“Failed to create Heap object\n”</font></font>); <font color=#0000ff size=2><font color=#0000ff size=2>        if</font></font> (heap != NULL)         {             WsFreeHeap(heap);             heap = NULL;         } <font color=#0000ff size=2><font color=#0000ff size=2>        if</font></font> (error != NULL)         {             WsFreeError(error);             error = NULL;         } <font color=#0000ff size=2><font color=#0000ff size=2>        return</font></font> -1;     } <font color=#008000 size=2><font color=#008000 size=2>    ////////////////kerberos </font></font><font color=#008000 size=2><font color=#008000 size=2>    //// declare and initialize a windows credential </font></font><font color=#008000 size=2><font color=#008000 size=2>    // use the default client credential if the thread opening the channel/proxy is impersonating,  </font></font><font color=#008000 size=2><font color=#008000 size=2>    // the thread token will be used; otherwise, the process token will be used </font></font>    WS\_DEFAULT\_WINDOWS\_INTEGRATED\_AUTH_CREDENTIAL defaultCred = {}; <font color=#008000 size=2><font color=#008000 size=2>// zero out the struct </font></font>    defaultCred.credential.credentialType = WS\_DEFAULT\_WINDOWS\_INTEGRATED\_AUTH\_CREDENTIAL\_TYPE; <font color=#008000 size=2><font color=#008000 size=2>    // declare and initialize properties to set the authentication scheme to Negotiate </font></font><font color=#008000 size=2><font color=#008000 size=2>    // Since the default scheme for WWSAPI header authentication is Negotiate, this property  </font></font><font color=#008000 size=2><font color=#008000 size=2>    // may be omitted. </font></font>    ULONG scheme = WS\_HTTP\_HEADER\_AUTH\_SCHEME_NEGOTIATE; <font color=#008000 size=2><font color=#008000 size=2>    // set the protection level to none – you have to do this to avoid the error: E_INVALIDARG when you call a SOAP method </font></font>    WS\_PROTECTION\_LEVEL protectionLevel = WS\_PROTECTION\_LEVEL_NONE;     WS\_SECURITY\_PROPERTY securityProperties[1] = {         {WS\_SECURITY\_PROPERTY\_TRANSPORT\_PROTECTION_LEVEL, &protectionLevel, <font color=#0000ff size=2><font color=#0000ff size=2>sizeof</font></font>(protectionLevel)}     };     WS\_SECURITY\_BINDING_PROPERTY headerAuthBindingProperties [1] =     {         { WS\_SECURITY\_BINDING\_PROPERTY\_HTTP\_HEADER\_AUTH_SCHEME, &scheme, <font color=#0000ff size=2><font color=#0000ff size=2>sizeof</font></font>(scheme) }     }; <font color=#008000 size=2><font color=#008000 size=2><font color=#000000>    </font>// declare and initialize an header authentication security binding </font></font>    WS\_HTTP\_HEADER\_AUTH\_SECURITY_BINDING headerAuthBinding = {}; <font color=#008000 size=2><font color=#008000 size=2>// zero out the struct </font></font>    headerAuthBinding.binding.bindingType = WS\_HTTP\_HEADER\_AUTH\_SECURITY\_BINDING\_TYPE;     headerAuthBinding.binding.properties = headerAuthBindingProperties;     headerAuthBinding.binding.propertyCount = WsCountOf(headerAuthBindingProperties);     headerAuthBinding.clientCredential = &defaultCred.credential; <font color=#008000 size=2><font color=#008000 size=2><font color=#000000>    </font>// declare and initialize the array of all security bindings </font></font>    WS\_SECURITY\_BINDING* securityBindings[1] = { &headerAuthBinding.binding }; <font color=#008000 size=2><font color=#008000 size=2><font color=#000000>    </font>// declare and initialize the security description </font></font>    WS\_SECURITY\_DESCRIPTION securityDescription = {}; <font color=#008000 size=2><font color=#008000 size=2>// zero out the struct </font></font>    securityDescription.securityBindings = securityBindings;     securityDescription.securityBindingCount = WsCountOf(securityBindings);     securityDescription.properties = securityProperties;     securityDescription.propertyCount = WsCountOf(securityProperties); <font color=#008000 size=2><font color=#008000 size=2><font color=#000000>    </font>// size this array to the number of properties you will have </font></font>    WS\_CHANNEL\_PROPERTY channelProperties[1];     ULONG channelPropertyCount = 0;     WS\_ADDRESSING\_VERSION addressingVersion = WS\_ADDRESSING\_VERSION_TRANSPORT;     channelProperties[channelPropertyCount].id = WS\_CHANNEL\_PROPERTY\_ADDRESSING\_VERSION;     channelProperties[channelPropertyCount].value = &addressingVersion ;     channelProperties[channelPropertyCount].valueSize = <font color=#0000ff size=2><font color=#0000ff size=2>sizeof</font></font>(addressingVersion );     channelPropertyCount++; <font color=#008000 size=2><font color=#008000 size=2><font color=#000000>    </font>// add more channel properties here </font></font><font color=#008000 size=2><font color=#008000 size=2><font color=#000000>    </font>/*WS\_ENVELOPE\_VERSION soapVersion = WS\_ENVELOPE\_VERSION\_SOAP\_1_1; <font color=#000000>    </font>channelProperties[channelPropertyCount].id = WS\_CHANNEL\_PROPERTY\_ENVELOPE\_VERSION; <font color=#000000>    </font>channelProperties[channelPropertyCount].value = &soapVersion; <font color=#000000>    </font>channelProperties[channelPropertyCount].valueSize = sizeof(soapVersion); <font color=#000000>    </font>channelPropertyCount++;*/ <font color=#000000>    </font> </font></font><font color=#008000 size=2><font color=#008000 size=2><font color=#000000>    </font>// Then you pass the channelProperties and channelPropertyCount to WsCreateServiceProxy (or WsCreateChannel if you are working at channel layer). </font></font><font color=#008000 size=2><font color=#008000 size=2><font color=#000000>    </font>// Create the proxy </font></font>    hr = WsCreateServiceProxy(         WS\_CHANNEL\_TYPE_REQUEST,          WS\_HTTP\_CHANNEL_BINDING,          &securityDescription, <font color=#008000 size=2><font color=#008000 size=2>// added for Negotiate </font></font>        NULL,           0,          channelProperties, <font color=#008000 size=2><font color=#008000 size=2>// channel properties </font></font>        channelPropertyCount, <font color=#008000 size=2><font color=#008000 size=2>// channel property count </font></font>        &serviceProxy,          error); <font color=#0000ff size=2><font color=#0000ff size=2> <font color=#000000>    </font>if</font></font> (FAILED(hr))      {         WsFreeHeap(heap);         WsFreeError(error); <font color=#0000ff size=2><font color=#0000ff size=2>    <font color=#000000>    </font>return</font></font> -1;     }     WS\_ENDPOINT\_ADDRESS address = {};     WS\_STRING Url = WS\_STRING_VALUE(L<font color=#a31515 size=2><font color=#a31515 size=2>“http://jsandershv2003/WebSite/MyWebService.asmx”</font></font>);     address.url = Url;     hr = WsOpenServiceProxy(serviceProxy, &address, NULL, error); <font color=#0000ff size=2><font color=#0000ff size=2><font color=#000000>    </font>if</font></font> (FAILED(hr))      {         WsFreeServiceProxy(serviceProxy);         WsFreeHeap(heap);         WsFreeError(error); <font color=#0000ff size=2><font color=#0000ff size=2><font color=#000000>    </font>    return</font></font> -1;     }     WCHAR *aResult = NULL;     hr = WebServiceSoap12_HelloWorld(                         serviceProxy,                         &aResult,                          heap,                         NULL, 0,                         NULL,                         error); <font color=#0000ff size=2><font color=#0000ff size=2><font color=#000000>    </font>if</font></font> (SUCCEEDED(hr))     {             wprintf(L<font color=#a31515 size=2><font color=#a31515 size=2>“%s\n”</font></font>, aResult);     } <font color=#0000ff size=2><font color=#0000ff size=2><font color=#000000>    </font>else</font></font>      {         wprintf(L<font color=#a31515 size=2><font color=#a31515 size=2>“failed\n”</font></font>);     } <font color=#0000ff size=2><font color=#0000ff size=2><font color=#000000>    </font>if</font></font> (serviceProxy != NULL)     {          WsCloseServiceProxy(serviceProxy, NULL, error);         WsFreeServiceProxy(serviceProxy);         serviceProxy = NULL;     } <font color=#0000ff size=2><font color=#0000ff size=2><font color=#000000>    </font>if</font></font> (heap != NULL)     {         WsFreeHeap(heap);         heap = NULL;     } <font color=#0000ff size=2><font color=#0000ff size=2><font color=#000000>    </font>if</font></font> (error != NULL)     {         WsFreeError(error);         error = NULL;     }     <font color=#0000ff size=2><font color=#0000ff size=2> <font color=#000000>    </font>return</font></font> 0; } <font color=#0000ff size=2><font color=#0000ff size=2> int</font></font> _tmain(<font color=#0000ff size=2><font color=#0000ff size=2>int</font></font> argc, _TCHAR* argv[]) {        <font color=#0000ff size=2><font color=#0000ff size=2>return</font></font> mainHttpKerb(argc,argv); } </font></font></font></font></font></font></div>

<< Go Back