/*--

Copyright (c) 1987-1993  Microsoft Corporation

Module Name:

    ssptest.c

Abstract:

    Test program for the NtLmSsp service.

Author:

    28-Jun-1993 (cliffv)

Environment:

    User mode only.
    Contains NT-specific code.
    Requires ANSI C extensions: slash-slash comments, long external names.

Revision History:

--*/


//
// Common include files.
//

extern "C"
{
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntseapi.h>
#include <ntlsa.h>
#include <windef.h>
#include <winbase.h>
#include <winsvc.h>     // Needed for service controller APIs
#include <lmcons.h>
#include <lmerr.h>
#include <lmaccess.h>
#include <lmsname.h>
#include <rpc.h>
#include <stdio.h>      // printf
#include <stdlib.h>     // strtoul
}
#include <wchar.h>
extern "C" {
#include <netlib.h>     // NetpGetLocalDomainId
#include <tstring.h>    // NetpAllocWStrFromWStr
#define SECURITY_KERBEROS
#define SECURITY_PACKAGE
#include <security.h>   // General definition of a Security Support Provider
#include <secint.h>
#include <kerbcomm.h>
#include <negossp.h>
#include <wincrypt.h>
#include <cryptui.h>
}
#include <sclogon.h>



BOOLEAN QuietMode = FALSE; // Don't be verbose
BOOLEAN DoAnsi = FALSE;
ULONG RecursionDepth = 0;
CredHandle ServerCredHandleStorage;
PCredHandle ServerCredHandle = NULL;
#define MAX_RECURSION_DEPTH 1


VOID
DumpBuffer(
    PVOID Buffer,
    DWORD BufferSize
    )
/*++

Routine Description:

    Dumps the buffer content on to the debugger output.

Arguments:

    Buffer: buffer pointer.

    BufferSize: size of the buffer.

Return Value:

    none

--*/
{
#define NUM_CHARS 16

    DWORD i, limit;
    CHAR TextBuffer[NUM_CHARS + 1];
    LPBYTE BufferPtr = (PBYTE) Buffer;


    printf("------------------------------------\n");

    //
    // Hex dump of the bytes
    //
    limit = ((BufferSize - 1) / NUM_CHARS + 1) * NUM_CHARS;

    for (i = 0; i < limit; i++) {

        if (i < BufferSize) {

            printf("%02x ", BufferPtr[i]);

            if (BufferPtr[i] < 31 ) {
                TextBuffer[i % NUM_CHARS] = '.';
            } else if (BufferPtr[i] == '\0') {
                TextBuffer[i % NUM_CHARS] = ' ';
            } else {
                TextBuffer[i % NUM_CHARS] = (CHAR) BufferPtr[i];
            }

        } else {

            printf("  ");
            TextBuffer[i % NUM_CHARS] = ' ';

        }

        if ((i + 1) % NUM_CHARS == 0) {
            TextBuffer[NUM_CHARS] = 0;
            printf("  %s\n", TextBuffer);
        }

    }

    printf("------------------------------------\n");
}


VOID
PrintTime(
    LPSTR Comment,
    TimeStamp ConvertTime
    )
/*++

Routine Description:

    Print the specified time

Arguments:

    Comment - Comment to print in front of the time

    Time - Local time to print

Return Value:

    None

--*/
{
    LARGE_INTEGER LocalTime;

    LocalTime.HighPart = ConvertTime.HighPart;
    LocalTime.LowPart = ConvertTime.LowPart;

    printf( "%s", Comment );

    //
    // If the time is infinite,
    //  just say so.
    //

    if ( LocalTime.HighPart == 0x7FFFFFFF && LocalTime.LowPart == 0xFFFFFFFF ) {
        printf( "Infinite\n" );

    //
    // Otherwise print it more clearly
    //

    } else {

        TIME_FIELDS TimeFields;

        RtlTimeToTimeFields( &LocalTime, &TimeFields );

        printf( "%ld/%ld/%ld %ld:%2.2ld:%2.2ld\n",
                TimeFields.Month,
                TimeFields.Day,
                TimeFields.Year,
                TimeFields.Hour,
                TimeFields.Minute,
                TimeFields.Second );
    }

}

VOID
PrintStatus(
    NET_API_STATUS NetStatus
    )
/*++

Routine Description:

    Print a net status code.

Arguments:

    NetStatus - The net status code to print.

Return Value:

    None

--*/
{
    printf( "Status = %lu 0x%lx", NetStatus, NetStatus );

    switch (NetStatus) {
    case NERR_Success:
        printf( " NERR_Success" );
        break;

    case NERR_DCNotFound:
        printf( " NERR_DCNotFound" );
        break;

    case ERROR_LOGON_FAILURE:
        printf( " ERROR_LOGON_FAILURE" );
        break;

    case ERROR_ACCESS_DENIED:
        printf( " ERROR_ACCESS_DENIED" );
        break;

    case ERROR_NOT_SUPPORTED:
        printf( " ERROR_NOT_SUPPORTED" );
        break;

    case ERROR_NO_LOGON_SERVERS:
        printf( " ERROR_NO_LOGON_SERVERS" );
        break;

    case ERROR_NO_SUCH_DOMAIN:
        printf( " ERROR_NO_SUCH_DOMAIN" );
        break;

    case ERROR_NO_TRUST_LSA_SECRET:
        printf( " ERROR_NO_TRUST_LSA_SECRET" );
        break;

    case ERROR_NO_TRUST_SAM_ACCOUNT:
        printf( " ERROR_NO_TRUST_SAM_ACCOUNT" );
        break;

    case ERROR_DOMAIN_TRUST_INCONSISTENT:
        printf( " ERROR_DOMAIN_TRUST_INCONSISTENT" );
        break;

    case ERROR_BAD_NETPATH:
        printf( " ERROR_BAD_NETPATH" );
        break;

    case ERROR_FILE_NOT_FOUND:
        printf( " ERROR_FILE_NOT_FOUND" );
        break;

    case NERR_NetNotStarted:
        printf( " NERR_NetNotStarted" );
        break;

    case NERR_WkstaNotStarted:
        printf( " NERR_WkstaNotStarted" );
        break;

    case NERR_ServerNotStarted:
        printf( " NERR_ServerNotStarted" );
        break;

    case NERR_BrowserNotStarted:
        printf( " NERR_BrowserNotStarted" );
        break;

    case NERR_ServiceNotInstalled:
        printf( " NERR_ServiceNotInstalled" );
        break;

    case NERR_BadTransactConfig:
        printf( " NERR_BadTransactConfig" );
        break;

    case SEC_E_NO_SPM:
        printf( " SEC_E_NO_SPM" );
        break;
    case SEC_E_BAD_PKGID:
        printf( " SEC_E_BAD_PKGID" ); break;
    case SEC_E_NOT_OWNER:
        printf( " SEC_E_NOT_OWNER" ); break;
    case SEC_E_CANNOT_INSTALL:
        printf( " SEC_E_CANNOT_INSTALL" ); break;
    case SEC_E_INVALID_TOKEN:
        printf( " SEC_E_INVALID_TOKEN" ); break;
    case SEC_E_CANNOT_PACK:
        printf( " SEC_E_CANNOT_PACK" ); break;
    case SEC_E_QOP_NOT_SUPPORTED:
        printf( " SEC_E_QOP_NOT_SUPPORTED" ); break;
    case SEC_E_NO_IMPERSONATION:
        printf( " SEC_E_NO_IMPERSONATION" ); break;
    case SEC_E_LOGON_DENIED:
        printf( " SEC_E_LOGON_DENIED" ); break;
    case SEC_E_UNKNOWN_CREDENTIALS:
        printf( " SEC_E_UNKNOWN_CREDENTIALS" ); break;
    case SEC_E_NO_CREDENTIALS:
        printf( " SEC_E_NO_CREDENTIALS" ); break;
    case SEC_E_MESSAGE_ALTERED:
        printf( " SEC_E_MESSAGE_ALTERED" ); break;
    case SEC_E_OUT_OF_SEQUENCE:
        printf( " SEC_E_OUT_OF_SEQUENCE" ); break;
    case SEC_E_INSUFFICIENT_MEMORY:
        printf( " SEC_E_INSUFFICIENT_MEMORY" ); break;
    case SEC_E_INVALID_HANDLE:
        printf( " SEC_E_INVALID_HANDLE" ); break;
    case SEC_E_NOT_SUPPORTED:
        printf( " SEC_E_NOT_SUPPORTED" ); break;


    }

    printf( "\n" );
}

VOID
TestGetKdcCert(
    IN LPWSTR ServiceName,
    IN LPWSTR ContainerName,
    IN LPWSTR CaLocation,
    IN LPWSTR CaName
    )
{
    PCCERT_CONTEXT CertContext = NULL;
    DWORD Status = 0;
    WCHAR UsageOid[100];
    WCHAR ComputerName[100];
    ULONG ComputerNameLength = 100;
    LPSTR UsageString;
    CERT_ENHKEY_USAGE KeyUsage;
    CRYPTUI_WIZ_CERT_REQUEST_INFO CertInfo;
    CRYPTUI_WIZ_CERT_REQUEST_PVK_NEW PvkNew;

    RtlZeroMemory(
        &CertInfo,
        sizeof(CRYPTUI_WIZ_CERT_REQUEST_INFO)
        );

    RtlZeroMemory(
        &PvkNew,
        sizeof(CRYPTUI_WIZ_CERT_REQUEST_PVK_NEW)
        );

    GetComputerName(
        ComputerName,
        &ComputerNameLength
        );

    CertInfo.dwSize = sizeof(CertInfo);
    CertInfo.dwPurpose = CRYPTUI_WIZ_CERT_ENROLL;
    CertInfo.pwszMachineName = ComputerName;            // local computer
    CertInfo.pwszAccountName = NULL;            // ServiceName;
    CertInfo.pAuthentication = NULL;            // MBZ
    CertInfo.pCertRequestString = NULL;         // ??
    CertInfo.pwszDesStore = ContainerName;
    CertInfo.dwCertOpenStoreFlag = CERT_SYSTEM_STORE_SERVICES;
    CertInfo.pRenewCertContext = NULL;          // we aren't renewing
    CertInfo.dwPvkChoice = CRYPTUI_WIZ_CERT_REQUEST_PVK_CHOICE_NEW;
                                                // generate new key
    CertInfo.pPvkNew = &PvkNew;
    PvkNew.dwSize = sizeof(PvkNew);
    PvkNew.pKeyProvInfo = NULL;                 // use default provider
    PvkNew.dwGenKeyFlags = 0; // CRYPT_MACHINE_KEYSET;                   // no flags
    CertInfo.pwszCALocation = CaLocation;           // ignore for no-ui enrollment
    CertInfo.pwszCAName = CaName;
    CertInfo.dwPostOption = 0; // CRYPTUI_WIZ_CERT_REQUEST_POST_ON_CSP;
    KeyUsage.cUsageIdentifier = 1;
    UsageString = szOID_PKIX_KP_SERVER_AUTH;
    KeyUsage.rgpszUsageIdentifier = &UsageString;
    CertInfo.pKeyUsage = &KeyUsage;

    CertInfo.pwszFriendlyName = NULL;           // friendly name is optional
    CertInfo.pwszDescription = NULL;            // description is optional

    //
    // Request a certificate
    //

    if (!CryptUIWizCertRequest(
            CRYPTUI_WIZ_NO_UI,
            NULL,                       // no window
            NULL,                       // no title
            &CertInfo,
            &CertContext,
            &Status
            ))
    {
        printf("CryptUIWizCertRequest failed: 0x%x\n",GetLastError());
        return;
    }
    //
    // Check the real status
    //

    if (Status == CRYPTUI_WIZ_CERT_REQUEST_STATUS_SUCCEEDED)
    {
        printf("Cert request succeeded!\n");
    }
    if (Status == CRYPTUI_WIZ_CERT_REQUEST_STATUS_REQUEST_ERROR)
    {
        printf("Cert request failed: request error\n");
    }
    if (Status == CRYPTUI_WIZ_CERT_REQUEST_STATUS_REQUEST_DENIED)
    {
        printf("Cert request denied\n");
    }
    if (Status == CRYPTUI_WIZ_CERT_REQUEST_STATUS_ISSUED_SEPARATELY)
    {
        printf("Cert request issued seperately\n");
    }
    if (Status == CRYPTUI_WIZ_CERT_REQUEST_STATUS_UNDER_SUBMISSION)
    {
        printf("Cert request under submission\n");
    }

    return;
}

VOID
TestScLogonRoutine(
    IN ULONG Count,
    IN LPSTR Pin
    )
{
    NTSTATUS Status;
    PKERB_SMART_CARD_LOGON LogonInfo;
    ULONG LogonInfoSize = sizeof(KERB_SMART_CARD_LOGON);
    BOOLEAN WasEnabled;
    STRING PinString;
    STRING Name;
    ULONG Dummy;
    HANDLE LogonHandle = NULL;
    ULONG PackageId;
    TOKEN_SOURCE SourceContext;
    PKERB_SMART_CARD_PROFILE Profile = NULL;
    ULONG ProfileSize;
    LUID LogonId;
    HANDLE TokenHandle = NULL;
    QUOTA_LIMITS Quotas;
    NTSTATUS SubStatus;
    WCHAR UserNameString[100];
    ULONG NameLength = 100;
    PUCHAR Where;
    ULONG Index;
    HANDLE ScHandle = NULL;
    PBYTE ScLogonInfo = NULL;
    ULONG ScLogonInfoSize;
    ULONG WaitResult = 0;
    PCCERT_CONTEXT CertContext = NULL;

    printf("Waiting for smart card insertion\n");

    //
    // First register for insertion notification
    //

    Status = ScHelperInitialize();
    if (!NT_SUCCESS(Status))
    {
        printf("Failed to initialize schelper: 0x%x\n",Status);
        return;
    }

    ScHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (ScHandle == NULL)
    {
        printf("Failed to create event: %d\n",GetLastError());
        return;
    }

    Status = ScHelperWatchForSas(
                ScHandle,
                &ScLogonInfo
                );
    if (!NT_SUCCESS(Status))
    {
        printf("Failed to watch for SAS: 0x%x\n",Status);
        return;
    }

    WaitResult = WaitForSingleObject(ScHandle,INFINITE);
    if (WaitResult != WAIT_OBJECT_0)
    {
        printf("Failed to wait for single object: %d\n",GetLastError());
        return;
    }

    //
    // We should now have logon info.
    //

    if (ScLogonInfo == NULL)
    {
        printf("Failed to get logon info!\n");
        return;
    }

    ScLogonInfoSize = ((struct LogonInfo *) ScLogonInfo)->dwLogonInfoLen;

    Status = ScHelperInitializeContext(
                ScLogonInfo,
                ScLogonInfoSize
                );
    if (!NT_SUCCESS(Status))
    {
        printf("Failed to initialize context: 0x%x\n",Status);
        return;
    }

    ScHelperRelease(ScLogonInfo);

    RtlInitString(
        &PinString,
        Pin
        );


    LogonInfoSize += (PinString.Length+1 ) * sizeof(WCHAR) + ScLogonInfoSize;

    LogonInfo = (PKERB_SMART_CARD_LOGON) LocalAlloc(LMEM_ZEROINIT, LogonInfoSize);

    LogonInfo->MessageType = KerbSmartCardLogon;


    Where = (PUCHAR) (LogonInfo + 1);

    LogonInfo->Pin.Buffer = (LPWSTR) Where;
    LogonInfo->Pin.MaximumLength = (USHORT) LogonInfoSize;
    RtlAnsiStringToUnicodeString(
        &LogonInfo->Pin,
        &PinString,
        FALSE
        );
    Where += LogonInfo->Pin.Length + sizeof(WCHAR);

    LogonInfo->CspDataLength = ScLogonInfoSize;
    LogonInfo->CspData = Where;
    RtlCopyMemory(
        LogonInfo->CspData,
        ScLogonInfo,
        ScLogonInfoSize
        );
    Where += ScLogonInfoSize;

    //
    // Turn on the TCB privilege
    //

    Status = RtlAdjustPrivilege(SE_TCB_PRIVILEGE, TRUE, FALSE, &WasEnabled);
    if (!NT_SUCCESS(Status))
    {
        printf("Failed to adjust privilege: 0x%x\n",Status);
        return;
    }
    RtlInitString(
        &Name,
        "SspTest"
        );
    Status = LsaRegisterLogonProcess(
                &Name,
                &LogonHandle,
                &Dummy
                );
    if (!NT_SUCCESS(Status))
    {
        printf("Failed to register as a logon process: 0x%x\n",Status);
        return;
    }

    strncpy(
        SourceContext.SourceName,
        "ssptest        ",sizeof(SourceContext.SourceName)
        );
    NtAllocateLocallyUniqueId(
        &SourceContext.SourceIdentifier
        );


    RtlInitString(
        &Name,
        MICROSOFT_KERBEROS_NAME_A
        );
    Status = LsaLookupAuthenticationPackage(
                LogonHandle,
                &Name,
                &PackageId
                );
    if (!NT_SUCCESS(Status))
    {
        printf("Failed to lookup package %Z: 0x%x\n",&Name, Status);
        return;
    }

    //
    // Now call LsaLogonUser
    //

    RtlInitString(
        &Name,
        "ssptest"
        );

    for (Index = 0; Index < Count ; Index++ )
    {
        printf("Logging on with PIN  %s\n",Pin);

        Status = LsaLogonUser(
                    LogonHandle,
                    &Name,
                    Interactive,
                    PackageId,
                    LogonInfo,
                    LogonInfoSize,
                    NULL,           // no token groups
                    &SourceContext,
                    (PVOID *) &Profile,
                    &ProfileSize,
                    &LogonId,
                    &TokenHandle,
                    &Quotas,
                    &SubStatus
                    );
        if (!NT_SUCCESS(Status))
        {
            printf("lsalogonuser failed: 0x%x\n",Status);
            return;
        }
        if (!NT_SUCCESS(SubStatus))
        {
            printf("LsalogonUser failed: substatus = 0x%x\n",SubStatus);
            return;
        }

        ImpersonateLoggedOnUser( TokenHandle );
        GetUserName(UserNameString,&NameLength);
        printf("Username = %ws\n",UserNameString);
        RevertToSelf();
        NtClose(TokenHandle);

        if (Profile->Profile.MessageType = MsV1_0SmartCardProfile)
        {
            CertContext = CertCreateCertificateContext(
                            X509_ASN_ENCODING,
                            Profile->CertificateData,
                            Profile->CertificateSize
                            );
            if (CertContext == NULL)
            {
                printf("Failed to create cert context: 0x%x\n",GetLastError());
            }
            else
            {
                printf("Built certificate context\n");
                CertFreeCertificateContext( CertContext );
                CertContext = NULL;
            }
        }
        else
        {
            printf("No certificate in profile\n");
        }

        

        LsaFreeReturnBuffer(Profile);
        Profile = NULL;

    }

}


VOID
PrintKdcName(
    IN PKERB_INTERNAL_NAME Name
    )
{
    ULONG Index;
    for (Index = 0; Index < Name->NameCount ; Index++ )
    {
        printf(" %wZ ",&Name->Names[Index]);
    }
    printf("\n");
}

VOID
TestCallPackageRoutine(
    IN LPWSTR Function
    )
{
    NTSTATUS Status;
    BOOLEAN WasEnabled;
    STRING Name;
    ULONG Dummy;
    HANDLE LogonHandle = NULL;
    ULONG PackageId;
    KERB_DEBUG_REQUEST DebugRequest;
    KERB_QUERY_TKT_CACHE_REQUEST CacheRequest;
    PKERB_QUERY_TKT_CACHE_RESPONSE CacheResponse = NULL;
    ULONG Index;
    PVOID Response;
    ULONG ResponseSize;
    NTSTATUS SubStatus;
    BOOLEAN Trusted = TRUE;

    //
    // Turn on the TCB privilege
    //

    Status = RtlAdjustPrivilege(SE_TCB_PRIVILEGE, TRUE, FALSE, &WasEnabled);
    if (!NT_SUCCESS(Status))
    {
        printf("Failed to adjust privilege: 0x%x\n",Status);
        Trusted = FALSE;
    }
    RtlInitString(
        &Name,
        "SspTest"
        );

    if (Trusted)
    {
        Status = LsaRegisterLogonProcess(
                    &Name,
                    &LogonHandle,
                    &Dummy
                    );

    }
    else
    {
        Status = LsaConnectUntrusted(
                    &LogonHandle
                    );
    }

    if (!NT_SUCCESS(Status))
    {
        printf("Failed to register as a logon process: 0x%x\n",Status);
        return;
    }



    RtlInitString(
        &Name,
        MICROSOFT_KERBEROS_NAME_A
        );
    Status = LsaLookupAuthenticationPackage(
                LogonHandle,
                &Name,
                &PackageId
                );
    if (!NT_SUCCESS(Status))
    {
        printf("Failed to lookup package %Z: 0x%x\n",&Name, Status);
        return;
    }

    if (_wcsicmp(Function,L"bp") == 0)
    {
        DebugRequest.MessageType = KerbDebugRequestMessage;
        DebugRequest.DebugRequest = KERB_DEBUG_REQ_BREAKPOINT;

        Status = LsaCallAuthenticationPackage(
                    LogonHandle,
                    PackageId,
                    &DebugRequest,
                    sizeof(DebugRequest),
                    &Response,
                    &ResponseSize,
                    &SubStatus
                    );
        if (!NT_SUCCESS(Status) || !NT_SUCCESS(SubStatus))
        {
            printf("bp failed: 0x%x, 0x %x\n",Status, SubStatus);
        }

    }
    else if (_wcsicmp(Function,L"tickets")  == 0)
    {
        CacheRequest.MessageType = KerbQueryTicketCacheMessage;
        CacheRequest.LogonId.LowPart = 0;
        CacheRequest.LogonId.HighPart = 0;

        Status = LsaCallAuthenticationPackage(
                    LogonHandle,
                    PackageId,
                    &CacheRequest,
                    sizeof(CacheRequest),
                    (PVOID *) &CacheResponse,
                    &ResponseSize,
                    &SubStatus
                    );
        if (!NT_SUCCESS(Status) || !NT_SUCCESS(SubStatus))
        {
            printf("bp failed: 0x%x, 0x %x\n",Status, SubStatus);
        }
        else
        {
            printf("Cached Tickets:\n");
            for (Index = 0; Index < CacheResponse->CountOfTickets ; Index++ )
            {
                printf("\tServer: %wZ\n",&CacheResponse->Tickets[Index].ServerName);
                PrintTime("\t\tEnd Time: ",CacheResponse->Tickets[Index].EndTime);
                PrintTime("\t\tRenew Time: ",CacheResponse->Tickets[Index].RenewTime);

            }
        }


    }

    if (LogonHandle != NULL)
    {
        LsaDeregisterLogonProcess(LogonHandle);
    }

    if (CacheResponse != NULL)
    {
        LsaFreeReturnBuffer(CacheResponse);
    }
}


int __cdecl
main(
    IN int argc,
    IN char ** argv
    )
/*++

Routine Description:

    Drive the NtLmSsp service

Arguments:

    argc - the number of command-line arguments.

    argv - an array of pointers to the arguments.

Return Value:

    Exit status

--*/
{
    LPSTR argument;
    int i;
    ULONG j;
    ULONG Iterations;
    LPSTR Pin;
    LPWSTR PackageFunction;
    ULONG ContextReq = 0;
    WCHAR ContainerName[100];
    WCHAR CaName[100];
    WCHAR CaLocation[100];
    WCHAR ServiceName[100];




    enum {
        NoAction,
#define LOGON_PARAM "/Logon"
#define LOGON_PARAM2 "/Logon:"
        TestLogon,
#define PACKAGE_PARAM "/callpackage:"
        TestPackage,
#define CERT_PARAM "/getcert"
        GetCert,
    } Action = NoAction;





    //
    // Loop through the arguments handle each in turn
    //

    for ( i=1; i<argc; i++ ) {

        argument = argv[i];

        //
        // Handle /ConfigureService
        //

        if ( _strnicmp( argument, LOGON_PARAM, sizeof(LOGON_PARAM)-1 ) == 0 ) {
            if ( Action != NoAction ) {
                goto Usage;
            }
            Iterations = 1;
            if ( _strnicmp( argument, LOGON_PARAM2, sizeof(LOGON_PARAM2)-1 ) == 0 ) {
                sscanf(&argument[sizeof(LOGON_PARAM2)-1], "%d",&Iterations);
            }


            Action = TestLogon;

            if (argc < i + 1)
            {
                goto Usage;
            }
            Pin = argv[++i];
        } else if ( _strnicmp( argument, PACKAGE_PARAM, sizeof(PACKAGE_PARAM) - 1 ) == 0 ) {
            if ( Action != NoAction ) {
                goto Usage;
            }

            argument = &argument[sizeof(PACKAGE_PARAM)-1];
            PackageFunction = NetpAllocWStrFromStr( argument );

            Action = TestPackage;
        } else if ( _stricmp( argument, CERT_PARAM) == 0 ) {
            if ( Action != NoAction ) {
                goto Usage;
            }
            if (argc < i + 4)
            {
                goto Usage;
            }

            mbstowcs(ServiceName,argv[++i],100);
            mbstowcs(ContainerName,argv[++i],100);
            mbstowcs(CaLocation,argv[++i],100);
            mbstowcs(CaName,argv[++i],100);

            Action = GetCert;
        } else {
            printf("Invalid parameter : %s\n",argument);
            goto Usage;
        }


    }

    //
    // Perform the action requested
    //

    switch ( Action ) {

    case TestPackage:
        TestCallPackageRoutine(PackageFunction);
        break;

    case TestLogon :
        TestScLogonRoutine(
            Iterations,
            Pin
            );
        break;
    case GetCert:
        TestGetKdcCert(
            ServiceName,
            ContainerName,
            CaLocation,
            CaName
            );

    }
    return 0;
Usage:
    printf("%s /logon username password [domainname]\n",argv[0]);
    printf("%s /testssp [/package:pacakgename] [/target:targetname] [/user:username] [/serveruser:username]\n",
        argv[0]);
    printf("%s /getcert service-name container-name ca-location ca-name\n",
        argv[0]);
    return 0;

}