#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <readpassphrase.h>
#include <DirectoryService/DirectoryService.h>


#ifndef bool
typedef enum    { false = 0, true = 1 } bool;
#endif  /* bool */


// 32k buffer
#define BUFFERSIZE  32 * 1024  


tDirReference DoNTLMv2Auth(
    tDirReference       dirRef,
    tDirNodeReference  nodeRef,
    const char           *user, 
    const char      *challange, 
          long    challangeLen, 
    const char       *response, 
          long     responseLen, 
    const char         *domain, 
    bool           *authResult,
          char     **resultMsg  )
{
    long               dirStatus = eDSNoErr ;
    tDataBufferPtr    authBuffer ;
    tDataBufferPtr    respBuffer ;
    tContextData     authContext = NULL ;
    tDataNodePtr        authType ;

    long                 userLen = strlen( user );
    long               domainLen = strlen( domain );
    long          authBufferLen  ;


    authBufferLen = sizeof(long) + userLen
                  + sizeof(long) + challangeLen 
                  + sizeof(long) + responseLen 
                  + sizeof(long) + userLen
                  + sizeof(long) + domainLen  ;

    authBuffer    = dsDataBufferAllocate( dirRef, authBufferLen );
    respBuffer    = dsDataBufferAllocate( dirRef, BUFFERSIZE );

#if ( MAC_OS_X_VERSION_MAX_ALLOWED  >= MAC_OS_X_VERSION_10_5 )
    // Leopard

    dsFillAuthBuffer( authBuffer, 5,
                      userLen, user,
                 challangeLen, challange, 
                  responseLen, response,
                      userLen, user,
                    domainLen, domain );

#else // tiger or earier
    long             pos         = 0;
   // user name
    memcpy( &(authBuffer->fBufferData[pos]),      &userLen, sizeof(long) );
    pos += sizeof(long);
    memcpy( &(authBuffer->fBufferData[pos]),          user,      userLen );
    pos += userLen ;

   // server challange
    memcpy( &(authBuffer->fBufferData[pos]), &challangeLen, sizeof(long) );
    pos += sizeof(long);
    memcpy( &(authBuffer->fBufferData[pos]),     challange, challangeLen );
    pos += challangeLen ;

   // client response ( response + client blob )
    memcpy( &(authBuffer->fBufferData[pos]),  &responseLen, sizeof(long) );
    pos += sizeof(long);
    memcpy( &(authBuffer->fBufferData[pos]),      response,  responseLen );
    pos += responseLen ;

   // samba user name 
    memcpy( &(authBuffer->fBufferData[pos]),      &userLen, sizeof(long) );
    pos += sizeof(long);
    memcpy( &(authBuffer->fBufferData[pos]),          user,      userLen );
    pos += userLen ;

   // samba domain
    memcpy( &(authBuffer->fBufferData[pos]),    &domainLen, sizeof(long) );
    pos += sizeof(long);
    memcpy( &(authBuffer->fBufferData[pos]),        domain,    domainLen );
    pos += domainLen ;

    authBuffer->fBufferLength = pos ;
#endif

    authType  = dsDataNodeAllocateString( dirRef, kDSStdAuthNTLMv2 );
    dirStatus =  dsDoDirNodeAuth( nodeRef, authType, true, authBuffer, respBuffer, &authContext );
    switch( dirStatus )
    {
    case eDSNoErr: // Auth Success
       *authResult = true ;
        if( resultMsg ) *resultMsg = "Auth Success." ;
        break;
    case eDSAuthFailed : // Auth Fail 
       *authResult = false ;
        if( resultMsg ) *resultMsg = "Auth Failure." ;
        break;
    case eDSAuthMethodNotSupported : // Auth not supported
       *authResult = false ;
        if( resultMsg ) *resultMsg = "This Auth Type not supported." ;
        break;
    default: 
       *authResult = false ;
        if( resultMsg ) *resultMsg = "unknown error." ;
        break;
    }
    dsDataBufferDeAllocate ( dirRef, authBuffer );
    dsDataBufferDeAllocate ( dirRef, respBuffer );

    return dirStatus;
}

int main( int argc, char *argv[] )
{
    int                         ret = 0 ;
    long                  dirStatus = eDSNoErr ;

    tDirReference            dirRef = 0 ;
    tDirNodeReference       nodeRef = 0 ;

    const char            *nodepath = (argc > 1 ? argv[1] : "/Local/Default" );
    tDataListPtr           nodeName = NULL ;
    const char                *user = (argc > 2 ? argv[2] : "me" );
    char           pass[BUFFERSIZE] ;  // tenuki
    const char              *domain = (argc > 3 ? argv[3] : "WORKGROUP" );
    const char          challange[] = { 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 } ; // 8byte challange
    const char               blob[] = { 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // signature & reserved
                                        0x00, 0x90, 0xd3, 0x36, 0xb7, 0x34, 0xc3, 0x01, // timestamp
                                        0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, // client once
                                        0x00, 0x00, 0x00, 0x00, // unknown 1
                                        0x02, 0x00, 0x0c, 0x00, 0x44, 0x00, 0x4f, 0x00, // target information
                                        0x4d, 0x00, 0x41, 0x00, 0x49, 0x00, 0x4e, 0x00,
                                        0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x45, 0x00,
                                        0x52, 0x00, 0x56, 0x00, 0x45, 0x00, 0x52, 0x00,
                                        0x04, 0x00, 0x14, 0x00, 0x64, 0x00, 0x6f, 0x00,
                                        0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00,
                                        0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00,
                                        0x03, 0x00, 0x22, 0x00, 0x73, 0x00, 0x65, 0x00,
                                        0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00,
                                        0x2e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00,
                                        0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00,
                                        0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00,
                                        0x00, 0x00, 0x00, 0x00,
                                        0x00, 0x00             // unknown 2
                                      };

    char               response[16 + sizeof( blob ) ] ; // response
    char                 *resultMsg = NULL ;
    bool                   isAuthOK = false;


  // open OD
    dirStatus = dsOpenDirService( &dirRef );
    if( dirStatus != eDSNoErr ) goto errexit ;

  // open node
    nodeName = dsBuildFromPath( dirRef, nodepath, "/" );
    if( nodeName == NULL )
    {
        fprintf( stderr, "name buffer allocate error\n") ;
        ret = 1; 
        goto toexit;
    }
    dirStatus = dsOpenDirNode( dirRef, nodeName, &nodeRef );
    if( dirStatus != eDSNoErr ) goto errexit ;

    if( NULL == readpassphrase("Passwd: ", pass, sizeof( pass ), 0 ) )
        goto errexit ;

    makeNTLMv2Resnpose( user, pass, challange, sizeof(challange), blob, sizeof(blob), domain, response ); 
    dirStatus = DoNTLMv2Auth( dirRef, 
                             nodeRef, 
                                user, 
                           challange, sizeof(challange),
                            response, sizeof(response),
                              domain,
                           &isAuthOK, 
                          &resultMsg );

    printf( "user %s, auth %s.\n", user, ( isAuthOK ? "OK" : "NG" ) );
    if( resultMsg != NULL )
         printf( "msg = %s\n", resultMsg );

    goto toexit;


errexit:
    ret = 1;
    fprintf( stderr, "errocode = %d\n", dirStatus );
toexit:
    if( nodeRef != 0 )
        dsCloseDirNode( nodeRef );
    if( dirRef != 0 )
        dsCloseDirService( dirRef );
    exit(ret);
}