Welcome to MSDN Blogs Sign in | Join | Help

Common WWSAPI errors: property value set incorrectly

As mentioned in my previous post, WWSAPI follows a common pattern to set properties (which in most cases are just configuration settings). Each property structure consists of three fields: id, value and valueSize. The field id is an enum value of the property. The enum value has a corresponding type whose size should be set in the field valueSize. The value field is defined as void* and should always be a pointer to the actual type of the property. A common mistake is to assign the actual type to the value field. The result of that is typically Access Violation that brings down the process.

 

For example, WS_XML_SECURITY_TOKEN_PROPERTY is defined this way:

typedef struct _WS_XML_SECURITY_TOKEN_PROPERTY {

    WS_XML_SECURITY_TOKEN_PROPERTY_ID id;

    __field_bcount(valueSize) void* value;

    ULONG valueSize;

} WS_XML_SECURITY_TOKEN_PROPERTY;

 

The WS_XML_SECURITY_TOKEN_PROPERTY_ID enum contains a value WS_XML_SECURITY_TOKEN_PROPERTY_ATTACHED_REFERENCE with a corresponding type WS_XML_BUFFER*. The correct way to use this property is:

WS_XML_SECURITY_TOKEN_PROPERTY tokenProperty[1];

WS_XML_BUFFER* buffer = GetAttachedReference();

tokenProperty[0].id = WS_XML_SECURITY_TOKEN_PROPERTY_ATTACHED_REFERENCE;

tokenProperty[0].value = &buffer;

tokenProperty[0].valueSize = sizeof(buffer);

 

Assigning buffer to tokenProperty[0].value will not give you a compiler error, but will probably lead to AV when you run the program. If the property type is a non-pointer type (BOOL, ULONG, WS_DATETIME, etc), the compiler will catch the error if the value is directly assigned, but the error is less obvious when the type is a pointer type.

Common WWSAPI errors: A NULL WS_STRUCT_DESCRIPTION was specified.

When you use WsUtil to generate stub code and then work with the generated structures, you may get E_INVALIDARG when making the call to the service and, with WWSAPI tracing turned on, see the error message “A NULL WS_STRUCT_DESCRIPTION was specified.”.

 

WS_STRUCT_DESCRIPTION is one of the WWSAPI serialization structures and describes how a structure should be serialized into XML and deserialized from XML. When you use WsUtil to generate the stub code, lots of complex structures will be generated, including WS_STRUCT_DESCRIPTION. You’ll need to make sure that the structures are initialized properly. By default, if you use a structure that has either a base structure or derived structure(s), you should use a generated function to initialize it. For example, say the schema in the WSDL defines a type Location that has two derived types UserLocation and GeocodeLocation, WsUtil will generate the Location type like the following:

 

#if !defined(WS_CPLUSPLUS)

typedef struct Location

{

    const struct _WS_STRUCT_DESCRIPTION* _type;

    double Altitude;

    double Latitude;

    double Longitude;

} Location;

void WINAPI Location_Init(Location*);

struct UserLocation* WINAPI Location_AsUserLocation(Location*);

struct GeocodeLocation* WINAPI Location_AsGeocodeLocation(Location*);

#endif

 

Notice the function Location_Init after the structure. In order to properly initialize a Location structure, you need to call the Location_Init function like this:

 

    Location location = {NULL, 0, latitude, longitude};

    Location_Init(&location);

 

When you get the E_INVALIDARG with the error message I mentioned above, it’s because you forgot to call the *_Init function, which will set the WS_STRUCT_DESCRIPTION structure (the location._type field in this example). The Location_Init is generated like the following:

 

#if !defined(WS_CPLUSPLUS)

void WINAPI Location_Init(Location* _Location)

{

    ((Location*)_Location)->_type =

        (WS_STRUCT_DESCRIPTION*)&dev_virtualearth_net_webservices_v1_common1_xsd.globalTypes.Location;

}

#endif

 

The *_Init functions (and the _type field) won’t be generated for types that don’t have base type or derived types. When you are consuming a service without knowing the details of the server type definitions, it’s always a good idea to open up the generated header file to see if *_Init functions are generated for the structures you use.

 

Please note that this is the default behavior. If you define a preprocessor WS_CPLUSPLUS, the requirement is different. I’ll discuss that in a later post.

 

For further reading on WsUtil’s support of XML schema, please go to http://msdn.microsoft.com/en-us/library/dd815313(VS.85).aspx.

 

WWSAPI to WCF interop 11: security binding templates

In my previous post on WsUtil, I explained how the generated helper functions can simplify the creation of WS_SERVICE_PROXY and WS_SERVICE_ENDPOINT. In both of these functions, the first parameter is a pointer to a same binding template. When no security is used in the binding, the template will be a simple wrapper of channel properties (e.g. WS_HTTP_BINDING_TEMPLATE, WS_TCP_BINDING_TEMPLATE), in which case the parameter can be NULL if no channel properties need to be set (pointer to an empty structure will also work). When security is used in the binding, passing NULL or empty structure will not work in cases where the client needs to be authenticated (HTTPS without client certificate being the only exception). This is because the template function requires the client credential to be specified explicitly. Next I’ll give an example of how to fill in a binding template for HTTP Basic header authentication over HTTPS. In this scenario, WsUtil will generate the helper function with the following prototype:

 

HRESULT BasicHttpBinding_ICalculator_CreateServiceProxy(

    __in_opt WS_HTTP_SSL_HEADER_AUTH_BINDING_TEMPLATE* templateValue,

    __in_ecount_opt(proxyPropertyCount) const WS_PROXY_PROPERTY* proxyProperties,

    __in const ULONG proxyPropertyCount,

    __deref_out_opt WS_SERVICE_PROXY** _serviceProxy,

    __in_opt WS_ERROR* error);

 

struct BasicHttpBinding_ICalculatorFunctionTable;

HRESULT BasicHttpBinding_ICalculator_CreateServiceEndpoint(

    __in_opt WS_HTTP_SSL_HEADER_AUTH_BINDING_TEMPLATE* templateValue,

    __in_opt CONST WS_STRING* address,

    __in_opt struct WSHttpBinding_IChatFunctionTable* functionTable,

    __in_opt WS_SERVICE_SECURITY_CALLBACK authorizationCallback,

    __in_ecount_opt(endpointPropertyCount) WS_SERVICE_ENDPOINT_PROPERTY* endpointProperties,

    __in const ULONG endpointPropertyCount,

    __in WS_HEAP* heap,

    __deref_out_opt WS_SERVICE_ENDPOINT** serviceEndpoint,

    __in_opt WS_ERROR* error);

 

WS_HTTP_SSL_HEADER_AUTH_BINDING_TEMPLATE is defined this way:

 

struct _WS_HTTP_SSL_HEADER_AUTH_BINDING_TEMPLATE {

    WS_CHANNEL_PROPERTIES channelProperties;

    WS_SECURITY_PROPERTIES securityProperties;

    WS_SSL_TRANSPORT_SECURITY_BINDING_TEMPLATE sslTransportSecurityBinding;

    WS_HTTP_HEADER_AUTH_SECURITY_BINDING_TEMPLATE httpHeaderAuthSecurityBinding;

};

struct _WS_SSL_TRANSPORT_SECURITY_BINDING_TEMPLATE {

    WS_SECURITY_BINDING_PROPERTIES securityBindingProperties;

    WS_CERT_CREDENTIAL* localCertCredential;

};

struct _WS_HTTP_HEADER_AUTH_SECURITY_BINDING_TEMPLATE {

    WS_SECURITY_BINDING_PROPERTIES securityBindingProperties;

    WS_WINDOWS_INTEGRATED_AUTH_CREDENTIAL* clientCredential;

};

 

The naming convention of these structures may not be clear at first look, but is actually straightforward. The first parameter of the two helper functions is called a binding template (ending with “_BINDING_TEMPLATE”) – the word “BINDING” corresponds to the wsdl:binding element in the WSDL document (more specifically it corresponds to the extension information – policy extension through wsp:PolicyReference or soap extension through soap:binding – in the wsdl:binding element).  The extension information is mainly organized into two parts: the soap extension information and non-security policy information are put into WS_CHANNEL_PROPERTIES, while all the security information into WS_SECURITY_PROPERTIES and structures called security binding templates (ending with “_SECURITY_BINDING_TEMPLATE”).

 

In this scenario, the binding template contains two security binding templates: one for SSL, the other for HTTP header authentication. As I mentioned earlier, the client credential should be explicitly specified. Since we are only using Windows credential for HTTP header authentication, we only need to specify the clientCredential in the WS_HTTP_HEADER_AUTH_SECURITY_BINDING_TEMPLATE (The generated code will contain information on which scheme to use). Here is the code to initialize WS_HTTP_SSL_HEADER_AUTH_BINDING_TEMPLATE:

 

        WS_STRING_WINDOWS_INTEGRATED_AUTH_CREDENTIAL explicitCredential = {

            {WS_STRING_WINDOWS_INTEGRATED_AUTH_CREDENTIAL_TYPE},

            WS_STRING_VALUE(L"FooUser"),

            WS_STRING_VALUE(L"F00Pwd!"),

            WS_STRING_VALUE(L"FooDomain")

        };

        WS_HTTP_SSL_HEADER_AUTH_BINDING_TEMPLATE bindingTemplate = {};

        bindingTemplate.httpHeaderAuthSecurityBinding.clientCredential = &explicitCredential.credential;

 

If the server requires client certificate for SSL, you’ll have to specify it in bindingTemplate.sslTransportSecurityBinding.localCertCredential.

 

Note: the corresponding WCF endpoint uses this binding config:

 

      <basicHttpBinding>

        <binding name="basicWithSsl">

          <security mode="Transport">

            <transport clientCredentialType="Basic" />

          </security>

        </binding>

      </basicHttpBinding>

 

The binding can be created in code as following:

 

BasicHttpBinding basicWithSsl = new BasicHttpBinding(BasicHttpSecurityMode.Transport);

basicWithSsl.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

 

Common WWSAPI errors: usage asserts

One of the design principles of WWSAPI is to be very stringent at the API contract, as we believe ambiguous or loose API contract does not help developers write high quality applications. Being stringent means not only to document the API contract clearly, but also to enforce at runtime that the contract is honored. Considering the fact that it’s very easy to overlook special remarks on API contract in the documentation, we built many usage asserts into the product to ensure the integrity of WWSAPI objects or the contract of using the objects and API. The asserts are placed near the API entry points to make application failure more diagnosable. These asserts, when hit, will bring down the application (and therefore require immediate fixes). The user may see the Windows Error Reporting popup.

 

Here are the common API contract violations that will lead to usage asserts and bring down applications:

1.       Free WWSAPI objects in wrong states: e.g. freeing a WS_SERVICE_PROXY object without closing it first will crash your application.

2.       Use a single-threaded object in multiple threads concurrently: this is commonly seen in the use of WS_HEAP, but could happen to the other objects like WS_MESSAGE, WS_ERROR, WS_XML_READER and WS_XML_WRITER due to their dependency on WS_HEAP

3.       Use a freed WWSAPI object

 

If a debugger is attached to the crashing application with proper Windows symbols loaded, you may see a function near the top of the stack with self-explanatory name webservices!HandleApiContractViolation. You may see a more specific function above it like highlighted part in the example below:

 

KERNELBASE!DebugBreak+0x2

webservices!ReportFatalException+0xa4

webservices!MultiThread_not_allowed_fatal_error+0x32

webservices!HandleApiContractViolation+0xf6

 

Most of the contract violations will be caught at the first time you run the offending code. The asserts of single-threaded objects may not be easily caught due to the undeterministic nature of multi-threaded execution.

Common WWSAPI errors: wrong property value size

WWSAPI properties are designed to control the behavior of the WWSAPI objects like service proxy, channel, XML reader/writer, etc. Each property has a property id, an associated value type, a target object and allowed actions. All property structures have the same set of fields: a property id, a pointer to the value buffer and the size of the buffer. The property accessors follow the same design as well. When using WWSAPI properties, it’s important to know these rules:

1.       Only use in allowed functions: some properties can be used at creation time only while some can be set and get at any point of the object’s lifetime.

2.       Set the right value size

 

E_INVALIDARG (0x80070057) will be returned if either of the rules are broken. More than once I heard customer asking: “My codes works fine in x86 version. I get E_INVALIDARG when running x64 version”. The problem turned out to be that the property value size was wrong. The property value type was SIZE_T (e.g. WS_SERVICE_ENDPOINT_PROPERTY_BODY_HEAP_MAX_SIZE), which has different size on 32-bit and 64-bit build. The bug in code was that the actual property value was defined as ULONG (4 bytes) instead of SIZE_T. The property code checks the value size passed in against the expected size and returns E_INVALIDARG if they don’t match. Since SIZE_T is 4-byte in 32-bit build and 8-byte in 64-bit build, using ULONG will work fine on x86 machine but break on x64 machine.

 

The other similar error is the use of the type bool for properties with type BOOL. The builtin type bool in the newer VS++ compilers has 1 byte, whereas the Windows type BOOL has 4 bytes.

WWSAPI to WCF interop 10: WsUtil.exe, the silver bullet

In my previous post on interoperating with WCF BasicHttpBinding endpoint, I explained that you had to set the channel properties to match SOAP version and addressing version on the server side. Wouldn’t it be great if you don’t have to do all that? That’s one of the goal of building WsUtil.exe, the silver bullet for interoperating with WCF (and any other web services stacks, as long as WSDL is available) by generating the matching proxy/stub files from the WSDL files. Now, as promised before, let’s see how WsUtil.exe can help you build the matching binding.

 

WsUtil.exe takes WSDL files and/or XSD files as input, so you first need to get the WSDLs from the service endpoint. SvcUtil.exe, a WCF tool also included Win7 SDK, can be used to get the WSDL/XSD files. For example, if your service endpoint is http://localhost/CalculatorService/ and the service also exposes a MEX endpoint or HTTP GET endpoint, you can run SvcUtil.exe /t:metadata http://localhost/CalculatorService/ to download the WSDL/XSD files. You can then generate the proxy/stub files using this command:

WsUtil.exe *.wsdl *.xsd

 

The files generated will look like: tempuri.org.wsdl.h and tempuri.org.wsdl.c. The .c file contains all the definitions of function stubs and serialization structures that are not easy to read, but the header file contains only declarations and lots of comments. The header files are meant for developers to read. Once you generate the files, open the header file for the WSDL file (e.g. the tempura.org.wsdl.h). The file starts with lots of comments explaining the contents and usage. Scrolling down, you will see two helper function prototypes like these:

 

HRESULT BasicHttpBinding_ICalculator_CreateServiceProxy(

    __in_opt WS_HTTP_BINDING_TEMPLATE* templateValue,

    __in_ecount_opt(proxyPropertyCount) const WS_PROXY_PROPERTY* proxyProperties,

    __in const ULONG proxyPropertyCount,

    __deref_out_opt WS_SERVICE_PROXY** _serviceProxy,

    __in_opt WS_ERROR* error);

 

struct BasicHttpBinding_ICalculatorFunctionTable;

HRESULT BasicHttpBinding_ICalculator_CreateServiceEndpoint(

    __in_opt WS_HTTP_BINDING_TEMPLATE* templateValue,

    __in_opt CONST WS_STRING* address,

    __in_opt struct WSHttpBinding_IChatFunctionTable* functionTable,

    __in_opt WS_SERVICE_SECURITY_CALLBACK authorizationCallback,

    __in_ecount_opt(endpointPropertyCount) WS_SERVICE_ENDPOINT_PROPERTY* endpointProperties,

    __in const ULONG endpointPropertyCount,

    __in WS_HEAP* heap,

    __deref_out_opt WS_SERVICE_ENDPOINT** serviceEndpoint,

    __in_opt WS_ERROR* error);

 

The first function prototype is for creating WS_SERVICE_PROXY on the client side and the second is for creating WS_SERVICE_HOST on the server side. We only need to use the client side helper function.

 

    hr = BasicHttpBinding_IChat_CreateServiceProxy(

            NULL,

            NULL,

            0,

            &serviceProxy,

            error);

 

As you can see, instead of setting the two channel properties and then call WsCreateServiceProxy as shown in my previous post, you just need to call the helper function once to create the service proxy. You can then call WsOpenServiceProxy to open it before making calls that are listed in the same header file just below the helper functions.

 

You can set more channel properties (like send/receive timeout) in WS_HTTP_BINDING_TEMPLATE. In this scenario we just pass NULL as the template value since we don’t need to define any more channel properties.

 

For more information on WsUtil.exe, please refer to this WSDN page: http://msdn.microsoft.com/en-us/library/dd430644(VS.85).aspx.

Common WWSAPI errors: addressing version mismatch

WWSAPI supports two WS-Addressing versions: the existing W3C recommendation version (1.0) and the older 2004/08 version (0.9). WS-Addressing defines a set of SOAP headers to describe the message recipient, targeted action and some other basic messaging information. When HTTP transport is used, WWSAPI also supports a mode that does not use WS-Addressing. This is called transport addressing mode, where the recipient and action information is carried in HTTP headers. The addressing mode can be set through WS_CHANNEL_PROPERTY_ADDRESSING_VERSION, which defaults to WS-Addressing 1.0. When a WWSAPI client using the default addressing version (1.0) communicates with a WCF endpoint using BasicHttpBinding (transport addressing), you will see the following error messages in the client side trace or in the WS_ERROR object if you use it:

 

The server returned a SOAP envelope fault: 'One or more mandatory SOAP header blocks not understood'.

The fault reason was: 'The header 'Action' from the namespace 'http://www.w3.org/2005/08/addressing' was not understood by the recipient of this message, causing the message to not be processed.  This error typically indicates that the sender of this message has enabled a communication protocol that the receiver cannot process.  Please ensure that the configuration of the client's binding is consistent with the service's binding. '.

 

In this case, since the client sends a WS-Addressing Action header that is marked with mustUnderstand=”1”, the server sends back a fault complaining the Action header is not understood. The actual WWSAPI error code returned is WS_E_INVALID_FORMAT (0x803d0000) since the SOAP fault does not contain the required infrastructure (WS-Addressing) information the client is looking for.

 

If the WCF server is using WS-Addressing 1.0 but the WWSAPI client is using transport addressing, the error code will be WS_E_ENDPOINT_FAULT_RECEIVED (0x803d0013). The error messages will look like the following:

 

The body of the received message contained a fault.

The fault reason was: 'The SOAP action specified on the message, '', does not match the HTTP SOAP Action, 'Add'. '.

 

To fix the error in either case, you’ll need to set the channel properties on the WWSAPI client to use the right addressing version (as well as the right SOAP version) as described in my previous post.

Common WWSAPI errors: SOAP version mismatch

There are two versions of SOAP supported by WWSAPI and most other web services stacks: SOAP 1.1 and SOAP 1.2. Although the basic message layout in the two SOAP versions is the same (Header and Body inside Envelope), there are three main differences: the namespace, the content type for HTTP binding and the SOAP fault layout. Since the HTTP content type header is typically the first thing a receiver checks, the error message for SOAP version mismatch often refers to content type instead of namespace. When a WWSAPI client using the default SOAP version (1.2) communicates with a WCF endpoint using BasicHttpBinding (SOAP 1.1), you will see the following error messages in the client side trace or in the WS_ERROR object if you use it:

 

There was an error communicating with the endpoint at 'http://localhost:8000/CalculatorService/'.

The server returned HTTP status code '415 (0x19F)' with text 'Cannot process the message because the content type 'application/soap+xml; charset=utf-8' was not the expected type 'text/xml; charset=utf-8'.'.

The format of the HTTP request was not supported by the server.

 

The actual WWSAPI error code returned is WS_E_INVALID_FORMAT (0x803d0000), which typically means that certain contract (API contract, network protocol contract, etc.) is not followed. In this case, 415 status code is returned when the client is expecting a 200/202/500 status code. Since WS_E_INVALID_FORMAT can be returned in many error cases, you probably need to turn on WWSAPI tracing or look at the error strings in the WS_ERROR object in order to figure out the exact cause. From the second error string above, you can tell that the error is due to SOAP version mismatch since SOAP 1.2 uses content type application/soap+xml, whereas SOAP 1.1 uses content type text/xml.

 

To fix this error, you’ll need to set the channel properties on the WWSAPI client to use the right SOAP version (as well as the right WS-Addressing version) as described in my previous post.

WWSAPI RC bits for downlevel platforms are available!

Not many people are aware of our commitments to ship WWSAPI to downlevel platforms. Nikola just sent out an email annoucing the availability of the RC bits for downlevel platforms: https://connect.microsoft.com/WNDP/Downloads/DownloadDetails.aspx?DownloadID=18901. There are bits for XP SP3, Windows 2003 SP2, Vista SP1 and Windows 2008 SP1.
Posted by haoxu | 2 Comments
Filed under: ,

One thought on testing

I have been in the testing discipline for almost 8 years. Over the years there have been different forces to push me into development. Somehow I stayed, despite the fact many people outside and even inside Microsoft don’t give much regard to testers. As I think of relationship between development discipline and testing discipline, I think about the relationship of parents and teachers. As a parent, I’d make decisions for my daughters on which programs they enroll in, what books they read, which clothes and shoes they wear, how they should behave in difference situations, etc, not unlike developers making decisions on what features to implement and how these features work. When the girls go to school, it’s the teachers who spend the time playing with them, observing them, evaluating how they behave as human beings and helping them become better. Like any other caring parent, I am confident that I know everything about my daughters (not unlike the developers being confident about their features), but I am often surprised by the feedback the teachers give me. This is what testers do all the time – surprise the developers by identifying issues, future improvement and opportunities.

 

As much as I appreciate my daughters' teachers, I appreciate being a tester, knowing full well that not all testers/teachers are made equal.

Posted by haoxu | 2 Comments
Filed under:

LiveID support in WWSAPI on Win7

In Windows 7, LiveID can be used with WWSAPI in two scenarios:

1.       SSPI over TCP (WS_TCP_SSPI_TRANSPORT_SECURITY_BINDING) with the default SPNEGO package: on both client and server

2.       HTTP Negotiate header authentication (WS_HTTP_HEADER_AUTH_SECURITY_BINDING): on the client side only

 

When you install the latest LiveID SSP onto your Windows 7 machine and configure the machine and live site properly, you can use LiveID credential as the client credential. This client credential has to be represented as WS_OPAQUE_WINDOWS_INTEGRATED_AUTH_CREDENTIAL, whose opaqueAuthIdentity field can be obtained by calling the new Win7 API SspiPromptForCredentials. On the server side, you can add a Active Directory mapping to map the live ID’s unique ID to a domain account. When the authentication completes, the server will get a client token of the mapped domain account.

 

Since LiveID SSP does not support being called from kernel mode yet, this doesn’t work when Negotiate header authentication happens in HTTP.SYS, which is used by WWSAPI server. However, you can use this with server hosted in IIS by turning off kernel mode authentication.

 

The configuration is a little involved. If you are interested, send me an email and I’ll be happy to explain in details.

Posted by haoxu | 1 Comments

More on HTTP header authentication

My previous post on header authentication comparison between WWSAPI and WCF mentioned the impersonation level. Here is a bit more detail as people still seem to be caught by surprise due to this difference. I mentioned that WCF client could set the impersonation level, but I didn’t mention the default value. The default impersonation level in the WCF client is Identification, except for Basic authentication, which always allows Delegation since the server receives the username/password. Since WWSAPI’s client always has impersonation level Impersonation, the difference can lead to behavior difference when the service needs to impersonate the client token and do some work. In that case, the WCF client may not work with the default config and code generated by SvcUtil.exe. You’d have to change the impersonation level to Impersonation, e.g.:

client.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;

 

Posted by haoxu | 1 Comments

WWSAPI to WCF interop 9: secure conversation bootstrapped by Kerberos AP-REQ token

In my post on WWSAPI federation support, I explained how to set up secure conversation on the WWSAPI client to work with a WCF server using WSFederationHttpBinding. In this post, I’ll show how to use secure conversation without federation. Secure conversation can be helpful in reducing the payload size. For example, when Kerberos AP-REQ token is used for authentication, the base64 encoded binary token alone is around 6KB in text UTF-8 encoding. Using secure conversation to negotiate a smaller security context token (SCT) can improve performance when there is frequent message exchange between client and server.

 

To use Kerberos AP-REQ in mixed security in WCF, we have to use custom binding. Here is the config section:

      <customBinding>

        <binding name="SCWithKerberos">

          <security authenticationMode="SecureConversation">

            <secureConversationBootstrap authenticationMode="KerberosOverTransport" />

          </security>

          <httpsTransport />

        </binding>

      </customBinding>

 

The corresponding WS_SECURITY_DESCRIPTION can be set up using the following code:

   // declare and initialize a default windows credential

    WS_DEFAULT_WINDOWS_INTEGRATED_AUTH_CREDENTIAL defaultCredential = {

        {WS_DEFAULT_WINDOWS_INTEGRATED_AUTH_CREDENTIAL_TYPE}

    };   

 

    // declare and initialize a kerberos APREQ message security binding

    WS_KERBEROS_APREQ_MESSAGE_SECURITY_BINDING kerberosBinding = {

        {WS_KERBEROS_APREQ_MESSAGE_SECURITY_BINDING_TYPE},

        WS_SUPPORTING_MESSAGE_SECURITY_USAGE,

        &defaultCredential.credential

    };

   

    // declare and initialize an SSL transport security binding

    WS_SSL_TRANSPORT_SECURITY_BINDING sslBinding = {

        {WS_SSL_TRANSPORT_SECURITY_BINDING_TYPE}

    };

   

    // declare and initialize the array of all security bindings

    WS_SECURITY_BINDING* bootstrapSecurityBindings[2] = {

        &sslBinding.binding,

        &kerberosBinding.binding

    };

   

    // declare and initialize the security description

    WS_SECURITY_DESCRIPTION bootstrapSecurityDescription = {

        bootstrapSecurityBindings,

        WsCountOf(bootstrapSecurityBindings)

    };

 

As described in my post on WWSAPI federation support, we then need to include the bootstrap WS_SECURITY_DESCRIPTION above into WS_SECURITY_CONTEXT_MESSAGE_SECURITY_BINDING.

    // declare and initialize a secure conversation message security binding

    WS_SECURITY_CONTEXT_MESSAGE_SECURITY_BINDING contextBinding = {

        {WS_SECURITY_CONTEXT_MESSAGE_SECURITY_BINDING_TYPE},

        WS_SUPPORTING_MESSAGE_SECURITY_USAGE,

        &bootstrapSecurityDescription

    };

 

    // declare and initialize the array of all security bindings

    WS_SECURITY_BINDING* securityBindings[2] = {

        &contextBinding.binding,

        &sslBinding.binding

    };

 

    // declare and initialize the security description

    WS_SECURITY_DESCRIPTION securityDescription = {

        securityBindings,

        WsCountOf(securityBindings)

    };

 

One thing to note about Kerberos AP-REQ token is that the symmetric key size can be 128-bit, as opposed to the 256-bit required in the Basic256 algorithm suite, the default used by both WWSAPI and WCF.

One note about running the examples using HTTP

It turns out that with the example code as is, starting a server at an HTTP or HTTPS address does not require the process to be run as an admin (elevated on Vista and Win7). No namespace needs to be reserved either. This is because the code uses localhost as host name, which will only match traffic sent to localhost (i.e. from a local client) and is considered safe. A practical scenario of this is building and running HTTP.SYS based application from Visual Studio without running as an admin. My experiments show that using the wildcards, machine name, or even the loopback addressing 127.0.0.1 requires admin token or namespace reservation. For XP, this behavior appears to be new in SP3.

Posted by haoxu | 1 Comments
Filed under: ,

A simple way to run the WWSAPI Kerberos over SSL samples

The Kerberos over SSL samples (like the calculator one) demonstrate WWSAPI mixed mode security that matches the WCF’s KerberosOverTransport authentication mode. In this mode, the Kerberos AP-REQ ticket is wrapped in a WS-Security header for client and server authentication. There is no negotiation here as in Negotiate header authentication or default SSPI over TCP binding. In order to get a Kerberos ticket, you’ll need a KDC, which means the machine needs to be joined to a domain. The other thing about Kerberos is that the AP-REQ ticket is encrypted for a certain server account and only that server can decrypt it to complete the authentication. The server is identified on the client using the Service Principal Name (SPN), which is set in WS_ENDPOINT_ADDRESS as the identity field. SPN can be set on the domain controller for domain accounts. The machines joined to a domain will automatically get an SPN. Processes running as Local System or Network Service will have the machine credential and therefore can decrypt Kerberos tickets issued for the machine account. The SPN for a given machine account looks like “HOST/<MachineName>”.

 

With the background information above, we can now start running the Kerberos over SSL samples. First, please follow the steps in my previous post to set up SSL. In order to avoid confusion around which SPN to use for an arbitrary domain user, I am leveraging the fact the machine accounts get SPNs automatically when joining a domain. Let’s run the server under Local System account! You can do this in Task Scheduler, but here is how you do it in a command line prompt (elevated when you use Vista and above):

1.       Find out the current time:

echo %time%

2.       Schedule the server to run at a future time. Please give the full path of the executable and make sure WebServices.dll is available in the system path. For example:

at.exe 11:04am "C:\Program Files\Microsoft SDKs\Windows\v7.0\Samples\bin\WsCalculatorServiceKerberosOverSsl.exe" /interactive

3.       Wait until 11:04am and verify that WsCalculatorServiceKerberosOverSsl.exe has been started (Task Manager shows SYSTEM account), then run the client with the SPN as the parameter:

WsCalculatorClientKerberosOverSsl.exe HOST/%ComputerName%

 

If you turn on WWSAPI message tracing, you’ll see that a big Security header with base64-encoded Kerberos AP-REQ ticket there.

Posted by haoxu | 1 Comments
More Posts Next page »
 
Page view tracker